mirror of https://github.com/M66B/FairEmail.git
Show images inline
This commit is contained in:
parent
b9ef1ec8f9
commit
b652db3e09
|
@ -23,16 +23,22 @@ import android.content.Context;
|
|||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.Editable;
|
||||
import android.text.Html;
|
||||
import android.text.Layout;
|
||||
import android.text.Spannable;
|
||||
import android.text.Spanned;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.style.URLSpan;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
|
@ -40,6 +46,7 @@ import android.view.MenuItem;
|
|||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
@ -47,6 +54,11 @@ import android.widget.Toast;
|
|||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
|
||||
import org.xml.sax.XMLReader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.text.Collator;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
@ -83,6 +95,7 @@ public class FragmentMessage extends FragmentEx {
|
|||
private RecyclerView rvAttachment;
|
||||
private TextView tvError;
|
||||
private View vSeparatorBody;
|
||||
private Button btnImages;
|
||||
private TextView tvBody;
|
||||
private ProgressBar pbBody;
|
||||
private FloatingActionButton fab;
|
||||
|
@ -125,6 +138,7 @@ public class FragmentMessage extends FragmentEx {
|
|||
rvAttachment = view.findViewById(R.id.rvAttachment);
|
||||
tvError = view.findViewById(R.id.tvError);
|
||||
vSeparatorBody = view.findViewById(R.id.vSeparatorBody);
|
||||
btnImages = view.findViewById(R.id.btnImages);
|
||||
tvBody = view.findViewById(R.id.tvBody);
|
||||
pbBody = view.findViewById(R.id.pbBody);
|
||||
fab = view.findViewById(R.id.fab);
|
||||
|
@ -264,6 +278,7 @@ public class FragmentMessage extends FragmentEx {
|
|||
grpHeader.setVisibility(View.GONE);
|
||||
grpAddresses.setVisibility(View.GONE);
|
||||
grpAttachments.setVisibility(View.GONE);
|
||||
btnImages.setVisibility(View.GONE);
|
||||
grpMessage.setVisibility(View.GONE);
|
||||
pbBody.setVisibility(View.GONE);
|
||||
bottom_navigation.setVisibility(View.GONE);
|
||||
|
@ -353,28 +368,90 @@ public class FragmentMessage extends FragmentEx {
|
|||
|
||||
if (tvBody.getTag() == null) {
|
||||
// Spanned text needs to be loaded after recreation too
|
||||
Bundle args = new Bundle();
|
||||
final Bundle args = new Bundle();
|
||||
args.putLong("id", message.id);
|
||||
args.putBoolean("has_images", false);
|
||||
args.putBoolean("show_images", false);
|
||||
|
||||
pbBody.setVisibility(View.VISIBLE);
|
||||
new SimpleTask<Spanned>() {
|
||||
final SimpleTask<Spanned> bodyTask = new SimpleTask<Spanned>() {
|
||||
@Override
|
||||
protected Spanned onLoad(Context context, Bundle args) throws Throwable {
|
||||
protected Spanned onLoad(final Context context, final Bundle args) throws Throwable {
|
||||
final boolean show_images = args.getBoolean("show_images");
|
||||
String body = EntityMessage.read(context, args.getLong("id"));
|
||||
args.putInt("size", body.length());
|
||||
return Html.fromHtml(HtmlHelper.sanitize(getContext(), body, false));
|
||||
|
||||
return Html.fromHtml(HtmlHelper.sanitize(getContext(), body, false), new Html.ImageGetter() {
|
||||
@Override
|
||||
public Drawable getDrawable(String source) {
|
||||
float scale = context.getResources().getDisplayMetrics().density;
|
||||
int px = (int) (24 * scale + 0.5f);
|
||||
|
||||
if (show_images) {
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = new URL(source).openStream();
|
||||
Bitmap bm = BitmapFactory.decodeStream(is);
|
||||
if (bm == null)
|
||||
throw new IllegalArgumentException();
|
||||
Drawable d = new BitmapDrawable(context.getResources(), bm);
|
||||
d.setBounds(0, 0, bm.getWidth(), bm.getHeight());
|
||||
// TODO image caching?
|
||||
return d;
|
||||
} catch (Throwable ex) {
|
||||
Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex));
|
||||
Drawable d = context.getResources().getDrawable(R.drawable.baseline_warning_24, context.getTheme());
|
||||
d.setBounds(0, 0, px, px);
|
||||
return d;
|
||||
} finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
Log.w(Helper.TAG, e + "\n" + Log.getStackTraceString(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
args.putBoolean("has_images", true);
|
||||
Drawable d = context.getResources().getDrawable(R.drawable.baseline_image_24, context.getTheme());
|
||||
d.setBounds(0, 0, px, px);
|
||||
return d;
|
||||
}
|
||||
}
|
||||
}, new Html.TagHandler() {
|
||||
@Override
|
||||
public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLoaded(Bundle args, Spanned body) {
|
||||
boolean has_images = args.getBoolean("has_images");
|
||||
boolean show_images = args.getBoolean("show_images");
|
||||
tvSize.setText(Helper.humanReadableByteCount(args.getInt("size"), false));
|
||||
tvBody.setText(body);
|
||||
tvBody.setTag(true);
|
||||
btnImages.setVisibility(has_images && !show_images ? View.VISIBLE : View.GONE);
|
||||
grpMessage.setVisibility(View.VISIBLE);
|
||||
fab.setVisibility(free ? View.GONE : View.VISIBLE);
|
||||
pbBody.setVisibility(View.GONE);
|
||||
|
||||
}
|
||||
}.load(FragmentMessage.this, args);
|
||||
};
|
||||
|
||||
bodyTask.load(FragmentMessage.this, args);
|
||||
|
||||
btnImages.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
v.setEnabled(false);
|
||||
args.putBoolean("show_images", true);
|
||||
bodyTask.load(FragmentMessage.this, args);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
int typeface = (message.ui_seen ? Typeface.NORMAL : Typeface.BOLD);
|
||||
|
|
|
@ -95,6 +95,7 @@ public class HtmlHelper implements NodeVisitor {
|
|||
alt = context.getString(R.string.title_image);
|
||||
alt = Html.escapeHtml(alt);
|
||||
sb.append(" ").append(String.format("<a href=\"%s\">%s [%d]</a>", ref, alt, refs.size()));
|
||||
sb.append("<img src=\"" + ref + "\" alt=\"" + alt + "\">");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
|
||||
</vector>
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M1,21h22L12,2 1,21zM13,18h-2v-2h2v2zM13,14h-2v-4h2v4z"/>
|
||||
</vector>
|
|
@ -226,17 +226,32 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvError" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnImages"
|
||||
style="?android:attr/buttonStyleSmall"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="6dp"
|
||||
android:layout_marginTop="3dp"
|
||||
android:minHeight="0dp"
|
||||
android:minWidth="0dp"
|
||||
android:text="@string/title_show_images"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/vSeparatorBody" />
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/scroll"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginLeft="6dp"
|
||||
android:layout_marginRight="6dp"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:layout_marginStart="6dp"
|
||||
android:layout_marginTop="3dp"
|
||||
android:fillViewport="true"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toTopOf="@+id/bottom_navigation"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/vSeparatorBody">
|
||||
app:layout_constraintTop_toBottomOf="@id/btnImages">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvBody"
|
||||
|
|
|
@ -124,6 +124,7 @@
|
|||
|
||||
<string name="title_link">link</string>
|
||||
<string name="title_image">image</string>
|
||||
<string name="title_show_images">Show images</string>
|
||||
<string name="title_subject_reply">Re: %1$s</string>
|
||||
<string name="title_subject_forward">Fwd: %1$s</string>
|
||||
|
||||
|
|
Loading…
Reference in New Issue