mirror of
https://github.com/M66B/FairEmail.git
synced 2025-01-03 13:44:40 +00:00
Confirm opening attachments
This commit is contained in:
parent
6b7a242672
commit
aa63f76276
6 changed files with 157 additions and 9 deletions
3
FAQ.md
3
FAQ.md
|
@ -36,7 +36,8 @@ For:
|
|||
Since FairEmail is meant to be privacy friendly, the following will not be added:
|
||||
|
||||
* Show images without confirmation
|
||||
* Show original message without confirmation
|
||||
* Show attachments without confirmation
|
||||
* show original message without confirmation
|
||||
* Open links without confirmation
|
||||
* Hide addresses by default: addresses play an important role in determining if a message is authentic
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ See also [this FAQ](https://github.com/M66B/open-source-email/blob/master/FAQ.md
|
|||
* Accept valid security certificates only
|
||||
* Authentication required
|
||||
* Safe message view (styling, scripting and unsafe HTML removed)
|
||||
* Confirm opening links, images and attachments
|
||||
* No special permissions required
|
||||
* No advertisements
|
||||
* No analytics and no tracking
|
||||
|
|
|
@ -23,6 +23,7 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
|
@ -31,7 +32,10 @@ import android.util.Log;
|
|||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
@ -43,6 +47,7 @@ import java.util.Comparator;
|
|||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.content.FileProvider;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
@ -158,7 +163,7 @@ public class AdapterAttachment extends RecyclerView.Adapter<AdapterAttachment.Vi
|
|||
File file = EntityAttachment.getFile(context, attachment.id);
|
||||
|
||||
// https://developer.android.com/reference/android/support/v4/content/FileProvider
|
||||
Uri uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID, file);
|
||||
final Uri uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID, file);
|
||||
Log.i(Helper.TAG, "uri=" + uri);
|
||||
|
||||
// Build intent
|
||||
|
@ -170,20 +175,49 @@ public class AdapterAttachment extends RecyclerView.Adapter<AdapterAttachment.Vi
|
|||
Log.i(Helper.TAG, "Sharing " + file + " type=" + attachment.type);
|
||||
Log.i(Helper.TAG, "Intent=" + intent);
|
||||
|
||||
// Set permissions
|
||||
List<ResolveInfo> targets = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
|
||||
for (ResolveInfo resolveInfo : targets) {
|
||||
Log.i(Helper.TAG, "Target=" + resolveInfo);
|
||||
context.grantUriPermission(resolveInfo.activityInfo.packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
// Get targets
|
||||
PackageManager pm = context.getPackageManager();
|
||||
List<NameResolveInfo> targets = new ArrayList<>();
|
||||
List<ResolveInfo> ris = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
|
||||
for (ResolveInfo ri : ris) {
|
||||
Log.i(Helper.TAG, "Target=" + ri);
|
||||
targets.add(new NameResolveInfo(
|
||||
pm.getApplicationIcon(ri.activityInfo.applicationInfo),
|
||||
pm.getApplicationLabel(ri.activityInfo.applicationInfo).toString(),
|
||||
ri));
|
||||
}
|
||||
|
||||
// Check if viewer available
|
||||
if (targets.size() == 0) {
|
||||
if (ris.size() == 0) {
|
||||
Toast.makeText(context, context.getString(R.string.title_no_viewer, attachment.type), Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
Helper.view(context, intent);
|
||||
View dview = LayoutInflater.from(context).inflate(R.layout.dialog_attachment, null);
|
||||
final AlertDialog dialog = new DialogBuilderLifecycle(context, owner)
|
||||
.setView(dview)
|
||||
.create();
|
||||
|
||||
TextView tvName = dview.findViewById(R.id.tvName);
|
||||
TextView tvType = dview.findViewById(R.id.tvType);
|
||||
ListView lvApp = dview.findViewById(R.id.lvApp);
|
||||
|
||||
tvName.setText(attachment.name);
|
||||
tvType.setText(attachment.type);
|
||||
|
||||
lvApp.setAdapter(new TargetAdapter(context, R.layout.item_target, targets));
|
||||
lvApp.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
NameResolveInfo selected = (NameResolveInfo) parent.getItemAtPosition(position);
|
||||
context.grantUriPermission(selected.info.activityInfo.packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
intent.setPackage(selected.info.activityInfo.packageName);
|
||||
context.startActivity(intent);
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
} else {
|
||||
if (attachment.progress == null) {
|
||||
Bundle args = new Bundle();
|
||||
|
@ -221,6 +255,40 @@ public class AdapterAttachment extends RecyclerView.Adapter<AdapterAttachment.Vi
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class NameResolveInfo {
|
||||
Drawable icon;
|
||||
String name;
|
||||
ResolveInfo info;
|
||||
|
||||
NameResolveInfo(Drawable icon, String name, ResolveInfo info) {
|
||||
this.icon = icon;
|
||||
this.name = name;
|
||||
this.info = info;
|
||||
}
|
||||
}
|
||||
|
||||
public class TargetAdapter extends ArrayAdapter<NameResolveInfo> {
|
||||
private Context context;
|
||||
|
||||
TargetAdapter(Context context, int resid, List<NameResolveInfo> items) {
|
||||
super(context, resid, items);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
NameResolveInfo item = getItem(position);
|
||||
|
||||
View view = LayoutInflater.from(context).inflate(R.layout.item_target, null);
|
||||
ImageView ivIcon = view.findViewById(R.id.ivIcon);
|
||||
TextView tvName = view.findViewById(R.id.tvName);
|
||||
|
||||
ivIcon.setImageDrawable(item.icon);
|
||||
tvName.setText(item.name);
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AdapterAttachment(Context context, LifecycleOwner owner, boolean readonly) {
|
||||
|
|
46
app/src/main/res/layout/dialog_attachment.xml
Normal file
46
app/src/main/res/layout/dialog_attachment.xml
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp"
|
||||
tools:context=".ActivityView">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Name"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvType"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="text/plain"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvName" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvOpenWith"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:text="@string/title_open_with"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvType" />
|
||||
|
||||
<ListView
|
||||
android:id="@+id/lvApp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvOpenWith" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
31
app/src/main/res/layout/item_target.xml
Normal file
31
app/src/main/res/layout/item_target.xml
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingBottom="6dp"
|
||||
tools:context=".ActivityView">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivIcon"
|
||||
android:layout_width="?android:attr/listPreferredItemHeightSmall"
|
||||
android:layout_height="?android:attr/listPreferredItemHeightSmall"
|
||||
android:src="@drawable/baseline_mail_24"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvName"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:text="type"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large"
|
||||
app:layout_constraintBottom_toBottomOf="@id/ivIcon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/ivIcon"
|
||||
app:layout_constraintTop_toTopOf="@id/ivIcon" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -211,6 +211,7 @@
|
|||
<string name="title_archive">Archive</string>
|
||||
<string name="title_reply">Reply</string>
|
||||
<string name="title_moving">Moving message to %1$s</string>
|
||||
<string name="title_open_with">Open with</string>
|
||||
|
||||
<string name="title_no_answers">No reply templates defined</string>
|
||||
<string name="title_no_viewer">No viewer app available for %1$s</string>
|
||||
|
|
Loading…
Reference in a new issue