Fix travel bookmarks

This commit is contained in:
max-klaus 2021-01-05 21:03:06 +03:00
parent ae9fc3234c
commit 03dc11febe
9 changed files with 217 additions and 127 deletions

View file

@ -289,18 +289,17 @@ public class WikiArticleHelper {
@Nullable @Nullable
public static String getPartialContent(String source) { public static String getPartialContent(String source) {
if (source == null) { if (Algorithms.isEmpty(source)) {
return null; return null;
} }
String content = source.replaceAll("\\n", ""); String content = source.replaceAll("\\n", "");
int firstParagraphStart = content.indexOf(P_OPENED); int firstParagraphStart = content.indexOf(P_OPENED);
int firstParagraphEnd = content.indexOf(P_CLOSED); int firstParagraphEnd = content.indexOf(P_CLOSED);
firstParagraphEnd = firstParagraphEnd < firstParagraphStart ? content.indexOf(P_CLOSED, firstParagraphStart) : firstParagraphEnd; firstParagraphEnd = firstParagraphEnd < firstParagraphStart ? content.indexOf(P_CLOSED, firstParagraphStart) : firstParagraphEnd;
if (firstParagraphStart == -1 || firstParagraphEnd == -1 String firstParagraphHtml = null;
|| firstParagraphEnd < firstParagraphStart) { if (firstParagraphStart != -1 && firstParagraphEnd != -1
return null; && firstParagraphEnd >= firstParagraphStart) {
} firstParagraphHtml = content.substring(firstParagraphStart, firstParagraphEnd + P_CLOSED.length());
String firstParagraphHtml = content.substring(firstParagraphStart, firstParagraphEnd + P_CLOSED.length());
while (firstParagraphHtml.substring(P_OPENED.length(), firstParagraphHtml.length() - P_CLOSED.length()).trim().isEmpty() while (firstParagraphHtml.substring(P_OPENED.length(), firstParagraphHtml.length() - P_CLOSED.length()).trim().isEmpty()
&& (firstParagraphEnd + P_CLOSED.length()) < content.length()) { && (firstParagraphEnd + P_CLOSED.length()) < content.length()) {
firstParagraphStart = content.indexOf(P_OPENED, firstParagraphEnd); firstParagraphStart = content.indexOf(P_OPENED, firstParagraphEnd);
@ -311,11 +310,18 @@ public class WikiArticleHelper {
break; break;
} }
} }
}
if (Algorithms.isEmpty(firstParagraphHtml)) {
firstParagraphHtml = source;
}
if (Algorithms.isEmpty(firstParagraphHtml)) {
return null;
}
String firstParagraphText = Html.fromHtml(firstParagraphHtml.replaceAll("(<(/)(a|img)>)|(<(a|img).+?>)|(<div.+?/div>)", "")) String firstParagraphText = Html.fromHtml(firstParagraphHtml.replaceAll("(<(/)(a|img)>)|(<(a|img).+?>)|(<div.+?/div>)", ""))
.toString().trim(); .toString().trim();
String[] phrases = firstParagraphText.split("\\. "); String[] phrases = firstParagraphText.split("\\. ");
StringBuilder res = new StringBuilder(); StringBuilder res = new StringBuilder();
int limit = Math.min(phrases.length, PARTIAL_CONTENT_PHRASES); int limit = Math.min(phrases.length, PARTIAL_CONTENT_PHRASES);
for (int i = 0; i < limit; i++) { for (int i = 0; i < limit; i++) {
@ -324,7 +330,6 @@ public class WikiArticleHelper {
res.append(". "); res.append(". ");
} }
} }
return res.toString(); return res.toString();
} }

View file

