Refactoring travel helpers

This commit is contained in:
max-klaus 2020-12-30 15:53:50 +03:00
parent a5f0622dcd
commit 6236da3a02
20 changed files with 639 additions and 295 deletions

View file

@ -1727,13 +1727,12 @@ public class BinaryMapIndexReader {
double half16t = MapUtils.getDistance(lat, MapUtils.getLongitudeFromTile(16, ((int) dx) + 0.5), double half16t = MapUtils.getDistance(lat, MapUtils.getLongitudeFromTile(16, ((int) dx) + 0.5),
lat, MapUtils.getLongitudeFromTile(16, (int) dx)); lat, MapUtils.getLongitudeFromTile(16, (int) dx));
double cf31 = ((double) radiusMeters / (half16t * 2)) * (1 << 15); double cf31 = ((double) radiusMeters / (half16t * 2)) * (1 << 15);
int y31 = MapUtils.get31TileNumberY(lat); y = MapUtils.get31TileNumberY(lat);
int x31 = MapUtils.get31TileNumberX(lon); x = MapUtils.get31TileNumberX(lon);
left = (int) (x31 - cf31); left = (int) (x - cf31);
right = (int) (x31 + cf31); right = (int) (x + cf31);
top = (int) (y31 - cf31); top = (int) (y - cf31);
bottom = (int) (y31 + cf31); bottom = (int) (y + cf31);
} }
public boolean publish(T obj) { public boolean publish(T obj) {

View file

@ -44,6 +44,7 @@ public class Amenity extends MapObject {
public static final String IS_AGGR_PART = "is_aggr_part"; public static final String IS_AGGR_PART = "is_aggr_part";
public static final String CONTENT_JSON = "content_json"; public static final String CONTENT_JSON = "content_json";
public static final String ROUTE_ID = "route_id"; public static final String ROUTE_ID = "route_id";
public static final String ROUTE_SOURCE = "route_source";
private String subType; private String subType;

View file

@ -16,11 +16,13 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;

View file

@ -78,6 +78,10 @@ public class Algorithms {
return map == null || map.size() == 0; return map == null || map.size() == 0;
} }
public static String emptyIfNull(String s) {
return s == null ? "" : s;
}
public static boolean isEmpty(CharSequence s) { public static boolean isEmpty(CharSequence s) {
return s == null || s.length() == 0; return s == null || s.length() == 0;
} }
@ -86,6 +90,10 @@ public class Algorithms {
return s == null || s.trim().length() == 0; return s == null || s.trim().length() == 0;
} }
public static int hash(Object... values) {
return Arrays.hashCode(values);
}
public static boolean stringsEqual(String s1, String s2) { public static boolean stringsEqual(String s1, String s2) {
if (s1 == null && s2 == null) { if (s1 == null && s2 == null) {
return true; return true;

View file

@ -427,7 +427,7 @@ public class MapMarkersGroupsAdapter extends RecyclerView.Adapter<RecyclerView.V
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if (mapActivity.getSupportFragmentManager() != null) { if (mapActivity.getSupportFragmentManager() != null) {
WikivoyageArticleDialogFragment.showInstanceByTitle(app, mapActivity.getSupportFragmentManager(), article.getTitle(), article.getLang()); WikivoyageArticleDialogFragment.showInstance(app, mapActivity.getSupportFragmentManager(), article.generateIdentifier(), article.getLang());
} }
} }
}; };

View file

@ -39,6 +39,7 @@ import com.squareup.picasso.RequestCreator;
import net.osmand.AndroidUtils; import net.osmand.AndroidUtils;
import net.osmand.GPXUtilities; import net.osmand.GPXUtilities;
import net.osmand.GPXUtilities.GPXFile; import net.osmand.GPXUtilities.GPXFile;
import net.osmand.GPXUtilities.Metadata;
import net.osmand.GPXUtilities.WptPt; import net.osmand.GPXUtilities.WptPt;
import net.osmand.PicassoUtils; import net.osmand.PicassoUtils;
import net.osmand.data.PointDescription; import net.osmand.data.PointDescription;
@ -422,7 +423,7 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
return null; return null;
} }
TravelArticle article = getTravelArticle(gpx.metadata); TravelArticle article = getTravelArticle(gpx);
if (article != null) { if (article != null) {
return createTravelArticleCard(context, article); return createTravelArticleCard(context, article);
} }
@ -448,7 +449,7 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
} }
@Nullable @Nullable
private String getMetadataDescription(@NonNull GPXUtilities.Metadata metadata) { private String getMetadataDescription(@NonNull Metadata metadata) {
String descHtml = metadata.desc; String descHtml = metadata.desc;
if (TextUtils.isEmpty(descHtml)) { if (TextUtils.isEmpty(descHtml)) {
Map<String, String> extensions = metadata.getExtensionsToRead(); Map<String, String> extensions = metadata.getExtensionsToRead();
@ -466,7 +467,7 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
} }
@Nullable @Nullable
private String getMetadataImageLink(@NonNull GPXUtilities.Metadata metadata) { private String getMetadataImageLink(@NonNull Metadata metadata) {
String link = metadata.link; String link = metadata.link;
if (!TextUtils.isEmpty(link)) { if (!TextUtils.isEmpty(link)) {
String lowerCaseLink = link.toLowerCase(); String lowerCaseLink = link.toLowerCase();
@ -482,11 +483,12 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
} }
@Nullable @Nullable
private TravelArticle getTravelArticle(@NonNull GPXUtilities.Metadata metadata) { private TravelArticle getTravelArticle(@NonNull GPXFile gpx) {
Metadata metadata = gpx.metadata;
String title = metadata.getArticleTitle(); String title = metadata.getArticleTitle();
String lang = metadata.getArticleLang(); String lang = metadata.getArticleLang();
if (!TextUtils.isEmpty(title) && !TextUtils.isEmpty(lang)) { if (!TextUtils.isEmpty(title) && !TextUtils.isEmpty(lang)) {
return app.getTravelHelper().getArticleByTitle(title, lang); return app.getTravelHelper().getArticleByTitle(title, gpx.getRect(), lang);
} }
return null; return null;
} }
@ -506,8 +508,8 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
public void onClick(View v) { public void onClick(View v) {
TrackActivity activity = getTrackActivity(); TrackActivity activity = getTrackActivity();
if (activity != null) { if (activity != null) {
WikivoyageArticleDialogFragment.showInstance(app, WikivoyageArticleDialogFragment.showInstance(app, activity.getSupportFragmentManager(),
activity.getSupportFragmentManager(), article.getRouteId(), article.getLang()); article.generateIdentifier(), article.getLang());
} }
} }
}; };

View file

@ -14,6 +14,7 @@ import androidx.fragment.app.FragmentManager;
import net.osmand.AndroidUtils; import net.osmand.AndroidUtils;
import net.osmand.GPXUtilities; import net.osmand.GPXUtilities;
import net.osmand.GPXUtilities.WptPt;
import net.osmand.data.LatLon; import net.osmand.data.LatLon;
import net.osmand.data.PointDescription; import net.osmand.data.PointDescription;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
@ -22,6 +23,7 @@ import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.wikipedia.WikiArticleHelper; import net.osmand.plus.wikipedia.WikiArticleHelper;
import net.osmand.plus.wikivoyage.article.WikivoyageArticleDialogFragment; import net.osmand.plus.wikivoyage.article.WikivoyageArticleDialogFragment;
import net.osmand.plus.wikivoyage.data.TravelArticle; import net.osmand.plus.wikivoyage.data.TravelArticle;
import net.osmand.plus.wikivoyage.data.TravelArticle.TravelArticleIdentifier;
import net.osmand.plus.wikivoyage.explore.WikivoyageExploreActivity; import net.osmand.plus.wikivoyage.explore.WikivoyageExploreActivity;
import java.io.File; import java.io.File;
@ -67,8 +69,8 @@ public class WikivoyageWebViewClient extends WebViewClient {
if (url.contains(WIKIVOYAGE_DOMAIN) && isWebPage) { if (url.contains(WIKIVOYAGE_DOMAIN) && isWebPage) {
String lang = WikiArticleHelper.getLang(url); String lang = WikiArticleHelper.getLang(url);
String articleName = WikiArticleHelper.getArticleNameFromUrl(url, lang); String articleName = WikiArticleHelper.getArticleNameFromUrl(url, lang);
String articleId = app.getTravelHelper().getArticleId(articleName, lang); TravelArticleIdentifier articleId = app.getTravelHelper().getArticleId(articleName, lang);
if (!articleId.isEmpty()) { if (articleId != null) {
WikivoyageArticleDialogFragment.showInstance(app, fragmentManager, articleId, lang); WikivoyageArticleDialogFragment.showInstance(app, fragmentManager, articleId, lang);
} else { } else {
WikiArticleHelper.warnAboutExternalLoad(url, activity, nightMode); WikiArticleHelper.warnAboutExternalLoad(url, activity, nightMode);
@ -83,8 +85,8 @@ public class WikivoyageWebViewClient extends WebViewClient {
WikiArticleHelper.warnAboutExternalLoad(url, activity, nightMode); WikiArticleHelper.warnAboutExternalLoad(url, activity, nightMode);
} else if (url.startsWith(PREFIX_GEO)) { } else if (url.startsWith(PREFIX_GEO)) {
if (article != null) { if (article != null) {
List<GPXUtilities.WptPt> points = article.getGpxFile().getPoints(); List<WptPt> points = article.getGpxFile().getPoints();
GPXUtilities.WptPt gpxPoint = null; WptPt gpxPoint = null;
String coordinates = url.replace(PREFIX_GEO, ""); String coordinates = url.replace(PREFIX_GEO, "");
double lat; double lat;
double lon; double lon;
@ -96,7 +98,7 @@ public class WikivoyageWebViewClient extends WebViewClient {
Log.w(TAG, e.getMessage(), e); Log.w(TAG, e.getMessage(), e);
return true; return true;
} }
for (GPXUtilities.WptPt point : points) { for (WptPt point : points) {
if (point.getLatitude() == lat && point.getLongitude() == lon) { if (point.getLatitude() == lat && point.getLongitude() == lon) {
gpxPoint = point; gpxPoint = point;
break; break;

View file

@ -30,17 +30,18 @@ import net.osmand.AndroidUtils;
import net.osmand.IndexConstants; import net.osmand.IndexConstants;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
import net.osmand.plus.OsmandPlugin; import net.osmand.plus.OsmandPlugin;
import net.osmand.plus.UiUtilities;
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.activities.TrackActivity; import net.osmand.plus.activities.TrackActivity;
import net.osmand.plus.development.OsmandDevelopmentPlugin; import net.osmand.plus.development.OsmandDevelopmentPlugin;
import net.osmand.plus.helpers.FileNameTranslationHelper; import net.osmand.plus.helpers.FileNameTranslationHelper;
import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.wikipedia.WikiArticleBaseDialogFragment; import net.osmand.plus.wikipedia.WikiArticleBaseDialogFragment;
import net.osmand.plus.wikipedia.WikiArticleHelper; import net.osmand.plus.wikipedia.WikiArticleHelper;
import net.osmand.plus.wikivoyage.WikivoyageShowPicturesDialogFragment; import net.osmand.plus.wikivoyage.WikivoyageShowPicturesDialogFragment;
import net.osmand.plus.wikivoyage.WikivoyageWebViewClient; import net.osmand.plus.wikivoyage.WikivoyageWebViewClient;
import net.osmand.plus.wikivoyage.data.TravelArticle; import net.osmand.plus.wikivoyage.data.TravelArticle;
import net.osmand.plus.wikivoyage.data.TravelArticle.TravelArticleIdentifier;
import net.osmand.plus.wikivoyage.data.TravelHelper; import net.osmand.plus.wikivoyage.data.TravelHelper;
import net.osmand.plus.wikivoyage.data.TravelLocalDataHelper; import net.osmand.plus.wikivoyage.data.TravelLocalDataHelper;
import net.osmand.util.Algorithms; import net.osmand.util.Algorithms;
@ -58,15 +59,15 @@ public class WikivoyageArticleDialogFragment extends WikiArticleBaseDialogFragme
public static final String TAG = "WikivoyageArticleDialogFragment"; public static final String TAG = "WikivoyageArticleDialogFragment";
private static final String ROUTE_ID_KEY = "route_id_key"; private static final String ARTICLE_ID_KEY = "article_id";
private static final String LANGS_KEY = "langs_key"; private static final String LANGS_KEY = "langs";
private static final String SELECTED_LANG_KEY = "selected_lang_key"; private static final String SELECTED_LANG_KEY = "selected_lang";
private static final String EMPTY_URL = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d4//"; private static final String EMPTY_URL = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d4//";
private static final int MENU_ITEM_SHARE = 0; private static final int MENU_ITEM_SHARE = 0;
private String routeId = ""; private TravelArticleIdentifier articleId;
private ArrayList<String> langs; private ArrayList<String> langs;
private String selectedLang; private String selectedLang;
private TravelArticle article; private TravelArticle article;
@ -192,15 +193,17 @@ public class WikivoyageArticleDialogFragment extends WikiArticleBaseDialogFragme
if (requestCode == WikivoyageArticleContentsFragment.SHOW_CONTENT_ITEM_REQUEST_CODE) { if (requestCode == WikivoyageArticleContentsFragment.SHOW_CONTENT_ITEM_REQUEST_CODE) {
String link = data.getStringExtra(WikivoyageArticleContentsFragment.CONTENT_ITEM_LINK_KEY); String link = data.getStringExtra(WikivoyageArticleContentsFragment.CONTENT_ITEM_LINK_KEY);
String title = data.getStringExtra(WikivoyageArticleContentsFragment.CONTENT_ITEM_TITLE_KEY); String title = data.getStringExtra(WikivoyageArticleContentsFragment.CONTENT_ITEM_TITLE_KEY);
if (title != null) {
moveToAnchor(link, title); moveToAnchor(link, title);
}
} else if (requestCode == WikivoyageShowPicturesDialogFragment.SHOW_PICTURES_CHANGED_REQUEST_CODE) { } else if (requestCode == WikivoyageShowPicturesDialogFragment.SHOW_PICTURES_CHANGED_REQUEST_CODE) {
updateWebSettings(); updateWebSettings();
populateArticle(); populateArticle();
} else if (requestCode == WikivoyageArticleNavigationFragment.OPEN_ARTICLE_REQUEST_CODE) { } else if (requestCode == WikivoyageArticleNavigationFragment.OPEN_ARTICLE_REQUEST_CODE) {
String tripId = data.getStringExtra(WikivoyageArticleNavigationFragment.ROUTE_ID_KEY); TravelArticleIdentifier articleId = data.getParcelableExtra(WikivoyageArticleNavigationFragment.ARTICLE_ID_KEY);
String selectedLang = data.getStringExtra(WikivoyageArticleNavigationFragment.SELECTED_LANG_KEY); String selectedLang = data.getStringExtra(WikivoyageArticleNavigationFragment.SELECTED_LANG_KEY);
if (!Algorithms.isEmpty(tripId) && !TextUtils.isEmpty(selectedLang)) { if (articleId != null && !TextUtils.isEmpty(selectedLang)) {
this.routeId = tripId; this.articleId = articleId;
this.selectedLang = selectedLang; this.selectedLang = selectedLang;
populateArticle(); populateArticle();
} }
@ -286,21 +289,21 @@ public class WikivoyageArticleDialogFragment extends WikiArticleBaseDialogFragme
@Override @Override
protected void populateArticle() { protected void populateArticle() {
if (Algorithms.isEmpty(routeId) || langs == null) { if (articleId == null || langs == null) {
Bundle args = getArguments(); Bundle args = getArguments();
if (args != null) { if (args != null) {
routeId = args.getString(ROUTE_ID_KEY); articleId = args.getParcelable(ARTICLE_ID_KEY);
langs = args.getStringArrayList(LANGS_KEY); langs = args.getStringArrayList(LANGS_KEY);
} }
} }
if (Algorithms.isEmpty(routeId) || langs == null || langs.isEmpty()) { if (articleId == null || langs == null || langs.isEmpty()) {
return; return;
} }
if (selectedLang == null) { if (selectedLang == null) {
selectedLang = langs.get(0); selectedLang = langs.get(0);
} }
articleToolbarText.setText(""); articleToolbarText.setText("");
article = getMyApplication().getTravelHelper().getArticleById(routeId, selectedLang); article = getMyApplication().getTravelHelper().getArticleById(articleId, selectedLang);
if (article == null) { if (article == null) {
return; return;
} }
@ -369,31 +372,31 @@ public class WikivoyageArticleDialogFragment extends WikiArticleBaseDialogFragme
@NonNull FragmentManager fm, @NonNull FragmentManager fm,
@NonNull String title, @NonNull String title,
@NonNull String lang) { @NonNull String lang) {
String articleId = app.getTravelHelper().getArticleId(title, lang); TravelArticleIdentifier articleId = app.getTravelHelper().getArticleId(title, lang);
return showInstance(app, fm, articleId, lang); return showInstance(app, fm, articleId, lang);
} }
public static boolean showInstance(@NonNull OsmandApplication app, public static boolean showInstance(@NonNull OsmandApplication app,
@NonNull FragmentManager fm, @NonNull FragmentManager fm,
@NonNull String routeId, @NonNull TravelArticleIdentifier articleId,
@Nullable String selectedLang) { @Nullable String selectedLang) {
ArrayList<String> langs = app.getTravelHelper().getArticleLangs(routeId); ArrayList<String> langs = app.getTravelHelper().getArticleLangs(articleId);
return showInstance(fm, routeId, langs, selectedLang); return showInstance(fm, articleId, langs, selectedLang);
} }
public static boolean showInstance(@NonNull FragmentManager fm, public static boolean showInstance(@NonNull FragmentManager fm,
String routeId, @NonNull TravelArticleIdentifier articleId,
@NonNull ArrayList<String> langs) { @NonNull ArrayList<String> langs) {
return showInstance(fm, routeId, langs, null); return showInstance(fm, articleId, langs, null);
} }
public static boolean showInstance(@NonNull FragmentManager fm, private static boolean showInstance(@NonNull FragmentManager fm,
String routeId, @NonNull TravelArticleIdentifier articleId,
@NonNull ArrayList<String> langs, @NonNull ArrayList<String> langs,
@Nullable String selectedLang) { @Nullable String selectedLang) {
try { try {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putString(ROUTE_ID_KEY, routeId); args.putParcelable(ARTICLE_ID_KEY, articleId);
args.putStringArrayList(LANGS_KEY, langs); args.putStringArrayList(LANGS_KEY, langs);
if (langs.contains(selectedLang)) { if (langs.contains(selectedLang)) {
args.putString(SELECTED_LANG_KEY, selectedLang); args.putString(SELECTED_LANG_KEY, selectedLang);
@ -416,7 +419,7 @@ public class WikivoyageArticleDialogFragment extends WikiArticleBaseDialogFragme
return; return;
} }
WikivoyageArticleNavigationFragment.showInstance(fm, WikivoyageArticleNavigationFragment.showInstance(fm,
WikivoyageArticleDialogFragment.this, routeId, selectedLang); WikivoyageArticleDialogFragment.this, articleId, selectedLang);
} }
} }

View file

@ -26,6 +26,7 @@ import net.osmand.plus.base.MenuBottomSheetDialogFragment;
import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem;
import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem;
import net.osmand.plus.wikivoyage.data.TravelArticle; import net.osmand.plus.wikivoyage.data.TravelArticle;
import net.osmand.plus.wikivoyage.data.TravelArticle.TravelArticleIdentifier;
import net.osmand.plus.wikivoyage.data.WikivoyageSearchResult; import net.osmand.plus.wikivoyage.data.WikivoyageSearchResult;
import net.osmand.util.Algorithms; import net.osmand.util.Algorithms;
@ -38,14 +39,14 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr
public static final String TAG = WikivoyageArticleNavigationFragment.class.getSimpleName(); public static final String TAG = WikivoyageArticleNavigationFragment.class.getSimpleName();
public static final String ROUTE_ID_KEY = "route_id_key"; public static final String ARTICLE_ID_KEY = "article_id";
public static final String SELECTED_LANG_KEY = "selected_lang_key"; public static final String SELECTED_LANG_KEY = "selected_lang";
public static final int OPEN_ARTICLE_REQUEST_CODE = 2; public static final int OPEN_ARTICLE_REQUEST_CODE = 2;
private static final long UNDEFINED = -1; private static final long UNDEFINED = -1;
private String routeId = ""; private TravelArticleIdentifier articleId;
private String selectedLang; private String selectedLang;
private TravelArticle article; private TravelArticle article;
private List<String> parentsList; private List<String> parentsList;
@ -61,26 +62,27 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr
if (savedInstanceState != null) { if (savedInstanceState != null) {
selectedLang = savedInstanceState.getString(SELECTED_LANG_KEY); selectedLang = savedInstanceState.getString(SELECTED_LANG_KEY);
routeId = savedInstanceState.getString(ROUTE_ID_KEY); articleId = savedInstanceState.getParcelable(ARTICLE_ID_KEY);
} else { } else {
Bundle args = getArguments(); Bundle args = getArguments();
if (args != null) { if (args != null) {
selectedLang = args.getString(SELECTED_LANG_KEY); selectedLang = args.getString(SELECTED_LANG_KEY);
routeId = args.getString(ROUTE_ID_KEY); articleId = args.getParcelable(ARTICLE_ID_KEY);
} }
} }
if (Algorithms.isEmpty(routeId) || TextUtils.isEmpty(selectedLang)) { if (articleId == null || TextUtils.isEmpty(selectedLang)) {
return; return;
} }
article = getMyApplication().getTravelHelper().getArticleById(routeId, selectedLang); article = getMyApplication().getTravelHelper().getArticleById(articleId, selectedLang);
if (article == null) { if (article == null) {
return; return;
} }
parentsList = new ArrayList<>(Arrays.asList(article.getAggregatedPartOf().split(","))); parentsList = new ArrayList<>(Arrays.asList(article.getAggregatedPartOf().split(",")));
Map<WikivoyageSearchResult, List<WikivoyageSearchResult>> navigationMap = getMyApplication().getTravelHelper().getNavigationMap(article); Map<WikivoyageSearchResult, List<WikivoyageSearchResult>> navigationMap
= getMyApplication().getTravelHelper().getNavigationMap(article);
items.add(new TitleItem(getString(R.string.shared_string_navigation))); items.add(new TitleItem(getString(R.string.shared_string_navigation)));
@ -102,7 +104,7 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr
public boolean onChildClick(ExpandableListView parent, View v, public boolean onChildClick(ExpandableListView parent, View v,
int groupPosition, int childPosition, long id) { int groupPosition, int childPosition, long id) {
WikivoyageSearchResult articleItem = listAdapter.getArticleItem(groupPosition, childPosition); WikivoyageSearchResult articleItem = listAdapter.getArticleItem(groupPosition, childPosition);
sendResults(articleItem.getRouteId()); sendResults(articleItem.getArticleId());
dismiss(); dismiss();
return true; return true;
} }
@ -111,10 +113,10 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr
@Override @Override
public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) { public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
WikivoyageSearchResult articleItem = (WikivoyageSearchResult) listAdapter.getGroup(groupPosition); WikivoyageSearchResult articleItem = (WikivoyageSearchResult) listAdapter.getGroup(groupPosition);
if (Algorithms.isEmpty(articleItem.getRouteId())) { if (Algorithms.isEmpty(articleItem.getArticleRouteId())) {
Toast.makeText(getContext(), R.string.wiki_article_not_found, Toast.LENGTH_LONG).show(); Toast.makeText(getContext(), R.string.wiki_article_not_found, Toast.LENGTH_LONG).show();
} else { } else {
sendResults(articleItem.getRouteId()); sendResults(articleItem.getArticleId());
dismiss(); dismiss();
} }
return true; return true;
@ -134,7 +136,7 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr
@Override @Override
public void onSaveInstanceState(Bundle outState) { public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
outState.putString(ROUTE_ID_KEY, routeId); outState.putParcelable(ARTICLE_ID_KEY, articleId);
outState.putString(SELECTED_LANG_KEY, selectedLang); outState.putString(SELECTED_LANG_KEY, selectedLang);
} }
@ -148,17 +150,17 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr
return nightMode ? R.color.wikivoyage_bottom_bar_bg_dark : R.color.list_background_color_light; return nightMode ? R.color.wikivoyage_bottom_bar_bg_dark : R.color.list_background_color_light;
} }
private void sendResults(String routeId) { private void sendResults(TravelArticleIdentifier articleId) {
WikivoyageArticleDialogFragment.showInstance(getMyApplication(), getFragmentManager(), routeId, selectedLang); WikivoyageArticleDialogFragment.showInstance(getMyApplication(), getFragmentManager(), articleId, selectedLang);
} }
public static boolean showInstance(@NonNull FragmentManager fm, public static boolean showInstance(@NonNull FragmentManager fm,
@Nullable Fragment targetFragment, @Nullable Fragment targetFragment,
String routeId, @NonNull TravelArticleIdentifier articleId,
@NonNull String selectedLang) { @NonNull String selectedLang) {
try { try {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putString(ROUTE_ID_KEY, routeId); args.putParcelable(ARTICLE_ID_KEY, articleId);
args.putString(SELECTED_LANG_KEY, selectedLang); args.putString(SELECTED_LANG_KEY, selectedLang);
WikivoyageArticleNavigationFragment fragment = new WikivoyageArticleNavigationFragment(); WikivoyageArticleNavigationFragment fragment = new WikivoyageArticleNavigationFragment();
if (targetFragment != null) { if (targetFragment != null) {
@ -204,7 +206,7 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr
@Override @Override
public Object getChild(int groupPosition, int childPosititon) { public Object getChild(int groupPosition, int childPosititon) {
return getArticleItem(groupPosition, childPosititon).getArticleTitles().get(0); return getArticleItem(groupPosition, childPosititon).getArticleTitle();
} }
@Override @Override
@ -236,8 +238,8 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr
public View getChildView(int groupPosition, final int childPosition, public View getChildView(int groupPosition, final int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) { boolean isLastChild, View convertView, ViewGroup parent) {
WikivoyageSearchResult articleItem = getArticleItem(groupPosition, childPosition); WikivoyageSearchResult articleItem = getArticleItem(groupPosition, childPosition);
String childTitle = articleItem.getArticleTitles().get(0); String childTitle = articleItem.getArticleTitle();
boolean selected = Algorithms.stringsEqual(routeId, articleItem.getRouteId()) || parentsList.contains(childTitle); boolean selected = articleItem.getArticleId().equals(articleId) || parentsList.contains(childTitle);
if (convertView == null) { if (convertView == null) {
convertView = LayoutInflater.from(context) convertView = LayoutInflater.from(context)
@ -263,7 +265,7 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr
@Override @Override
public View getGroupView(final int groupPosition, final boolean isExpanded, public View getGroupView(final int groupPosition, final boolean isExpanded,
View convertView, ViewGroup parent) { View convertView, ViewGroup parent) {
String groupTitle = ((WikivoyageSearchResult) getGroup(groupPosition)).getArticleTitles().get(0); String groupTitle = ((WikivoyageSearchResult) getGroup(groupPosition)).getArticleTitle();
boolean selected = parentsList.contains(groupTitle) || article.getTitle().equals(groupTitle); boolean selected = parentsList.contains(groupTitle) || article.getTitle().equals(groupTitle);
if (convertView == null) { if (convertView == null) {
convertView = LayoutInflater.from(context) convertView = LayoutInflater.from(context)

View file

@ -1,5 +1,7 @@
package net.osmand.plus.wikivoyage.data; package net.osmand.plus.wikivoyage.data;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils; import android.text.TextUtils;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -7,13 +9,21 @@ 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.aidl.search.SearchResult;
import net.osmand.data.LatLon;
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;
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 {
@ -21,19 +31,29 @@ public class TravelArticle {
private static final String THUMB_PREFIX = "320px-"; private static final String THUMB_PREFIX = "320px-";
private static final String REGULAR_PREFIX = "1280px-";//1280, 1024, 800 private static final String REGULAR_PREFIX = "1280px-";//1280, 1024, 800
File file;
String title; String title;
String content; String content;
String isPartOf; String isPartOf;
double lat; double lat = Double.NaN;
double lon; double lon = Double.NaN;
String imageTitle; String imageTitle;
GPXFile gpxFile; GPXFile gpxFile;
String routeId; String routeId;
String routeSource;
long originalId; long originalId;
String lang; String lang;
String contentsJson; String contentsJson;
String aggregatedPartOf; String aggregatedPartOf;
@NonNull
public TravelArticleIdentifier generateIdentifier() {
return new TravelArticleIdentifier(this);
}
public File getFile() {
return file;
}
public String getTitle() { public String getTitle() {
return title; return title;
@ -67,6 +87,10 @@ public class TravelArticle {
return routeId; return routeId;
} }
public String getRouteSource() {
return routeSource;
}
public long getOriginalId() { public long getOriginalId() {
return originalId; return originalId;
} }
@ -126,4 +150,92 @@ public class TravelArticle {
String md5 = new String(Hex.encodeHex(DigestUtils.md5(s))); String md5 = new String(Hex.encodeHex(DigestUtils.md5(s)));
return new String[]{md5.substring(0, 1), md5.substring(0, 2)}; return new String[]{md5.substring(0, 1), md5.substring(0, 2)};
} }
public static class TravelArticleIdentifier implements Parcelable {
@Nullable File file;
double lat;
double lon;
@Nullable String title;
@Nullable String routeId;
@Nullable String routeSource;
public static final Creator<TravelArticleIdentifier> CREATOR = new Creator<TravelArticleIdentifier>() {
@Override
public TravelArticleIdentifier createFromParcel(Parcel in) {
return new TravelArticleIdentifier(in);
}
@Override
public TravelArticleIdentifier[] newArray(int size) {
return new TravelArticleIdentifier[size];
}
};
private TravelArticleIdentifier(@NonNull Parcel in) {
readFromParcel(in);
}
private TravelArticleIdentifier(@NonNull TravelArticle article) {
file = article.file;
lat = article.lat;
lon = article.lon;
title = article.title;
routeId = article.routeId;
routeSource = article.routeSource;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeDouble(lat);
out.writeDouble(lon);
out.writeString(title);
out.writeString(routeId);
out.writeString(routeSource);
out.writeString(file != null ? file.getAbsolutePath() : null);
}
private void readFromParcel(Parcel in) {
lat = in.readDouble();
lon = in.readDouble();
title = in.readString();
routeId = in.readString();
routeSource = in.readString();
String filePath = in.readString();
if (!Algorithms.isEmpty(filePath)) {
file = new File(filePath);
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TravelArticleIdentifier that = (TravelArticleIdentifier) o;
return areLatLonEqual(that.lat, that.lon, lat, lon) &&
Algorithms.objectEquals(file, that.file) &&
Algorithms.stringsEqual(title, that.title) &&
Algorithms.stringsEqual(routeId, that.routeId) &&
Algorithms.stringsEqual(routeSource, that.routeSource);
}
@Override
public int hashCode() {
return Algorithms.hash(file, lat, lon, title, routeId, routeSource);
}
private static boolean areLatLonEqual(double lat1, double lon1, double lat2, double lon2) {
boolean latEqual = (Double.isNaN(lat1) && Double.isNaN(lat2)) || Math.abs(lat1 - lat2) < 0.00001;
boolean lonEqual = (Double.isNaN(lon1) && Double.isNaN(lon2)) || Math.abs(lon1 - lon2) < 0.00001;
return latEqual && lonEqual;
}
}
} }

View file

@ -16,10 +16,12 @@ import net.osmand.OsmAndCollator;
import net.osmand.PlatformUtil; import net.osmand.PlatformUtil;
import net.osmand.data.Amenity; import net.osmand.data.Amenity;
import net.osmand.data.LatLon; import net.osmand.data.LatLon;
import net.osmand.data.QuadRect;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R; import net.osmand.plus.R;
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.wikivoyage.data.TravelArticle.TravelArticleIdentifier;
import net.osmand.util.Algorithms; import net.osmand.util.Algorithms;
import net.osmand.util.MapUtils; import net.osmand.util.MapUtils;
@ -245,12 +247,15 @@ public class TravelDbHelper implements TravelHelper {
if (cursor != null) { if (cursor != null) {
if (cursor.moveToFirst()) { if (cursor.moveToFirst()) {
do { do {
WikivoyageSearchResult rs = new WikivoyageSearchResult(); String routeId = cursor.getLong(0) + "";
rs.routeId = cursor.getLong(0) + ""; String articleTitle = cursor.getString(1);
rs.articleTitles.add(cursor.getString(1)); String lang = cursor.getString(2);
rs.langs.add(cursor.getString(2)); String isPartOf = cursor.getString(3);
rs.isPartOf.add(cursor.getString(3)); String imageTitle = cursor.getString(4);
rs.imageTitle = cursor.getString(4); List<String> langs = new ArrayList<>();
langs.add(lang);
WikivoyageSearchResult rs = new WikivoyageSearchResult(routeId, articleTitle,
isPartOf, imageTitle, langs);
res.add(rs); res.add(rs);
} while (cursor.moveToNext()); } while (cursor.moveToNext());
} }
@ -386,12 +391,12 @@ public class TravelDbHelper implements TravelHelper {
Collections.sort(list, new Comparator<WikivoyageSearchResult>() { Collections.sort(list, new Comparator<WikivoyageSearchResult>() {
@Override @Override
public int compare(WikivoyageSearchResult o1, WikivoyageSearchResult o2) { public int compare(WikivoyageSearchResult o1, WikivoyageSearchResult o2) {
boolean c1 = CollatorStringMatcher.cmatches(collator, searchQuery, o1.articleTitles.get(0), boolean c1 = CollatorStringMatcher.cmatches(collator, searchQuery, o1.getArticleTitle(),
StringMatcherMode.CHECK_ONLY_STARTS_WITH); StringMatcherMode.CHECK_ONLY_STARTS_WITH);
boolean c2 = CollatorStringMatcher.cmatches(collator, searchQuery, o2.articleTitles.get(0), boolean c2 = CollatorStringMatcher.cmatches(collator, searchQuery, o2.getArticleTitle(),
StringMatcherMode.CHECK_ONLY_STARTS_WITH); StringMatcherMode.CHECK_ONLY_STARTS_WITH);
if (c1 == c2) { if (c1 == c2) {
return collator.compare(o1.articleTitles.get(0), o2.articleTitles.get(0)); return collator.compare(o1.getArticleTitle(), o2.getArticleTitle());
} else if (c1) { } else if (c1) {
return -1; return -1;
} else if (c2) { } else if (c2) {
@ -422,28 +427,27 @@ public class TravelDbHelper implements TravelHelper {
} }
} }
private Collection<WikivoyageSearchResult> groupSearchResultsByRouteId(List<WikivoyageSearchResult> res) { private Collection<WikivoyageSearchResult> groupSearchResultsByRouteId(List<WikivoyageSearchResult> res) {
String baseLng = application.getLanguage(); String baseLng = application.getLanguage();
Map<String, WikivoyageSearchResult> wikivoyage = new HashMap<>(); Map<String, WikivoyageSearchResult> wikivoyage = new HashMap<>();
for (WikivoyageSearchResult rs : res) { for (WikivoyageSearchResult rs : res) {
WikivoyageSearchResult prev = wikivoyage.get(rs.routeId); WikivoyageSearchResult prev = wikivoyage.get(rs.getArticleRouteId());
if (prev != null) { if (prev != null) {
int insInd = prev.langs.size(); boolean matchLang = false;
if (rs.langs.get(0).equals(baseLng)) { if (rs.langs.get(0).equals(baseLng)) {
insInd = 0; matchLang = true;
} else if (rs.langs.get(0).equals("en")) { } else if (rs.langs.get(0).equals("en")) {
if (!prev.langs.get(0).equals(baseLng)) { if (!prev.langs.get(0).equals(baseLng)) {
insInd = 0; matchLang = true;
} else {
insInd = 1;
} }
} }
prev.articleTitles.add(insInd, rs.articleTitles.get(0)); if (matchLang) {
prev.langs.add(insInd, rs.langs.get(0)); prev.articleId.title = rs.getArticleTitle();
prev.isPartOf.add(insInd, rs.isPartOf.get(0)); prev.isPartOf = rs.getIsPartOf();
}
prev.langs.add(matchLang ? 0 : 1, rs.langs.get(0));
} else { } else {
wikivoyage.put(rs.routeId, rs); wikivoyage.put(rs.getArticleRouteId(), rs);
} }
} }
return wikivoyage.values(); return wikivoyage.values();
@ -451,12 +455,11 @@ public class TravelDbHelper implements TravelHelper {
@NonNull @NonNull
@Override @Override
public LinkedHashMap<WikivoyageSearchResult, List<WikivoyageSearchResult>> getNavigationMap( public Map<WikivoyageSearchResult, List<WikivoyageSearchResult>> getNavigationMap(@NonNull final TravelArticle article) {
@NonNull final TravelArticle article) {
String lang = article.getLang(); String lang = article.getLang();
String title = article.getTitle(); String title = article.getTitle();
if (TextUtils.isEmpty(lang) || TextUtils.isEmpty(title)) { if (TextUtils.isEmpty(lang) || TextUtils.isEmpty(title)) {
return new LinkedHashMap<>(); return Collections.emptyMap();
} }
String[] parts = null; String[] parts = null;
if (!TextUtils.isEmpty(article.getAggregatedPartOf())) { if (!TextUtils.isEmpty(article.getAggregatedPartOf())) {
@ -498,20 +501,20 @@ public class TravelDbHelper implements TravelHelper {
SQLiteCursor cursor = conn.rawQuery(query.toString(), params.toArray(new String[0])); SQLiteCursor cursor = conn.rawQuery(query.toString(), params.toArray(new String[0]));
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {
do { do {
WikivoyageSearchResult rs = new WikivoyageSearchResult(); String routeId = cursor.getLong(0) + "";
rs.routeId = cursor.getLong(0) + ""; String articleTitle = cursor.getString(1);
rs.articleTitles.add(cursor.getString(1)); String articleLang = cursor.getString(2);
rs.langs.add(cursor.getString(2)); String isPartOf = cursor.getString(3);
rs.isPartOf.add(cursor.getString(3)); WikivoyageSearchResult rs = new WikivoyageSearchResult(routeId, articleTitle,
List<WikivoyageSearchResult> l = navMap.get(rs.isPartOf.get(0)); isPartOf, null, Collections.singletonList(articleLang));
List<WikivoyageSearchResult> l = navMap.get(rs.isPartOf);
if (l == null) { if (l == null) {
l = new ArrayList<>(); l = new ArrayList<>();
navMap.put(rs.isPartOf.get(0), l); navMap.put(rs.isPartOf, l);
} }
l.add(rs); l.add(rs);
String key = rs.getArticleTitles().get(0); if (headers != null && headers.contains(articleTitle)) {
if (headers != null && headers.contains(key)) { headerObjs.put(articleTitle, rs);
headerObjs.put(key, rs);
} }
} while (cursor.moveToNext()); } while (cursor.moveToNext());
} }
@ -527,12 +530,10 @@ public class TravelDbHelper implements TravelHelper {
Collections.sort(results, new Comparator<WikivoyageSearchResult>() { Collections.sort(results, new Comparator<WikivoyageSearchResult>() {
@Override @Override
public int compare(WikivoyageSearchResult o1, WikivoyageSearchResult o2) { public int compare(WikivoyageSearchResult o1, WikivoyageSearchResult o2) {
return collator.compare(o1.articleTitles.get(0), o2.articleTitles.get(0)); return collator.compare(o1.getArticleTitle(), o2.getArticleTitle());
} }
}); });
WikivoyageSearchResult emptyResult = new WikivoyageSearchResult(); WikivoyageSearchResult emptyResult = new WikivoyageSearchResult("", header, null, null, null);
emptyResult.articleTitles.add(header);
emptyResult.routeId = "";
searchResult = searchResult != null ? searchResult : emptyResult; searchResult = searchResult != null ? searchResult : emptyResult;
res.put(searchResult, results); res.put(searchResult, results);
} }
@ -542,9 +543,10 @@ public class TravelDbHelper implements TravelHelper {
@Override @Override
@Nullable @Nullable
public TravelArticle getArticleById(@NonNull String routeId, @NonNull String lang) { public TravelArticle getArticleById(@NonNull TravelArticleIdentifier articleId, @NonNull String lang) {
TravelArticle res = null; TravelArticle res = null;
SQLiteConnection conn = openConnection(); SQLiteConnection conn = openConnection();
String routeId = articleId.routeId;
if (conn != null && !Algorithms.isEmpty(routeId)) { if (conn != null && !Algorithms.isEmpty(routeId)) {
SQLiteCursor cursor = conn.rawQuery(ARTICLES_TABLE_SELECT + " WHERE " + ARTICLES_COL_TRIP_ID + " = ? AND " SQLiteCursor cursor = conn.rawQuery(ARTICLES_TABLE_SELECT + " WHERE " + ARTICLES_COL_TRIP_ID + " = ? AND "
+ ARTICLES_COL_LANG + " = ?", new String[] { routeId, lang }); + ARTICLES_COL_LANG + " = ?", new String[] { routeId, lang });
@ -558,9 +560,21 @@ public class TravelDbHelper implements TravelHelper {
return res; return res;
} }
@Override
@Nullable @Nullable
public TravelArticle getArticleByTitle(@NonNull String title, @NonNull String lang) { @Override
public TravelArticle getArticleByTitle(@NonNull final String title, @NonNull final String lang) {
return getArticleByTitle(title, new QuadRect(), lang);
}
@Nullable
@Override
public TravelArticle getArticleByTitle(@NonNull final String title, @NonNull LatLon latLon, @NonNull final String lang) {
return getArticleByTitle(title, new QuadRect(), lang);
}
@Nullable
@Override
public TravelArticle getArticleByTitle(@NonNull final String title, @NonNull QuadRect rect, @NonNull final String lang) {
TravelArticle res = null; TravelArticle res = null;
SQLiteConnection conn = openConnection(); SQLiteConnection conn = openConnection();
if (conn != null) { if (conn != null) {
@ -576,33 +590,32 @@ public class TravelDbHelper implements TravelHelper {
return res; return res;
} }
@NonNull @Nullable
@Override @Override
public String getArticleId(@NonNull String title, @NonNull String lang) { public TravelArticleIdentifier getArticleId(@NonNull String title, @NonNull String lang) {
String res = ""; TravelArticle article = null;
SQLiteConnection conn = openConnection(); SQLiteConnection conn = openConnection();
if (conn != null) { if (conn != null) {
SQLiteCursor cursor = conn.rawQuery("SELECT " + ARTICLES_COL_TRIP_ID + " FROM " SQLiteCursor cursor = conn.rawQuery(ARTICLES_TABLE_SELECT + " WHERE " + ARTICLES_COL_TITLE + " = ? AND "
+ ARTICLES_TABLE_NAME + " WHERE " + ARTICLES_COL_TITLE + " = ? AND "
+ ARTICLES_COL_LANG + " = ?", new String[]{title, lang}); + ARTICLES_COL_LANG + " = ?", new String[]{title, lang});
if (cursor != null) { if (cursor != null) {
if (cursor.moveToFirst()) { if (cursor.moveToFirst()) {
res = cursor.getLong(0) + ""; article = readArticle(cursor);
} }
cursor.close(); cursor.close();
} }
} }
return res; return article != null ? article.generateIdentifier() : null;
} }
@NonNull @NonNull
@Override @Override
public ArrayList<String> getArticleLangs(@NonNull String routeId) { public ArrayList<String> getArticleLangs(@NonNull TravelArticleIdentifier articleId) {
ArrayList<String> res = new ArrayList<>(); ArrayList<String> res = new ArrayList<>();
SQLiteConnection conn = openConnection(); SQLiteConnection conn = openConnection();
if (conn != null) { if (conn != null) {
SQLiteCursor cursor = conn.rawQuery("SELECT " + ARTICLES_COL_LANG + " FROM " + ARTICLES_TABLE_NAME SQLiteCursor cursor = conn.rawQuery("SELECT " + ARTICLES_COL_LANG + " FROM " + ARTICLES_TABLE_NAME
+ " WHERE " + ARTICLES_COL_TRIP_ID + " = ?", new String[]{routeId}); + " WHERE " + ARTICLES_COL_TRIP_ID + " = ?", new String[]{articleId.routeId});
if (cursor != null) { if (cursor != null) {
if (cursor.moveToFirst()) { if (cursor.moveToFirst()) {
String baseLang = application.getLanguage(); String baseLang = application.getLanguage();

View file

@ -3,6 +3,10 @@ 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.data.LatLon;
import net.osmand.data.QuadRect;
import net.osmand.plus.wikivoyage.data.TravelArticle.TravelArticleIdentifier;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -28,16 +32,22 @@ public interface TravelHelper {
Map<WikivoyageSearchResult, List<WikivoyageSearchResult>> getNavigationMap(@NonNull final TravelArticle article); Map<WikivoyageSearchResult, List<WikivoyageSearchResult>> getNavigationMap(@NonNull final TravelArticle article);
@Nullable @Nullable
TravelArticle getArticleById(@NonNull String routeId, @NonNull String lang); TravelArticle getArticleById(@NonNull TravelArticleIdentifier articleId, @NonNull String lang);
@Nullable @Nullable
TravelArticle getArticleByTitle(@NonNull String title, @NonNull String lang); public TravelArticle getArticleByTitle(@NonNull final String title, @NonNull final String lang);
@Nullable
public TravelArticle getArticleByTitle(@NonNull final String title, @NonNull LatLon latLon, @NonNull final String lang);
@Nullable
TravelArticle getArticleByTitle(@NonNull String title, @NonNull QuadRect rect, @NonNull String lang);
@Nullable
TravelArticleIdentifier getArticleId(@NonNull String title, @NonNull String lang);
@NonNull @NonNull
String getArticleId(@NonNull String title, @NonNull String lang); ArrayList<String> getArticleLangs(@NonNull TravelArticleIdentifier articleId);
@NonNull
ArrayList<String> getArticleLangs(@NonNull String routeId);
@NonNull @NonNull
String getGPXName(@NonNull final TravelArticle article); String getGPXName(@NonNull final TravelArticle article);

View file

@ -1,10 +1,11 @@
package net.osmand.plus.wikivoyage.data; package net.osmand.plus.wikivoyage.data;
import android.text.TextUtils;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import net.osmand.Collator; import net.osmand.Collator;
import net.osmand.CollatorStringMatcher;
import net.osmand.GPXUtilities; import net.osmand.GPXUtilities;
import net.osmand.GPXUtilities.GPXFile; import net.osmand.GPXUtilities.GPXFile;
import net.osmand.IndexConstants; import net.osmand.IndexConstants;
@ -13,10 +14,13 @@ import net.osmand.PlatformUtil;
import net.osmand.ResultMatcher; import net.osmand.ResultMatcher;
import net.osmand.binary.BinaryMapIndexReader; import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.binary.BinaryMapIndexReader.SearchPoiTypeFilter; import net.osmand.binary.BinaryMapIndexReader.SearchPoiTypeFilter;
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
import net.osmand.data.Amenity; import net.osmand.data.Amenity;
import net.osmand.data.LatLon; import net.osmand.data.LatLon;
import net.osmand.data.QuadRect;
import net.osmand.osm.PoiCategory; import net.osmand.osm.PoiCategory;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
import net.osmand.plus.wikivoyage.data.TravelArticle.TravelArticleIdentifier;
import net.osmand.util.Algorithms; import net.osmand.util.Algorithms;
import net.osmand.util.MapUtils; import net.osmand.util.MapUtils;
@ -25,33 +29,39 @@ import org.apache.commons.logging.Log;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import static net.osmand.CollatorStringMatcher.StringMatcherMode.CHECK_EQUALS_FROM_SPACE; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class TravelObfHelper implements TravelHelper { public class TravelObfHelper implements TravelHelper {
private static final Log LOG = PlatformUtil.getLog(TravelObfHelper.class); private static final Log LOG = PlatformUtil.getLog(TravelObfHelper.class);
public static final String ROUTE_ARTICLE = "route_article"; public static final String ROUTE_ARTICLE = "route_article";
public static final int SEARCH_RADIUS = 100000; public static final int POPULAR_ARTICLES_SEARCH_RADIUS = 100000;
public static final int ARTICLE_SEARCH_RADIUS = 50000;
public static final int MAX_POPULAR_ARTICLES_COUNT = 100;
private final OsmandApplication app; private final OsmandApplication app;
private final Collator collator; private final Collator collator;
private List<TravelArticle> popularArticles = new ArrayList<>(); private List<TravelArticle> popularArticles = new ArrayList<>();
private final Map<String, TravelArticle> cachedArticles; private Map<TravelArticleIdentifier, Map<String, TravelArticle>> cachedArticles = new ConcurrentHashMap<>();
private final TravelLocalDataHelper localDataHelper; private final TravelLocalDataHelper localDataHelper;
public TravelObfHelper(OsmandApplication app) { public TravelObfHelper(OsmandApplication app) {
this.app = app; this.app = app;
collator = OsmAndCollator.primaryCollator(); collator = OsmAndCollator.primaryCollator();
localDataHelper = new TravelLocalDataHelper(app); localDataHelper = new TravelLocalDataHelper(app);
cachedArticles = new HashMap<>();
} }
@Override @Override
@ -71,25 +81,26 @@ public class TravelObfHelper implements TravelHelper {
@NonNull @NonNull
public List<TravelArticle> loadPopularArticles() { public List<TravelArticle> loadPopularArticles() {
String language = app.getLanguage(); String lang = app.getLanguage();
List<TravelArticle> popularArticles = new ArrayList<>(); List<TravelArticle> popularArticles = new ArrayList<>();
for (BinaryMapIndexReader travelBookReader : getTravelBookReaders()) { for (BinaryMapIndexReader reader : getReaders()) {
try { try {
final LatLon location = app.getMapViewTrackingUtilities().getMapLocation(); final LatLon location = app.getMapViewTrackingUtilities().getMapLocation();
BinaryMapIndexReader.SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest( SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(
location, SEARCH_RADIUS, -1, getSearchRouteArticleFilter(), null); location, POPULAR_ARTICLES_SEARCH_RADIUS, -1, getSearchRouteArticleFilter(), null);
List<Amenity> amenities = travelBookReader.searchPoi(req); List<Amenity> amenities = reader.searchPoi(req);
if (amenities.size() > 0) { if (amenities.size() > 0) {
for (Amenity a : amenities) { for (Amenity amenity : amenities) {
if (!Algorithms.isEmpty(a.getName(language))) { if (!Algorithms.isEmpty(amenity.getName(lang))) {
TravelArticle article = readArticle(a, language); TravelArticle article = cacheTravelArticles(reader.getFile(), amenity, lang);
if (article != null) {
popularArticles.add(article); popularArticles.add(article);
cachedArticles.put(article.routeId, article); if (popularArticles.size() >= MAX_POPULAR_ARTICLES_COUNT) {
if (popularArticles.size() >= 100) {
break; break;
} }
} }
} }
}
Collections.sort(popularArticles, new Comparator<TravelArticle>() { Collections.sort(popularArticles, new Comparator<TravelArticle>() {
@Override @Override
public int compare(TravelArticle article1, TravelArticle article2) { public int compare(TravelArticle article1, TravelArticle article2) {
@ -102,13 +113,25 @@ public class TravelObfHelper implements TravelHelper {
}); });
} }
} catch (Exception e) { } catch (Exception e) {
LOG.error(e.getMessage()); LOG.error(e.getMessage(), e);
} }
} }
this.popularArticles = popularArticles; this.popularArticles = popularArticles;
return popularArticles; return popularArticles;
} }
@Nullable
private TravelArticle cacheTravelArticles(File file, Amenity amenity, String lang) {
TravelArticle article = null;
Map<String, TravelArticle> articles = readArticles(file, amenity);
if (!Algorithms.isEmpty(articles)) {
TravelArticleIdentifier newArticleId = articles.values().iterator().next().generateIdentifier();
cachedArticles.put(newArticleId, articles);
article = getCachedArticle(newArticleId, lang);
}
return article;
}
SearchPoiTypeFilter getSearchRouteArticleFilter() { SearchPoiTypeFilter getSearchRouteArticleFilter() {
return new SearchPoiTypeFilter() { return new SearchPoiTypeFilter() {
@Override @Override
@ -123,23 +146,29 @@ public class TravelObfHelper implements TravelHelper {
}; };
} }
private TravelArticle readArticle(@NonNull Amenity amenity, @Nullable String lang) { @NonNull
private Map<String, TravelArticle> readArticles(@NonNull File file, @NonNull Amenity amenity) {
Map<String, TravelArticle> articles = new HashMap<>();
Set<String> langs = getLanguages(amenity);
for (String lang : langs) {
articles.put(lang, readArticle(file, amenity, lang));
}
return articles;
}
@NonNull
private TravelArticle readArticle(@NonNull File file, @NonNull Amenity amenity, @Nullable String lang) {
TravelArticle res = new TravelArticle(); TravelArticle res = new TravelArticle();
String title = Algorithms.isEmpty(amenity.getName(lang)) ? amenity.getName() : amenity.getName(lang); res.file = file;
if (Algorithms.isEmpty(title)) { String title = amenity.getName(lang);
Map<String, String> namesMap = amenity.getNamesMap(true); res.title = Algorithms.isEmpty(title) ? amenity.getName() : title;
if (!namesMap.isEmpty()) {
lang = namesMap.keySet().iterator().next();
title = amenity.getName(lang);
}
}
res.title = title;
res.content = amenity.getDescription(lang); res.content = amenity.getDescription(lang);
res.isPartOf = emptyIfNull(amenity.getTagContent(Amenity.IS_PART, lang)); res.isPartOf = emptyIfNull(amenity.getTagContent(Amenity.IS_PART, lang));
res.lat = amenity.getLocation().getLatitude(); res.lat = amenity.getLocation().getLatitude();
res.lon = amenity.getLocation().getLongitude(); res.lon = amenity.getLocation().getLongitude();
res.imageTitle = emptyIfNull(amenity.getTagContent(Amenity.IMAGE_TITLE, lang)); res.imageTitle = emptyIfNull(amenity.getTagContent(Amenity.IMAGE_TITLE, null));
res.routeId = getRouteId(amenity); res.routeId = emptyIfNull(amenity.getTagContent(Amenity.ROUTE_ID, null));
res.routeSource = emptyIfNull(amenity.getTagContent(Amenity.ROUTE_SOURCE, null));
res.originalId = 0; res.originalId = 0;
res.lang = lang; res.lang = lang;
res.contentsJson = emptyIfNull(amenity.getTagContent(Amenity.CONTENT_JSON, lang)); res.contentsJson = emptyIfNull(amenity.getTagContent(Amenity.CONTENT_JSON, lang));
@ -151,87 +180,74 @@ public class TravelObfHelper implements TravelHelper {
return text == null ? "" : text; return text == null ? "" : text;
} }
private String getRouteId(Amenity amenity) {
return amenity.getTagContent(Amenity.ROUTE_ID, null);
}
@Override @Override
public boolean isAnyTravelBookPresent() { public boolean isAnyTravelBookPresent() {
return !Algorithms.isEmpty(getTravelBookReaders()); return !Algorithms.isEmpty(getReaders());
} }
@NonNull @NonNull
@Override @Override
public List<WikivoyageSearchResult> search(@NonNull String searchQuery) { public List<WikivoyageSearchResult> search(@NonNull String searchQuery) {
List<WikivoyageSearchResult> res = new ArrayList<>(); List<WikivoyageSearchResult> res = new ArrayList<>();
List<Amenity> searchObjects = null; Map<File, List<Amenity>> amenityMap = new HashMap<>();
for (BinaryMapIndexReader reader : app.getResourceManager().getTravelRepositories()) { for (BinaryMapIndexReader reader : getReaders()) {
try { try {
BinaryMapIndexReader.SearchRequest<Amenity> searchRequest = BinaryMapIndexReader. SearchRequest<Amenity> searchRequest = BinaryMapIndexReader.buildSearchPoiRequest(0, 0, searchQuery,
buildSearchPoiRequest(0, 0, searchQuery,
0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, getSearchRouteArticleFilter(), null, null); 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, getSearchRouteArticleFilter(), null, null);
searchObjects = reader.searchPoiByName(searchRequest); List<Amenity> amenities = reader.searchPoiByName(searchRequest);
if (!Algorithms.isEmpty(amenities)) {
amenityMap.put(reader.getFile(), amenities);
}
} catch (IOException e) { } catch (IOException e) {
LOG.error(e); LOG.error(e.getMessage(), e);
} }
} }
if (!Algorithms.isEmpty(searchObjects)) { if (!Algorithms.isEmpty(amenityMap)) {
String baseLng = app.getLanguage(); String appLang = app.getLanguage();
for (Amenity obj : searchObjects) { for (Entry<File, List<Amenity>> entry : amenityMap.entrySet()) {
WikivoyageSearchResult r = new WikivoyageSearchResult(); File file = entry.getKey();
TravelArticle article = readArticle(obj, baseLng); for (Amenity amenity : entry.getValue()) {
r.articleTitles = new ArrayList<>(Collections.singletonList(article.title)); Set<String> nameLangs = getLanguages(amenity);
r.imageTitle = article.imageTitle; if (nameLangs.contains(appLang)) {
r.routeId = article.routeId; TravelArticle article = readArticle(file, amenity, appLang);
r.isPartOf = new ArrayList<>(Collections.singletonList(article.isPartOf)); WikivoyageSearchResult r = new WikivoyageSearchResult(article, new ArrayList<>(nameLangs));
r.langs = new ArrayList<>(Collections.singletonList(baseLng));
res.add(r); res.add(r);
cachedArticles.put(article.routeId, article);
} }
res = new ArrayList<>(groupSearchResultsByRouteId(res)); }
}
sortSearchResults(res); sortSearchResults(res);
} }
return res; return res;
} }
private Set<String> getLanguages(@NonNull Amenity amenity) {
Set<String> langs = new HashSet<>();
String descrStart = Amenity.DESCRIPTION + ":";
String partStart = Amenity.IS_PART + ":";
for (String infoTag : amenity.getAdditionalInfoKeys()) {
if (infoTag.startsWith(descrStart)) {
if (infoTag.length() > descrStart.length()) {
langs.add(infoTag.substring(descrStart.length()));
}
} else if (infoTag.startsWith(partStart)) {
if (infoTag.length() > partStart.length()) {
langs.add(infoTag.substring(partStart.length()));
}
}
}
return langs;
}
private void sortSearchResults(@NonNull List<WikivoyageSearchResult> list) { private void sortSearchResults(@NonNull List<WikivoyageSearchResult> list) {
Collections.sort(list, new Comparator<WikivoyageSearchResult>() { Collections.sort(list, new Comparator<WikivoyageSearchResult>() {
@Override @Override
public int compare(WikivoyageSearchResult res1, WikivoyageSearchResult res2) { public int compare(WikivoyageSearchResult res1, WikivoyageSearchResult res2) {
return collator.compare(res1.articleTitles.get(0), res2.articleTitles.get(0)); return collator.compare(res1.articleId.title, res2.articleId.title);
} }
}); });
} }
@NonNull
private Collection<WikivoyageSearchResult> groupSearchResultsByRouteId(@NonNull List<WikivoyageSearchResult> res) {
String baseLng = app.getLanguage();
Map<String, WikivoyageSearchResult> wikivoyage = new HashMap<>();
for (WikivoyageSearchResult rs : res) {
WikivoyageSearchResult prev = wikivoyage.get(rs.routeId);
if (prev != null) {
int insInd = prev.langs.size();
if (rs.langs.get(0).equals(baseLng)) {
insInd = 0;
} else if (rs.langs.get(0).equals("en")) {
if (!prev.langs.get(0).equals(baseLng)) {
insInd = 0;
} else {
insInd = 1;
}
}
prev.articleTitles.add(insInd, rs.articleTitles.get(0));
prev.langs.add(insInd, rs.langs.get(0));
prev.isPartOf.add(insInd, rs.isPartOf.get(0));
} else {
wikivoyage.put(rs.routeId, rs);
}
}
return wikivoyage.values();
}
@NonNull @NonNull
@Override @Override
public List<TravelArticle> getPopularArticles() { public List<TravelArticle> getPopularArticles() {
@ -241,71 +257,153 @@ public class TravelObfHelper implements TravelHelper {
@NonNull @NonNull
@Override @Override
public Map<WikivoyageSearchResult, List<WikivoyageSearchResult>> getNavigationMap(@NonNull final TravelArticle article) { public Map<WikivoyageSearchResult, List<WikivoyageSearchResult>> getNavigationMap(@NonNull final TravelArticle article) {
final String lang = article.getLang();
final String title = article.getTitle();
if (TextUtils.isEmpty(lang) || TextUtils.isEmpty(title)) {
return Collections.emptyMap(); return Collections.emptyMap();
} }
final String[] parts;
@Override if (!TextUtils.isEmpty(article.getAggregatedPartOf())) {
public TravelArticle getArticleById(@NonNull String routeId, @NonNull String lang) { String[] originalParts = article.getAggregatedPartOf().split(",");
TravelArticle article = cachedArticles.get(routeId); if (originalParts.length > 1) {
if (article != null) { parts = new String[originalParts.length];
return article; for (int i = 0; i < originalParts.length; i++) {
parts[i] = originalParts[originalParts.length - i - 1];
}
} else { } else {
return getArticleByIdFromTravelBooks(routeId, lang); parts = originalParts;
} }
} else {
parts = null;
} }
Map<String, List<WikivoyageSearchResult>> navMap = new HashMap<>();
private TravelArticle getArticleByIdFromTravelBooks(final String routeId, final String lang) { Set<String> headers = new LinkedHashSet<String>();
TravelArticle article = null; Map<String, WikivoyageSearchResult> headerObjs = new HashMap<>();
final List<Amenity> amenities = new ArrayList<>(); Map<File, List<Amenity>> amenityMap = new HashMap<>();
for (BinaryMapIndexReader travelBookReader : getTravelBookReaders()) { for (BinaryMapIndexReader reader : getReaders()) {
try { try {
BinaryMapIndexReader.SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest( SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(0,
0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, -1, getSearchRouteArticleFilter(), Integer.MAX_VALUE, 0, Integer.MAX_VALUE, -1, getSearchRouteArticleFilter(), new ResultMatcher<Amenity>() {
new ResultMatcher<Amenity>() {
boolean done = false;
@Override @Override
public boolean publish(Amenity amenity) { public boolean publish(Amenity amenity) {
if (getRouteId(amenity).equals(routeId)) { String isPartOf = amenity.getTagContent(Amenity.IS_PART, lang);
amenities.add(amenity); if (Algorithms.stringsEqual(title, isPartOf)) {
done = true; return true;
} else if (parts != null && parts.length > 0) {
String title = amenity.getName(lang);
title = Algorithms.isEmpty(title) ? amenity.getName() : title;
for (int i = 0; i < parts.length; i++) {
String part = parts[i];
if (i == 0 && Algorithms.stringsEqual(part, title) || Algorithms.stringsEqual(part, isPartOf)) {
return true;
}
}
} }
return false; return false;
} }
@Override @Override
public boolean isCancelled() { public boolean isCancelled() {
return done; return false;
} }
}); });
List<Amenity> amenities = reader.searchPoi(req);
if (!Algorithms.isEmpty(amenities)) {
amenityMap.put(reader.getFile(), amenities);
}
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
if (parts != null && parts.length > 0) {
headers.addAll(Arrays.asList(parts));
headers.add(title);
}
if (!Algorithms.isEmpty(amenityMap)) {
for (Entry<File, List<Amenity>> entry : amenityMap.entrySet()) {
File file = entry.getKey();
for (Amenity amenity : entry.getValue()) {
Set<String> nameLangs = getLanguages(amenity);
if (nameLangs.contains(lang)) {
TravelArticle a = readArticle(file, amenity, lang);
WikivoyageSearchResult rs = new WikivoyageSearchResult(a, new ArrayList<>(nameLangs));
List<WikivoyageSearchResult> l = navMap.get(rs.isPartOf);
if (l == null) {
l = new ArrayList<>();
navMap.put(rs.isPartOf, l);
}
l.add(rs);
if (headers != null && headers.contains(a.getTitle())) {
headerObjs.put(a.getTitle(), rs);
}
}
}
}
}
travelBookReader.searchPoi(req); LinkedHashMap<WikivoyageSearchResult, List<WikivoyageSearchResult>> res = new LinkedHashMap<>();
} catch (IOException e) { for (String header : headers) {
LOG.error(e.getMessage()); WikivoyageSearchResult searchResult = headerObjs.get(header);
List<WikivoyageSearchResult> results = navMap.get(header);
if (results != null) {
Collections.sort(results, new Comparator<WikivoyageSearchResult>() {
@Override
public int compare(WikivoyageSearchResult o1, WikivoyageSearchResult o2) {
return collator.compare(o1.getArticleTitle(), o2.getArticleTitle());
} }
if (!amenities.isEmpty()) { });
article = readArticle(amenities.get(0), lang); WikivoyageSearchResult emptyResult = new WikivoyageSearchResult("", header, null, null, null);
cachedArticles.put(article.routeId, article); searchResult = searchResult != null ? searchResult : emptyResult;
res.put(searchResult, results);
} }
} }
return article; return res;
}
@Override
public TravelArticle getArticleById(@NonNull TravelArticleIdentifier articleId, @NonNull String lang) {
TravelArticle article = getCachedArticle(articleId, lang);
return article == null ? findArticleById(articleId, lang) : article;
} }
@Nullable @Nullable
@Override private TravelArticle getCachedArticle(@NonNull TravelArticleIdentifier articleId, @NonNull String lang) {
public TravelArticle getArticleByTitle(@NonNull final String title, @NonNull final String lang) { TravelArticle article = null;
Map<String, TravelArticle> articles = cachedArticles.get(articleId);
if (articles != null) {
if (Algorithms.isEmpty(lang)) {
Collection<TravelArticle> ac = articles.values();
if (!ac.isEmpty()) {
article = ac.iterator().next();
}
} else {
article = articles.get(lang);
if (article == null) {
article = articles.get("");
}
}
}
return article == null ? findArticleById(articleId, lang) : article;
}
private TravelArticle findArticleById(@NonNull final TravelArticleIdentifier articleId, final String lang) {
TravelArticle article = null; TravelArticle article = null;
final List<Amenity> amenities = new ArrayList<>(); final List<Amenity> amenities = new ArrayList<>();
for (BinaryMapIndexReader travelBookReader : getTravelBookReaders()) { for (BinaryMapIndexReader reader : getReaders()) {
try { try {
BinaryMapIndexReader.SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest( if (articleId.file != null && !articleId.file.equals(reader.getFile())) {
0, 0, title, 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, getSearchRouteArticleFilter(), continue;
new ResultMatcher<Amenity>() { }
SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(0, 0,
Algorithms.emptyIfNull(articleId.title), 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE,
getSearchRouteArticleFilter(), new ResultMatcher<Amenity>() {
boolean done = false; boolean done = false;
@Override @Override
public boolean publish(Amenity amenity) { public boolean publish(Amenity amenity) {
if (CollatorStringMatcher.cmatches(collator, title, amenity.getName(lang), CHECK_EQUALS_FROM_SPACE)) { 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)))) {
amenities.add(amenity); amenities.add(amenity);
done = true; done = true;
} }
@ -318,19 +416,74 @@ public class TravelObfHelper implements TravelHelper {
} }
}, null); }, null);
travelBookReader.searchPoiByName(req); if (!Double.isNaN(articleId.lat)) {
req.setBBoxRadius(articleId.lat, articleId.lon, ARTICLE_SEARCH_RADIUS);
if (!Algorithms.isEmpty(articleId.title)) {
reader.searchPoiByName(req);
} else {
reader.searchPoi(req);
}
} else {
reader.searchPoi(req);
}
} catch (IOException e) { } catch (IOException e) {
LOG.error(e.getMessage()); LOG.error(e.getMessage());
} }
if (!amenities.isEmpty()) { if (!amenities.isEmpty()) {
article = readArticle(amenities.get(0), lang); article = cacheTravelArticles(reader.getFile(), amenities.get(0), lang);
cachedArticles.put(article.routeId, article);
} }
} }
return article; return article;
} }
private List<BinaryMapIndexReader> getTravelBookReaders() { @Nullable
@Override
public TravelArticle getArticleByTitle(@NonNull final String title, @NonNull final String lang) {
return getArticleByTitle(title, new QuadRect(), lang);
}
@Nullable
@Override
public TravelArticle getArticleByTitle(@NonNull final String title, @NonNull LatLon latLon, @NonNull final String lang) {
QuadRect rect = latLon != null ? MapUtils.calculateLatLonBbox(latLon.getLatitude(), latLon.getLongitude(), ARTICLE_SEARCH_RADIUS) : new QuadRect();
return getArticleByTitle(title, rect, lang);
}
@Nullable
@Override
public TravelArticle getArticleByTitle(@NonNull final String title, @NonNull QuadRect rect, @NonNull final String lang) {
TravelArticle article = null;
List<Amenity> amenities = null;
int x = 0;
int y = 0;
int left = 0;
int right = Integer.MAX_VALUE;
int top = 0;
int bottom = Integer.MAX_VALUE;
if (rect.height() > 0 && rect.width() > 0) {
x = (int) rect.centerX();
y = (int) rect.centerY();
left = (int) rect.left;
right = (int) rect.right;
top = (int) rect.top;
bottom = (int) rect.bottom;
}
for (BinaryMapIndexReader reader : getReaders()) {
try {
SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(
x, y, title, left, right, top, bottom, getSearchRouteArticleFilter(), null, null);
amenities = reader.searchPoiByName(req);
} catch (IOException e) {
LOG.error(e.getMessage());
}
if (!Algorithms.isEmpty(amenities)) {
article = cacheTravelArticles(reader.getFile(), amenities.get(0), lang);
}
}
return article;
}
private List<BinaryMapIndexReader> getReaders() {
if (!app.isApplicationInitializing()) { if (!app.isApplicationInitializing()) {
return app.getResourceManager().getTravelRepositories(); return app.getResourceManager().getTravelRepositories();
} else { } else {
@ -338,33 +491,36 @@ public class TravelObfHelper implements TravelHelper {
} }
} }
@NonNull @Nullable
@Override @Override
public String getArticleId(@NonNull String title, @NonNull String lang) { public TravelArticleIdentifier getArticleId(@NonNull String title, @NonNull String lang) {
TravelArticle a = null; TravelArticle a = null;
for (TravelArticle article : cachedArticles.values()) { for (Map<String, TravelArticle> articles : cachedArticles.values()) {
for (TravelArticle article : articles.values()) {
if (article.getTitle().equals(title)) { if (article.getTitle().equals(title)) {
a = article; a = article;
break; break;
} }
} }
}
if (a == null) { if (a == null) {
TravelArticle article = getArticleByTitle(title, lang); TravelArticle article = getArticleByTitle(title, lang);
if (article != null) { if (article != null) {
a = article; a = article;
} }
} }
return a != null && a.getRouteId() != null ? a.getRouteId() : ""; return a != null ? a.generateIdentifier() : null;
} }
@NonNull @NonNull
@Override @Override
public ArrayList<String> getArticleLangs(@NonNull String routeId) { public ArrayList<String> getArticleLangs(@NonNull TravelArticleIdentifier articleId) {
ArrayList<String> res = new ArrayList<>(); ArrayList<String> res = new ArrayList<>();
res.add("en"); TravelArticle article = getArticleById(articleId, "");
for (TravelArticle article : popularArticles) { if (article != null) {
if (article.getRouteId().equals(routeId)) { Map<String, TravelArticle> articles = cachedArticles.get(articleId);
res.add(article.getLang()); if (articles != null) {
res.addAll(articles.keySet());
} }
} }
return res; return res;

View file

@ -1,5 +1,9 @@
package net.osmand.plus.wikivoyage.data; package net.osmand.plus.wikivoyage.data;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import net.osmand.plus.wikivoyage.data.TravelArticle.TravelArticleIdentifier;
import net.osmand.util.Algorithms; import net.osmand.util.Algorithms;
import java.util.ArrayList; import java.util.ArrayList;
@ -9,25 +13,52 @@ public class WikivoyageSearchResult {
private static final int SHOW_LANGS = 3; private static final int SHOW_LANGS = 3;
String routeId; TravelArticleIdentifier articleId;
List<String> articleTitles = new ArrayList<>();
List<String> langs = new ArrayList<>();
List<String> isPartOf = new ArrayList<>();
String imageTitle;
public String getRouteId() { String imageTitle;
return routeId; String isPartOf;
List<String> langs = new ArrayList<>();
public WikivoyageSearchResult(@NonNull TravelArticle article, @Nullable List<String> langs) {
articleId = article.generateIdentifier();
imageTitle = article.imageTitle;
isPartOf = article.isPartOf;
if (langs != null) {
this.langs = langs;
}
} }
public List<String> getArticleTitles() { public WikivoyageSearchResult(String routeId, String articleTitle, String isPartOf, String imageTitle, @Nullable List<String> langs) {
return articleTitles; TravelArticle article = new TravelArticle();
article.routeId = routeId;
article.title = articleTitle;
this.articleId = article.generateIdentifier();
this.imageTitle = imageTitle;
this.isPartOf = isPartOf;
if (langs != null) {
this.langs = langs;
}
}
public TravelArticleIdentifier getArticleId() {
return articleId;
}
public String getArticleTitle() {
return articleId.title;
}
public String getArticleRouteId() {
return articleId.routeId;
} }
public List<String> getLangs() { public List<String> getLangs() {
return langs; return langs;
} }
public List<String> getIsPartOf() { public String getIsPartOf() {
return isPartOf; return isPartOf;
} }

View file

@ -50,7 +50,7 @@ public class SavedArticlesTabFragment extends BaseOsmAndFragment implements Trav
public void openArticle(TravelArticle article) { public void openArticle(TravelArticle article) {
FragmentManager fm = getFragmentManager(); FragmentManager fm = getFragmentManager();
if (fm != null) { if (fm != null) {
WikivoyageArticleDialogFragment.showInstanceByTitle(app, fm, article.getTitle(), article.getLang()); WikivoyageArticleDialogFragment.showInstance(app, fm, article.generateIdentifier(), article.getLang());
} }
} }
}); });

View file

@ -37,6 +37,7 @@ import net.osmand.plus.download.DownloadIndexesThread.DownloadEvents;
import net.osmand.plus.wikipedia.WikiArticleHelper; import net.osmand.plus.wikipedia.WikiArticleHelper;
import net.osmand.plus.wikivoyage.article.WikivoyageArticleDialogFragment; import net.osmand.plus.wikivoyage.article.WikivoyageArticleDialogFragment;
import net.osmand.plus.wikivoyage.data.TravelArticle; import net.osmand.plus.wikivoyage.data.TravelArticle;
import net.osmand.plus.wikivoyage.data.TravelArticle.TravelArticleIdentifier;
import net.osmand.plus.wikivoyage.data.TravelHelper; import net.osmand.plus.wikivoyage.data.TravelHelper;
import net.osmand.plus.wikivoyage.data.TravelLocalDataHelper; import net.osmand.plus.wikivoyage.data.TravelLocalDataHelper;
import net.osmand.plus.wikivoyage.search.WikivoyageSearchDialogFragment; import net.osmand.plus.wikivoyage.search.WikivoyageSearchDialogFragment;
@ -50,8 +51,8 @@ public class WikivoyageExploreActivity extends TabActivity implements DownloadEv
TravelLocalDataHelper.Listener { TravelLocalDataHelper.Listener {
private static final String TAB_SELECTED = "tab_selected"; private static final String TAB_SELECTED = "tab_selected";
private static final String ROUTE_ID_KEY = "route_id_key"; private static final String ARTICLE_ID_KEY = "article_id";
private static final String SELECTED_LANG_KEY = "selected_lang_key"; private static final String SELECTED_LANG_KEY = "selected_lang";
private static final int EXPLORE_POSITION = 0; private static final int EXPLORE_POSITION = 0;
private static final int SAVED_ARTICLES_POSITION = 1; private static final int SAVED_ARTICLES_POSITION = 1;
@ -182,9 +183,9 @@ public class WikivoyageExploreActivity extends TabActivity implements DownloadEv
BottomNavigationView bottomNav = (BottomNavigationView) findViewById(R.id.bottom_navigation); BottomNavigationView bottomNav = (BottomNavigationView) findViewById(R.id.bottom_navigation);
bottomNav.setSelectedItemId(R.id.action_saved_articles); bottomNav.setSelectedItemId(R.id.action_saved_articles);
} }
String articleId = intent.getStringExtra(ROUTE_ID_KEY); TravelArticleIdentifier articleId = intent.getParcelableExtra(ARTICLE_ID_KEY);
String selectedLang = intent.getStringExtra(SELECTED_LANG_KEY); String selectedLang = intent.getStringExtra(SELECTED_LANG_KEY);
if (!Algorithms.isEmpty(articleId)) { if (articleId != null) {
WikivoyageArticleDialogFragment.showInstance(app, getSupportFragmentManager(), articleId, selectedLang); WikivoyageArticleDialogFragment.showInstance(app, getSupportFragmentManager(), articleId, selectedLang);
} }
} }
@ -201,8 +202,8 @@ public class WikivoyageExploreActivity extends TabActivity implements DownloadEv
String title = WikiArticleHelper.decodeTitleFromTravelUrl(data.getQueryParameter("title")); String title = WikiArticleHelper.decodeTitleFromTravelUrl(data.getQueryParameter("title"));
String selectedLang = data.getQueryParameter("lang"); String selectedLang = data.getQueryParameter("lang");
if (!Algorithms.isEmpty(title) && !Algorithms.isEmpty(selectedLang)) { if (!Algorithms.isEmpty(title) && !Algorithms.isEmpty(selectedLang)) {
String articleId = app.getTravelHelper().getArticleId(title, selectedLang); TravelArticleIdentifier articleId = app.getTravelHelper().getArticleId(title, selectedLang);
if (!articleId.isEmpty()) { if (articleId != null) {
WikivoyageArticleDialogFragment.showInstance(app, getSupportFragmentManager(), articleId, selectedLang); WikivoyageArticleDialogFragment.showInstance(app, getSupportFragmentManager(), articleId, selectedLang);
} }
} }
@ -279,7 +280,7 @@ public class WikivoyageExploreActivity extends TabActivity implements DownloadEv
private void applyIntentParameters(Intent intent, TravelArticle article) { private void applyIntentParameters(Intent intent, TravelArticle article) {
intent.putExtra(TAB_SELECTED, viewPager.getCurrentItem()); intent.putExtra(TAB_SELECTED, viewPager.getCurrentItem());
intent.putExtra(ROUTE_ID_KEY, article.getRouteId()); intent.putExtra(ARTICLE_ID_KEY, article.generateIdentifier());
intent.putExtra(SELECTED_LANG_KEY, article.getLang()); intent.putExtra(SELECTED_LANG_KEY, article.getLang());
} }

View file

@ -76,7 +76,8 @@ public class ArticleTravelCard extends BaseTravelCard {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if (fragmentManager != null) { if (fragmentManager != null) {
WikivoyageArticleDialogFragment.showInstance(app, fragmentManager, article.getRouteId(), article.getLang()); WikivoyageArticleDialogFragment.showInstance(app, fragmentManager,
article.generateIdentifier(), article.getLang());
} }
} }
}; };

View file

@ -7,6 +7,7 @@ import androidx.annotation.NonNull;
import net.osmand.GPXUtilities.GPXFile; import net.osmand.GPXUtilities.GPXFile;
import net.osmand.GPXUtilities.Metadata; import net.osmand.GPXUtilities.Metadata;
import net.osmand.GPXUtilities.WptPt; import net.osmand.GPXUtilities.WptPt;
import net.osmand.data.LatLon;
import net.osmand.data.PointDescription; import net.osmand.data.PointDescription;
import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile; import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
import net.osmand.plus.R; import net.osmand.plus.R;
@ -14,12 +15,13 @@ import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.mapcontextmenu.controllers.WptPtMenuController; import net.osmand.plus.mapcontextmenu.controllers.WptPtMenuController;
import net.osmand.plus.wikivoyage.article.WikivoyageArticleDialogFragment; import net.osmand.plus.wikivoyage.article.WikivoyageArticleDialogFragment;
import net.osmand.plus.wikivoyage.data.TravelArticle; import net.osmand.plus.wikivoyage.data.TravelArticle;
import net.osmand.plus.wikivoyage.data.TravelArticle.TravelArticleIdentifier;
public class WikivoyageWptPtMenuController extends WptPtMenuController { public class WikivoyageWptPtMenuController extends WptPtMenuController {
private WikivoyageWptPtMenuController(@NonNull MapActivity mapActivity, @NonNull PointDescription pointDescription, @NonNull WptPt wpt, @NonNull TravelArticle article) { private WikivoyageWptPtMenuController(@NonNull MapActivity mapActivity, @NonNull PointDescription pointDescription, @NonNull WptPt wpt, @NonNull TravelArticle article) {
super(new WikivoyageWptPtMenuBuilder(mapActivity, wpt), mapActivity, pointDescription, wpt); super(new WikivoyageWptPtMenuBuilder(mapActivity, wpt), mapActivity, pointDescription, wpt);
final String tripId = article.getRouteId(); final TravelArticleIdentifier articleId = article.generateIdentifier();
final String lang = article.getLang(); final String lang = article.getLang();
leftTitleButtonController = new TitleButtonController() { leftTitleButtonController = new TitleButtonController() {
@Override @Override
@ -27,7 +29,7 @@ public class WikivoyageWptPtMenuController extends WptPtMenuController {
MapActivity mapActivity = getMapActivity(); MapActivity mapActivity = getMapActivity();
if (mapActivity != null) { if (mapActivity != null) {
WikivoyageArticleDialogFragment.showInstance(mapActivity.getMyApplication(), WikivoyageArticleDialogFragment.showInstance(mapActivity.getMyApplication(),
mapActivity.getSupportFragmentManager(), tripId, lang); mapActivity.getSupportFragmentManager(), articleId, lang);
} }
} }
}; };
@ -42,7 +44,7 @@ public class WikivoyageWptPtMenuController extends WptPtMenuController {
String title = metadata != null ? metadata.getArticleTitle() : null; String title = metadata != null ? metadata.getArticleTitle() : null;
String lang = metadata != null ? metadata.getArticleLang() : null; String lang = metadata != null ? metadata.getArticleLang() : null;
if (!TextUtils.isEmpty(title) && !TextUtils.isEmpty(lang)) { if (!TextUtils.isEmpty(title) && !TextUtils.isEmpty(lang)) {
return mapActivity.getMyApplication().getTravelHelper().getArticleByTitle(title, lang); return mapActivity.getMyApplication().getTravelHelper().getArticleByTitle(title, new LatLon(wpt.lat, wpt.lon), lang);
} }
return null; return null;
} }

View file

@ -83,8 +83,8 @@ public class SearchRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView
rc.transform(new CropCircleTransformation()) rc.transform(new CropCircleTransformation())
.placeholder(placeholder) .placeholder(placeholder)
.into(holder.icon); .into(holder.icon);
holder.title.setText(searchRes.getArticleTitles().get(0)); holder.title.setText(searchRes.getArticleTitle());
holder.leftDescr.setText(searchRes.getIsPartOf().get(0)); holder.leftDescr.setText(searchRes.getIsPartOf());
holder.rightDescr.setText(searchRes.getFirstLangsString()); holder.rightDescr.setText(searchRes.getFirstLangsString());
} else { } else {
WikivoyageSearchHistoryItem historyItem = (WikivoyageSearchHistoryItem) item; WikivoyageSearchHistoryItem historyItem = (WikivoyageSearchHistoryItem) item;

View file

@ -112,8 +112,7 @@ public class WikivoyageSearchDialogFragment extends WikiBaseDialogFragment {
Object item = adapter.getItem(pos); Object item = adapter.getItem(pos);
if (item instanceof WikivoyageSearchResult) { if (item instanceof WikivoyageSearchResult) {
WikivoyageSearchResult res = (WikivoyageSearchResult) item; WikivoyageSearchResult res = (WikivoyageSearchResult) item;
WikivoyageArticleDialogFragment WikivoyageArticleDialogFragment.showInstance(fm, res.getArticleId(), new ArrayList<>(res.getLangs()));
.showInstance(fm, res.getRouteId(), new ArrayList<>(res.getLangs()));
} else if (item instanceof WikivoyageSearchHistoryItem) { } else if (item instanceof WikivoyageSearchHistoryItem) {
WikivoyageSearchHistoryItem historyItem = (WikivoyageSearchHistoryItem) item; WikivoyageSearchHistoryItem historyItem = (WikivoyageSearchHistoryItem) item;
WikivoyageArticleDialogFragment WikivoyageArticleDialogFragment