Ensure images sent to stories respect media quality settings.

Stories should always use "Standard" quality, not L3 (high quality). This change ensures that we:

1. Always send stories at the appropriate quality
2. Do not corrupt or overwrite pre-existing image attachments
3. Close several streams when done (thanks StrictMode!)
This commit is contained in:
Alex Hart
2022-07-13 12:48:17 -03:00
committed by Cody Henthorne
parent c4bef8099f
commit b18542a839
20 changed files with 384 additions and 79 deletions

View File

@@ -51,6 +51,7 @@ import org.thoughtcrime.securesms.components.ThumbnailView;
import org.thoughtcrime.securesms.components.location.SignalMapView;
import org.thoughtcrime.securesms.components.location.SignalPlace;
import org.thoughtcrime.securesms.conversation.MessageSendType;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.giph.ui.GiphyActivity;
import org.thoughtcrime.securesms.maps.PlacePickerActivity;
import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionActivity;
@@ -316,7 +317,7 @@ public class AttachmentManager {
}
Log.d(TAG, "remote slide with size " + fileSize + " took " + (System.currentTimeMillis() - start) + "ms");
return mediaType.createSlide(context, uri, fileName, mimeType, null, fileSize, width, height, false);
return mediaType.createSlide(context, uri, fileName, mimeType, null, fileSize, width, height, false, null);
}
} finally {
if (cursor != null) cursor.close();
@@ -326,17 +327,19 @@ public class AttachmentManager {
}
private @NonNull Slide getManuallyCalculatedSlideInfo(Uri uri, int width, int height) throws IOException {
long start = System.currentTimeMillis();
Long mediaSize = null;
String fileName = null;
String mimeType = null;
boolean gif = false;
long start = System.currentTimeMillis();
Long mediaSize = null;
String fileName = null;
String mimeType = null;
boolean gif = false;
AttachmentDatabase.TransformProperties transformProperties = null;
if (PartAuthority.isLocalUri(uri)) {
mediaSize = PartAuthority.getAttachmentSize(context, uri);
fileName = PartAuthority.getAttachmentFileName(context, uri);
mimeType = PartAuthority.getAttachmentContentType(context, uri);
gif = PartAuthority.getAttachmentIsVideoGif(context, uri);
mediaSize = PartAuthority.getAttachmentSize(context, uri);
fileName = PartAuthority.getAttachmentFileName(context, uri);
mimeType = PartAuthority.getAttachmentContentType(context, uri);
gif = PartAuthority.getAttachmentIsVideoGif(context, uri);
transformProperties = PartAuthority.getAttachmentTransformProperties(uri);
}
if (mediaSize == null) {
@@ -354,7 +357,7 @@ public class AttachmentManager {
}
Log.d(TAG, "local slide with size " + mediaSize + " took " + (System.currentTimeMillis() - start) + "ms");
return mediaType.createSlide(context, uri, fileName, mimeType, null, mediaSize, width, height, gif);
return mediaType.createSlide(context, uri, fileName, mimeType, null, mediaSize, width, height, gif, transformProperties);
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

View File

@@ -39,6 +39,10 @@ public abstract class MediaConstraints {
public abstract int getImageMaxHeight(Context context);
public abstract int getImageMaxSize(Context context);
public boolean isHighQuality() {
return false;
}
/**
* Provide a list of dimensions that should be attempted during compression. We will keep moving
* down the list until the image can be scaled to fit under {@link #getImageMaxSize(Context)}.

View File

@@ -12,6 +12,7 @@ import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.AttachmentId;
import org.thoughtcrime.securesms.avatar.AvatarPickerStorage;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.emoji.EmojiFiles;
import org.thoughtcrime.securesms.providers.BlobProvider;
@@ -152,6 +153,16 @@ public class PartAuthority {
}
}
public static @Nullable AttachmentDatabase.TransformProperties getAttachmentTransformProperties(@NonNull Uri uri) {
int match = uriMatcher.match(uri);
switch (match) {
case PART_ROW:
return SignalDatabase.attachments().getTransformProperties(new PartUriParser(uri).getPartId());
default:
return null;
}
}
public static Uri getAttachmentPublicUri(Uri uri) {
PartUriParser partUri = new PartUriParser(uri);
return PartProvider.getContentUri(partUri.getPartId());

View File

@@ -23,6 +23,11 @@ public class PushMediaConstraints extends MediaConstraints {
currentConfig = getCurrentConfig(ApplicationDependencies.getApplication(), sentMediaQuality);
}
@Override
public boolean isHighQuality() {
return currentConfig == MediaConfig.LEVEL_3;
}
@Override
public int getImageMaxWidth(Context context) {
return currentConfig.imageSizeTargets[0];

View File

@@ -13,6 +13,7 @@ import androidx.annotation.WorkerThread;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.blurhash.BlurHash;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.util.MediaUtil;
import java.io.IOException;
@@ -31,25 +32,25 @@ public final class SlideFactory {
/**
* Generates a slide from the given parameters.
*
* @param context Application context
* @param contentType The contentType of the given Uri
* @param uri The Uri pointing to the resource to create a slide out of
* @param width (Optional) width, can be 0.
* @param height (Optional) height, can be 0.
* @param context Application context
* @param contentType The contentType of the given Uri
* @param uri The Uri pointing to the resource to create a slide out of
* @param width (Optional) width, can be 0.
* @param height (Optional) height, can be 0.
* @param transformProperties (Optional) transformProperties, can be 0.
*
* @return A Slide with all the information we can gather about it.
*/
@WorkerThread
public static @Nullable Slide getSlide(@NonNull Context context, @Nullable String contentType, @NonNull Uri uri, int width, int height) {
public static @Nullable Slide getSlide(@NonNull Context context, @Nullable String contentType, @NonNull Uri uri, int width, int height, @Nullable AttachmentDatabase.TransformProperties transformProperties) {
MediaType mediaType = MediaType.from(contentType);
try {
if (PartAuthority.isLocalUri(uri)) {
return getManuallyCalculatedSlideInfo(context, mediaType, uri, width, height);
return getManuallyCalculatedSlideInfo(context, mediaType, uri, width, height, transformProperties);
} else {
Slide result = getContentResolverSlideInfo(context, mediaType, uri, width, height);
Slide result = getContentResolverSlideInfo(context, mediaType, uri, width, height, transformProperties);
if (result == null) return getManuallyCalculatedSlideInfo(context, mediaType, uri, width, height);
if (result == null) return getManuallyCalculatedSlideInfo(context, mediaType, uri, width, height, transformProperties);
else return result;
}
} catch (IOException e) {
@@ -58,7 +59,14 @@ public final class SlideFactory {
}
}
private static @Nullable Slide getContentResolverSlideInfo(@NonNull Context context, @Nullable MediaType mediaType, @NonNull Uri uri, int width, int height) {
private static @Nullable Slide getContentResolverSlideInfo(
@NonNull Context context,
@Nullable MediaType mediaType,
@NonNull Uri uri,
int width,
int height,
@Nullable AttachmentDatabase.TransformProperties transformProperties
) {
long start = System.currentTimeMillis();
try (Cursor cursor = context.getContentResolver().query(uri, null, null, null, null)) {
@@ -78,14 +86,22 @@ public final class SlideFactory {
}
Log.d(TAG, "remote slide with size " + fileSize + " took " + (System.currentTimeMillis() - start) + "ms");
return mediaType.createSlide(context, uri, fileName, mimeType, null, fileSize, width, height, false);
return mediaType.createSlide(context, uri, fileName, mimeType, null, fileSize, width, height, false, transformProperties);
}
}
return null;
}
private static @NonNull Slide getManuallyCalculatedSlideInfo(@NonNull Context context, @Nullable MediaType mediaType, @NonNull Uri uri, int width, int height) throws IOException {
private static @NonNull Slide getManuallyCalculatedSlideInfo(
@NonNull Context context,
@Nullable MediaType mediaType,
@NonNull Uri uri,
int width,
int height,
@Nullable AttachmentDatabase.TransformProperties transformProperties
) throws IOException
{
long start = System.currentTimeMillis();
Long mediaSize = null;
String fileName = null;
@@ -118,7 +134,7 @@ public final class SlideFactory {
}
Log.d(TAG, "local slide with size " + mediaSize + " took " + (System.currentTimeMillis() - start) + "ms");
return mediaType.createSlide(context, uri, fileName, mimeType, null, mediaSize, width, height, gif);
return mediaType.createSlide(context, uri, fileName, mimeType, null, mediaSize, width, height, gif, transformProperties);
}
public enum MediaType {
@@ -142,17 +158,18 @@ public final class SlideFactory {
@Nullable String fileName,
@Nullable String mimeType,
@Nullable BlurHash blurHash,
long dataSize,
int width,
int height,
boolean gif)
long dataSize,
int width,
int height,
boolean gif,
@Nullable AttachmentDatabase.TransformProperties transformProperties)
{
if (mimeType == null) {
mimeType = "application/octet-stream";
}
switch (this) {
case IMAGE: return new ImageSlide(context, uri, dataSize, width, height, blurHash);
case IMAGE: return new ImageSlide(context, uri, mimeType, dataSize, width, height, false, null, blurHash, transformProperties);
case GIF: return new GifSlide(context, uri, dataSize, width, height);
case AUDIO: return new AudioSlide(context, uri, dataSize, false);
case VIDEO: return new VideoSlide(context, uri, dataSize, gif);