master
M66B 4 months ago
parent 887abb0c4b
commit 3faf7ed57d

@ -21,6 +21,7 @@ package eu.faircode.email;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.content.ClipData; import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@ -248,15 +249,17 @@ public class ActivityCompose extends ActivityBase implements FragmentManager.OnB
if (!TextUtils.isEmpty(html)) if (!TextUtils.isEmpty(html))
args.putString("body", html); args.putString("body", html);
ArrayList<Uri> uris = new ArrayList<>(); ArrayList<UriType> uris = new ArrayList<>();
ClipData clip = intent.getClipData(); ClipData clip = intent.getClipData();
ClipDescription description = (clip == null ? null : clip.getDescription());
if (clip != null) if (clip != null)
for (int i = 0; i < clip.getItemCount(); i++) { for (int i = 0; i < clip.getItemCount(); i++) {
ClipData.Item item = clip.getItemAt(i); ClipData.Item item = clip.getItemAt(i);
Uri stream = (item == null ? null : item.getUri()); Uri stream = (item == null ? null : item.getUri());
if (stream != null) if (stream != null)
uris.add(stream); uris.add(new UriType(stream,
description != null && i < description.getMimeTypeCount() ? description.getMimeType(i) : null));
} }
if (intent.hasExtra(Intent.EXTRA_STREAM)) { if (intent.hasExtra(Intent.EXTRA_STREAM)) {
@ -268,13 +271,13 @@ public class ActivityCompose extends ActivityBase implements FragmentManager.OnB
for (Uri stream : streams) for (Uri stream : streams)
if (stream != null) { if (stream != null) {
boolean found = false; boolean found = false;
for (Uri e : uris) for (UriType e : uris)
if (stream.equals(e)) { if (stream.equals(e.getUri())) {
found = true; found = true;
break; break;
} }
if (!found) if (!found)
uris.add(stream); uris.add(new UriType(stream, streams.size() == 1 ? intent.getType() : null));
} }
} }
} }