@ -373,7 +373,7 @@ public class WikivoyageArticleDialogFragment extends WikiArticleBaseDialogFragme
@NonNull String title, @NonNull String title,
@NonNull String lang) { @NonNull String lang) {
TravelArticleIdentifier articleId = app.getTravelHelper().getArticleId(title, lang); TravelArticleIdentifier articleId = app.getTravelHelper().getArticleId(title, lang);
return showInstance(app, fm, articleId, lang); return articleId != null && showInstance(app, fm, articleId, lang);
} }
public static boolean showInstance(@NonNull OsmandApplication app, public static boolean showInstance(@NonNull OsmandApplication app,

View file

@ -9,11 +9,9 @@ import androidx.annotation.Nullable;
import androidx.annotation.Size; import androidx.annotation.Size;
import net.osmand.GPXUtilities.GPXFile; import net.osmand.GPXUtilities.GPXFile;
import net.osmand.Location; import net.osmand.IndexConstants;
import net.osmand.aidl.search.SearchResult; import net.osmand.plus.OsmandApplication;
import net.osmand.data.LatLon;
import net.osmand.util.Algorithms; import net.osmand.util.Algorithms;
import net.osmand.util.MapUtils;
import org.apache.commons.codec.binary.Hex; import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.digest.DigestUtils;
@ -22,8 +20,6 @@ import java.io.File;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Objects;
public class TravelArticle { public class TravelArticle {
@ -40,22 +36,40 @@ public class TravelArticle {
String imageTitle; String imageTitle;
GPXFile gpxFile; GPXFile gpxFile;
String routeId; String routeId;
String routeSource; String routeSource = "";
long originalId; long originalId;
String lang; String lang;
String contentsJson; String contentsJson;
String aggregatedPartOf; String aggregatedPartOf;
String fullContent;
long lastModified;
@NonNull @NonNull
public TravelArticleIdentifier generateIdentifier() { public TravelArticleIdentifier generateIdentifier() {
return new TravelArticleIdentifier(this); return new TravelArticleIdentifier(this);
} }
@NonNull
public static String getTravelBook(@NonNull OsmandApplication app, @NonNull File file) {
return file.getPath().replace(app.getAppPath(IndexConstants.WIKIVOYAGE_INDEX_DIR).getPath() + "/", "");
}
@Nullable
public String getTravelBook(@NonNull OsmandApplication app) {
return file != null ? getTravelBook(app, file) : null;
}
public File getFile() { public File getFile() {
return file; return file;
} }
public long getLastModified() {
if (lastModified > 0) {
return lastModified;
}
return file != null ? file.lastModified() : 0;
}
public String getTitle() { public String getTitle() {
return title; return title;
} }

View file

@ -563,6 +563,9 @@ public class TravelDbHelper implements TravelHelper {
cursor.close(); cursor.close();
} }
} }
if (res == null) {
res = localDataHelper.getSavedArticle(articleId.file, articleId.routeId, lang);
}
return res; return res;
} }
@ -643,13 +646,19 @@ public class TravelDbHelper implements TravelHelper {
cursor.close(); cursor.close();
} }
} }
if (res.isEmpty()) {
List<TravelArticle> articles = localDataHelper.getSavedArticles(articleId.file, articleId.routeId);
for (TravelArticle a : articles) {
res.add(a.getLang());
}
}
return res; return res;
} }
@NonNull @NonNull
private TravelArticle readArticle(SQLiteCursor cursor) { private TravelArticle readArticle(SQLiteCursor cursor) {
TravelArticle res = new TravelArticle(); TravelArticle res = new TravelArticle();
res.file = selectedTravelBook;
res.title = cursor.getString(0); res.title = cursor.getString(0);
try { try {
res.content = Algorithms.gzipToString(cursor.getBlob(1)).trim(); res.content = Algorithms.gzipToString(cursor.getBlob(1)).trim();
@ -671,7 +680,6 @@ public class TravelDbHelper implements TravelHelper {
} catch (IOException e) { } catch (IOException e) {
LOG.error(e.getMessage(), e); LOG.error(e.getMessage(), e);
} }
return res; return res;
} }

View file

@ -56,8 +56,7 @@ public interface TravelHelper {
File createGpxFile(@NonNull final TravelArticle article); File createGpxFile(@NonNull final TravelArticle article);
// TODO: this method should be deleted once TravelDBHelper is deleted // TODO: this method should be deleted once TravelDBHelper is deleted
// For TravelOBFHelper it could always return "" and should be no problem @Nullable
// Bookmarks should be refactored properly to support multiple files
String getSelectedTravelBookName(); String getSelectedTravelBookName();
String getWikivoyageFileName(); String getWikivoyageFileName();

View file

@ -4,11 +4,13 @@ package net.osmand.plus.wikivoyage.data;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import net.osmand.IndexConstants;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
import net.osmand.plus.api.SQLiteAPI.SQLiteConnection; import net.osmand.plus.api.SQLiteAPI.SQLiteConnection;
import net.osmand.plus.api.SQLiteAPI.SQLiteCursor; import net.osmand.plus.api.SQLiteAPI.SQLiteCursor;
import net.osmand.plus.wikipedia.WikiArticleHelper; import net.osmand.util.Algorithms;
import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
@ -24,12 +26,12 @@ public class TravelLocalDataHelper {
private static final int HISTORY_ITEMS_LIMIT = 300; private static final int HISTORY_ITEMS_LIMIT = 300;
private WikivoyageLocalDataDbHelper dbHelper; private final WikivoyageLocalDataDbHelper dbHelper;
private Map<String, WikivoyageSearchHistoryItem> historyMap = new HashMap<>(); private Map<String, WikivoyageSearchHistoryItem> historyMap = new HashMap<>();
private List<TravelArticle> savedArticles = new ArrayList<>(); private List<TravelArticle> savedArticles = new ArrayList<>();
private Set<Listener> listeners = new HashSet<>(); private final Set<Listener> listeners = new HashSet<>();
public void addListener(Listener listener) { public void addListener(Listener listener) {
listeners.add(listener); listeners.add(listener);
@ -70,21 +72,21 @@ public class TravelLocalDataHelper {
} }
public void addToHistory(@NonNull TravelArticle article) { public void addToHistory(@NonNull TravelArticle article) {
addToHistory(article.getTitle(), article.getLang(), article.getIsPartOf()); File file = article.getFile();
} String title = article.getTitle();
String lang = article.getLang();
String isPartOf = article.getIsPartOf();
public void addToHistory(String title, String lang, String isPartOf) { WikivoyageSearchHistoryItem item = new WikivoyageSearchHistoryItem();
String key = getHistoryKey(lang, title); item.articleFile = file;
WikivoyageSearchHistoryItem item = historyMap.get(key);
boolean newItem = item == null;
if (newItem) {
item = new WikivoyageSearchHistoryItem();
}
item.articleTitle = title; item.articleTitle = title;
item.lang = lang; item.lang = lang;
item.isPartOf = isPartOf; item.isPartOf = isPartOf;
item.lastAccessed = System.currentTimeMillis(); item.lastAccessed = System.currentTimeMillis();
if (newItem) {
String key = item.getKey();
boolean exists = historyMap.containsKey(key);
if (!exists) {
dbHelper.addHistoryItem(item); dbHelper.addHistoryItem(item);
historyMap.put(key, item); historyMap.put(key, item);
} else { } else {
@ -98,10 +100,6 @@ public class TravelLocalDataHelper {
} }
} }
static String getHistoryKey(String lang, String title) {
return lang + ":"+title;
}
@NonNull @NonNull
public List<TravelArticle> getSavedArticles() { public List<TravelArticle> getSavedArticles() {
return new ArrayList<>(savedArticles); return new ArrayList<>(savedArticles);
@ -109,19 +107,8 @@ public class TravelLocalDataHelper {
public void addArticleToSaved(@NonNull TravelArticle article) { public void addArticleToSaved(@NonNull TravelArticle article) {
if (!isArticleSaved(article)) { if (!isArticleSaved(article)) {
TravelArticle saved = new TravelArticle(); savedArticles.add(article);
saved.title = article.title; dbHelper.addSavedArticle(article);
saved.lang = article.lang;
saved.aggregatedPartOf = article.aggregatedPartOf;
saved.imageTitle = article.imageTitle;
saved.content = WikiArticleHelper.getPartialContent(article.getContent());
saved.lat = article.lat;
saved.lon = article.lon;
saved.routeId = article.routeId;
saved.fullContent = article.getContent();
saved.contentsJson = article.contentsJson;
savedArticles.add(saved);
dbHelper.addSavedArticle(saved);
notifySavedUpdated(); notifySavedUpdated();
} }
} }
@ -164,17 +151,29 @@ public class TravelLocalDataHelper {
} }
@Nullable @Nullable
public TravelArticle getSavedArticle(String routeId, String lang) { public TravelArticle getSavedArticle(File file, String routeId, String lang) {
for (TravelArticle article : savedArticles) { for (TravelArticle article : savedArticles) {
if (article.routeId != null && article.routeId.equals(routeId) if (Algorithms.objectEquals(article.file, file)
&& article.lang != null && article.lang.equals(lang)) { && Algorithms.stringsEqual(article.routeId, routeId)
article.content = article.fullContent; && Algorithms.stringsEqual(article.lang, lang)) {
return article; return article;
} }
} }
return null; return null;
} }
@NonNull
public List<TravelArticle> getSavedArticles(File file, String routeId) {
List<TravelArticle> articles = new ArrayList<>();
for (TravelArticle article : savedArticles) {
if (Algorithms.objectEquals(article.file, file)
&& Algorithms.stringsEqual(article.routeId, routeId)) {
articles.add(article);
}
}
return articles;
}
public interface Listener { public interface Listener {
void savedArticlesUpdated(); void savedArticlesUpdated();
@ -182,7 +181,7 @@ public class TravelLocalDataHelper {
private static class WikivoyageLocalDataDbHelper { private static class WikivoyageLocalDataDbHelper {
private static final int DB_VERSION = 6; private static final int DB_VERSION = 7;
private static final String DB_NAME = "wikivoyage_local_data"; private static final String DB_NAME = "wikivoyage_local_data";
private static final String HISTORY_TABLE_NAME = "wikivoyage_search_history"; private static final String HISTORY_TABLE_NAME = "wikivoyage_search_history";
@ -219,6 +218,7 @@ public class TravelLocalDataHelper {
private static final String BOOKMARKS_COL_ROUTE_ID = "route_id"; private static final String BOOKMARKS_COL_ROUTE_ID = "route_id";
private static final String BOOKMARKS_COL_CONTENT_JSON = "content_json"; private static final String BOOKMARKS_COL_CONTENT_JSON = "content_json";
private static final String BOOKMARKS_COL_CONTENT = "content"; private static final String BOOKMARKS_COL_CONTENT = "content";
private static final String BOOKMARKS_COL_LAST_MODIFIED = "last_modified";
private static final String BOOKMARKS_TABLE_CREATE = "CREATE TABLE IF NOT EXISTS " + private static final String BOOKMARKS_TABLE_CREATE = "CREATE TABLE IF NOT EXISTS " +
BOOKMARKS_TABLE_NAME + " (" + BOOKMARKS_TABLE_NAME + " (" +
@ -226,25 +226,26 @@ public class TravelLocalDataHelper {
BOOKMARKS_COL_LANG + " TEXT, " + BOOKMARKS_COL_LANG + " TEXT, " +
BOOKMARKS_COL_IS_PART_OF + " TEXT, " + BOOKMARKS_COL_IS_PART_OF + " TEXT, " +
BOOKMARKS_COL_IMAGE_TITLE + " TEXT, " + BOOKMARKS_COL_IMAGE_TITLE + " TEXT, " +
BOOKMARKS_COL_PARTIAL_CONTENT + " TEXT, " +
BOOKMARKS_COL_TRAVEL_BOOK + " TEXT, " + BOOKMARKS_COL_TRAVEL_BOOK + " TEXT, " +
BOOKMARKS_COL_LAT + " double, " + BOOKMARKS_COL_LAT + " double, " +
BOOKMARKS_COL_LON + " double, " + BOOKMARKS_COL_LON + " double, " +
BOOKMARKS_COL_ROUTE_ID + " TEXT, " + BOOKMARKS_COL_ROUTE_ID + " TEXT, " +
BOOKMARKS_COL_CONTENT_JSON + " TEXT, " + BOOKMARKS_COL_CONTENT_JSON + " TEXT, " +
BOOKMARKS_COL_CONTENT + " TEXT" + ");"; BOOKMARKS_COL_CONTENT + " TEXT, " +
BOOKMARKS_COL_LAST_MODIFIED + " long" + ");";
private static final String BOOKMARKS_TABLE_SELECT = "SELECT " + private static final String BOOKMARKS_TABLE_SELECT = "SELECT " +
BOOKMARKS_COL_ARTICLE_TITLE + ", " + BOOKMARKS_COL_ARTICLE_TITLE + ", " +
BOOKMARKS_COL_LANG + ", " + BOOKMARKS_COL_LANG + ", " +
BOOKMARKS_COL_IS_PART_OF + ", " + BOOKMARKS_COL_IS_PART_OF + ", " +
BOOKMARKS_COL_IMAGE_TITLE + ", " + BOOKMARKS_COL_IMAGE_TITLE + ", " +
BOOKMARKS_COL_PARTIAL_CONTENT + ", " + BOOKMARKS_COL_TRAVEL_BOOK + ", " +
BOOKMARKS_COL_LAT + ", " + BOOKMARKS_COL_LAT + ", " +
BOOKMARKS_COL_LON + ", " + BOOKMARKS_COL_LON + ", " +
BOOKMARKS_COL_ROUTE_ID + ", " + BOOKMARKS_COL_ROUTE_ID + ", " +
BOOKMARKS_COL_CONTENT_JSON + ", " + BOOKMARKS_COL_CONTENT_JSON + ", " +
BOOKMARKS_COL_CONTENT + BOOKMARKS_COL_CONTENT + ", " +
BOOKMARKS_COL_LAST_MODIFIED +
" FROM " + BOOKMARKS_TABLE_NAME; " FROM " + BOOKMARKS_TABLE_NAME;
private final OsmandApplication context; private final OsmandApplication context;
@ -253,8 +254,12 @@ public class TravelLocalDataHelper {
this.context = context; this.context = context;
} }
@Nullable
private SQLiteConnection openConnection(boolean readonly) { private SQLiteConnection openConnection(boolean readonly) {
SQLiteConnection conn = context.getSQLiteAPI().getOrCreateDatabase(DB_NAME, readonly); SQLiteConnection conn = context.getSQLiteAPI().getOrCreateDatabase(DB_NAME, readonly);
if (conn == null) {
return null;
}
if (conn.getVersion() < DB_VERSION) { if (conn.getVersion() < DB_VERSION) {
if (readonly) { if (readonly) {
conn.close(); conn.close();
@ -283,7 +288,7 @@ public class TravelLocalDataHelper {
if (oldVersion < 3) { if (oldVersion < 3) {
conn.execSQL("ALTER TABLE " + HISTORY_TABLE_NAME + " ADD " + HISTORY_COL_TRAVEL_BOOK + " TEXT"); conn.execSQL("ALTER TABLE " + HISTORY_TABLE_NAME + " ADD " + HISTORY_COL_TRAVEL_BOOK + " TEXT");
conn.execSQL("ALTER TABLE " + BOOKMARKS_TABLE_NAME + " ADD " + BOOKMARKS_COL_TRAVEL_BOOK + " TEXT"); conn.execSQL("ALTER TABLE " + BOOKMARKS_TABLE_NAME + " ADD " + BOOKMARKS_COL_TRAVEL_BOOK + " TEXT");
String selectedTravelBookName = getSelectedTravelBookName(); String selectedTravelBookName = context.getTravelHelper().getSelectedTravelBookName();
if (selectedTravelBookName != null) { if (selectedTravelBookName != null) {
Object[] args = new Object[]{selectedTravelBookName}; Object[] args = new Object[]{selectedTravelBookName};
conn.execSQL("UPDATE " + HISTORY_TABLE_NAME + " SET " + HISTORY_COL_TRAVEL_BOOK + " = ?", args); conn.execSQL("UPDATE " + HISTORY_TABLE_NAME + " SET " + HISTORY_COL_TRAVEL_BOOK + " = ?", args);
@ -301,20 +306,23 @@ public class TravelLocalDataHelper {
conn.execSQL("ALTER TABLE " + BOOKMARKS_TABLE_NAME + " ADD " + BOOKMARKS_COL_CONTENT_JSON + " TEXT"); conn.execSQL("ALTER TABLE " + BOOKMARKS_TABLE_NAME + " ADD " + BOOKMARKS_COL_CONTENT_JSON + " TEXT");
conn.execSQL("ALTER TABLE " + BOOKMARKS_TABLE_NAME + " ADD " + BOOKMARKS_COL_CONTENT + " TEXT"); conn.execSQL("ALTER TABLE " + BOOKMARKS_TABLE_NAME + " ADD " + BOOKMARKS_COL_CONTENT + " TEXT");
} }
if (oldVersion < 7) {
conn.execSQL("ALTER TABLE " + BOOKMARKS_TABLE_NAME + " ADD " + BOOKMARKS_COL_LAST_MODIFIED + " long");
conn.execSQL("UPDATE " + BOOKMARKS_TABLE_NAME +
" SET " + BOOKMARKS_COL_CONTENT + " = " + BOOKMARKS_COL_PARTIAL_CONTENT +
" WHERE " + BOOKMARKS_COL_CONTENT + " is null");
conn.execSQL("UPDATE " + BOOKMARKS_TABLE_NAME +
" SET " + BOOKMARKS_COL_PARTIAL_CONTENT + " = null");
}
} }
@NonNull @NonNull
Map<String, WikivoyageSearchHistoryItem> getAllHistoryMap() { Map<String, WikivoyageSearchHistoryItem> getAllHistoryMap() {
Map<String, WikivoyageSearchHistoryItem> res = new LinkedHashMap<>(); Map<String, WikivoyageSearchHistoryItem> res = new LinkedHashMap<>();
String travelBook = getSelectedTravelBookName();
if (travelBook == null) {
return res;
}
SQLiteConnection conn = openConnection(true); SQLiteConnection conn = openConnection(true);
if (conn != null) { if (conn != null) {
try { try {
String query = HISTORY_TABLE_SELECT + " WHERE " + HISTORY_COL_TRAVEL_BOOK + " = ?"; SQLiteCursor cursor = conn.rawQuery(HISTORY_TABLE_SELECT, null);
SQLiteCursor cursor = conn.rawQuery(query, new String[]{travelBook});
if (cursor != null) { if (cursor != null) {
if (cursor.moveToFirst()) { if (cursor.moveToFirst()) {
do { do {
@ -322,8 +330,8 @@ public class TravelLocalDataHelper {
res.put(item.getKey(), item); res.put(item.getKey(), item);
} while (cursor.moveToNext()); } while (cursor.moveToNext());
} }
}
cursor.close(); cursor.close();
}
} finally { } finally {
conn.close(); conn.close();
} }
@ -331,8 +339,8 @@ public class TravelLocalDataHelper {
return res; return res;
} }
void addHistoryItem(WikivoyageSearchHistoryItem item) { void addHistoryItem(@NonNull WikivoyageSearchHistoryItem item) {
String travelBook = getSelectedTravelBookName(); String travelBook = item.getTravelBook(context);
if (travelBook == null) { if (travelBook == null) {
return; return;
} }
@ -349,8 +357,8 @@ public class TravelLocalDataHelper {
} }
} }
void updateHistoryItem(WikivoyageSearchHistoryItem item) { void updateHistoryItem(@NonNull WikivoyageSearchHistoryItem item) {
String travelBook = getSelectedTravelBookName(); String travelBook = item.getTravelBook(context);
if (travelBook == null) { if (travelBook == null) {
return; return;
} }
@ -371,8 +379,8 @@ public class TravelLocalDataHelper {
} }
} }
void removeHistoryItem(WikivoyageSearchHistoryItem item) { void removeHistoryItem(@NonNull WikivoyageSearchHistoryItem item) {
String travelBook = getSelectedTravelBookName(); String travelBook = item.getTravelBook(context);
if (travelBook == null) { if (travelBook == null) {
return; return;
} }
@ -391,16 +399,10 @@ public class TravelLocalDataHelper {
} }
void clearAllHistory() { void clearAllHistory() {
String travelBook = getSelectedTravelBookName();
if (travelBook == null) {
return;
}
SQLiteConnection conn = openConnection(false); SQLiteConnection conn = openConnection(false);
if (conn != null) { if (conn != null) {
try { try {
conn.execSQL("DELETE FROM " + HISTORY_TABLE_NAME + conn.execSQL("DELETE FROM " + HISTORY_TABLE_NAME);
" WHERE " + HISTORY_COL_TRAVEL_BOOK + " = ?",
new Object[]{travelBook});
} finally { } finally {
conn.close(); conn.close();
} }
@ -410,19 +412,21 @@ public class TravelLocalDataHelper {
@NonNull @NonNull
List<TravelArticle> readSavedArticles() { List<TravelArticle> readSavedArticles() {
List<TravelArticle> res = new ArrayList<>(); List<TravelArticle> res = new ArrayList<>();
String travelBook = getSelectedTravelBookName();
if (travelBook == null) {
return res;
}
SQLiteConnection conn = openConnection(true); SQLiteConnection conn = openConnection(true);
if (conn != null) { if (conn != null) {
try { try {
String query = BOOKMARKS_TABLE_SELECT + " WHERE " + BOOKMARKS_COL_TRAVEL_BOOK + " = ?"; SQLiteCursor cursor = conn.rawQuery(BOOKMARKS_TABLE_SELECT, null);
SQLiteCursor cursor = conn.rawQuery(query, new String[]{travelBook});
if (cursor != null) { if (cursor != null) {
if (cursor.moveToFirst()) { if (cursor.moveToFirst()) {
do { do {
res.add(readSavedArticle(cursor)); TravelArticle dbArticle = readSavedArticle(cursor);
TravelArticle article = context.getTravelHelper().getArticleById(dbArticle.generateIdentifier(), dbArticle.lang);
if (article != null && article.getLastModified() > dbArticle.getLastModified()) {
updateSavedArticle(dbArticle, article);
res.add(article);
} else {
res.add(dbArticle);
}
} while (cursor.moveToNext()); } while (cursor.moveToNext());
} }
cursor.close(); cursor.close();
@ -434,8 +438,8 @@ public class TravelLocalDataHelper {
return res; return res;
} }
void addSavedArticle(TravelArticle article) { void addSavedArticle(@NonNull TravelArticle article) {
String travelBook = getSelectedTravelBookName(); String travelBook = article.getTravelBook(context);
if (travelBook == null) { if (travelBook == null) {
return; return;
} }
@ -447,26 +451,26 @@ public class TravelLocalDataHelper {
BOOKMARKS_COL_LANG + ", " + BOOKMARKS_COL_LANG + ", " +
BOOKMARKS_COL_IS_PART_OF + ", " + BOOKMARKS_COL_IS_PART_OF + ", " +
BOOKMARKS_COL_IMAGE_TITLE + ", " + BOOKMARKS_COL_IMAGE_TITLE + ", " +
BOOKMARKS_COL_PARTIAL_CONTENT + ", " +
BOOKMARKS_COL_TRAVEL_BOOK + ", " + BOOKMARKS_COL_TRAVEL_BOOK + ", " +
BOOKMARKS_COL_LAT + ", " + BOOKMARKS_COL_LAT + ", " +
BOOKMARKS_COL_LON + ", " + BOOKMARKS_COL_LON + ", " +
BOOKMARKS_COL_ROUTE_ID + ", " + BOOKMARKS_COL_ROUTE_ID + ", " +
BOOKMARKS_COL_CONTENT_JSON + ", " + BOOKMARKS_COL_CONTENT_JSON + ", " +
BOOKMARKS_COL_CONTENT + BOOKMARKS_COL_CONTENT + ", " +
BOOKMARKS_COL_LAST_MODIFIED +
") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
conn.execSQL(query, new Object[]{article.title, article.lang, conn.execSQL(query, new Object[]{article.title, article.lang,
article.aggregatedPartOf, article.imageTitle, article.content, article.aggregatedPartOf, article.imageTitle,
travelBook, article.lat, article.lon, article.routeId, article.contentsJson, travelBook, article.lat, article.lon, article.routeId, article.contentsJson,
article.fullContent}); article.content, article.getFile().lastModified()});
} finally { } finally {
conn.close(); conn.close();
} }
} }
} }
void removeSavedArticle(TravelArticle article) { void removeSavedArticle(@NonNull TravelArticle article) {
String travelBook = getSelectedTravelBookName(); String travelBook = article.getTravelBook(context);
if (travelBook == null) { if (travelBook == null) {
return; return;
} }
@ -475,44 +479,78 @@ public class TravelLocalDataHelper {
try { try {
conn.execSQL("DELETE FROM " + BOOKMARKS_TABLE_NAME + conn.execSQL("DELETE FROM " + BOOKMARKS_TABLE_NAME +
" WHERE " + BOOKMARKS_COL_ARTICLE_TITLE + " = ?" + " WHERE " + BOOKMARKS_COL_ARTICLE_TITLE + " = ?" +
" AND " + BOOKMARKS_COL_ROUTE_ID + " = ?" +
" AND " + BOOKMARKS_COL_LANG + " = ?" + " AND " + BOOKMARKS_COL_LANG + " = ?" +
" AND " + BOOKMARKS_COL_TRAVEL_BOOK + " = ?", " AND " + BOOKMARKS_COL_TRAVEL_BOOK + " = ?",
new Object[]{article.title, article.lang, travelBook}); new Object[]{article.title, article.routeId, article.lang, travelBook});
} finally { } finally {
conn.close(); conn.close();
} }
} }
} }
@Nullable void updateSavedArticle(@NonNull TravelArticle odlArticle, @NonNull TravelArticle newArticle) {
private String getSelectedTravelBookName() { String travelBook = odlArticle.getTravelBook(context);
return context.getTravelHelper().getSelectedTravelBookName(); if (travelBook == null) {
return;
}
SQLiteConnection conn = openConnection(false);
if (conn != null) {
try {
conn.execSQL("UPDATE " + BOOKMARKS_TABLE_NAME + " SET " +
BOOKMARKS_COL_ARTICLE_TITLE + " = ?, " +
BOOKMARKS_COL_LANG + " = ?, " +
BOOKMARKS_COL_IS_PART_OF + " = ?, " +
BOOKMARKS_COL_IMAGE_TITLE + " = ?, " +
BOOKMARKS_COL_TRAVEL_BOOK + " = ?, " +
BOOKMARKS_COL_LAT + " = ?, " +
BOOKMARKS_COL_LON + " = ?, " +
BOOKMARKS_COL_ROUTE_ID + " = ?, " +
BOOKMARKS_COL_CONTENT_JSON + " = ?, " +
BOOKMARKS_COL_CONTENT + " = ?, " +
BOOKMARKS_COL_LAST_MODIFIED + " = ?, " +
"WHERE " + BOOKMARKS_COL_ARTICLE_TITLE + " = ? " +
" AND " + BOOKMARKS_COL_ROUTE_ID + " = ?" +
" AND " + BOOKMARKS_COL_LANG + " = ?" +
" AND " + BOOKMARKS_COL_TRAVEL_BOOK + " = ?",
new Object[]{newArticle.title, newArticle.lang, newArticle.aggregatedPartOf,
newArticle.imageTitle, travelBook, newArticle.lat, newArticle.lon,
newArticle.routeId, newArticle.content, newArticle.contentsJson,
odlArticle.title, odlArticle.routeId, odlArticle.lang, travelBook});
} finally {
conn.close();
}
}
} }
@NonNull
private WikivoyageSearchHistoryItem readHistoryItem(SQLiteCursor cursor) { private WikivoyageSearchHistoryItem readHistoryItem(SQLiteCursor cursor) {
WikivoyageSearchHistoryItem res = new WikivoyageSearchHistoryItem(); WikivoyageSearchHistoryItem res = new WikivoyageSearchHistoryItem();
res.articleTitle = cursor.getString(cursor.getColumnIndex(HISTORY_COL_ARTICLE_TITLE)); res.articleTitle = cursor.getString(cursor.getColumnIndex(HISTORY_COL_ARTICLE_TITLE));
res.lang = cursor.getString(cursor.getColumnIndex(HISTORY_COL_LANG)); res.lang = cursor.getString(cursor.getColumnIndex(HISTORY_COL_LANG));
res.isPartOf = cursor.getString(cursor.getColumnIndex(HISTORY_COL_IS_PART_OF)); res.isPartOf = cursor.getString(cursor.getColumnIndex(HISTORY_COL_IS_PART_OF));
res.lastAccessed = cursor.getLong(cursor.getColumnIndex(HISTORY_COL_LAST_ACCESSED)); res.lastAccessed = cursor.getLong(cursor.getColumnIndex(HISTORY_COL_LAST_ACCESSED));
return res; return res;
} }
@NonNull
private TravelArticle readSavedArticle(SQLiteCursor cursor) { private TravelArticle readSavedArticle(SQLiteCursor cursor) {
TravelArticle res = new TravelArticle(); TravelArticle res = new TravelArticle();
res.title = cursor.getString(cursor.getColumnIndex(BOOKMARKS_COL_ARTICLE_TITLE)); res.title = cursor.getString(cursor.getColumnIndex(BOOKMARKS_COL_ARTICLE_TITLE));
res.lang = cursor.getString(cursor.getColumnIndex(BOOKMARKS_COL_LANG)); res.lang = cursor.getString(cursor.getColumnIndex(BOOKMARKS_COL_LANG));
res.aggregatedPartOf = cursor.getString(cursor.getColumnIndex(BOOKMARKS_COL_IS_PART_OF)); res.aggregatedPartOf = cursor.getString(cursor.getColumnIndex(BOOKMARKS_COL_IS_PART_OF));
res.imageTitle = cursor.getString(cursor.getColumnIndex(BOOKMARKS_COL_IMAGE_TITLE)); res.imageTitle = cursor.getString(cursor.getColumnIndex(BOOKMARKS_COL_IMAGE_TITLE));
res.content = cursor.getString(cursor.getColumnIndex(BOOKMARKS_COL_PARTIAL_CONTENT)); res.content = cursor.getString(cursor.getColumnIndex(BOOKMARKS_COL_CONTENT));
res.lat = cursor.getDouble(cursor.getColumnIndex(BOOKMARKS_COL_LAT)); res.lat = cursor.getDouble(cursor.getColumnIndex(BOOKMARKS_COL_LAT));
res.lon = cursor.getDouble(cursor.getColumnIndex(BOOKMARKS_COL_LON)); res.lon = cursor.getDouble(cursor.getColumnIndex(BOOKMARKS_COL_LON));
res.routeId = cursor.getString(cursor.getColumnIndex(BOOKMARKS_COL_ROUTE_ID)); res.routeId = cursor.getString(cursor.getColumnIndex(BOOKMARKS_COL_ROUTE_ID));
res.contentsJson = cursor.getString(cursor.getColumnIndex(BOOKMARKS_COL_CONTENT_JSON)); res.contentsJson = cursor.getString(cursor.getColumnIndex(BOOKMARKS_COL_CONTENT_JSON));
res.fullContent = cursor.getString(cursor.getColumnIndex(BOOKMARKS_COL_CONTENT)); String travelBook = cursor.getString(cursor.getColumnIndex(BOOKMARKS_COL_TRAVEL_BOOK));
if (!Algorithms.isEmpty(travelBook)) {
res.file = context.getAppPath(IndexConstants.WIKIVOYAGE_INDEX_DIR + travelBook);
res.lastModified = cursor.getLong(cursor.getColumnIndex(BOOKMARKS_COL_LAST_MODIFIED));
}
return res; return res;
} }
} }

View file

@ -278,7 +278,7 @@ public class TravelObfHelper implements TravelHelper {
parts = null; parts = null;
} }
Map<String, List<WikivoyageSearchResult>> navMap = new HashMap<>(); Map<String, List<WikivoyageSearchResult>> navMap = new HashMap<>();
Set<String> headers = new LinkedHashSet<String>(); Set<String> headers = new LinkedHashSet<>();
Map<String, WikivoyageSearchResult> headerObjs = new HashMap<>(); Map<String, WikivoyageSearchResult> headerObjs = new HashMap<>();
Map<File, List<Amenity>> amenityMap = new HashMap<>(); Map<File, List<Amenity>> amenityMap = new HashMap<>();
for (BinaryMapIndexReader reader : getReaders()) { for (BinaryMapIndexReader reader : getReaders()) {
@ -335,7 +335,7 @@ public class TravelObfHelper implements TravelHelper {
navMap.put(rs.isPartOf, l); navMap.put(rs.isPartOf, l);
} }
l.add(rs); l.add(rs);
if (headers != null && headers.contains(a.getTitle())) { if (headers.contains(a.getTitle())) {
headerObjs.put(a.getTitle(), rs); headerObjs.put(a.getTitle(), rs);
} }
} }
@ -365,7 +365,7 @@ public class TravelObfHelper implements TravelHelper {
@Override @Override
public TravelArticle getArticleById(@NonNull TravelArticleIdentifier articleId, @NonNull String lang) { public TravelArticle getArticleById(@NonNull TravelArticleIdentifier articleId, @NonNull String lang) {
TravelArticle article = getCachedArticle(articleId, lang); TravelArticle article = getCachedArticle(articleId, lang);
return article == null ? findArticleById(articleId, lang) : article; return article == null ? localDataHelper.getSavedArticle(articleId.file, articleId.routeId, lang) : article;
} }
@Nullable @Nullable
@ -390,10 +390,11 @@ public class TravelObfHelper implements TravelHelper {
private TravelArticle findArticleById(@NonNull final TravelArticleIdentifier articleId, final String lang) { private TravelArticle findArticleById(@NonNull final TravelArticleIdentifier articleId, final String lang) {
TravelArticle article = null; TravelArticle article = null;
final boolean isDbArticle = articleId.file != null && articleId.file.getName().endsWith(IndexConstants.BINARY_WIKIVOYAGE_MAP_INDEX_EXT);
final List<Amenity> amenities = new ArrayList<>(); final List<Amenity> amenities = new ArrayList<>();
for (BinaryMapIndexReader reader : getReaders()) { for (BinaryMapIndexReader reader : getReaders()) {
try { try {
if (articleId.file != null && !articleId.file.equals(reader.getFile())) { if (articleId.file != null && !articleId.file.equals(reader.getFile()) && !isDbArticle) {
continue; continue;
} }
SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(0, 0, SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(0, 0,
@ -404,7 +405,7 @@ public class TravelObfHelper implements TravelHelper {
@Override @Override
public boolean publish(Amenity amenity) { public boolean publish(Amenity amenity) {
if (Algorithms.stringsEqual(articleId.routeId, Algorithms.emptyIfNull(amenity.getTagContent(Amenity.ROUTE_ID, null))) if (Algorithms.stringsEqual(articleId.routeId, Algorithms.emptyIfNull(amenity.getTagContent(Amenity.ROUTE_ID, null)))
&& Algorithms.stringsEqual(articleId.routeSource, Algorithms.emptyIfNull(amenity.getTagContent(Amenity.ROUTE_SOURCE, null)))) { && Algorithms.stringsEqual(articleId.routeSource, Algorithms.emptyIfNull(amenity.getTagContent(Amenity.ROUTE_SOURCE, null))) || isDbArticle) {
amenities.add(amenity); amenities.add(amenity);
done = true; done = true;
} }
@ -519,10 +520,15 @@ public class TravelObfHelper implements TravelHelper {
ArrayList<String> res = new ArrayList<>(); ArrayList<String> res = new ArrayList<>();
TravelArticle article = getArticleById(articleId, ""); TravelArticle article = getArticleById(articleId, "");
if (article != null) { if (article != null) {
Map<String, TravelArticle> articles = cachedArticles.get(articleId); Map<String, TravelArticle> articles = cachedArticles.get(article.generateIdentifier());
if (articles != null) { if (articles != null) {
res.addAll(articles.keySet()); res.addAll(articles.keySet());
} }
} else {
List<TravelArticle> articles = localDataHelper.getSavedArticles(articleId.file, articleId.routeId);
for (TravelArticle a : articles) {
res.add(a.getLang());
}
} }
return res; return res;
} }
@ -547,7 +553,7 @@ public class TravelObfHelper implements TravelHelper {
@Override @Override
public String getSelectedTravelBookName() { public String getSelectedTravelBookName() {
return ""; return null;
} }
@Override @Override

View file

@ -1,17 +1,36 @@
package net.osmand.plus.wikivoyage.data; package net.osmand.plus.wikivoyage.data;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import net.osmand.plus.OsmandApplication;
import java.io.File;
public class WikivoyageSearchHistoryItem { public class WikivoyageSearchHistoryItem {
File articleFile;
String articleTitle; String articleTitle;
String lang; String lang;
String isPartOf; String isPartOf;
long lastAccessed; long lastAccessed;
public static String getKey(String lang, String title, @Nullable File file) {
public String getKey() { return lang + ":" + title + (file != null ? ":" + file.getName() : "");
return TravelLocalDataHelper.getHistoryKey(lang, articleTitle);
} }
public String getKey() {
return getKey(lang, articleTitle, articleFile);
}
public File getArticleFile() {
return articleFile;
}
@Nullable
public String getTravelBook(@NonNull OsmandApplication app) {
return articleFile != null ? TravelArticle.getTravelBook(app, articleFile) : null;
}
public String getArticleTitle() { public String getArticleTitle() {
return articleTitle; return articleTitle;

View file

@ -22,6 +22,7 @@ import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.R; import net.osmand.plus.R;
import net.osmand.plus.UiUtilities; import net.osmand.plus.UiUtilities;
import net.osmand.plus.widgets.tools.CropCircleTransformation; import net.osmand.plus.widgets.tools.CropCircleTransformation;
import net.osmand.plus.wikipedia.WikiArticleHelper;
import net.osmand.plus.wikivoyage.WikivoyageUtils; import net.osmand.plus.wikivoyage.WikivoyageUtils;
import net.osmand.plus.wikivoyage.data.TravelArticle; import net.osmand.plus.wikivoyage.data.TravelArticle;
import net.osmand.plus.wikivoyage.data.TravelLocalDataHelper; import net.osmand.plus.wikivoyage.data.TravelLocalDataHelper;
@ -102,7 +103,7 @@ public class SavedArticlesRvAdapter extends RecyclerView.Adapter<RecyclerView.Vi
holder.icon.setVisibility(loaded == null || loaded.booleanValue() ? View.VISIBLE : View.GONE); holder.icon.setVisibility(loaded == null || loaded.booleanValue() ? View.VISIBLE : View.GONE);
holder.title.setText(article.getTitle()); holder.title.setText(article.getTitle());
holder.content.setText(article.getContent()); holder.content.setText(WikiArticleHelper.getPartialContent(article.getContent()));
holder.partOf.setText(article.getGeoDescription()); holder.partOf.setText(article.getGeoDescription());
holder.leftButton.setText(app.getString(R.string.shared_string_read)); holder.leftButton.setText(app.getString(R.string.shared_string_read));
holder.leftButton.setCompoundDrawablesWithIntrinsicBounds(readIcon, null, null, null); holder.leftButton.setCompoundDrawablesWithIntrinsicBounds(readIcon, null, null, null);
@ -178,7 +179,7 @@ public class SavedArticlesRvAdapter extends RecyclerView.Adapter<RecyclerView.Vi
@Override @Override
public void onClick(View view) { public void onClick(View view) {
Object item = getItemByPosition(); Object item = getItemByPosition();
if (item != null && item instanceof TravelArticle) { if (item instanceof TravelArticle) {
if (listener != null) { if (listener != null) {
listener.openArticle((TravelArticle) item); listener.openArticle((TravelArticle) item);
} }
@ -193,7 +194,7 @@ public class SavedArticlesRvAdapter extends RecyclerView.Adapter<RecyclerView.Vi
@Override @Override
public void onClick(View view) { public void onClick(View view) {
Object item = getItemByPosition(); Object item = getItemByPosition();
if (item != null && item instanceof TravelArticle) { if (item instanceof TravelArticle) {
final TravelArticle article = (TravelArticle) item; final TravelArticle article = (TravelArticle) item;
final TravelLocalDataHelper ldh = app.getTravelHelper().getBookmarksHelper(); final TravelLocalDataHelper ldh = app.getTravelHelper().getBookmarksHelper();
ldh.removeArticleFromSaved(article); ldh.removeArticleFromSaved(article);