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),
lat, MapUtils.getLongitudeFromTile(16, (int) dx));
double cf31 = ((double) radiusMeters / (half16t * 2)) * (1 << 15);
int y31 = MapUtils.get31TileNumberY(lat);
int x31 = MapUtils.get31TileNumberX(lon);
left = (int) (x31 - cf31);
right = (int) (x31 + cf31);
top = (int) (y31 - cf31);
bottom = (int) (y31 + cf31);
y = MapUtils.get31TileNumberY(lat);
x = MapUtils.get31TileNumberX(lon);
left = (int) (x - cf31);
right = (int) (x + cf31);
top = (int) (y - cf31);
bottom = (int) (y + cf31);
}
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 CONTENT_JSON = "content_json";
public static final String ROUTE_ID = "route_id";
public static final String ROUTE_SOURCE = "route_source";
private String subType;

View file

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

View file

@ -78,6 +78,10 @@ public class Algorithms {
return map == null || map.size() == 0;
}
public static String emptyIfNull(String s) {
return s == null ? "" : s;
}
public static boolean isEmpty(CharSequence s) {
return s == null || s.length() == 0;
}
@ -86,6 +90,10 @@ public class Algorithms {
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) {
if (s1 == null && s2 == null) {
return true;

View file

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

View file

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

View file

@ -30,17 +30,18 @@ import net.osmand.AndroidUtils;
import net.osmand.IndexConstants;
import net.osmand.plus.OsmandApplication;
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.UiUtilities;
import net.osmand.plus.activities.TrackActivity;
import net.osmand.plus.development.OsmandDevelopmentPlugin;
import net.osmand.plus.helpers.FileNameTranslationHelper;
import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.wikipedia.WikiArticleBaseDialogFragment;
import net.osmand.plus.wikipedia.WikiArticleHelper;
import net.osmand.plus.wikivoyage.WikivoyageShowPicturesDialogFragment;
import net.osmand.plus.wikivoyage.WikivoyageWebViewClient;
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.TravelLocalDataHelper;
import net.osmand.util.Algorithms;
@ -58,15 +59,15 @@ public class WikivoyageArticleDialogFragment extends WikiArticleBaseDialogFragme
public static final String TAG = "WikivoyageArticleDialogFragment";
private static final String ROUTE_ID_KEY = "route_id_key";
private static final String LANGS_KEY = "langs_key";
private static final String SELECTED_LANG_KEY = "selected_lang_key";
private static final String ARTICLE_ID_KEY = "article_id";
private static final String LANGS_KEY = "langs";
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 int MENU_ITEM_SHARE = 0;
private String routeId = "";
private TravelArticleIdentifier articleId;
private ArrayList<String> langs;
private String selectedLang;
private TravelArticle article;
@ -192,15 +193,17 @@ public class WikivoyageArticleDialogFragment extends WikiArticleBaseDialogFragme
if (requestCode == WikivoyageArticleContentsFragment.SHOW_CONTENT_ITEM_REQUEST_CODE) {
String link = data.getStringExtra(WikivoyageArticleContentsFragment.CONTENT_ITEM_LINK_KEY);
String title = data.getStringExtra(WikivoyageArticleContentsFragment.CONTENT_ITEM_TITLE_KEY);
moveToAnchor(link, title);
if (title != null) {
moveToAnchor(link, title);
}
} else if (requestCode == WikivoyageShowPicturesDialogFragment.SHOW_PICTURES_CHANGED_REQUEST_CODE) {
updateWebSettings();
populateArticle();
} 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);
if (!Algorithms.isEmpty(tripId) && !TextUtils.isEmpty(selectedLang)) {
this.routeId = tripId;
if (articleId != null && !TextUtils.isEmpty(selectedLang)) {
this.articleId = articleId;
this.selectedLang = selectedLang;
populateArticle();
}
@ -286,21 +289,21 @@ public class WikivoyageArticleDialogFragment extends WikiArticleBaseDialogFragme
@Override
protected void populateArticle() {
if (Algorithms.isEmpty(routeId) || langs == null) {
if (articleId == null || langs == null) {
Bundle args = getArguments();
if (args != null) {
routeId = args.getString(ROUTE_ID_KEY);
articleId = args.getParcelable(ARTICLE_ID_KEY);
langs = args.getStringArrayList(LANGS_KEY);
}
}
if (Algorithms.isEmpty(routeId) || langs == null || langs.isEmpty()) {
if (articleId == null || langs == null || langs.isEmpty()) {
return;
}
if (selectedLang == null) {
selectedLang = langs.get(0);
}
articleToolbarText.setText("");
article = getMyApplication().getTravelHelper().getArticleById(routeId, selectedLang);
article = getMyApplication().getTravelHelper().getArticleById(articleId, selectedLang);
if (article == null) {
return;
}
@ -366,34 +369,34 @@ public class WikivoyageArticleDialogFragment extends WikiArticleBaseDialogFragme
}
public static boolean showInstanceByTitle(@NonNull OsmandApplication app,
@NonNull FragmentManager fm,
@NonNull String title,
@NonNull String lang) {
String articleId = app.getTravelHelper().getArticleId(title, lang);
@NonNull FragmentManager fm,
@NonNull String title,
@NonNull String lang) {
TravelArticleIdentifier articleId = app.getTravelHelper().getArticleId(title, lang);
return showInstance(app, fm, articleId, lang);
}
public static boolean showInstance(@NonNull OsmandApplication app,
@NonNull FragmentManager fm,
@NonNull String routeId,
@NonNull TravelArticleIdentifier articleId,
@Nullable String selectedLang) {
ArrayList<String> langs = app.getTravelHelper().getArticleLangs(routeId);
return showInstance(fm, routeId, langs, selectedLang);
ArrayList<String> langs = app.getTravelHelper().getArticleLangs(articleId);
return showInstance(fm, articleId, langs, selectedLang);
}
public static boolean showInstance(@NonNull FragmentManager fm,
String routeId,
@NonNull TravelArticleIdentifier articleId,
@NonNull ArrayList<String> langs) {
return showInstance(fm, routeId, langs, null);
return showInstance(fm, articleId, langs, null);
}
public static boolean showInstance(@NonNull FragmentManager fm,
String routeId,
@NonNull ArrayList<String> langs,
@Nullable String selectedLang) {
private static boolean showInstance(@NonNull FragmentManager fm,
@NonNull TravelArticleIdentifier articleId,
@NonNull ArrayList<String> langs,
@Nullable String selectedLang) {
try {
Bundle args = new Bundle();
args.putString(ROUTE_ID_KEY, routeId);
args.putParcelable(ARTICLE_ID_KEY, articleId);
args.putStringArrayList(LANGS_KEY, langs);
if (langs.contains(selectedLang)) {
args.putString(SELECTED_LANG_KEY, selectedLang);
@ -416,7 +419,7 @@ public class WikivoyageArticleDialogFragment extends WikiArticleBaseDialogFragme
return;
}
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.simpleitems.TitleItem;
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.util.Algorithms;
@ -38,14 +39,14 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr
public static final String TAG = WikivoyageArticleNavigationFragment.class.getSimpleName();
public static final String ROUTE_ID_KEY = "route_id_key";
public static final String SELECTED_LANG_KEY = "selected_lang_key";
public static final String ARTICLE_ID_KEY = "article_id";
public static final String SELECTED_LANG_KEY = "selected_lang";
public static final int OPEN_ARTICLE_REQUEST_CODE = 2;
private static final long UNDEFINED = -1;
private String routeId = "";
private TravelArticleIdentifier articleId;
private String selectedLang;
private TravelArticle article;
private List<String> parentsList;
@ -61,26 +62,27 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr
if (savedInstanceState != null) {
selectedLang = savedInstanceState.getString(SELECTED_LANG_KEY);
routeId = savedInstanceState.getString(ROUTE_ID_KEY);
articleId = savedInstanceState.getParcelable(ARTICLE_ID_KEY);
} else {
Bundle args = getArguments();
if (args != null) {
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;
}
article = getMyApplication().getTravelHelper().getArticleById(routeId, selectedLang);
article = getMyApplication().getTravelHelper().getArticleById(articleId, selectedLang);
if (article == null) {
return;
}
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)));
@ -102,7 +104,7 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr
public boolean onChildClick(ExpandableListView parent, View v,
int groupPosition, int childPosition, long id) {
WikivoyageSearchResult articleItem = listAdapter.getArticleItem(groupPosition, childPosition);
sendResults(articleItem.getRouteId());
sendResults(articleItem.getArticleId());
dismiss();
return true;
}
@ -111,10 +113,10 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr
@Override
public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
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();
} else {
sendResults(articleItem.getRouteId());
sendResults(articleItem.getArticleId());
dismiss();
}
return true;
@ -134,7 +136,7 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(ROUTE_ID_KEY, routeId);
outState.putParcelable(ARTICLE_ID_KEY, articleId);
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;
}
private void sendResults(String routeId) {
WikivoyageArticleDialogFragment.showInstance(getMyApplication(), getFragmentManager(), routeId, selectedLang);
private void sendResults(TravelArticleIdentifier articleId) {
WikivoyageArticleDialogFragment.showInstance(getMyApplication(), getFragmentManager(), articleId, selectedLang);
}
public static boolean showInstance(@NonNull FragmentManager fm,
@Nullable Fragment targetFragment,
String routeId,
@NonNull TravelArticleIdentifier articleId,
@NonNull String selectedLang) {
try {
Bundle args = new Bundle();
args.putString(ROUTE_ID_KEY, routeId);
args.putParcelable(ARTICLE_ID_KEY, articleId);
args.putString(SELECTED_LANG_KEY, selectedLang);
WikivoyageArticleNavigationFragment fragment = new WikivoyageArticleNavigationFragment();
if (targetFragment != null) {
@ -204,7 +206,7 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr
@Override
public Object getChild(int groupPosition, int childPosititon) {
return getArticleItem(groupPosition, childPosititon).getArticleTitles().get(0);
return getArticleItem(groupPosition, childPosititon).getArticleTitle();
}
@Override
@ -236,8 +238,8 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr
public View getChildView(int groupPosition, final int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
WikivoyageSearchResult articleItem = getArticleItem(groupPosition, childPosition);
String childTitle = articleItem.getArticleTitles().get(0);
boolean selected = Algorithms.stringsEqual(routeId, articleItem.getRouteId()) || parentsList.contains(childTitle);
String childTitle = articleItem.getArticleTitle();
boolean selected = articleItem.getArticleId().equals(articleId) || parentsList.contains(childTitle);
if (convertView == null) {
convertView = LayoutInflater.from(context)
@ -263,7 +265,7 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr
@Override
public View getGroupView(final int groupPosition, final boolean isExpanded,
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);
if (convertView == null) {
convertView = LayoutInflater.from(context)

View file

@ -1,5 +1,7 @@
package net.osmand.plus.wikivoyage.data;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import androidx.annotation.NonNull;
@ -7,13 +9,21 @@ import androidx.annotation.Nullable;
import androidx.annotation.Size;
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.digest.DigestUtils;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Objects;
public class TravelArticle {
@ -21,19 +31,29 @@ public class TravelArticle {
private static final String THUMB_PREFIX = "320px-";
private static final String REGULAR_PREFIX = "1280px-";//1280, 1024, 800
File file;
String title;
String content;
String isPartOf;
double lat;
double lon;
double lat = Double.NaN;
double lon = Double.NaN;
String imageTitle;
GPXFile gpxFile;
String routeId;
String routeSource;
long originalId;
String lang;
String contentsJson;
String aggregatedPartOf;
@NonNull
public TravelArticleIdentifier generateIdentifier() {
return new TravelArticleIdentifier(this);
}
public File getFile() {
return file;
}
public String getTitle() {
return title;
@ -67,6 +87,10 @@ public class TravelArticle {
return routeId;
}
public String getRouteSource() {
return routeSource;
}
public long getOriginalId() {
return originalId;
}
@ -126,4 +150,92 @@ public class TravelArticle {
String md5 = new String(Hex.encodeHex(DigestUtils.md5(s)));
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.data.Amenity;
import net.osmand.data.LatLon;
import net.osmand.data.QuadRect;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.api.SQLiteAPI.SQLiteConnection;
import net.osmand.plus.api.SQLiteAPI.SQLiteCursor;
import net.osmand.plus.wikivoyage.data.TravelArticle.TravelArticleIdentifier;
import net.osmand.util.Algorithms;
import net.osmand.util.MapUtils;
@ -245,12 +247,15 @@ public class TravelDbHelper implements TravelHelper {
if (cursor != null) {
if (cursor.moveToFirst()) {
do {
WikivoyageSearchResult rs = new WikivoyageSearchResult();
rs.routeId = cursor.getLong(0) + "";
rs.articleTitles.add(cursor.getString(1));
rs.langs.add(cursor.getString(2));
rs.isPartOf.add(cursor.getString(3));
rs.imageTitle = cursor.getString(4);
String routeId = cursor.getLong(0) + "";
String articleTitle = cursor.getString(1);
String lang = cursor.getString(2);
String isPartOf = cursor.getString(3);
String imageTitle = cursor.getString(4);
List<String> langs = new ArrayList<>();
langs.add(lang);
WikivoyageSearchResult rs = new WikivoyageSearchResult(routeId, articleTitle,
isPartOf, imageTitle, langs);
res.add(rs);
} while (cursor.moveToNext());
}
@ -386,12 +391,12 @@ public class TravelDbHelper implements TravelHelper {
Collections.sort(list, new Comparator<WikivoyageSearchResult>() {
@Override
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);
boolean c2 = CollatorStringMatcher.cmatches(collator, searchQuery, o2.articleTitles.get(0),
boolean c2 = CollatorStringMatcher.cmatches(collator, searchQuery, o2.getArticleTitle(),
StringMatcherMode.CHECK_ONLY_STARTS_WITH);
if (c1 == c2) {
return collator.compare(o1.articleTitles.get(0), o2.articleTitles.get(0));
return collator.compare(o1.getArticleTitle(), o2.getArticleTitle());
} else if (c1) {
return -1;
} else if (c2) {
@ -422,28 +427,27 @@ public class TravelDbHelper implements TravelHelper {
}
}
private Collection<WikivoyageSearchResult> groupSearchResultsByRouteId(List<WikivoyageSearchResult> res) {
String baseLng = application.getLanguage();
Map<String, WikivoyageSearchResult> wikivoyage = new HashMap<>();
for (WikivoyageSearchResult rs : res) {
WikivoyageSearchResult prev = wikivoyage.get(rs.routeId);
WikivoyageSearchResult prev = wikivoyage.get(rs.getArticleRouteId());
if (prev != null) {
int insInd = prev.langs.size();
boolean matchLang = false;
if (rs.langs.get(0).equals(baseLng)) {
insInd = 0;
matchLang = true;
} else if (rs.langs.get(0).equals("en")) {
if (!prev.langs.get(0).equals(baseLng)) {
insInd = 0;
} else {
insInd = 1;
matchLang = true;
}
}
prev.articleTitles.add(insInd, rs.articleTitles.get(0));
prev.langs.add(insInd, rs.langs.get(0));
prev.isPartOf.add(insInd, rs.isPartOf.get(0));
if (matchLang) {
prev.articleId.title = rs.getArticleTitle();
prev.isPartOf = rs.getIsPartOf();
}
prev.langs.add(matchLang ? 0 : 1, rs.langs.get(0));
} else {
wikivoyage.put(rs.routeId, rs);
wikivoyage.put(rs.getArticleRouteId(), rs);
}
}
return wikivoyage.values();
@ -451,12 +455,11 @@ public class TravelDbHelper implements TravelHelper {
@NonNull
@Override
public LinkedHashMap<WikivoyageSearchResult, List<WikivoyageSearchResult>> getNavigationMap(
@NonNull final TravelArticle article) {
public Map<WikivoyageSearchResult, List<WikivoyageSearchResult>> getNavigationMap(@NonNull final TravelArticle article) {
String lang = article.getLang();
String title = article.getTitle();
if (TextUtils.isEmpty(lang) || TextUtils.isEmpty(title)) {
return new LinkedHashMap<>();
return Collections.emptyMap();
}
String[] parts = null;
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]));
if (cursor != null && cursor.moveToFirst()) {
do {
WikivoyageSearchResult rs = new WikivoyageSearchResult();
rs.routeId = cursor.getLong(0) + "";
rs.articleTitles.add(cursor.getString(1));
rs.langs.add(cursor.getString(2));
rs.isPartOf.add(cursor.getString(3));
List<WikivoyageSearchResult> l = navMap.get(rs.isPartOf.get(0));
String routeId = cursor.getLong(0) + "";
String articleTitle = cursor.getString(1);
String articleLang = cursor.getString(2);
String isPartOf = cursor.getString(3);
WikivoyageSearchResult rs = new WikivoyageSearchResult(routeId, articleTitle,
isPartOf, null, Collections.singletonList(articleLang));
List<WikivoyageSearchResult> l = navMap.get(rs.isPartOf);
if (l == null) {
l = new ArrayList<>();
navMap.put(rs.isPartOf.get(0), l);
navMap.put(rs.isPartOf, l);
}
l.add(rs);
String key = rs.getArticleTitles().get(0);
if (headers != null && headers.contains(key)) {
headerObjs.put(key, rs);
if (headers != null && headers.contains(articleTitle)) {
headerObjs.put(articleTitle, rs);
}
} while (cursor.moveToNext());
}
@ -527,12 +530,10 @@ public class TravelDbHelper implements TravelHelper {
Collections.sort(results, new Comparator<WikivoyageSearchResult>() {
@Override
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();
emptyResult.articleTitles.add(header);
emptyResult.routeId = "";
WikivoyageSearchResult emptyResult = new WikivoyageSearchResult("", header, null, null, null);
searchResult = searchResult != null ? searchResult : emptyResult;
res.put(searchResult, results);
}
@ -542,9 +543,10 @@ public class TravelDbHelper implements TravelHelper {
@Override
@Nullable
public TravelArticle getArticleById(@NonNull String routeId, @NonNull String lang) {
public TravelArticle getArticleById(@NonNull TravelArticleIdentifier articleId, @NonNull String lang) {
TravelArticle res = null;
SQLiteConnection conn = openConnection();
String routeId = articleId.routeId;
if (conn != null && !Algorithms.isEmpty(routeId)) {
SQLiteCursor cursor = conn.rawQuery(ARTICLES_TABLE_SELECT + " WHERE " + ARTICLES_COL_TRIP_ID + " = ? AND "
+ ARTICLES_COL_LANG + " = ?", new String[] { routeId, lang });
@ -558,9 +560,21 @@ public class TravelDbHelper implements TravelHelper {
return res;
}
@Override
@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;
SQLiteConnection conn = openConnection();
if (conn != null) {
@ -576,33 +590,32 @@ public class TravelDbHelper implements TravelHelper {
return res;
}
@NonNull
@Nullable
@Override
public String getArticleId(@NonNull String title, @NonNull String lang) {
String res = "";
public TravelArticleIdentifier getArticleId(@NonNull String title, @NonNull String lang) {
TravelArticle article = null;
SQLiteConnection conn = openConnection();
if (conn != null) {
SQLiteCursor cursor = conn.rawQuery("SELECT " + ARTICLES_COL_TRIP_ID + " FROM "
+ ARTICLES_TABLE_NAME + " WHERE " + ARTICLES_COL_TITLE + " = ? AND "
SQLiteCursor cursor = conn.rawQuery(ARTICLES_TABLE_SELECT + " WHERE " + ARTICLES_COL_TITLE + " = ? AND "
+ ARTICLES_COL_LANG + " = ?", new String[]{title, lang});
if (cursor != null) {
if (cursor.moveToFirst()) {
res = cursor.getLong(0) + "";
article = readArticle(cursor);
}
cursor.close();
}
}
return res;
return article != null ? article.generateIdentifier() : null;
}
@NonNull
@Override
public ArrayList<String> getArticleLangs(@NonNull String routeId) {
public ArrayList<String> getArticleLangs(@NonNull TravelArticleIdentifier articleId) {
ArrayList<String> res = new ArrayList<>();
SQLiteConnection conn = openConnection();
if (conn != null) {
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.moveToFirst()) {
String baseLang = application.getLanguage();

View file

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

View file

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

View file

@ -1,5 +1,9 @@
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 java.util.ArrayList;
@ -9,25 +13,52 @@ public class WikivoyageSearchResult {
private static final int SHOW_LANGS = 3;
String routeId;
List<String> articleTitles = new ArrayList<>();
List<String> langs = new ArrayList<>();
List<String> isPartOf = new ArrayList<>();
String imageTitle;
TravelArticleIdentifier articleId;
public String getRouteId() {
return routeId;
String imageTitle;
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() {
return articleTitles;
public WikivoyageSearchResult(String routeId, String articleTitle, String isPartOf, String imageTitle, @Nullable List<String> langs) {
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() {
return langs;
}
public List<String> getIsPartOf() {
public String getIsPartOf() {
return isPartOf;
}

View file

@ -50,7 +50,7 @@ public class SavedArticlesTabFragment extends BaseOsmAndFragment implements Trav
public void openArticle(TravelArticle article) {
FragmentManager fm = getFragmentManager();
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.wikivoyage.article.WikivoyageArticleDialogFragment;
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.TravelLocalDataHelper;
import net.osmand.plus.wikivoyage.search.WikivoyageSearchDialogFragment;
@ -50,8 +51,8 @@ public class WikivoyageExploreActivity extends TabActivity implements DownloadEv
TravelLocalDataHelper.Listener {
private static final String TAB_SELECTED = "tab_selected";
private static final String ROUTE_ID_KEY = "route_id_key";
private static final String SELECTED_LANG_KEY = "selected_lang_key";
private static final String ARTICLE_ID_KEY = "article_id";
private static final String SELECTED_LANG_KEY = "selected_lang";
private static final int EXPLORE_POSITION = 0;
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);
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);
if (!Algorithms.isEmpty(articleId)) {
if (articleId != null) {
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 selectedLang = data.getQueryParameter("lang");
if (!Algorithms.isEmpty(title) && !Algorithms.isEmpty(selectedLang)) {
String articleId = app.getTravelHelper().getArticleId(title, selectedLang);
if (!articleId.isEmpty()) {
TravelArticleIdentifier articleId = app.getTravelHelper().getArticleId(title, selectedLang);
if (articleId != null) {
WikivoyageArticleDialogFragment.showInstance(app, getSupportFragmentManager(), articleId, selectedLang);
}
}
@ -279,7 +280,7 @@ public class WikivoyageExploreActivity extends TabActivity implements DownloadEv
private void applyIntentParameters(Intent intent, TravelArticle article) {
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());
}

View file

@ -76,7 +76,8 @@ public class ArticleTravelCard extends BaseTravelCard {
@Override
public void onClick(View v) {
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.Metadata;
import net.osmand.GPXUtilities.WptPt;
import net.osmand.data.LatLon;
import net.osmand.data.PointDescription;
import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
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.wikivoyage.article.WikivoyageArticleDialogFragment;
import net.osmand.plus.wikivoyage.data.TravelArticle;
import net.osmand.plus.wikivoyage.data.TravelArticle.TravelArticleIdentifier;
public class WikivoyageWptPtMenuController extends WptPtMenuController {
private WikivoyageWptPtMenuController(@NonNull MapActivity mapActivity, @NonNull PointDescription pointDescription, @NonNull WptPt wpt, @NonNull TravelArticle article) {
super(new WikivoyageWptPtMenuBuilder(mapActivity, wpt), mapActivity, pointDescription, wpt);
final String tripId = article.getRouteId();
final TravelArticleIdentifier articleId = article.generateIdentifier();
final String lang = article.getLang();
leftTitleButtonController = new TitleButtonController() {
@Override
@ -27,7 +29,7 @@ public class WikivoyageWptPtMenuController extends WptPtMenuController {
MapActivity mapActivity = getMapActivity();
if (mapActivity != null) {
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 lang = metadata != null ? metadata.getArticleLang() : null;
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;
}

View file

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

View file

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