Load inline images async, named tasks

This commit is contained in:
M66B 2019-01-06 17:44:03 +00:00
parent b9dd42772a
commit 4c53f9b5b1
3 changed files with 94 additions and 40 deletions

View File

@ -97,10 +97,6 @@ public abstract class DB extends RoomDatabase {
return sInstance; return sInstance;
} }
public static synchronized DB getInstanceMainThread(Context context) {
return migrate(context, getBuilder(context).allowMainThreadQueries());
}
private static RoomDatabase.Builder getBuilder(Context context) { private static RoomDatabase.Builder getBuilder(Context context) {
return Room return Room
.databaseBuilder(context.getApplicationContext(), DB.class, DB_NAME) .databaseBuilder(context.getApplicationContext(), DB.class, DB_NAME)

View File

@ -30,9 +30,11 @@ import android.content.Intent;
import android.content.IntentSender; import android.content.IntentSender;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.graphics.drawable.LevelListDrawable;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.Network; import android.net.Network;
import android.net.NetworkCapabilities; import android.net.NetworkCapabilities;
@ -40,6 +42,7 @@ import android.net.NetworkRequest;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Looper;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import android.provider.OpenableColumns; import android.provider.OpenableColumns;
@ -493,7 +496,7 @@ public class FragmentCompose extends FragmentEx {
args.putString("subject", getArguments().getString("subject")); args.putString("subject", getArguments().getString("subject"));
args.putString("body", getArguments().getString("body")); args.putString("body", getArguments().getString("body"));
args.putParcelableArrayList("attachments", getArguments().getParcelableArrayList("attachments")); args.putParcelableArrayList("attachments", getArguments().getParcelableArrayList("attachments"));
draftLoader.execute(this, args); draftLoader.execute(this, args, "draft:new");
} else { } else {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putString("action", "edit"); args.putString("action", "edit");
@ -501,7 +504,7 @@ public class FragmentCompose extends FragmentEx {
args.putLong("account", -1); args.putLong("account", -1);
args.putLong("reference", -1); args.putLong("reference", -1);
args.putLong("answer", -1); args.putLong("answer", -1);
draftLoader.execute(this, args); draftLoader.execute(this, args, "draft:edit");
} }
} else { } else {
working = savedInstanceState.getLong("working"); working = savedInstanceState.getLong("working");
@ -511,7 +514,7 @@ public class FragmentCompose extends FragmentEx {
args.putLong("account", -1); args.putLong("account", -1);
args.putLong("reference", -1); args.putLong("reference", -1);
args.putLong("answer", -1); args.putLong("answer", -1);
draftLoader.execute(this, args); draftLoader.execute(this, args, "draft:instance");
} }
} }
@ -912,7 +915,7 @@ public class FragmentCompose extends FragmentEx {
else else
Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex);
} }
}.execute(this, args); }.execute(this, args, "encrypt");
} }
@Override @Override
@ -1040,7 +1043,7 @@ public class FragmentCompose extends FragmentEx {
else else
Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex);
} }
}.execute(this, args); }.execute(this, args, "add:attachment");
} }
private void handleExit() { private void handleExit() {
@ -1098,7 +1101,7 @@ public class FragmentCompose extends FragmentEx {
dirty = false; dirty = false;
Log.i("Run execute id=" + working); Log.i("Run execute id=" + working);
actionLoader.execute(this, args); actionLoader.execute(this, args, "action:" + action);
} }
private boolean isEmpty() { private boolean isEmpty() {
@ -1655,7 +1658,7 @@ public class FragmentCompose extends FragmentEx {
protected void onException(Bundle args, Throwable ex) { protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex);
} }
}.execute(this, args); }.execute(this, args, "draft:check");
} }
private void showDraft(EntityMessage draft) { private void showDraft(EntityMessage draft) {
@ -1667,11 +1670,6 @@ public class FragmentCompose extends FragmentEx {
args.putLong("reference", draft.forwarding); args.putLong("reference", draft.forwarding);
new SimpleTask<Spanned[]>() { new SimpleTask<Spanned[]>() {
@Override
protected void onPreExecute(Bundle args) {
state = State.LOADING;
}
@Override @Override
protected void onPostExecute(Bundle args) { protected void onPostExecute(Bundle args) {
state = State.LOADED; state = State.LOADED;
@ -1731,7 +1729,7 @@ public class FragmentCompose extends FragmentEx {
protected void onException(Bundle args, Throwable ex) { protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex);
} }
}.execute(FragmentCompose.this, args); }.execute(FragmentCompose.this, args, "draft:show");
} }
private SimpleTask<EntityMessage> actionLoader = new SimpleTask<EntityMessage>() { private SimpleTask<EntityMessage> actionLoader = new SimpleTask<EntityMessage>() {
@ -1989,25 +1987,72 @@ public class FragmentCompose extends FragmentEx {
private Html.ImageGetter cidGetter = new Html.ImageGetter() { private Html.ImageGetter cidGetter = new Html.ImageGetter() {
@Override @Override
public Drawable getDrawable(String source) { public Drawable getDrawable(final String source) {
if (source != null && source.startsWith("cid:")) { final LevelListDrawable lld = new LevelListDrawable();
DB db = DB.getInstanceMainThread(getContext());
String cid = "<" + source.substring(4) + ">";
EntityAttachment attachment = db.attachment().getAttachment(working, cid);
if (attachment != null) {
File file = EntityAttachment.getFile(getContext(), attachment.id);
Drawable d = Drawable.createFromPath(file.getAbsolutePath());
if (d != null) {
d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
return d;
}
}
}
Resources res = getContext().getResources();
int px = Helper.dp2pixels(getContext(), 48); int px = Helper.dp2pixels(getContext(), 48);
Drawable d = getContext().getResources().getDrawable(R.drawable.baseline_broken_image_24, getContext().getTheme());
d.setBounds(0, 0, px, px); // Level 0: broken image
return d; Drawable broken = res.getDrawable(R.drawable.baseline_broken_image_24, getContext().getTheme());
broken.setBounds(0, 0, px, px);
lld.addLevel(0, 0, broken);
// Level 1: place holder
Drawable placeholder = res.getDrawable(R.drawable.baseline_image_24, getContext().getTheme());
placeholder.setBounds(0, 0, px, px);
lld.addLevel(1, 1, placeholder);
lld.setBounds(0, 0, px, px);
if (source != null && source.startsWith("cid:")) {
lld.setLevel(1); // placeholder
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
Bundle args = new Bundle();
args.putLong("id", working);
args.putString("cid", "<" + source.substring(4) + ">");
new SimpleTask<Drawable>() {
@Override
protected Drawable onExecute(Context context, Bundle args) {
long id = args.getLong("id");
String cid = args.getString("cid");
DB db = DB.getInstance(context);
EntityAttachment attachment = db.attachment().getAttachment(id, cid);
if (attachment == null)
return null;
File file = EntityAttachment.getFile(getContext(), attachment.id);
return Drawable.createFromPath(file.getAbsolutePath());
}
@Override
protected void onExecuted(Bundle args, Drawable image) {
if (image == null)
lld.setLevel(0); // broken
else {
lld.addLevel(2, 2, image);
lld.setLevel(2); // image
lld.setBounds(0, 0, image.getIntrinsicWidth(), image.getIntrinsicHeight());
}
etBody.requestLayout();
}
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex);
}
}.execute(FragmentCompose.this, args, source);
}
});
} else
lld.setLevel(0); // broken
return lld;
} }
}; };

