diff --git a/app/build.gradle b/app/build.gradle
index d1098bbf0..be21a8902 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -6,7 +6,7 @@ android {
defaultConfig {
applicationId "fr.gaulupeau.apps.InThePoche"
- minSdkVersion 21
+ minSdkVersion 14
targetSdkVersion 30
versionCode 228
versionName "2.4.2"
@@ -66,12 +66,12 @@ dependencies {
implementation 'org.greenrobot:eventbus:3.2.0'
implementation 'org.greenrobot:greendao:3.3.0'
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.2.0'
- implementation 'com.squareup.okhttp3:okhttp:4.9.0'
- implementation 'com.squareup.okhttp3:okhttp-urlconnection:4.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:3.12.12'
+ implementation 'com.squareup.okhttp3:okhttp-urlconnection:3.12.12'
implementation 'org.conscrypt:conscrypt-android:2.5.1'
implementation 'com.facebook.stetho:stetho:1.5.1'
implementation 'com.facebook.stetho:stetho-okhttp3:1.5.1'
- implementation 'com.mikepenz:aboutlibraries:7.1.0'
+ implementation 'com.mikepenz:aboutlibraries:6.2.1'
implementation 'com.github.di72nn.wallabag-api-wrapper:api-wrapper:v2.0.0-beta.6'
implementation 'org.slf4j:slf4j-android:1.7.30'
}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index 34e8da605..3e4c6c5d9 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -14,6 +14,9 @@
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
+-keepclassmembers class fr.gaulupeau.apps.Poche.ui.JsActionController {
+ public *;
+}
-keepclassmembers class fr.gaulupeau.apps.Poche.ui.JsAnnotationController {
public *;
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 1cf7969ae..5004fb5cd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -138,6 +138,14 @@
android:name="android.appwidget.provider"
android:resource="@xml/icon_unread_info" />
+
+
+
+
+
diff --git a/app/src/main/assets/annotations-android-app.js b/app/src/main/assets/annotations-android-app.js
index d8592b650..658929639 100644
--- a/app/src/main/assets/annotations-android-app.js
+++ b/app/src/main/assets/annotations-android-app.js
@@ -6,7 +6,7 @@ document.addEventListener("DOMContentLoaded", function() {
});
const authorization = {
- permits() { return true; },
+ permits: function() { return true; },
};
app.registry.registerUtility(authorization, 'authorizationPolicy');
@@ -46,7 +46,7 @@ document.addEventListener("DOMContentLoaded", function() {
app.include(myStorage);
- app.start().then(() => {
+ app.start().then(function() {
app.annotations.load({});
});
});
diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/data/DbConnection.java b/app/src/main/java/fr/gaulupeau/apps/Poche/data/DbConnection.java
index e3692a3c7..130c97673 100644
--- a/app/src/main/java/fr/gaulupeau/apps/Poche/data/DbConnection.java
+++ b/app/src/main/java/fr/gaulupeau/apps/Poche/data/DbConnection.java
@@ -1,7 +1,10 @@
package fr.gaulupeau.apps.Poche.data;
+import android.database.sqlite.SQLiteDatabase;
+import android.os.Build;
import android.util.Log;
+import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.query.QueryBuilder;
import fr.gaulupeau.apps.InThePoche.BuildConfig;
@@ -25,8 +28,16 @@ public static DaoSession getSession() {
Log.d(TAG, "creating new db session");
WallabagDbOpenHelper dbHelper = new WallabagDbOpenHelper(App.getInstance(), dbPath, null);
- dbHelper.setWriteAheadLoggingEnabled(true);
- DaoMaster daoMaster = new DaoMaster(dbHelper.getWritableDb());
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ dbHelper.setWriteAheadLoggingEnabled(true);
+ }
+ Database db = dbHelper.getWritableDb();
+ if(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
+ if(!((SQLiteDatabase)db.getRawDatabase()).enableWriteAheadLogging()) {
+ Log.w(TAG, "write ahead logging was not enabled");
+ }
+ }
+ DaoMaster daoMaster = new DaoMaster(db);
Holder.session = daoMaster.newSession();
} else {
Log.d(TAG, "using existing db session");
diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java b/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java
index a309162f2..5ed52a175 100644
--- a/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java
+++ b/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java
@@ -19,6 +19,7 @@
import fr.gaulupeau.apps.InThePoche.R;
import fr.gaulupeau.apps.Poche.App;
+import fr.gaulupeau.apps.Poche.network.ConnectivityChangeReceiver;
import fr.gaulupeau.apps.Poche.service.WallabagJobService;
import fr.gaulupeau.apps.Poche.ui.HttpSchemeHandlerActivity;
import fr.gaulupeau.apps.Poche.ui.Sortable;
@@ -51,7 +52,11 @@ public static boolean checkFirstRunInit(Context context) {
}
public static void enableConnectivityChangeReceiver(Context context, boolean enable) {
- WallabagJobService.enable(context, enable);
+ if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ enableComponent(context, ConnectivityChangeReceiver.class, enable);
+ } else {
+ WallabagJobService.enable(context, enable);
+ }
}
// TODO: reuse in setHandleHttpScheme
diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/data/dao/FtsDao.java b/app/src/main/java/fr/gaulupeau/apps/Poche/data/dao/FtsDao.java
index 8ea4040d8..0f1cd9e0f 100644
--- a/app/src/main/java/fr/gaulupeau/apps/Poche/data/dao/FtsDao.java
+++ b/app/src/main/java/fr/gaulupeau/apps/Poche/data/dao/FtsDao.java
@@ -1,5 +1,7 @@
package fr.gaulupeau.apps.Poche.data.dao;
+import android.os.Build;
+
import org.greenrobot.greendao.database.Database;
import fr.gaulupeau.apps.Poche.App;
@@ -29,31 +31,44 @@ public class FtsDao {
"article_content_after_delete_tr"
};
+ public static boolean isFtsSupported() {
+ // https://www.sqlite.org/changes.html#version_3_7_9 is required for the 'content' option
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; // https://stackoverflow.com/a/4377116
+ }
+
public static String getQueryString() {
return "select " + COLUMN_ID + " from " + TABLE_NAME + " where " + TABLE_NAME + " match ";
}
public static void createAll(Database db, boolean ifNotExists) {
+ if (!isFtsSupported()) return;
+
createViewForFts(db, ifNotExists);
createTable(db, ifNotExists);
createTriggers(db, ifNotExists);
}
public static void dropAll(Database db, boolean ifExists) {
+ if (!isFtsSupported()) return;
+
dropTriggers(db, ifExists);
dropTable(db, ifExists);
dropViewForFts(db, ifExists);
}
public static void deleteAllArticles(Database db) {
+ if (!isFtsSupported()) return;
+
dropTable(db, true);
createTable(db, true);
}
private static void createTable(Database db, boolean ifNotExists) {
- String options = ", content=\"" + VIEW_FOR_FTS_NAME + "\""
- + ", tokenize="
- + (App.getSettings().isFtsIcuTokenizerEnabled() ? "icu" : "unicode61");
+ String options = ", content=\"" + VIEW_FOR_FTS_NAME + "\"";
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ options += ", tokenize="
+ + (App.getSettings().isFtsIcuTokenizerEnabled() ? "icu" : "unicode61");
+ }
db.execSQL("create virtual table " + getIfNotExistsConstraint(ifNotExists) +
TABLE_NAME + " using fts4(" +
diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/events/ConnectivityChangedEvent.java b/app/src/main/java/fr/gaulupeau/apps/Poche/events/ConnectivityChangedEvent.java
index 6db7d1717..3c02adb6a 100644
--- a/app/src/main/java/fr/gaulupeau/apps/Poche/events/ConnectivityChangedEvent.java
+++ b/app/src/main/java/fr/gaulupeau/apps/Poche/events/ConnectivityChangedEvent.java
@@ -1,3 +1,17 @@
package fr.gaulupeau.apps.Poche.events;
-public class ConnectivityChangedEvent {}
+public class ConnectivityChangedEvent {
+
+ private boolean noConnectivity;
+
+ public ConnectivityChangedEvent() {}
+
+ public ConnectivityChangedEvent(boolean noConnectivity) {
+ this.noConnectivity = noConnectivity;
+ }
+
+ public boolean isNoConnectivity() {
+ return noConnectivity;
+ }
+
+}
diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/events/EventProcessor.java b/app/src/main/java/fr/gaulupeau/apps/Poche/events/EventProcessor.java
index 578d38c32..f73dd6b6c 100644
--- a/app/src/main/java/fr/gaulupeau/apps/Poche/events/EventProcessor.java
+++ b/app/src/main/java/fr/gaulupeau/apps/Poche/events/EventProcessor.java
@@ -66,6 +66,8 @@ public class EventProcessor {
private Handler mainHandler;
private NotificationManager notificationManager;
+ private boolean delayedNetworkChangedTask;
+
private NotificationCompat.Builder syncQueueNotificationBuilder;
private NotificationCompat.Builder updateArticlesNotificationBuilder;
private NotificationCompat.Builder sweepDeletedArticlesNotificationBuilder;
@@ -118,11 +120,12 @@ public void onAlarmReceivedEvent(AlarmReceivedEvent event) {
public void onConnectivityChangedEvent(ConnectivityChangedEvent event) {
Log.d(TAG, "onConnectivityChangedEvent() started");
- Settings settings = getSettings();
- if (settings.isOfflineQueuePending() && settings.isConfigurationOk()) {
- Log.d(TAG, "networkChanged() requesting SyncQueue operation");
- OperationsHelper.syncQueue(getContext(), true);
+ if(event.isNoConnectivity()) {
+ Log.d(TAG, "onConnectivityChangedEvent() isNoConnectivity is true; ignoring event");
+ return;
}
+
+ networkChanged(false);
}
@Subscribe
@@ -559,6 +562,33 @@ public void onActionResultEvent(ActionResultEvent event) {
}
}
+ private void networkChanged(boolean delayed) {
+ if(!delayed && delayedNetworkChangedTask) return;
+
+ if(!WallabagConnection.isNetworkAvailable()) return;
+
+ Settings settings = getSettings();
+
+ if(settings.isOfflineQueuePending() && settings.isConfigurationOk()) {
+ if(delayed) {
+ Log.d(TAG, "networkChanged() requesting SyncQueue operation");
+
+ OperationsHelper.syncQueue(getContext(), true);
+
+ delayedNetworkChangedTask = false;
+ } else {
+ getMainHandler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ networkChanged(true);
+ }
+ }, 3000);
+
+ delayedNetworkChangedTask = true;
+ }
+ }
+ }
+
private void enableConnectivityChangeReceiver(boolean enable) {
if(getSettings().isAutoSyncQueueEnabled()) {
Log.d(TAG, "enableConnectivityChangeReceiver() enable connectivity change receiver: " + enable);
diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/network/ConnectivityChangeReceiver.java b/app/src/main/java/fr/gaulupeau/apps/Poche/network/ConnectivityChangeReceiver.java
new file mode 100644
index 000000000..f2326eedf
--- /dev/null
+++ b/app/src/main/java/fr/gaulupeau/apps/Poche/network/ConnectivityChangeReceiver.java
@@ -0,0 +1,24 @@
+package fr.gaulupeau.apps.Poche.network;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.util.Log;
+
+import fr.gaulupeau.apps.Poche.events.ConnectivityChangedEvent;
+import fr.gaulupeau.apps.Poche.events.EventHelper;
+
+public class ConnectivityChangeReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if(ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
+ Log.d("ConnectivityChangeRcvr", "Connectivity changed");
+
+ EventHelper.postEvent(new ConnectivityChangedEvent(
+ intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false)));
+ }
+ }
+
+}
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 4b4546693..6ce099f0a 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
@@ -191,7 +191,7 @@ private void readyToStop() {
private void enqueueTask(ParameterizedRunnable task, boolean ensureStarted) {
Log.d(tag, "enqueueTask()");
- Objects.requireNonNull(task, "task is null");
+ Objects.requireNonNull(task);
Log.v(tag, "enqueueTask() enqueueing task");
taskQueue.add(task);
diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/service/WallabagJobService.java b/app/src/main/java/fr/gaulupeau/apps/Poche/service/WallabagJobService.java
index 3398dbbf5..f466a8e31 100644
--- a/app/src/main/java/fr/gaulupeau/apps/Poche/service/WallabagJobService.java
+++ b/app/src/main/java/fr/gaulupeau/apps/Poche/service/WallabagJobService.java
@@ -6,11 +6,15 @@
import android.app.job.JobService;
import android.content.ComponentName;
import android.content.Context;
+import android.os.Build;
import android.util.Log;
+import androidx.annotation.RequiresApi;
+
import fr.gaulupeau.apps.Poche.events.ConnectivityChangedEvent;
import fr.gaulupeau.apps.Poche.events.EventHelper;
+@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class WallabagJobService extends JobService {
private static final int CONNECTIVITY_CHANGE_JOB_ID = 1;
diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/service/tasks/ActionRequestTask.java b/app/src/main/java/fr/gaulupeau/apps/Poche/service/tasks/ActionRequestTask.java
index 0e617c5a7..0881b4756 100644
--- a/app/src/main/java/fr/gaulupeau/apps/Poche/service/tasks/ActionRequestTask.java
+++ b/app/src/main/java/fr/gaulupeau/apps/Poche/service/tasks/ActionRequestTask.java
@@ -16,7 +16,7 @@ public class ActionRequestTask extends SimpleTask {
protected ActionRequest actionRequest;
public ActionRequestTask(ActionRequest actionRequest) {
- Objects.requireNonNull(actionRequest, "actionRequest is null");
+ Objects.requireNonNull(actionRequest);
this.actionRequest = actionRequest;
}
diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/service/tasks/SimpleTask.java b/app/src/main/java/fr/gaulupeau/apps/Poche/service/tasks/SimpleTask.java
index 2a8dbd203..17c2f05cf 100644
--- a/app/src/main/java/fr/gaulupeau/apps/Poche/service/tasks/SimpleTask.java
+++ b/app/src/main/java/fr/gaulupeau/apps/Poche/service/tasks/SimpleTask.java
@@ -52,7 +52,7 @@ public T createFromParcel(Parcel in) {
T instance;
try {
instance = clazz.newInstance();
- } catch (IllegalAccessException | InstantiationException e) {
+ } catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Uh oh");
}
diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/service/workers/ArticleUpdater.java b/app/src/main/java/fr/gaulupeau/apps/Poche/service/workers/ArticleUpdater.java
index b78109f2c..2547acab9 100644
--- a/app/src/main/java/fr/gaulupeau/apps/Poche/service/workers/ArticleUpdater.java
+++ b/app/src/main/java/fr/gaulupeau/apps/Poche/service/workers/ArticleUpdater.java
@@ -5,6 +5,8 @@
import android.util.Log;
import android.util.Pair;
+import androidx.core.util.ObjectsCompat;
+
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
@@ -13,7 +15,6 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Set;
import fr.gaulupeau.apps.Poche.data.DbUtils;
@@ -357,15 +358,15 @@ private void processArticle(ArticlesChangedEvent event, boolean full,
article.setUpdateDate(apiArticle.updatedAt);
articleChanges.add(ChangeType.UPDATED_DATE_CHANGED);
}
- if (!Objects.equals(article.getPublishedAt(), apiArticle.publishedAt)) {
+ if (!ObjectsCompat.equals(article.getPublishedAt(), apiArticle.publishedAt)) {
article.setPublishedAt(apiArticle.publishedAt);
articleChanges.add(ChangeType.PUBLISHED_AT_CHANGED);
}
- if (!Objects.equals(article.getStarredAt(), apiArticle.starredAt)) {
+ if (!ObjectsCompat.equals(article.getStarredAt(), apiArticle.starredAt)) {
article.setStarredAt(apiArticle.starredAt);
articleChanges.add(ChangeType.STARRED_AT_CHANGED);
}
- if (!Objects.equals(article.getIsPublic(), apiArticle.isPublic)) {
+ if (!ObjectsCompat.equals(article.getIsPublic(), apiArticle.isPublic)) {
article.setIsPublic(apiArticle.isPublic);
articleChanges.add(ChangeType.IS_PUBLIC_CHANGED);
}
@@ -552,11 +553,11 @@ private boolean processAnnotations(wallabag.apiwrapper.models.Article apiArticle
annotation.setQuote(apiAnnotation.quote);
annotationChanged = true;
}
- if (!Objects.equals(annotation.getCreatedAt(), apiAnnotation.createdAt)) {
+ if (!ObjectsCompat.equals(annotation.getCreatedAt(), apiAnnotation.createdAt)) {
annotation.setCreatedAt(apiAnnotation.createdAt);
annotationChanged = true;
}
- if (!Objects.equals(annotation.getUpdatedAt(), apiAnnotation.updatedAt)) {
+ if (!ObjectsCompat.equals(annotation.getUpdatedAt(), apiAnnotation.updatedAt)) {
annotation.setUpdatedAt(apiAnnotation.updatedAt);
annotationChanged = true;
}
diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/service/workers/OfflineChangesSynchronizer.java b/app/src/main/java/fr/gaulupeau/apps/Poche/service/workers/OfflineChangesSynchronizer.java
index 371931374..e83fe6f60 100644
--- a/app/src/main/java/fr/gaulupeau/apps/Poche/service/workers/OfflineChangesSynchronizer.java
+++ b/app/src/main/java/fr/gaulupeau/apps/Poche/service/workers/OfflineChangesSynchronizer.java
@@ -5,11 +5,12 @@
import android.util.Log;
import android.util.Pair;
+import androidx.core.util.ObjectsCompat;
+
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.Objects;
import fr.gaulupeau.apps.Poche.data.DbUtils;
import fr.gaulupeau.apps.Poche.data.QueueHelper;
@@ -307,7 +308,7 @@ private void addLink(AddLinkItem item) throws IncorrectConfigurationException,
}
private void updateGivenUrl(Article article, String givenUrl) {
- if (!Objects.equals(article.getGivenUrl(), givenUrl)) {
+ if (!ObjectsCompat.equals(article.getGivenUrl(), givenUrl)) {
article.setGivenUrl(givenUrl);
article.update();
}
diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/tts/TextItem.java b/app/src/main/java/fr/gaulupeau/apps/Poche/tts/TextItem.java
index 59db8947f..84565977d 100644
--- a/app/src/main/java/fr/gaulupeau/apps/Poche/tts/TextItem.java
+++ b/app/src/main/java/fr/gaulupeau/apps/Poche/tts/TextItem.java
@@ -29,7 +29,7 @@ static Type getType(String type) {
Extra() {}
Extra(Extra.Type type, int start, int end) {
- this.type = Objects.requireNonNull(type, "type cannot be null");
+ this.type = Objects.requireNonNull(type);
this.start = start;
this.end = end;
}
diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/tts/TtsService.java b/app/src/main/java/fr/gaulupeau/apps/Poche/tts/TtsService.java
index ca24c14a5..fce4c9ca8 100644
--- a/app/src/main/java/fr/gaulupeau/apps/Poche/tts/TtsService.java
+++ b/app/src/main/java/fr/gaulupeau/apps/Poche/tts/TtsService.java
@@ -34,6 +34,7 @@
import androidx.core.util.Pair;
import androidx.media.session.MediaButtonReceiver;
+import java.util.HashMap;
import java.util.Locale;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -744,7 +745,14 @@ private void ttsSpeak(CharSequence text, int queueMode, String utteranceId) {
Log.v(TAG, "ttsSpeak() speaking " + utteranceId + ": " + text);
- tts.speak(text, queueMode, null, utteranceId);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ tts.speak(text, queueMode, null, utteranceId);
+ } else {
+ HashMap params = new HashMap<>(2);
+ params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, utteranceId);
+ //noinspection deprecation
+ tts.speak(text.toString(), queueMode, params);
+ }
Log.v(TAG, "ttsSpeak() call returned");
}
@@ -825,55 +833,59 @@ private void onTtsInit(int status) {
state = State.CREATED;
}
- tts.setOnUtteranceProgressListener(new UtteranceProgressListener() {
- @Override
- public void onStart(String utteranceId) {
-// Log.v(TAG, "utteranceProgressListener.onStart()");
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
+ tts.setOnUtteranceProgressListener(new UtteranceProgressListener() {
+ @Override
+ public void onStart(String utteranceId) {
+ // Log.v(TAG, "utteranceProgressListener.onStart()");
- setCurrentItemProgress(utteranceId, -1, -1);
- }
+ setCurrentItemProgress(utteranceId, -1, -1);
+ }
- @Override
- public synchronized void onDone(String utteranceId) {
- onSpeakDoneListener(utteranceId);
- }
+ @Override
+ public synchronized void onDone(String utteranceId) {
+ onSpeakDoneListener(utteranceId);
+ }
- @Override
- public void onError(String utteranceId, int errorCode) {
- Log.w(TAG, "utteranceProgressListener.onError() " + utteranceId
- + ", errorCode: " + errorCode);
- super.onError(utteranceId, errorCode);
- }
+ @Override
+ public void onError(String utteranceId, int errorCode) {
+ Log.w(TAG, "utteranceProgressListener.onError() " + utteranceId
+ + ", errorCode: " + errorCode);
+ super.onError(utteranceId, errorCode);
+ }
- @Override
- public void onError(String utteranceId) {
- Log.w(TAG, "utteranceProgressListener.onError() " + utteranceId);
+ @Override
+ public void onError(String utteranceId) {
+ Log.w(TAG, "utteranceProgressListener.onError() " + utteranceId);
- resetCurrentItemProgress();
+ resetCurrentItemProgress();
- onSpeakDoneListener(utteranceId);
- }
+ onSpeakDoneListener(utteranceId);
+ }
- @Override
- public void onStop(String utteranceId, boolean interrupted) {
- Log.d(TAG, "utteranceProgressListener.onStop() " + utteranceId
- + ", " + interrupted);
+ @Override
+ public void onStop(String utteranceId, boolean interrupted) {
+ Log.d(TAG, "utteranceProgressListener.onStop() " + utteranceId
+ + ", " + interrupted);
- resetCurrentItemProgress();
+ resetCurrentItemProgress();
- onSpeakDoneListener(utteranceId);
- }
+ onSpeakDoneListener(utteranceId);
+ }
- @Override
- public void onRangeStart(String utteranceId, int start, int end, int frame) {
- super.onRangeStart(utteranceId, start, end, frame);
+ @Override
+ public void onRangeStart(String utteranceId, int start, int end, int frame) {
+ super.onRangeStart(utteranceId, start, end, frame);
-// Log.v(TAG, String.format("utteranceProgressListener.onRangeStart(%s, %d, %d, %d)",
-// utteranceId, start, end, frame));
+ // Log.v(TAG, String.format("utteranceProgressListener.onRangeStart(%s, %d, %d, %d)",
+ // utteranceId, start, end, frame));
- setCurrentItemProgress(utteranceId, start, end);
- }
- });
+ setCurrentItemProgress(utteranceId, start, end);
+ }
+ });
+ } else {
+ tts.setOnUtteranceCompletedListener(this::onSpeakDoneListener);
+ }
tts.setLanguage(convertVoiceNameToLocale(ttsVoice));
@@ -934,7 +946,11 @@ public void setEngineAndVoice(String engine, String voice) {
final TextToSpeech ttsToShutdown = tts;
new Thread(() -> {
- ttsToShutdown.setOnUtteranceProgressListener(null);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
+ ttsToShutdown.setOnUtteranceProgressListener(null);
+ } else {
+ ttsToShutdown.setOnUtteranceCompletedListener(null);
+ }
ttsToShutdown.stop();
ttsToShutdown.shutdown();
}).start();
diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/tts/WebViewText.java b/app/src/main/java/fr/gaulupeau/apps/Poche/tts/WebViewText.java
index 3969f6541..2bbb70a92 100644
--- a/app/src/main/java/fr/gaulupeau/apps/Poche/tts/WebViewText.java
+++ b/app/src/main/java/fr/gaulupeau/apps/Poche/tts/WebViewText.java
@@ -59,8 +59,8 @@ void parseWebViewDocument(Runnable callback) {
parsingFinishedCallback = callback;
ttsHost.getJsTtsController().setWebViewText(this);
- ttsHost.getWebView().evaluateJavascript("javascript:" + JS_PARSE_DOCUMENT_SCRIPT
- + ";parseDocumentText();", null);
+ ttsHost.getWebView().loadUrl("javascript:" + JS_PARSE_DOCUMENT_SCRIPT);
+ ttsHost.getWebView().loadUrl("javascript:parseDocumentText()");
}
void onDocumentParseStart() {
diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ArticleListFragment.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ArticleListFragment.java
index 746b298df..fbf819569 100644
--- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ArticleListFragment.java
+++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ArticleListFragment.java
@@ -219,8 +219,12 @@ private QueryBuilder getQueryBuilder() {
}
if (!TextUtils.isEmpty(searchQuery)) {
- qb.where(new WhereCondition.PropertyCondition(ArticleDao.Properties.Id, " IN (" +
- FtsDao.getQueryString() + DatabaseUtils.sqlEscapeString(searchQuery) + ")"));
+ if (FtsDao.isFtsSupported()) {
+ qb.where(new WhereCondition.PropertyCondition(ArticleDao.Properties.Id, " IN (" +
+ FtsDao.getQueryString() + DatabaseUtils.sqlEscapeString(searchQuery) + ")"));
+ } else {
+ qb.where(ArticleDao.Properties.Title.like("%" + searchQuery + "%"));
+ }
}
switch (sortOrder) {
diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/JsActionController.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/JsActionController.java
new file mode 100644
index 000000000..c18f02776
--- /dev/null
+++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/JsActionController.java
@@ -0,0 +1,25 @@
+package fr.gaulupeau.apps.Poche.ui;
+
+import android.webkit.JavascriptInterface;
+
+public class JsActionController {
+
+ public interface Callback {
+ void selectedText(String text);
+ }
+
+ private final Callback callback;
+
+ public JsActionController(Callback callback) {
+ this.callback = callback;
+ }
+
+ @SuppressWarnings("unused")
+ @JavascriptInterface
+ public void selectedText(String text) {
+ if (callback != null) {
+ callback.selectedText(text);
+ }
+ }
+
+}
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 6ca77994c..9f09b8f07 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
@@ -2,6 +2,7 @@
import android.annotation.SuppressLint;
import android.content.Intent;
+import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
@@ -19,7 +20,6 @@
import android.webkit.ConsoleMessage;
import android.webkit.HttpAuthHandler;
import android.webkit.WebChromeClient;
-import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
@@ -64,7 +64,7 @@
import fr.gaulupeau.apps.Poche.tts.TtsFragment;
import fr.gaulupeau.apps.Poche.tts.TtsHost;
-import static android.text.Html.escapeHtml;
+import static fr.gaulupeau.apps.Poche.utils.TextTools.escapeHtml;
public class ReadArticleActivity extends BaseActionBarActivity {
@@ -161,7 +161,9 @@ public void onCreate(Bundle savedInstanceState) {
WallabagConnection.initConscrypt();
if (BuildConfig.DEBUG) {
- WebView.setWebContentsDebuggingEnabled(true);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ WebView.setWebContentsDebuggingEnabled(true);
+ }
}
settings = App.getSettings();
@@ -404,17 +406,21 @@ public void onActionModeStarted(ActionMode mode) {
mode.getMenuInflater().inflate(R.menu.read_article_activity, menu);
menu.findItem(R.id.menu_tag).setOnMenuItemClickListener(item -> {
- webViewContent.evaluateJavascript(
- "(function(){return window.getSelection().toString()})()",
- this::createTagFromSelection);
- mode.finish();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ webViewContent.evaluateJavascript(
+ "(function(){return window.getSelection().toString()})()",
+ this::createTagFromSelection);
+ mode.finish();
+ } else {
+ webViewContent.loadUrl("javascript:hostActionController.selectedText(window.getSelection().toString())");
+ }
return true;
});
MenuItem annotateItem = menu.findItem(R.id.menu_annotate);
if (annotationsEnabled) {
annotateItem.setOnMenuItemClickListener(i -> {
- webViewContent.evaluateJavascript("invokeAnnotator();", null);
+ webViewContent.loadUrl("javascript:invokeAnnotator()");
// mode.finish(); // seems to reset selection too early (not on emulator though)
return true;
});
@@ -620,6 +626,7 @@ private void updatePrevNextButtons() {
private void initWebView() {
webViewContent.getSettings().setJavaScriptEnabled(true);
+ initJsActionController();
initTtsController();
initAnnotationController();
@@ -677,10 +684,9 @@ public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
}
+ @SuppressWarnings("deprecation") // can't use newer method until API 21
@Override
- public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
- String url = request.getUrl().toString();
-
+ public boolean shouldOverrideUrlLoading(WebView webView, String url) {
// If we try to open current URL, do not propose to save it, directly open browser
if (url.equals(articleUrl)) {
openURL(url);
@@ -789,6 +795,12 @@ public boolean onSingleTapConfirmed(MotionEvent e) {
webViewContent.setOnTouchListener((v, event) -> gestureDetector.onTouchEvent(event));
}
+ private void initJsActionController() {
+ JsActionController jsActionController = new JsActionController(this::createTagFromSelection);
+
+ webViewContent.addJavascriptInterface(jsActionController, "hostActionController");
+ }
+
private void initTtsController() {
// add the controller now even if TTS is not used,
// otherwise it won't be possible to enable TTS without content reloading
diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/SettingsActivity.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/SettingsActivity.java
index f408fe6ec..461208d00 100644
--- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/SettingsActivity.java
+++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/SettingsActivity.java
@@ -5,6 +5,7 @@
import android.app.AlarmManager;
import android.content.DialogInterface;
import android.content.SharedPreferences;
+import android.os.Build;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
@@ -174,6 +175,14 @@ public void onCreate(Bundle savedInstanceState) {
handleHttpSchemePreference.setOnPreferenceChangeListener(this);
}
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ CheckBoxPreference ftsIcuTokenizerPreference = (CheckBoxPreference) findPreference(
+ getString(R.string.pref_key_misc_ftsIcuTokenizer_enabled));
+ if (ftsIcuTokenizerPreference != null) {
+ ftsIcuTokenizerPreference.setEnabled(false);
+ }
+ }
+
ListPreference dbPathListPreference = (ListPreference)findPreference(
getString(R.string.pref_key_storage_dbPath));
if(dbPathListPreference != null) {
diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/utils/TextTools.java b/app/src/main/java/fr/gaulupeau/apps/Poche/utils/TextTools.java
index 4e8eb5629..c22d49433 100644
--- a/app/src/main/java/fr/gaulupeau/apps/Poche/utils/TextTools.java
+++ b/app/src/main/java/fr/gaulupeau/apps/Poche/utils/TextTools.java
@@ -4,6 +4,8 @@
import android.text.Html;
import android.text.TextUtils;
+import androidx.core.text.TextUtilsCompat;
+
public class TextTools {
/**
@@ -19,6 +21,14 @@ public static boolean equalOrEmpty(CharSequence s1, CharSequence s2) {
|| TextUtils.equals(s1, s2);
}
+ public static String escapeHtml(String s) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ return Html.escapeHtml(s);
+ } else {
+ return TextUtilsCompat.htmlEncode(s); // not sure
+ }
+ }
+
public static String unescapeHtml(String s) {
if (s == null) return null;
diff --git a/app/src/main/res/layout/activity_manage_article_tags.xml b/app/src/main/res/layout/activity_manage_article_tags.xml
index e1f8d7d8a..71ccab1b0 100644
--- a/app/src/main/res/layout/activity_manage_article_tags.xml
+++ b/app/src/main/res/layout/activity_manage_article_tags.xml
@@ -21,6 +21,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/activity_horizontal_margin"
+ android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:text="@string/manageTags_currentTags_none"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?android:attr/textColorPrimary" />
diff --git a/app/src/main/res/layout/connection_wizard_provider_selection_fragment.xml b/app/src/main/res/layout/connection_wizard_provider_selection_fragment.xml
index 0afb16de1..4a7f06460 100644
--- a/app/src/main/res/layout/connection_wizard_provider_selection_fragment.xml
+++ b/app/src/main/res/layout/connection_wizard_provider_selection_fragment.xml
@@ -70,6 +70,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableStart="@drawable/ic_qrcode_24dp"
+ android:drawableLeft="@drawable/ic_qrcode_24dp"
android:text="@string/connectionWizard_misc_scanQrCode" />
diff --git a/app/src/main/res/layout/fragment_tts.xml b/app/src/main/res/layout/fragment_tts.xml
index e4611011c..e19bc27c8 100644
--- a/app/src/main/res/layout/fragment_tts.xml
+++ b/app/src/main/res/layout/fragment_tts.xml
@@ -1,4 +1,5 @@
+ app:srcCompat="@drawable/ic_snooze_24dp" />
+ app:srcCompat="@drawable/ic_delete_black_24dp" />
diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml
new file mode 100644
index 000000000..1dc9082f5
--- /dev/null
+++ b/app/src/main/res/values-v21/styles.xml
@@ -0,0 +1,197 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 4d155b2eb..fe5d947a3 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -50,8 +50,6 @@
@@ -63,12 +61,11 @@
@@ -107,11 +102,10 @@