Confirm opening attachments

This commit is contained in:
M66B 2018-11-11 10:08:57 +00:00
parent 6b7a242672
commit aa63f76276
6 changed files with 157 additions and 9 deletions

3
FAQ.md
View File

@ -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

View File

@ -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

View File

@ -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) {

View 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>

View 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>

View File

@ -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>