View File

@ -26,6 +26,7 @@ import android.os.Handler;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.Lifecycle; import androidx.lifecycle.Lifecycle;
@ -45,25 +46,30 @@ public abstract class SimpleTask<T> implements LifecycleObserver {
private boolean paused; private boolean paused;
private boolean destroyed; private boolean destroyed;
private Bundle args; private Bundle args;
private String name;
private Result stored; private Result stored;
private static ExecutorService executor = Executors.newCachedThreadPool(Helper.backgroundThreadFactory); private static ExecutorService executor = Executors.newCachedThreadPool(Helper.backgroundThreadFactory);
public void execute(Context context, LifecycleOwner owner, Bundle args) { public void execute(Context context, LifecycleOwner owner, Bundle args) {
run(context, owner, args); run(context, owner, args, null);
} }
public void execute(LifecycleService service, Bundle args) { public void execute(LifecycleService service, Bundle args) {
run(service, service, args); run(service, service, args, null);
} }
public void execute(AppCompatActivity activity, Bundle args) { public void execute(AppCompatActivity activity, Bundle args) {
run(activity, activity, args); run(activity, activity, args, null);
} }
public void execute(final Fragment fragment, Bundle args) { public void execute(Fragment fragment, Bundle args) {
execute(fragment, args, null);
}
public void execute(final Fragment fragment, Bundle args, String name) {
try { try {
run(fragment.getContext(), fragment.getViewLifecycleOwner(), args); run(fragment.getContext(), fragment.getViewLifecycleOwner(), args, name);
} catch (IllegalStateException ex) { } catch (IllegalStateException ex) {
Log.w(ex); Log.w(ex);
} }
@ -111,11 +117,12 @@ public abstract class SimpleTask<T> implements LifecycleObserver {
stored = null; stored = null;
} }
private void run(final Context context, LifecycleOwner owner, final Bundle args) { private void run(final Context context, LifecycleOwner owner, final Bundle args, String name) {
this.owner = owner; this.owner = owner;
this.paused = false; this.paused = false;
this.destroyed = false; this.destroyed = false;
this.args = null; this.args = null;
this.name = name;
this.stored = null; this.stored = null;
owner.getLifecycle().addObserver(this); owner.getLifecycle().addObserver(this);
@ -198,4 +205,10 @@ public abstract class SimpleTask<T> implements LifecycleObserver {
Throwable ex; Throwable ex;
Object data; Object data;
} }
@NonNull
@Override
public String toString() {
return (name == null ? super.toString() : name);
}
} }