M .builds/debian-stable.yml => .builds/debian-stable.yml +2 -2
@@ 28,6 28,6 @@ tasks:
sed -ie 's/\/\/ INSERT/implementation "io.sentry:sentry-android:5.6.1"/' build.gradle
- build: |
cd cheogram-android
- ./gradlew assembleCheogramFreeCompatDebug
+ ./gradlew assembleCheogramFreeDebug
- assets: |
- mv cheogram-android/build/outputs/apk/cheogramFreeCompat/debug/*.apk cheogram.apk
+ mv cheogram-android/build/outputs/apk/cheogramFree/debug/*.apk cheogram.apk
M .github/workflows/android.yml => .github/workflows/android.yml +4 -8
@@ 22,14 22,10 @@ jobs:
run: mkdir libs && wget -O libs/libwebrtc-m92.aar https://gultsch.de/files/libwebrtc-m92.aar
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- - name: Build Quicksy (Compat)
- run: ./gradlew assembleQuicksyFreeCompatDebug
- - name: Build Quicksy (System)
- run: ./gradlew assembleQuicksyFreeSystemDebug
- - name: Build Conversations (Compat)
- run: ./gradlew assembleConversationsFreeCompatDebug
- - name: Build Conversations (System)
- run: ./gradlew assembleConversationsFreeSystemDebug
+ - name: Build Quicksy
+ run: ./gradlew assembleQuicksyFreeDebug
+ - name: Build Conversations
+ run: ./gradlew assembleConversationsFreeDebug
- uses: actions/upload-artifact@v2
with:
name: Conversations all-flavors (debug)
M CHANGELOG.md => CHANGELOG.md +5 -0
@@ 1,5 1,10 @@
# Changelog
+### Version 2.10.3
+
+* Store files in location appropriate for Android 11
+* Attempt to reconnect call after network switch
+
### Version 2.10.2
* Fix crash when rendering some quotes
M build.gradle => build.gradle +23 -75
@@ 6,7 6,7 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:7.1.1'
+ classpath 'com.android.tools.build:gradle:7.1.2'
}
}
@@ 38,14 38,13 @@ def urlFile = { url, name ->
configurations {
playstoreImplementation
- compatImplementation
- conversationsFreeCompatImplementation
- cheogramFreeCompatImplementation
- conversationsPlaystoreCompatImplementation
- conversationsPlaystoreSystemImplementation
- quicksyPlaystoreCompatImplementation
- quicksyPlaystoreSystemImplementation
- quicksyFreeCompatImplementation
+ freeImplementation
+ conversationsFreeImplementation
+ conversationsPlaystorImplementation
+ conversationsPlaystoreImplementation
+ quicksyPlaystoreImplementation
+ quicksyPlaystoreImplementation
+ quicksyFreeImplementation
quicksyImplementation
}
@@ 57,22 56,19 @@ dependencies {
exclude group: 'com.google.firebase', module: 'firebase-analytics'
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
}
- conversationsPlaystoreCompatImplementation("com.android.installreferrer:installreferrer:2.2")
- conversationsPlaystoreSystemImplementation("com.android.installreferrer:installreferrer:2.2")
- quicksyPlaystoreCompatImplementation 'com.google.android.gms:play-services-auth-api-phone:18.0.1'
- quicksyPlaystoreSystemImplementation 'com.google.android.gms:play-services-auth-api-phone:18.0.1'
+ conversationsPlaystoreImplementation("com.android.installreferrer:installreferrer:2.2")
+ quicksyPlaystoreImplementation 'com.google.android.gms:play-services-auth-api-phone:18.0.1'
implementation 'org.sufficientlysecure:openpgp-api:10.0'
implementation 'com.theartofdev.edmodo:android-image-cropper:2.8.0'
- implementation 'androidx.appcompat:appcompat:1.3.1'
+ implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.exifinterface:exifinterface:1.3.3'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
- implementation 'androidx.emoji:emoji:1.1.0'
implementation 'com.google.android.material:material:1.4.0'
- compatImplementation 'androidx.emoji:emoji-appcompat:1.1.0'
- conversationsFreeCompatImplementation 'androidx.emoji:emoji-bundled:1.1.0'
- cheogramFreeCompatImplementation 'androidx.emoji:emoji-bundled:1.1.0'
- quicksyFreeCompatImplementation 'androidx.emoji:emoji-bundled:1.1.0'
+
+ implementation "androidx.emoji2:emoji2:1.1.0-rc01"
+ freeImplementation "androidx.emoji2:emoji2-bundled:1.1.0-rc01"
+
implementation 'org.bouncycastle:bcmail-jdk15on:1.64'
//zxing stopped supporting Java 7 so we have to stick with 3.3.3
//https://github.com/zxing/zxing/issues/1170
@@ 138,7 134,7 @@ android {
targetCompatibility JavaVersion.VERSION_1_8
}
- flavorDimensions("mode", "distribution", "emoji")
+ flavorDimensions("mode", "distribution")
productFlavors {
@@ 169,45 165,21 @@ android {
playstore {
dimension "distribution"
- versionNameSuffix "+p"
+ versionNameSuffix "+playstore"
}
free {
dimension "distribution"
- versionNameSuffix "+f"
- }
- system {
- dimension "emoji"
- versionNameSuffix "s"
- }
- compat {
- dimension "emoji"
- versionNameSuffix "c"
+ versionNameSuffix "+free"
}
}
sourceSets {
- quicksyFreeSystem {
- java {
- srcDir 'src/quicksyFree/java'
- }
- }
- quicksyFreeCompat {
+ quicksyFree {
java {
- srcDir 'src/freeCompat/java'
srcDir 'src/quicksyFree/java'
}
}
- quicksyPlaystoreCompat {
- java {
- srcDir 'src/playstoreCompat/java'
- srcDir 'src/quicksyPlaystore/java'
- }
- res {
- srcDir 'src/playstoreCompat/res'
- srcDir 'src/quicksyPlaystore/res'
- }
- }
- quicksyPlaystoreSystem {
+ quicksyPlaystore {
java {
srcDir 'src/quicksyPlaystore/java'
}
@@ 215,39 187,17 @@ android {
srcDir 'src/quicksyPlaystore/res'
}
}
- conversationsFreeCompat {
+ conversationsFree {
java {
- srcDir 'src/freeCompat/java'
srcDir 'src/conversationsFree/java'
}
}
- conversationsFreeSystem {
+ cheogramFree {
java {
srcDir 'src/conversationsFree/java'
}
}
- cheogramFreeCompat {
- java {
- srcDir 'src/freeCompat/java'
- srcDir 'src/conversationsFree/java'
- }
- }
- cheogramFreeSystem {
- java {
- srcDir 'src/conversationsFree/java'
- }
- }
- conversationsPlaystoreCompat {
- java {
- srcDir 'src/playstoreCompat/java'
- srcDir 'src/conversationsPlaystore/java'
- }
- res {
- srcDir 'src/playstoreCompat/res'
- srcDir 'src/conversationsPlaystore/res'
- }
- }
- conversationsPlaystoreSystem {
+ conversationsPlaystore {
java {
srcDir 'src/conversationsPlaystore/java'
}
@@ 262,13 212,11 @@ android {
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- versionNameSuffix "r"
}
debug {
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- versionNameSuffix "d"
}
}
M src/cheogram/java/eu/siacs/conversations/services/ImportBackupService.java => src/cheogram/java/eu/siacs/conversations/services/ImportBackupService.java +7 -4
@@ 128,16 128,19 @@ public class ImportBackupService extends Service {
final List<Jid> accounts = mDatabaseBackend.getAccountJids(false);
final ArrayList<BackupFile> backupFiles = new ArrayList<>();
final Set<String> apps = new HashSet<>(Arrays.asList("Conversations", "Quicksy", getString(R.string.app_name)));
- for (String app : apps) {
- final File directory = new File(FileBackend.getBackupDirectory(app));
+ final List<File> directories = new ArrayList<>();
+ for (final String app : apps) {
+ directories.add(FileBackend.getLegacyBackupDirectory(app));
+ }
+ directories.add(FileBackend.getBackupDirectory(this));
+ for (final File directory : directories) {
if (!directory.exists() || !directory.isDirectory()) {
Log.d(Config.LOGTAG, "directory not found: " + directory.getAbsolutePath());
continue;
}
final File[] files = directory.listFiles();
if (files == null) {
- onBackupFilesLoaded.onBackupFilesLoaded(backupFiles);
- return;
+ continue;
}
for (final File file : files) {
if (file.isFile() && file.getName().endsWith(".ceb")) {
D src/compat/java/eu/siacs/conversations/ui/widget/EmojiWrapperEditText.java => src/compat/java/eu/siacs/conversations/ui/widget/EmojiWrapperEditText.java +0 -18
@@ 1,18 0,0 @@
-package eu.siacs.conversations.ui.widget;
-
-import android.content.Context;
-import android.util.AttributeSet;
-
-import androidx.emoji.widget.EmojiAppCompatEditText;
-
-public class EmojiWrapperEditText extends EmojiAppCompatEditText {
-
- public EmojiWrapperEditText(Context context) {
- super(context);
- }
-
- public EmojiWrapperEditText(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
-}>
\ No newline at end of file
D src/compat/java/eu/siacs/conversations/utils/EmojiWrapper.java => src/compat/java/eu/siacs/conversations/utils/EmojiWrapper.java +0 -47
@@ 1,47 0,0 @@
-/*
- * Copyright (c) 2017, Daniel Gultsch All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation and/or
- * other materials provided with the distribution.
- *
- * 3. Neither the name of the copyright holder nor the names of its contributors
- * may be used to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package eu.siacs.conversations.utils;
-
-import androidx.emoji.text.EmojiCompat;
-
-public class EmojiWrapper {
-
- public static CharSequence transform(CharSequence input) {
- try {
- if (EmojiCompat.get().getLoadState() == EmojiCompat.LOAD_STATE_SUCCEEDED) {
- return EmojiCompat.get().process(input);
- } else {
- return input;
- }
- } catch (IllegalStateException e) {
- return input;
- }
- }
-}
M src/conversations/java/eu/siacs/conversations/services/ImportBackupService.java => src/conversations/java/eu/siacs/conversations/services/ImportBackupService.java +7 -4
@@ 128,16 128,19 @@ public class ImportBackupService extends Service {
final List<Jid> accounts = mDatabaseBackend.getAccountJids(false);
final ArrayList<BackupFile> backupFiles = new ArrayList<>();
final Set<String> apps = new HashSet<>(Arrays.asList("Conversations", "Quicksy", getString(R.string.app_name)));
- for (String app : apps) {
- final File directory = new File(FileBackend.getBackupDirectory(app));
+ final List<File> directories = new ArrayList<>();
+ for (final String app : apps) {
+ directories.add(FileBackend.getLegacyBackupDirectory(app));
+ }
+ directories.add(FileBackend.getBackupDirectory(this));
+ for (final File directory : directories) {
if (!directory.exists() || !directory.isDirectory()) {
Log.d(Config.LOGTAG, "directory not found: " + directory.getAbsolutePath());
continue;
}
final File[] files = directory.listFiles();
if (files == null) {
- onBackupFilesLoaded.onBackupFilesLoaded(backupFiles);
- return;
+ continue;
}
for (final File file : files) {
if (file.isFile() && file.getName().endsWith(".ceb")) {
M src/conversations/res/values-gl/strings.xml => src/conversations/res/values-gl/strings.xml +4 -4
@@ 1,15 1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <string name="pick_a_server">Escolle o teu provedor XMPP</string>
+ <string name="pick_a_server">Elixe o teu provedor XMPP</string>
<string name="use_conversations.im">Utilizar conversations.im</string>
<string name="create_new_account">Crear nova conta</string>
<string name="do_you_have_an_account">Xa posúes unha conta XMPP? Este pode ser o caso se xa estás a utilizar outro cliente XMPP ou utilizaches Conversations previamente. Se non é así podes crear unha nova conta agora mesmo.\nTruco: Algúns provedores de correo tamén proporcionan contas XMPP.</string>
<string name="server_select_text">XMPP é unha rede de mensaxería independente do provedor. Podes utilizar este cliente con calquera provedor XMPP da túa elección.\nMais para a tua conveniencia fixemos que fose doado crear unha conta en conversations.im¹; un provedor especialmente axeitado para utilizar con Conversations.</string>
- <string name="magic_create_text_on_x">Convidáronte a %1$s. Guiarémoste no proceso para crear unha conta.\nAo escoller %1$s como provedor poderás comunicarte con usuarias de outros provedores cando lles deas o teu enderezo XMPP completo.</string>
- <string name="magic_create_text_fixed">Convidáronte a %1$s. Escollemos un nome de usuaria por ti. Guiarémoste no proceso de crear unha conta.\nPoderás comunicarte con usuarias de outros provedores cando lles digas o teu enderezo XMPP completo.</string>
+ <string name="magic_create_text_on_x">Convidáronte a %1$s. Guiarémoste no proceso para crear unha conta.\nAo elexir %1$s como provedor poderás comunicarte con usuarias doutros provedores cando lles deas o teu enderezo XMPP completo.</string>
+ <string name="magic_create_text_fixed">Convidáronte a %1$s. Xa eleximos un nome de usuaria para ti. Guiarémoste no proceso de crear unha conta.\nPoderás comunicarte con usuarias doutros provedores cando lles digas o teu enderezo XMPP completo.</string>
<string name="your_server_invitation">O convite do teu servidor</string>
<string name="improperly_formatted_provisioning">Código de aprovisionamento con formato non válido</string>
- <string name="tap_share_button_send_invite">Toca no botón compartir para convidar ó teu contacto a %1$s.</string>
+ <string name="tap_share_button_send_invite">Toca no botón compartir para convidar ao teu contacto a %1$s.</string>
<string name="if_contact_is_nearby_use_qr">Se o contacto está preto de ti, pode escanear o código inferior para aceptar o teu convite.</string>
<string name="easy_invite_share_text">Únete a %1$s e conversa conmigo: %2$s</string>
<string name="share_invite_with">Enviar convite a...</string>
A src/free/java/eu/siacs/conversations/services/EmojiInitializationService.java => src/free/java/eu/siacs/conversations/services/EmojiInitializationService.java +14 -0
@@ 0,0 1,14 @@
+package eu.siacs.conversations.services;
+
+import android.content.Context;
+
+import androidx.emoji2.bundled.BundledEmojiCompatConfig;
+import androidx.emoji2.text.EmojiCompat;
+
+public class EmojiInitializationService {
+
+ public static void execute(final Context context) {
+ EmojiCompat.init(new BundledEmojiCompatConfig(context).setReplaceAll(true));
+ }
+
+}
D src/freeCompat/java/eu/siacs/conversations/ui/service/EmojiService.java => src/freeCompat/java/eu/siacs/conversations/ui/service/EmojiService.java +0 -27
@@ 1,27 0,0 @@
-package eu.siacs.conversations.ui.service;
-
-import android.content.Context;
-import android.os.Build;
-import androidx.emoji.text.EmojiCompat;
-import androidx.emoji.text.FontRequestEmojiCompatConfig;
-import androidx.emoji.bundled.BundledEmojiCompatConfig;
-
-public class EmojiService {
-
- private final Context context;
-
- public EmojiService(Context context) {
- this.context = context;
- }
-
- public void init() {
- BundledEmojiCompatConfig config = new BundledEmojiCompatConfig(context);
- //On recent Androids we assume to have the latest emojis
- //there are some annoying bugs with emoji compat that make it a safer choice not to use it when possible
- // a) the text preview has annoying glitches when the cut of text contains emojis (the emoji will be half visible)
- // b) can trigger a hardware rendering bug https://issuetracker.google.com/issues/67102093
- config.setReplaceAll(Build.VERSION.SDK_INT < Build.VERSION_CODES.O);
- EmojiCompat.init(config);
- }
-
-}>
\ No newline at end of file
M src/main/AndroidManifest.xml => src/main/AndroidManifest.xml +5 -0
@@ 50,6 50,10 @@
android:name="android.hardware.microphone"
android:required="false" />
+ <queries>
+ <package android:name="org.sufficientlysecure.keychain"/>
+ </queries>
+
<application
android:allowBackup="true"
@@ 61,6 65,7 @@
android:largeHeap="true"
android:networkSecurityConfig="@xml/network_security_configuration"
android:requestLegacyExternalStorage="true"
+ android:preserveLegacyExternalStorage="true"
android:theme="@style/ConversationsTheme"
tools:replace="android:label"
tools:targetApi="q">
M src/main/java/eu/siacs/conversations/crypto/PgpDecryptionService.java => src/main/java/eu/siacs/conversations/crypto/PgpDecryptionService.java +5 -7
@@ 9,6 9,7 @@ import org.openintents.openpgp.util.OpenPgpApi;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
@@ 147,9 148,6 @@ public class PgpDecryptionService {
try {
os.flush();
final String body = os.toString();
- if (body == null) {
- throw new IOException("body was null");
- }
message.setBody(body);
message.setEncryption(Message.ENCRYPTION_DECRYPTED);
final HttpConnectionManager manager = mXmppConnectionService.getHttpConnectionManager();
@@ 194,9 192,9 @@ public class PgpDecryptionService {
String originalExtension = originalFilename == null ? null : MimeUtils.extractRelevantExtension(originalFilename);
if (originalExtension != null && MimeUtils.extractRelevantExtension(outputFile.getName()) == null) {
Log.d(Config.LOGTAG,"detected original filename during pgp decryption");
- String mime = MimeUtils.guessMimeTypeFromExtension(originalExtension);
- String path = outputFile.getName()+"."+originalExtension;
- DownloadableFile fixedFile = mXmppConnectionService.getFileBackend().getFileForPath(path,mime);
+ final String mime = MimeUtils.guessMimeTypeFromExtension(originalExtension);
+ final String filename = outputFile.getName()+"."+originalExtension;
+ final File fixedFile = mXmppConnectionService.getFileBackend().getStorageLocation(filename,mime);
if (fixedFile.getParentFile().mkdirs()) {
Log.d(Config.LOGTAG,"created parent directories for "+fixedFile.getAbsolutePath());
}
@@ 205,7 203,7 @@ public class PgpDecryptionService {
}
if (outputFile.renameTo(fixedFile)) {
Log.d(Config.LOGTAG, "renamed " + outputFile.getAbsolutePath() + " to " + fixedFile.getAbsolutePath());
- message.setRelativeFilePath(path);
+ message.setRelativeFilePath(fixedFile.getAbsolutePath());
}
}
final String url = message.getFileParams().url;
M src/main/java/eu/siacs/conversations/entities/DownloadableFile.java => src/main/java/eu/siacs/conversations/entities/DownloadableFile.java +4 -0
@@ 16,6 16,10 @@ public class DownloadableFile extends File {
private byte[] aeskey;
private byte[] iv;
+ public DownloadableFile(final File parent, final String file) {
+ super(parent, file);
+ }
+
public DownloadableFile(String path) {
super(path);
}
M src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java => src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java +7 -9
@@ 96,11 96,8 @@ public class HttpDownloadConnection implements Transferable {
this.message.setEncryption(Message.ENCRYPTION_NONE);
}
final String ext = extension.getExtension();
- if (ext != null) {
- message.setRelativeFilePath(String.format("%s.%s", message.getUuid(), ext));
- } else if (Strings.isNullOrEmpty(message.getRelativeFilePath())) {
- message.setRelativeFilePath(message.getUuid());
- }
+ final String filename = Strings.isNullOrEmpty(ext) ? message.getUuid() : String.format("%s.%s", message.getUuid(), ext);
+ mXmppConnectionService.getFileBackend().setupRelativeFilePath(message, filename);
setupFile();
if (this.message.getEncryption() == Message.ENCRYPTION_AXOLOTL && this.file.getKey() == null) {
this.message.setEncryption(Message.ENCRYPTION_NONE);
@@ 122,7 119,7 @@ public class HttpDownloadConnection implements Transferable {
private void setupFile() {
final String reference = mUrl.fragment();
if (reference != null && AesGcmURL.IV_KEY.matcher(reference).matches()) {
- this.file = new DownloadableFile(mXmppConnectionService.getCacheDir().getAbsolutePath() + "/" + message.getUuid());
+ this.file = new DownloadableFile(mXmppConnectionService.getCacheDir(), message.getUuid());
this.file.setKeyAndIv(CryptoHelper.hexToBytes(reference));
Log.d(Config.LOGTAG, "create temporary OMEMO encrypted file: " + this.file.getAbsolutePath() + "(" + message.getMimeType() + ")");
} else {
@@ 326,7 323,7 @@ public class HttpDownloadConnection implements Transferable {
if (Strings.isNullOrEmpty(extension.getExtension()) && contentType != null) {
final String fileExtension = MimeUtils.guessExtensionFromMimeType(contentType);
if (fileExtension != null) {
- message.setRelativeFilePath(String.format("%s.%s", message.getUuid(), fileExtension));
+ mXmppConnectionService.getFileBackend().setupRelativeFilePath(message, String.format("%s.%s", message.getUuid(), fileExtension), contentType);
Log.d(Config.LOGTAG, "rewriting name after not finding extension in url but in content type");
setupFile();
}
@@ 419,8 416,9 @@ public class HttpDownloadConnection implements Transferable {
Log.d(Config.LOGTAG, "content-length reported on GET (" + size + ") did not match Content-Length reported on HEAD (" + expected + ")");
}
file.getParentFile().mkdirs();
+ Log.d(Config.LOGTAG,"creating file: "+file.getAbsolutePath());
if (!file.exists() && !file.createNewFile()) {
- throw new FileWriterException();
+ throw new FileWriterException(file);
}
outputStream = AbstractConnectionManager.createOutputStream(file, false, false);
}
@@ 431,7 429,7 @@ public class HttpDownloadConnection implements Transferable {
try {
outputStream.write(buffer, 0, count);
} catch (IOException e) {
- throw new FileWriterException();
+ throw new FileWriterException(file);
}
updateProgress(Math.round(((double) transmitted / expected) * 100));
}
M src/main/java/eu/siacs/conversations/persistance/FileBackend.java => src/main/java/eu/siacs/conversations/persistance/FileBackend.java +356 -227
@@ 1,6 1,5 @@
package eu.siacs.conversations.persistance;
-import android.annotation.TargetApi;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
@@ 33,6 32,7 @@ import androidx.annotation.StringRes;
import androidx.core.content.FileProvider;
import androidx.exifinterface.media.ExifInterface;
+import com.google.common.base.Strings;
import com.google.common.io.ByteStreams;
import java.io.ByteArrayOutputStream;
@@ 63,7 63,7 @@ import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.services.AttachFileToConversationRunnable;
import eu.siacs.conversations.services.XmppConnectionService;
-import eu.siacs.conversations.ui.RecordingActivity;
+import eu.siacs.conversations.ui.adapter.MediaAdapter;
import eu.siacs.conversations.ui.util.Attachment;
import eu.siacs.conversations.utils.Compatibility;
import eu.siacs.conversations.utils.CryptoHelper;
@@ 76,7 76,8 @@ public class FileBackend {
private static final Object THUMBNAIL_LOCK = new Object();
- private static final SimpleDateFormat IMAGE_DATE_FORMAT = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US);
+ private static final SimpleDateFormat IMAGE_DATE_FORMAT =
+ new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US);
private static final String FILE_PROVIDER = ".files";
private static final float IGNORE_PADDING = 0.15f;
@@ 86,19 87,6 @@ public class FileBackend {
this.mXmppConnectionService = service;
}
- private static boolean isInDirectoryThatShouldNotBeScanned(Context context, File file) {
- return isInDirectoryThatShouldNotBeScanned(context, file.getAbsolutePath());
- }
-
- public static boolean isInDirectoryThatShouldNotBeScanned(Context context, String path) {
- for (String type : new String[]{RecordingActivity.STORAGE_DIRECTORY_TYPE_NAME, "Files"}) {
- if (path.startsWith(getConversationsDirectory(context, type))) {
- return true;
- }
- }
- return false;
- }
-
public static long getFileSize(Context context, Uri uri) {
try {
final Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
@@ 114,11 102,14 @@ public class FileBackend {
}
}
- public static boolean allFilesUnderSize(Context context, List<Attachment> attachments, long max) {
- final boolean compressVideo = !AttachFileToConversationRunnable.getVideoCompression(context).equals("uncompressed");
+ public static boolean allFilesUnderSize(
+ Context context, List<Attachment> attachments, long max) {
+ final boolean compressVideo =
+ !AttachFileToConversationRunnable.getVideoCompression(context)
+ .equals("uncompressed");
if (max <= 0) {
Log.d(Config.LOGTAG, "server did not report max file size for http upload");
- return true; //exception to be compatible with HTTP Upload < v0.2
+ return true; // exception to be compatible with HTTP Upload < v0.2
}
for (Attachment attachment : attachments) {
if (attachment.getType() != Attachment.Type.FILE) {
@@ 127,41 118,42 @@ public class FileBackend {
String mime = attachment.getMime();
if (mime != null && mime.startsWith("video/") && compressVideo) {
try {
- Dimensions dimensions = FileBackend.getVideoDimensions(context, attachment.getUri());
+ Dimensions dimensions =
+ FileBackend.getVideoDimensions(context, attachment.getUri());
if (dimensions.getMin() > 720) {
- Log.d(Config.LOGTAG, "do not consider video file with min width larger than 720 for size check");
+ Log.d(
+ Config.LOGTAG,
+ "do not consider video file with min width larger than 720 for size check");
continue;
}
} catch (NotAVideoFile notAVideoFile) {
- //ignore and fall through
+ // ignore and fall through
}
}
if (FileBackend.getFileSize(context, attachment.getUri()) > max) {
- Log.d(Config.LOGTAG, "not all files are under " + max + " bytes. suggesting falling back to jingle");
+ Log.d(
+ Config.LOGTAG,
+ "not all files are under "
+ + max
+ + " bytes. suggesting falling back to jingle");
return false;
}
}
return true;
}
- public static String getConversationsDirectory(Context context, final String type) {
- if (Config.ONLY_INTERNAL_STORAGE) {
- return context.getFilesDir().getAbsolutePath() + "/" + type + "/";
- } else {
- return getAppMediaDirectory(context) + context.getString(R.string.app_name) + " " + type + "/";
- }
- }
-
- public static String getAppMediaDirectory(Context context) {
- return Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + context.getString(R.string.app_name) + "/Media/";
+ public static File getBackupDirectory(final Context context) {
+ final File conversationsDownloadDirectory =
+ new File(
+ Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOWNLOADS),
+ context.getString(R.string.app_name));
+ return new File(conversationsDownloadDirectory, "Backup");
}
- public static String getBackupDirectory(Context context) {
- return getBackupDirectory(context.getString(R.string.app_name));
- }
-
- public static String getBackupDirectory(String app) {
- return Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + app + "/Backup/";
+ public static File getLegacyBackupDirectory(final String app) {
+ final File appDirectory = new File(Environment.getExternalStorageDirectory(), app);
+ return new File(appDirectory, "Backup");
}
private static Bitmap rotate(final Bitmap bitmap, final int degree) {
@@ 180,7 172,8 @@ public class FileBackend {
}
public static boolean isPathBlacklisted(String path) {
- final String androidDataPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/";
+ final String androidDataPath =
+ Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/";
return path.startsWith(androidDataPath);
}
@@ 192,10 185,6 @@ public class FileBackend {
return paint;
}
- private static String getTakePhotoPath() {
- return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + "/Camera/";
- }
-
public static Uri getUriForUri(Context context, Uri uri) {
if ("file".equals(uri.getScheme())) {
return getUriForFile(context, new File(uri.getPath()));
@@ 246,7 235,6 @@ public class FileBackend {
return calcSampleSize(options, size);
}
-
private static int calcSampleSize(BitmapFactory.Options options, int size) {
int height = options.outHeight;
int width = options.outWidth;
@@ 256,8 244,7 @@ public class FileBackend {
int halfHeight = height / 2;
int halfWidth = width / 2;
- while ((halfHeight / inSampleSize) > size
- && (halfWidth / inSampleSize) > size) {
+ while ((halfHeight / inSampleSize) > size && (halfWidth / inSampleSize) > size) {
inSampleSize *= 2;
}
}
@@ 274,7 261,8 @@ public class FileBackend {
return getVideoDimensions(mediaMetadataRetriever);
}
- private static Dimensions getVideoDimensionsOfFrame(MediaMetadataRetriever mediaMetadataRetriever) {
+ private static Dimensions getVideoDimensionsOfFrame(
+ MediaMetadataRetriever mediaMetadataRetriever) {
Bitmap bitmap = null;
try {
bitmap = mediaMetadataRetriever.getFrameAtTime();
@@ 288,8 276,10 @@ public class FileBackend {
}
}
- private static Dimensions getVideoDimensions(MediaMetadataRetriever metadataRetriever) throws NotAVideoFile {
- String hasVideo = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO);
+ private static Dimensions getVideoDimensions(MediaMetadataRetriever metadataRetriever)
+ throws NotAVideoFile {
+ String hasVideo =
+ metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO);
if (hasVideo == null) {
throw new NotAVideoFile();
}
@@ 301,14 291,18 @@ public class FileBackend {
boolean rotated = rotation == 90 || rotation == 270;
int height;
try {
- String h = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
+ String h =
+ metadataRetriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
height = Integer.parseInt(h);
} catch (Exception e) {
height = -1;
}
int width;
try {
- String w = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
+ String w =
+ metadataRetriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
width = Integer.parseInt(w);
} catch (Exception e) {
width = -1;
@@ 319,7 313,9 @@ public class FileBackend {
}
private static int extractRotationFromMediaRetriever(MediaMetadataRetriever metadataRetriever) {
- String r = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
+ String r =
+ metadataRetriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
try {
return Integer.parseInt(r);
} catch (Exception e) {
@@ 357,36 353,20 @@ public class FileBackend {
}
}
- public static boolean weOwnFile(Context context, Uri uri) {
+ public static boolean weOwnFile(final Uri uri) {
if (uri == null || !ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
return false;
- } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
- return fileIsInFilesDir(context, uri);
} else {
return weOwnFileLollipop(uri);
}
}
- /**
- * This is more than hacky but probably way better than doing nothing
- * Further 'optimizations' might contain to get the parents of CacheDir and NoBackupDir
- * and check against those as well
- */
- private static boolean fileIsInFilesDir(Context context, Uri uri) {
- try {
- final String haystack = context.getFilesDir().getParentFile().getCanonicalPath();
- final String needle = new File(uri.getPath()).getCanonicalPath();
- return needle.startsWith(haystack);
- } catch (IOException e) {
- return false;
- }
- }
-
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
- private static boolean weOwnFileLollipop(Uri uri) {
+ private static boolean weOwnFileLollipop(final Uri uri) {
try {
File file = new File(uri.getPath());
- FileDescriptor fd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY).getFileDescriptor();
+ FileDescriptor fd =
+ ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY)
+ .getFileDescriptor();
StructStat st = Os.fstat(fd);
return st.st_uid == android.os.Process.myUid();
} catch (FileNotFoundException e) {
@@ 400,18 380,22 @@ public class FileBackend {
final String filePath = file.getAbsolutePath();
final Cursor cursor;
try {
- cursor = context.getContentResolver().query(
- MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
- new String[]{MediaStore.Images.Media._ID},
- MediaStore.Images.Media.DATA + "=? ",
- new String[]{filePath}, null);
+ cursor =
+ context.getContentResolver()
+ .query(
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+ new String[] {MediaStore.Images.Media._ID},
+ MediaStore.Images.Media.DATA + "=? ",
+ new String[] {filePath},
+ null);
} catch (SecurityException e) {
return null;
}
if (cursor != null && cursor.moveToFirst()) {
final int id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID));
cursor.close();
- return Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, String.valueOf(id));
+ return Uri.withAppendedPath(
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI, String.valueOf(id));
} else {
return null;
}
@@ 433,15 417,30 @@ public class FileBackend {
final String mime = attachment.getMime();
if ("application/pdf".equals(mime) && Compatibility.runsTwentyOne()) {
bitmap = cropCenterSquarePdf(attachment.getUri(), size);
- drawOverlay(bitmap, paintOverlayBlackPdf(bitmap) ? R.drawable.open_pdf_black : R.drawable.open_pdf_white, 0.75f);
+ drawOverlay(
+ bitmap,
+ paintOverlayBlackPdf(bitmap)
+ ? R.drawable.open_pdf_black
+ : R.drawable.open_pdf_white,
+ 0.75f);
} else if (mime != null && mime.startsWith("video/")) {
bitmap = cropCenterSquareVideo(attachment.getUri(), size);
- drawOverlay(bitmap, paintOverlayBlack(bitmap) ? R.drawable.play_video_black : R.drawable.play_video_white, 0.75f);
+ drawOverlay(
+ bitmap,
+ paintOverlayBlack(bitmap)
+ ? R.drawable.play_video_black
+ : R.drawable.play_video_white,
+ 0.75f);
} else {
bitmap = cropCenterSquare(attachment.getUri(), size);
if (bitmap != null && "image/gif".equals(mime)) {
Bitmap withGifOverlay = bitmap.copy(Bitmap.Config.ARGB_8888, true);
- drawOverlay(withGifOverlay, paintOverlayBlack(withGifOverlay) ? R.drawable.play_gif_black : R.drawable.play_gif_white, 1.0f);
+ drawOverlay(
+ withGifOverlay,
+ paintOverlayBlack(withGifOverlay)
+ ? R.drawable.play_gif_black
+ : R.drawable.play_gif_white,
+ 1.0f);
bitmap.recycle();
bitmap = withGifOverlay;
}
@@ 452,53 451,31 @@ public class FileBackend {
return bitmap;
}
- private void createNoMedia(File diretory) {
- final File noMedia = new File(diretory, ".nomedia");
- if (!noMedia.exists()) {
- try {
- if (!noMedia.createNewFile()) {
- Log.d(Config.LOGTAG, "created nomedia file " + noMedia.getAbsolutePath());
- }
- } catch (Exception e) {
- Log.d(Config.LOGTAG, "could not create nomedia file");
- }
- }
- }
-
public void updateMediaScanner(File file) {
updateMediaScanner(file, null);
}
public void updateMediaScanner(File file, final Runnable callback) {
- if (!isInDirectoryThatShouldNotBeScanned(mXmppConnectionService, file)) {
- MediaScannerConnection.scanFile(mXmppConnectionService, new String[]{file.getAbsolutePath()}, null, new MediaScannerConnection.MediaScannerConnectionClient() {
- @Override
- public void onMediaScannerConnected() {
-
- }
-
- @Override
- public void onScanCompleted(String path, Uri uri) {
- if (callback != null && file.getAbsolutePath().equals(path)) {
- callback.run();
- } else {
- Log.d(Config.LOGTAG, "media scanner scanned wrong file");
- if (callback != null) {
+ MediaScannerConnection.scanFile(
+ mXmppConnectionService,
+ new String[] {file.getAbsolutePath()},
+ null,
+ new MediaScannerConnection.MediaScannerConnectionClient() {
+ @Override
+ public void onMediaScannerConnected() {}
+
+ @Override
+ public void onScanCompleted(String path, Uri uri) {
+ if (callback != null && file.getAbsolutePath().equals(path)) {
callback.run();
+ } else {
+ Log.d(Config.LOGTAG, "media scanner scanned wrong file");
+ if (callback != null) {
+ callback.run();
+ }
}
}
- }
- });
- return;
- /*Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
- intent.setData(Uri.fromFile(file));
- mXmppConnectionService.sendBroadcast(intent);*/
- } else if (file.getAbsolutePath().startsWith(getAppMediaDirectory(mXmppConnectionService))) {
- createNoMedia(file.getParentFile());
- }
- if (callback != null) {
- callback.run();
- }
+ });
}
public boolean deleteFile(Message message) {
@@ 515,25 492,30 @@ public class FileBackend {
return getFile(message, true);
}
-
public DownloadableFile getFileForPath(String path) {
- return getFileForPath(path, MimeUtils.guessMimeTypeFromExtension(MimeUtils.extractRelevantExtension(path)));
+ return getFileForPath(
+ path,
+ MimeUtils.guessMimeTypeFromExtension(MimeUtils.extractRelevantExtension(path)));
}
- public DownloadableFile getFileForPath(String path, String mime) {
- final DownloadableFile file;
+ private DownloadableFile getFileForPath(final String path, final String mime) {
if (path.startsWith("/")) {
- file = new DownloadableFile(path);
+ return new DownloadableFile(path);
} else {
- if (mime != null && mime.startsWith("image/")) {
- file = new DownloadableFile(getConversationsDirectory("Images") + path);
- } else if (mime != null && mime.startsWith("video/")) {
- file = new DownloadableFile(getConversationsDirectory("Videos") + path);
- } else {
- file = new DownloadableFile(getConversationsDirectory("Files") + path);
- }
+ return getLegacyFileForFilename(path, mime);
+ }
+ }
+
+ public DownloadableFile getLegacyFileForFilename(final String filename, final String mime) {
+ if (Strings.isNullOrEmpty(mime)) {
+ return new DownloadableFile(getLegacyStorageLocation("Files"), filename);
+ } else if (mime.startsWith("image/")) {
+ return new DownloadableFile(getLegacyStorageLocation("Images"), filename);
+ } else if (mime.startsWith("video/")) {
+ return new DownloadableFile(getLegacyStorageLocation("Videos"), filename);
+ } else {
+ return new DownloadableFile(getLegacyStorageLocation("Files"), filename);
}
- return file;
}
public boolean isInternalFile(final File file) {
@@ 542,33 524,50 @@ public class FileBackend {
}
public DownloadableFile getFile(Message message, boolean decrypted) {
- final boolean encrypted = !decrypted
- && (message.getEncryption() == Message.ENCRYPTION_PGP
- || message.getEncryption() == Message.ENCRYPTION_DECRYPTED);
+ final boolean encrypted =
+ !decrypted
+ && (message.getEncryption() == Message.ENCRYPTION_PGP
+ || message.getEncryption() == Message.ENCRYPTION_DECRYPTED);
String path = message.getRelativeFilePath();
if (path == null) {
path = message.getUuid();
}
final DownloadableFile file = getFileForPath(path, message.getMimeType());
if (encrypted) {
- return new DownloadableFile(getConversationsDirectory("Files") + file.getName() + ".pgp");
+ return new DownloadableFile(
+ mXmppConnectionService.getCacheDir(),
+ String.format("%s.%s", file.getName(), "pgp"));
} else {
return file;
}
}
public List<Attachment> convertToAttachments(List<DatabaseBackend.FilePath> relativeFilePaths) {
- List<Attachment> attachments = new ArrayList<>();
- for (DatabaseBackend.FilePath relativeFilePath : relativeFilePaths) {
- final String mime = MimeUtils.guessMimeTypeFromExtension(MimeUtils.extractRelevantExtension(relativeFilePath.path));
+ final List<Attachment> attachments = new ArrayList<>();
+ for (final DatabaseBackend.FilePath relativeFilePath : relativeFilePaths) {
+ final String mime =
+ MimeUtils.guessMimeTypeFromExtension(
+ MimeUtils.extractRelevantExtension(relativeFilePath.path));
final File file = getFileForPath(relativeFilePath.path, mime);
attachments.add(Attachment.of(relativeFilePath.uuid, file, mime));
}
return attachments;
}
- private String getConversationsDirectory(final String type) {
- return getConversationsDirectory(mXmppConnectionService, type);
+ private File getLegacyStorageLocation(final String type) {
+ if (Config.ONLY_INTERNAL_STORAGE) {
+ return new File(mXmppConnectionService.getFilesDir(), type);
+ } else {
+ final File appDirectory =
+ new File(
+ Environment.getExternalStorageDirectory(),
+ mXmppConnectionService.getString(R.string.app_name));
+ final File appMediaDirectory = new File(appDirectory, "Media");
+ final String locationName =
+ String.format(
+ "%s %s", mXmppConnectionService.getString(R.string.app_name), type);
+ return new File(appMediaDirectory, locationName);
+ }
}
private Bitmap resize(final Bitmap originalBitmap, int size) throws IOException {
@@ 586,7 585,8 @@ public class FileBackend {
scalledW = size;
scalledH = Math.max((int) (h / ((double) w / size)), 1);
}
- final Bitmap result = Bitmap.createScaledBitmap(originalBitmap, scalledW, scalledH, true);
+ final Bitmap result =
+ Bitmap.createScaledBitmap(originalBitmap, scalledW, scalledH, true);
if (!originalBitmap.isRecycled()) {
originalBitmap.recycle();
}
@@ 603,19 603,26 @@ public class FileBackend {
}
final File file = new File(path);
long size = file.length();
- if (size == 0 || size >= mXmppConnectionService.getResources().getInteger(R.integer.auto_accept_filesize)) {
+ if (size == 0
+ || size
+ >= mXmppConnectionService
+ .getResources()
+ .getInteger(R.integer.auto_accept_filesize)) {
return false;
}
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
try {
- final InputStream inputStream = mXmppConnectionService.getContentResolver().openInputStream(uri);
+ final InputStream inputStream =
+ mXmppConnectionService.getContentResolver().openInputStream(uri);
BitmapFactory.decodeStream(inputStream, null, options);
close(inputStream);
if (options.outMimeType == null || options.outHeight <= 0 || options.outWidth <= 0) {
return false;
}
- return (options.outWidth <= Config.IMAGE_SIZE && options.outHeight <= Config.IMAGE_SIZE && options.outMimeType.contains(Config.IMAGE_FORMAT.name().toLowerCase()));
+ return (options.outWidth <= Config.IMAGE_SIZE
+ && options.outHeight <= Config.IMAGE_SIZE
+ && options.outMimeType.contains(Config.IMAGE_FORMAT.name().toLowerCase()));
} catch (FileNotFoundException e) {
Log.d(Config.LOGTAG, "unable to get image dimensions", e);
return false;
@@ 627,7 634,9 @@ public class FileBackend {
}
private void copyFileToPrivateStorage(File file, Uri uri) throws FileCopyException {
- Log.d(Config.LOGTAG, "copy file (" + uri.toString() + ") to private storage " + file.getAbsolutePath());
+ Log.d(
+ Config.LOGTAG,
+ "copy file (" + uri.toString() + ") to private storage " + file.getAbsolutePath());
file.getParentFile().mkdirs();
try {
file.createNewFile();
@@ 635,19 644,20 @@ public class FileBackend {
throw new FileCopyException(R.string.error_unable_to_create_temporary_file);
}
try (final OutputStream os = new FileOutputStream(file);
- final InputStream is = mXmppConnectionService.getContentResolver().openInputStream(uri)) {
+ final InputStream is =
+ mXmppConnectionService.getContentResolver().openInputStream(uri)) {
if (is == null) {
throw new FileCopyException(R.string.error_file_not_found);
}
try {
ByteStreams.copy(is, os);
} catch (IOException e) {
- throw new FileWriterException();
+ throw new FileWriterException(file);
}
try {
os.flush();
} catch (IOException e) {
- throw new FileWriterException();
+ throw new FileWriterException(file);
}
} catch (final FileNotFoundException e) {
cleanup(file);
@@ 664,7 674,8 @@ public class FileBackend {
}
}
- public void copyFileToPrivateStorage(Message message, Uri uri, String type) throws FileCopyException {
+ public void copyFileToPrivateStorage(Message message, Uri uri, String type)
+ throws FileCopyException {
String mime = MimeUtils.guessMimeTypeFromUriAndMime(mXmppConnectionService, uri, type);
Log.d(Config.LOGTAG, "copy " + uri.toString() + " to private storage (mime=" + mime + ")");
String extension = MimeUtils.guessExtensionFromMimeType(mime);
@@ 675,29 686,22 @@ public class FileBackend {
if ("ogg".equals(extension) && type != null && type.startsWith("audio/")) {
extension = "oga";
}
- message.setRelativeFilePath(message.getUuid() + "." + extension);
+ setupRelativeFilePath(message, String.format("%s.%s", message.getUuid(), extension));
copyFileToPrivateStorage(mXmppConnectionService.getFileBackend().getFile(message), uri);
}
- private String getExtensionFromUri(Uri uri) {
- String[] projection = {MediaStore.MediaColumns.DATA};
+ private String getExtensionFromUri(final Uri uri) {
+ final String[] projection = {MediaStore.MediaColumns.DATA};
String filename = null;
- Cursor cursor;
- try {
- cursor = mXmppConnectionService.getContentResolver().query(uri, projection, null, null, null);
- } catch (IllegalArgumentException e) {
- cursor = null;
- }
- if (cursor != null) {
- try {
- if (cursor.moveToFirst()) {
- filename = cursor.getString(0);
- }
- } catch (Exception e) {
- filename = null;
- } finally {
- cursor.close();
+ try (final Cursor cursor =
+ mXmppConnectionService
+ .getContentResolver()
+ .query(uri, projection, null, null, null)) {
+ if (cursor != null && cursor.moveToFirst()) {
+ filename = cursor.getString(0);
}
+ } catch (final SecurityException | IllegalArgumentException e) {
+ filename = null;
}
if (filename == null) {
final List<String> segments = uri.getPathSegments();
@@ 705,11 709,12 @@ public class FileBackend {
filename = segments.get(segments.size() - 1);
}
}
- int pos = filename == null ? -1 : filename.lastIndexOf('.');
+ final int pos = filename == null ? -1 : filename.lastIndexOf('.');
return pos > 0 ? filename.substring(pos + 1) : null;
}
- private void copyImageToPrivateStorage(File file, Uri image, int sampleSize) throws FileCopyException, ImageCompressionException {
+ private void copyImageToPrivateStorage(File file, Uri image, int sampleSize)
+ throws FileCopyException, ImageCompressionException {
final File parent = file.getParentFile();
if (parent != null && parent.mkdirs()) {
Log.d(Config.LOGTAG, "created parent directory");
@@ 743,7 748,10 @@ public class FileBackend {
scaledBitmap = rotate(scaledBitmap, rotation);
boolean targetSizeReached = false;
int quality = Config.IMAGE_QUALITY;
- final int imageMaxSize = mXmppConnectionService.getResources().getInteger(R.integer.auto_accept_filesize);
+ final int imageMaxSize =
+ mXmppConnectionService
+ .getResources()
+ .getInteger(R.integer.auto_accept_filesize);
while (!targetSizeReached) {
os = new FileOutputStream(file);
Log.d(Config.LOGTAG, "compressing image with quality " + quality);
@@ 788,32 796,79 @@ public class FileBackend {
}
}
- public void copyImageToPrivateStorage(File file, Uri image) throws FileCopyException, ImageCompressionException {
- Log.d(Config.LOGTAG, "copy image (" + image.toString() + ") to private storage " + file.getAbsolutePath());
+ public void copyImageToPrivateStorage(File file, Uri image)
+ throws FileCopyException, ImageCompressionException {
+ Log.d(
+ Config.LOGTAG,
+ "copy image ("
+ + image.toString()
+ + ") to private storage "
+ + file.getAbsolutePath());
copyImageToPrivateStorage(file, image, 0);
}
- public void copyImageToPrivateStorage(Message message, Uri image) throws FileCopyException, ImageCompressionException {
+ public void copyImageToPrivateStorage(Message message, Uri image)
+ throws FileCopyException, ImageCompressionException {
+ final String filename;
switch (Config.IMAGE_FORMAT) {
case JPEG:
- message.setRelativeFilePath(message.getUuid() + ".jpg");
+ filename = String.format("%s.%s", message.getUuid(), "jpg");
break;
case PNG:
- message.setRelativeFilePath(message.getUuid() + ".png");
+ filename = String.format("%s.%s", message.getUuid(), "png");
break;
case WEBP:
- message.setRelativeFilePath(message.getUuid() + ".webp");
+ filename = String.format("%s.%s", message.getUuid(), "webp");
break;
+ default:
+ throw new IllegalStateException("Unknown image format");
}
+ setupRelativeFilePath(message, filename);
copyImageToPrivateStorage(getFile(message), image);
updateFileParams(message);
}
+ public void setupRelativeFilePath(final Message message, final String filename) {
+ final String extension = MimeUtils.extractRelevantExtension(filename);
+ final String mime = MimeUtils.guessMimeTypeFromExtension(extension);
+ setupRelativeFilePath(message, filename, mime);
+ }
+
+ public File getStorageLocation(final String filename, final String mime) {
+ final File parentDirectory;
+ if (Strings.isNullOrEmpty(mime)) {
+ parentDirectory =
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
+ } else if (mime.startsWith("image/")) {
+ parentDirectory =
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
+ } else if (mime.startsWith("video/")) {
+ parentDirectory =
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES);
+ } else if (MediaAdapter.DOCUMENT_MIMES.contains(mime)) {
+ parentDirectory =
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
+ } else {
+ parentDirectory =
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
+ }
+ final File appDirectory =
+ new File(parentDirectory, mXmppConnectionService.getString(R.string.app_name));
+ return new File(appDirectory, filename);
+ }
+
+ public void setupRelativeFilePath(
+ final Message message, final String filename, final String mime) {
+ final File file = getStorageLocation(filename, mime);
+ message.setRelativeFilePath(file.getAbsolutePath());
+ }
+
public boolean unusualBounds(final Uri image) {
try {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
- final InputStream inputStream = mXmppConnectionService.getContentResolver().openInputStream(image);
+ final InputStream inputStream =
+ mXmppConnectionService.getContentResolver().openInputStream(image);
BitmapFactory.decodeStream(inputStream, null, options);
close(inputStream);
float ratio = (float) options.outHeight / options.outWidth;
@@ 833,7 888,8 @@ public class FileBackend {
}
private int getRotation(final Uri image) {
- try (final InputStream is = mXmppConnectionService.getContentResolver().openInputStream(image)) {
+ try (final InputStream is =
+ mXmppConnectionService.getContentResolver().openInputStream(image)) {
return is == null ? 0 : getRotation(is);
} catch (final Exception e) {
return 0;
@@ 842,7 898,9 @@ public class FileBackend {
private static int getRotation(final InputStream inputStream) throws IOException {
final ExifInterface exif = new ExifInterface(inputStream);
- final int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
+ final int orientation =
+ exif.getAttributeInt(
+ ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_180:
return 180;
@@ 880,7 938,12 @@ public class FileBackend {
thumbnail = rotate(thumbnail, getRotation(file));
if (mime.equals("image/gif")) {
Bitmap withGifOverlay = thumbnail.copy(Bitmap.Config.ARGB_8888, true);
- drawOverlay(withGifOverlay, paintOverlayBlack(withGifOverlay) ? R.drawable.play_gif_black : R.drawable.play_gif_white, 1.0f);
+ drawOverlay(
+ withGifOverlay,
+ paintOverlayBlack(withGifOverlay)
+ ? R.drawable.play_gif_black
+ : R.drawable.play_gif_white,
+ 1.0f);
thumbnail.recycle();
thumbnail = withGifOverlay;
}
@@ 903,27 966,36 @@ public class FileBackend {
}
private void drawOverlay(Bitmap bitmap, int resource, float factor) {
- Bitmap overlay = BitmapFactory.decodeResource(mXmppConnectionService.getResources(), resource);
+ Bitmap overlay =
+ BitmapFactory.decodeResource(mXmppConnectionService.getResources(), resource);
Canvas canvas = new Canvas(bitmap);
float targetSize = Math.min(canvas.getWidth(), canvas.getHeight()) * factor;
- Log.d(Config.LOGTAG, "target size overlay: " + targetSize + " overlay bitmap size was " + overlay.getHeight());
+ Log.d(
+ Config.LOGTAG,
+ "target size overlay: "
+ + targetSize
+ + " overlay bitmap size was "
+ + overlay.getHeight());
float left = (canvas.getWidth() - targetSize) / 2.0f;
float top = (canvas.getHeight() - targetSize) / 2.0f;
RectF dst = new RectF(left, top, left + targetSize - 1, top + targetSize - 1);
canvas.drawBitmap(overlay, null, dst, createAntiAliasingPaint());
}
- /**
- * https://stackoverflow.com/a/3943023/210897
- */
+ /** https://stackoverflow.com/a/3943023/210897 */
private boolean paintOverlayBlack(final Bitmap bitmap) {
final int h = bitmap.getHeight();
final int w = bitmap.getWidth();
int record = 0;
for (int y = Math.round(h * IGNORE_PADDING); y < h - Math.round(h * IGNORE_PADDING); ++y) {
- for (int x = Math.round(w * IGNORE_PADDING); x < w - Math.round(w * IGNORE_PADDING); ++x) {
+ for (int x = Math.round(w * IGNORE_PADDING);
+ x < w - Math.round(w * IGNORE_PADDING);
+ ++x) {
int pixel = bitmap.getPixel(x, y);
- if ((Color.red(pixel) * 0.299 + Color.green(pixel) * 0.587 + Color.blue(pixel) * 0.114) > 186) {
+ if ((Color.red(pixel) * 0.299
+ + Color.green(pixel) * 0.587
+ + Color.blue(pixel) * 0.114)
+ > 186) {
--record;
} else {
++record;
@@ 940,7 1012,10 @@ public class FileBackend {
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
int pixel = bitmap.getPixel(x, y);
- if ((Color.red(pixel) * 0.299 + Color.green(pixel) * 0.587 + Color.blue(pixel) * 0.114) > 186) {
+ if ((Color.red(pixel) * 0.299
+ + Color.green(pixel) * 0.587
+ + Color.blue(pixel) * 0.114)
+ > 186) {
white++;
}
}
@@ 975,16 1050,27 @@ public class FileBackend {
frame = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
frame.eraseColor(0xff000000);
}
- drawOverlay(frame, paintOverlayBlack(frame) ? R.drawable.play_video_black : R.drawable.play_video_white, 0.75f);
+ drawOverlay(
+ frame,
+ paintOverlayBlack(frame)
+ ? R.drawable.play_video_black
+ : R.drawable.play_video_white,
+ 0.75f);
return frame;
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private Bitmap getPdfDocumentPreview(final File file, final int size) {
try {
- final ParcelFileDescriptor fileDescriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
+ final ParcelFileDescriptor fileDescriptor =
+ ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
final Bitmap rendered = renderPdfDocument(fileDescriptor, size, true);
- drawOverlay(rendered, paintOverlayBlackPdf(rendered) ? R.drawable.open_pdf_black : R.drawable.open_pdf_white, 0.75f);
+ drawOverlay(
+ rendered,
+ paintOverlayBlackPdf(rendered)
+ ? R.drawable.open_pdf_black
+ : R.drawable.open_pdf_white,
+ 0.75f);
return rendered;
} catch (final IOException | SecurityException e) {
Log.d(Config.LOGTAG, "unable to render PDF document preview", e);
@@ 994,11 1080,11 @@ public class FileBackend {
}
}
-
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private Bitmap cropCenterSquarePdf(final Uri uri, final int size) {
try {
- ParcelFileDescriptor fileDescriptor = mXmppConnectionService.getContentResolver().openFileDescriptor(uri, "r");
+ ParcelFileDescriptor fileDescriptor =
+ mXmppConnectionService.getContentResolver().openFileDescriptor(uri, "r");
final Bitmap bitmap = renderPdfDocument(fileDescriptor, size, false);
return cropCenterSquare(bitmap, size);
} catch (Exception e) {
@@ 1009,11 1095,15 @@ public class FileBackend {
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
- private Bitmap renderPdfDocument(ParcelFileDescriptor fileDescriptor, int targetSize, boolean fit) throws IOException {
+ private Bitmap renderPdfDocument(
+ ParcelFileDescriptor fileDescriptor, int targetSize, boolean fit) throws IOException {
final PdfRenderer pdfRenderer = new PdfRenderer(fileDescriptor);
final PdfRenderer.Page page = pdfRenderer.openPage(0);
- final Dimensions dimensions = scalePdfDimensions(new Dimensions(page.getHeight(), page.getWidth()), targetSize, fit);
- final Bitmap rendered = Bitmap.createBitmap(dimensions.width, dimensions.height, Bitmap.Config.ARGB_8888);
+ final Dimensions dimensions =
+ scalePdfDimensions(
+ new Dimensions(page.getHeight(), page.getWidth()), targetSize, fit);
+ final Bitmap rendered =
+ Bitmap.createBitmap(dimensions.width, dimensions.height, Bitmap.Config.ARGB_8888);
rendered.eraseColor(0xffffffff);
page.render(rendered, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
page.close();
@@ 1023,12 1113,19 @@ public class FileBackend {
}
public Uri getTakePhotoUri() {
- File file;
+ final String filename =
+ String.format("IMG_%s.%s", IMAGE_DATE_FORMAT.format(new Date()), "jpg");
+ final File directory;
if (Config.ONLY_INTERNAL_STORAGE) {
- file = new File(mXmppConnectionService.getCacheDir().getAbsolutePath(), "Camera/IMG_" + IMAGE_DATE_FORMAT.format(new Date()) + ".jpg");
+ directory = new File(mXmppConnectionService.getCacheDir(), "Camera");
} else {
- file = new File(getTakePhotoPath() + "IMG_" + IMAGE_DATE_FORMAT.format(new Date()) + ".jpg");
+ directory =
+ new File(
+ Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DCIM),
+ "Camera");
}
+ final File file = new File(directory, filename);
file.getParentFile().mkdirs();
return getUriForFile(mXmppConnectionService, file);
}
@@ 1036,11 1133,15 @@ public class FileBackend {
public Avatar getPepAvatar(Uri image, int size, Bitmap.CompressFormat format) {
final Avatar uncompressAvatar = getUncompressedAvatar(image);
- if (uncompressAvatar != null && uncompressAvatar.image.length() <= Config.AVATAR_CHAR_LIMIT) {
+ if (uncompressAvatar != null
+ && uncompressAvatar.image.length() <= Config.AVATAR_CHAR_LIMIT) {
return uncompressAvatar;
}
if (uncompressAvatar != null) {
- Log.d(Config.LOGTAG, "uncompressed avatar exceeded char limit by " + (uncompressAvatar.image.length() - Config.AVATAR_CHAR_LIMIT));
+ Log.d(
+ Config.LOGTAG,
+ "uncompressed avatar exceeded char limit by "
+ + (uncompressAvatar.image.length() - Config.AVATAR_CHAR_LIMIT));
}
Bitmap bm = cropCenterSquare(image, size);
@@ 1059,7 1160,9 @@ public class FileBackend {
private Avatar getUncompressedAvatar(Uri uri) {
Bitmap bitmap = null;
try {
- bitmap = BitmapFactory.decodeStream(mXmppConnectionService.getContentResolver().openInputStream(uri));
+ bitmap =
+ BitmapFactory.decodeStream(
+ mXmppConnectionService.getContentResolver().openInputStream(uri));
return getPepAvatar(bitmap, Bitmap.CompressFormat.PNG, 100);
} catch (Exception e) {
return null;
@@ 1073,18 1176,24 @@ public class FileBackend {
private Avatar getPepAvatar(Bitmap bitmap, Bitmap.CompressFormat format, int quality) {
try {
ByteArrayOutputStream mByteArrayOutputStream = new ByteArrayOutputStream();
- Base64OutputStream mBase64OutputStream = new Base64OutputStream(mByteArrayOutputStream, Base64.DEFAULT);
+ Base64OutputStream mBase64OutputStream =
+ new Base64OutputStream(mByteArrayOutputStream, Base64.DEFAULT);
MessageDigest digest = MessageDigest.getInstance("SHA-1");
- DigestOutputStream mDigestOutputStream = new DigestOutputStream(mBase64OutputStream, digest);
+ DigestOutputStream mDigestOutputStream =
+ new DigestOutputStream(mBase64OutputStream, digest);
if (!bitmap.compress(format, quality, mDigestOutputStream)) {
return null;
}
mDigestOutputStream.flush();
mDigestOutputStream.close();
long chars = mByteArrayOutputStream.size();
- if (format != Bitmap.CompressFormat.PNG && quality >= 50 && chars >= Config.AVATAR_CHAR_LIMIT) {
+ if (format != Bitmap.CompressFormat.PNG
+ && quality >= 50
+ && chars >= Config.AVATAR_CHAR_LIMIT) {
int q = quality - 2;
- Log.d(Config.LOGTAG, "avatar char length was " + chars + " reducing quality to " + q);
+ Log.d(
+ Config.LOGTAG,
+ "avatar char length was " + chars + " reducing quality to " + q);
return getPepAvatar(bitmap, format, q);
}
Log.d(Config.LOGTAG, "settled on char length " + chars + " with quality=" + quality);
@@ 1123,7 1232,8 @@ public class FileBackend {
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
is = new FileInputStream(file);
ByteArrayOutputStream mByteArrayOutputStream = new ByteArrayOutputStream();
- Base64OutputStream mBase64OutputStream = new Base64OutputStream(mByteArrayOutputStream, Base64.DEFAULT);
+ Base64OutputStream mBase64OutputStream =
+ new Base64OutputStream(mByteArrayOutputStream, Base64.DEFAULT);
MessageDigest digest = MessageDigest.getInstance("SHA-1");
DigestOutputStream os = new DigestOutputStream(mBase64OutputStream, digest);
byte[] buffer = new byte[4096];
@@ 1157,14 1267,20 @@ public class FileBackend {
file = getAvatarFile(avatar.getFilename());
avatar.size = file.length();
} else {
- file = new File(mXmppConnectionService.getCacheDir().getAbsolutePath() + "/" + UUID.randomUUID().toString());
+ file =
+ new File(
+ mXmppConnectionService.getCacheDir().getAbsolutePath()
+ + "/"
+ + UUID.randomUUID().toString());
if (file.getParentFile().mkdirs()) {
Log.d(Config.LOGTAG, "created cache directory");
}
OutputStream os = null;
try {
if (!file.createNewFile()) {
- Log.d(Config.LOGTAG, "unable to create temporary file " + file.getAbsolutePath());
+ Log.d(
+ Config.LOGTAG,
+ "unable to create temporary file " + file.getAbsolutePath());
}
os = new FileOutputStream(file);
MessageDigest digest = MessageDigest.getInstance("SHA-1");
@@ 1182,7 1298,9 @@ public class FileBackend {
}
final File avatarFile = getAvatarFile(avatar.getFilename());
if (!file.renameTo(avatarFile)) {
- Log.d(Config.LOGTAG, "unable to rename " + file.getAbsolutePath() + " to " + outputFile);
+ Log.d(
+ Config.LOGTAG,
+ "unable to rename " + file.getAbsolutePath() + " to " + outputFile);
return false;
}
} else {
@@ 1294,7 1412,7 @@ public class FileBackend {
}
return dest;
} catch (SecurityException e) {
- return null; //android 6.0 with revoked permissions for example
+ return null; // android 6.0 with revoked permissions for example
} catch (FileNotFoundException e) {
return null;
} finally {
@@ 1323,10 1441,12 @@ public class FileBackend {
return output;
}
- private int calcSampleSize(Uri image, int size) throws FileNotFoundException, SecurityException {
+ private int calcSampleSize(Uri image, int size)
+ throws FileNotFoundException, SecurityException {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
- final InputStream inputStream = mXmppConnectionService.getContentResolver().openInputStream(image);
+ final InputStream inputStream =
+ mXmppConnectionService.getContentResolver().openInputStream(image);
BitmapFactory.decodeStream(inputStream, null, options);
close(inputStream);
return calcSampleSize(options, size);
@@ 1340,7 1460,9 @@ public class FileBackend {
DownloadableFile file = getFile(message);
final String mime = file.getMimeType();
final boolean privateMessage = message.isPrivateMessage();
- final boolean image = message.getType() == Message.TYPE_IMAGE || (mime != null && mime.startsWith("image/"));
+ final boolean image =
+ message.getType() == Message.TYPE_IMAGE
+ || (mime != null && mime.startsWith("image/"));
final boolean video = mime != null && mime.startsWith("video/");
final boolean audio = mime != null && mime.startsWith("audio/");
final boolean pdf = "application/pdf".equals(mime);
@@ 1363,22 1485,29 @@ public class FileBackend {
body.append('|').append(dimensions.width).append('|').append(dimensions.height);
}
} catch (NotAVideoFile notAVideoFile) {
- Log.d(Config.LOGTAG, "file with mime type " + file.getMimeType() + " was not a video file");
- //fall threw
+ Log.d(
+ Config.LOGTAG,
+ "file with mime type " + file.getMimeType() + " was not a video file");
+ // fall threw
}
} else if (audio) {
body.append("|0|0|").append(getMediaRuntime(file));
}
message.setBody(body.toString());
message.setDeleted(false);
- message.setType(privateMessage ? Message.TYPE_PRIVATE_FILE : (image ? Message.TYPE_IMAGE : Message.TYPE_FILE));
+ message.setType(
+ privateMessage
+ ? Message.TYPE_PRIVATE_FILE
+ : (image ? Message.TYPE_IMAGE : Message.TYPE_FILE));
}
private int getMediaRuntime(File file) {
try {
MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
mediaMetadataRetriever.setDataSource(file.toString());
- return Integer.parseInt(mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION));
+ return Integer.parseInt(
+ mediaMetadataRetriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_DURATION));
} catch (RuntimeException e) {
return 0;
}
@@ 1431,12 1560,14 @@ public class FileBackend {
}
private Dimensions scalePdfDimensions(Dimensions in) {
- final DisplayMetrics displayMetrics = mXmppConnectionService.getResources().getDisplayMetrics();
+ final DisplayMetrics displayMetrics =
+ mXmppConnectionService.getResources().getDisplayMetrics();
final int target = (int) (displayMetrics.density * 288);
return scalePdfDimensions(in, target, true);
}
- private static Dimensions scalePdfDimensions(final Dimensions in, final int target, final boolean fit) {
+ private static Dimensions scalePdfDimensions(
+ final Dimensions in, final int target, final boolean fit) {
final int w, h;
if (fit == (in.width <= in.height)) {
w = Math.max((int) (in.width / ((double) in.height / target)), 1);
@@ 1491,7 1622,6 @@ public class FileBackend {
}
}
-
public static class FileCopyException extends Exception {
private final int resId;
@@ 1499,8 1629,7 @@ public class FileBackend {
this.resId = resId;
}
- public @StringRes
- int getResId() {
+ public @StringRes int getResId() {
return resId;
}
}
M src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java => src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java +1 -1
@@ 91,7 91,7 @@ public class AttachFileToConversationRunnable implements Runnable, TranscoderLis
private void processAsVideo() throws FileNotFoundException {
Log.d(Config.LOGTAG, "processing file as video");
mXmppConnectionService.startForcingForegroundNotification();
- message.setRelativeFilePath(message.getUuid() + ".mp4");
+ mXmppConnectionService.getFileBackend().setupRelativeFilePath(message, String.format("%s.%s", message.getUuid(), "mp4"));
final DownloadableFile file = mXmppConnectionService.getFileBackend().getFile(message);
if (Objects.requireNonNull(file.getParentFile()).mkdirs()) {
Log.d(Config.LOGTAG, "created parent directory for video file");
M src/main/java/eu/siacs/conversations/services/ExportBackupService.java => src/main/java/eu/siacs/conversations/services/ExportBackupService.java +3 -3
@@ 291,7 291,7 @@ public class ExportBackupService extends Service {
secureRandom.nextBytes(salt);
final BackupFileHeader backupFileHeader = new BackupFileHeader(getString(R.string.app_name), account.getJid(), System.currentTimeMillis(), IV, salt);
final Progress progress = new Progress(mBuilder, max, count);
- final File file = new File(FileBackend.getBackupDirectory(this) + account.getJid().asBareJid().toEscapedString() + ".ceb");
+ final File file = new File(FileBackend.getBackupDirectory(this), account.getJid().asBareJid().toEscapedString() + ".ceb");
files.add(file);
final File directory = file.getParentFile();
if (directory != null && directory.mkdirs()) {
@@ 335,7 335,7 @@ public class ExportBackupService extends Service {
}
private void notifySuccess(final List<File> files) {
- final String path = FileBackend.getBackupDirectory(this);
+ final String path = FileBackend.getBackupDirectory(this).getAbsolutePath();
PendingIntent openFolderIntent = null;
@@ 363,7 363,7 @@ public class ExportBackupService extends Service {
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext(), "backup");
mBuilder.setContentTitle(getString(R.string.notification_backup_created_title))
.setContentText(getString(R.string.notification_backup_created_subtitle, path))
- .setStyle(new NotificationCompat.BigTextStyle().bigText(getString(R.string.notification_backup_created_subtitle, FileBackend.getBackupDirectory(this))))
+ .setStyle(new NotificationCompat.BigTextStyle().bigText(getString(R.string.notification_backup_created_subtitle, FileBackend.getBackupDirectory(this).getAbsolutePath())))
.setAutoCancel(true)
.setContentIntent(openFolderIntent)
.setSmallIcon(R.drawable.ic_archive_white_24dp);
M src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java => src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java +3 -4
@@ 46,7 46,6 @@ import eu.siacs.conversations.ui.util.MyLinkify;
import eu.siacs.conversations.ui.util.SoftKeyboardUtils;
import eu.siacs.conversations.utils.AccountUtils;
import eu.siacs.conversations.utils.Compatibility;
-import eu.siacs.conversations.utils.EmojiWrapper;
import eu.siacs.conversations.utils.StringUtils;
import eu.siacs.conversations.utils.StylingHelper;
import eu.siacs.conversations.utils.XmppUri;
@@ 471,11 470,11 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
String subject = mucOptions.getSubject();
final boolean hasTitle;
if (printableValue(roomName)) {
- this.binding.mucTitle.setText(EmojiWrapper.transform(roomName));
+ this.binding.mucTitle.setText(roomName);
this.binding.mucTitle.setVisibility(View.VISIBLE);
hasTitle = true;
} else if (!printableValue(subject)) {
- this.binding.mucTitle.setText(EmojiWrapper.transform(mConversation.getName()));
+ this.binding.mucTitle.setText(mConversation.getName());
hasTitle = true;
this.binding.mucTitle.setVisibility(View.VISIBLE);
} else {
@@ 486,7 485,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
SpannableStringBuilder spannable = new SpannableStringBuilder(subject);
StylingHelper.format(spannable, this.binding.mucSubject.getCurrentTextColor());
MyLinkify.addLinks(spannable, false);
- this.binding.mucSubject.setText(EmojiWrapper.transform(spannable));
+ this.binding.mucSubject.setText(spannable);
this.binding.mucSubject.setTextAppearance(this, subject.length() > (hasTitle ? 128 : 196) ? R.style.TextAppearance_Conversations_Body1_Linkified : R.style.TextAppearance_Conversations_Subhead);
this.binding.mucSubject.setAutoLinkMask(0);
this.binding.mucSubject.setVisibility(View.VISIBLE);
M src/main/java/eu/siacs/conversations/ui/ConversationFragment.java => src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +7 -6
@@ 6,6 6,7 @@ import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ 1183,8 1184,8 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
cancelTransmission.setVisible(true);
}
if (m.isFileOrImage() && !deleted && !cancelable) {
- String path = m.getRelativeFilePath();
- if (path == null || !path.startsWith("/") || FileBackend.isInDirectoryThatShouldNotBeScanned(getActivity(), path)) {
+ final String path = m.getRelativeFilePath();
+ if (path == null || !path.startsWith("/")) {
deleteFile.setVisible(true);
deleteFile.setTitle(activity.getString(R.string.delete_x_file, UIHelper.getFileDescriptionString(activity, m)));
}
@@ 1744,7 1745,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
if (context == null) {
return;
}
- if (intent.resolveActivity(context.getPackageManager()) != null) {
+ try {
if (chooser) {
startActivityForResult(
Intent.createChooser(intent, getString(R.string.perform_action_with)),
@@ 1752,7 1753,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
} else {
startActivityForResult(intent, attachmentChoice);
}
- } else {
+ } catch (final ActivityNotFoundException e) {
Toast.makeText(context, R.string.no_application_found, Toast.LENGTH_LONG).show();
}
}
@@ 2254,10 2255,10 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
private List<Uri> cleanUris(final List<Uri> uris) {
- Iterator<Uri> iterator = uris.iterator();
+ final Iterator<Uri> iterator = uris.iterator();
while (iterator.hasNext()) {
final Uri uri = iterator.next();
- if (FileBackend.weOwnFile(getActivity(), uri)) {
+ if (FileBackend.weOwnFile(uri)) {
iterator.remove();
Toast.makeText(getActivity(), R.string.security_violation_not_attaching_file, Toast.LENGTH_SHORT).show();
}
M src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java => src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java +1 -2
@@ 81,7 81,6 @@ import eu.siacs.conversations.ui.util.ActivityResult;
import eu.siacs.conversations.ui.util.ConversationMenuConfigurator;
import eu.siacs.conversations.ui.util.MenuDoubleTabUtil;
import eu.siacs.conversations.ui.util.PendingItem;
-import eu.siacs.conversations.utils.EmojiWrapper;
import eu.siacs.conversations.utils.ExceptionHelper;
import eu.siacs.conversations.utils.SignupUtils;
import eu.siacs.conversations.utils.XmppUri;
@@ 615,7 614,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
if (mainFragment instanceof ConversationFragment) {
final Conversation conversation = ((ConversationFragment) mainFragment).getConversation();
if (conversation != null) {
- actionBar.setTitle(EmojiWrapper.transform(conversation.getName()));
+ actionBar.setTitle(conversation.getName());
actionBar.setDisplayHomeAsUpEnabled(true);
ActionBarUtil.setActionBarOnClickListener(
binding.toolbar,
M src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java => src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java +247 -230
@@ 22,6 22,7 @@ import java.util.List;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.EnterJidDialogBinding;
+import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
import eu.siacs.conversations.ui.interfaces.OnBackendConnected;
import eu.siacs.conversations.ui.util.DelayedHintHelper;
@@ 29,234 30,250 @@ import eu.siacs.conversations.xmpp.Jid;
public class EnterJidDialog extends DialogFragment implements OnBackendConnected, TextWatcher {
-
- private static final List<String> SUSPICIOUS_DOMAINS = Arrays.asList("conference","muc","room","rooms","chat");
-
- private OnEnterJidDialogPositiveListener mListener = null;
-
- private static final String TITLE_KEY = "title";
- private static final String POSITIVE_BUTTON_KEY = "positive_button";
- private static final String PREFILLED_JID_KEY = "prefilled_jid";
- private static final String ACCOUNT_KEY = "account";
- private static final String ALLOW_EDIT_JID_KEY = "allow_edit_jid";
- private static final String ACCOUNTS_LIST_KEY = "activated_accounts_list";
- private static final String SANITY_CHECK_JID = "sanity_check_jid";
-
- private KnownHostsAdapter knownHostsAdapter;
- private Collection<String> whitelistedDomains = Collections.emptyList();
-
- private EnterJidDialogBinding binding;
- private AlertDialog dialog;
- private boolean sanityCheckJid = false;
-
-
- private boolean issuedWarning = false;
-
- public static EnterJidDialog newInstance(final List<String> activatedAccounts,
- final String title, final String positiveButton,
- final String prefilledJid, final String account,
- boolean allowEditJid, final boolean sanity_check_jid) {
- EnterJidDialog dialog = new EnterJidDialog();
- Bundle bundle = new Bundle();
- bundle.putString(TITLE_KEY, title);
- bundle.putString(POSITIVE_BUTTON_KEY, positiveButton);
- bundle.putString(PREFILLED_JID_KEY, prefilledJid);
- bundle.putString(ACCOUNT_KEY, account);
- bundle.putBoolean(ALLOW_EDIT_JID_KEY, allowEditJid);
- bundle.putStringArrayList(ACCOUNTS_LIST_KEY, (ArrayList<String>) activatedAccounts);
- bundle.putBoolean(SANITY_CHECK_JID, sanity_check_jid);
- dialog.setArguments(bundle);
- return dialog;
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- setRetainInstance(true);
- }
-
- @Override
- public void onStart() {
- super.onStart();
- final Activity activity = getActivity();
- if (activity instanceof XmppActivity && ((XmppActivity) activity).xmppConnectionService != null) {
- refreshKnownHosts();
- }
- }
-
- @NonNull
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- builder.setTitle(getArguments().getString(TITLE_KEY));
- binding = DataBindingUtil.inflate(getActivity().getLayoutInflater(), R.layout.enter_jid_dialog, null, false);
- this.knownHostsAdapter = new KnownHostsAdapter(getActivity(), R.layout.simple_list_item);
- binding.jid.setAdapter(this.knownHostsAdapter);
- binding.jid.addTextChangedListener(this);
- String prefilledJid = getArguments().getString(PREFILLED_JID_KEY);
- if (prefilledJid != null) {
- binding.jid.append(prefilledJid);
- if (!getArguments().getBoolean(ALLOW_EDIT_JID_KEY)) {
- binding.jid.setFocusable(false);
- binding.jid.setFocusableInTouchMode(false);
- binding.jid.setClickable(false);
- binding.jid.setCursorVisible(false);
- }
- }
- sanityCheckJid = getArguments().getBoolean(SANITY_CHECK_JID, false);
-
- DelayedHintHelper.setHint(R.string.account_settings_example_jabber_id, binding.jid);
-
- String account = getArguments().getString(ACCOUNT_KEY);
- if (account == null) {
- StartConversationActivity.populateAccountSpinner(getActivity(), getArguments().getStringArrayList(ACCOUNTS_LIST_KEY), binding.account);
- } else {
- ArrayAdapter<String> adapter = new ArrayAdapter<>(getActivity(),
- R.layout.simple_list_item,
- new String[]{account});
- binding.account.setEnabled(false);
- adapter.setDropDownViewResource(R.layout.simple_list_item);
- binding.account.setAdapter(adapter);
- }
-
-
-
- builder.setView(binding.getRoot());
- builder.setNegativeButton(R.string.cancel, null);
- builder.setPositiveButton(getArguments().getString(POSITIVE_BUTTON_KEY), null);
- this.dialog = builder.create();
-
- View.OnClickListener dialogOnClick = v -> {
- handleEnter(binding, account);
- };
-
- binding.jid.setOnEditorActionListener((v, actionId, event) -> {
- handleEnter(binding, account);
- return true;
- });
-
- dialog.show();
- dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(dialogOnClick);
- return dialog;
- }
-
- private void handleEnter(EnterJidDialogBinding binding, String account) {
- final Jid accountJid;
- if (!binding.account.isEnabled() && account == null) {
- return;
- }
- try {
- if (Config.DOMAIN_LOCK != null) {
- accountJid = Jid.ofEscaped((String) binding.account.getSelectedItem(), Config.DOMAIN_LOCK, null);
- } else {
- accountJid = Jid.ofEscaped((String) binding.account.getSelectedItem());
- }
- } catch (final IllegalArgumentException e) {
- return;
- }
- final Jid contactJid;
- try {
- contactJid = Jid.ofEscaped(binding.jid.getText().toString());
- } catch (final IllegalArgumentException e) {
- binding.jidLayout.setError(getActivity().getString(R.string.invalid_jid));
- return;
- }
-
- if (!issuedWarning && sanityCheckJid) {
- if (contactJid.isDomainJid()) {
- binding.jidLayout.setError(getActivity().getString(R.string.this_looks_like_a_domain));
- dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add_anway);
- issuedWarning = true;
- return;
- }
- if (suspiciousSubDomain(contactJid.getDomain().toEscapedString())) {
- binding.jidLayout.setError(getActivity().getString(R.string.this_looks_like_channel));
- dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add_anway);
- issuedWarning = true;
- return;
- }
- }
-
- if (mListener != null) {
- try {
- if (mListener.onEnterJidDialogPositive(accountJid, contactJid)) {
- dialog.dismiss();
- }
- } catch (JidError error) {
- binding.jidLayout.setError(error.toString());
- dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add);
- issuedWarning = false;
- }
- }
- }
-
- public void setOnEnterJidDialogPositiveListener(OnEnterJidDialogPositiveListener listener) {
- this.mListener = listener;
- }
-
- @Override
- public void onBackendConnected() {
- refreshKnownHosts();
- }
-
- private void refreshKnownHosts() {
- Activity activity = getActivity();
- if (activity instanceof XmppActivity) {
- Collection<String> hosts = ((XmppActivity) activity).xmppConnectionService.getKnownHosts();
- this.knownHostsAdapter.refresh(hosts);
- this.whitelistedDomains = hosts;
- }
- }
-
- @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 (issuedWarning) {
- dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add);
- binding.jidLayout.setError(null);
- issuedWarning = false;
- }
- }
-
- public interface OnEnterJidDialogPositiveListener {
- boolean onEnterJidDialogPositive(Jid account, Jid contact) throws EnterJidDialog.JidError;
- }
-
- public static class JidError extends Exception {
- final String msg;
-
- public JidError(final String msg) {
- this.msg = msg;
- }
-
- public String toString() {
- return msg;
- }
- }
-
- @Override
- public void onDestroyView() {
- Dialog dialog = getDialog();
- if (dialog != null && getRetainInstance()) {
- dialog.setDismissMessage(null);
- }
- super.onDestroyView();
- }
-
- private boolean suspiciousSubDomain(String domain) {
- if (this.whitelistedDomains.contains(domain)) {
- return false;
- }
- final String[] parts = domain.split("\\.");
- return parts.length >= 3 && SUSPICIOUS_DOMAINS.contains(parts[0]);
- }
+ private static final List<String> SUSPICIOUS_DOMAINS =
+ Arrays.asList("conference", "muc", "room", "rooms", "chat");
+
+ private OnEnterJidDialogPositiveListener mListener = null;
+
+ private static final String TITLE_KEY = "title";
+ private static final String POSITIVE_BUTTON_KEY = "positive_button";
+ private static final String PREFILLED_JID_KEY = "prefilled_jid";
+ private static final String ACCOUNT_KEY = "account";
+ private static final String ALLOW_EDIT_JID_KEY = "allow_edit_jid";
+ private static final String ACCOUNTS_LIST_KEY = "activated_accounts_list";
+ private static final String SANITY_CHECK_JID = "sanity_check_jid";
+
+ private KnownHostsAdapter knownHostsAdapter;
+ private Collection<String> whitelistedDomains = Collections.emptyList();
+
+ private EnterJidDialogBinding binding;
+ private AlertDialog dialog;
+ private boolean sanityCheckJid = false;
+
+ private boolean issuedWarning = false;
+
+ public static EnterJidDialog newInstance(
+ final List<String> activatedAccounts,
+ final String title,
+ final String positiveButton,
+ final String prefilledJid,
+ final String account,
+ boolean allowEditJid,
+ final boolean sanity_check_jid) {
+ EnterJidDialog dialog = new EnterJidDialog();
+ Bundle bundle = new Bundle();
+ bundle.putString(TITLE_KEY, title);
+ bundle.putString(POSITIVE_BUTTON_KEY, positiveButton);
+ bundle.putString(PREFILLED_JID_KEY, prefilledJid);
+ bundle.putString(ACCOUNT_KEY, account);
+ bundle.putBoolean(ALLOW_EDIT_JID_KEY, allowEditJid);
+ bundle.putStringArrayList(ACCOUNTS_LIST_KEY, (ArrayList<String>) activatedAccounts);
+ bundle.putBoolean(SANITY_CHECK_JID, sanity_check_jid);
+ dialog.setArguments(bundle);
+ return dialog;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ setRetainInstance(true);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ final Activity activity = getActivity();
+ if (activity instanceof XmppActivity
+ && ((XmppActivity) activity).xmppConnectionService != null) {
+ refreshKnownHosts();
+ }
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setTitle(getArguments().getString(TITLE_KEY));
+ binding =
+ DataBindingUtil.inflate(
+ getActivity().getLayoutInflater(), R.layout.enter_jid_dialog, null, false);
+ this.knownHostsAdapter = new KnownHostsAdapter(getActivity(), R.layout.simple_list_item);
+ binding.jid.setAdapter(this.knownHostsAdapter);
+ binding.jid.addTextChangedListener(this);
+ String prefilledJid = getArguments().getString(PREFILLED_JID_KEY);
+ if (prefilledJid != null) {
+ binding.jid.append(prefilledJid);
+ if (!getArguments().getBoolean(ALLOW_EDIT_JID_KEY)) {
+ binding.jid.setFocusable(false);
+ binding.jid.setFocusableInTouchMode(false);
+ binding.jid.setClickable(false);
+ binding.jid.setCursorVisible(false);
+ }
+ }
+ sanityCheckJid = getArguments().getBoolean(SANITY_CHECK_JID, false);
+
+ DelayedHintHelper.setHint(R.string.account_settings_example_jabber_id, binding.jid);
+
+ String account = getArguments().getString(ACCOUNT_KEY);
+ if (account == null) {
+ StartConversationActivity.populateAccountSpinner(
+ getActivity(),
+ getArguments().getStringArrayList(ACCOUNTS_LIST_KEY),
+ binding.account);
+ } else {
+ ArrayAdapter<String> adapter =
+ new ArrayAdapter<>(
+ getActivity(), R.layout.simple_list_item, new String[] {account});
+ binding.account.setEnabled(false);
+ adapter.setDropDownViewResource(R.layout.simple_list_item);
+ binding.account.setAdapter(adapter);
+ }
+
+ builder.setView(binding.getRoot());
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(getArguments().getString(POSITIVE_BUTTON_KEY), null);
+ this.dialog = builder.create();
+
+ View.OnClickListener dialogOnClick =
+ v -> {
+ handleEnter(binding, account);
+ };
+
+ binding.jid.setOnEditorActionListener(
+ (v, actionId, event) -> {
+ handleEnter(binding, account);
+ return true;
+ });
+
+ dialog.show();
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(dialogOnClick);
+ return dialog;
+ }
+
+ private void handleEnter(EnterJidDialogBinding binding, String account) {
+ final Jid accountJid;
+ if (!binding.account.isEnabled() && account == null) {
+ return;
+ }
+ try {
+ if (Config.DOMAIN_LOCK != null) {
+ accountJid =
+ Jid.ofEscaped(
+ (String) binding.account.getSelectedItem(),
+ Config.DOMAIN_LOCK,
+ null);
+ } else {
+ accountJid = Jid.ofEscaped((String) binding.account.getSelectedItem());
+ }
+ } catch (final IllegalArgumentException e) {
+ return;
+ }
+ final Jid contactJid;
+ try {
+ contactJid = Jid.ofEscaped(binding.jid.getText().toString());
+ } catch (final IllegalArgumentException e) {
+ binding.jidLayout.setError(getActivity().getString(R.string.invalid_jid));
+ return;
+ }
+
+ if (!issuedWarning && sanityCheckJid) {
+ if (contactJid.isDomainJid()) {
+ binding.jidLayout.setError(
+ getActivity().getString(R.string.this_looks_like_a_domain));
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add_anway);
+ issuedWarning = true;
+ return;
+ }
+ if (suspiciousSubDomain(contactJid.getDomain().toEscapedString())) {
+ binding.jidLayout.setError(
+ getActivity().getString(R.string.this_looks_like_channel));
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add_anway);
+ issuedWarning = true;
+ return;
+ }
+ }
+
+ if (mListener != null) {
+ try {
+ if (mListener.onEnterJidDialogPositive(accountJid, contactJid)) {
+ dialog.dismiss();
+ }
+ } catch (JidError error) {
+ binding.jidLayout.setError(error.toString());
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add);
+ issuedWarning = false;
+ }
+ }
+ }
+
+ public void setOnEnterJidDialogPositiveListener(OnEnterJidDialogPositiveListener listener) {
+ this.mListener = listener;
+ }
+
+ @Override
+ public void onBackendConnected() {
+ refreshKnownHosts();
+ }
+
+ private void refreshKnownHosts() {
+ final Activity activity = getActivity();
+ if (activity instanceof XmppActivity) {
+ final XmppConnectionService service = ((XmppActivity) activity).xmppConnectionService;
+ if (service == null) {
+ return;
+ }
+ final Collection<String> hosts = service.getKnownHosts();
+ this.knownHostsAdapter.refresh(hosts);
+ this.whitelistedDomains = hosts;
+ }
+ }
+
+ @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 (issuedWarning) {
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add);
+ binding.jidLayout.setError(null);
+ issuedWarning = false;
+ }
+ }
+
+ public interface OnEnterJidDialogPositiveListener {
+ boolean onEnterJidDialogPositive(Jid account, Jid contact) throws EnterJidDialog.JidError;
+ }
+
+ public static class JidError extends Exception {
+ final String msg;
+
+ public JidError(final String msg) {
+ this.msg = msg;
+ }
+
+ @NonNull
+ public String toString() {
+ return msg;
+ }
+ }
+
+ @Override
+ public void onDestroyView() {
+ Dialog dialog = getDialog();
+ if (dialog != null && getRetainInstance()) {
+ dialog.setDismissMessage(null);
+ }
+ super.onDestroyView();
+ }
+
+ private boolean suspiciousSubDomain(String domain) {
+ if (this.whitelistedDomains.contains(domain)) {
+ return false;
+ }
+ final String[] parts = domain.split("\\.");
+ return parts.length >= 3 && SUSPICIOUS_DOMAINS.contains(parts[0]);
+ }
}
M src/main/java/eu/siacs/conversations/ui/RecordingActivity.java => src/main/java/eu/siacs/conversations/ui/RecordingActivity.java +63 -51
@@ 1,11 1,12 @@
package eu.siacs.conversations.ui;
import android.app.Activity;
-import android.content.Context;
import android.content.Intent;
import android.media.MediaRecorder;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
+import android.os.Environment;
import android.os.FileObserver;
import android.os.Handler;
import android.os.SystemClock;
@@ 17,25 18,22 @@ import android.widget.Toast;
import androidx.databinding.DataBindingUtil;
import java.io.File;
-import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
+import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ActivityRecordingBinding;
-import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.ui.util.SettingsUtils;
import eu.siacs.conversations.utils.ThemeHelper;
import eu.siacs.conversations.utils.TimeFrameUtils;
public class RecordingActivity extends Activity implements View.OnClickListener {
- public static String STORAGE_DIRECTORY_TYPE_NAME = "Recordings";
-
private ActivityRecordingBinding binding;
private MediaRecorder mRecorder;
@@ 44,13 42,14 @@ public class RecordingActivity extends Activity implements View.OnClickListener
private final CountDownLatch outputFileWrittenLatch = new CountDownLatch(1);
private final Handler mHandler = new Handler();
- private final Runnable mTickExecutor = new Runnable() {
- @Override
- public void run() {
- tick();
- mHandler.postDelayed(mTickExecutor, 100);
- }
- };
+ private final Runnable mTickExecutor =
+ new Runnable() {
+ @Override
+ public void run() {
+ tick();
+ mHandler.postDelayed(mTickExecutor, 100);
+ }
+ };
private File mOutputFile;
@@ 68,7 67,7 @@ public class RecordingActivity extends Activity implements View.OnClickListener
}
@Override
- protected void onResume(){
+ protected void onResume() {
super.onResume();
SettingsUtils.applyScreenshotPreventionSetting(this);
}
@@ 137,56 136,69 @@ public class RecordingActivity extends Activity implements View.OnClickListener
}
}
if (saveFile) {
- new Thread(() -> {
- try {
- if (!outputFileWrittenLatch.await(2, TimeUnit.SECONDS)) {
- Log.d(Config.LOGTAG, "time out waiting for output file to be written");
- }
- } catch (InterruptedException e) {
- Log.d(Config.LOGTAG, "interrupted while waiting for output file to be written", e);
- }
- runOnUiThread(() -> {
- setResult(Activity.RESULT_OK, new Intent().setData(Uri.fromFile(mOutputFile)));
- finish();
- });
- }).start();
+ new Thread(
+ () -> {
+ try {
+ if (!outputFileWrittenLatch.await(2, TimeUnit.SECONDS)) {
+ Log.d(
+ Config.LOGTAG,
+ "time out waiting for output file to be written");
+ }
+ } catch (InterruptedException e) {
+ Log.d(
+ Config.LOGTAG,
+ "interrupted while waiting for output file to be written",
+ e);
+ }
+ runOnUiThread(
+ () -> {
+ setResult(
+ Activity.RESULT_OK,
+ new Intent()
+ .setData(Uri.fromFile(mOutputFile)));
+ finish();
+ });
+ })
+ .start();
}
}
- private static File generateOutputFilename(Context context) {
- SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
- String filename = "RECORDING_" + dateFormat.format(new Date()) + ".m4a";
- return new File(FileBackend.getConversationsDirectory(context, STORAGE_DIRECTORY_TYPE_NAME) + "/" + filename);
+ private File generateOutputFilename() {
+ final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
+ final String filename = "RECORDING_" + dateFormat.format(new Date()) + ".m4a";
+ final File parentDirectory;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ parentDirectory =
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_RECORDINGS);
+ } else {
+ parentDirectory =
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
+ }
+ final File conversationsDirectory = new File(parentDirectory, getString(R.string.app_name));
+ return new File(conversationsDirectory, filename);
}
private void setupOutputFile() {
- mOutputFile = generateOutputFilename(this);
- File parentDirectory = mOutputFile.getParentFile();
- if (parentDirectory.mkdirs()) {
+ mOutputFile = generateOutputFilename();
+ final File parentDirectory = mOutputFile.getParentFile();
+ if (Objects.requireNonNull(parentDirectory).mkdirs()) {
Log.d(Config.LOGTAG, "created " + parentDirectory.getAbsolutePath());
}
- File noMedia = new File(parentDirectory, ".nomedia");
- if (!noMedia.exists()) {
- try {
- if (noMedia.createNewFile()) {
- Log.d(Config.LOGTAG, "created nomedia file in " + parentDirectory.getAbsolutePath());
- }
- } catch (IOException e) {
- Log.d(Config.LOGTAG, "unable to create nomedia file in " + parentDirectory.getAbsolutePath(), e);
- }
- }
setupFileObserver(parentDirectory);
}
private void setupFileObserver(File directory) {
- mFileObserver = new FileObserver(directory.getAbsolutePath()) {
- @Override
- public void onEvent(int event, String s) {
- if (s != null && s.equals(mOutputFile.getName()) && event == FileObserver.CLOSE_WRITE) {
- outputFileWrittenLatch.countDown();
- }
- }
- };
+ mFileObserver =
+ new FileObserver(directory.getAbsolutePath()) {
+ @Override
+ public void onEvent(int event, String s) {
+ if (s != null
+ && s.equals(mOutputFile.getName())
+ && event == FileObserver.CLOSE_WRITE) {
+ outputFileWrittenLatch.countDown();
+ }
+ }
+ };
mFileObserver.startWatching();
}
M src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java => src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java +338 -217
@@ 71,10 71,9 @@ import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
import eu.siacs.conversations.xmpp.jingle.Media;
import eu.siacs.conversations.xmpp.jingle.RtpEndUserState;
-import static eu.siacs.conversations.utils.PermissionUtils.getFirstDenied;
-import static java.util.Arrays.asList;
-
-public class RtpSessionActivity extends XmppActivity implements XmppConnectionService.OnJingleRtpConnectionUpdate, eu.siacs.conversations.ui.widget.SurfaceViewRenderer.OnAspectRatioChanged {
+public class RtpSessionActivity extends XmppActivity
+ implements XmppConnectionService.OnJingleRtpConnectionUpdate,
+ eu.siacs.conversations.ui.widget.SurfaceViewRenderer.OnAspectRatioChanged {
public static final String EXTRA_WITH = "with";
public static final String EXTRA_SESSION_ID = "session_id";
@@ 86,33 85,31 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
private static final int CALL_DURATION_UPDATE_INTERVAL = 333;
- public static final List<RtpEndUserState> END_CARD = Arrays.asList(
- RtpEndUserState.APPLICATION_ERROR,
- RtpEndUserState.SECURITY_ERROR,
- RtpEndUserState.DECLINED_OR_BUSY,
- RtpEndUserState.CONNECTIVITY_ERROR,
- RtpEndUserState.CONNECTIVITY_LOST_ERROR,
- RtpEndUserState.RETRACTED
- );
- private static final List<RtpEndUserState> STATES_SHOWING_HELP_BUTTON = Arrays.asList(
- RtpEndUserState.APPLICATION_ERROR,
- RtpEndUserState.CONNECTIVITY_ERROR,
- RtpEndUserState.SECURITY_ERROR
- );
- private static final List<RtpEndUserState> STATES_SHOWING_SWITCH_TO_CHAT = Arrays.asList(
- RtpEndUserState.CONNECTING,
- RtpEndUserState.CONNECTED,
- RtpEndUserState.RECONNECTING
- );
- private static final List<RtpEndUserState> STATES_CONSIDERED_CONNECTED = Arrays.asList(
- RtpEndUserState.CONNECTED,
- RtpEndUserState.RECONNECTING
- );
- private static final List<RtpEndUserState> STATES_SHOWING_PIP_PLACEHOLDER = Arrays.asList(
- RtpEndUserState.ACCEPTING_CALL,
- RtpEndUserState.CONNECTING,
- RtpEndUserState.RECONNECTING
- );
+ public static final List<RtpEndUserState> END_CARD =
+ Arrays.asList(
+ RtpEndUserState.APPLICATION_ERROR,
+ RtpEndUserState.SECURITY_ERROR,
+ RtpEndUserState.DECLINED_OR_BUSY,
+ RtpEndUserState.CONNECTIVITY_ERROR,
+ RtpEndUserState.CONNECTIVITY_LOST_ERROR,
+ RtpEndUserState.RETRACTED);
+ private static final List<RtpEndUserState> STATES_SHOWING_HELP_BUTTON =
+ Arrays.asList(
+ RtpEndUserState.APPLICATION_ERROR,
+ RtpEndUserState.CONNECTIVITY_ERROR,
+ RtpEndUserState.SECURITY_ERROR);
+ private static final List<RtpEndUserState> STATES_SHOWING_SWITCH_TO_CHAT =
+ Arrays.asList(
+ RtpEndUserState.CONNECTING,
+ RtpEndUserState.CONNECTED,
+ RtpEndUserState.RECONNECTING);
+ private static final List<RtpEndUserState> STATES_CONSIDERED_CONNECTED =
+ Arrays.asList(RtpEndUserState.CONNECTED, RtpEndUserState.RECONNECTING);
+ private static final List<RtpEndUserState> STATES_SHOWING_PIP_PLACEHOLDER =
+ Arrays.asList(
+ RtpEndUserState.ACCEPTING_CALL,
+ RtpEndUserState.CONNECTING,
+ RtpEndUserState.RECONNECTING);
private static final String PROXIMITY_WAKE_LOCK_TAG = "conversations:in-rtp-session";
private static final int REQUEST_ACCEPT_CALL = 0x1111;
private WeakReference<JingleRtpConnection> rtpConnectionReference;
@@ 121,13 118,14 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
private PowerManager.WakeLock mProximityWakeLock;
private final Handler mHandler = new Handler();
- private final Runnable mTickExecutor = new Runnable() {
- @Override
- public void run() {
- updateCallDuration();
- mHandler.postDelayed(mTickExecutor, CALL_DURATION_UPDATE_INTERVAL);
- }
- };
+ private final Runnable mTickExecutor =
+ new Runnable() {
+ @Override
+ public void run() {
+ updateCallDuration();
+ mHandler.postDelayed(mTickExecutor, CALL_DURATION_UPDATE_INTERVAL);
+ }
+ };
private static Set<Media> actionToMedia(final String action) {
if (ACTION_MAKE_VIDEO_CALL.equals(action)) {
@@ 137,21 135,27 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
}
- private static void addSink(final VideoTrack videoTrack, final SurfaceViewRenderer surfaceViewRenderer) {
+ private static void addSink(
+ final VideoTrack videoTrack, final SurfaceViewRenderer surfaceViewRenderer) {
try {
videoTrack.addSink(surfaceViewRenderer);
} catch (final IllegalStateException e) {
- Log.e(Config.LOGTAG, "possible race condition on trying to display video track. ignoring", e);
+ Log.e(
+ Config.LOGTAG,
+ "possible race condition on trying to display video track. ignoring",
+ e);
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
- | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
- | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
- | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
+ getWindow()
+ .addFlags(
+ WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_rtp_session);
setSupportActionBar(binding.toolbar);
@@ 194,7 198,8 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
return STATES_SHOWING_HELP_BUTTON.contains(requireRtpConnection().getEndUserState());
} catch (IllegalStateException e) {
final Intent intent = getIntent();
- final String state = intent != null ? intent.getStringExtra(EXTRA_LAST_REPORTED_STATE) : null;
+ final String state =
+ intent != null ? intent.getStringExtra(EXTRA_LAST_REPORTED_STATE) : null;
if (state != null) {
return STATES_SHOWING_HELP_BUTTON.contains(RtpEndUserState.valueOf(state));
} else {
@@ 204,8 209,10 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
private boolean isSwitchToConversationVisible() {
- final JingleRtpConnection connection = this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null;
- return connection != null && STATES_SHOWING_SWITCH_TO_CHAT.contains(connection.getEndUserState());
+ final JingleRtpConnection connection =
+ this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null;
+ return connection != null
+ && STATES_SHOWING_SWITCH_TO_CHAT.contains(connection.getEndUserState());
}
private boolean isAudioOnlyConversation() {
@@ 217,7 224,9 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
private void switchToConversation() {
final Contact contact = getWith();
- final Conversation conversation = xmppConnectionService.findOrCreateConversation(contact.getAccount(), contact.getJid(), false, true);
+ final Conversation conversation =
+ xmppConnectionService.findOrCreateConversation(
+ contact.getAccount(), contact.getJid(), false, true);
switchToConversation(conversation);
}
@@ 250,7 259,8 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
try {
startActivity(intent);
} catch (final ActivityNotFoundException e) {
- Toast.makeText(this, R.string.no_application_found_to_open_link, Toast.LENGTH_LONG).show();
+ Toast.makeText(this, R.string.no_application_found_to_open_link, Toast.LENGTH_LONG)
+ .show();
}
}
@@ 273,10 283,15 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
final Account account = extractAccount(intent);
final Jid with = Jid.ofEscaped(intent.getStringExtra(EXTRA_WITH));
final String state = intent.getStringExtra(EXTRA_LAST_REPORTED_STATE);
- if (!Intent.ACTION_VIEW.equals(action) || state == null || !END_CARD.contains(RtpEndUserState.valueOf(state))) {
- resetIntent(account, with, RtpEndUserState.RETRACTED, actionToMedia(intent.getAction()));
+ if (!Intent.ACTION_VIEW.equals(action)
+ || state == null
+ || !END_CARD.contains(RtpEndUserState.valueOf(state))) {
+ resetIntent(
+ account, with, RtpEndUserState.RETRACTED, actionToMedia(intent.getAction()));
}
- xmppConnectionService.getJingleConnectionManager().retractSessionProposal(account, with.asBareJid());
+ xmppConnectionService
+ .getJingleConnectionManager()
+ .retractSessionProposal(account, with.asBareJid());
}
private void rejectCall(View view) {
@@ 291,7 306,8 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
private void requestPermissionsAndAcceptCall() {
final List<String> permissions;
if (getMedia().contains(Media.VIDEO)) {
- permissions = ImmutableList.of(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO);
+ permissions =
+ ImmutableList.of(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO);
} else {
permissions = ImmutableList.of(Manifest.permission.RECORD_AUDIO);
}
@@ 302,7 318,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
private void checkRecorderAndAcceptCall() {
- checkMicrophoneAvailability();
+ checkMicrophoneAvailabilityAsync();
try {
requireRtpConnection().acceptCall();
} catch (final IllegalStateException e) {
@@ 310,18 326,22 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
}
+ private void checkMicrophoneAvailabilityAsync() {
+ new Thread(this::checkMicrophoneAvailability).start();
+ }
+
private void checkMicrophoneAvailability() {
- new Thread(() -> {
- final long start = SystemClock.elapsedRealtime();
- final boolean isMicrophoneAvailable = AppRTCAudioManager.isMicrophoneAvailable();
- final long stop = SystemClock.elapsedRealtime();
- Log.d(Config.LOGTAG, "checking microphone availability took " + (stop - start) + "ms");
- if (isMicrophoneAvailable) {
- return;
- }
- runOnUiThread(() -> Toast.makeText(this, R.string.microphone_unavailable, Toast.LENGTH_LONG).show());
+ final long start = SystemClock.elapsedRealtime();
+ final boolean isMicrophoneAvailable = AppRTCAudioManager.isMicrophoneAvailable();
+ final long stop = SystemClock.elapsedRealtime();
+ Log.d(Config.LOGTAG, "checking microphone availability took " + (stop - start) + "ms");
+ if (isMicrophoneAvailable) {
+ return;
}
- ).start();
+ runOnUiThread(
+ () ->
+ Toast.makeText(this, R.string.microphone_unavailable, Toast.LENGTH_LONG)
+ .show());
}
private void putScreenInCallMode() {
@@ 331,9 351,13 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
private void putScreenInCallMode(final Set<Media> media) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
if (!media.contains(Media.VIDEO)) {
- final JingleRtpConnection rtpConnection = rtpConnectionReference != null ? rtpConnectionReference.get() : null;
- final AppRTCAudioManager audioManager = rtpConnection == null ? null : rtpConnection.getAudioManager();
- if (audioManager == null || audioManager.getSelectedAudioDevice() == AppRTCAudioManager.AudioDevice.EARPIECE) {
+ final JingleRtpConnection rtpConnection =
+ rtpConnectionReference != null ? rtpConnectionReference.get() : null;
+ final AppRTCAudioManager audioManager =
+ rtpConnection == null ? null : rtpConnection.getAudioManager();
+ if (audioManager == null
+ || audioManager.getSelectedAudioDevice()
+ == AppRTCAudioManager.AudioDevice.EARPIECE) {
acquireProximityWakeLock();
}
}
@@ 346,30 370,31 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
Log.e(Config.LOGTAG, "power manager not available");
return;
}
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- if (this.mProximityWakeLock == null) {
- this.mProximityWakeLock = powerManager.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, PROXIMITY_WAKE_LOCK_TAG);
- }
- if (!this.mProximityWakeLock.isHeld()) {
- Log.d(Config.LOGTAG, "acquiring proximity wake lock");
- this.mProximityWakeLock.acquire();
- }
+ if (isFinishing()) {
+ Log.e(Config.LOGTAG, "do not acquire wakelock. activity is finishing");
+ return;
+ }
+ if (this.mProximityWakeLock == null) {
+ this.mProximityWakeLock =
+ powerManager.newWakeLock(
+ PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, PROXIMITY_WAKE_LOCK_TAG);
+ }
+ if (!this.mProximityWakeLock.isHeld()) {
+ Log.d(Config.LOGTAG, "acquiring proximity wake lock");
+ this.mProximityWakeLock.acquire();
}
}
private void releaseProximityWakeLock() {
if (this.mProximityWakeLock != null && mProximityWakeLock.isHeld()) {
Log.d(Config.LOGTAG, "releasing proximity wake lock");
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- this.mProximityWakeLock.release(PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY);
- } else {
- this.mProximityWakeLock.release();
- }
+ this.mProximityWakeLock.release(PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY);
this.mProximityWakeLock = null;
}
}
- private void putProximityWakeLockInProperState(final AppRTCAudioManager.AudioDevice audioDevice) {
+ private void putProximityWakeLockInProperState(
+ final AppRTCAudioManager.AudioDevice audioDevice) {
if (audioDevice == AppRTCAudioManager.AudioDevice.EARPIECE) {
acquireProximityWakeLock();
} else {
@@ 378,9 403,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
@Override
- protected void refreshUiReal() {
-
- }
+ protected void refreshUiReal() {}
@Override
public void onNewIntent(final Intent intent) {
@@ 388,7 411,9 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
super.onNewIntent(intent);
setIntent(intent);
if (xmppConnectionService == null) {
- Log.d(Config.LOGTAG, "RtpSessionActivity: background service wasn't bound in onNewIntent()");
+ Log.d(
+ Config.LOGTAG,
+ "RtpSessionActivity: background service wasn't bound in onNewIntent()");
return;
}
final Account account = extractAccount(intent);
@@ 407,8 432,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
} else if (asList(ACTION_MAKE_VIDEO_CALL, ACTION_MAKE_VOICE_CALL).contains(action)) {
proposeJingleRtpSession(account, with, actionToMedia(action));
- binding.with.setText(account.getRoster().getContact(with).getDisplayName());
- binding.withJid.setText(with.asBareJid());
+ setWith(account.getRoster().getContact(with));
} else {
throw new IllegalStateException("received onNewIntent without sessionId");
}
@@ 432,25 456,29 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
} else if (asList(ACTION_MAKE_VIDEO_CALL, ACTION_MAKE_VOICE_CALL).contains(action)) {
proposeJingleRtpSession(account, with, actionToMedia(action));
- binding.with.setText(account.getRoster().getContact(with).getDisplayName());
- binding.withJid.setText(with.asBareJid());
+ setWith(account.getRoster().getContact(with));
} else if (Intent.ACTION_VIEW.equals(action)) {
final String extraLastState = intent.getStringExtra(EXTRA_LAST_REPORTED_STATE);
- final RtpEndUserState state = extraLastState == null ? null : RtpEndUserState.valueOf(extraLastState);
+ final RtpEndUserState state =
+ extraLastState == null ? null : RtpEndUserState.valueOf(extraLastState);
if (state != null) {
Log.d(Config.LOGTAG, "restored last state from intent extra");
updateButtonConfiguration(state);
updateVerifiedShield(false);
updateStateDisplay(state);
- updateProfilePicture(state);
+ updateIncomingCallScreen(state);
invalidateOptionsMenu();
}
- binding.with.setText(account.getRoster().getContact(with).getDisplayName());
- binding.withJid.setText(with.asBareJid());
- if (xmppConnectionService.getJingleConnectionManager().fireJingleRtpConnectionStateUpdates()) {
+ setWith(account.getRoster().getContact(with));
+ if (xmppConnectionService
+ .getJingleConnectionManager()
+ .fireJingleRtpConnectionStateUpdates()) {
return;
}
- if (END_CARD.contains(state) || xmppConnectionService.getJingleConnectionManager().hasMatchingProposal(account, with)) {
+ if (END_CARD.contains(state)
+ || xmppConnectionService
+ .getJingleConnectionManager()
+ .hasMatchingProposal(account, with)) {
return;
}
Log.d(Config.LOGTAG, "restored state (" + state + ") was not an end card. finishing");
@@ 458,12 486,27 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
}
- private void proposeJingleRtpSession(final Account account, final Jid with, final Set<Media> media) {
- checkMicrophoneAvailability();
+ private void setWith() {
+ setWith(getWith());
+ }
+
+ private void setWith(final Contact contact) {
+ binding.with.setText(contact.getDisplayName());
+ binding.withJid.setText(contact.getJid().asBareJid().toEscapedString());
+ }
+
+ private void proposeJingleRtpSession(
+ final Account account, final Jid with, final Set<Media> media) {
+ checkMicrophoneAvailabilityAsync();
if (with.isBareJid()) {
- xmppConnectionService.getJingleConnectionManager().proposeJingleRtpSession(account, with, media);
+ xmppConnectionService
+ .getJingleConnectionManager()
+ .proposeJingleRtpSession(account, with, media);
} else {
- final String sessionId = xmppConnectionService.getJingleConnectionManager().initializeRtpSession(account, with, media);
+ final String sessionId =
+ xmppConnectionService
+ .getJingleConnectionManager()
+ .initializeRtpSession(account, with, media);
initializeActivityWithRunningRtpSession(account, with, sessionId);
resetIntent(account, with, sessionId);
}
@@ 471,7 514,8 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
@Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ public void onRequestPermissionsResult(
+ int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (PermissionUtils.allGranted(grantResults)) {
if (requestCode == REQUEST_ACCEPT_CALL) {
@@ 487,7 531,8 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
} else {
throw new IllegalStateException("Invalid permission result request");
}
- Toast.makeText(this, getString(res, getString(R.string.app_name)), Toast.LENGTH_SHORT).show();
+ Toast.makeText(this, getString(res, getString(R.string.app_name)), Toast.LENGTH_SHORT)
+ .show();
}
}
@@ 505,7 550,8 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
binding.remoteVideo.setOnAspectRatioChanged(null);
binding.localVideo.release();
final WeakReference<JingleRtpConnection> weakReference = this.rtpConnectionReference;
- final JingleRtpConnection jingleRtpConnection = weakReference == null ? null : weakReference.get();
+ final JingleRtpConnection jingleRtpConnection =
+ weakReference == null ? null : weakReference.get();
if (jingleRtpConnection != null) {
releaseVideoTracks(jingleRtpConnection);
}
@@ 542,15 588,18 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
if (switchToPictureInPicture()) {
return;
}
- //TODO apparently this method is not getting called on Android 10 when using the task switcher
+ // TODO apparently this method is not getting called on Android 10 when using the task
+ // switcher
if (emptyReference(rtpConnectionReference) && xmppConnectionService != null) {
retractSessionProposal();
}
}
private boolean isConnected() {
- final JingleRtpConnection connection = this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null;
- return connection != null && STATES_CONSIDERED_CONNECTED.contains(connection.getEndUserState());
+ final JingleRtpConnection connection =
+ this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null;
+ return connection != null
+ && STATES_CONSIDERED_CONNECTED.contains(connection.getEndUserState());
}
private boolean switchToPictureInPicture() {
@@ 568,14 617,13 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
try {
final Rational rational = this.binding.remoteVideo.getAspectRatio();
final Rational clippedRational = Rationals.clip(rational);
- Log.d(Config.LOGTAG, "suggested rational " + rational + ". clipped to " + clippedRational);
+ Log.d(
+ Config.LOGTAG,
+ "suggested rational " + rational + ". clipped to " + clippedRational);
enterPictureInPictureMode(
- new PictureInPictureParams.Builder()
- .setAspectRatio(clippedRational)
- .build()
- );
+ new PictureInPictureParams.Builder().setAspectRatio(clippedRational).build());
} catch (final IllegalStateException e) {
- //this sometimes happens on Samsung phones (possibly when Knox is enabled)
+ // this sometimes happens on Samsung phones (possibly when Knox is enabled)
Log.w(Config.LOGTAG, "unable to enter picture in picture mode", e);
}
}
@@ 584,10 632,14 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
public void onAspectRatioChanged(final Rational rational) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && isPictureInPicture()) {
final Rational clippedRational = Rationals.clip(rational);
- Log.d(Config.LOGTAG, "suggested rational after aspect ratio change " + rational + ". clipped to " + clippedRational);
- setPictureInPictureParams(new PictureInPictureParams.Builder()
- .setAspectRatio(clippedRational)
- .build());
+ Log.d(
+ Config.LOGTAG,
+ "suggested rational after aspect ratio change "
+ + rational
+ + ". clipped to "
+ + clippedRational);
+ setPictureInPictureParams(
+ new PictureInPictureParams.Builder().setAspectRatio(clippedRational).build());
}
}
@@ 602,24 654,31 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
private boolean shouldBePictureInPicture() {
try {
final JingleRtpConnection rtpConnection = requireRtpConnection();
- return rtpConnection.getMedia().contains(Media.VIDEO) && Arrays.asList(
- RtpEndUserState.ACCEPTING_CALL,
- RtpEndUserState.CONNECTING,
- RtpEndUserState.CONNECTED
- ).contains(rtpConnection.getEndUserState());
+ return rtpConnection.getMedia().contains(Media.VIDEO)
+ && Arrays.asList(
+ RtpEndUserState.ACCEPTING_CALL,
+ RtpEndUserState.CONNECTING,
+ RtpEndUserState.CONNECTED)
+ .contains(rtpConnection.getEndUserState());
} catch (final IllegalStateException e) {
return false;
}
}
- private boolean initializeActivityWithRunningRtpSession(final Account account, Jid with, String sessionId) {
- final WeakReference<JingleRtpConnection> reference = xmppConnectionService.getJingleConnectionManager()
- .findJingleRtpConnection(account, with, sessionId);
+ private boolean initializeActivityWithRunningRtpSession(
+ final Account account, Jid with, String sessionId) {
+ final WeakReference<JingleRtpConnection> reference =
+ xmppConnectionService
+ .getJingleConnectionManager()
+ .findJingleRtpConnection(account, with, sessionId);
if (reference == null || reference.get() == null) {
- final JingleConnectionManager.TerminatedRtpSession terminatedRtpSession = xmppConnectionService
- .getJingleConnectionManager().getTerminalSessionState(with, sessionId);
+ final JingleConnectionManager.TerminatedRtpSession terminatedRtpSession =
+ xmppConnectionService
+ .getJingleConnectionManager()
+ .getTerminalSessionState(with, sessionId);
if (terminatedRtpSession == null) {
- throw new IllegalStateException("failed to initialize activity with running rtp session. session not found");
+ throw new IllegalStateException(
+ "failed to initialize activity with running rtp session. session not found");
}
initializeWithTerminatedSessionState(account, with, terminatedRtpSession);
return true;
@@ 628,7 687,6 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
final RtpEndUserState currentState = requireRtpConnection().getEndUserState();
final boolean verified = requireRtpConnection().isVerified();
if (currentState == RtpEndUserState.ENDED) {
- reference.get().throwStateTransitionException();
finish();
return true;
}
@@ 636,21 694,24 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
if (currentState == RtpEndUserState.INCOMING_CALL) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
- if (JingleRtpConnection.STATES_SHOWING_ONGOING_CALL.contains(requireRtpConnection().getState())) {
+ if (JingleRtpConnection.STATES_SHOWING_ONGOING_CALL.contains(
+ requireRtpConnection().getState())) {
putScreenInCallMode();
}
- binding.with.setText(getWith().getDisplayName());
- binding.withJid.setText(with.asBareJid());
+ setWith();
updateVideoViews(currentState);
updateStateDisplay(currentState, media);
updateVerifiedShield(verified && STATES_SHOWING_SWITCH_TO_CHAT.contains(currentState));
updateButtonConfiguration(currentState, media);
- updateProfilePicture(currentState);
+ updateIncomingCallScreen(currentState);
invalidateOptionsMenu();
return false;
}
- private void initializeWithTerminatedSessionState(final Account account, final Jid with, final JingleConnectionManager.TerminatedRtpSession terminatedRtpSession) {
+ private void initializeWithTerminatedSessionState(
+ final Account account,
+ final Jid with,
+ final JingleConnectionManager.TerminatedRtpSession terminatedRtpSession) {
Log.d(Config.LOGTAG, "initializeWithTerminatedSessionState()");
if (terminatedRtpSession.state == RtpEndUserState.ENDED) {
finish();
@@ 660,15 721,15 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
resetIntent(account, with, terminatedRtpSession.state, terminatedRtpSession.media);
updateButtonConfiguration(state);
updateStateDisplay(state);
- updateProfilePicture(state);
+ updateIncomingCallScreen(state);
updateCallDuration();
updateVerifiedShield(false);
invalidateOptionsMenu();
- binding.with.setText(account.getRoster().getContact(with).getDisplayName());
- binding.withJid.setText(with.asBareJid());
+ setWith(account.getRoster().getContact(with));
}
- private void reInitializeActivityWithRunningRtpSession(final Account account, Jid with, String sessionId) {
+ private void reInitializeActivityWithRunningRtpSession(
+ final Account account, Jid with, String sessionId) {
runOnUiThread(() -> initializeActivityWithRunningRtpSession(account, with, sessionId));
resetIntent(account, with, sessionId);
}
@@ 686,7 747,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
try {
surfaceViewRenderer.init(requireRtpConnection().getEglBaseContext(), null);
} catch (final IllegalStateException e) {
- //Log.d(Config.LOGTAG, "SurfaceViewRenderer was already initialized");
+ // Log.d(Config.LOGTAG, "SurfaceViewRenderer was already initialized");
}
surfaceViewRenderer.setEnableHardwareScaler(true);
}
@@ 745,9 806,11 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
setTitle(R.string.rtp_state_security_error);
break;
case ENDED:
- throw new IllegalStateException("Activity should have called finishAndReleaseWakeLock();");
+ throw new IllegalStateException(
+ "Activity should have called finishAndReleaseWakeLock();");
default:
- throw new IllegalStateException(String.format("State %s has not been handled in UI", state));
+ throw new IllegalStateException(
+ String.format("State %s has not been handled in UI", state));
}
}
@@ 759,24 822,33 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
this.binding.verified.setVisibility(verified ? View.VISIBLE : View.GONE);
}
- private void updateProfilePicture(final RtpEndUserState state) {
- updateProfilePicture(state, null);
+ private void updateIncomingCallScreen(final RtpEndUserState state) {
+ updateIncomingCallScreen(state, null);
}
- private void updateProfilePicture(final RtpEndUserState state, final Contact contact) {
+ private void updateIncomingCallScreen(final RtpEndUserState state, final Contact contact) {
if (state == RtpEndUserState.INCOMING_CALL || state == RtpEndUserState.ACCEPTING_CALL) {
final boolean show = getResources().getBoolean(R.bool.show_avatar_incoming_call);
if (show) {
binding.contactPhoto.setVisibility(View.VISIBLE);
if (contact == null) {
- AvatarWorkerTask.loadAvatar(getWith(), binding.contactPhoto, R.dimen.publish_avatar_size);
+ AvatarWorkerTask.loadAvatar(
+ getWith(), binding.contactPhoto, R.dimen.publish_avatar_size);
} else {
- AvatarWorkerTask.loadAvatar(contact, binding.contactPhoto, R.dimen.publish_avatar_size);
+ AvatarWorkerTask.loadAvatar(
+ contact, binding.contactPhoto, R.dimen.publish_avatar_size);
}
} else {
binding.contactPhoto.setVisibility(View.GONE);
}
+ final Account account = contact == null ? getWith().getAccount() : contact.getAccount();
+ binding.usingAccount.setVisibility(View.VISIBLE);
+ binding.usingAccount.setText(
+ getString(
+ R.string.using_account,
+ account.getJid().asBareJid().toEscapedString()));
} else {
+ binding.usingAccount.setVisibility(View.GONE);
binding.contactPhoto.setVisibility(View.GONE);
}
}
@@ 816,12 888,12 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
this.binding.acceptCall.setImageResource(R.drawable.ic_voicemail_white_24dp);
this.binding.acceptCall.setVisibility(View.VISIBLE);
} else if (asList(
- RtpEndUserState.CONNECTIVITY_ERROR,
- RtpEndUserState.CONNECTIVITY_LOST_ERROR,
- RtpEndUserState.APPLICATION_ERROR,
- RtpEndUserState.RETRACTED,
- RtpEndUserState.SECURITY_ERROR
- ).contains(state)) {
+ RtpEndUserState.CONNECTIVITY_ERROR,
+ RtpEndUserState.CONNECTIVITY_LOST_ERROR,
+ RtpEndUserState.APPLICATION_ERROR,
+ RtpEndUserState.RETRACTED,
+ RtpEndUserState.SECURITY_ERROR)
+ .contains(state)) {
this.binding.rejectCall.setContentDescription(getString(R.string.exit));
this.binding.rejectCall.setOnClickListener(this::exit);
this.binding.rejectCall.setImageResource(R.drawable.ic_clear_white_48dp);
@@ 851,26 923,29 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
private void updateInCallButtonConfiguration() {
- updateInCallButtonConfiguration(requireRtpConnection().getEndUserState(), requireRtpConnection().getMedia());
+ updateInCallButtonConfiguration(
+ requireRtpConnection().getEndUserState(), requireRtpConnection().getMedia());
}
@SuppressLint("RestrictedApi")
- private void updateInCallButtonConfiguration(final RtpEndUserState state, final Set<Media> media) {
+ private void updateInCallButtonConfiguration(
+ final RtpEndUserState state, final Set<Media> media) {
if (STATES_CONSIDERED_CONNECTED.contains(state) && !isPictureInPicture()) {
Preconditions.checkArgument(media.size() > 0, "Media must not be empty");
if (media.contains(Media.VIDEO)) {
final JingleRtpConnection rtpConnection = requireRtpConnection();
- updateInCallButtonConfigurationVideo(rtpConnection.isVideoEnabled(), rtpConnection.isCameraSwitchable());
+ updateInCallButtonConfigurationVideo(
+ rtpConnection.isVideoEnabled(), rtpConnection.isCameraSwitchable());
} else {
final AppRTCAudioManager audioManager = requireRtpConnection().getAudioManager();
updateInCallButtonConfigurationSpeaker(
audioManager.getSelectedAudioDevice(),
- audioManager.getAudioDevices().size()
- );
+ audioManager.getAudioDevices().size());
this.binding.inCallActionFarRight.setVisibility(View.GONE);
}
if (media.contains(Media.AUDIO)) {
- updateInCallButtonConfigurationMicrophone(requireRtpConnection().isMicrophoneEnabled());
+ updateInCallButtonConfigurationMicrophone(
+ requireRtpConnection().isMicrophoneEnabled());
} else {
this.binding.inCallActionLeft.setVisibility(View.GONE);
}
@@ 882,10 957,12 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
@SuppressLint("RestrictedApi")
- private void updateInCallButtonConfigurationSpeaker(final AppRTCAudioManager.AudioDevice selectedAudioDevice, final int numberOfChoices) {
+ private void updateInCallButtonConfigurationSpeaker(
+ final AppRTCAudioManager.AudioDevice selectedAudioDevice, final int numberOfChoices) {
switch (selectedAudioDevice) {
case EARPIECE:
- this.binding.inCallActionRight.setImageResource(R.drawable.ic_volume_off_black_24dp);
+ this.binding.inCallActionRight.setImageResource(
+ R.drawable.ic_volume_off_black_24dp);
if (numberOfChoices >= 2) {
this.binding.inCallActionRight.setOnClickListener(this::switchToSpeaker);
} else {
@@ 908,7 985,8 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
break;
case BLUETOOTH:
- this.binding.inCallActionRight.setImageResource(R.drawable.ic_bluetooth_audio_black_24dp);
+ this.binding.inCallActionRight.setImageResource(
+ R.drawable.ic_bluetooth_audio_black_24dp);
this.binding.inCallActionRight.setOnClickListener(null);
this.binding.inCallActionRight.setClickable(false);
break;
@@ 917,10 995,12 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
@SuppressLint("RestrictedApi")
- private void updateInCallButtonConfigurationVideo(final boolean videoEnabled, final boolean isCameraSwitchable) {
+ private void updateInCallButtonConfigurationVideo(
+ final boolean videoEnabled, final boolean isCameraSwitchable) {
this.binding.inCallActionRight.setVisibility(View.VISIBLE);
if (isCameraSwitchable) {
- this.binding.inCallActionFarRight.setImageResource(R.drawable.ic_flip_camera_android_black_24dp);
+ this.binding.inCallActionFarRight.setImageResource(
+ R.drawable.ic_flip_camera_android_black_24dp);
this.binding.inCallActionFarRight.setVisibility(View.VISIBLE);
this.binding.inCallActionFarRight.setOnClickListener(this::switchCamera);
} else {
@@ 936,18 1016,28 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
private void switchCamera(final View view) {
- Futures.addCallback(requireRtpConnection().switchCamera(), new FutureCallback<Boolean>() {
- @Override
- public void onSuccess(@NullableDecl Boolean isFrontCamera) {
- binding.localVideo.setMirror(isFrontCamera);
- }
-
- @Override
- public void onFailure(@NonNull final Throwable throwable) {
- Log.d(Config.LOGTAG, "could not switch camera", Throwables.getRootCause(throwable));
- Toast.makeText(RtpSessionActivity.this, R.string.could_not_switch_camera, Toast.LENGTH_LONG).show();
- }
- }, MainThreadExecutor.getInstance());
+ Futures.addCallback(
+ requireRtpConnection().switchCamera(),
+ new FutureCallback<Boolean>() {
+ @Override
+ public void onSuccess(@NullableDecl Boolean isFrontCamera) {
+ binding.localVideo.setMirror(isFrontCamera);
+ }
+
+ @Override
+ public void onFailure(@NonNull final Throwable throwable) {
+ Log.d(
+ Config.LOGTAG,
+ "could not switch camera",
+ Throwables.getRootCause(throwable));
+ Toast.makeText(
+ RtpSessionActivity.this,
+ R.string.could_not_switch_camera,
+ Toast.LENGTH_LONG)
+ .show();
+ }
+ },
+ MainThreadExecutor.getInstance());
}
private void enableVideo(View view) {
@@ 963,7 1053,6 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
private void disableVideo(View view) {
requireRtpConnection().setVideoEnabled(false);
updateInCallButtonConfigurationVideo(false, requireRtpConnection().isCameraSwitchable());
-
}
@SuppressLint("RestrictedApi")
@@ 979,7 1068,8 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
private void updateCallDuration() {
- final JingleRtpConnection connection = this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null;
+ final JingleRtpConnection connection =
+ this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null;
if (connection == null || connection.getMedia().contains(Media.VIDEO)) {
this.binding.duration.setVisibility(View.GONE);
return;
@@ 987,7 1077,8 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
if (connection.zeroDuration()) {
this.binding.duration.setVisibility(View.GONE);
} else {
- this.binding.duration.setText(TimeFrameUtils.formatElapsedTime(connection.getCallDuration(), false));
+ this.binding.duration.setText(
+ TimeFrameUtils.formatElapsedTime(connection.getCallDuration(), false));
this.binding.duration.setVisibility(View.VISIBLE);
}
}
@@ 1003,9 1094,9 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
binding.appBarLayout.setVisibility(View.GONE);
binding.pipPlaceholder.setVisibility(View.VISIBLE);
if (Arrays.asList(
- RtpEndUserState.APPLICATION_ERROR,
- RtpEndUserState.CONNECTIVITY_ERROR,
- RtpEndUserState.SECURITY_ERROR)
+ RtpEndUserState.APPLICATION_ERROR,
+ RtpEndUserState.CONNECTIVITY_ERROR,
+ RtpEndUserState.SECURITY_ERROR)
.contains(state)) {
binding.pipWarning.setVisibility(View.VISIBLE);
binding.pipWaiting.setVisibility(View.GONE);
@@ 1033,7 1124,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
final Optional<VideoTrack> localVideoTrack = getLocalVideoTrack();
if (localVideoTrack.isPresent() && !isPictureInPicture()) {
ensureSurfaceViewRendererIsSetup(binding.localVideo);
- //paint local view over remote view
+ // paint local view over remote view
binding.localVideo.setZOrderMediaOverlay(true);
binding.localVideo.setMirror(requireRtpConnection().isFrontCamera());
addSink(localVideoTrack.get(), binding.localVideo);
@@ 1046,8 1137,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
addSink(remoteVideoTrack.get(), binding.remoteVideo);
binding.remoteVideo.setScalingType(
RendererCommon.ScalingType.SCALE_ASPECT_FILL,
- RendererCommon.ScalingType.SCALE_ASPECT_FIT
- );
+ RendererCommon.ScalingType.SCALE_ASPECT_FIT);
if (state == RtpEndUserState.CONNECTED) {
binding.appBarLayout.setVisibility(View.GONE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
@@ 1070,7 1160,8 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
private Optional<VideoTrack> getLocalVideoTrack() {
- final JingleRtpConnection connection = this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null;
+ final JingleRtpConnection connection =
+ this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null;
if (connection == null) {
return Optional.absent();
}
@@ 1078,7 1169,8 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
private Optional<VideoTrack> getRemoteVideoTrack() {
- final JingleRtpConnection connection = this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null;
+ final JingleRtpConnection connection =
+ this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null;
if (connection == null) {
return Optional.absent();
}
@@ 1100,12 1192,16 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
private void switchToEarpiece(View view) {
- requireRtpConnection().getAudioManager().setDefaultAudioDevice(AppRTCAudioManager.AudioDevice.EARPIECE);
+ requireRtpConnection()
+ .getAudioManager()
+ .setDefaultAudioDevice(AppRTCAudioManager.AudioDevice.EARPIECE);
acquireProximityWakeLock();
}
private void switchToSpeaker(View view) {
- requireRtpConnection().getAudioManager().setDefaultAudioDevice(AppRTCAudioManager.AudioDevice.SPEAKER_PHONE);
+ requireRtpConnection()
+ .getAudioManager()
+ .setDefaultAudioDevice(AppRTCAudioManager.AudioDevice.SPEAKER_PHONE);
releaseProximityWakeLock();
}
@@ 1129,12 1225,15 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
final Intent intent = getIntent();
final Account account = extractAccount(intent);
final Jid with = Jid.ofEscaped(intent.getStringExtra(EXTRA_WITH));
- final Conversation conversation = xmppConnectionService.findOrCreateConversation(account, with, false, true);
+ final Conversation conversation =
+ xmppConnectionService.findOrCreateConversation(account, with, false, true);
final Intent launchIntent = new Intent(this, ConversationsActivity.class);
launchIntent.setAction(ConversationsActivity.ACTION_VIEW_CONVERSATION);
launchIntent.putExtra(ConversationsActivity.EXTRA_CONVERSATION, conversation.getUuid());
launchIntent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- launchIntent.putExtra(ConversationsActivity.EXTRA_POST_INIT_ACTION, ConversationsActivity.POST_ACTION_RECORD_VOICE);
+ launchIntent.putExtra(
+ ConversationsActivity.EXTRA_POST_INIT_ACTION,
+ ConversationsActivity.POST_ACTION_RECORD_VOICE);
startActivity(launchIntent);
finish();
}
@@ 1146,7 1245,8 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
private JingleRtpConnection requireRtpConnection() {
- final JingleRtpConnection connection = this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null;
+ final JingleRtpConnection connection =
+ this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null;
if (connection == null) {
throw new IllegalStateException("No RTP connection found");
}
@@ 1154,12 1254,14 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
@Override
- public void onJingleRtpConnectionUpdate(Account account, Jid with, final String sessionId, RtpEndUserState state) {
+ public void onJingleRtpConnectionUpdate(
+ Account account, Jid with, final String sessionId, RtpEndUserState state) {
Log.d(Config.LOGTAG, "onJingleRtpConnectionUpdate(" + state + ")");
if (END_CARD.contains(state)) {
Log.d(Config.LOGTAG, "end card reached");
releaseProximityWakeLock();
- runOnUiThread(() -> getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON));
+ runOnUiThread(
+ () -> getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON));
}
if (with.isBareJid()) {
updateRtpSessionProposalState(account, with, state);
@@ 1170,7 1272,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
Log.d(Config.LOGTAG, "not reinitializing session");
return;
}
- //this happens when going from proposed session to actual session
+ // this happens when going from proposed session to actual session
reInitializeActivityWithRunningRtpSession(account, with, sessionId);
return;
}
@@ 1183,14 1285,16 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
finish();
return;
}
- runOnUiThread(() -> {
- updateStateDisplay(state, media);
- updateVerifiedShield(verified && STATES_SHOWING_SWITCH_TO_CHAT.contains(state));
- updateButtonConfiguration(state, media);
- updateVideoViews(state);
- updateProfilePicture(state, contact);
- invalidateOptionsMenu();
- });
+ runOnUiThread(
+ () -> {
+ updateStateDisplay(state, media);
+ updateVerifiedShield(
+ verified && STATES_SHOWING_SWITCH_TO_CHAT.contains(state));
+ updateButtonConfiguration(state, media);
+ updateVideoViews(