diff --git a/build.gradle b/build.gradle index f8826dd67..d91a6eba5 100644 --- a/build.gradle +++ b/build.gradle @@ -3,10 +3,10 @@ buildscript { repositories { google() - jcenter() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.2.2' + classpath 'com.android.tools.build:gradle:7.0.2' } } @@ -33,11 +33,11 @@ repositories { configurations { playstoreImplementation gitImplementation - compile.exclude group: 'org.jetbrains' , module:'annotations' + implementation.exclude group: 'org.jetbrains' , module:'annotations' } dependencies { - implementation 'org.webrtc:google-webrtc:1.0.32006' + implementation 'org.webrtc:google-webrtc:1.+' implementation project(':libs:android-transcoder') playstoreImplementation('com.google.firebase:firebase-messaging:22.0.0') { ///higher versions are causing crashes due to missing project IDs exclude group: 'com.google.firebase', module: 'firebase-core' @@ -63,7 +63,7 @@ dependencies { implementation 'androidx.multidex:multidex:2.0.1' implementation 'androidx.legacy:legacy-support-v13:1.0.0' implementation 'androidx.appcompat:appcompat:1.2.0' - implementation 'androidx.exifinterface:exifinterface:1.3.2' + implementation 'androidx.exifinterface:exifinterface:1.3.3' implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.emoji:emoji:1.1.0' gitImplementation 'androidx.emoji:emoji-appcompat:1.1.0' @@ -112,8 +112,8 @@ android { targetSdkVersion 29 //bversionNameSuffix " beta_(2021-06-28)" // " beta_(XXXX-XX-XX)" // activate for beta versions - versionCode 104 - versionName "1.3.1" + versionCode 105 + versionName "1.3.2" //resConfigs "en" archivesBaseName += "-$versionName" @@ -145,13 +145,6 @@ android { //exclude "lib/armeabi/**" } - dexOptions { - // Skip pre-dexing when running on Travis CI or when disabled via -Dpre-dex=false. - preDexLibraries = preDexEnabled && !travisBuild - javaMaxHeapSize "4g" - jumboMode true - } - compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 diff --git a/fastlane/metadata/android/de/changelogs/105.txt b/fastlane/metadata/android/de/changelogs/105.txt new file mode 100644 index 000000000..503fb7de6 --- /dev/null +++ b/fastlane/metadata/android/de/changelogs/105.txt @@ -0,0 +1,8 @@ +* Vermeiden von Wiederholung in der Beschreibung zu Screenshots +* dexOptionen wird nicht mehr in agp7 verwendet +* stoppe agp7 Beschwerde über fehlende Proguard-Regeln +* PIP-Aspektverhältnis soll mit Video-Aspektverhältnis übereinstimmen +* Kehre "Always show Quote as last action" um +* Verwende androidx ExifInterface zur Parse Rotation +* standardmäßig "auto akzeptieren von Dateien" zu "Niemals" für Wi-Fi und mobile Datenverbindung + diff --git a/fastlane/metadata/android/en-US/changelogs/105.txt b/fastlane/metadata/android/en-US/changelogs/105.txt new file mode 100644 index 000000000..e33a9470a --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/105.txt @@ -0,0 +1,7 @@ +* Avoid description repetition screenshots +* dexOptions is no longer used in agp7 +* stop agp7 complaining about missing proguard rules +* PIP aspect ratio should match video aspect ratio +* Revert "Always show Quote as last action" +* use androidx ExifInterface to parse rotation +* default "auto accept files" to Never for wifi and mobile data connection diff --git a/fastlane/metadata/android/en-US/icon.png b/fastlane/metadata/android/en-US/icon.png new file mode 100644 index 000000000..609d27692 Binary files /dev/null and b/fastlane/metadata/android/en-US/icon.png differ diff --git a/fastlane/metadata/android/en-US/images/featureGraphic.png b/fastlane/metadata/android/en-US/images/featureGraphic.png new file mode 100644 index 000000000..fdd80f956 Binary files /dev/null and b/fastlane/metadata/android/en-US/images/featureGraphic.png differ diff --git a/fastlane/metadata/android/en-US/images/icon.png b/fastlane/metadata/android/en-US/images/icon.png new file mode 100644 index 000000000..609d27692 Binary files /dev/null and b/fastlane/metadata/android/en-US/images/icon.png differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index da9702f9e..90f60f340 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Sun Sep 19 02:58:45 CEST 2021 distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip -zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/proguard-rules.pro b/proguard-rules.pro index efb765a26..30b755906 100644 --- a/proguard-rules.pro +++ b/proguard-rules.pro @@ -29,6 +29,16 @@ -dontwarn java.lang.** -dontwarn javax.lang.** +-dontwarn com.android.org.conscrypt.SSLParametersImpl +-dontwarn org.apache.harmony.xnet.provider.jsse.SSLParametersImpl +-dontwarn org.bouncycastle.jsse.BCSSLParameters +-dontwarn org.bouncycastle.jsse.BCSSLSocket +-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider +-dontwarn org.openjsse.javax.net.ssl.SSLParameters +-dontwarn org.openjsse.javax.net.ssl.SSLSocket +-dontwarn org.openjsse.net.ssl.OpenJSSE + + -keepclassmembers class eu.siacs.conversations.http.services.** { !transient ; } diff --git a/src/main/java/eu/siacs/conversations/entities/Account.java b/src/main/java/eu/siacs/conversations/entities/Account.java index a106f8d37..c471cabf3 100644 --- a/src/main/java/eu/siacs/conversations/entities/Account.java +++ b/src/main/java/eu/siacs/conversations/entities/Account.java @@ -5,6 +5,8 @@ import android.database.Cursor; import android.os.SystemClock; import android.util.Log; +import com.google.common.base.Strings; + import org.json.JSONException; import org.json.JSONObject; @@ -248,7 +250,7 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable } public String getHostname() { - return this.hostname == null ? "" : this.hostname; + return Strings.nullToEmpty(this.hostname); } public void setHostname(String hostname) { diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java index 13334e39d..1361f689e 100644 --- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java @@ -35,6 +35,8 @@ import android.util.DisplayMetrics; import android.util.Log; import android.util.LruCache; +import androidx.exifinterface.media.ExifInterface; + import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.annotation.StringRes; @@ -76,7 +78,6 @@ import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.ui.util.Attachment; import eu.siacs.conversations.utils.Compatibility; import eu.siacs.conversations.utils.CryptoHelper; -import eu.siacs.conversations.utils.ExifHelper; import eu.siacs.conversations.utils.FileUtils; import eu.siacs.conversations.utils.FileWriterException; import eu.siacs.conversations.utils.MimeUtils; @@ -805,19 +806,34 @@ public class FileBackend { } } - private int getRotation(File file) { - return getRotation(Uri.parse("file://" + file.getAbsolutePath())); + private int getRotation(final File file) { + try (final InputStream inputStream = new FileInputStream(file)) { + return getRotation(inputStream); + } catch (Exception e) { + return 0; + } } - private int getRotation(Uri image) { - InputStream is = null; - try { - is = mXmppConnectionService.getContentResolver().openInputStream(image); - return ExifHelper.getOrientation(is); - } catch (FileNotFoundException e) { + private int getRotation(final Uri image) { + try (final InputStream is = mXmppConnectionService.getContentResolver().openInputStream(image)) { + return is == null ? 0 : getRotation(is); + } catch (final Exception e) { return 0; - } finally { - close(is); + } + } + + 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); + switch (orientation) { + case ExifInterface.ORIENTATION_ROTATE_180: + return 180; + case ExifInterface.ORIENTATION_ROTATE_90: + return 90; + case ExifInterface.ORIENTATION_ROTATE_270: + return 270; + default: + return 0; } } @@ -1773,7 +1789,8 @@ public class FileBackend { this.resId = resId; } - public @StringRes int getResId() { + public @StringRes + int getResId() { return resId; } } diff --git a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java index bbd3b2801..892069d53 100644 --- a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java @@ -1,5 +1,9 @@ package eu.siacs.conversations.ui; +import static java.util.Arrays.asList; +import static eu.siacs.conversations.utils.PermissionUtils.getFirstDenied; +import eu.siacs.conversations.ui.util.Rationals; + import android.Manifest; import android.annotation.SuppressLint; import android.app.PictureInPictureParams; @@ -63,10 +67,8 @@ import eu.siacs.conversations.xmpp.jingle.Media; import eu.siacs.conversations.xmpp.jingle.RtpEndUserState; import me.drakeet.support.toast.ToastCompat; -import static eu.siacs.conversations.utils.PermissionUtils.getFirstDenied; -import static java.util.Arrays.asList; -public class RtpSessionActivity extends XmppActivity implements XmppConnectionService.OnJingleRtpConnectionUpdate { +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"; @@ -446,12 +448,14 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe public void onStart() { super.onStart(); mHandler.postDelayed(mTickExecutor, CALL_DURATION_UPDATE_INTERVAL); + this.binding.remoteVideo.setOnAspectRatioChanged(this); } @Override public void onStop() { mHandler.removeCallbacks(mTickExecutor); binding.remoteVideo.release(); + binding.remoteVideo.setOnAspectRatioChanged(null); binding.localVideo.release(); final WeakReference weakReference = this.rtpConnectionReference; final JingleRtpConnection jingleRtpConnection = weakReference == null ? null : weakReference.get(); @@ -516,9 +520,12 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe @RequiresApi(api = Build.VERSION_CODES.O) private void startPictureInPicture() { try { + final Rational rational = this.binding.remoteVideo.getAspectRatio(); + final Rational clippedRational = Rationals.clip(rational); + Log.d(Config.LOGTAG, "suggested rational " + rational + ". clipped to " + clippedRational); enterPictureInPictureMode( new PictureInPictureParams.Builder() - .setAspectRatio(new Rational(10, 16)) + .setAspectRatio(clippedRational) .build() ); } catch (final IllegalStateException e) { @@ -526,6 +533,16 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe Log.w(Config.LOGTAG, "unable to enter picture in picture mode", e); } } + @Override + 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()); + } + } private boolean deviceSupportsPictureInPicture() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { diff --git a/src/main/java/eu/siacs/conversations/ui/util/Rationals.java b/src/main/java/eu/siacs/conversations/ui/util/Rationals.java new file mode 100644 index 000000000..bc469a013 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/ui/util/Rationals.java @@ -0,0 +1,26 @@ +package eu.siacs.conversations.ui.util; + +import android.util.Rational; + +public final class Rationals { + + //between 2.39:1 and 1:2.39 (inclusive). + private static final Rational MIN = new Rational(100,239); + private static final Rational MAX = new Rational(239,100); + + private Rationals() { + + } + + + public static Rational clip(final Rational input) { + if (input.compareTo(MIN) < 0) { + return MIN; + } + if (input.compareTo(MAX) > 0) { + return MAX; + } + return input; + } + +} \ No newline at end of file diff --git a/src/main/java/eu/siacs/conversations/ui/widget/SurfaceViewRenderer.java b/src/main/java/eu/siacs/conversations/ui/widget/SurfaceViewRenderer.java new file mode 100644 index 000000000..818cd60ef --- /dev/null +++ b/src/main/java/eu/siacs/conversations/ui/widget/SurfaceViewRenderer.java @@ -0,0 +1,48 @@ +package eu.siacs.conversations.ui.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.Log; +import android.util.Rational; + +import eu.siacs.conversations.Config; + +public class SurfaceViewRenderer extends org.webrtc.SurfaceViewRenderer { + + private Rational aspectRatio = new Rational(1,1); + + private OnAspectRatioChanged onAspectRatioChanged; + + public SurfaceViewRenderer(Context context) { + super(context); + } + + public SurfaceViewRenderer(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public void onFrameResolutionChanged(int videoWidth, int videoHeight, int rotation) { + super.onFrameResolutionChanged(videoWidth, videoHeight, rotation); + final int rotatedWidth = rotation != 0 && rotation != 180 ? videoHeight : videoWidth; + final int rotatedHeight = rotation != 0 && rotation != 180 ? videoWidth : videoHeight; + final Rational currentRational = this.aspectRatio; + this.aspectRatio = new Rational(rotatedWidth, rotatedHeight); + Log.d(Config.LOGTAG,"onFrameResolutionChanged("+rotatedWidth+","+rotatedHeight+","+aspectRatio+")"); + if (currentRational.equals(this.aspectRatio) || onAspectRatioChanged == null) { + return; + } + onAspectRatioChanged.onAspectRatioChanged(this.aspectRatio); + } + + public void setOnAspectRatioChanged(final OnAspectRatioChanged onAspectRatioChanged) { + this.onAspectRatioChanged = onAspectRatioChanged; + } + + public Rational getAspectRatio() { + return this.aspectRatio; + } + + public interface OnAspectRatioChanged { + void onAspectRatioChanged(final Rational rational); + } +} \ No newline at end of file diff --git a/src/main/res/layout/activity_rtp_session.xml b/src/main/res/layout/activity_rtp_session.xml index 0af72a372..c991b603b 100644 --- a/src/main/res/layout/activity_rtp_session.xml +++ b/src/main/res/layout/activity_rtp_session.xml @@ -98,7 +98,7 @@ - - + - true true true - 10485760 - 524288 + 0 + 0 0 0 0 diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index cce82f21f..cee7e37d0 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -1144,6 +1144,7 @@ The app you used to share this file did not provide enough permissions. Unable to enable video. Do you really want to cancel and delete your recording? + Hide app contents in the app switcher and block screenshots Pause voice message automatically Pause the current voice message when you move your device away from your ear. Plain text document