From 2a9ddd575b0b87ebbe6fa3f836962e49c5b99173 Mon Sep 17 00:00:00 2001 From: Dmitriy Bogdanov Date: Wed, 29 Jul 2020 18:49:05 +0400 Subject: [PATCH 1/3] Add support of Future to TaskService and ServiceHelper --- .../service/CallableParameterizedAdapter.java | 18 ++++++ .../apps/Poche/service/Parameterized.java | 7 +++ .../Poche/service/ParameterizedAdapter.java | 14 +++++ .../Poche/service/ParameterizedCallable.java | 7 +++ .../Poche/service/ParameterizedRunnable.java | 7 +++ .../service/ParameterizedRunnableAdapter.java | 27 +++++++++ .../service/RunnableParameterizedAdapter.java | 16 ++++++ .../apps/Poche/service/ServiceHelper.java | 57 +++++++++++++++++-- .../apps/Poche/service/TaskService.java | 18 +++--- 9 files changed, 156 insertions(+), 15 deletions(-) create mode 100644 app/src/main/java/fr/gaulupeau/apps/Poche/service/CallableParameterizedAdapter.java create mode 100644 app/src/main/java/fr/gaulupeau/apps/Poche/service/Parameterized.java create mode 100644 app/src/main/java/fr/gaulupeau/apps/Poche/service/ParameterizedAdapter.java create mode 100644 app/src/main/java/fr/gaulupeau/apps/Poche/service/ParameterizedCallable.java create mode 100644 app/src/main/java/fr/gaulupeau/apps/Poche/service/ParameterizedRunnable.java create mode 100644 app/src/main/java/fr/gaulupeau/apps/Poche/service/ParameterizedRunnableAdapter.java create mode 100644 app/src/main/java/fr/gaulupeau/apps/Poche/service/RunnableParameterizedAdapter.java diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/service/CallableParameterizedAdapter.java b/app/src/main/java/fr/gaulupeau/apps/Poche/service/CallableParameterizedAdapter.java new file mode 100644 index 000000000..ffbc469c4 --- /dev/null +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/service/CallableParameterizedAdapter.java @@ -0,0 +1,18 @@ +package fr.gaulupeau.apps.Poche.service; + +import java.util.concurrent.Callable; + +class CallableParameterizedAdapter extends ParameterizedAdapter implements Callable { + + protected final ParameterizedCallable parameterizedCallable; + + public CallableParameterizedAdapter(ParameterizedCallable parameterizedCallable) { + this.parameterizedCallable = parameterizedCallable; + } + + @Override + public V call() throws Exception { + return parameterizedCallable.call(context); + } + +} diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/service/Parameterized.java b/app/src/main/java/fr/gaulupeau/apps/Poche/service/Parameterized.java new file mode 100644 index 000000000..6c543e200 --- /dev/null +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/service/Parameterized.java @@ -0,0 +1,7 @@ +package fr.gaulupeau.apps.Poche.service; + +import android.content.Context; + +interface Parameterized { + void setContext(Context context); +} diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/service/ParameterizedAdapter.java b/app/src/main/java/fr/gaulupeau/apps/Poche/service/ParameterizedAdapter.java new file mode 100644 index 000000000..4c59b32bc --- /dev/null +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/service/ParameterizedAdapter.java @@ -0,0 +1,14 @@ +package fr.gaulupeau.apps.Poche.service; + +import android.content.Context; + +abstract class ParameterizedAdapter implements Parameterized { + + protected Context context; + + @Override + public void setContext(Context context) { + this.context = context; + } + +} diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/service/ParameterizedCallable.java b/app/src/main/java/fr/gaulupeau/apps/Poche/service/ParameterizedCallable.java new file mode 100644 index 000000000..f1d3c0b0b --- /dev/null +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/service/ParameterizedCallable.java @@ -0,0 +1,7 @@ +package fr.gaulupeau.apps.Poche.service; + +import android.content.Context; + +public interface ParameterizedCallable { + V call(Context context) throws Exception; +} diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/service/ParameterizedRunnable.java b/app/src/main/java/fr/gaulupeau/apps/Poche/service/ParameterizedRunnable.java new file mode 100644 index 000000000..2aead565a --- /dev/null +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/service/ParameterizedRunnable.java @@ -0,0 +1,7 @@ +package fr.gaulupeau.apps.Poche.service; + +import android.content.Context; + +public interface ParameterizedRunnable { + void run(Context context); +} diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/service/ParameterizedRunnableAdapter.java b/app/src/main/java/fr/gaulupeau/apps/Poche/service/ParameterizedRunnableAdapter.java new file mode 100644 index 000000000..33077c3cd --- /dev/null +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/service/ParameterizedRunnableAdapter.java @@ -0,0 +1,27 @@ +package fr.gaulupeau.apps.Poche.service; + +import android.content.Context; + +import java.util.Objects; + +class ParameterizedRunnableAdapter implements ParameterizedRunnable { + + protected Runnable runnable; + protected Parameterized parameterized; + + public ParameterizedRunnableAdapter(Runnable runnable, Parameterized parameterized) { + this.runnable = Objects.requireNonNull(runnable); + this.parameterized = parameterized; + } + + @Override + public void run(Context context) { + try { + if (parameterized != null) parameterized.setContext(context); + runnable.run(); + } finally { + if (parameterized != null) parameterized.setContext(null); + } + } + +} diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/service/RunnableParameterizedAdapter.java b/app/src/main/java/fr/gaulupeau/apps/Poche/service/RunnableParameterizedAdapter.java new file mode 100644 index 000000000..4e74beca5 --- /dev/null +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/service/RunnableParameterizedAdapter.java @@ -0,0 +1,16 @@ +package fr.gaulupeau.apps.Poche.service; + +class RunnableParameterizedAdapter extends ParameterizedAdapter implements Runnable { + + protected final ParameterizedRunnable parameterizedRunnable; + + public RunnableParameterizedAdapter(ParameterizedRunnable parameterizedRunnable) { + this.parameterizedRunnable = parameterizedRunnable; + } + + @Override + public void run() { + parameterizedRunnable.run(context); + } + +} diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/service/ServiceHelper.java b/app/src/main/java/fr/gaulupeau/apps/Poche/service/ServiceHelper.java index 36c60a473..bf491e0e0 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/service/ServiceHelper.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/service/ServiceHelper.java @@ -10,6 +10,9 @@ import androidx.core.util.Consumer; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; + import fr.gaulupeau.apps.Poche.service.tasks.ActionRequestTask; import fr.gaulupeau.apps.Poche.service.tasks.DownloadArticleAsFileTask; import fr.gaulupeau.apps.Poche.service.tasks.FetchArticleImagesTask; @@ -67,23 +70,69 @@ public static void enqueueSimpleServiceTask(Context context, context.startService(TaskService.newSimpleTaskIntent(context, serviceClass, task)); } - public static void enqueueServiceTask(Context context, TaskService.Task task, + public static void enqueueServiceTask(Context context, ParameterizedRunnable task, Runnable postCallCallback) { enqueueServiceTask(context, MainService.class, task, postCallCallback); } + public static Future submitServiceCallableTask(Context context, + ParameterizedCallable task, + Runnable postCallCallback) { + return submit(context, MainService.class, task, postCallCallback); + } + + public static Future submitServiceTask(Context context, ParameterizedRunnable task, + Runnable postCallCallback) { + return submit(context, MainService.class, task, postCallCallback); + } + + public static Future submit(Context context, + Class serviceClass, + ParameterizedCallable callable, + Runnable postCallCallback) { + CallableParameterizedAdapter parameterizedCallable + = new CallableParameterizedAdapter<>(callable); + FutureTask future = new FutureTask<>(parameterizedCallable); + + enqueueServiceTask(context, serviceClass, future, parameterizedCallable, postCallCallback); + + return future; + } + + public static Future submit(Context context, + Class serviceClass, + ParameterizedRunnable runnable, + Runnable postCallCallback) { + RunnableParameterizedAdapter parameterizedRunnable + = new RunnableParameterizedAdapter(runnable); + FutureTask future = new FutureTask<>(parameterizedRunnable, null); + + enqueueServiceTask(context, serviceClass, future, parameterizedRunnable, postCallCallback); + + return future; + } + + private static void enqueueServiceTask(Context context, + Class serviceClass, + Runnable runnable, Parameterized parameterized, + Runnable postCallCallback) { + enqueueServiceTask(context, serviceClass, + new ParameterizedRunnableAdapter(runnable, parameterized), + postCallCallback); + } + public static void enqueueServiceTask(Context context, Class serviceClass, - TaskService.Task task, + ParameterizedRunnable task, Runnable postCallCallback) { performBoundServiceCall(context, serviceClass, binder -> { TaskService.TaskServiceBinder service = (TaskService.TaskServiceBinder) binder; - service.enqueueTask(task); + service.enqueue(task); }, postCallCallback); } public static void performBoundServiceCall(Context context, - Class serviceClass, + Class serviceClass, Consumer action, Runnable postCallCallback) { Log.d(TAG, "performBoundServiceCall() started"); diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/service/TaskService.java b/app/src/main/java/fr/gaulupeau/apps/Poche/service/TaskService.java index f946ba824..669c5edc1 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/service/TaskService.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/service/TaskService.java @@ -23,13 +23,9 @@ public class TaskService extends Service { public static final String ACTION_SIMPLE_TASK = "action_simple_task"; - public interface Task { - void run(Context context); - } - public class TaskServiceBinder extends Binder { - public void enqueueTask(Task task) { - TaskService.this.enqueueTask(task, true); + public void enqueue(ParameterizedRunnable parameterizedRunnable) { + TaskService.this.enqueueTask(parameterizedRunnable, true); } } @@ -46,7 +42,7 @@ public void enqueueTask(Task task) { private final Object startIdLock = new Object(); private volatile int lastStartId; - private final BlockingQueue taskQueue = new LinkedBlockingQueue<>(); + private final BlockingQueue taskQueue = new LinkedBlockingQueue<>(); private volatile boolean running; @@ -98,7 +94,7 @@ public int onStartCommand(Intent intent, int flags, int startId) { Log.d(tag, "onStartCommand()"); if (ACTION_SIMPLE_TASK.equals(intent.getAction())) { - Task task = taskFromSimpleTask(SimpleTask.fromIntent(intent)); + ParameterizedRunnable task = taskFromSimpleTask(SimpleTask.fromIntent(intent)); if (task != null) { enqueueTask(task, false); } @@ -111,7 +107,7 @@ public int onStartCommand(Intent intent, int flags, int startId) { return START_NOT_STICKY; } - private Task taskFromSimpleTask(SimpleTask simpleTask) { + private ParameterizedRunnable taskFromSimpleTask(SimpleTask simpleTask) { if (simpleTask == null) { Log.d(tag, "taskFromActionRequest() request is null"); return null; @@ -144,7 +140,7 @@ private void run() { Process.setThreadPriority(getThreadPriority()); while (running) { - Task task; + ParameterizedRunnable task; try { task = taskQueue.poll(WAIT_TIME, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { @@ -183,7 +179,7 @@ private void readyToStop() { } } - private void enqueueTask(Task task, boolean ensureStarted) { + private void enqueueTask(ParameterizedRunnable task, boolean ensureStarted) { Log.d(tag, "enqueueTask()"); Objects.requireNonNull(task, "task is null"); From 72265b47a10ef413d70edc62407c7aacca714f71 Mon Sep 17 00:00:00 2001 From: Dmitriy Bogdanov Date: Wed, 29 Jul 2020 18:52:10 +0400 Subject: [PATCH 2/3] Fix issues with new annotations --- .../apps/Poche/service/OperationsHelper.java | 6 ++++-- .../Poche/service/workers/OperationsWorker.java | 3 ++- .../apps/Poche/ui/ReadArticleActivity.java | 17 +++++++++++++---- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/service/OperationsHelper.java b/app/src/main/java/fr/gaulupeau/apps/Poche/service/OperationsHelper.java index 34e85cebd..bd0dbcf14 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/service/OperationsHelper.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/service/OperationsHelper.java @@ -5,6 +5,7 @@ import android.util.Log; import java.util.List; +import java.util.concurrent.Future; import fr.gaulupeau.apps.Poche.data.DbConnection; import fr.gaulupeau.apps.Poche.data.DbUtils; @@ -26,6 +27,7 @@ import static fr.gaulupeau.apps.Poche.service.ServiceHelper.enqueueServiceTask; import static fr.gaulupeau.apps.Poche.service.ServiceHelper.enqueueSimpleServiceTask; import static fr.gaulupeau.apps.Poche.service.ServiceHelper.startService; +import static fr.gaulupeau.apps.Poche.service.ServiceHelper.submitServiceCallableTask; public class OperationsHelper { @@ -73,8 +75,8 @@ public static void setArticleTags(Context context, Article article, List ne .setArticleTags(article, newTags), postCallCallback); } - public static void addAnnotation(Context context, int articleId, Annotation annotation) { - enqueueServiceTask(context, ctx -> new OperationsWorker(ctx) + public static Future addAnnotation(Context context, int articleId, Annotation annotation) { + return submitServiceCallableTask(context, ctx -> new OperationsWorker(ctx) .addAnnotation(articleId, annotation), null); } diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/service/workers/OperationsWorker.java b/app/src/main/java/fr/gaulupeau/apps/Poche/service/workers/OperationsWorker.java index b23eec9de..11ed07f1a 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/service/workers/OperationsWorker.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/service/workers/OperationsWorker.java @@ -400,7 +400,7 @@ private void setArticleTagsInternal(Article article, List newTags) { Log.d(TAG, "setArticleTagsInternal() finished"); } - public void addAnnotation(int articleId, Annotation annotation) { + public Annotation addAnnotation(int articleId, Annotation annotation) { Log.d(TAG, String.format("addAnnotation(%d, %s) started", articleId, annotation)); Article article = getArticle(articleId); @@ -441,6 +441,7 @@ public void addAnnotation(int articleId, Annotation annotation) { } Log.d(TAG, "addAnnotation() finished"); + return annotation; } public void updateAnnotation(int articleId, Annotation annotation) { diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ReadArticleActivity.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ReadArticleActivity.java index c2b852724..6c5326b78 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ReadArticleActivity.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ReadArticleActivity.java @@ -43,6 +43,7 @@ import java.util.Date; import java.util.EnumSet; import java.util.List; +import java.util.concurrent.Future; import fr.gaulupeau.apps.InThePoche.BuildConfig; import fr.gaulupeau.apps.InThePoche.R; @@ -798,10 +799,9 @@ public List getAnnotations() { } @Override - public Annotation createAnnotation(Annotation annotation) { - OperationsHelper.addAnnotation(ReadArticleActivity.this, - article.getArticleId(), annotation); - return annotation; + public Annotation createAnnotation(Annotation annotation) { // TODO: fix: waiting call + return waitForFuture(OperationsHelper.addAnnotation( + ReadArticleActivity.this, article.getArticleId(), annotation)); } @Override @@ -817,6 +817,15 @@ public Annotation deleteAnnotation(Annotation annotation) { article.getArticleId(), annotation); return annotation; } + + private T waitForFuture(Future future) { + try { + return future.get(); + } catch (Exception e) { + Log.w("JsAnnotCtrl.Callback", "waitForFuture() exception", e); + } + return null; + } }); webViewContent.addJavascriptInterface(annotationController, "hostAnnotationController"); From fc9324e332207881564b1269b692c802b7bdeb6f Mon Sep 17 00:00:00 2001 From: Dmitriy Bogdanov Date: Wed, 29 Jul 2020 18:52:48 +0400 Subject: [PATCH 3/3] Fix annotation deletion --- .../Poche/service/workers/OperationsWorker.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/service/workers/OperationsWorker.java b/app/src/main/java/fr/gaulupeau/apps/Poche/service/workers/OperationsWorker.java index 11ed07f1a..7f69f5056 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/service/workers/OperationsWorker.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/service/workers/OperationsWorker.java @@ -455,9 +455,7 @@ public void updateAnnotation(int articleId, Annotation annotation) { String newText = annotation.getText(); - AnnotationDao annotationDao = getDaoSession().getAnnotationDao(); - annotation = annotationDao.queryBuilder() - .where(AnnotationDao.Properties.Id.eq(annotationId)).unique(); + annotation = getAnnotation(annotationId); if (TextUtils.equals(annotation.getText(), newText)) { Log.w(TAG, "updateAnnotation() annotation ID=" + annotationId @@ -467,7 +465,7 @@ public void updateAnnotation(int articleId, Annotation annotation) { annotation.setText(newText); - annotationDao.update(annotation); + getDaoSession().getAnnotationDao().update(annotation); Log.d(TAG, "updateAnnotation() annotation object updated"); Article article = getArticle(articleId); @@ -485,6 +483,8 @@ public void updateAnnotation(int articleId, Annotation annotation) { public void deleteAnnotation(int articleId, Annotation annotation) { Log.d(TAG, String.format("deleteAnnotation(%d, %s) started", articleId, annotation)); + annotation = getAnnotation(annotation.getId()); + Integer remoteId = annotation.getAnnotationId(); if (annotation.getId() != null) { @@ -514,6 +514,11 @@ public void deleteAnnotation(int articleId, Annotation annotation) { Log.d(TAG, "deleteAnnotation() finished"); } + private Annotation getAnnotation(long annotationId) { + return getDaoSession().getAnnotationDao().queryBuilder() + .where(AnnotationDao.Properties.Id.eq(annotationId)).unique(); + } + private Article getArticle(int articleId) { return getArticleDao().queryBuilder() .where(ArticleDao.Properties.ArticleId.eq(articleId))