Merge branch 'master' of ssh://github.com/osmandapp/Osmand into WikivoyageExploreCards

This commit is contained in:
Chumva 2018-04-24 12:47:35 +03:00
commit 688b1df38c
11 changed files with 405 additions and 28 deletions

View file

@ -174,7 +174,7 @@ pre {
@font-face {
font-family: RobotoSlab-Regular;
src: url("file:///assets/fonts/RobotoSlab-Regular.ttf")
src: url("fonts/RobotoSlab-Regular.ttf")
}
td {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

View file

@ -30,7 +30,7 @@
android:ellipsize="end"
android:maxLines="1"
android:textColor="?attr/wikivoyage_primary_text_color"
android:textSize="@dimen/travel_card_title_text_size"
android:textSize="@dimen/travel_card_primary_text_size"
osmand:typeface="@string/font_roboto_medium"
tools:text="Download file"/>
@ -48,7 +48,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/wikivoyage_secondary_text"
android:textSize="@dimen/travel_card_title_text_size"
android:textSize="@dimen/travel_card_primary_text_size"
osmand:typeface="@string/font_roboto_regular"
tools:text="Download this Wikivoyage travel guides file to view articles about places around the world without an internet connection."/>
@ -81,7 +81,7 @@
android:layout_weight="1"
android:orientation="vertical">
<TextView
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/file_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -89,9 +89,10 @@
android:maxLines="1"
android:textColor="?attr/wikivoyage_primary_text_color"
android:textSize="@dimen/default_list_text_size"
osmand:typeface="@string/font_roboto_regular"
tools:text="Wikivoyage"/>
<TextView
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/file_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -99,6 +100,7 @@
android:maxLines="1"
android:textColor="@color/wikivoyage_secondary_text"
android:textSize="@dimen/default_sub_text_size"
osmand:typeface="@string/font_roboto_regular"
tools:text="255 Mb • Update 11 April"/>
</LinearLayout>
@ -119,17 +121,19 @@
android:background="@drawable/wikivoyage_secondary_btn_bg">
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/left_button"
android:id="@+id/secondary_button"
android:layout_width="match_parent"
android:layout_height="@dimen/wikivoyage_card_button_height"
android:layout_gravity="center"
android:background="?attr/selectableItemBackgroundBorderless"
android:ellipsize="end"
android:gravity="center"
android:letterSpacing="@dimen/text_button_letter_spacing"
android:maxLines="1"
android:textColor="?attr/wikivoyage_active_color"
android:textSize="@dimen/text_button_text_size"
osmand:typeface="@string/font_roboto_medium"
tools:ignore="UnusedAttribute"
tools:text="Later"/>
</FrameLayout>
@ -145,17 +149,19 @@
android:background="@drawable/wikivoyage_primary_btn_bg">
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/right_button"
android:id="@+id/primary_button"
android:layout_width="match_parent"
android:layout_height="@dimen/wikivoyage_card_button_height"
android:layout_gravity="center"
android:background="?attr/selectableItemBackgroundBorderless"
android:ellipsize="end"
android:gravity="center"
android:letterSpacing="@dimen/text_button_letter_spacing"
android:maxLines="1"
android:textColor="?attr/wikivoyage_primary_btn_text_color"
android:textSize="@dimen/text_button_text_size"
osmand:typeface="@string/font_roboto_medium"
tools:ignore="UnusedAttribute"
tools:text="Update"/>
</FrameLayout>

View file

@ -101,7 +101,7 @@
<dimen name="default_sub_text_size_small">12sp</dimen>
<dimen name="welcome_header_text_size">27sp</dimen>
<dimen name="text_button_text_size">22sp</dimen>
<dimen name="travel_card_title_text_size">22sp</dimen>
<dimen name="travel_card_primary_text_size">22sp</dimen>
<dimen name="dialog_header_text_size">24sp</dimen>

View file

@ -175,7 +175,7 @@
<dimen name="default_sub_text_size_small">10sp</dimen>
<dimen name="welcome_header_text_size">23sp</dimen>
<dimen name="text_button_text_size">15sp</dimen>
<dimen name="travel_card_title_text_size">15sp</dimen>
<dimen name="travel_card_primary_text_size">15sp</dimen>
<dimen name="default_split_segments_overview">13sp</dimen>
<dimen name="default_split_segments_data">13sp</dimen>

View file

@ -2860,4 +2860,12 @@
<string name="toast_empty_name_error">Location has no name</string>
<string name="tunnel_warning">Tunnel ahead</string>
<string name="show_tunnels">Tunnels</string>
<string name="download_wikipedia_description">Download the Wikipedia articles for %1$s to read them offline.</string>
<string name="download_wikipedia_label">Download Wikipedia data</string>
<string name="open_in_browser_wiki">Open article online</string>
<string name="open_in_browser_wiki_description">View this article in a browser.</string>
<string name="download_wiki_region_placeholder">this region</string>
<string name="wiki_article_search_text">Searching for the necessary wiki article</string>
<string name="wiki_article_not_found">Article not found</string>
<string name="how_to_open_wiki_title">How to open Wikipedia articles?</string>
</resources>

View file

@ -850,7 +850,20 @@ public class ResourceManager {
}
return map;
}
public List<AmenityIndexRepositoryBinary> getWikiAmenityRepository(double lat, double lon) {
List<AmenityIndexRepositoryBinary> repos = new ArrayList<>();
for (String filename : amenityRepositories.keySet()) {
if (filename.contains("_wiki")
|| filename.contains(IndexConstants.BINARY_WIKI_MAP_INDEX_EXT)) {
AmenityIndexRepository repository = amenityRepositories.get(filename);
if (repository.checkContains(lat, lon) && repository instanceof AmenityIndexRepositoryBinary) {
repos.add((AmenityIndexRepositoryBinary) repository);
}
}
}
return repos;
}
////////////////////////////////////////////// Working with address ///////////////////////////////////////////

View file

@ -1,10 +1,14 @@
package net.osmand.plus.wikivoyage;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AlertDialog;
@ -12,29 +16,42 @@ import android.util.Log;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import net.osmand.binary.BinaryMapDataObject;
import net.osmand.data.Amenity;
import net.osmand.data.PointDescription;
import net.osmand.map.OsmandRegions;
import net.osmand.plus.GPXUtilities;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.OsmandSettings;
import net.osmand.plus.R;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.mapcontextmenu.WikipediaDialogFragment;
import net.osmand.plus.resources.AmenityIndexRepositoryBinary;
import net.osmand.plus.wikivoyage.article.WikivoyageArticleDialogFragment;
import net.osmand.plus.wikivoyage.article.WikivoyageArticleWikiLinkFragment;
import net.osmand.plus.wikivoyage.data.TravelArticle;
import net.osmand.plus.wikivoyage.explore.WikivoyageExploreDialogFragment;
import net.osmand.util.MapUtils;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.ref.WeakReference;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import static android.support.v4.app.FragmentManager.POP_BACK_STACK_INCLUSIVE;
interface RegionCallback {
void onRegionFound(String s);
}
/**
* Custom WebView client to handle the internal links.
*/
public class WikivoyageWebViewClient extends WebViewClient {
public class WikivoyageWebViewClient extends WebViewClient implements RegionCallback {
private static final String TAG = WikivoyageWebViewClient.class.getSimpleName();
@ -43,11 +60,16 @@ public class WikivoyageWebViewClient extends WebViewClient {
private Context context;
private TravelArticle article;
private boolean nightMode;
private String regionName;
private static final String PREFIX_GEO = "geo:";
private static final String PAGE_PREFIX_HTTP = "http://";
private static final String PAGE_PREFIX_HTTPS = "https://";
private static final String WEB_DOMAIN = ".wikivoyage.com/wiki/";
private static final String WIKIVOAYAGE_DOMAIN = ".wikivoyage.com/wiki/";
private static final String WIKI_DOMAIN = ".wikipedia.org/wiki/";
private FetchWikiRegion fetchRegionTask;
private WikiArticleSearchTask articleSearchTask;
public WikivoyageWebViewClient(FragmentActivity context, FragmentManager fm, boolean nightMode) {
app = (OsmandApplication) context.getApplication();
@ -58,24 +80,21 @@ public class WikivoyageWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.contains(WEB_DOMAIN)) {
String lang = url.substring(url.startsWith(PAGE_PREFIX_HTTPS) ? PAGE_PREFIX_HTTPS.length() : 0, url.indexOf("."));
String articleName = url.replace(PAGE_PREFIX_HTTPS + lang + WEB_DOMAIN, "")
.replaceAll("_", " ");
try {
articleName = URLDecoder.decode(articleName, "UTF-8");
} catch (UnsupportedEncodingException e) {
Log.w(TAG, e.getMessage(), e);
}
if (url.contains(WIKIVOAYAGE_DOMAIN)) {
String lang = getLang(url);
String articleName = getArticleNameFromUrl(url, lang);
long articleId = app.getTravelDbHelper().getArticleId(articleName, lang);
if (articleId != 0) {
WikivoyageArticleDialogFragment.showInstance(app, fragmentManager, articleId, lang);
} else {
warnAboutExternalLoad(url);
warnAboutExternalLoad(url, context, nightMode);
}
return true;
} else if (url.contains(WIKI_DOMAIN)) {
String articleName = getArticleNameFromUrl(url, getLang(url));
getWikiArticle(articleName, url);
} else if (url.startsWith(PAGE_PREFIX_HTTP) || url.startsWith(PAGE_PREFIX_HTTPS)) {
warnAboutExternalLoad(url);
warnAboutExternalLoad(url, context, nightMode);
} else if (url.startsWith(PREFIX_GEO)) {
if (article != null) {
List<GPXUtilities.WptPt> points = article.getGpxFile().getPoints();
@ -120,7 +139,27 @@ public class WikivoyageWebViewClient extends WebViewClient {
return true;
}
private void warnAboutExternalLoad(final String url) {
@NonNull
private String getLang(String url) {
return url.substring(url.startsWith(PAGE_PREFIX_HTTPS) ? PAGE_PREFIX_HTTPS.length() : 0, url.indexOf("."));
}
private String getArticleNameFromUrl(String url, String lang) {
String domain = url.contains(WIKIVOAYAGE_DOMAIN) ? WIKIVOAYAGE_DOMAIN : WIKI_DOMAIN;
String articleName = url.replace(PAGE_PREFIX_HTTPS + lang + domain, "")
.replaceAll("_", " ");
try {
articleName = URLDecoder.decode(articleName, "UTF-8");
} catch (UnsupportedEncodingException e) {
Log.w(TAG, e.getMessage(), e);
}
return articleName;
}
private static void warnAboutExternalLoad(final String url, final Context context, final boolean nightMode) {
if (context == null) {
return;
}
new AlertDialog.Builder(context)
.setTitle(url)
.setMessage(R.string.online_webpage_warning)
@ -136,5 +175,160 @@ public class WikivoyageWebViewClient extends WebViewClient {
public void setArticle(TravelArticle article) {
this.article = article;
if (this.article != null && app != null) {
fetchRegionTask = new FetchWikiRegion(this, app.getRegions(), article.getLat(), article.getLon());
fetchRegionTask.execute();
}
}
private void getWikiArticle(String name, String url) {
List<AmenityIndexRepositoryBinary> indexes = app.getResourceManager()
.getWikiAmenityRepository(article.getLat(), article.getLon());
if (indexes.isEmpty()) {
WikivoyageArticleWikiLinkFragment.showInstance(fragmentManager, regionName == null ?
"" : regionName, url);
} else {
articleSearchTask = new WikiArticleSearchTask(name, indexes, (MapActivity) context, nightMode, url);
articleSearchTask.execute();
}
}
@Override
public void onRegionFound(String s) {
regionName = s;
}
public void stopRunningAsyncTasks() {
if (articleSearchTask != null && articleSearchTask.getStatus() == AsyncTask.Status.RUNNING) {
articleSearchTask.cancel(false);
}
if (fetchRegionTask != null && fetchRegionTask.getStatus() == AsyncTask.Status.RUNNING) {
fetchRegionTask.cancel(false);
}
}
private static class FetchWikiRegion extends AsyncTask<Void, Void, String> {
private RegionCallback callback;
private OsmandRegions osmandRegions;
private double lat;
private double lon;
FetchWikiRegion(RegionCallback callback, OsmandRegions osmandRegions, double lat, double lon) {
this.callback = callback;
this.osmandRegions = osmandRegions;
this.lat = lat;
this.lon = lon;
}
@Override
protected String doInBackground(Void... voids) {
if (osmandRegions != null) {
int x31 = MapUtils.get31TileNumberX(lon);
int y31 = MapUtils.get31TileNumberY(lat);
List<BinaryMapDataObject> dataObjects = null;
try {
if (isCancelled()) {
return null;
}
dataObjects = osmandRegions.query(x31, y31);
} catch (IOException e) {
e.printStackTrace();
}
if (dataObjects != null) {
for (BinaryMapDataObject b : dataObjects) {
if (isCancelled()) {
break;
}
if(osmandRegions.contain(b, x31, y31)) {
return osmandRegions.getLocaleName(osmandRegions.getDownloadName(b), false);
}
}
}
}
return "";
}
@Override
protected void onCancelled(){
callback = null;
}
@Override
protected void onPostExecute(String result) {
if (callback != null) {
callback.onRegionFound(result);
}
}
}
private static class WikiArticleSearchTask extends AsyncTask<Void, Void, List<Amenity>> {
private ProgressDialog dialog;
private String name;
private List<AmenityIndexRepositoryBinary> indexes;
private WeakReference<MapActivity> weakContext;
private boolean isNightMode;
private String url;
WikiArticleSearchTask(String articleName, List<AmenityIndexRepositoryBinary> indexes,
MapActivity context, boolean isNightMode, String url) {
name = articleName;
this.indexes = indexes;
weakContext = new WeakReference<>(context);
dialog = createProgressDialog();
this.isNightMode = isNightMode;
this.url = url;
}
@Override
protected void onPreExecute() {
if (dialog != null) {
dialog.show();
}
}
@Override
protected List<Amenity> doInBackground(Void... voids) {
List<Amenity> found = new ArrayList<>();
for (AmenityIndexRepositoryBinary repo : indexes) {
if (isCancelled()) {
break;
}
found.addAll(repo.searchAmenitiesByName(0, 0, 0, 0,
Integer.MAX_VALUE, Integer.MAX_VALUE, name, null));
}
return found;
}
@Override
protected void onCancelled(){
dialog = null;
indexes.clear();
}
@Override
protected void onPostExecute(List<Amenity> found) {
MapActivity activity = weakContext.get();
if (activity != null && !activity.isActivityDestroyed() && dialog != null) {
dialog.dismiss();
if (!found.isEmpty()) {
WikipediaDialogFragment.showInstance(activity, found.get(0));
} else {
warnAboutExternalLoad(url, weakContext.get(), isNightMode);
}
}
}
private ProgressDialog createProgressDialog() {
MapActivity activity = weakContext.get();
if (activity != null && !activity.isActivityDestroyed()) {
ProgressDialog dialog = new ProgressDialog(activity);
dialog.setCancelable(false);
dialog.setMessage(activity.getString(R.string.wiki_article_search_text));
return dialog;
}
return null;
}
}
}

View file

@ -247,6 +247,14 @@ public class WikivoyageArticleDialogFragment extends WikivoyageBaseDialogFragmen
}
}
@Override
public void onPause() {
super.onPause();
if (webViewClient != null) {
webViewClient.stopRunningAsyncTasks();
}
}
@Override
public void onResume() {
super.onResume();

View file

@ -0,0 +1,135 @@
package net.osmand.plus.wikivoyage.article;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.FragmentManager;
import android.view.View;
import net.osmand.plus.R;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.base.MenuBottomSheetDialogFragment;
import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem;
import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithDescription;
import net.osmand.plus.base.bottomsheetmenu.simpleitems.DividerHalfItem;
import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleDividerItem;
import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem;
import net.osmand.plus.mapcontextmenu.WikipediaDialogFragment;
public class WikivoyageArticleWikiLinkFragment extends MenuBottomSheetDialogFragment {
public static final String TAG = WikivoyageArticleWikiLinkFragment.class.getSimpleName();
public static final String ARTICLE_URL_KEY = "article_url";
private static final String WIKI_REGION = "region";
private String articleUrl;
private String wikiRegion;
@Override
public void createMenuItems(Bundle savedInstanceState) {
Context ctx = getContext();
if (ctx == null) {
return;
}
if (savedInstanceState != null) {
articleUrl = savedInstanceState.getString(ARTICLE_URL_KEY);
wikiRegion = savedInstanceState.getString(WIKI_REGION);
} else {
Bundle args = getArguments();
if (args != null) {
articleUrl = args.getString(ARTICLE_URL_KEY);
wikiRegion = args.getString(WIKI_REGION);
}
}
items.add(new TitleItem(getString(R.string.how_to_open_wiki_title)));
BaseBottomSheetItem wikiLinkitem = new TitleItem.Builder().setTitle(articleUrl)
.setTitleColorId(nightMode
? R.color.wikivoyage_contents_parent_icon_dark : R.color.wikivoyage_contents_parent_icon_light)
.setLayoutId(R.layout.bottom_sheet_item_title)
.create();
items.add(wikiLinkitem);
items.add(new TitleDividerItem(getContext()));
Drawable downloadIcon = getIcon(R.drawable.ic_action_import, nightMode
? R.color.wikivoyage_contents_parent_icon_dark : R.color.wikivoyage_contents_parent_icon_light);
Drawable viewOnlineIcon = getIcon(R.drawable.ic_world_globe_dark, nightMode
? R.color.wikivoyage_contents_parent_icon_dark : R.color.wikivoyage_contents_parent_icon_light);
BaseBottomSheetItem wikiDownloadItem = new BottomSheetItemWithDescription.Builder()
.setDescription(getString(R.string.download_wikipedia_description, wikiRegion.isEmpty() ?
getString(R.string.download_wiki_region_placeholder) : wikiRegion))
.setIcon(downloadIcon)
.setTitle(getString(R.string.download_wikipedia_label))
.setLayoutId(R.layout.bottom_sheet_item_with_descr_56dp)
.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MapActivity mapActivity = (MapActivity) getActivity();
Intent newIntent = new Intent(mapActivity, mapActivity.getMyApplication().getAppCustomization()
.getDownloadActivity());
newIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
mapActivity.startActivity(newIntent);
dismiss();
}
})
.create();
items.add(wikiDownloadItem);
items.add(new DividerHalfItem(getContext()));
BaseBottomSheetItem wikiArticleOnlineItem = new BottomSheetItemWithDescription.Builder()
.setDescription(getString(R.string.open_in_browser_wiki_description))
.setIcon(viewOnlineIcon)
.setTitle(getString(R.string.open_in_browser_wiki))
.setLayoutId(R.layout.bottom_sheet_item_with_descr_56dp)
.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
WikipediaDialogFragment.showFullArticle(getContext(), Uri.parse(articleUrl), nightMode);
dismiss();
}
})
.create();
items.add(wikiArticleOnlineItem);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(ARTICLE_URL_KEY, articleUrl);
outState.putString(WIKI_REGION, wikiRegion);
}
@Override
protected boolean useScrollableItemsContainer() {
return false;
}
@Override
protected int getBgColorId() {
return nightMode ? R.color.wikivoyage_bottom_bar_bg_dark : R.color.bg_color_light;
}
public static boolean showInstance(@NonNull FragmentManager fm,
@NonNull String region,
@NonNull String articleUrl) {
try {
Bundle args = new Bundle();
args.putString(ARTICLE_URL_KEY, articleUrl);
args.putString(WIKI_REGION, region);
WikivoyageArticleWikiLinkFragment fragment = new WikivoyageArticleWikiLinkFragment();
fragment.setArguments(args);
fragment.show(fm, TAG);
return true;
} catch (RuntimeException e) {
return false;
}
}
}

View file

@ -91,14 +91,27 @@ public class TravelArticle {
return null;
}
int firstParagraphStart = content.indexOf("<p>");
int firstParagraphEnd = content.indexOf("</p>");
String pOpened = "<p>";
String pClosed = "</p>";
int firstParagraphStart = content.indexOf(pOpened);
int firstParagraphEnd = content.indexOf(pClosed);
if (firstParagraphStart == -1 || firstParagraphEnd == -1) {
return null;
}
int pClosedLength = pClosed.length();
String firstParagraphHtml = content.substring(firstParagraphStart, firstParagraphEnd + pClosedLength);
while (firstParagraphHtml.length() == (pOpened.length() + pClosedLength)
&& (firstParagraphEnd + pClosedLength) < content.length()) {
firstParagraphStart = content.indexOf(pOpened, firstParagraphEnd);
firstParagraphEnd = firstParagraphStart == -1 ? -1 : content.indexOf(pClosed, firstParagraphStart);
if (firstParagraphStart != -1 && firstParagraphEnd != -1) {
firstParagraphHtml = content.substring(firstParagraphStart, firstParagraphEnd + pClosedLength);
} else {
break;
}
}
// 4 is the length of </p> tag
String firstParagraphHtml = content.substring(firstParagraphStart, firstParagraphEnd + 4);
String firstParagraphText = Html.fromHtml(firstParagraphHtml).toString().trim();
String[] phrases = firstParagraphText.split("\\. ");