@ -580,7 +580,7 @@ public class EntityAnswer implements Serializable {
for (Uri file : attachments) for (Uri file : attachments)
try { try {
EntityAttachment attachment = new EntityAttachment(); EntityAttachment attachment = new EntityAttachment();
Helper.UriInfo info = Helper.getInfo(file, context); Helper.UriInfo info = Helper.getInfo(new UriType(file, null), context);
attachment.message = id; attachment.message = id;
attachment.sequence = db.attachment().getAttachmentSequence(id) + 1; attachment.sequence = db.attachment().getAttachmentSequence(id) + 1;

@ -28,6 +28,7 @@ import android.Manifest;
import android.app.Activity; import android.app.Activity;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.ClipData; import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
@ -392,7 +393,7 @@ public class FragmentCompose extends FragmentBase {
pickImages = pickImages =
registerForActivityResult(new ActivityResultContracts.PickMultipleVisualMedia(max), uris -> { registerForActivityResult(new ActivityResultContracts.PickMultipleVisualMedia(max), uris -> {
if (!uris.isEmpty()) if (!uris.isEmpty())
onAddImageFile(uris, false); onAddImageFile(UriType.getList(uris), false);
}); });
} }
@ -654,8 +655,7 @@ public class FragmentCompose extends FragmentBase {
int resize = prefs.getInt("resize", FragmentCompose.REDUCED_IMAGE_SIZE); int resize = prefs.getInt("resize", FragmentCompose.REDUCED_IMAGE_SIZE);
boolean resize_width_only = prefs.getBoolean("resize_width_only", false); boolean resize_width_only = prefs.getBoolean("resize_width_only", false);
onAddAttachment( onAddAttachment(
Arrays.asList(uri), Arrays.asList(new UriType(uri, type)),
type == null ? null : new String[]{type},
true, true,
resize_paste ? resize : 0, resize_paste ? resize : 0,
resize_width_only, resize_width_only,
@ -3382,13 +3382,13 @@ public class FragmentCompose extends FragmentBase {
case REQUEST_TAKE_PHOTO: case REQUEST_TAKE_PHOTO:
if (resultCode == RESULT_OK) { if (resultCode == RESULT_OK) {
if (photoURI != null) if (photoURI != null)
onAddImageFile(Arrays.asList(photoURI), false); onAddImageFile(Arrays.asList(new UriType(photoURI, null)), false);
} }
break; break;
case REQUEST_ATTACHMENT: case REQUEST_ATTACHMENT:
case REQUEST_RECORD_AUDIO: case REQUEST_RECORD_AUDIO:
if (resultCode == RESULT_OK && data != null) if (resultCode == RESULT_OK && data != null)
onAddAttachment(getUris(data), null, false, 0, false, false, false); onAddAttachment(getUris(data), false, 0, false, false, false);
break; break;
case REQUEST_OPENPGP: case REQUEST_OPENPGP:
if (resultCode == RESULT_OK && data != null) if (resultCode == RESULT_OK && data != null)
@ -3733,21 +3733,20 @@ public class FragmentCompose extends FragmentBase {
} }
} }
private void onAddImageFile(List<Uri> uri, boolean focus) { private void onAddImageFile(List<UriType> uri, boolean focus) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
boolean add_inline = prefs.getBoolean("add_inline", true); boolean add_inline = prefs.getBoolean("add_inline", true);
boolean resize_images = prefs.getBoolean("resize_images", true); boolean resize_images = prefs.getBoolean("resize_images", true);
boolean resize_width_only = prefs.getBoolean("resize_width_only", false); boolean resize_width_only = prefs.getBoolean("resize_width_only", false);
boolean privacy_images = prefs.getBoolean("privacy_images", false); boolean privacy_images = prefs.getBoolean("privacy_images", false);
int resize = prefs.getInt("resize", FragmentCompose.REDUCED_IMAGE_SIZE); int resize = prefs.getInt("resize", FragmentCompose.REDUCED_IMAGE_SIZE);
onAddAttachment(uri, null, add_inline, resize_images ? resize : 0, resize_width_only, privacy_images, focus); onAddAttachment(uri, add_inline, resize_images ? resize : 0, resize_width_only, privacy_images, focus);
} }
private void onAddAttachment(List<Uri> uris, String[] types, boolean image, int resize, boolean resize_width_only, boolean privacy, boolean focus) { private void onAddAttachment(List<UriType> uris, boolean image, int resize, boolean resize_width_only, boolean privacy, boolean focus) {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putLong("id", working); args.putLong("id", working);
args.putParcelableArrayList("uris", new ArrayList<>(uris)); args.putParcelableArrayList("uris", new ArrayList<>(uris));
args.putStringArray("types", types);
args.putBoolean("image", image); args.putBoolean("image", image);
args.putInt("resize", resize); args.putInt("resize", resize);
args.putBoolean("resize_width_only", resize_width_only); args.putBoolean("resize_width_only", resize_width_only);
@ -3761,8 +3760,7 @@ public class FragmentCompose extends FragmentBase {
@Override @Override
protected Spanned onExecute(Context context, Bundle args) throws IOException, SecurityException { protected Spanned onExecute(Context context, Bundle args) throws IOException, SecurityException {
final long id = args.getLong("id"); final long id = args.getLong("id");
List<Uri> uris = args.getParcelableArrayList("uris"); List<UriType> uris = args.getParcelableArrayList("uris");
String[] types = args.getStringArray("types");
boolean image = args.getBoolean("image"); boolean image = args.getBoolean("image");
int resize = args.getInt("resize"); int resize = args.getInt("resize");
boolean resize_width_only = args.getBoolean("resize_width_only"); boolean resize_width_only = args.getBoolean("resize_width_only");
@ -3779,10 +3777,9 @@ public class FragmentCompose extends FragmentBase {
start = s.length(); start = s.length();
for (int i = 0; i < uris.size(); i++) { for (int i = 0; i < uris.size(); i++) {
Uri uri = uris.get(i); UriType uri = uris.get(i);
String type = (types != null && i < types.length ? types[i] : null);
EntityAttachment attachment = addAttachment(context, id, uri, type, image, resize, resize_width_only, privacy); EntityAttachment attachment = addAttachment(context, id, uri, image, resize, resize_width_only, privacy);
if (attachment == null) if (attachment == null)
continue; continue;
if (!image || !attachment.isImage()) if (!image || !attachment.isImage())
@ -3884,25 +3881,25 @@ public class FragmentCompose extends FragmentBase {
}.serial().execute(this, args, "compose:attachment:add"); }.serial().execute(this, args, "compose:attachment:add");
} }
void onSharedAttachments(ArrayList<Uri> uris) { void onSharedAttachments(ArrayList<UriType> uris) {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putLong("id", working); args.putLong("id", working);
args.putParcelableArrayList("uris", uris); args.putParcelableArrayList("uris", uris);
new SimpleTask<ArrayList<Uri>>() { new SimpleTask<ArrayList<UriType>>() {
@Override @Override
protected ArrayList<Uri> onExecute(Context context, Bundle args) throws Throwable { protected ArrayList<UriType> onExecute(Context context, Bundle args) throws Throwable {
long id = args.getLong("id"); long id = args.getLong("id");
List<Uri> uris = args.getParcelableArrayList("uris"); List<UriType> uris = args.getParcelableArrayList("uris");
ArrayList<Uri> images = new ArrayList<>(); ArrayList<UriType> images = new ArrayList<>();
for (Uri uri : uris) for (UriType uri : uris)
try { try {
Helper.UriInfo info = Helper.getInfo(uri, context); Helper.UriInfo info = Helper.getInfo(uri, context);
if (info.isImage()) if (info.isImage())
images.add(uri); images.add(uri);
else else
addAttachment(context, id, uri, null, false, 0, false, false); addAttachment(context, id, uri, false, 0, false, false);
} catch (IOException ex) { } catch (IOException ex) {
Log.e(ex); Log.e(ex);
} }
@ -3911,7 +3908,7 @@ public class FragmentCompose extends FragmentBase {
} }
@Override @Override
protected void onExecuted(Bundle args, ArrayList<Uri> images) { protected void onExecuted(Bundle args, ArrayList<UriType> images) {
if (images.size() == 0) if (images.size() == 0)
return; return;
@ -3940,20 +3937,22 @@ public class FragmentCompose extends FragmentBase {
}.serial().execute(this, args, "compose:shared"); }.serial().execute(this, args, "compose:shared");
} }
private List<Uri> getUris(Intent data) { private List<UriType> getUris(Intent data) {
List<Uri> result = new ArrayList<>(); List<UriType> result = new ArrayList<>();
ClipData clipData = data.getClipData(); ClipData clipData = data.getClipData();
if (clipData == null) { if (clipData == null) {
Uri uri = data.getData(); Uri uri = data.getData();
if (uri != null) if (uri != null)
result.add(uri); result.add(new UriType(uri, null));
} else { } else {
ClipDescription description = clipData.getDescription();
for (int i = 0; i < clipData.getItemCount(); i++) { for (int i = 0; i < clipData.getItemCount(); i++) {
ClipData.Item item = clipData.getItemAt(i); ClipData.Item item = clipData.getItemAt(i);
Uri uri = item.getUri(); Uri uri = item.getUri();
if (uri != null) if (uri != null)
result.add(uri); result.add(new UriType(uri,
description != null && i < description.getMimeTypeCount() ? description.getMimeType(i) : null));
} }
} }
@ -3963,7 +3962,7 @@ public class FragmentCompose extends FragmentBase {
if (result.size() == 0 && data.hasExtra("media-uri-list")) if (result.size() == 0 && data.hasExtra("media-uri-list"))
try { try {
List<Uri> uris = data.getParcelableArrayListExtra("media-uri-list"); List<Uri> uris = data.getParcelableArrayListExtra("media-uri-list");
result.addAll(uris); result.addAll(UriType.getList(uris));
} catch (Throwable ex) { } catch (Throwable ex) {
Log.e(ex); Log.e(ex);
} }
@ -5377,25 +5376,22 @@ public class FragmentCompose extends FragmentBase {
} }
private static EntityAttachment addAttachment( private static EntityAttachment addAttachment(
Context context, long id, Uri uri, String type, boolean image, int resize, boolean resize_width_only, boolean privacy) throws IOException, SecurityException { Context context, long id, UriType uri, boolean image, int resize, boolean resize_width_only, boolean privacy) throws IOException, SecurityException {
Log.w("Add attachment uri=" + uri + " image=" + image + " resize=" + resize + "/" + resize_width_only + " privacy=" + privacy); Log.w("Add attachment uri=" + uri + " image=" + image + " resize=" + resize + "/" + resize_width_only + " privacy=" + privacy);
NoStreamException.check(uri, context); NoStreamException.check(uri.getUri(), context);
EntityAttachment attachment = new EntityAttachment(); EntityAttachment attachment = new EntityAttachment();
Helper.UriInfo info = Helper.getInfo(uri, context); Helper.UriInfo info = Helper.getInfo(uri, context);
EntityLog.log(context, "Add attachment" + EntityLog.log(context, "Add attachment" +
" uri=" + uri + " type=" + type + " image=" + image + " resize=" + resize + " privacy=" + privacy + " uri=" + uri + " image=" + image + " resize=" + resize + " privacy=" + privacy +
" name=" + info.name + " type=" + info.type + " size=" + info.size); " name=" + info.name + " type=" + info.type + " size=" + info.size);
if (type == null)
type = info.type;
String ext = Helper.getExtension(info.name); String ext = Helper.getExtension(info.name);
if (info.name != null && ext == null && type != null) { if (info.name != null && ext == null && uri.getType() != null) {
String guessed = MimeTypeMap.getSingleton() String guessed = MimeTypeMap.getSingleton()
.getExtensionFromMimeType(type.toLowerCase(Locale.ROOT)); .getExtensionFromMimeType(uri.getType().toLowerCase(Locale.ROOT));
if (!TextUtils.isEmpty(guessed)) { if (!TextUtils.isEmpty(guessed)) {
ext = guessed; ext = guessed;
info.name += '.' + ext; info.name += '.' + ext;
@ -5418,7 +5414,7 @@ public class FragmentCompose extends FragmentBase {
attachment.name = "img" + attachment.sequence + (ext == null ? "" : "." + ext); attachment.name = "img" + attachment.sequence + (ext == null ? "" : "." + ext);
else else
attachment.name = info.name; attachment.name = info.name;
attachment.type = type; attachment.type = info.type;
attachment.disposition = (image ? Part.INLINE : Part.ATTACHMENT); attachment.disposition = (image ? Part.INLINE : Part.ATTACHMENT);
attachment.size = info.size; attachment.size = info.size;
attachment.progress = 0; attachment.progress = 0;
@ -5439,7 +5435,7 @@ public class FragmentCompose extends FragmentBase {
InputStream is = null; InputStream is = null;
OutputStream os = null; OutputStream os = null;
try { try {
is = context.getContentResolver().openInputStream(uri); is = context.getContentResolver().openInputStream(uri.getUri());
os = new FileOutputStream(file); os = new FileOutputStream(file);
if (is == null) if (is == null)
@ -5494,15 +5490,15 @@ public class FragmentCompose extends FragmentBase {
db.attachment().setDownloaded(attachment.id, size); db.attachment().setDownloaded(attachment.id, size);
if (BuildConfig.APPLICATION_ID.equals(uri.getAuthority()) && if (BuildConfig.APPLICATION_ID.equals(uri.getUri().getAuthority()) &&
uri.getPathSegments().size() > 0 && uri.getUri().getPathSegments().size() > 0 &&
"photo".equals(uri.getPathSegments().get(0))) { "photo".equals(uri.getUri().getPathSegments().get(0))) {
// content://eu.faircode.email/photo/nnn.jpg // content://eu.faircode.email/photo/nnn.jpg
File tmp = new File(context.getFilesDir(), uri.getPath()); File tmp = new File(context.getFilesDir(), uri.getUri().getPath());
Log.i("Deleting " + tmp); Log.i("Deleting " + tmp);
Helper.secureDelete(tmp); Helper.secureDelete(tmp);
} else } else
Log.i("Authority=" + uri.getAuthority()); Log.i("Authority=" + uri.getUri().getAuthority());
if (resize > 0) if (resize > 0)
resizeAttachment(context, attachment, resize, resize_width_only); resizeAttachment(context, attachment, resize, resize_width_only);
@ -5710,7 +5706,7 @@ public class FragmentCompose extends FragmentBase {
String external_body = args.getString("body", ""); String external_body = args.getString("body", "");
String external_text = args.getString("text"); String external_text = args.getString("text");
CharSequence selected_text = args.getCharSequence("selected"); CharSequence selected_text = args.getCharSequence("selected");
ArrayList<Uri> uris = args.getParcelableArrayList("attachments"); ArrayList<UriType> uris = args.getParcelableArrayList("attachments");
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean plain_only = prefs.getBoolean("plain_only", false); boolean plain_only = prefs.getBoolean("plain_only", false);
@ -6453,14 +6449,14 @@ public class FragmentCompose extends FragmentBase {
} }
if ("new".equals(action) && uris != null) { if ("new".equals(action) && uris != null) {
ArrayList<Uri> images = new ArrayList<>(); ArrayList<UriType> images = new ArrayList<>();
for (Uri uri : uris) for (UriType uri : uris)
try { try {
Helper.UriInfo info = Helper.getInfo(uri, context); Helper.UriInfo info = Helper.getInfo(uri, context);
if (info.isImage()) if (info.isImage())
images.add(uri); images.add(uri);
else else
addAttachment(context, data.draft.id, uri, null, false, 0, false, false); addAttachment(context, data.draft.id, uri, false, 0, false, false);
} catch (IOException | SecurityException ex) { } catch (IOException | SecurityException ex) {
Log.e(ex); Log.e(ex);
} }
@ -6896,7 +6892,7 @@ public class FragmentCompose extends FragmentBase {
if (draft.content && state == State.NONE) { if (draft.content && state == State.NONE) {
Runnable postShow = null; Runnable postShow = null;
if (args.containsKey("images")) { if (args.containsKey("images")) {
ArrayList<Uri> images = args.getParcelableArrayList("images"); ArrayList<UriType> images = args.getParcelableArrayList("images");
args.remove("images"); // once args.remove("images"); // once
postShow = new Runnable() { postShow = new Runnable() {

@ -2962,13 +2962,13 @@ public class Helper {
} }
@NonNull @NonNull
static UriInfo getInfo(Uri uri, Context context) { static UriInfo getInfo(UriType uri, Context context) {
UriInfo result = new UriInfo(); UriInfo result = new UriInfo();
// https://stackoverflow.com/questions/76094229/android-13-photo-video-picker-file-name-from-the-uri-is-garbage // https://stackoverflow.com/questions/76094229/android-13-photo-video-picker-file-name-from-the-uri-is-garbage
DocumentFile dfile = null; DocumentFile dfile = null;
try { try {
dfile = DocumentFile.fromSingleUri(context, uri); dfile = DocumentFile.fromSingleUri(context, uri.getUri());
if (dfile != null) { if (dfile != null) {
result.name = dfile.getName(); result.name = dfile.getName();
result.type = dfile.getType(); result.type = dfile.getType();
@ -2981,9 +2981,11 @@ public class Helper {
// Check name // Check name
if (TextUtils.isEmpty(result.name)) if (TextUtils.isEmpty(result.name))
result.name = uri.getLastPathSegment(); result.name = uri.getUri().getLastPathSegment();
// Check type // Check type
if (uri.getType() != null)
result.type = uri.getType();
if (!TextUtils.isEmpty(result.type)) if (!TextUtils.isEmpty(result.type))
try { try {
new ContentType(result.type); new ContentType(result.type);

@ -0,0 +1,90 @@
package eu.faircode.email;
/*
This file is part of FairEmail.
FairEmail is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
FairEmail is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with FairEmail. If not, see <http://www.gnu.org/licenses/>.
Copyright 2018-2025 by Marcel Bokhorst (M66B)
*/
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import java.util.ArrayList;
import java.util.List;
public class UriType implements Parcelable {
private Uri uri;
private String type;
protected UriType(Parcel in) {
this.uri = in.readParcelable(Uri.class.getClassLoader());
this.type = in.readString();
}
public UriType(Uri uri, String type) {
this.uri = uri;
if (!TextUtils.isEmpty(type))
this.type = type;
}
public Uri getUri() {
return this.uri;
}
public String getType() {
return this.type;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel parcel, int flags) {
parcel.writeParcelable(this.uri, flags);
parcel.writeString(this.type);
}
public static final Creator<UriType> CREATOR = new Creator<UriType>() {
@Override
public UriType createFromParcel(Parcel in) {
return new UriType(in);
}
@Override
public UriType[] newArray(int size) {
return new UriType[size];
}
};
public static List<UriType> getList(List<Uri> uris) {
List<UriType> result = new ArrayList<>();
for (Uri uri : uris)
result.add(new UriType(uri, null));
return result;
}
@NonNull
@Override
public String toString() {
return uri + " type=" + type;
}
}
Loading…
Cancel
Save