diff --git a/OsmAnd/assets/article_style.css b/OsmAnd/assets/article_style.css index bf734b5f38..3e27245c95 100644 --- a/OsmAnd/assets/article_style.css +++ b/OsmAnd/assets/article_style.css @@ -21,10 +21,23 @@ body { .title-image { width: 100%; - height: 20%; + height: 30%; background: center; background-size: 100%; + background-size:cover; background-repeat: no-repeat; + background-color: #f2f2f2; +} + +h1 { + font-size: 3em; + color: #212121; + font-family: serif; + font-weight: bold; + letter-spacing: 0.03em; + word-wrap: break-word; + padding-top: 5%; + } h2 { @@ -33,7 +46,8 @@ h2 { font-family: serif; font-weight: bold; word-wrap: break-word; - padding-top: 5%; + margin-top: 3%; + margin-bottom: 3%; } h3 { @@ -41,21 +55,23 @@ h3 { font-size: 1.5em; font-family: sans-serif; word-wrap: break-word; - padding-top: 5%; + margin-top: 3%; + margin-bottom: 3%; } p { font-family: sans-serif; font-size: 1.1em; - line-height: 1.6em; + line-height: 1.5em; } ul { /* font-size: 1.1em; */ - line-height: 1.6em; - padding-top: 3%; - padding-bottom: 3%; list-style-type: disc; + line-height: 1.6em; + margin-left: 5%; + padding: 0%; + } li { @@ -64,6 +80,17 @@ li { padding-bottom: 1%; } +.toc { + font-size: 1.1em; +} + +.mw-headline { + font-family: sans-serif; + font-size: 1.1em; + line-height: 1.6em; + font-weight: bold; +} + img { width: 100%; } diff --git a/OsmAnd/res/drawable/wikivoyage_search_history_placeholder.xml b/OsmAnd/res/drawable/wikivoyage_search_history_placeholder.xml new file mode 100644 index 0000000000..5ecd952e4e --- /dev/null +++ b/OsmAnd/res/drawable/wikivoyage_search_history_placeholder.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + diff --git a/OsmAnd/res/drawable/wikivoyage_search_placeholder.xml b/OsmAnd/res/drawable/wikivoyage_search_placeholder.xml index b93920d87e..54e09eedc7 100644 --- a/OsmAnd/res/drawable/wikivoyage_search_placeholder.xml +++ b/OsmAnd/res/drawable/wikivoyage_search_placeholder.xml @@ -1,6 +1,5 @@ - + diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/WikivoyageArticleDialogFragment.java b/OsmAnd/src/net/osmand/plus/wikivoyage/WikivoyageArticleDialogFragment.java index 28c2f63cc5..eaf12f0a68 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/WikivoyageArticleDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/WikivoyageArticleDialogFragment.java @@ -22,6 +22,7 @@ import net.osmand.AndroidUtils; import net.osmand.IndexConstants; import net.osmand.plus.R; import net.osmand.plus.wikivoyage.data.WikivoyageArticle; +import net.osmand.plus.wikivoyage.data.WikivoyageLocalDataHelper; import java.io.File; import java.util.ArrayList; @@ -40,7 +41,7 @@ public class WikivoyageArticleDialogFragment extends WikivoyageBaseDialogFragmen "\n" + "\n" + "\n" + - ""; + "\n"; private static final String FOOTER_INNER = ""; private long cityId = NO_VALUE; @@ -146,6 +147,8 @@ public class WikivoyageArticleDialogFragment extends WikivoyageBaseDialogFragmen return; } + WikivoyageLocalDataHelper.getInstance(getMyApplication()).addToHistory(article); + contentWebView.loadDataWithBaseURL(getBaseUrl(), createHtmlContent(article), "text/html", "UTF-8", null); } @@ -155,8 +158,8 @@ public class WikivoyageArticleDialogFragment extends WikivoyageBaseDialogFragmen String articleTitle = article.getImageTitle(); if (!TextUtils.isEmpty(articleTitle)) { - String url = WikivoyageArticle.getImageUrl(articleTitle); - sb.append(""); + String url = WikivoyageArticle.getImageUrl(articleTitle, false); + sb.append("
"); } sb.append("
\n"); sb.append("

").append(article.getTitle()).append("

"); diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/data/WikivoyageArticle.java b/OsmAnd/src/net/osmand/plus/wikivoyage/data/WikivoyageArticle.java index ab7595f7e1..808d5b887b 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/data/WikivoyageArticle.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/data/WikivoyageArticle.java @@ -12,6 +12,7 @@ public class WikivoyageArticle { private static final String IMAGE_ROOT_URL = "https://upload.wikimedia.org/wikipedia/commons/"; private static final String THUMB_PREFIX = "320px-"; + private static final String REGULAR_PREFIX = "800px-"; String id; String title; @@ -75,15 +76,10 @@ public class WikivoyageArticle { } @NonNull - public static String getThumbImageUrl(@NonNull String imageTitle) { + public static String getImageUrl(@NonNull String imageTitle, boolean thumbnail) { String[] hash = getHash(imageTitle); - return IMAGE_ROOT_URL + "thumb/" + hash[0] + "/" + hash[1] + "/" + imageTitle + "/" + THUMB_PREFIX + imageTitle; - } - - @NonNull - public static String getImageUrl(@NonNull String imageTitle) { - String[] hash = getHash(imageTitle); - return IMAGE_ROOT_URL + hash[0] + "/" + hash[1] + "/" + imageTitle; + String prefix = thumbnail ? THUMB_PREFIX : REGULAR_PREFIX; + return IMAGE_ROOT_URL + "thumb/" + hash[0] + "/" + hash[1] + "/" + imageTitle + "/" + prefix + imageTitle; } @Size(2) diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/data/WikivoyageDbHelper.java b/OsmAnd/src/net/osmand/plus/wikivoyage/data/WikivoyageDbHelper.java index 04f70d8dec..92859744f6 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/data/WikivoyageDbHelper.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/data/WikivoyageDbHelper.java @@ -62,22 +62,6 @@ public class WikivoyageDbHelper { private static final String SEARCH_COL_ARTICLE_TITLE = "article_title"; private static final String SEARCH_COL_LANG = "lang"; - private static final String SEARCH_QUERY = "SELECT " + - SEARCH_COL_SEARCH_TERM + ", " + - SEARCH_TABLE_NAME + "." + SEARCH_COL_CITY_ID + ", " + - SEARCH_COL_ARTICLE_TITLE + ", " + - SEARCH_TABLE_NAME + "." + SEARCH_COL_LANG + ", " + - ARTICLES_COL_IS_PART_OF + ", " + - ARTICLES_COL_IMAGE_TITLE + - " FROM " + SEARCH_TABLE_NAME + - " JOIN " + ARTICLES_TABLE_NAME + - " ON " + SEARCH_TABLE_NAME + "." + SEARCH_COL_ARTICLE_TITLE + " = " + ARTICLES_TABLE_NAME + "." + ARTICLES_COL_TITLE + - " AND " + SEARCH_TABLE_NAME + "." + SEARCH_COL_LANG + " = " + ARTICLES_TABLE_NAME + "." + ARTICLES_COL_LANG + - " WHERE " + SEARCH_TABLE_NAME + "." + SEARCH_COL_CITY_ID + - " IN (SELECT " + SEARCH_TABLE_NAME + "." + SEARCH_COL_CITY_ID + - " FROM " + SEARCH_TABLE_NAME + - " WHERE " + SEARCH_COL_SEARCH_TERM + " LIKE ?)"; - private final OsmandApplication application; private Collator collator; @@ -91,13 +75,41 @@ public class WikivoyageDbHelper { public List search(final String searchQuery) { List res = new ArrayList<>(); SQLiteConnection conn = openConnection(); + String[] queries = searchQuery.replace('_', ' ').replace('/', ' ').split(" "); if (conn != null) { try { - SQLiteCursor cursor = conn.rawQuery(SEARCH_QUERY, new String[]{searchQuery + "%"}); - if (cursor.moveToFirst()) { - do { - res.add(readSearchResult(cursor)); - } while (cursor.moveToNext()); + List params = new ArrayList<>(); + String query = "SELECT distinct wa.city_id, wa.title, wa.lang, wa.is_part_of, wa.image_title " + + "FROM wikivoyage_articles wa WHERE wa.city_id in " + + " (SELECT city_id FROM wikivoyage_search WHERE search_term LIKE"; + for (String q : queries) { + if (q.trim().length() > 0) { + if (params.size() > 5) { + // don't explode the query search much + break; + } + if (params.size() > 0) { + query += " AND city_id IN (SELECT city_id FROM wikivoyage_search WHERE search_term LIKE ?) "; + } else { + query += "?"; + } + params.add(q.trim() + "%"); + } + } + query += ") "; + if (params.size() > 0) { + SQLiteCursor cursor = conn.rawQuery(query, params.toArray(new String[params.size()])); + if (cursor.moveToFirst()) { + do { + WikivoyageSearchResult rs = new WikivoyageSearchResult(); + rs.cityId = cursor.getLong(0); + rs.articleTitles.add(cursor.getString(1)); + rs.langs.add(cursor.getString(2)); + rs.isPartOf = cursor.getString(3); + rs.imageTitle = cursor.getString(4); + res.add(rs); + } while (cursor.moveToNext()); + } } } finally { conn.close(); @@ -105,6 +117,12 @@ public class WikivoyageDbHelper { } List list = new ArrayList<>(groupSearchResultsByCityId(res)); + sortSearchResults(searchQuery, list); + + return list; + } + + private void sortSearchResults(final String searchQuery, List list) { Collections.sort(list, new Comparator() { @Override public int compare(WikivoyageSearchResult o1, WikivoyageSearchResult o2) { @@ -122,8 +140,6 @@ public class WikivoyageDbHelper { return 0; } }); - - return list; } private Collection groupSearchResultsByCityId(List res) { @@ -144,7 +160,6 @@ public class WikivoyageDbHelper { } prev.articleTitles.add(insInd, rs.articleTitles.get(0)); prev.langs.add(insInd, rs.langs.get(0)); - prev.searchTerms.add(insInd, rs.searchTerms.get(0)); } else { wikivoyage.put(rs.cityId, rs); } @@ -179,20 +194,6 @@ public class WikivoyageDbHelper { return application.getSQLiteAPI().openByAbsolutePath(path, true); } - @NonNull - private WikivoyageSearchResult readSearchResult(SQLiteCursor cursor) { - WikivoyageSearchResult res = new WikivoyageSearchResult(); - - res.searchTerms.add(cursor.getString(0)); - res.cityId = cursor.getLong(1); - res.articleTitles.add(cursor.getString(2)); - res.langs.add(cursor.getString(3)); - res.isPartOf = cursor.getString(4); - res.imageTitle = cursor.getString(5); - - return res; - } - @NonNull private WikivoyageArticle readArticle(SQLiteCursor cursor) { WikivoyageArticle res = new WikivoyageArticle(); diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/data/WikivoyageLocalDataHelper.java b/OsmAnd/src/net/osmand/plus/wikivoyage/data/WikivoyageLocalDataHelper.java new file mode 100644 index 0000000000..b4413846b1 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/data/WikivoyageLocalDataHelper.java @@ -0,0 +1,198 @@ +package net.osmand.plus.wikivoyage.data; + +import android.support.annotation.NonNull; + +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.api.SQLiteAPI.SQLiteConnection; +import net.osmand.plus.api.SQLiteAPI.SQLiteCursor; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import gnu.trove.map.hash.TLongObjectHashMap; + +public class WikivoyageLocalDataHelper { + + private static WikivoyageLocalDataHelper instance; + + private WikivoyageLocalDataDbHelper dbHelper; + + private TLongObjectHashMap historyMap; + + private WikivoyageLocalDataHelper(OsmandApplication app) { + dbHelper = new WikivoyageLocalDataDbHelper(app); + historyMap = dbHelper.getAllHistoryMap(); + } + + public static WikivoyageLocalDataHelper getInstance(OsmandApplication app) { + if (instance == null) { + instance = new WikivoyageLocalDataHelper(app); + } + return instance; + } + + public List getAllHistory() { + List res = new ArrayList<>(historyMap.valueCollection()); + Collections.sort(res, new Comparator() { + @Override + public int compare(WikivoyageSearchHistoryItem item1, WikivoyageSearchHistoryItem item2) { + if (item1.lastAccessed > item2.lastAccessed) { + return -1; + } else if (item1.lastAccessed == item2.lastAccessed) { + return 0; + } + return 1; + } + }); + return res; + } + + public void addToHistory(WikivoyageArticle article) { + addToHistory(article.getCityId(), article.getTitle(), article.getLang(), article.getIsPartOf()); + } + + public void addToHistory(long cityId, String title, String lang, String isPartOf) { + WikivoyageSearchHistoryItem item = historyMap.get(cityId); + boolean newItem = item == null; + if (newItem) { + item = new WikivoyageSearchHistoryItem(); + item.cityId = cityId; + } + item.articleTitle = title; + item.lang = lang; + item.isPartOf = isPartOf; + item.lastAccessed = System.currentTimeMillis(); + if (newItem) { + dbHelper.addHistoryItem(item); + historyMap.put(item.cityId, item); + } else { + dbHelper.updateHistoryItem(item); + } + } + + private static class WikivoyageLocalDataDbHelper { + + private static final int DB_VERSION = 1; + private static final String DB_NAME = "wikivoyage_local_data"; + + private static final String HISTORY_TABLE_NAME = "wikivoyage_search_history"; + private static final String HISTORY_COL_CITY_ID = "city_id"; + private static final String HISTORY_COL_ARTICLE_TITLE = "article_title"; + private static final String HISTORY_COL_LANG = "lang"; + private static final String HISTORY_COL_IS_PART_OF = "is_part_of"; + private static final String HISTORY_COL_LAST_ACCESSED = "last_accessed"; + + private static final String HISTORY_TABLE_CREATE = "CREATE TABLE IF NOT EXISTS " + + HISTORY_TABLE_NAME + " (" + + HISTORY_COL_CITY_ID + " long, " + + HISTORY_COL_ARTICLE_TITLE + " TEXT, " + + HISTORY_COL_LANG + " TEXT, " + + HISTORY_COL_IS_PART_OF + " TEXT, " + + HISTORY_COL_LAST_ACCESSED + " long);"; + + private static final String HISTORY_TABLE_SELECT = "SELECT " + + HISTORY_COL_CITY_ID + ", " + + HISTORY_COL_ARTICLE_TITLE + ", " + + HISTORY_COL_LANG + ", " + + HISTORY_COL_IS_PART_OF + ", " + + HISTORY_COL_LAST_ACCESSED + + " FROM " + HISTORY_TABLE_NAME; + + private final OsmandApplication context; + + private WikivoyageLocalDataDbHelper(OsmandApplication context) { + this.context = context; + } + + private SQLiteConnection openConnection(boolean readonly) { + SQLiteConnection conn = context.getSQLiteAPI().getOrCreateDatabase(DB_NAME, readonly); + int version = conn.getVersion(); + if (version == 0 || DB_VERSION != version) { + if (readonly) { + conn.close(); + conn = context.getSQLiteAPI().getOrCreateDatabase(DB_NAME, false); + } + version = conn.getVersion(); + conn.setVersion(DB_VERSION); + if (version == 0) { + onCreate(conn); + } else { + onUpgrade(conn, version, DB_VERSION); + } + } + return conn; + } + + private void onCreate(SQLiteConnection conn) { + conn.execSQL(HISTORY_TABLE_CREATE); + } + + @SuppressWarnings("unused") + private void onUpgrade(SQLiteConnection conn, int oldVersion, int newVersion) { + + } + + @NonNull + TLongObjectHashMap getAllHistoryMap() { + TLongObjectHashMap res = new TLongObjectHashMap<>(); + SQLiteConnection conn = openConnection(true); + if (conn != null) { + try { + SQLiteCursor cursor = conn.rawQuery(HISTORY_TABLE_SELECT, null); + if (cursor.moveToFirst()) { + do { + WikivoyageSearchHistoryItem item = readHistoryItem(cursor); + res.put(item.cityId, item); + } while (cursor.moveToNext()); + } + } finally { + conn.close(); + } + } + return res; + } + + void addHistoryItem(WikivoyageSearchHistoryItem item) { + SQLiteConnection conn = openConnection(false); + if (conn != null) { + try { + conn.execSQL("INSERT INTO " + HISTORY_TABLE_NAME + " VALUES (?, ?, ?, ?, ?)", + new Object[]{item.cityId, item.articleTitle, item.lang, item.isPartOf, item.lastAccessed}); + } finally { + conn.close(); + } + } + } + + void updateHistoryItem(WikivoyageSearchHistoryItem item) { + SQLiteConnection conn = openConnection(false); + if (conn != null) { + try { + conn.execSQL("UPDATE " + HISTORY_TABLE_NAME + " SET " + + HISTORY_COL_ARTICLE_TITLE + " = ?, " + + HISTORY_COL_LANG + " = ?, " + + HISTORY_COL_IS_PART_OF + " = ?, " + + HISTORY_COL_LAST_ACCESSED + " = ? " + + "WHERE " + HISTORY_COL_CITY_ID + " = ?", + new Object[]{item.articleTitle, item.lang, item.isPartOf, item.lastAccessed, item.cityId}); + } finally { + conn.close(); + } + } + } + + private WikivoyageSearchHistoryItem readHistoryItem(SQLiteCursor cursor) { + WikivoyageSearchHistoryItem res = new WikivoyageSearchHistoryItem(); + + res.cityId = cursor.getLong(0); + res.articleTitle = cursor.getString(1); + res.lang = cursor.getString(2); + res.isPartOf = cursor.getString(3); + res.lastAccessed = cursor.getLong(4); + + return res; + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/data/WikivoyageSearchHistoryItem.java b/OsmAnd/src/net/osmand/plus/wikivoyage/data/WikivoyageSearchHistoryItem.java new file mode 100644 index 0000000000..62831e73bd --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/data/WikivoyageSearchHistoryItem.java @@ -0,0 +1,30 @@ +package net.osmand.plus.wikivoyage.data; + +public class WikivoyageSearchHistoryItem { + + long cityId; + String articleTitle; + String lang; + String isPartOf; + long lastAccessed; + + public long getCityId() { + return cityId; + } + + public String getArticleTitle() { + return articleTitle; + } + + public String getLang() { + return lang; + } + + public String getIsPartOf() { + return isPartOf; + } + + public long getLastAccessed() { + return lastAccessed; + } +} diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/data/WikivoyageSearchResult.java b/OsmAnd/src/net/osmand/plus/wikivoyage/data/WikivoyageSearchResult.java index 59d60ea5aa..ab50ba563c 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/data/WikivoyageSearchResult.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/data/WikivoyageSearchResult.java @@ -9,17 +9,12 @@ public class WikivoyageSearchResult { private static final int SHOW_LANGS = 3; - List searchTerms = new ArrayList<>(); long cityId; List articleTitles = new ArrayList<>(); List langs = new ArrayList<>(); String isPartOf; String imageTitle; - public List getSearchTerms() { - return searchTerms; - } - public long getCityId() { return cityId; } diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/search/SearchRecyclerViewAdapter.java b/OsmAnd/src/net/osmand/plus/wikivoyage/search/SearchRecyclerViewAdapter.java index 6798676a59..a3f2b5679f 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/search/SearchRecyclerViewAdapter.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/search/SearchRecyclerViewAdapter.java @@ -18,7 +18,9 @@ import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.widgets.tools.CropCircleTransformation; import net.osmand.plus.wikivoyage.data.WikivoyageArticle; +import net.osmand.plus.wikivoyage.data.WikivoyageSearchHistoryItem; import net.osmand.plus.wikivoyage.data.WikivoyageSearchResult; +import net.osmand.util.Algorithms; import java.util.ArrayList; import java.util.List; @@ -29,7 +31,9 @@ public class SearchRecyclerViewAdapter extends RecyclerView.Adapter items = new ArrayList<>(); @@ -41,11 +45,8 @@ public class SearchRecyclerViewAdapter extends RecyclerView.Adapter historyItems) { + this.items.clear(); + if (historyItems != null && !historyItems.isEmpty()) { + this.items.add(app.getString(R.string.shared_string_history)); + this.items.addAll(historyItems); + } + notifyDataSetChanged(); + } + public void setItems(@Nullable List items) { this.items.clear(); if (items != null && !items.isEmpty()) { @@ -110,6 +129,21 @@ public class SearchRecyclerViewAdapter extends RecyclerView.Adapter items) { + if (items == null || items.isEmpty()) { + adapter.setHistoryItems(WikivoyageLocalDataHelper + .getInstance(getMyApplication()).getAllHistory()); + } else { + adapter.setItems(items); + } + } + private void runSearch() { switchProgressBarVisibility(true); cancelled = false; @@ -141,7 +155,7 @@ public class WikivoyageSearchDialogFragment extends WikivoyageBaseDialogFragment getMyApplication().runInUIThread(new Runnable() { public void run() { if (!isCancelled()) { - adapter.setItems(results); + setAdapterItems(results); switchProgressBarVisibility(false); } }