Merge branch 'r3.9' into upload-photo-progress-bar

This commit is contained in:
Vitaliy 2021-02-01 04:03:47 +02:00
commit b5ff545647
50 changed files with 1487 additions and 684 deletions

157
GPX.md
View file

@ -1,157 +0,0 @@
The OsmAnd's GPX file format conforms to the GPX 1.1 specification with additional data written as extensions. There are several sections of such data:
## Track appearance
The following parameters are used to customize the appearance of a track on the map. They are used inside the "gpx" tag and apply to all tracks contained in the gpx.
#### Parameters
* **show_arrows** [*true, false*] - show / hide arrows along the path line.
* **width** [*thin, medium, bold, 1-24*] - width of the track line on the map. The thin, medium, and bold are style depended values (should be defined as currentTrackWidth attribute).
* **color** [*#AARRGGBB, #RRGGBB*] - color of a track line on the map. Hex value.
* **split_type** [*no_split, distance, time*] - split type for a track.
* **split_interval** [*double*] - split interval for a track. Distance (meters), time (seconds).
#### Example:
```xml
<gpx version="1.1" creator="OsmAndRouterV2" xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
...
<extensions>
<show_arrows>true</show_arrows>
<color>#4e4eff</color>
<split_type>distance</split_type>
<split_interval>2000.0</split_interval>
<width>bold</width>
</extensions>
</gpx>
```
## Details of a track point (trkpt)
Written to a gpx file while recording a track.
* **speed** (meters per second)
* **heading** (0-359 degrees)
#### Example:
```xml
<trkpt lat="52.397799" lon="4.575998">
<ele>203</ele>
<time>2019-05-08T10:36:43Z</time>
<hdop>3</hdop>
<extensions>
<heading>273</heading>
<speed>5.02</speed>
</extensions>
</trkpt>
```
## Calculated route(s)
This data contains all details of a route built with **OsmAnd** (route segments, turns, road names, road types, restrictions, etc.). The route can be completely restored as if just built, even in the absence of the respective offline maps.
A gpx file may contain several routes. Each of them is contained in a specific segment under **trkseg** / **extensions**. A gpx file is saved in this form when exporting a constructed route or when saving a track that consists of several separate segments via the **Plan a route** functionality.
**Plan a route** also adds one (or several, in accordance with the number of contained separate segments / tracks) **rte** blocks to the gpx file, containing route key points (**rtept**).
#### Gpx structure:
```xml
<trk>
<trkseg>
<!-- List of segment points. The order of the points corresponds to the order and length of the route segments (<route><segment length="x" ... />). -->
<!-- The value of the "length" attribute corresponds to the number of points in this segment of the route. -->
<trkpt ... ></trkpt>
<extensions>
<!-- List of route segments -->
<route>
<segment ... />
</route>
<!-- Properties of segments included in the route. -->
<!-- This data is taken from offline maps during the initial construction of a route. -->
<types>
<type ... />
</types>
</extensions>
</trkseg>
</trk>
<!-- List of intermediate route points. If there are multiple routes, the order of the rte list matches the order of the route segments. -->
<rte>
<rtept ... />
<!-- For routes built with the "Plan route", the parameters of key points are saved. -->
<extensions>
<!-- Route profile type for next segment (car, bicycle, pedestrian, etc.). -->
<profile>...</profile>
<!-- The index of the point in the gpx segment that corresponds to the first point of the calculated route for this segment. -->
<trkpt_idx>...</trkpt_idx>
</extensions>
</rtept>
</rte>
```
#### Example:
```xml
<gpx version="1.1" creator="OsmAndRouterV2" xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
<metadata>
<name>Fri 06 Nov 2020</name>
</metadata>
<trk>
<name>Fri 06 Nov 2020</name>
<trkseg>
<trkpt lat="52.3639849" lon="4.8900533">
<ele>0.801</ele>
</trkpt>
<trkpt lat="52.3636917" lon="4.8922849">
<ele>0.998</ele>
</trkpt>
<trkpt lat="52.3636885" lon="4.892309">
<ele>1</ele>
</trkpt>
<trkpt lat="52.3636426" lon="4.8922902">
<ele>0.963</ele>
</trkpt>
<trkpt lat="52.363564" lon="4.8922607">
<ele>0.899</ele>
</trkpt>
....
<extensions>
<route>
<segment id="7372058" length="3" segmentTime="178.44" speed="1.11" turnType="C" types="0,1,2,3,4,5,6" names="57" />
<segment id="334164679" length="5" segmentTime="86.11" speed="1.11" turnType="TR" turnAngle="91.88" types="7,8,0,9,10,11,12,13,6" pointTypes=";;14,15;16,17,18;" names="58" />
<segment id="334603581" length="6" segmentTime="75.5" speed="1.11" types="19,20,21,7,8,0,22,9,10,11,12,13,23,6" pointTypes=";14;16,24;16,24;14;" names="58" />
<segment id="446707354" length="3" segmentTime="8.32" speed="1.11" turnType="TSLL" turnAngle="-25.44" types="19,25,21,7,8,22,9,1,11,12,13,6" names="58" />
...
</route>
<types>
<type t="lit" v="yes" />
<type t="oneway" v="yes" />
<type t="highway" v="unclassified" />
<type t="surface" v="paving_stones" />
<type t="maxspeed" v="30" />
...
</types>
</extensions>
</trkseg>
</trk>
<rte>
<rtept lat="52.3639945" lon="4.8900532">
<extensions>
<profile>pedestrian</profile>
<trkpt_idx>0</trkpt_idx>
</extensions>
</rtept>
<rtept lat="52.3612797" lon="4.8911677">
<extensions>
<profile>pedestrian</profile>
<trkpt_idx>24</trkpt_idx>
</extensions>
</rtept>
<rtept lat="52.356996" lon="4.8912071">
<extensions>
<profile>pedestrian</profile>
<trkpt_idx>89</trkpt_idx>
</extensions>
</rtept>
<rtept lat="52.3542374" lon="4.8947024">
<extensions>
<profile>pedestrian</profile>
<trkpt_idx>121</trkpt_idx>
</extensions>
</rtept>
</rte>
</gpx>
```

View file

@ -8,6 +8,7 @@ import net.osmand.aidlapi.AidlParams;
import net.osmand.aidlapi.profile.AExportSettingsType; import net.osmand.aidlapi.profile.AExportSettingsType;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import static net.osmand.aidlapi.profile.ExportProfileParams.SETTINGS_TYPE_KEY; import static net.osmand.aidlapi.profile.ExportProfileParams.SETTINGS_TYPE_KEY;
@ -15,16 +16,18 @@ public class ProfileSettingsParams extends AidlParams {
public static final String VERSION_KEY = "version"; public static final String VERSION_KEY = "version";
public static final String REPLACE_KEY = "replace"; public static final String REPLACE_KEY = "replace";
public static final String SILENT_IMPORT_KEY = "silentImport";
public static final String LATEST_CHANGES_KEY = "latestChanges"; public static final String LATEST_CHANGES_KEY = "latestChanges";
public static final String PROFILE_SETTINGS_URI_KEY = "profileSettingsUri"; public static final String PROFILE_SETTINGS_URI_KEY = "profileSettingsUri";
private Uri profileSettingsUri; private Uri profileSettingsUri;
private String latestChanges; private String latestChanges;
private int version; private int version;
private ArrayList<String> settingsTypeKeyList = new ArrayList<>(); private List<String> settingsTypeKeyList = new ArrayList<>();
boolean replace; private boolean silent;
private boolean replace;
public ProfileSettingsParams(Uri profileSettingsUri, ArrayList<AExportSettingsType> settingsTypeList, boolean replace, public ProfileSettingsParams(Uri profileSettingsUri, List<AExportSettingsType> settingsTypeList, boolean replace,
String latestChanges, int version) { boolean silent, String latestChanges, int version) {
this.profileSettingsUri = profileSettingsUri; this.profileSettingsUri = profileSettingsUri;
for (AExportSettingsType settingsType : settingsTypeList) { for (AExportSettingsType settingsType : settingsTypeList) {
settingsTypeKeyList.add(settingsType.name()); settingsTypeKeyList.add(settingsType.name());
@ -32,6 +35,7 @@ public class ProfileSettingsParams extends AidlParams {
this.replace = replace; this.replace = replace;
this.latestChanges = latestChanges; this.latestChanges = latestChanges;
this.version = version; this.version = version;
this.silent = silent;
} }
public ProfileSettingsParams(Parcel in) { public ProfileSettingsParams(Parcel in) {
@ -62,7 +66,7 @@ public class ProfileSettingsParams extends AidlParams {
return profileSettingsUri; return profileSettingsUri;
} }
public ArrayList<String> getSettingsTypeKeys() { public List<String> getSettingsTypeKeys() {
return settingsTypeKeyList; return settingsTypeKeyList;
} }
@ -70,13 +74,18 @@ public class ProfileSettingsParams extends AidlParams {
return replace; return replace;
} }
public boolean isSilent() {
return silent;
}
@Override @Override
public void writeToBundle(Bundle bundle) { public void writeToBundle(Bundle bundle) {
bundle.putInt(VERSION_KEY, version); bundle.putInt(VERSION_KEY, version);
bundle.putString(LATEST_CHANGES_KEY, latestChanges); bundle.putString(LATEST_CHANGES_KEY, latestChanges);
bundle.putParcelable(PROFILE_SETTINGS_URI_KEY, profileSettingsUri); bundle.putParcelable(PROFILE_SETTINGS_URI_KEY, profileSettingsUri);
bundle.putStringArrayList(SETTINGS_TYPE_KEY, settingsTypeKeyList); bundle.putStringArrayList(SETTINGS_TYPE_KEY, new ArrayList<>(settingsTypeKeyList));
bundle.putBoolean(REPLACE_KEY, replace); bundle.putBoolean(REPLACE_KEY, replace);
bundle.putBoolean(SILENT_IMPORT_KEY, silent);
} }
@Override @Override
@ -86,5 +95,6 @@ public class ProfileSettingsParams extends AidlParams {
profileSettingsUri = bundle.getParcelable(PROFILE_SETTINGS_URI_KEY); profileSettingsUri = bundle.getParcelable(PROFILE_SETTINGS_URI_KEY);
settingsTypeKeyList = bundle.getStringArrayList(SETTINGS_TYPE_KEY); settingsTypeKeyList = bundle.getStringArrayList(SETTINGS_TYPE_KEY);
replace = bundle.getBoolean(REPLACE_KEY); replace = bundle.getBoolean(REPLACE_KEY);
silent = bundle.getBoolean(SILENT_IMPORT_KEY);
} }
} }

View file

@ -274,6 +274,10 @@ public class Amenity extends MapObject {
return null; return null;
} }
public String getTagContent(String tag) {
return getTagContent(tag, null);
}
public String getTagContent(String tag, String lang) { public String getTagContent(String tag, String lang) {
if (lang != null) { if (lang != null) {
String translateName = getAdditionalInfo(tag + ":" + lang); String translateName = getAdditionalInfo(tag + ":" + lang);

View file

@ -40,7 +40,7 @@
<string name="items_modified">items modified</string> <string name="items_modified">items modified</string>
<string name="osmand_unlimited">OsmAnd Unlimited</string> <string name="osmand_unlimited">OsmAnd Unlimited</string>
<string name="markers">Markers</string> <string name="markers">Markers</string>
<string name="opr_base_url">https://test.openplacereviews.org/</string> <string name="opr_base_url">https://openplacereviews.org/</string>
<string name="dev_opr_base_url">https://test.openplacereviews.org/</string> <string name="dev_opr_base_url">https://test.openplacereviews.org/</string>
<string name="osm_oauth_developer_key">v8G8r9NLJZGMV4he5lwbQlz620FNVARKjI9Bm5UJ</string> <string name="osm_oauth_developer_key">v8G8r9NLJZGMV4he5lwbQlz620FNVARKjI9Bm5UJ</string>
<string name="osm_oauth_developer_secret">jDvM95Ne1Bq2BDTmIfB6b3ZMxvdK87WGfp6DC07J</string> <string name="osm_oauth_developer_secret">jDvM95Ne1Bq2BDTmIfB6b3ZMxvdK87WGfp6DC07J</string>

View file

@ -10,43 +10,59 @@
android:orientation="horizontal"> android:orientation="horizontal">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginEnd="@dimen/content_padding" android:layout_marginEnd="@dimen/content_padding"
android:layout_marginRight="@dimen/content_padding" android:layout_marginRight="@dimen/content_padding"
android:gravity="center_vertical"
android:maxWidth="@dimen/grid_menu_item_width"
android:orientation="vertical"> android:orientation="vertical">
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"> android:gravity="center_vertical"
android:orientation="horizontal"
android:weightSum="2">
<net.osmand.plus.widgets.TextViewEx <net.osmand.plus.widgets.TextViewEx
android:id="@+id/value" android:id="@+id/value"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null" android:background="@null"
android:lines="1"
android:textColor="?android:attr/textColorPrimary" android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/default_desc_text_size" android:textSize="@dimen/default_desc_text_size"
osmand:typeface="@string/font_roboto_medium" osmand:typeface="@string/font_roboto_medium"
tools:text="700 km" /> tools:text="700 km" />
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginStart="@dimen/content_padding_half"
android:layout_marginLeft="@dimen/content_padding_half"
android:layout_weight="1">
<androidx.appcompat.widget.AppCompatImageView <androidx.appcompat.widget.AppCompatImageView
android:id="@+id/image" android:id="@+id/image"
android:layout_width="@dimen/context_menu_transport_icon_size" android:layout_width="@dimen/context_menu_transport_icon_size"
android:layout_height="@dimen/context_menu_transport_icon_size" android:layout_height="@dimen/context_menu_transport_icon_size"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical|end"
android:layout_marginStart="@dimen/context_menu_first_line_top_margin"
android:layout_marginLeft="@dimen/context_menu_first_line_top_margin"
tools:src="@drawable/ic_action_track_16" /> tools:src="@drawable/ic_action_track_16" />
</FrameLayout>
</LinearLayout> </LinearLayout>
<TextView <TextView
android:id="@+id/title" android:id="@+id/title"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@null" android:background="@null"
android:ellipsize="end"
android:lines="1"
android:maxWidth="@dimen/grid_menu_item_width"
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/default_desc_text_size" android:textSize="@dimen/default_desc_text_size"
tools:text="@string/distance" /> tools:text="@string/distance" />

View file

@ -91,6 +91,13 @@
</LinearLayout> </LinearLayout>
<LinearLayout
android:id="@+id/header_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/list_background_color"
android:orientation="vertical" />
<FrameLayout <FrameLayout
android:id="@+id/bottom_container" android:id="@+id/bottom_container"
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -0,0 +1,282 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:osmand="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<FrameLayout
android:id="@+id/background_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/wikivoyage_card_bg_color">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:orientation="vertical"
android:paddingTop="@dimen/content_padding">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/content_padding"
android:layout_marginRight="@dimen/content_padding"
android:layout_marginStart="@dimen/content_padding"
android:layout_marginEnd="@dimen/content_padding">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="@dimen/default_title_line_height"
android:layout_marginBottom="@dimen/measurement_tool_menu_title_padding_bottom"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?attr/active_color_basic"
android:textSize="@dimen/default_list_text_size"
osmand:typeface="@string/font_roboto_medium"
tools:text="London" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/distance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.ContextMenuSubtitle"
android:textColor="@null"
tools:text="5.3 km" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/distance_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/content_padding_half"
android:layout_marginRight="@dimen/content_padding_half"
android:layout_marginStart="@dimen/content_padding_half"
android:layout_marginLeft="@dimen/content_padding_half"
osmand:srcCompat="@drawable/ic_action_distance_16"
android:contentDescription="@string/distance"/>
<View
android:layout_width="1dp"
android:layout_height="16dp"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/content_padding_half"
android:layout_marginRight="@dimen/content_padding_half"
android:background="?attr/wikivoyage_card_divider_color" />
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/diff_ele_down"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.ContextMenuSubtitle"
android:textColor="@null"
tools:text="145 m" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/down_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/content_padding_half"
android:layout_marginRight="@dimen/content_padding_half"
android:layout_marginStart="@dimen/content_padding_half"
android:layout_marginLeft="@dimen/content_padding_half"
osmand:srcCompat="@drawable/ic_action_arrow_down_16"
android:tint="@color/icon_color_default_light"
android:contentDescription="@string/distance" />
<View
android:layout_width="1dp"
android:layout_height="16dp"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/content_padding_half"
android:layout_marginRight="@dimen/content_padding_half"
android:background="?attr/wikivoyage_card_divider_color" />
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/diff_ele_up"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.ContextMenuSubtitle"
android:textColor="@null"
tools:text="15 m" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/up_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/content_padding_half"
android:layout_marginRight="@dimen/content_padding_half"
android:layout_marginStart="@dimen/content_padding_half"
android:layout_marginLeft="@dimen/content_padding_half"
osmand:srcCompat="@drawable/ic_action_arrow_up_16"
android:tint="@color/icon_color_default_light"
android:contentDescription="@string/distance" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="@dimen/content_padding_half"
android:paddingBottom="@dimen/content_padding_small">
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/btn_border_bg_light"
android:gravity="center_vertical"
android:ellipsize="end"
android:maxLines="1"
android:paddingTop="@dimen/subHeaderPadding"
android:paddingBottom="@dimen/subHeaderPadding"
android:paddingLeft="@dimen/bottom_sheet_content_padding_small"
android:paddingRight="@dimen/bottom_sheet_content_padding_small"
android:textAppearance="@style/TextAppearance.ContextMenuSubtitle"
android:textColor="?attr/active_color_basic"
android:textSize="@dimen/default_desc_text_size"
osmand:typeface="@string/font_roboto_medium"
android:drawablePadding="@dimen/content_padding_small_half"
android:drawableStart="@drawable/ic_action_user_account_16"
android:drawableLeft="@drawable/ic_action_user_account_16"
tools:drawableTint="?attr/wikivoyage_active_color"
tools:text="Lorem Ipsum" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<View
android:layout_width="match_parent"
android:visibility="invisible"
android:layout_height="1dp"
android:background="?attr/wikivoyage_card_divider_color" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/content_padding_small_half"
android:paddingBottom="@dimen/content_padding_small_half">
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/bottom_sheet_content_padding_small"
android:layout_marginStart="@dimen/bottom_sheet_content_padding_small"
android:background="@drawable/rounded_background_3dp">
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/left_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackgroundBorderless"
android:drawablePadding="@dimen/content_padding_small"
android:ellipsize="end"
android:gravity="center_vertical"
android:letterSpacing="@dimen/text_button_letter_spacing"
android:maxLines="1"
android:paddingBottom="@dimen/content_padding_half"
android:paddingLeft="@dimen/bottom_sheet_content_padding_small"
android:paddingRight="@dimen/bottom_sheet_content_padding_small"
android:paddingTop="@dimen/content_padding_half"
android:textColor="?attr/wikivoyage_active_color"
android:textSize="@dimen/default_desc_text_size"
osmand:typeface="@string/font_roboto_medium"
tools:drawableLeft="@drawable/ic_action_read_article"
tools:drawableTint="?attr/wikivoyage_active_color"
tools:ignore="UnusedAttribute"
tools:text="Read"
android:paddingStart="@dimen/bottom_sheet_content_padding_small"
android:paddingEnd="@dimen/bottom_sheet_content_padding_small"
tools:drawableStart="@drawable/ic_action_read_article" />
</FrameLayout>
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/bottom_sheet_content_padding_small"
android:layout_marginRight="@dimen/bottom_sheet_content_padding_small"
android:background="@drawable/rounded_background_3dp">
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/right_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackgroundBorderless"
android:drawablePadding="@dimen/content_padding_small"
android:ellipsize="end"
android:gravity="center_vertical"
android:letterSpacing="@dimen/text_button_letter_spacing"
android:maxLines="1"
android:paddingBottom="@dimen/content_padding_half"
android:paddingLeft="@dimen/bottom_sheet_content_padding_small"
android:paddingRight="@dimen/bottom_sheet_content_padding_small"
android:paddingTop="@dimen/content_padding_half"
android:textColor="?attr/wikivoyage_active_color"
android:textSize="@dimen/default_desc_text_size"
osmand:typeface="@string/font_roboto_medium"
tools:drawableRight="@drawable/ic_action_read_later_fill"
tools:drawableTint="?attr/wikivoyage_active_color"
tools:ignore="UnusedAttribute"
tools:text="Delete"
tools:drawableEnd="@drawable/ic_action_read_later_fill"
android:paddingEnd="@dimen/bottom_sheet_content_padding_small"
android:paddingStart="@dimen/bottom_sheet_content_padding_small" />
</FrameLayout>
</LinearLayout>
</LinearLayout>
</FrameLayout>
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?attr/wikivoyage_card_divider_color" />
<include
android:id="@+id/shadow"
layout="@layout/card_bottom_divider"
android:visibility="gone"
tools:visibility="visible" />
<include
android:id="@+id/list_item_divider"
layout="@layout/list_item_divider"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>

View file

@ -3513,7 +3513,7 @@
<string name="shared_string_poi_types">أنواع نقاط الاهتمام</string> <string name="shared_string_poi_types">أنواع نقاط الاهتمام</string>
<string name="shared_string_nothing_selected">لا شيء محدد</string> <string name="shared_string_nothing_selected">لا شيء محدد</string>
<string name="shared_string_quick_actions">زر الإجراء السريع</string> <string name="shared_string_quick_actions">زر الإجراء السريع</string>
<string name="shared_string_profiles">ملفات التعريف</string> <string name="shared_string_profiles">الأوضاع</string>
<string name="replace_all_desc">سيتم استبدال العناصر الحالية بالعناصر التي في الملف</string> <string name="replace_all_desc">سيتم استبدال العناصر الحالية بالعناصر التي في الملف</string>
<string name="replace_all">استبدل الكل</string> <string name="replace_all">استبدل الكل</string>
<string name="get_discount_title">احصل على %1$d %2$s عند %3$s مقابل.</string> <string name="get_discount_title">احصل على %1$d %2$s عند %3$s مقابل.</string>
@ -3965,7 +3965,7 @@
<string name="export_not_enough_space_descr">مساحة جهازك %1$s الخالية فقط. يرجى تحرير بعض المساحة أو إلغاء تحديد بعض العناصر للتصدير.</string> <string name="export_not_enough_space_descr">مساحة جهازك %1$s الخالية فقط. يرجى تحرير بعض المساحة أو إلغاء تحديد بعض العناصر للتصدير.</string>
<string name="shared_string_resources">المصادر</string> <string name="shared_string_resources">المصادر</string>
<string name="approximate_file_size">حجم الملف التقريبي</string> <string name="approximate_file_size">حجم الملف التقريبي</string>
<string name="select_data_to_export">حدد البيانات التي سيتم تصديرها إلى الملف.</string> <string name="select_data_to_export">حدد البيانات التي سيتم تصديرها إلى ملف.</string>
<string name="file_size_needed_for_import">مطلوب للاستيراد</string> <string name="file_size_needed_for_import">مطلوب للاستيراد</string>
<string name="export_not_enough_space">لا يوجد مساحة كافية</string> <string name="export_not_enough_space">لا يوجد مساحة كافية</string>
<string name="add_to_mapillary">أضف إلى مابيلاي</string> <string name="add_to_mapillary">أضف إلى مابيلاي</string>
@ -3997,10 +3997,10 @@
\n • دعم ألوان مخصصة للمفضلة ونقاط لمسار الطريق \n • دعم ألوان مخصصة للمفضلة ونقاط لمسار الطريق
\n \n
\n</string> \n</string>
<string name="profile_type_osmand_string">ملف تعريف أوسماند</string> <string name="profile_type_osmand_string">وضع الاستعراض</string>
<string name="profile_type_user_string">ملف تعريف المستخدم</string> <string name="profile_type_user_string">ملف تعريف المستخدم</string>
<string name="reverse_all_points">عكس جميع النقاط</string> <string name="reverse_all_points">عكس جميع النقاط</string>
<string name="profile_by_default_description">حدد ملف التعريف، الذي سيتم استخدامه في بدء التطبيق.</string> <string name="profile_by_default_description">حدد الوضع الذي سيتم استخدامه في بدء التطبيق.</string>
<string name="shared_string_last_used">آخر استخدام</string> <string name="shared_string_last_used">آخر استخدام</string>
<string name="routing_attr_prefer_hiking_routes_description">تفضيل طرق التنزه</string> <string name="routing_attr_prefer_hiking_routes_description">تفضيل طرق التنزه</string>
<string name="routing_attr_prefer_hiking_routes_name">تفضيل طرق التنزه</string> <string name="routing_attr_prefer_hiking_routes_name">تفضيل طرق التنزه</string>
@ -4063,4 +4063,10 @@
<string name="routing_engine_vehicle_type_mtb">MTB</string> <string name="routing_engine_vehicle_type_mtb">MTB</string>
<string name="message_server_error">خطأ في الخادم: %1$s</string> <string name="message_server_error">خطأ في الخادم: %1$s</string>
<string name="message_name_is_already_exists">الاسم موجود بالفعل</string> <string name="message_name_is_already_exists">الاسم موجود بالفعل</string>
<string name="delete_online_routing_engine">هل تريد حذف محرك التوجيه عبر الإنترنت؟</string>
<string name="context_menu_read_full">قراءة كاملة</string>
<string name="context_menu_edit_descr">تحرير الوصف</string>
<string name="delete_waypoints">حذف نقاط الطريق</string>
<string name="copy_to_map_markers">نسخ لعلامات الخريطة</string>
<string name="copy_to_map_favorites">نسخ للمفضلة</string>
</resources> </resources>

View file

@ -24,4 +24,8 @@
<string name="osmand_purchases_item">ওএসএমও কেনাকাটা</string> <string name="osmand_purchases_item">ওএসএমও কেনাকাটা</string>
<string name="legend_item_description">একটি মানচিত্রের প্রতীকের জন্য গাইড</string> <string name="legend_item_description">একটি মানচিত্রের প্রতীকের জন্য গাইড</string>
<string name="navigation_profiles_item">ন্যাভিগেশন প্রোফাইল</string> <string name="navigation_profiles_item">ন্যাভিগেশন প্রোফাইল</string>
<string name="av_locations">অবস্থানগুলো</string>
<string name="shared_string_navigation">দিক নির্ণয়</string>
<string name="m_s">মি/সে</string>
<string name="nm">নটিকাল মাইল</string>
</resources> </resources>

View file

@ -3982,7 +3982,7 @@
<string name="announcement_time_prepare">Preparar</string> <string name="announcement_time_prepare">Preparar</string>
<string name="announcement_time_off_route">Fuera de la ruta</string> <string name="announcement_time_off_route">Fuera de la ruta</string>
<string name="announcement_time_arrive">Has llegado al destino</string> <string name="announcement_time_arrive">Has llegado al destino</string>
<string name="shared_string_turn">Giro</string> <string name="shared_string_turn">Girar</string>
<string name="announcement_time_intervals">Intervalos de tiempo y distancia</string> <string name="announcement_time_intervals">Intervalos de tiempo y distancia</string>
<string name="announcement_time_descr">El tiempo de anuncio de las diferentes indicaciones por voz depende del tipo de mensaje, la velocidad de navegación actual y la velocidad de navegación predefinida.</string> <string name="announcement_time_descr">El tiempo de anuncio de las diferentes indicaciones por voz depende del tipo de mensaje, la velocidad de navegación actual y la velocidad de navegación predefinida.</string>
<string name="announcement_time_title">Tiempo de anuncio</string> <string name="announcement_time_title">Tiempo de anuncio</string>
@ -4003,4 +4003,10 @@
<string name="routing_engine_vehicle_type_mtb">Bicicleta de montaña</string> <string name="routing_engine_vehicle_type_mtb">Bicicleta de montaña</string>
<string name="message_server_error">Error de servidor: %1$s</string> <string name="message_server_error">Error de servidor: %1$s</string>
<string name="message_name_is_already_exists">El nombre ya existe</string> <string name="message_name_is_already_exists">El nombre ya existe</string>
<string name="delete_online_routing_engine">¿Borrar este motor de navegación en línea\?</string>
<string name="context_menu_read_full">Leer completo</string>
<string name="context_menu_edit_descr">Editar descripción</string>
<string name="delete_waypoints">Borrar puntos de referencia</string>
<string name="copy_to_map_markers">Copiar a «Marcadores del mapa»</string>
<string name="copy_to_map_favorites">Copiar a favoritos</string>
</resources> </resources>

View file

@ -18,9 +18,8 @@
<string name="osmand_parking_pm">po piet</string> <string name="osmand_parking_pm">po piet</string>
<string name="osmand_parking_am">ryto</string> <string name="osmand_parking_am">ryto</string>
<string name="osmand_parking_position_name">Statymo vieta</string> <string name="osmand_parking_position_name">Statymo vieta</string>
<string name="osmand_parking_plugin_description">Šis įskiepis leidžia įsiminti vietą, kurioje palikote savo automobilį ir kiek laiko liko iki parkavimo pabaigos (jei ribojamas laikas). <string name="osmand_parking_plugin_description">Šis įskiepis leidžia įsiminti vietą, kurioje palikote savo automobilį ir kiek laiko liko iki parkavimo pabaigos .
\nTiek vieta, tiek laikas yra matomi OsmAnd valdymo skydelyje bei skydelyje žemėlapyje rodinyje. Šis įskiepis taip pat gali įrašyti priminimą į Android kalendorių.</string>
Tiek vieta, tiek laikas yra matomi OsmAnd valdymo skydelyje bei skydelyje žemėlapyje rodinyje. Šis įskiepis taip pat gali įrašyti priminimą į kalendorių.</string>
<string name="osmand_parking_plugin_name">Statymo vieta</string> <string name="osmand_parking_plugin_name">Statymo vieta</string>
<string name="context_menu_item_add_parking_point">Žymėti statymo vietą</string> <string name="context_menu_item_add_parking_point">Žymėti statymo vietą</string>
<string name="context_menu_item_delete_parking_point">Naikinti statymo žymę</string> <string name="context_menu_item_delete_parking_point">Naikinti statymo žymę</string>
@ -200,9 +199,8 @@ Tiek vieta, tiek laikas yra matomi OsmAnd valdymo skydelyje bei skydelyje žemė
<string name="osmand_service">Foninis režimas</string> <string name="osmand_service">Foninis režimas</string>
<string name="osmand_service_descr">Reikalinga, jei norite naudoti OsmAnd kai ekranas yra išjungtas.</string> <string name="osmand_service_descr">Reikalinga, jei norite naudoti OsmAnd kai ekranas yra išjungtas.</string>
<string name="download_files_not_enough_space">Neužtenka vietos parsiųsti %1$s MB (laisva: %2$s).</string> <string name="download_files_not_enough_space">Neužtenka vietos parsiųsti %1$s MB (laisva: %2$s).</string>
<string name="download_files_question_space">Atsiųsti {0} failą(-us)? <string name="download_files_question_space">Atsiųsti {0} failą(-us)\?
Tam prireiks {1} MB pastoviam saugojimui. \nTam prireiks {1} MB pastoviam saugojimui. (Dabar laisvos vietos yra {2} MB.)</string>
(Dabar laisvos vietos yra {2} MB.)</string>
<string name="use_transparent_map_theme">Permatoma tema</string> <string name="use_transparent_map_theme">Permatoma tema</string>
<string name="native_library_not_supported">Aparatinė biblioteka šiame įrenginyje nepalaikoma.</string> <string name="native_library_not_supported">Aparatinė biblioteka šiame įrenginyje nepalaikoma.</string>
<string name="init_native_library">Inicializuojama aparatinė biblioteka…</string> <string name="init_native_library">Inicializuojama aparatinė biblioteka…</string>
@ -779,7 +777,27 @@ Tam prireiks {1} MB pastoviam saugojimui.
<string name="snap_to_road_descr">Poziciją rodyti ant kelių kai naviguojama.</string> <string name="snap_to_road_descr">Poziciją rodyti ant kelių kai naviguojama.</string>
<string name="snap_to_road">Rodyti ant kelio</string> <string name="snap_to_road">Rodyti ant kelio</string>
<string name="osmand_long_description_1000_chars">OsmAnd (OSM Automated Navigation Directions) <string name="osmand_long_description_1000_chars">OsmAnd (OSM Automated Navigation Directions)
\nOsmAnd yra atviro kodo programa naudojanti įvairius OpenStreetMap (OSM) duomenis. Visi žemėlapių duomenys (vektoriniai ar lakštiniai) gali būti išsaugoti telefone ir naudojami be interneto prieigos. OsmAnd taip pat gali pasiūlyti maršruto skaičiavimo paslaugas internete ar įrenginyje, bei balso nurodymus kelionei apskaičiuotu maršrutu. Dalis pagrindinių savybių: - Veikia be interneto (išsaugokite parsiųstus žemėlapius ar jų lakštus įrenginyje) - Kompaktiški vektoriniai viso pasaulio žemėlapiai - Parsisųskite šalies ar regiono žemėlapius tiesiai iš pačios propgramos - Galima sulieti kelis žemėlapop sluoksnius, tokius kaip GPX ar judėjimo istoriją, Lankytinas Vietas, mėgiamas vietas, kontūrų linijas, viešojo transporto stoteles, papildomus žemėlapius su pasirinktinai nustatomu permatomumo lygiu - Adresų ir LV paieška nenaudojant interneto - Vidutinio ilgio maršruto apskaičiavimas be interneto paslaugų - Automobilio, dviračio ir pėsčiojo režimai su: - pasirinktiniu dineos/nakties rodinio perjungimu - pasirinktiniu pagal judėjimo greitį automatiškai nustatomu mąsteliu - pasirinktine žemėlapio orientacija pagal kompaso arba judėjimo kryptį - pasirinktinis eismo juostų nurodymas, greičio ribojimų rodymas, įrašyti ar generuojami balsai Šios nemokamos OsmAnd versijos apribojimai: - Ribojamas žemėlapių parsiuntimų skaičius - Nėra prieigos prie iš Wikipedia parsiunčiamų LV OsmAnd yra aktyviai tobulinama ir mūsų projektas bei jo tolesnis progresas priklauso nuo finansinės paramos, kuri įgalina tolesnį vystymą ir naujų funkcijų kūrimą. Norime paskatinti jus nusipirkti OsmAnd+ programą arba finansiškai prisidėti prie specifinių funkcijų kūrimo arba šiaip paremti programą osmand.net svetainėje.</string> \n
\nOsmAnd yra atviro kodo programa naudojanti įvairius OpenStreetMap (OSM) duomenis. Visi žemėlapių duomenys (vektoriniai ar lakštiniai) gali būti išsaugoti telefone ir naudojami be interneto prieigos. OsmAnd taip pat gali pasiūlyti maršruto skaičiavimo paslaugas internete ar įrenginyje, bei balso nurodymus kelionei apskaičiuotu maršrutu.
\n
\nDalis pagrindinių savybių:
\n- Veikia be interneto (išsaugokite parsiųstus žemėlapius ar jų lakštus įrenginyje)
\n- Kompaktiški vektoriniai viso pasaulio žemėlapiai
\n- Parsisųskite šalies ar regiono žemėlapius tiesiai iš pačios propgramos
\n- Galima sulieti kelis žemėlapop sluoksnius, tokius kaip GPX ar judėjimo istoriją, Lankytinas Vietas, mėgiamas vietas, kontūrų linijas, viešojo transporto stoteles, papildomus žemėlapius su pasirinktinai nustatomu permatomumo lygiu
\n - Adresų ir LV paieška nenaudojant interneto
\n- Vidutinio ilgio maršruto apskaičiavimas be interneto prieigos
\n- Automobilio, dviračio ir pėsčiojo režimai su:
\n- pasirinktiniu dineos/nakties rodinio perjungimu
\n- pasirinktiniu pagal judėjimo greitį automatiškai nustatomu mąsteliu
\n- pasirinktine žemėlapio orientacija pagal kompaso arba judėjimo kryptį
\n- pasirinktinis eismo juostų nurodymas, greičio ribojimų rodymas, įrašyti ar generuojami balsai
\n
\nNemokamos OsmAnd versijos apribojimai:
\n- Ribojamas žemėlapių parsiuntimų skaičius
\n- Nėra prieigos prie iš Wikipedia parsiunčiamų LV
\n
\n OsmAnd yra aktyviai tobulinama ir mūsų projektas bei jo tolesnis progresas priklauso nuo finansinės paramos, kuri įgalina tolesnį vystymą ir naujų funkcijų kūrimą. Norime paskatinti jus nusipirkti OsmAnd+ programą arba finansiškai prisidėti prie specifinių funkcijų kūrimo arba šiaip paremti programą https://osmand.net svetainėje.</string>
<string name="osmand_plus_short_description_80_chars">OsmAnd - atviro kodo navigacijos programa su žemėlapiais</string> <string name="osmand_plus_short_description_80_chars">OsmAnd - atviro kodo navigacijos programa su žemėlapiais</string>
<string name="osmand_plus_long_description_1000_chars">OsmAnd+ (OSM Automated Navigation Directions) <string name="osmand_plus_long_description_1000_chars">OsmAnd+ (OSM Automated Navigation Directions)
\n \n
@ -1872,9 +1890,9 @@ Failams reikalinga {3} MB laikinam ir {1} MB pastoviam saugojimui.
<string name="osm_live_header">Prenumerata leidžia kas valandą gauti visų pasaulio žemėlapių atnaujinimus. <string name="osm_live_header">Prenumerata leidžia kas valandą gauti visų pasaulio žemėlapių atnaujinimus.
Dalis pajamų grįžta OSM bendruomenei ir apmokamas kiekvienas indėlis į OSM kūrimą. Dalis pajamų grįžta OSM bendruomenei ir apmokamas kiekvienas indėlis į OSM kūrimą.
Tai yra puikus būdas paremti OsmAnd ir OSM, jei jie jums patinka.</string> Tai yra puikus būdas paremti OsmAnd ir OSM, jei jie jums patinka.</string>
<string name="download_files_question_space_with_temp">Ar siųsti {0} failų? <string name="download_files_question_space_with_temp">Ar siųsti {0} failų\?
Failams reikalinga {3} MB laikinam ir {1} MB pastoviam saugojimui. \nFailams reikalinga {3} MB laikinam ir {1} MB pastoviam saugojimui.
(Šiuo metu prieinama tik {2} MB vietos.)</string> \n(Šiuo metu prieinama tik {2} MB vietos.)</string>
<string name="lang_es_us">Ispanų (Amerikos)</string> <string name="lang_es_us">Ispanų (Amerikos)</string>
<string name="lang_en_gb">Anglų (Junginė Karalystė)</string> <string name="lang_en_gb">Anglų (Junginė Karalystė)</string>
<string name="lang_be_by">Belarusų (Lotynų)</string> <string name="lang_be_by">Belarusų (Lotynų)</string>
@ -2419,7 +2437,9 @@ Tai yra puikus būdas paremti OsmAnd ir OSM, jei jie jums patinka.</string>
<string name="passed">Paskutinį kartą naudota: %1$s</string> <string name="passed">Paskutinį kartą naudota: %1$s</string>
<string name="osm_recipient_stat">Taisymų %1$s, suma %2$s mBTC</string> <string name="osm_recipient_stat">Taisymų %1$s, suma %2$s mBTC</string>
<string name="lang_lo">Laosiečių</string> <string name="lang_lo">Laosiečių</string>
<string name="osmand_extended_description_part1">OsmAnd (OSM Automated Navigation Directions) yra žemėlapio ir navigacijos programa su prieiga prie nemokamų, pasaulinių ir aukštos kokybės OpenStreetMap (OSM) duomenų. Naudokitės balso ir vaizdo navigatoriumi, peržiūrėkite LV (lankytinas vietas), kurkite ir valdytkite GPX kelius, naudojkite kontūro linijos vizualizaciją ir aukščio info (įskiepių pagalba), pasirinkite vairavimo, dviračių, pėsčiųjų režimus, redaguokite OSM duomenis ir daug daugiau.</string> <string name="osmand_extended_description_part1">OsmAnd (OSM Automated Navigation Directions) yra žemėlapio ir navigacijos programa su prieiga prie nemokamų, pasaulinių ir aukštos kokybės OpenStreetMap (OSM) duomenų.
\n
\nNaudokitės balso ir vaizdo navigatoriumi, peržiūrėkite LV (lankytinas vietas), kurkite ir valdytkite GPX kelius, naudojkite kontūro linijos vizualizaciją ir aukščio info (įskiepių pagalba), pasirinkite vairavimo, dviračių, pėsčiųjų režimus, redaguokite OSM duomenis ir daug daugiau.</string>
<string name="quick_favorites_name_preset">Nuostatos pavadinimas</string> <string name="quick_favorites_name_preset">Nuostatos pavadinimas</string>
<string name="quick_action_add_marker_descr">Bakstelėję veiksmo mygtuką pridėsite žemėlapio žymeklį ekrano centre.</string> <string name="quick_action_add_marker_descr">Bakstelėję veiksmo mygtuką pridėsite žemėlapio žymeklį ekrano centre.</string>
<string name="quick_action_take_audio_note_descr">Bakstelėję veiksmo mygtuką pridėsite garso įrašą ekrano centre.</string> <string name="quick_action_take_audio_note_descr">Bakstelėję veiksmo mygtuką pridėsite garso įrašą ekrano centre.</string>

View file

@ -1526,7 +1526,7 @@
<string name="voice_data_not_supported">Taledataversjon som ikke støttes</string> <string name="voice_data_not_supported">Taledataversjon som ikke støttes</string>
<string name="background_router_service_descr">Sporer posisjonen din mens skjermen er slått av.</string> <string name="background_router_service_descr">Sporer posisjonen din mens skjermen er slått av.</string>
<string name="share_fav_subject">Favoritter delt via OsmAnd</string> <string name="share_fav_subject">Favoritter delt via OsmAnd</string>
<string name="import_file_favourites">Lagre data som GPX-fil eller importere rutepunkter til Favoritter\?</string> <string name="import_file_favourites">Lagre data som GPX-fil eller importere rutepunkter til favoritter\?</string>
<string name="coords_format_descr">Format for geografiske koordinater.</string> <string name="coords_format_descr">Format for geografiske koordinater.</string>
<string name="coords_format">Koordinatformat</string> <string name="coords_format">Koordinatformat</string>
<string name="map_widget_search">Søk</string> <string name="map_widget_search">Søk</string>
@ -3919,4 +3919,18 @@
<string name="routing_engine_vehicle_type_cycling_regular">Vanlig sykling</string> <string name="routing_engine_vehicle_type_cycling_regular">Vanlig sykling</string>
<string name="message_server_error">Tjenerfeil: %1$s</string> <string name="message_server_error">Tjenerfeil: %1$s</string>
<string name="message_name_is_already_exists">Navnet finnes allerede</string> <string name="message_name_is_already_exists">Navnet finnes allerede</string>
<string name="analyze_by_intervals">Analyser ved intervaller (delt intervall)</string>
<string name="announcement_time_off_route">Ruteavvik</string>
<string name="routing_engine_vehicle_type_hgv">Stor lastebil</string>
<string name="delete_online_routing_engine">Slett denne nettbaserte rutingsmotoren\?</string>
<string name="context_menu_read_full">Les hele</string>
<string name="delete_waypoints">Slett rutepunkt</string>
<string name="app_mode_gap">Luke</string>
<string name="login_open_street_map">Logg inn på OpenStreetMap</string>
<string name="user_login">Logg inn</string>
<string name="announcement_time_passing">Tidsforbruk</string>
<string name="announcement_time_approach">Ankomst</string>
<string name="context_menu_edit_descr">Rediger beskrivelse</string>
<string name="copy_to_map_markers">Kopier til kartmarkører</string>
<string name="copy_to_map_favorites">Kopier til favoritter</string>
</resources> </resources>

View file

@ -2178,8 +2178,8 @@
<string name="shared_string_time_span">Tijdsduur</string> <string name="shared_string_time_span">Tijdsduur</string>
<string name="shared_string_time_moving">Tijd in beweging</string> <string name="shared_string_time_moving">Tijd in beweging</string>
<string name="routing_attr_driving_style_name">Rijstijl</string> <string name="routing_attr_driving_style_name">Rijstijl</string>
<string name="routing_attr_height_obstacles_name">Gebruik hoogtegegevens</string> <string name="routing_attr_height_obstacles_name">Hoogtegegevens gebruiken</string>
<string name="routing_attr_height_obstacles_description">Factor in hoogtegegevens (van SRTM, ASTER en EU-DEM data).</string> <string name="routing_attr_height_obstacles_description">Gebruik hoogtegegevens (van SRTM, ASTER en EU-DEM data) bij bepalen route.</string>
<string name="shared_string_gpx_track">Track</string> <string name="shared_string_gpx_track">Track</string>
<string name="right_side_navigation">Rechts rijdend</string> <string name="right_side_navigation">Rechts rijdend</string>
<string name="driving_region_automatic">Automatisch</string> <string name="driving_region_automatic">Automatisch</string>
@ -3320,9 +3320,9 @@
<string name="route_recalculation">Route herberekening</string> <string name="route_recalculation">Route herberekening</string>
<string name="accessibility_announce">Meld</string> <string name="accessibility_announce">Meld</string>
<string name="login_and_pass">Gebruikersnaam en wachtwoord</string> <string name="login_and_pass">Gebruikersnaam en wachtwoord</string>
<string name="plugin_global_prefs_info">Deze instellingen hebben betrekking op alle profielen</string> <string name="plugin_global_prefs_info">Deze plugin-instellingen hebben betrekking op alle profielen</string>
<string name="osm_editing">OSM Bewerking</string> <string name="osm_editing">OSM Bewerking</string>
<string name="osm_edits_view_descr">U kan al uw niet geüploade bewerkingen of OSM fouten zien in %1$s. Geüploade punten ziet u niet meer.</string> <string name="osm_edits_view_descr">U kunt alle niet-geüploade bewerkingen of OSM-opmerkingen zien in %1$s. Geüploade wijzigingen ziet u niet meer.</string>
<string name="app_mode_osm">OSM</string> <string name="app_mode_osm">OSM</string>
<string name="select_nav_icon_descr">Tijdens navigatie of beweging getoond icoon.</string> <string name="select_nav_icon_descr">Tijdens navigatie of beweging getoond icoon.</string>
<string name="select_map_icon_descr">Bij rust getoond icoon.</string> <string name="select_map_icon_descr">Bij rust getoond icoon.</string>
@ -3667,8 +3667,8 @@
<string name="next_segment">Volgende segment</string> <string name="next_segment">Volgende segment</string>
<string name="all_next_segments">Alle volgende segmenten</string> <string name="all_next_segments">Alle volgende segmenten</string>
<string name="all_next_segments_will_be_recalc">Alle volgende segmenten worden opnieuw berekend met het geselecteerde profiel.</string> <string name="all_next_segments_will_be_recalc">Alle volgende segmenten worden opnieuw berekend met het geselecteerde profiel.</string>
<string name="whole_track">De ganse track</string> <string name="whole_track">De gehele route</string>
<string name="route_between_points_whole_track_button_desc">De ganse track wordt herberekend volgens het geselecteerde profiel.</string> <string name="route_between_points_whole_track_button_desc">De gehele route wordt herberekend volgens het geselecteerde profiel.</string>
<string name="pass_whole_track_descr">Punt van de track om te navigeren</string> <string name="pass_whole_track_descr">Punt van de track om te navigeren</string>
<string name="shared_string_save_as_gpx">Bewaar als nieuw trackbestand</string> <string name="shared_string_save_as_gpx">Bewaar als nieuw trackbestand</string>
<string name="save_as_new_track">Bewaar als nieuwe track</string> <string name="save_as_new_track">Bewaar als nieuwe track</string>
@ -3730,8 +3730,8 @@
\nDe grafiek is beschikbaar na herberekening.</string> \nDe grafiek is beschikbaar na herberekening.</string>
<string name="ltr_or_rtl_combine_via_dash">%1$s — %2$s</string> <string name="ltr_or_rtl_combine_via_dash">%1$s — %2$s</string>
<string name="import_track_descr">Kies een trackbestand om te volgen of importeer het, vanaf uw apparaat.</string> <string name="import_track_descr">Kies een trackbestand om te volgen of importeer het, vanaf uw apparaat.</string>
<string name="app_mode_gap">Kloof</string> <string name="app_mode_gap">Onderbreking</string>
<string name="shared_string_custom">Op maat</string> <string name="shared_string_custom">Aangepast</string>
<string name="perform_oauth_authorization_description">Voer een OAuth-login uit om osm edit functies te gebruiken</string> <string name="perform_oauth_authorization_description">Voer een OAuth-login uit om osm edit functies te gebruiken</string>
<string name="release_3_5">"• Bijgewerkte app- en profielinstellingen: instellingen zijn nu gerangschikt op type. Elk profiel kan afzonderlijk worden aangepast. <string name="release_3_5">"• Bijgewerkte app- en profielinstellingen: instellingen zijn nu gerangschikt op type. Elk profiel kan afzonderlijk worden aangepast.
\n \n
@ -3843,41 +3843,41 @@
<string name="icon_group_service">Onderhoud</string> <string name="icon_group_service">Onderhoud</string>
<string name="icon_group_symbols">Symbolen</string> <string name="icon_group_symbols">Symbolen</string>
<string name="icon_group_sport">Sport</string> <string name="icon_group_sport">Sport</string>
<string name="icon_group_emergency">Noodtoestand</string> <string name="icon_group_emergency">Noodgevallen</string>
<string name="icon_group_travel">Reizen</string> <string name="icon_group_travel">Reizen</string>
<string name="empty_state_my_tracks_desc">Trackbestanden importeren of opnemen</string> <string name="empty_state_my_tracks_desc">Trackbestanden importeren of opnemen</string>
<string name="quick_action_add_gpx">Track waypoint toevoegen</string> <string name="quick_action_add_gpx">Track waypoint toevoegen</string>
<string name="marker_save_as_track">Opslaan als trackbestand</string> <string name="marker_save_as_track">Opslaan als trackbestand</string>
<string name="monitoring_control_start">Rec</string> <string name="monitoring_control_start">Rec</string>
<string name="message_you_need_add_two_points_to_show_graphs">U moet minimaal twee punten toevoegen</string> <string name="message_you_need_add_two_points_to_show_graphs">U moet minimaal twee punten toevoegen</string>
<string name="login_open_street_map">Login op OpenStreetMap</string> <string name="login_open_street_map">Inloggen bij OpenStreetMap</string>
<string name="login_open_street_map_org">Login op OpenStreetMap.org</string> <string name="login_open_street_map_org">Inloggen bij OpenStreetMap.org</string>
<string name="sign_in_with_open_street_map">Login met OpenStreetMap</string> <string name="sign_in_with_open_street_map">Aanmelden via OpenStreetMap (OAuth)</string>
<string name="open_street_map_login_mode">U moet inloggen om nieuwe of gewijzigde wijzigingen te uploaden. <string name="open_street_map_login_mode">U moet inloggen om wijzigingen te uploaden.
\n \n
\nU kunt inloggen met de veilige OAuth-methode of uw login en wachtwoord gebruiken.</string> \nU kunt inloggen met de veilige OAuth-methode of uw loginnaam en wachtwoord gebruiken.</string>
<string name="use_login_password">Gebruik uw login gegevens</string> <string name="use_login_password">Inloggen met gebruikersnaam en wachtwoord.</string>
<string name="login_account">Account</string> <string name="login_account">Account</string>
<string name="user_login">Inloggen</string> <string name="user_login">Gebruikersnaam</string>
<string name="manage_subscription">Abonnement beheren</string> <string name="manage_subscription">Abonnement beheren</string>
<string name="subscription_payment_issue_title">Er is een probleem met uw abonnement. Klik op de knop om naar de Google Play-abonnementsinstellingen te gaan om uw betalingsmethode te corrigeren.</string> <string name="subscription_payment_issue_title">Er is een probleem met uw abonnement. Klik op de knop om naar de Google Play-abonnementsinstellingen te gaan om uw betalingsmethode te corrigeren.</string>
<string name="subscription_expired_title">OsmAnd Live-abonnement is verlopen</string> <string name="subscription_expired_title">OsmAnd Live-abonnement is verlopen</string>
<string name="subscription_paused_title">OsmAnd Live-abonnement is gepauzeerd</string> <string name="subscription_paused_title">OsmAnd Live-abonnement is gepauzeerd</string>
<string name="subscription_on_hold_title">Het OsmAnd Live-abonnement is op wacht gezet</string> <string name="subscription_on_hold_title">Het OsmAnd Live-abonnement is opgeschort</string>
<string name="markers_history">Markeervlaggen geschiedenis</string> <string name="markers_history">Markeervlaggetjes-geschiedenis</string>
<string name="send_files_to_openstreetmap">Stuur het GPX-bestand naar OpenStreetMap</string> <string name="send_files_to_openstreetmap">Stuur het GPX-bestand naar OpenStreetMap</string>
<string name="enter_text_separated">Voer tags in, gescheiden door komma\'s.</string> <string name="enter_text_separated">Voer tags in, gescheiden door komma\'s.</string>
<string name="gpx_upload_public_visibility_descr">Openbaar betekent dat de trace openbaar zal worden getoond in Uw GPS-sporen en in openbare GPS-traceerlijsten. Gegevens die via de API worden geleverd, verwijzen niet naar uw traceerpagina. Tijdaanduidingen van de traceerpunten zijn niet beschikbaar via de openbare GPS-API en de punten zijn niet chronologisch gerangschikt. Andere gebruikers kunnen de onbewerkte tracering echter nog steeds downloaden van de openbare traceringslijst en eventuele tijdaanduiding erin.</string> <string name="gpx_upload_public_visibility_descr">\"Publiek\" betekent dat de track openbaar wordt getoond in \"Jouw GPS-tracks\" en in openbare GPS-track-lijsten. Trackpoints die d.m.v. de API worden gedownload zullen niet aan je oorspronkelijke trackpagina refereren. Tijden in de tracks zijn niet beschikbaar in de publieke GPS API, maar de punten zullen wel chronologisch geordend zijn. Echter, andere gebruikers kunnen de track van de openbare GPS-track-lijsten downloaden met alle tijdinformatie.</string>
<string name="gpx_upload_private_visibility_descr">Privé betekent dat de trace niet in openbare vermeldingen zal verschijnen, maar trackpoints ervan zullen nog steeds beschikbaar zijn via de openbare GPS API zonder tijdaanduiding, maar zullen niet chronologisch worden gerangschikt.</string> <string name="gpx_upload_private_visibility_descr">\"Persoonlijk\" betekent dat de track niet wordt getoond in openbare GPS-track-lijsten, maar de trackpunten zijn wel beschikbaar in volgorde van tijd door de publieke GPS API, maar zonder tijdinformatie.</string>
<string name="gpx_upload_identifiable_visibility_descr">Identificeerbaar betekent dat de trace openbaar wordt getoond in uw GPS-sporen en in openbare GPS-trace-lijsten, dat wil zeggen dat andere gebruikers de onbewerkte trace kunnen downloaden en deze aan uw gebruikersnaam kunnen koppelen. Gegevens die via de trackpoints-API worden aangeboden, verwijzen naar uw oorspronkelijke tracepagina. Tijdaanduidingen van de traceerpunten zijn beschikbaar via de openbare GPS-API.</string> <string name="gpx_upload_identifiable_visibility_descr">\"Identificeerbaar\" betekent dat de trace openbaar wordt getoond in uw GPS-sporen en in openbare GPS-trace-lijsten. Dat wil zeggen dat andere gebruikers de onbewerkte trace kunnen downloaden en deze aan uw gebruikersnaam kunnen koppelen. Gegevens die via de trackpoints-API worden aangeboden, verwijzen naar uw oorspronkelijke tracepagina. Tijdaanduidingen van de traceerpunten zijn beschikbaar via de openbare GPS-API.</string>
<string name="gpx_upload_trackable_visibility_descr">Trackbaar betekent dat de trace niet in openbare vermeldingen zal verschijnen, maar trackpoints ervan zullen nog steeds beschikbaar zijn via de openbare GPS API met tijdaanduiding. Andere gebruikers kunnen alleen verwerkte trackpoints downloaden van uw trace die niet rechtstreeks aan u kunnen worden gekoppeld.</string> <string name="gpx_upload_trackable_visibility_descr">\"Traceerbaar\" betekent dat de track niet wordt getoond in openbare GPS-track-lijsten maar de trackpunten zijn wel beschikbaar door de publieke GPS API met tijdinformatie. Andere gebruikers kunnen alleen bewerkte trackpunten downloaden die niet aan jou herleid kunnen worden.</string>
<string name="osm_edit_close_note">Sluit OSM Note</string> <string name="osm_edit_close_note">Sluit OSM Note</string>
<string name="osm_edit_comment_note">Opmerking maken bij OSM-nota</string> <string name="osm_edit_comment_note">Opmerking maken bij OSM-nota</string>
<string name="osm_login_descr">U kunt inloggen met de veilige OAuth-methode of uw login en wachtwoord gebruiken.</string> <string name="osm_login_descr">U kunt inloggen met de veilige OAuth-methode, of uw loginnaam en wachtwoord gebruiken.</string>
<string name="shared_string_add_photo">Een foto toevoegen</string> <string name="shared_string_add_photo">Een foto toevoegen</string>
<string name="register_on_openplacereviews">Registreer u op <string name="register_on_openplacereviews">Registreer u op
\nOpenPlaceReviews.org</string> \nOpenPlaceReviews.org</string>
<string name="register_on_openplacereviews_desc">Foto\'s zijn afkomstig van het open data-project OpenPlaceReviews.org. Om uw foto\'s te uploaden, moet u zich aanmelden op deze website.</string> <string name="register_on_openplacereviews_desc">Foto\'s zijn afkomstig van het open data-project OpenPlaceReviews.org. Om uw foto\'s te uploaden, moet u zich aanmelden op hun website.</string>
<string name="register_opr_create_new_account">Maak een nieuw account aan</string> <string name="register_opr_create_new_account">Maak een nieuw account aan</string>
<string name="register_opr_have_account">Ik heb al een account</string> <string name="register_opr_have_account">Ik heb al een account</string>
<string name="ltr_or_rtl_combine_via_star">%1$s * %2$s</string> <string name="ltr_or_rtl_combine_via_star">%1$s * %2$s</string>
@ -3954,4 +3954,43 @@
<string name="routing_attr_allow_intermittent_name">Laat regelmatig droogvallende waterwegen toe</string> <string name="routing_attr_allow_intermittent_name">Laat regelmatig droogvallende waterwegen toe</string>
<string name="shared_string_enter_param">Geef een parameter</string> <string name="shared_string_enter_param">Geef een parameter</string>
<string name="keep_it_empty_if_not">Laat leeg indien niet</string> <string name="keep_it_empty_if_not">Laat leeg indien niet</string>
<string name="analyze_by_intervals">Analyseren met intervallen</string>
<string name="upload_to_openstreetmap">Uploaden naar OpenStreetMap</string>
<string name="edit_track">GPX-track bewerken</string>
<string name="rename_track">GPX-track hernoemen</string>
<string name="change_folder">Map wijzigen</string>
<string name="shared_string_sec">sec</string>
<string name="announcement_time_approach">Bij nadering</string>
<string name="announcement_time_passing">Bij passeren</string>
<string name="announcement_time_prepare_long">Ruime vooraankondiging</string>
<string name="announcement_time_prepare">Vooraankondiging</string>
<string name="announcement_time_off_route">Afwijking van de route</string>
<string name="announcement_time_arrive">Aankomst op bestemming</string>
<string name="shared_string_turn">Bocht</string>
<string name="announcement_time_intervals">Tijd- en afstandsintervallen</string>
<string name="announcement_time_descr">De aankondigingstijd van verschillende aankondigen hangt af van type en huidige snelheid.</string>
<string name="announcement_time_title">Aankondigingstijd</string>
<string name="start_recording">Start opname</string>
<string name="show_track_on_map">Toon GPX-track op de kaart</string>
<string name="routing_engine_vehicle_type_wheelchair">Rolstoel</string>
<string name="routing_engine_vehicle_type_hiking">Hiken</string>
<string name="routing_engine_vehicle_type_walking">Wandelen</string>
<string name="routing_engine_vehicle_type_cycling_electric">Elektische Fiets</string>
<string name="routing_engine_vehicle_type_cycling_mountain">Mountainbiken</string>
<string name="routing_engine_vehicle_type_cycling_road">Fietsen</string>
<string name="routing_engine_vehicle_type_cycling_regular">Normaal Fietsen</string>
<string name="routing_engine_vehicle_type_hgv">Vrachtwagen</string>
<string name="routing_engine_vehicle_type_small_truck">Kleine Vrachtwagen</string>
<string name="routing_engine_vehicle_type_truck">Vrachtwagen</string>
<string name="routing_engine_vehicle_type_scooter">Scooter</string>
<string name="routing_engine_vehicle_type_racingbike">Racefiets</string>
<string name="routing_engine_vehicle_type_mtb">Mountainbike</string>
<string name="message_server_error">Server fout: %1$s</string>
<string name="message_name_is_already_exists">Deze naam bestaat al</string>
<string name="delete_online_routing_engine">Deze Online-routeservice verwijderen\?</string>
<string name="context_menu_read_full">Helemaal inlezen</string>
<string name="context_menu_edit_descr">Beschrijving bewerken</string>
<string name="delete_waypoints">Waypoints verwijderen</string>
<string name="copy_to_map_markers">Kopiëren naar Markeervlaggetjes</string>
<string name="copy_to_map_favorites">Kopiëren naar Favorieten</string>
</resources> </resources>

View file

@ -1185,7 +1185,7 @@
<string name="no_route">Нет маршрута</string> <string name="no_route">Нет маршрута</string>
<string name="gps_not_available">Включите GPS в настройках</string> <string name="gps_not_available">Включите GPS в настройках</string>
<string name="map_widget_show_destination_arrow">Показывать направление к пункту назначения</string> <string name="map_widget_show_destination_arrow">Показывать направление к пункту назначения</string>
<string name="enable_plugin_monitoring_services">Включите плагин «Запись поездки» для использования сервисов мониторинга (запись GPX, сопровождение в реальном времени)</string> <string name="enable_plugin_monitoring_services">Включите плагин «Запись поездки» для использования служб мониторинга (запись GPX, сопровождение в реальном времени)</string>
<string name="rendering_attr_roadColors_description">Выберите цветовую схему дорог:</string> <string name="rendering_attr_roadColors_description">Выберите цветовую схему дорог:</string>
<string name="rendering_attr_roadColors_name">Цветовая схема дорог</string> <string name="rendering_attr_roadColors_name">Цветовая схема дорог</string>
<string name="route_descr_lat_lon">Ш %1$.3f Д %2$.3f</string> <string name="route_descr_lat_lon">Ш %1$.3f Д %2$.3f</string>
@ -1731,7 +1731,7 @@
<string name="shared_string_selected_lowercase">выбранные</string> <string name="shared_string_selected_lowercase">выбранные</string>
<string name="shared_string_none">Нет</string> <string name="shared_string_none">Нет</string>
<string name="shared_string_deselect">Отменить выбор</string> <string name="shared_string_deselect">Отменить выбор</string>
<string name="shared_string_deselect_all">Отменить выбор всех</string> <string name="shared_string_deselect_all">Отменить выбор</string>
<string name="shared_string_share">Поделиться</string> <string name="shared_string_share">Поделиться</string>
<string name="shared_string_my_places">Мои места</string> <string name="shared_string_my_places">Мои места</string>
<string name="shared_string_my_favorites">Избранные</string> <string name="shared_string_my_favorites">Избранные</string>
@ -3225,7 +3225,7 @@
\n</string> \n</string>
<string name="contour_lines_and_hillshade">Контурные линии и затенение рельефа</string> <string name="contour_lines_and_hillshade">Контурные линии и затенение рельефа</string>
<string name="routing_attr_prefer_unpaved_name">Предпочитать грунтовые дороги</string> <string name="routing_attr_prefer_unpaved_name">Предпочитать грунтовые дороги</string>
<string name="routing_attr_prefer_unpaved_description">Предпочитать грунтовые дороги.</string> <string name="routing_attr_prefer_unpaved_description">Предпочитать грунтовые дороги</string>
<string name="update_all_maps_q">Вы уверены, что хотите обновить все карты (%1$d)\?</string> <string name="update_all_maps_q">Вы уверены, что хотите обновить все карты (%1$d)\?</string>
<string name="apply_preference_to_all_profiles">Вы можете применить это ко всем или только к выбранному профилю.</string> <string name="apply_preference_to_all_profiles">Вы можете применить это ко всем или только к выбранному профилю.</string>
<string name="shared_preference">Общий</string> <string name="shared_preference">Общий</string>
@ -3713,7 +3713,7 @@
<string name="quick_action_remove_next_destination">Удалить ближайший пункт</string> <string name="quick_action_remove_next_destination">Удалить ближайший пункт</string>
<string name="please_provide_point_name_error">Задайте название точки</string> <string name="please_provide_point_name_error">Задайте название точки</string>
<string name="quick_action_remove_next_destination_descr">Следующая точка маршрута будет удалена. Если это конечный пункт, навигация завершится.</string> <string name="quick_action_remove_next_destination_descr">Следующая точка маршрута будет удалена. Если это конечный пункт, навигация завершится.</string>
<string name="plugin_wikipedia_description">Информация о достопримечательностях из Википедии. Ваш карманный офлайн-путеводитель — просто включите плагин Википедии и читайте об объектах вокруг вас.</string> <string name="plugin_wikipedia_description">Информация о достопримечательностях из Википедии, карманного автономного путеводителя со статьями о местах и направлениях.</string>
<string name="search_download_wikipedia_maps">Скачать карты Википедии</string> <string name="search_download_wikipedia_maps">Скачать карты Википедии</string>
<string name="app_mode_enduro_motorcycle">Эндуро мотоцикл</string> <string name="app_mode_enduro_motorcycle">Эндуро мотоцикл</string>
<string name="app_mode_motor_scooter">Мотороллер</string> <string name="app_mode_motor_scooter">Мотороллер</string>
@ -3937,4 +3937,53 @@
<string name="shared_string_folders">Папки</string> <string name="shared_string_folders">Папки</string>
<string name="select_folder">Выбор папки</string> <string name="select_folder">Выбор папки</string>
<string name="select_folder_descr">Выберите папку или добавьте новую</string> <string name="select_folder_descr">Выберите папку или добавьте новую</string>
<string name="voice_prompts_timetable">Время голосовых подсказок</string>
<string name="elevation_data">Можно использовать данные о высотах для учёта подъёма/спуска поездки</string>
<string name="routing_attr_prefer_hiking_routes_description">Предпочитать пешеходные маршруты</string>
<string name="shared_string_subtype">Подтип</string>
<string name="shared_string_enter_param">Введите параметр</string>
<string name="routing_engine_vehicle_type_walking">Пешеход</string>
<string name="routing_engine_vehicle_type_scooter">Самокат</string>
<string name="analyze_by_intervals">Анализ по интервалам (по отдельности)</string>
<string name="announcement_time_prepare_long">Заблаговременно</string>
<string name="announcement_time_prepare">Заранее</string>
<string name="announcement_time_passing">При прохождении</string>
<string name="announcement_time_approach">При приближении</string>
<string name="routing_engine_vehicle_type_wheelchair">Инвалидная коляска</string>
<string name="routing_engine_vehicle_type_hiking">Пеший туризм</string>
<string name="routing_engine_vehicle_type_small_truck">Малый грузовик</string>
<string name="routing_engine_vehicle_type_truck">Грузовик</string>
<string name="copy_to_map_markers">Копировать в маркеры</string>
<string name="copy_to_map_favorites">Копировать в избранное</string>
<string name="delete_online_routing_engine">Удалить онлайн-маршрутизатор\?</string>
<string name="routing_attr_prefer_hiking_routes_name">Предпочитать пешеходные маршруты</string>
<string name="add_online_routing_engine">Добавить онлайн-маршрутизатор</string>
<string name="edit_online_routing_engine">Редактировать онлайн-маршрутизатор</string>
<string name="shared_string_api_key">Ключ API</string>
<string name="shared_string_server_url">URL-адрес сервера</string>
<string name="keep_it_empty_if_not">Оставить пустым, если не</string>
<string name="online_routing_example_hint">URL со всеми параметрами будет выглядеть так:</string>
<string name="test_route_calculation">Тест расчёта маршрута</string>
<string name="routing_engine_vehicle_type_car">Автомобиль</string>
<string name="online_routing_engine">Онлайн-маршрутизатор</string>
<string name="online_routing_engines">Онлайн-маршрутизаторы</string>
<string name="shared_string_empty">Пусто</string>
<string name="upload_to_openstreetmap">Отправить в OpenStreetMap</string>
<string name="edit_track">Редактировать трек</string>
<string name="rename_track">Переименовать трек</string>
<string name="change_folder">Изменить папку</string>
<string name="shared_string_sec">сек.</string>
<string name="announcement_time_off_route">Вне маршрута</string>
<string name="announcement_time_arrive">Прибытие в пункт назначения</string>
<string name="shared_string_turn">Поворот</string>
<string name="announcement_time_intervals">Интервалы времени и расстояния</string>
<string name="announcement_time_title">Время объявления</string>
<string name="announcement_time_descr">Время объявления различных голосовых подсказок зависит от типа подсказки, текущей скорости навигации и скорости навигации по умолчанию.</string>
<string name="start_recording">Начать запись</string>
<string name="show_track_on_map">Показать трек на карте</string>
<string name="message_server_error">Ошибка сервера: %1$s</string>
<string name="message_name_is_already_exists">Имя уже существует</string>
<string name="context_menu_read_full">Читать полностью</string>
<string name="context_menu_edit_descr">Редактировать описание</string>
<string name="delete_waypoints">Удалить путевые точки</string>
</resources> </resources>

View file

@ -3995,4 +3995,10 @@
<string name="message_server_error">Chyba servera: %1$s</string> <string name="message_server_error">Chyba servera: %1$s</string>
<string name="message_name_is_already_exists">Názov už existuje</string> <string name="message_name_is_already_exists">Názov už existuje</string>
<string name="shared_string_turn">Odbočiť</string> <string name="shared_string_turn">Odbočiť</string>
<string name="delete_online_routing_engine">Odstrániť túto online navigačnú službu\?</string>
<string name="context_menu_read_full">Prečítať celé</string>
<string name="context_menu_edit_descr">Upraviť popis</string>
<string name="delete_waypoints">Odstrániť body trasy</string>
<string name="copy_to_map_markers">Skopírovať do mapových značiek</string>
<string name="copy_to_map_favorites">Skopírovať do obľúbených</string>
</resources> </resources>

View file

@ -3305,4 +3305,8 @@
<string name="routing_engine_vehicle_type_mtb">MTB</string> <string name="routing_engine_vehicle_type_mtb">MTB</string>
<string name="message_server_error">Napaka strežnika: %1$s</string> <string name="message_server_error">Napaka strežnika: %1$s</string>
<string name="message_name_is_already_exists">To ime že obstaja</string> <string name="message_name_is_already_exists">To ime že obstaja</string>
<string name="context_menu_read_full">Preberi v celoti</string>
<string name="context_menu_edit_descr">Uredi opis</string>
<string name="delete_waypoints">Brisanje točk</string>
<string name="copy_to_map_markers">Kopiraj na oznake na zemljevidu</string>
</resources> </resources>

View file

@ -3993,4 +3993,10 @@
<string name="routing_engine_vehicle_type_mtb">登山車</string> <string name="routing_engine_vehicle_type_mtb">登山車</string>
<string name="message_server_error">伺服器錯誤:%1$s</string> <string name="message_server_error">伺服器錯誤:%1$s</string>
<string name="message_name_is_already_exists">名稱已存在</string> <string name="message_name_is_already_exists">名稱已存在</string>
<string name="delete_online_routing_engine">刪除此線上路線引擎?</string>
<string name="context_menu_read_full">讀取全部</string>
<string name="context_menu_edit_descr">編輯描述</string>
<string name="delete_waypoints">刪除航點</string>
<string name="copy_to_map_markers">複製到地圖標記</string>
<string name="copy_to_map_favorites">複製到收藏</string>
</resources> </resources>

View file

@ -2444,7 +2444,7 @@
<string name="average">Average</string> <string name="average">Average</string>
<string name="of">%1$d of %2$d</string> <string name="of">%1$d of %2$d</string>
<string name="ascent_descent">Ascent/Descent</string> <string name="ascent_descent">Ascent/Descent</string>
<string name="moving_time">Moving time</string> <string name="moving_time">Time in motion</string>
<string name="max_min">Max/Min</string> <string name="max_min">Max/Min</string>
<string name="min_max">Min/Max</string> <string name="min_max">Min/Max</string>
<string name="index_tours">Tours</string> <string name="index_tours">Tours</string>

View file

@ -2296,13 +2296,13 @@ public class OsmandAidlApi {
return false; return false;
} }
public boolean importProfileV2(final Uri profileUri, ArrayList<String> settingsTypeKeys, boolean replace, public boolean importProfileV2(final Uri profileUri, List<String> settingsTypeKeys, boolean replace,
String latestChanges, int version) { boolean silent, String latestChanges, int version) {
if (profileUri != null) { if (profileUri != null) {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putStringArrayList(SettingsHelper.SETTINGS_TYPE_LIST_KEY, settingsTypeKeys); bundle.putStringArrayList(SettingsHelper.SETTINGS_TYPE_LIST_KEY, new ArrayList<>(settingsTypeKeys));
bundle.putBoolean(REPLACE_KEY, replace); bundle.putBoolean(REPLACE_KEY, replace);
bundle.putBoolean(SILENT_IMPORT_KEY, true); bundle.putBoolean(SILENT_IMPORT_KEY, silent);
bundle.putString(SettingsHelper.SETTINGS_LATEST_CHANGES_KEY, latestChanges); bundle.putString(SettingsHelper.SETTINGS_LATEST_CHANGES_KEY, latestChanges);
bundle.putInt(SettingsHelper.SETTINGS_VERSION_KEY, version); bundle.putInt(SettingsHelper.SETTINGS_VERSION_KEY, version);

View file

@ -1270,7 +1270,7 @@ public class OsmandAidlServiceV2 extends Service implements AidlCallbackListener
try { try {
OsmandAidlApi api = getApi("importProfile"); OsmandAidlApi api = getApi("importProfile");
return api != null && api.importProfileV2(params.getProfileSettingsUri(), params.getSettingsTypeKeys(), return api != null && api.importProfileV2(params.getProfileSettingsUri(), params.getSettingsTypeKeys(),
params.isReplace(), params.getLatestChanges(), params.getVersion()); params.isReplace(), params.isSilent(), params.getLatestChanges(), params.getVersion());
} catch (Exception e) { } catch (Exception e) {
handleException(e); handleException(e);
return false; return false;

View file

@ -210,6 +210,8 @@ public class OsmandApplication extends MultiDexApplication {
File mapillaryVectorTilesPath = new File(tilesPath, TileSourceManager.getMapillaryVectorSource().getName()); File mapillaryVectorTilesPath = new File(tilesPath, TileSourceManager.getMapillaryVectorSource().getName());
Algorithms.removeAllFiles(mapillaryRasterTilesPath); Algorithms.removeAllFiles(mapillaryRasterTilesPath);
Algorithms.removeAllFiles(mapillaryVectorTilesPath); Algorithms.removeAllFiles(mapillaryVectorTilesPath);
// Remove travel sqlite db files
removeSqliteDbTravelFiles();
} }
checkPreferredLocale(); checkPreferredLocale();
@ -234,6 +236,17 @@ public class OsmandApplication extends MultiDexApplication {
return externalStorageDirectoryReadOnly; return externalStorageDirectoryReadOnly;
} }
private void removeSqliteDbTravelFiles() {
File[] files = getAppPath(IndexConstants.WIKIVOYAGE_INDEX_DIR).listFiles();
if (files != null) {
for (File file : files) {
if (file.getName().endsWith(IndexConstants.BINARY_WIKIVOYAGE_MAP_INDEX_EXT)) {
file.delete();
}
}
}
}
@Override @Override
protected void attachBaseContext(Context base) { protected void attachBaseContext(Context base) {
super.attachBaseContext(base); super.attachBaseContext(base);

View file

@ -651,7 +651,9 @@ public class MapActivity extends OsmandActionBarActivity implements DownloadEven
protected void onNewIntent(final Intent intent) { protected void onNewIntent(final Intent intent) {
super.onNewIntent(intent); super.onNewIntent(intent);
setIntent(intent); setIntent(intent);
intentHelper.parseLaunchIntents(); if (!intentHelper.parseLaunchIntents()) {
intentHelper.parseContentIntent();
}
} }
@Override @Override
@ -1200,7 +1202,7 @@ public class MapActivity extends OsmandActionBarActivity implements DownloadEven
selectedGpxFile = app.getSelectedGpxHelper().getSelectedFileByPath(gpxFile.path); selectedGpxFile = app.getSelectedGpxHelper().getSelectedFileByPath(gpxFile.path);
} }
TrackAppearanceFragment.showInstance(this, selectedGpxFile); TrackAppearanceFragment.showInstance(this, selectedGpxFile, null);
} else if (toShow instanceof QuadRect) { } else if (toShow instanceof QuadRect) {
QuadRect qr = (QuadRect) toShow; QuadRect qr = (QuadRect) toShow;
mapView.fitRectToMap(qr.left, qr.right, qr.top, qr.bottom, (int) qr.width(), (int) qr.height(), 0); mapView.fitRectToMap(qr.left, qr.right, qr.top, qr.bottom, (int) qr.width(), (int) qr.height(), 0);

View file

@ -928,7 +928,7 @@ public class MapActivityActions implements DialogProvider {
MapActivity.clearPrevActivityIntent(); MapActivity.clearPrevActivityIntent();
TravelHelper travelHelper = getMyApplication().getTravelHelper(); TravelHelper travelHelper = getMyApplication().getTravelHelper();
travelHelper.initializeDataOnAppStartup(); travelHelper.initializeDataOnAppStartup();
if (!travelHelper.isAnyTravelBookPresent()) { if (!travelHelper.isAnyTravelBookPresent() && !travelHelper.getBookmarksHelper().hasSavedArticles()) {
WikivoyageWelcomeDialogFragment.showInstance(mapActivity.getSupportFragmentManager()); WikivoyageWelcomeDialogFragment.showInstance(mapActivity.getSupportFragmentManager());
} else { } else {
Intent intent = new Intent(mapActivity, WikivoyageExploreActivity.class); Intent intent = new Intent(mapActivity, WikivoyageExploreActivity.class);

View file

@ -40,7 +40,7 @@ import java.util.Map;
public class SavingTrackHelper extends SQLiteOpenHelper { public class SavingTrackHelper extends SQLiteOpenHelper {
public final static String DATABASE_NAME = "tracks"; //$NON-NLS-1$ public final static String DATABASE_NAME = "tracks"; //$NON-NLS-1$
public final static int DATABASE_VERSION = 6; public final static int DATABASE_VERSION = 7;
public final static String TRACK_NAME = "track"; //$NON-NLS-1$ public final static String TRACK_NAME = "track"; //$NON-NLS-1$
public final static String TRACK_COL_DATE = "date"; //$NON-NLS-1$ public final static String TRACK_COL_DATE = "date"; //$NON-NLS-1$
@ -59,6 +59,8 @@ public class SavingTrackHelper extends SQLiteOpenHelper {
public final static String POINT_COL_CATEGORY = "category"; //$NON-NLS-1$ public final static String POINT_COL_CATEGORY = "category"; //$NON-NLS-1$
public final static String POINT_COL_DESCRIPTION = "description"; //$NON-NLS-1$ public final static String POINT_COL_DESCRIPTION = "description"; //$NON-NLS-1$
public final static String POINT_COL_COLOR = "color"; //$NON-NLS-1$ public final static String POINT_COL_COLOR = "color"; //$NON-NLS-1$
public final static String POINT_COL_ICON = "icon"; //$NON-NLS-1$
public final static String POINT_COL_BACKGROUND = "background"; //$NON-NLS-1$
public final static float NO_HEADING = -1.0f; public final static float NO_HEADING = -1.0f;
@ -92,7 +94,10 @@ public class SavingTrackHelper extends SQLiteOpenHelper {
+ TRACK_COL_DATE + ", " + TRACK_COL_HEADING + ")" + TRACK_COL_DATE + ", " + TRACK_COL_HEADING + ")"
+ " VALUES (?, ?, ?, ?, ?, ?, ?)"; //$NON-NLS-1$ //$NON-NLS-2$ + " VALUES (?, ?, ?, ?, ?, ?, ?)"; //$NON-NLS-1$ //$NON-NLS-2$
insertPointsScript = "INSERT INTO " + POINT_NAME + " VALUES (?, ?, ?, ?, ?, ?, ?)"; //$NON-NLS-1$ //$NON-NLS-2$ insertPointsScript = "INSERT INTO " + POINT_NAME + " (" + POINT_COL_LAT + ", " + POINT_COL_LON + ", "
+ POINT_COL_DATE + ", " + POINT_COL_DESCRIPTION + ", " + POINT_COL_NAME + ", "
+ POINT_COL_CATEGORY + ", " + POINT_COL_COLOR + ", " + POINT_COL_ICON + ", "
+ POINT_COL_BACKGROUND + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; //$NON-NLS-1$ //$NON-NLS-2$
} }
@Override @Override
@ -112,7 +117,7 @@ public class SavingTrackHelper extends SQLiteOpenHelper {
try { try {
db.execSQL("CREATE TABLE " + POINT_NAME + " (" + POINT_COL_LAT + " double, " + POINT_COL_LON + " double, " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ db.execSQL("CREATE TABLE " + POINT_NAME + " (" + POINT_COL_LAT + " double, " + POINT_COL_LON + " double, " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ POINT_COL_DATE + " long, " + POINT_COL_DESCRIPTION + " text, " + POINT_COL_NAME + " text, " + POINT_COL_DATE + " long, " + POINT_COL_DESCRIPTION + " text, " + POINT_COL_NAME + " text, "
+ POINT_COL_CATEGORY + " text, " + POINT_COL_COLOR + " long" + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + POINT_COL_CATEGORY + " text, " + POINT_COL_COLOR + " long, " + POINT_COL_ICON + " text, " + POINT_COL_BACKGROUND + " text )"); //$NON-NLS-1$ //$NON-NLS-2$
} catch (RuntimeException e) { } catch (RuntimeException e) {
// ignore if already exists // ignore if already exists
} }
@ -136,6 +141,10 @@ public class SavingTrackHelper extends SQLiteOpenHelper {
if (oldVersion < 6) { if (oldVersion < 6) {
db.execSQL("ALTER TABLE " + TRACK_NAME + " ADD " + TRACK_COL_HEADING + " float"); db.execSQL("ALTER TABLE " + TRACK_NAME + " ADD " + TRACK_COL_HEADING + " float");
} }
if (oldVersion < 7) {
db.execSQL("ALTER TABLE " + POINT_NAME + " ADD " + POINT_COL_ICON + " text");
db.execSQL("ALTER TABLE " + POINT_NAME + " ADD " + POINT_COL_BACKGROUND + " text");
}
} }
@ -294,7 +303,8 @@ public class SavingTrackHelper extends SQLiteOpenHelper {
private void collectDBPoints(SQLiteDatabase db, Map<String, GPXFile> dataTracks) { private void collectDBPoints(SQLiteDatabase db, Map<String, GPXFile> dataTracks) {
Cursor query = db.rawQuery("SELECT " + POINT_COL_LAT + "," + POINT_COL_LON + "," + POINT_COL_DATE + "," //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ Cursor query = db.rawQuery("SELECT " + POINT_COL_LAT + "," + POINT_COL_LON + "," + POINT_COL_DATE + "," //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ POINT_COL_DESCRIPTION + "," + POINT_COL_NAME + "," + POINT_COL_CATEGORY + "," + POINT_COL_COLOR + " FROM " + POINT_NAME+" ORDER BY " + POINT_COL_DATE +" ASC", null); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + POINT_COL_DESCRIPTION + "," + POINT_COL_NAME + "," + POINT_COL_CATEGORY + "," + POINT_COL_COLOR + ","
+ POINT_COL_ICON + "," + POINT_COL_BACKGROUND + " FROM " + POINT_NAME + " ORDER BY " + POINT_COL_DATE + " ASC", null); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
if (query.moveToFirst()) { if (query.moveToFirst()) {
do { do {
WptPt pt = new WptPt(); WptPt pt = new WptPt();
@ -309,6 +319,8 @@ public class SavingTrackHelper extends SQLiteOpenHelper {
if (color != 0) { if (color != 0) {
pt.setColor(color); pt.setColor(color);
} }
pt.setIconName(query.getString(7));
pt.setBackgroundType(query.getString(8));
// check if name is extension (needed for audio/video plugin & josm integration) // check if name is extension (needed for audio/video plugin & josm integration)
if (pt.name != null && pt.name.length() > 4 && pt.name.charAt(pt.name.length() - 4) == '.') { if (pt.name != null && pt.name.length() > 4 && pt.name.charAt(pt.name.length() - 4) == '.') {
@ -370,7 +382,7 @@ public class SavingTrackHelper extends SQLiteOpenHelper {
if (!newInterval) { if (!newInterval) {
segment.points.add(pt); segment.points.add(pt);
} }
String date = new SimpleDateFormat("yyyy-MM-dd", Locale.US).format(new Date(time));; //$NON-NLS-1$ String date = new SimpleDateFormat("yyyy-MM-dd", Locale.US).format(new Date(time)); //$NON-NLS-1$
if (dataTracks.containsKey(date)) { if (dataTracks.containsKey(date)) {
GPXFile gpx = dataTracks.get(date); GPXFile gpx = dataTracks.get(date);
gpx.tracks.add(track); gpx.tracks.add(track);
@ -529,7 +541,7 @@ public class SavingTrackHelper extends SQLiteOpenHelper {
ctx.getSelectedGpxHelper().addPoint(pt, currentTrack.getModifiableGpxFile()); ctx.getSelectedGpxHelper().addPoint(pt, currentTrack.getModifiableGpxFile());
currentTrack.getModifiableGpxFile().modifiedTime = time; currentTrack.getModifiableGpxFile().modifiedTime = time;
points++; points++;
execWithClose(insertPointsScript, new Object[] { lat, lon, time, description, name, category, color }); execWithClose(insertPointsScript, new Object[]{lat, lon, time, description, name, category, color, iconName, backgroundName});
return pt; return pt;
} }
@ -549,6 +561,8 @@ public class SavingTrackHelper extends SQLiteOpenHelper {
params.add(name); params.add(name);
params.add(category); params.add(category);
params.add(color); params.add(color);
params.add(iconName);
params.add(iconBackground);
params.add(pt.getLatitude()); params.add(pt.getLatitude());
params.add(pt.getLongitude()); params.add(pt.getLongitude());
@ -563,7 +577,9 @@ public class SavingTrackHelper extends SQLiteOpenHelper {
+ POINT_COL_DESCRIPTION + "=?, " + POINT_COL_DESCRIPTION + "=?, "
+ POINT_COL_NAME + "=?, " + POINT_COL_NAME + "=?, "
+ POINT_COL_CATEGORY + "=?, " + POINT_COL_CATEGORY + "=?, "
+ POINT_COL_COLOR + "=? " + POINT_COL_COLOR + "=?, "
+ POINT_COL_ICON + "=?, "
+ POINT_COL_BACKGROUND + "=? "
+ "WHERE " + "WHERE "
+ POINT_COL_LAT + "=? AND " + POINT_COL_LAT + "=? AND "
+ POINT_COL_LON + "=? AND " + POINT_COL_LON + "=? AND "

View file

@ -946,6 +946,11 @@ public abstract class ContextMenuFragment extends BaseOsmAndFragment
@TargetApi(Build.VERSION_CODES.JELLY_BEAN) @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
protected void runLayoutListener() { protected void runLayoutListener() {
runLayoutListener(null);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
protected void runLayoutListener(final Runnable runnable) {
if (view != null) { if (view != null) {
ViewTreeObserver vto = view.getViewTreeObserver(); ViewTreeObserver vto = view.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@ -974,6 +979,9 @@ public abstract class ContextMenuFragment extends BaseOsmAndFragment
int menuState = getCurrentMenuState(); int menuState = getCurrentMenuState();
listener.onContextMenuStateChanged(ContextMenuFragment.this, menuState, menuState); listener.onContextMenuStateChanged(ContextMenuFragment.this, menuState, menuState);
} }
if (runnable != null) {
runnable.run();
}
} }
} }
}); });

View file

@ -292,10 +292,6 @@ public class DownloadActivityType {
public IndexItem parseIndexItem(OsmandApplication ctx, XmlPullParser parser) { public IndexItem parseIndexItem(OsmandApplication ctx, XmlPullParser parser) {
if (TRAVEL_FILE == this && !Version.isDeveloperVersion(ctx)) {
//todo remove "if" when .travel.obf will be used in production
return null;
}
String name = parser.getAttributeValue(null, "name"); //$NON-NLS-1$ String name = parser.getAttributeValue(null, "name"); //$NON-NLS-1$
if (!isAccepted(name)) { if (!isAccepted(name)) {
return null; return null;

View file

@ -102,7 +102,7 @@ class SettingsImportTask extends BaseLoadAsyncTask<Void, Void, String> {
} }
} else { } else {
Map<ExportSettingsType, List<?>> allSettingsMap = getSettingsToOperate(pluginIndependentItems, false); Map<ExportSettingsType, List<?>> allSettingsMap = getSettingsToOperate(pluginIndependentItems, false);
List<SettingsItem> settingsList = settingsHelper.getFilteredSettingsItems(allSettingsMap, settingsTypes, false); List<SettingsItem> settingsList = settingsHelper.getFilteredSettingsItems(allSettingsMap, settingsTypes, pluginIndependentItems, false);
settingsHelper.checkDuplicates(file, settingsList, settingsList, getDuplicatesListener(file, replace)); settingsHelper.checkDuplicates(file, settingsList, settingsList, getDuplicatesListener(file, replace));
} }
} }

View file

@ -421,7 +421,7 @@ public class MenuBuilder {
new Thread(new Runnable() { new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
if (openDBAPI.checkPrivateKeyValid(baseUrl, name, privateKey)) { if (openDBAPI.checkPrivateKeyValid(app, baseUrl, name, privateKey)) {
app.runInUIThread(new Runnable() { app.runInUIThread(new Runnable() {
@Override @Override
public void run() { public void run() {

View file

@ -137,12 +137,12 @@ public class UploadPhotosAsyncTask extends AsyncTask<Void, Integer, Void> {
try { try {
StringBuilder error = new StringBuilder(); StringBuilder error = new StringBuilder();
String privateKey = app.getSettings().OPR_ACCESS_TOKEN.get(); String privateKey = app.getSettings().OPR_ACCESS_TOKEN.get();
String username = app.getSettings().OPR_USERNAME.get(); String name = app.getSettings().OPR_BLOCKCHAIN_NAME.get();
res = openDBAPI.uploadImage( res = openDBAPI.uploadImage(
placeId, placeId,
baseUrl, baseUrl,
privateKey, privateKey,
username, name,
response, error); response, error);
if (res != 200) { if (res != 200) {
app.showToastMessage(error.toString()); app.showToastMessage(error.toString());
@ -179,7 +179,7 @@ public class UploadPhotosAsyncTask extends AsyncTask<Void, Integer, Void> {
String baseUrl = OPRConstants.getBaseUrl(app); String baseUrl = OPRConstants.getBaseUrl(app);
String name = app.getSettings().OPR_USERNAME.get(); String name = app.getSettings().OPR_USERNAME.get();
String privateKey = app.getSettings().OPR_ACCESS_TOKEN.get(); String privateKey = app.getSettings().OPR_ACCESS_TOKEN.get();
if (openDBAPI.checkPrivateKeyValid(baseUrl, name, privateKey)) { if (openDBAPI.checkPrivateKeyValid(app, baseUrl, name, privateKey)) {
app.showToastMessage(R.string.cannot_upload_image); app.showToastMessage(R.string.cannot_upload_image);
} else { } else {
app.runInUIThread(new Runnable() { app.runInUIThread(new Runnable() {

View file

@ -42,9 +42,10 @@ public class SelectedGpxMenuController extends MenuController {
leftTitleButtonController = new TitleButtonController() { leftTitleButtonController = new TitleButtonController() {
@Override @Override
public void buttonPressed() { public void buttonPressed() {
SelectedGpxFile selectedGpxFile = selectedGpxPoint.getSelectedGpxFile(); mapContextMenu.hide(false);
mapActivity.getContextMenu().hide(false); WptPt wptPt = selectedGpxPoint.selectedPoint;
TrackMenuFragment.showInstance(mapActivity, selectedGpxFile.getGpxFile().path, selectedGpxFile.isShowCurrentTrack()); LatLon latLon = new LatLon(wptPt.lat, wptPt.lon);
TrackMenuFragment.showInstance(mapActivity, selectedGpxPoint.getSelectedGpxFile(), latLon);
} }
}; };
leftTitleButtonController.caption = mapActivity.getString(R.string.shared_string_open_track); leftTitleButtonController.caption = mapActivity.getString(R.string.shared_string_open_track);

View file

@ -90,7 +90,7 @@ public class TripRecordingBottomSheet extends MenuBottomSheetDialogFragment {
if (mapActivity != null) { if (mapActivity != null) {
hide(); hide();
SelectedGpxFile selectedGpxFile = app.getSavingTrackHelper().getCurrentTrack(); SelectedGpxFile selectedGpxFile = app.getSavingTrackHelper().getCurrentTrack();
TrackAppearanceFragment.showInstance(mapActivity, selectedGpxFile); TrackAppearanceFragment.showInstance(mapActivity, selectedGpxFile, TripRecordingBottomSheet.this);
} }
} }
}); });

View file

@ -30,6 +30,7 @@ import net.osmand.GPXUtilities.GPXFile;
import net.osmand.GPXUtilities.WptPt; import net.osmand.GPXUtilities.WptPt;
import net.osmand.data.FavouritePoint; import net.osmand.data.FavouritePoint;
import net.osmand.plus.FavouritesDbHelper; import net.osmand.plus.FavouritesDbHelper;
import net.osmand.plus.GpxSelectionHelper;
import net.osmand.plus.GpxSelectionHelper.GpxDisplayGroup; import net.osmand.plus.GpxSelectionHelper.GpxDisplayGroup;
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem; import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem;
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItemType; import net.osmand.plus.GpxSelectionHelper.GpxDisplayItemType;
@ -70,19 +71,32 @@ public class EditTrackGroupDialogFragment extends MenuBottomSheetDialogFragment
public static final String TAG = EditTrackGroupDialogFragment.class.getSimpleName(); public static final String TAG = EditTrackGroupDialogFragment.class.getSimpleName();
private OsmandApplication app; private OsmandApplication app;
private GpxSelectionHelper selectedGpxHelper;
private MapMarkersHelper mapMarkersHelper;
private GpxDisplayGroup group; private GpxDisplayGroup group;
@Override @Override
public void createMenuItems(Bundle savedInstanceState) { public void createMenuItems(Bundle savedInstanceState) {
app = requiredMyApplication();
if (group == null) { if (group == null) {
return; return;
} }
app = requiredMyApplication();
selectedGpxHelper = app.getSelectedGpxHelper();
mapMarkersHelper = app.getMapMarkersHelper();
items.add(new TitleItem(getCategoryName(app, group.getName()))); items.add(new TitleItem(getCategoryName(app, group.getName())));
GPXFile gpxFile = group.getGpx();
boolean currentTrack = group.getGpx().showCurrentTrack;
SelectedGpxFile selectedGpxFile;
if (currentTrack) {
selectedGpxFile = selectedGpxHelper.getSelectedCurrentRecordingTrack();
} else {
selectedGpxFile = selectedGpxHelper.getSelectedFileByPath(gpxFile.path);
}
boolean trackPoints = group.getType() == GpxDisplayItemType.TRACK_POINTS; boolean trackPoints = group.getType() == GpxDisplayItemType.TRACK_POINTS;
SelectedGpxFile selectedGpxFile = app.getSelectedGpxHelper().getSelectedFileByPath(group.getGpx().path);
if (trackPoints && selectedGpxFile != null) { if (trackPoints && selectedGpxFile != null) {
items.add(createShowOnMapItem(selectedGpxFile)); items.add(createShowOnMapItem(selectedGpxFile));
} }
@ -92,7 +106,9 @@ public class EditTrackGroupDialogFragment extends MenuBottomSheetDialogFragment
} }
items.add(new OptionsDividerItem(app)); items.add(new OptionsDividerItem(app));
// items.add(createCopyToMarkersItem()); if (!currentTrack) {
items.add(createCopyToMarkersItem(gpxFile));
}
items.add(createCopyToFavoritesItem()); items.add(createCopyToFavoritesItem());
items.add(new OptionsDividerItem(app)); items.add(new OptionsDividerItem(app));
@ -175,27 +191,51 @@ public class EditTrackGroupDialogFragment extends MenuBottomSheetDialogFragment
.create(); .create();
} }
private BaseBottomSheetItem createCopyToMarkersItem() { private BaseBottomSheetItem createCopyToMarkersItem(final GPXFile gpxFile) {
final MapMarkersGroup markersGroup = getOrCreateMarkersGroup(gpxFile);
final Set<String> categories = markersGroup.getWptCategories();
final boolean synced = categories != null && categories.contains(group.getName());
return new SimpleBottomSheetItem.Builder() return new SimpleBottomSheetItem.Builder()
.setIcon(getContentIcon(R.drawable.ic_action_copy)) .setIcon(getContentIcon(synced ? R.drawable.ic_action_delete_dark : R.drawable.ic_action_copy))
.setTitle(getString(R.string.copy_to_map_markers)) .setTitle(getString(synced ? R.string.remove_from_map_markers : R.string.copy_to_map_markers))
.setLayoutId(R.layout.bottom_sheet_item_simple)
.setLayoutId(R.layout.bottom_sheet_item_simple_pad_32dp) .setLayoutId(R.layout.bottom_sheet_item_simple_pad_32dp)
.setOnClickListener(new View.OnClickListener() { .setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
// MapMarkersHelper markersHelper = app.getMapMarkersHelper(); updateGroupWptCategory(gpxFile, markersGroup, categories, synced);
// MapMarkersGroup markersGroup = markersHelper.getMarkersGroup(group); dismiss();
// if (markersGroup != null) {
// markersHelper.removeMarkersGroup(markersGroup);
// } else {
// markersHelper.addOrEnableGroup(group);
// }
} }
}) })
.create(); .create();
} }
private void updateGroupWptCategory(GPXFile gpxFile, MapMarkersGroup markersGroup, Set<String> categories, boolean synced) {
SelectedGpxFile selectedGpxFile = selectedGpxHelper.getSelectedFileByPath(gpxFile.path);
if (selectedGpxFile == null) {
selectedGpxHelper.selectGpxFile(gpxFile, true, false, false, false, false);
}
Set<String> selectedCategories = new HashSet<>();
if (categories != null) {
selectedCategories.addAll(categories);
}
if (synced) {
selectedCategories.remove(group.getName());
} else {
selectedCategories.add(group.getName());
}
mapMarkersHelper.updateGroupWptCategories(markersGroup, selectedCategories);
mapMarkersHelper.runSynchronization(markersGroup);
}
private MapMarkersGroup getOrCreateMarkersGroup(GPXFile gpxFile) {
MapMarkersGroup markersGroup = mapMarkersHelper.getMarkersGroup(gpxFile);
if (markersGroup == null) {
markersGroup = mapMarkersHelper.addOrEnableGroup(gpxFile);
}
return markersGroup;
}
private BaseBottomSheetItem createCopyToFavoritesItem() { private BaseBottomSheetItem createCopyToFavoritesItem() {
return new SimpleBottomSheetItem.Builder() return new SimpleBottomSheetItem.Builder()
.setIcon(getContentIcon(R.drawable.ic_action_copy)) .setIcon(getContentIcon(R.drawable.ic_action_copy))

View file

@ -3,10 +3,15 @@ package net.osmand.plus.osmedit.opr;
import android.net.TrafficStats; import android.net.TrafficStats;
import android.os.Build; import android.os.Build;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import net.osmand.PlatformUtil; import net.osmand.PlatformUtil;
import net.osmand.osm.io.NetworkUtils; import net.osmand.osm.io.NetworkUtils;
import net.osmand.plus.OsmandApplication;
import net.osmand.util.Algorithms;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider;
@ -27,6 +32,7 @@ import java.security.KeyPair;
import java.security.Security; import java.security.Security;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
@ -40,7 +46,7 @@ public class OpenDBAPI {
public static final String PURPOSE = "osmand-android"; public static final String PURPOSE = "osmand-android";
private static final Log log = PlatformUtil.getLog(SecUtils.class); private static final Log log = PlatformUtil.getLog(SecUtils.class);
private static final String checkLoginEndpoint = "api/auth/user-check-loginkey?"; private static final String checkLoginEndpoint = "api/auth/user-check-loginkey?";
private static final String LOGIN_SUCCESS_MESSAGE = "{\"result\":\"OK\"}"; private static final String LOGIN_SUCCESS_MESSAGE = "\"result\":\"OK\"";
private static final int THREAD_ID = 11200; private static final int THREAD_ID = 11200;
/* /*
@ -51,7 +57,7 @@ public class OpenDBAPI {
* Need to encode key * Need to encode key
* Do not call on mainThread * Do not call on mainThread
*/ */
public boolean checkPrivateKeyValid(String baseUrl, String username, String privateKey) { public boolean checkPrivateKeyValid(OsmandApplication app, String baseUrl, String username, String privateKey) {
String url = null; String url = null;
try { try {
String purposeParam = "purpose=" + PURPOSE; String purposeParam = "purpose=" + PURPOSE;
@ -65,9 +71,29 @@ public class OpenDBAPI {
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
return false; return false;
} }
StringBuilder response = new StringBuilder(); StringBuilder response = new StringBuilder();
return (NetworkUtils.sendGetRequest(url,null,response) == null) && String error = NetworkUtils.sendGetRequest(url, null, response);
response.toString().contains(LOGIN_SUCCESS_MESSAGE); if (error == null) {
String responseStr = response.toString();
try {
Map<String, String> map = new Gson().fromJson(
responseStr, new TypeToken<HashMap<String, String>>() {
}.getType()
);
if (!Algorithms.isEmpty(map) && map.containsKey("blockchain-name")) {
String blockchainName = map.get("blockchain-name");
app.getSettings().OPR_BLOCKCHAIN_NAME.set(blockchainName);
} else {
return false;
}
} catch (JsonSyntaxException e) {
return false;
}
return responseStr.contains(LOGIN_SUCCESS_MESSAGE);
} else {
return false;
}
} }
public int uploadImage(String[] placeId, String baseUrl, String privateKey, String username, String image, StringBuilder sb) throws FailedVerificationException { public int uploadImage(String[] placeId, String baseUrl, String privateKey, String username, String image, StringBuilder sb) throws FailedVerificationException {

View file

@ -1171,6 +1171,9 @@ public class OsmandSettings {
public final OsmandPreference<String> OPR_USERNAME = public final OsmandPreference<String> OPR_USERNAME =
new StringPreference(this, "opr_username_secret", "").makeGlobal(); new StringPreference(this, "opr_username_secret", "").makeGlobal();
public final OsmandPreference<String> OPR_BLOCKCHAIN_NAME =
new StringPreference(this, "opr_blockchain_name", "").makeGlobal();
// this value boolean is synchronized with settings_pref.xml preference offline POI/Bugs edition // this value boolean is synchronized with settings_pref.xml preference offline POI/Bugs edition
public final OsmandPreference<Boolean> OFFLINE_EDITION = new BooleanPreference(this, "offline_osm_editing", true).makeGlobal().makeShared(); public final OsmandPreference<Boolean> OFFLINE_EDITION = new BooleanPreference(this, "offline_osm_editing", true).makeGlobal().makeShared();
public final OsmandPreference<Boolean> USE_DEV_URL = new BooleanPreference(this, "use_dev_url", false).makeGlobal().makeShared(); public final OsmandPreference<Boolean> USE_DEV_URL = new BooleanPreference(this, "use_dev_url", false).makeGlobal().makeShared();

View file

@ -480,19 +480,21 @@ public class SettingsHelper {
typesMap.putAll(getMyPlacesItems()); typesMap.putAll(getMyPlacesItems());
typesMap.putAll(getResourcesItems()); typesMap.putAll(getResourcesItems());
return getFilteredSettingsItems(typesMap, settingsTypes, export); return getFilteredSettingsItems(typesMap, settingsTypes, Collections.<SettingsItem>emptyList(), export);
} }
public List<SettingsItem> getFilteredSettingsItems(Map<ExportSettingsType, List<?>> allSettingsMap, public List<SettingsItem> getFilteredSettingsItems(
List<ExportSettingsType> settingsTypes, boolean export) { Map<ExportSettingsType, List<?>> allSettingsMap, List<ExportSettingsType> settingsTypes,
List<SettingsItem> settingsItems = new ArrayList<>(); @NonNull List<SettingsItem> settingsItems, boolean export
) {
List<SettingsItem> filteredSettingsItems = new ArrayList<>();
for (ExportSettingsType settingsType : settingsTypes) { for (ExportSettingsType settingsType : settingsTypes) {
List<?> settingsDataObjects = allSettingsMap.get(settingsType); List<?> settingsDataObjects = allSettingsMap.get(settingsType);
if (settingsDataObjects != null) { if (settingsDataObjects != null) {
settingsItems.addAll(prepareSettingsItems(settingsDataObjects, export)); filteredSettingsItems.addAll(prepareSettingsItems(settingsDataObjects, settingsItems, export));
} }
} }
return settingsItems; return filteredSettingsItems;
} }
public Map<ExportSettingsCategory, SettingsCategoryItems> getSettingsByCategory(boolean addProfiles) { public Map<ExportSettingsCategory, SettingsCategoryItems> getSettingsByCategory(boolean addProfiles) {
@ -693,8 +695,8 @@ public class SettingsHelper {
return files; return files;
} }
public List<SettingsItem> prepareSettingsItems(List<?> data, boolean export) { public List<SettingsItem> prepareSettingsItems(List<?> data, List<SettingsItem> settingsItems, boolean export) {
List<SettingsItem> settingsItems = new ArrayList<>(); List<SettingsItem> result = new ArrayList<>();
List<QuickAction> quickActions = new ArrayList<>(); List<QuickAction> quickActions = new ArrayList<>();
List<PoiUIFilter> poiUIFilters = new ArrayList<>(); List<PoiUIFilter> poiUIFilters = new ArrayList<>();
List<ITileSource> tileSourceTemplates = new ArrayList<>(); List<ITileSource> tileSourceTemplates = new ArrayList<>();
@ -719,13 +721,15 @@ public class SettingsHelper {
try { try {
File file = (File) object; File file = (File) object;
if (file.getName().endsWith(IndexConstants.GPX_FILE_EXT)) { if (file.getName().endsWith(IndexConstants.GPX_FILE_EXT)) {
settingsItems.add(new GpxSettingsItem(app, file)); result.add(new GpxSettingsItem(app, file));
} else { } else {
settingsItems.add(new FileSettingsItem(app, file)); result.add(new FileSettingsItem(app, file));
} }
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
LOG.warn("Trying to export unsuported file type", e); LOG.warn("Trying to export unsuported file type", e);
} }
} else if (object instanceof FileSettingsItem) {
result.add((FileSettingsItem) object);
} else if (object instanceof AvoidRoadInfo) { } else if (object instanceof AvoidRoadInfo) {
avoidRoads.add((AvoidRoadInfo) object); avoidRoads.add((AvoidRoadInfo) object);
} else if (object instanceof ApplicationModeBean) { } else if (object instanceof ApplicationModeBean) {
@ -746,65 +750,100 @@ public class SettingsHelper {
} else if (object instanceof HistoryEntry) { } else if (object instanceof HistoryEntry) {
historyEntries.add((HistoryEntry) object); historyEntries.add((HistoryEntry) object);
} else if (object instanceof GlobalSettingsItem) { } else if (object instanceof GlobalSettingsItem) {
settingsItems.add((GlobalSettingsItem) object); result.add((GlobalSettingsItem) object);
} else if (object instanceof OnlineRoutingEngine) { } else if (object instanceof OnlineRoutingEngine) {
onlineRoutingEngines.add((OnlineRoutingEngine) object); onlineRoutingEngines.add((OnlineRoutingEngine) object);
} }
} }
if (!quickActions.isEmpty()) { if (!quickActions.isEmpty()) {
settingsItems.add(new QuickActionsSettingsItem(app, quickActions)); QuickActionsSettingsItem baseItem = getBaseItem(SettingsItemType.QUICK_ACTIONS, QuickActionsSettingsItem.class, settingsItems);
result.add(new QuickActionsSettingsItem(app, baseItem, quickActions));
} }
if (!poiUIFilters.isEmpty()) { if (!poiUIFilters.isEmpty()) {
settingsItems.add(new PoiUiFiltersSettingsItem(app, poiUIFilters)); PoiUiFiltersSettingsItem baseItem = getBaseItem(SettingsItemType.POI_UI_FILTERS, PoiUiFiltersSettingsItem.class, settingsItems);
result.add(new PoiUiFiltersSettingsItem(app, baseItem, poiUIFilters));
} }
if (!tileSourceTemplates.isEmpty()) { if (!tileSourceTemplates.isEmpty()) {
settingsItems.add(new MapSourcesSettingsItem(app, tileSourceTemplates)); MapSourcesSettingsItem baseItem = getBaseItem(SettingsItemType.MAP_SOURCES, MapSourcesSettingsItem.class, settingsItems);
result.add(new MapSourcesSettingsItem(app, baseItem, tileSourceTemplates));
} }
if (!avoidRoads.isEmpty()) { if (!avoidRoads.isEmpty()) {
settingsItems.add(new AvoidRoadsSettingsItem(app, avoidRoads)); AvoidRoadsSettingsItem baseItem = getBaseItem(SettingsItemType.AVOID_ROADS, AvoidRoadsSettingsItem.class, settingsItems);
result.add(new AvoidRoadsSettingsItem(app, baseItem, avoidRoads));
} }
if (!appModeBeans.isEmpty()) { if (!appModeBeans.isEmpty()) {
for (ApplicationModeBean modeBean : appModeBeans) { for (ApplicationModeBean modeBean : appModeBeans) {
if (export) { if (export) {
ApplicationMode mode = ApplicationMode.valueOfStringKey(modeBean.stringKey, null); ApplicationMode mode = ApplicationMode.valueOfStringKey(modeBean.stringKey, null);
if (mode != null) { if (mode != null) {
settingsItems.add(new ProfileSettingsItem(app, mode)); result.add(new ProfileSettingsItem(app, mode));
} }
} else { } else {
settingsItems.add(new ProfileSettingsItem(app, null, modeBean)); result.add(new ProfileSettingsItem(app, getBaseProfileSettingsItem(modeBean, settingsItems), modeBean));
} }
} }
} }
if (!osmNotesPointList.isEmpty()) { if (!osmNotesPointList.isEmpty()) {
settingsItems.add(new OsmNotesSettingsItem(app, osmNotesPointList)); OsmNotesSettingsItem baseItem = getBaseItem(SettingsItemType.OSM_NOTES, OsmNotesSettingsItem.class, settingsItems);
result.add(new OsmNotesSettingsItem(app, baseItem, osmNotesPointList));
} }
if (!osmEditsPointList.isEmpty()) { if (!osmEditsPointList.isEmpty()) {
settingsItems.add(new OsmEditsSettingsItem(app, osmEditsPointList)); OsmEditsSettingsItem baseItem = getBaseItem(SettingsItemType.OSM_EDITS, OsmEditsSettingsItem.class, settingsItems);
result.add(new OsmEditsSettingsItem(app, baseItem, osmEditsPointList));
} }
if (!favoriteGroups.isEmpty()) { if (!favoriteGroups.isEmpty()) {
settingsItems.add(new FavoritesSettingsItem(app, favoriteGroups)); FavoritesSettingsItem baseItem = getBaseItem(SettingsItemType.FAVOURITES, FavoritesSettingsItem.class, settingsItems);
result.add(new FavoritesSettingsItem(app, baseItem, favoriteGroups));
} }
if (!markersGroups.isEmpty()) { if (!markersGroups.isEmpty()) {
List<MapMarker> mapMarkers = new ArrayList<>(); List<MapMarker> mapMarkers = new ArrayList<>();
for (MapMarkersGroup group : markersGroups) { for (MapMarkersGroup group : markersGroups) {
mapMarkers.addAll(group.getMarkers()); mapMarkers.addAll(group.getMarkers());
} }
settingsItems.add(new MarkersSettingsItem(app, mapMarkers)); MarkersSettingsItem baseItem = getBaseItem(SettingsItemType.ACTIVE_MARKERS, MarkersSettingsItem.class, settingsItems);
result.add(new MarkersSettingsItem(app, baseItem, mapMarkers));
} }
if (!markersHistoryGroups.isEmpty()) { if (!markersHistoryGroups.isEmpty()) {
List<MapMarker> mapMarkers = new ArrayList<>(); List<MapMarker> mapMarkers = new ArrayList<>();
for (MapMarkersGroup group : markersHistoryGroups) { for (MapMarkersGroup group : markersHistoryGroups) {
mapMarkers.addAll(group.getMarkers()); mapMarkers.addAll(group.getMarkers());
} }
settingsItems.add(new HistoryMarkersSettingsItem(app, mapMarkers)); HistoryMarkersSettingsItem baseItem = getBaseItem(SettingsItemType.HISTORY_MARKERS, HistoryMarkersSettingsItem.class, settingsItems);
result.add(new HistoryMarkersSettingsItem(app, baseItem, mapMarkers));
} }
if (!historyEntries.isEmpty()) { if (!historyEntries.isEmpty()) {
settingsItems.add(new SearchHistorySettingsItem(app, historyEntries)); SearchHistorySettingsItem baseItem = getBaseItem(SettingsItemType.SEARCH_HISTORY, SearchHistorySettingsItem.class, settingsItems);
result.add(new SearchHistorySettingsItem(app, baseItem, historyEntries));
} }
if (!onlineRoutingEngines.isEmpty()) { if (!onlineRoutingEngines.isEmpty()) {
settingsItems.add(new OnlineRoutingSettingsItem(app, onlineRoutingEngines)); OnlineRoutingSettingsItem baseItem = getBaseItem(SettingsItemType.ONLINE_ROUTING_ENGINES, OnlineRoutingSettingsItem.class, settingsItems);
result.add(new OnlineRoutingSettingsItem(app, baseItem, onlineRoutingEngines));
} }
return settingsItems; return result;
}
@Nullable
private ProfileSettingsItem getBaseProfileSettingsItem(ApplicationModeBean modeBean, List<SettingsItem> settingsItems) {
for (SettingsItem settingsItem : settingsItems) {
if (settingsItem.getType() == SettingsItemType.PROFILE) {
ProfileSettingsItem profileItem = (ProfileSettingsItem) settingsItem;
ApplicationModeBean bean = profileItem.getModeBean();
if (Algorithms.objectEquals(bean.stringKey, modeBean.stringKey) && Algorithms.objectEquals(bean.userProfileName, modeBean.userProfileName)) {
return profileItem;
}
}
}
return null;
}
@Nullable
private <T> T getBaseItem(SettingsItemType settingsItemType, Class<T> clazz, List<SettingsItem> settingsItems) {
for (SettingsItem settingsItem : settingsItems) {
if (settingsItem.getType() == settingsItemType && clazz.isInstance(settingsItem)) {
return clazz.cast(settingsItem);
}
}
return null;
} }
public static Map<ExportSettingsCategory, SettingsCategoryItems> getSettingsToOperateByCategory(List<SettingsItem> items, boolean importComplete) { public static Map<ExportSettingsCategory, SettingsCategoryItems> getSettingsToOperateByCategory(List<SettingsItem> items, boolean importComplete) {

View file

@ -36,6 +36,7 @@ import org.apache.commons.logging.Log;
import java.io.File; import java.io.File;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -150,7 +151,7 @@ public class ExportSettingsFragment extends BaseSettingsListFragment {
showExportProgressDialog(); showExportProgressDialog();
File tempDir = FileUtils.getTempDir(app); File tempDir = FileUtils.getTempDir(app);
String fileName = getFileName(); String fileName = getFileName();
List<SettingsItem> items = app.getSettingsHelper().prepareSettingsItems(adapter.getData(), true); List<SettingsItem> items = app.getSettingsHelper().prepareSettingsItems(adapter.getData(), Collections.<SettingsItem>emptyList(), true);
progress.setMax(getMaxProgress(items)); progress.setMax(getMaxProgress(items));
app.getSettingsHelper().exportSettings(tempDir, fileName, getSettingsExportListener(), items, true); app.getSettingsHelper().exportSettings(tempDir, fileName, getSettingsExportListener(), items, true);
} }

View file

@ -18,57 +18,24 @@ import androidx.fragment.app.FragmentManager;
import com.google.android.material.appbar.CollapsingToolbarLayout; import com.google.android.material.appbar.CollapsingToolbarLayout;
import net.osmand.IndexConstants;
import net.osmand.PlatformUtil; import net.osmand.PlatformUtil;
import net.osmand.map.ITileSource;
import net.osmand.map.TileSourceManager.TileSourceTemplate;
import net.osmand.plus.AppInitializer; import net.osmand.plus.AppInitializer;
import net.osmand.plus.FavouritesDbHelper.FavoriteGroup;
import net.osmand.plus.OsmandPlugin; import net.osmand.plus.OsmandPlugin;
import net.osmand.plus.R; import net.osmand.plus.R;
import net.osmand.plus.SQLiteTileSource;
import net.osmand.plus.UiUtilities; import net.osmand.plus.UiUtilities;
import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.audionotes.AudioVideoNotesPlugin; import net.osmand.plus.audionotes.AudioVideoNotesPlugin;
import net.osmand.plus.download.ReloadIndexesTask; import net.osmand.plus.download.ReloadIndexesTask;
import net.osmand.plus.download.ReloadIndexesTask.ReloadIndexesListener; import net.osmand.plus.download.ReloadIndexesTask.ReloadIndexesListener;
import net.osmand.plus.helpers.AvoidSpecificRoads.AvoidRoadInfo;
import net.osmand.plus.helpers.SearchHistoryHelper.HistoryEntry;
import net.osmand.plus.mapmarkers.MapMarker;
import net.osmand.plus.mapmarkers.MapMarkersGroup;
import net.osmand.plus.onlinerouting.engine.OnlineRoutingEngine;
import net.osmand.plus.osmedit.OpenstreetmapPoint;
import net.osmand.plus.osmedit.OsmNotesPoint;
import net.osmand.plus.poi.PoiUIFilter;
import net.osmand.plus.quickaction.QuickAction;
import net.osmand.plus.settings.backend.ApplicationMode.ApplicationModeBean;
import net.osmand.plus.settings.backend.ExportSettingsType;
import net.osmand.plus.settings.backend.backup.AvoidRoadsSettingsItem;
import net.osmand.plus.settings.backend.backup.FavoritesSettingsItem;
import net.osmand.plus.settings.backend.backup.FileSettingsItem; import net.osmand.plus.settings.backend.backup.FileSettingsItem;
import net.osmand.plus.settings.backend.backup.GlobalSettingsItem;
import net.osmand.plus.settings.backend.backup.GpxSettingsItem;
import net.osmand.plus.settings.backend.backup.HistoryMarkersSettingsItem;
import net.osmand.plus.settings.backend.backup.MapSourcesSettingsItem;
import net.osmand.plus.settings.backend.backup.MarkersSettingsItem;
import net.osmand.plus.settings.backend.backup.OnlineRoutingSettingsItem;
import net.osmand.plus.settings.backend.backup.OsmEditsSettingsItem;
import net.osmand.plus.settings.backend.backup.OsmNotesSettingsItem;
import net.osmand.plus.settings.backend.backup.PoiUiFiltersSettingsItem;
import net.osmand.plus.settings.backend.backup.ProfileSettingsItem;
import net.osmand.plus.settings.backend.backup.QuickActionsSettingsItem;
import net.osmand.plus.settings.backend.backup.SearchHistorySettingsItem;
import net.osmand.plus.settings.backend.backup.SettingsHelper; import net.osmand.plus.settings.backend.backup.SettingsHelper;
import net.osmand.plus.settings.backend.backup.SettingsHelper.ImportAsyncTask; import net.osmand.plus.settings.backend.backup.SettingsHelper.ImportAsyncTask;
import net.osmand.plus.settings.backend.backup.SettingsItem; import net.osmand.plus.settings.backend.backup.SettingsItem;
import net.osmand.plus.settings.backend.backup.SettingsItemType;
import net.osmand.util.Algorithms;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import java.io.File; import java.io.File;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List; import java.util.List;
public class ImportSettingsFragment extends BaseSettingsListFragment { public class ImportSettingsFragment extends BaseSettingsListFragment {
@ -177,7 +144,7 @@ public class ImportSettingsFragment extends BaseSettingsListFragment {
} }
private void importItems() { private void importItems() {
List<SettingsItem> selectedItems = getSettingsItemsFromData(adapter.getData()); List<SettingsItem> selectedItems = settingsHelper.prepareSettingsItems(adapter.getData(), settingsItems, false);
if (file != null && settingsItems != null) { if (file != null && settingsItems != null) {
duplicateStartTime = System.currentTimeMillis(); duplicateStartTime = System.currentTimeMillis();
settingsHelper.checkDuplicates(file, settingsItems, selectedItems, getDuplicatesListener()); settingsHelper.checkDuplicates(file, settingsItems, selectedItems, getDuplicatesListener());
@ -272,181 +239,6 @@ public class ImportSettingsFragment extends BaseSettingsListFragment {
this.settingsItems = settingsItems; this.settingsItems = settingsItems;
} }
@Nullable
private ProfileSettingsItem getBaseProfileSettingsItem(ApplicationModeBean modeBean) {
for (SettingsItem settingsItem : settingsItems) {
if (settingsItem.getType() == SettingsItemType.PROFILE) {
ProfileSettingsItem profileItem = (ProfileSettingsItem) settingsItem;
ApplicationModeBean bean = profileItem.getModeBean();
if (Algorithms.objectEquals(bean.stringKey, modeBean.stringKey) && Algorithms.objectEquals(bean.userProfileName, modeBean.userProfileName)) {
return profileItem;
}
}
}
return null;
}
@Nullable
private QuickActionsSettingsItem getBaseQuickActionsSettingsItem() {
for (SettingsItem settingsItem : settingsItems) {
if (settingsItem.getType() == SettingsItemType.QUICK_ACTIONS) {
return (QuickActionsSettingsItem) settingsItem;
}
}
return null;
}
@Nullable
private PoiUiFiltersSettingsItem getBasePoiUiFiltersSettingsItem() {
for (SettingsItem settingsItem : settingsItems) {
if (settingsItem.getType() == SettingsItemType.POI_UI_FILTERS) {
return (PoiUiFiltersSettingsItem) settingsItem;
}
}
return null;
}
@Nullable
private MapSourcesSettingsItem getBaseMapSourcesSettingsItem() {
for (SettingsItem settingsItem : settingsItems) {
if (settingsItem.getType() == SettingsItemType.MAP_SOURCES) {
return (MapSourcesSettingsItem) settingsItem;
}
}
return null;
}
@Nullable
private AvoidRoadsSettingsItem getBaseAvoidRoadsSettingsItem() {
for (SettingsItem settingsItem : settingsItems) {
if (settingsItem.getType() == SettingsItemType.AVOID_ROADS) {
return (AvoidRoadsSettingsItem) settingsItem;
}
}
return null;
}
@Nullable
private <T> T getBaseItem(SettingsItemType settingsItemType, Class<T> clazz) {
for (SettingsItem settingsItem : settingsItems) {
if (settingsItem.getType() == settingsItemType && clazz.isInstance(settingsItem)) {
return clazz.cast(settingsItem);
}
}
return null;
}
private List<SettingsItem> getSettingsItemsFromData(List<?> data) {
List<SettingsItem> settingsItems = new ArrayList<>();
List<ApplicationModeBean> appModeBeans = new ArrayList<>();
List<QuickAction> quickActions = new ArrayList<>();
List<PoiUIFilter> poiUIFilters = new ArrayList<>();
List<ITileSource> tileSourceTemplates = new ArrayList<>();
List<AvoidRoadInfo> avoidRoads = new ArrayList<>();
List<OsmNotesPoint> osmNotesPointList = new ArrayList<>();
List<OpenstreetmapPoint> osmEditsPointList = new ArrayList<>();
List<FavoriteGroup> favoriteGroups = new ArrayList<>();
List<MapMarkersGroup> markersGroups = new ArrayList<>();
List<MapMarkersGroup> markersHistoryGroups = new ArrayList<>();
List<HistoryEntry> historyEntries = new ArrayList<>();
List<OnlineRoutingEngine> onlineRoutingEngines = new ArrayList<>();
for (Object object : data) {
if (object instanceof ApplicationModeBean) {
appModeBeans.add((ApplicationModeBean) object);
} else if (object instanceof QuickAction) {
quickActions.add((QuickAction) object);
} else if (object instanceof PoiUIFilter) {
poiUIFilters.add((PoiUIFilter) object);
} else if (object instanceof TileSourceTemplate || object instanceof SQLiteTileSource) {
tileSourceTemplates.add((ITileSource) object);
} else if (object instanceof File) {
File file = (File) object;
if (file.getName().endsWith(IndexConstants.GPX_FILE_EXT)) {
settingsItems.add(new GpxSettingsItem(app, file));
} else {
settingsItems.add(new FileSettingsItem(app, file));
}
} else if (object instanceof FileSettingsItem) {
settingsItems.add((FileSettingsItem) object);
} else if (object instanceof AvoidRoadInfo) {
avoidRoads.add((AvoidRoadInfo) object);
} else if (object instanceof OsmNotesPoint) {
osmNotesPointList.add((OsmNotesPoint) object);
} else if (object instanceof OpenstreetmapPoint) {
osmEditsPointList.add((OpenstreetmapPoint) object);
} else if (object instanceof FavoriteGroup) {
favoriteGroups.add((FavoriteGroup) object);
} else if (object instanceof GlobalSettingsItem) {
settingsItems.add((GlobalSettingsItem) object);
} else if (object instanceof MapMarkersGroup) {
MapMarkersGroup markersGroup = (MapMarkersGroup) object;
if (ExportSettingsType.ACTIVE_MARKERS.name().equals(markersGroup.getId())) {
markersGroups.add((MapMarkersGroup) object);
} else if (ExportSettingsType.HISTORY_MARKERS.name().equals(markersGroup.getId())) {
markersHistoryGroups.add((MapMarkersGroup) object);
}
} else if (object instanceof HistoryEntry) {
historyEntries.add((HistoryEntry) object);
} else if (object instanceof OnlineRoutingEngine) {
onlineRoutingEngines.add((OnlineRoutingEngine) object);
}
}
if (!appModeBeans.isEmpty()) {
for (ApplicationModeBean modeBean : appModeBeans) {
settingsItems.add(new ProfileSettingsItem(app, getBaseProfileSettingsItem(modeBean), modeBean));
}
}
if (!quickActions.isEmpty()) {
settingsItems.add(new QuickActionsSettingsItem(app, getBaseQuickActionsSettingsItem(), quickActions));
}
if (!poiUIFilters.isEmpty()) {
settingsItems.add(new PoiUiFiltersSettingsItem(app, getBasePoiUiFiltersSettingsItem(), poiUIFilters));
}
if (!tileSourceTemplates.isEmpty()) {
settingsItems.add(new MapSourcesSettingsItem(app, getBaseMapSourcesSettingsItem(), tileSourceTemplates));
}
if (!avoidRoads.isEmpty()) {
settingsItems.add(new AvoidRoadsSettingsItem(app, getBaseAvoidRoadsSettingsItem(), avoidRoads));
}
if (!osmNotesPointList.isEmpty()) {
OsmNotesSettingsItem baseItem = getBaseItem(SettingsItemType.OSM_NOTES, OsmNotesSettingsItem.class);
settingsItems.add(new OsmNotesSettingsItem(app, baseItem, osmNotesPointList));
}
if (!osmEditsPointList.isEmpty()) {
OsmEditsSettingsItem baseItem = getBaseItem(SettingsItemType.OSM_EDITS, OsmEditsSettingsItem.class);
settingsItems.add(new OsmEditsSettingsItem(app, baseItem, osmEditsPointList));
}
if (!favoriteGroups.isEmpty()) {
FavoritesSettingsItem baseItem = getBaseItem(SettingsItemType.FAVOURITES, FavoritesSettingsItem.class);
settingsItems.add(new FavoritesSettingsItem(app, baseItem, favoriteGroups));
}
if (!markersGroups.isEmpty()) {
List<MapMarker> mapMarkers = new ArrayList<>();
for (MapMarkersGroup group : markersGroups) {
mapMarkers.addAll(group.getMarkers());
}
MarkersSettingsItem baseItem = getBaseItem(SettingsItemType.ACTIVE_MARKERS, MarkersSettingsItem.class);
settingsItems.add(new MarkersSettingsItem(app, baseItem, mapMarkers));
}
if (!markersHistoryGroups.isEmpty()) {
List<MapMarker> mapMarkers = new ArrayList<>();
for (MapMarkersGroup group : markersHistoryGroups) {
mapMarkers.addAll(group.getMarkers());
}
HistoryMarkersSettingsItem baseItem = getBaseItem(SettingsItemType.HISTORY_MARKERS, HistoryMarkersSettingsItem.class);
settingsItems.add(new HistoryMarkersSettingsItem(app, baseItem, mapMarkers));
}
if (!historyEntries.isEmpty()) {
SearchHistorySettingsItem baseItem = getBaseItem(SettingsItemType.SEARCH_HISTORY, SearchHistorySettingsItem.class);
settingsItems.add(new SearchHistorySettingsItem(app, baseItem, historyEntries));
}
if (!onlineRoutingEngines.isEmpty()) {
OnlineRoutingSettingsItem baseItem = getBaseItem(SettingsItemType.ONLINE_ROUTING_ENGINES, OnlineRoutingSettingsItem.class);
settingsItems.add(new OnlineRoutingSettingsItem(app, baseItem, onlineRoutingEngines));
}
return settingsItems;
}
public void setFile(File file) { public void setFile(File file) {
this.file = file; this.file = file;
} }

View file

@ -19,6 +19,7 @@ import androidx.activity.OnBackPressedCallback;
import androidx.annotation.ColorInt; import androidx.annotation.ColorInt;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
@ -37,6 +38,7 @@ import net.osmand.plus.R;
import net.osmand.plus.UiUtilities; import net.osmand.plus.UiUtilities;
import net.osmand.plus.UiUtilities.DialogButtonType; import net.osmand.plus.UiUtilities.DialogButtonType;
import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.base.ContextMenuFragment;
import net.osmand.plus.base.ContextMenuScrollFragment; import net.osmand.plus.base.ContextMenuScrollFragment;
import net.osmand.plus.dialogs.GpxAppearanceAdapter; import net.osmand.plus.dialogs.GpxAppearanceAdapter;
import net.osmand.plus.dialogs.GpxAppearanceAdapter.AppearanceListItem; import net.osmand.plus.dialogs.GpxAppearanceAdapter.AppearanceListItem;
@ -165,16 +167,7 @@ public class TrackAppearanceFragment extends ContextMenuScrollFragment implement
} }
requireMyActivity().getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) { requireMyActivity().getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
public void handleOnBackPressed() { public void handleOnBackPressed() {
MapActivity mapActivity = getMapActivity(); dismiss();
if (mapActivity != null) {
TripRecordingBottomSheet fragment = mapActivity.getTripRecordingBottomSheet();
if (fragment != null) {
fragment.show();
} else {
mapActivity.launchPrevActivityIntent();
}
dismissImmediate();
}
} }
}); });
} }
@ -385,6 +378,14 @@ public class TrackAppearanceFragment extends ContextMenuScrollFragment implement
return y; return y;
} }
@Override
public void onContextMenuDismiss(@NonNull ContextMenuFragment fragment) {
Fragment target = getTargetFragment();
if (target instanceof TripRecordingBottomSheet) {
((TripRecordingBottomSheet) target).show();
}
}
private void updateAppearanceIcon() { private void updateAppearanceIcon() {
Drawable icon = getTrackIcon(app, trackDrawInfo.getWidth(), trackDrawInfo.isShowArrows(), trackDrawInfo.getColor()); Drawable icon = getTrackIcon(app, trackDrawInfo.getWidth(), trackDrawInfo.isShowArrows(), trackDrawInfo.getColor());
trackIcon.setImageDrawable(icon); trackIcon.setImageDrawable(icon);
@ -725,11 +726,12 @@ public class TrackAppearanceFragment extends ContextMenuScrollFragment implement
return totalScreenHeight - frameTotalHeight; return totalScreenHeight - frameTotalHeight;
} }
public static boolean showInstance(@NonNull MapActivity mapActivity, @NonNull SelectedGpxFile selectedGpxFile) { public static boolean showInstance(@NonNull MapActivity mapActivity, @NonNull SelectedGpxFile selectedGpxFile, Fragment target) {
try { try {
TrackAppearanceFragment fragment = new TrackAppearanceFragment(); TrackAppearanceFragment fragment = new TrackAppearanceFragment();
fragment.setSelectedGpxFile(selectedGpxFile);
fragment.setRetainInstance(true); fragment.setRetainInstance(true);
fragment.setSelectedGpxFile(selectedGpxFile);
fragment.setTargetFragment(target, 0);
mapActivity.getSupportFragmentManager() mapActivity.getSupportFragmentManager()
.beginTransaction() .beginTransaction()

View file

@ -138,6 +138,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
private ImageView searchButton; private ImageView searchButton;
private EditText searchEditText; private EditText searchEditText;
private TextView toolbarTextView; private TextView toolbarTextView;
private ViewGroup headerContainer;
private View routeMenuTopShadowAll; private View routeMenuTopShadowAll;
private BottomNavigationView bottomNav; private BottomNavigationView bottomNav;
@ -148,6 +149,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
private Location lastLocation; private Location lastLocation;
private UpdateLocationViewCache updateLocationViewCache; private UpdateLocationViewCache updateLocationViewCache;
private boolean locationUpdateStarted; private boolean locationUpdateStarted;
private LatLon latLon;
private int menuTitleHeight; private int menuTitleHeight;
private int toolbarHeightPx; private int toolbarHeightPx;
@ -185,13 +187,17 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
@Override @Override
public int getToolbarHeight() { public int getToolbarHeight() {
return toolbarHeightPx; return isPortrait() ? toolbarHeightPx : 0;
} }
public float getMiddleStateKoef() { public float getMiddleStateKoef() {
return 0.5f; return 0.5f;
} }
public int getMinY() {
return getFullScreenTopPosY();
}
@Override @Override
public int getSupportedMenuStatesPortrait() { public int getSupportedMenuStatesPortrait() {
return MenuState.HEADER_ONLY | MenuState.HALF_SCREEN | MenuState.FULL_SCREEN; return MenuState.HEADER_ONLY | MenuState.HALF_SCREEN | MenuState.FULL_SCREEN;
@ -226,11 +232,22 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
FragmentActivity activity = requireMyActivity(); FragmentActivity activity = requireMyActivity();
activity.getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) { activity.getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
public void handleOnBackPressed() { public void handleOnBackPressed() {
if (getCurrentMenuState() != MenuState.HEADER_ONLY && isPortrait()) {
openMenuHeaderOnly();
} else {
dismiss();
MapActivity mapActivity = getMapActivity(); MapActivity mapActivity = getMapActivity();
if (mapActivity != null) { if (mapActivity != null) {
MapContextMenu contextMenu = mapActivity.getContextMenu();
if (contextMenu.isActive() && contextMenu.getPointDescription() != null
&& contextMenu.getPointDescription().isGpxPoint()) {
contextMenu.show();
} else {
mapActivity.launchPrevActivityIntent(); mapActivity.launchPrevActivityIntent();
} }
dismiss(); }
}
} }
}); });
} }
@ -243,12 +260,17 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
this.selectedGpxFile = selectedGpxFile; this.selectedGpxFile = selectedGpxFile;
} }
public void setLatLon(LatLon latLon) {
this.latLon = latLon;
}
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState); View view = super.onCreateView(inflater, container, savedInstanceState);
if (view != null) { if (view != null) {
bottomNav = view.findViewById(R.id.bottom_navigation); bottomNav = view.findViewById(R.id.bottom_navigation);
routeMenuTopShadowAll = view.findViewById(R.id.route_menu_top_shadow_all); routeMenuTopShadowAll = view.findViewById(R.id.route_menu_top_shadow_all);
headerContainer = view.findViewById(R.id.header_container);
headerTitle = view.findViewById(R.id.title); headerTitle = view.findViewById(R.id.title);
headerIcon = view.findViewById(R.id.icon_view); headerIcon = view.findViewById(R.id.icon_view);
toolbarContainer = view.findViewById(R.id.context_menu_toolbar_container); toolbarContainer = view.findViewById(R.id.context_menu_toolbar_container);
@ -258,7 +280,6 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
if (isPortrait()) { if (isPortrait()) {
AndroidUiHelper.updateVisibility(getTopShadow(), true); AndroidUiHelper.updateVisibility(getTopShadow(), true);
AndroidUiHelper.updateVisibility(view.findViewById(R.id.map_my_location_button), false);
} else { } else {
int widthNoShadow = getLandscapeNoShadowWidth(); int widthNoShadow = getLandscapeNoShadowWidth();
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(widthNoShadow, ViewGroup.LayoutParams.WRAP_CONTENT); FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(widthNoShadow, ViewGroup.LayoutParams.WRAP_CONTENT);
@ -270,7 +291,8 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
setupToolbar(); setupToolbar();
updateHeader(); updateHeader();
setupButtons(view); setupButtons(view);
runLayoutListener(); updateCardsLayout();
calculateLayoutAndUpdateMenuState();
} }
return view; return view;
} }
@ -281,7 +303,6 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
} }
private void updateHeader() { private void updateHeader() {
ViewGroup headerContainer = (ViewGroup) routeMenuTopShadowAll;
if (menuType == TrackMenuType.OVERVIEW) { if (menuType == TrackMenuType.OVERVIEW) {
setHeaderTitle(gpxTitle, true); setHeaderTitle(gpxTitle, true);
if (overviewCard != null && overviewCard.getView() != null) { if (overviewCard != null && overviewCard.getView() != null) {
@ -428,6 +449,17 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
} }
} }
private void updateCardsLayout() {
FrameLayout bottomContainer = getBottomContainer();
if (menuType == TrackMenuType.OPTIONS) {
AndroidUtils.setBackground(app, bottomContainer, isNightMode(),
R.color.list_background_color_light, R.color.list_background_color_dark);
} else {
AndroidUtils.setBackground(app, bottomContainer, isNightMode(),
R.color.activity_background_color_light, R.color.activity_background_color_dark);
}
}
@Override @Override
protected void calculateLayout(View view, boolean initLayout) { protected void calculateLayout(View view, boolean initLayout) {
menuTitleHeight = routeMenuTopShadowAll.getHeight() menuTitleHeight = routeMenuTopShadowAll.getHeight()
@ -463,7 +495,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
updateControlsVisibility(true); updateControlsVisibility(true);
} }
if (currentMenuState != MenuState.FULL_SCREEN && (changed || !mapPositionAdjusted)) { if (currentMenuState != MenuState.FULL_SCREEN && (changed || !mapPositionAdjusted)) {
adjustMapPosition(getViewY()); adjustMapPosition(getMenuStatePosY(currentMenuState));
} }
} }
@ -529,10 +561,9 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
MapActivity mapActivity = getMapActivity(); MapActivity mapActivity = getMapActivity();
View view = overviewCard.getView(); View view = overviewCard.getView();
if (mapActivity != null && view != null) { if (mapActivity != null && view != null) {
MapContextMenu menu = mapActivity.getContextMenu();
TextView distanceText = (TextView) view.findViewById(R.id.distance); TextView distanceText = (TextView) view.findViewById(R.id.distance);
ImageView direction = (ImageView) view.findViewById(R.id.direction); ImageView direction = (ImageView) view.findViewById(R.id.direction);
app.getUIUtilities().updateLocationView(updateLocationViewCache, direction, distanceText, menu.getLatLon()); app.getUIUtilities().updateLocationView(updateLocationViewCache, direction, distanceText, latLon);
} }
} }
@ -659,7 +690,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
app.getSelectedGpxHelper().selectGpxFile(gpxFile, gpxFileSelected, false); app.getSelectedGpxHelper().selectGpxFile(gpxFile, gpxFileSelected, false);
mapActivity.refreshMap(); mapActivity.refreshMap();
} else if (buttonIndex == APPEARANCE_BUTTON_INDEX) { } else if (buttonIndex == APPEARANCE_BUTTON_INDEX) {
TrackAppearanceFragment.showInstance(mapActivity, selectedGpxFile); TrackAppearanceFragment.showInstance(mapActivity, selectedGpxFile, this);
} else if (buttonIndex == DIRECTIONS_BUTTON_INDEX) { } else if (buttonIndex == DIRECTIONS_BUTTON_INDEX) {
MapActivityActions mapActions = mapActivity.getMapActions(); MapActivityActions mapActions = mapActivity.getMapActions();
if (app.getRoutingHelper().isFollowingMode()) { if (app.getRoutingHelper().isFollowingMode()) {
@ -797,7 +828,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
@Override @Override
protected void onHeaderClick() { protected void onHeaderClick() {
adjustMapPosition(getViewY()); updateMenuState();
} }
private void adjustMapPosition(int y) { private void adjustMapPosition(int y) {
@ -837,6 +868,8 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
menuType = type; menuType = type;
setupCards(); setupCards();
updateHeader(); updateHeader();
updateCardsLayout();
calculateLayoutAndUpdateMenuState();
break; break;
} }
} }
@ -845,6 +878,25 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
}); });
} }
private void calculateLayoutAndUpdateMenuState() {
runLayoutListener(new Runnable() {
@Override
public void run() {
updateMenuState();
}
});
}
private void updateMenuState() {
if (menuType == TrackMenuType.OPTIONS) {
openMenuFullScreen();
} else if (menuType == TrackMenuType.OVERVIEW) {
openMenuHeaderOnly();
} else {
openMenuHalfScreen();
}
}
@Override @Override
public void updateContent() { public void updateContent() {
if (segmentsCard != null) { if (segmentsCard != null) {
@ -1065,7 +1117,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
selectedGpxFile = app.getSelectedGpxHelper().getSelectedFileByPath(path); selectedGpxFile = app.getSelectedGpxHelper().getSelectedFileByPath(path);
} }
if (selectedGpxFile != null) { if (selectedGpxFile != null) {
showInstance(mapActivity, selectedGpxFile); showInstance(mapActivity, selectedGpxFile, null);
} else if (!Algorithms.isEmpty(path)) { } else if (!Algorithms.isEmpty(path)) {
String title = app.getString(R.string.loading_smth, ""); String title = app.getString(R.string.loading_smth, "");
final ProgressDialog progress = ProgressDialog.show(mapActivity, title, app.getString(R.string.loading_data)); final ProgressDialog progress = ProgressDialog.show(mapActivity, title, app.getString(R.string.loading_data));
@ -1078,7 +1130,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
if (mapActivity != null) { if (mapActivity != null) {
OsmandApplication app = mapActivity.getMyApplication(); OsmandApplication app = mapActivity.getMyApplication();
SelectedGpxFile selectedGpxFile = app.getSelectedGpxHelper().selectGpxFile(result, true, false); SelectedGpxFile selectedGpxFile = app.getSelectedGpxHelper().selectGpxFile(result, true, false);
showInstance(mapActivity, selectedGpxFile); showInstance(mapActivity, selectedGpxFile, null);
} }
if (progress != null && AndroidUtils.isActivityNotDestroyed(mapActivity)) { if (progress != null && AndroidUtils.isActivityNotDestroyed(mapActivity)) {
progress.dismiss(); progress.dismiss();
@ -1090,7 +1142,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
} }
} }
public static boolean showInstance(@NonNull MapActivity mapActivity, SelectedGpxFile selectedGpxFile) { public static boolean showInstance(@NonNull MapActivity mapActivity, SelectedGpxFile selectedGpxFile, @Nullable LatLon latLon) {
try { try {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putInt(ContextMenuFragment.MENU_STATE_KEY, MenuState.HEADER_ONLY); args.putInt(ContextMenuFragment.MENU_STATE_KEY, MenuState.HEADER_ONLY);
@ -1100,6 +1152,14 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
fragment.setRetainInstance(true); fragment.setRetainInstance(true);
fragment.setSelectedGpxFile(selectedGpxFile); fragment.setSelectedGpxFile(selectedGpxFile);
if (latLon != null) {
fragment.setLatLon(latLon);
} else {
QuadRect rect = selectedGpxFile.getGpxFile().getRect();
LatLon latLonRect = new LatLon(rect.centerY(), rect.centerX());
fragment.setLatLon(latLonRect);
}
mapActivity.getSupportFragmentManager() mapActivity.getSupportFragmentManager()
.beginTransaction() .beginTransaction()
.replace(R.id.fragmentContainer, fragment, TAG) .replace(R.id.fragmentContainer, fragment, TAG)

View file

@ -1,8 +1,12 @@
package net.osmand.plus.wikipedia; package net.osmand.plus.wikipedia;
import android.app.Activity;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.text.SpannableString;
import android.text.style.StyleSpan;
import android.view.View; import android.view.View;
import android.widget.CompoundButton; import android.widget.CompoundButton;
@ -12,6 +16,8 @@ import androidx.core.content.ContextCompat;
import androidx.core.os.ConfigurationCompat; import androidx.core.os.ConfigurationCompat;
import androidx.core.os.LocaleListCompat; import androidx.core.os.LocaleListCompat;
import com.google.android.material.snackbar.Snackbar;
import net.osmand.AndroidUtils; import net.osmand.AndroidUtils;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
import net.osmand.plus.OsmandPlugin; import net.osmand.plus.OsmandPlugin;
@ -124,6 +130,12 @@ public class SelectWikiLanguagesBottomSheet extends MenuBottomSheetDialogFragmen
} }
} }
@Nullable
public MapActivity getMapActivity() {
Activity activity = getActivity();
return (MapActivity) activity;
}
private void initLanguagesData() { private void initLanguagesData() {
languages = new ArrayList<>(); languages = new ArrayList<>();
@ -188,12 +200,44 @@ public class SelectWikiLanguagesBottomSheet extends MenuBottomSheetDialogFragmen
localesForSaving.add(language.getLocale()); localesForSaving.add(language.getLocale());
} }
} }
wikiPlugin.setLanguagesToShow(localesForSaving); applyPreferenceWithSnackBar(localesForSaving, isGlobalWikiPoiEnabled);
wikiPlugin.setShowAllLanguages(isGlobalWikiPoiEnabled);
wikiPlugin.updateWikipediaState();
dismiss(); dismiss();
} }
protected final void applyPreference(boolean applyToAllProfiles, List<String> localesForSaving, boolean global) {
if (applyToAllProfiles) {
for (ApplicationMode mode : ApplicationMode.allPossibleValues()) {
wikiPlugin.setLanguagesToShow(mode, localesForSaving);
wikiPlugin.setShowAllLanguages(mode, global);
}
} else {
wikiPlugin.setLanguagesToShow(localesForSaving);
wikiPlugin.setShowAllLanguages(global);
}
wikiPlugin.updateWikipediaState();
}
protected void applyPreferenceWithSnackBar(final List<String> localesForSaving, final boolean global) {
applyPreference(false, localesForSaving, global);
MapActivity mapActivity = getMapActivity();
if (mapActivity != null) {
String modeName = appMode.toHumanString();
String text = app.getString(R.string.changes_applied_to_profile, modeName);
SpannableString message = UiUtilities.createSpannableString(text, new StyleSpan(Typeface.BOLD), modeName);
Snackbar snackbar = Snackbar.make(mapActivity.getLayout(), message, Snackbar.LENGTH_LONG)
.setAction(R.string.apply_to_all_profiles, new View.OnClickListener() {
@Override
public void onClick(View view) {
applyPreference(true, localesForSaving, global);
}
});
UiUtilities.setupSnackbarVerticalLayout(snackbar);
UiUtilities.setupSnackbar(snackbar, nightMode);
snackbar.show();
}
}
private View getCustomButtonView() { private View getCustomButtonView() {
OsmandApplication app = getMyApplication(); OsmandApplication app = getMyApplication();
if (app == null) { if (app == null) {

View file

@ -30,6 +30,7 @@ import net.osmand.plus.search.QuickSearchDialogFragment;
import net.osmand.plus.search.QuickSearchListAdapter; import net.osmand.plus.search.QuickSearchListAdapter;
import net.osmand.plus.search.listitems.QuickSearchBannerListItem; import net.osmand.plus.search.listitems.QuickSearchBannerListItem;
import net.osmand.plus.search.listitems.QuickSearchFreeBannerListItem; import net.osmand.plus.search.listitems.QuickSearchFreeBannerListItem;
import net.osmand.plus.settings.backend.ApplicationMode;
import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.views.layers.DownloadedRegionsLayer; import net.osmand.plus.views.layers.DownloadedRegionsLayer;
import net.osmand.plus.views.OsmandMapTileView; import net.osmand.plus.views.OsmandMapTileView;
@ -189,26 +190,50 @@ public class WikipediaPlugin extends OsmandPlugin {
return !isShowAllLanguages() && getLanguagesToShow() != null; return !isShowAllLanguages() && getLanguagesToShow() != null;
} }
public boolean hasCustomSettings(ApplicationMode profile) {
return !isShowAllLanguages(profile) && getLanguagesToShow(profile) != null;
}
public boolean hasLanguagesFilter() { public boolean hasLanguagesFilter() {
return settings.WIKIPEDIA_POI_ENABLED_LANGUAGES.get() != null; return settings.WIKIPEDIA_POI_ENABLED_LANGUAGES.get() != null;
} }
public boolean hasLanguagesFilter(ApplicationMode profile) {
return settings.WIKIPEDIA_POI_ENABLED_LANGUAGES.getModeValue(profile) != null;
}
public boolean isShowAllLanguages() { public boolean isShowAllLanguages() {
return settings.GLOBAL_WIKIPEDIA_POI_ENABLED.get(); return settings.GLOBAL_WIKIPEDIA_POI_ENABLED.get();
} }
public boolean isShowAllLanguages(ApplicationMode mode) {
return settings.GLOBAL_WIKIPEDIA_POI_ENABLED.getModeValue(mode);
}
public void setShowAllLanguages(boolean showAllLanguages) { public void setShowAllLanguages(boolean showAllLanguages) {
settings.GLOBAL_WIKIPEDIA_POI_ENABLED.set(showAllLanguages); settings.GLOBAL_WIKIPEDIA_POI_ENABLED.set(showAllLanguages);
} }
public void setShowAllLanguages(ApplicationMode mode, boolean showAllLanguages) {
settings.GLOBAL_WIKIPEDIA_POI_ENABLED.setModeValue(mode, showAllLanguages);
}
public List<String> getLanguagesToShow() { public List<String> getLanguagesToShow() {
return settings.WIKIPEDIA_POI_ENABLED_LANGUAGES.getStringsList(); return settings.WIKIPEDIA_POI_ENABLED_LANGUAGES.getStringsList();
} }
public List<String> getLanguagesToShow(ApplicationMode mode) {
return settings.WIKIPEDIA_POI_ENABLED_LANGUAGES.getStringsListForProfile(mode);
}
public void setLanguagesToShow(List<String> languagesToShow) { public void setLanguagesToShow(List<String> languagesToShow) {
settings.WIKIPEDIA_POI_ENABLED_LANGUAGES.setStringsList(languagesToShow); settings.WIKIPEDIA_POI_ENABLED_LANGUAGES.setStringsList(languagesToShow);
} }
public void setLanguagesToShow(ApplicationMode mode, List<String> languagesToShow) {
settings.WIKIPEDIA_POI_ENABLED_LANGUAGES.setStringsListForProfile(mode, languagesToShow);
}
public void toggleWikipediaPoi(boolean enable, CallbackWithObject<Boolean> callback) { public void toggleWikipediaPoi(boolean enable, CallbackWithObject<Boolean> callback) {
if (enable) { if (enable) {
showWikiOnMap(); showWikiOnMap();

View file

@ -1,6 +1,7 @@
package net.osmand.plus.wikivoyage.article; package net.osmand.plus.wikivoyage.article;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
@ -339,19 +340,22 @@ public class WikivoyageArticleDialogFragment extends WikiArticleBaseDialogFragme
} }
private void updateTrackButton(boolean processing, @Nullable GPXFile gpxFile) { private void updateTrackButton(boolean processing, @Nullable GPXFile gpxFile) {
Context ctx = getContext();
if (ctx != null) {
if (processing) { if (processing) {
trackButton.setVisibility(View.GONE); trackButton.setVisibility(View.GONE);
gpxProgress.setVisibility(View.VISIBLE); gpxProgress.setVisibility(View.VISIBLE);
} else { } else {
if (gpxFile != null && gpxFile.getPointsSize() > 0) { if (gpxFile != null && gpxFile.getPointsSize() > 0) {
trackButton.setVisibility(View.VISIBLE); trackButton.setVisibility(View.VISIBLE);
trackButton.setText(getString(R.string.shared_string_gpx_points) + " (" + gpxFile.getPointsSize() + ")"); trackButton.setText(ctx.getString(R.string.shared_string_gpx_points) + " (" + gpxFile.getPointsSize() + ")");
} else { } else {
trackButton.setVisibility(View.GONE); trackButton.setVisibility(View.GONE);
} }
gpxProgress.setVisibility(View.GONE); gpxProgress.setVisibility(View.GONE);
} }
} }
}
@NonNull @NonNull
@Override @Override

View file

@ -0,0 +1,14 @@
package net.osmand.plus.wikivoyage.data;
public class TravelGpx extends TravelArticle {
public static final String DISTANCE = "distance";
public static final String DIFF_ELE_UP = "diff_ele_up";
public static final String DIFF_ELE_DOWN = "diff_ele_down";
public static final String USER = "user";
public String user;
public float totalDistance = 0;
public double diffElevationUp = 0;
public double diffElevationDown = 0;
}

View file

@ -1,6 +1,9 @@
package net.osmand.plus.wikivoyage.data; package net.osmand.plus.wikivoyage.data;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -100,6 +103,10 @@ public class TravelLocalDataHelper {
} }
} }
public boolean hasSavedArticles() {
return !savedArticles.isEmpty() || dbHelper.hasSavedArticles();
}
@NonNull @NonNull
public List<TravelArticle> getSavedArticles() { public List<TravelArticle> getSavedArticles() {
return new ArrayList<>(savedArticles); return new ArrayList<>(savedArticles);
@ -143,7 +150,8 @@ public class TravelLocalDataHelper {
@Nullable @Nullable
private TravelArticle getArticle(String title, String lang) { private TravelArticle getArticle(String title, String lang) {
for (TravelArticle article : savedArticles) { for (TravelArticle article : savedArticles) {
if (article.title != null && article.title.equals(title) && article.lang != null && article.lang.equals(lang)) { if (Algorithms.stringsEqual(article.title, title)
&& Algorithms.stringsEqual(article.lang, lang)) {
return article; return article;
} }
} }
@ -438,6 +446,25 @@ public class TravelLocalDataHelper {
return res; return res;
} }
boolean hasSavedArticles() {
int count = 0;
SQLiteConnection conn = openConnection(true);
if (conn != null) {
try {
SQLiteCursor cursor = conn.rawQuery("SELECT COUNT(*) FROM " + BOOKMARKS_TABLE_NAME, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
count = cursor.getInt(0);
}
cursor.close();
}
} finally {
conn.close();
}
}
return count > 0;
}
void addSavedArticle(@NonNull TravelArticle article) { void addSavedArticle(@NonNull TravelArticle article) {
String travelBook = article.getTravelBook(context); String travelBook = article.getTravelBook(context);
if (travelBook == null) { if (travelBook == null) {
@ -477,12 +504,12 @@ public class TravelLocalDataHelper {
SQLiteConnection conn = openConnection(false); SQLiteConnection conn = openConnection(false);
if (conn != null) { if (conn != null) {
try { try {
conn.execSQL("DELETE FROM " + BOOKMARKS_TABLE_NAME + String query = "DELETE FROM " + BOOKMARKS_TABLE_NAME +
" WHERE " + BOOKMARKS_COL_ARTICLE_TITLE + " = ?" + " WHERE " + BOOKMARKS_COL_ARTICLE_TITLE + " = ?" +
" AND " + BOOKMARKS_COL_ROUTE_ID + " = ?" + " AND " + BOOKMARKS_COL_ROUTE_ID + " = ?" +
" AND " + BOOKMARKS_COL_LANG + " = ?" + " AND " + BOOKMARKS_COL_LANG + ((article.lang != null) ? " = '" + article.lang + "'" : " IS NULL") +
" AND " + BOOKMARKS_COL_TRAVEL_BOOK + " = ?", " AND " + BOOKMARKS_COL_TRAVEL_BOOK + " = ?";
new Object[]{article.title, article.routeId, article.lang, travelBook}); conn.execSQL(query, new Object[]{article.title, article.routeId, travelBook});
} finally { } finally {
conn.close(); conn.close();
} }
@ -537,7 +564,12 @@ public class TravelLocalDataHelper {
@NonNull @NonNull
private TravelArticle readSavedArticle(SQLiteCursor cursor) { private TravelArticle readSavedArticle(SQLiteCursor cursor) {
TravelArticle res = new TravelArticle(); TravelArticle res;
if (cursor.getString(cursor.getColumnIndex(BOOKMARKS_COL_LANG)) == null) {
res = new TravelGpx();
} else {
res = new TravelArticle();
}
res.title = cursor.getString(cursor.getColumnIndex(BOOKMARKS_COL_ARTICLE_TITLE)); res.title = cursor.getString(cursor.getColumnIndex(BOOKMARKS_COL_ARTICLE_TITLE));
res.lang = cursor.getString(cursor.getColumnIndex(BOOKMARKS_COL_LANG)); res.lang = cursor.getString(cursor.getColumnIndex(BOOKMARKS_COL_LANG));
res.aggregatedPartOf = cursor.getString(cursor.getColumnIndex(BOOKMARKS_COL_IS_PART_OF)); res.aggregatedPartOf = cursor.getString(cursor.getColumnIndex(BOOKMARKS_COL_IS_PART_OF));

View file

@ -2,6 +2,7 @@ package net.osmand.plus.wikivoyage.data;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Pair;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -13,6 +14,7 @@ import net.osmand.IndexConstants;
import net.osmand.OsmAndCollator; import net.osmand.OsmAndCollator;
import net.osmand.PlatformUtil; import net.osmand.PlatformUtil;
import net.osmand.ResultMatcher; import net.osmand.ResultMatcher;
import net.osmand.binary.BinaryMapDataObject;
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.binary.BinaryMapIndexReader.SearchRequest;
@ -46,8 +48,18 @@ import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import gnu.trove.iterator.TIntObjectIterator;
import static net.osmand.GPXUtilities.Track;
import static net.osmand.GPXUtilities.TrkSegment;
import static net.osmand.GPXUtilities.WptPt; import static net.osmand.GPXUtilities.WptPt;
import static net.osmand.GPXUtilities.writeGpxFile; import static net.osmand.GPXUtilities.writeGpxFile;
import static net.osmand.plus.helpers.GpxUiHelper.getGpxTitle;
import static net.osmand.plus.wikivoyage.data.TravelGpx.DIFF_ELE_DOWN;
import static net.osmand.plus.wikivoyage.data.TravelGpx.DIFF_ELE_UP;
import static net.osmand.plus.wikivoyage.data.TravelGpx.DISTANCE;
import static net.osmand.plus.wikivoyage.data.TravelGpx.USER;
import static net.osmand.util.Algorithms.capitalizeFirstLetter;
public class TravelObfHelper implements TravelHelper { public class TravelObfHelper implements TravelHelper {
@ -55,9 +67,13 @@ public class TravelObfHelper implements TravelHelper {
private static final String WORLD_WIKIVOYAGE_FILE_NAME = "World_wikivoyage.travel.obf"; private static final String WORLD_WIKIVOYAGE_FILE_NAME = "World_wikivoyage.travel.obf";
public static final String ROUTE_ARTICLE = "route_article"; public static final String ROUTE_ARTICLE = "route_article";
public static final String ROUTE_ARTICLE_POINT = "route_article_point"; public static final String ROUTE_ARTICLE_POINT = "route_article_point";
public static final String ROUTE_TRACK = "route_track";
public static final int POPULAR_ARTICLES_SEARCH_RADIUS = 100000; public static final int POPULAR_ARTICLES_SEARCH_RADIUS = 100000;
public static final int ARTICLE_SEARCH_RADIUS = 50000; public static final int ARTICLE_SEARCH_RADIUS = 50000;
public static final int GPX_TRACKS_SEARCH_RADIUS = 10000;
public static final int MAX_POPULAR_ARTICLES_COUNT = 30; public static final int MAX_POPULAR_ARTICLES_COUNT = 30;
public static final String REF_TAG = "ref";
public static final String NAME_TAG = "name";
private final OsmandApplication app; private final OsmandApplication app;
private final Collator collator; private final Collator collator;
@ -91,26 +107,29 @@ public class TravelObfHelper implements TravelHelper {
public synchronized List<TravelArticle> loadPopularArticles() { public synchronized List<TravelArticle> loadPopularArticles() {
String lang = app.getLanguage(); String lang = app.getLanguage();
List<TravelArticle> popularArticles = new ArrayList<>(); List<TravelArticle> popularArticles = new ArrayList<>();
for (BinaryMapIndexReader reader : getReaders()) { final List<Pair<File, Amenity>> amenities = new ArrayList<>();
try {
final LatLon location = app.getMapViewTrackingUtilities().getMapLocation(); final LatLon location = app.getMapViewTrackingUtilities().getMapLocation();
SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest( for (final BinaryMapIndexReader reader : getReaders()) {
location, POPULAR_ARTICLES_SEARCH_RADIUS, -1, getSearchFilter(false), null); try {
List<Amenity> amenities = reader.searchPoi(req); searchAmenity(amenities, location, reader, POPULAR_ARTICLES_SEARCH_RADIUS, -1, ROUTE_ARTICLE);
searchAmenity(amenities, location, reader, GPX_TRACKS_SEARCH_RADIUS, 15, ROUTE_TRACK);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
if (amenities.size() > 0) { if (amenities.size() > 0) {
Collections.sort(amenities, new Comparator<Amenity>() { Collections.sort(amenities, new Comparator<Pair<File, Amenity>>() {
@Override @Override
public int compare(Amenity a1, Amenity a2) { public int compare(Pair article1, Pair article2) {
int d1 = (int) (MapUtils.getDistance(a1.getLocation().getLatitude(), a1.getLocation().getLongitude(), int d1 = (int) (MapUtils.getDistance(((Amenity) article1.second).getLocation(), location));
location.getLatitude(), location.getLongitude())); int d2 = (int) (MapUtils.getDistance(((Amenity) article2.second).getLocation(), location));
int d2 = (int) (MapUtils.getDistance(a2.getLocation().getLatitude(), a2.getLocation().getLongitude(),
location.getLatitude(), location.getLongitude()));
return d1 < d2 ? -1 : (d1 == d2 ? 0 : 1); return d1 < d2 ? -1 : (d1 == d2 ? 0 : 1);
} }
}); });
for (Amenity amenity : amenities) { for (Pair<File, Amenity> amenity : amenities) {
if (!Algorithms.isEmpty(amenity.getName(lang))) { if (!Algorithms.isEmpty(amenity.second.getName(lang))) {
TravelArticle article = cacheTravelArticles(reader.getFile(), amenity, lang, false, null); TravelArticle article = cacheTravelArticles(amenity.first, amenity.second, lang, false, null);
if (article != null) { if (article != null) {
popularArticles.add(article); popularArticles.add(article);
if (popularArticles.size() >= MAX_POPULAR_ARTICLES_COUNT) { if (popularArticles.size() >= MAX_POPULAR_ARTICLES_COUNT) {
@ -120,18 +139,37 @@ public class TravelObfHelper implements TravelHelper {
} }
} }
} }
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
this.popularArticles = popularArticles; this.popularArticles = popularArticles;
return popularArticles; return popularArticles;
} }
private void searchAmenity(final List<Pair<File, Amenity>> amenitiesList, LatLon location,
final BinaryMapIndexReader reader, int searchRadius, int zoom,
String searchFilter) throws IOException {
reader.searchPoi(BinaryMapIndexReader.buildSearchPoiRequest(
location, searchRadius, zoom, getSearchFilter(searchFilter), new ResultMatcher<Amenity>() {
@Override
public boolean publish(Amenity object) {
amenitiesList.add(new Pair<>(reader.getFile(), object));
return false;
}
@Override
public boolean isCancelled() {
return false;
}
}));
}
@Nullable @Nullable
private TravelArticle cacheTravelArticles(File file, Amenity amenity, String lang, boolean readPoints, @Nullable GpxReadCallback callback) { private TravelArticle cacheTravelArticles(File file, Amenity amenity, String lang, boolean readPoints, @Nullable GpxReadCallback callback) {
TravelArticle article = null; TravelArticle article = null;
Map<String, TravelArticle> articles = readArticles(file, amenity); Map<String, TravelArticle> articles;
if (ROUTE_TRACK.equals(amenity.getSubType())) {
articles = readRoutePoint(file, amenity);
} else {
articles = readArticles(file, amenity);
}
if (!Algorithms.isEmpty(articles)) { if (!Algorithms.isEmpty(articles)) {
TravelArticleIdentifier newArticleId = articles.values().iterator().next().generateIdentifier(); TravelArticleIdentifier newArticleId = articles.values().iterator().next().generateIdentifier();
cachedArticles.put(newArticleId, articles); cachedArticles.put(newArticleId, articles);
@ -140,12 +178,41 @@ public class TravelObfHelper implements TravelHelper {
return article; return article;
} }
private Map<String, TravelArticle> readRoutePoint(File file, Amenity amenity) {
Map<String, TravelArticle> articles = new HashMap<>();
TravelGpx res = new TravelGpx();
res.file = file;
String title = amenity.getName("en");
res.title = createTitle(Algorithms.isEmpty(title) ? amenity.getName() : title);
res.lat = amenity.getLocation().getLatitude();
res.lon = amenity.getLocation().getLongitude();
res.routeId = Algorithms.emptyIfNull(amenity.getTagContent(Amenity.ROUTE_ID));
try {
res.totalDistance = Float.parseFloat(Algorithms.emptyIfNull(amenity.getTagContent(DISTANCE)));
} catch (NumberFormatException e) {
LOG.debug(e.getMessage(), e);
}
try {
res.diffElevationUp = Double.parseDouble(Algorithms.emptyIfNull(amenity.getTagContent(DIFF_ELE_UP)));
} catch (NumberFormatException e) {
LOG.debug(e.getMessage(), e);
}
try {
res.diffElevationDown = Double.parseDouble(Algorithms.emptyIfNull(amenity.getTagContent(DIFF_ELE_DOWN)));
} catch (NumberFormatException e) {
LOG.debug(e.getMessage(), e);
}
res.user = Algorithms.emptyIfNull(amenity.getTagContent(USER));
articles.put("en", res);
return articles;
}
@NonNull @NonNull
private SearchPoiTypeFilter getSearchFilter(final boolean articlePoints) { private SearchPoiTypeFilter getSearchFilter(final String filterSubcategory) {
return new SearchPoiTypeFilter() { return new SearchPoiTypeFilter() {
@Override @Override
public boolean accept(PoiCategory type, String subcategory) { public boolean accept(PoiCategory type, String subcategory) {
return subcategory.equals(articlePoints ? ROUTE_ARTICLE_POINT : ROUTE_ARTICLE); return subcategory.equals(filterSubcategory);
} }
@Override @Override
@ -176,9 +243,9 @@ public class TravelObfHelper implements TravelHelper {
res.isParentOf = Algorithms.emptyIfNull(amenity.getTagContent(Amenity.IS_PARENT_OF, lang)); res.isParentOf = Algorithms.emptyIfNull(amenity.getTagContent(Amenity.IS_PARENT_OF, lang));
res.lat = amenity.getLocation().getLatitude(); res.lat = amenity.getLocation().getLatitude();
res.lon = amenity.getLocation().getLongitude(); res.lon = amenity.getLocation().getLongitude();
res.imageTitle = Algorithms.emptyIfNull(amenity.getTagContent(Amenity.IMAGE_TITLE, null)); res.imageTitle = Algorithms.emptyIfNull(amenity.getTagContent(Amenity.IMAGE_TITLE));
res.routeId = Algorithms.emptyIfNull(amenity.getTagContent(Amenity.ROUTE_ID, null)); res.routeId = Algorithms.emptyIfNull(amenity.getTagContent(Amenity.ROUTE_ID));
res.routeSource = Algorithms.emptyIfNull(amenity.getTagContent(Amenity.ROUTE_SOURCE, null)); res.routeSource = Algorithms.emptyIfNull(amenity.getTagContent(Amenity.ROUTE_SOURCE));
res.originalId = 0; res.originalId = 0;
res.lang = lang; res.lang = lang;
res.contentsJson = Algorithms.emptyIfNull(amenity.getTagContent(Amenity.CONTENT_JSON, lang)); res.contentsJson = Algorithms.emptyIfNull(amenity.getTagContent(Amenity.CONTENT_JSON, lang));
@ -186,6 +253,82 @@ public class TravelObfHelper implements TravelHelper {
return res; return res;
} }
@Nullable
private GPXFile buildTravelGpxFile(@NonNull final TravelGpx article) {
String routeId = article.getRouteId();
final String ref = routeId.substring(routeId.length() - 3);
final List<BinaryMapDataObject> segmentList = new ArrayList<>();
for (BinaryMapIndexReader reader : getReaders()) {
try {
if (article.file != null && !article.file.equals(reader.getFile())) {
continue;
}
BinaryMapIndexReader.SearchRequest<BinaryMapDataObject> sr = BinaryMapIndexReader.buildSearchRequest(
0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, 15, null,
new ResultMatcher<BinaryMapDataObject>() {
@Override
public boolean publish(BinaryMapDataObject object) {
if (object.getPointsLength() > 1) {
if (getTagValue(object, REF_TAG).equals(ref)
&& createTitle(getTagValue(object, NAME_TAG)).equals(article.title)) {
segmentList.add(object);
}
}
return false;
}
@Override
public boolean isCancelled() {
return false;
}
});
reader.searchMapIndex(sr);
if (!Algorithms.isEmpty(segmentList)) {
break;
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
GPXFile gpxFile = null;
if (!segmentList.isEmpty()) {
Track track = new Track();
for (BinaryMapDataObject segment : segmentList) {
TrkSegment trkSegment = new TrkSegment();
for (int i = 0; i < segment.getPointsLength(); i++) {
WptPt point = new WptPt();
point.lat = MapUtils.get31LatitudeY(segment.getPoint31YTile(i));
point.lon = MapUtils.get31LongitudeX(segment.getPoint31XTile(i));
trkSegment.points.add(point);
}
track.segments.add(trkSegment);
}
gpxFile = new GPXFile(article.getTitle(), article.getLang(), "");
gpxFile.tracks = new ArrayList<>();
gpxFile.tracks.add(track);
}
article.gpxFile = gpxFile;
return gpxFile;
}
private String getTagValue(BinaryMapDataObject object, String tag) {
BinaryMapIndexReader.MapIndex mi = object.getMapIndex();
TIntObjectIterator<String> it = object.getObjectNames().iterator();
while (it.hasNext()) {
it.advance();
BinaryMapIndexReader.TagValuePair tp = mi.decodeType(it.key());
if (tp.tag.equals(tag)) {
return it.value();
}
}
return "";
}
private String createTitle(String name) {
return capitalizeFirstLetter(getGpxTitle(name));
}
@NonNull @NonNull
private synchronized List<Amenity> getPointList(@NonNull final TravelArticle article) { private synchronized List<Amenity> getPointList(@NonNull final TravelArticle article) {
final List<Amenity> pointList = new ArrayList<>(); final List<Amenity> pointList = new ArrayList<>();
@ -197,14 +340,14 @@ public class TravelObfHelper implements TravelHelper {
} }
SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(0, 0, SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(0, 0,
Algorithms.emptyIfNull(article.title), 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, Algorithms.emptyIfNull(article.title), 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE,
getSearchFilter(true), new ResultMatcher<Amenity>() { getSearchFilter(ROUTE_ARTICLE_POINT), new ResultMatcher<Amenity>() {
@Override @Override
public boolean publish(Amenity amenity) { public boolean publish(Amenity amenity) {
String amenityLang = amenity.getTagSuffix(Amenity.LANG_YES + ":"); String amenityLang = amenity.getTagSuffix(Amenity.LANG_YES + ":");
if (Algorithms.stringsEqual(lang, amenityLang) if (Algorithms.stringsEqual(lang, amenityLang)
&& Algorithms.stringsEqual(article.routeId, && Algorithms.stringsEqual(article.routeId,
Algorithms.emptyIfNull(amenity.getTagContent(Amenity.ROUTE_ID, null)))) { Algorithms.emptyIfNull(amenity.getTagContent(Amenity.ROUTE_ID)))) {
pointList.add(amenity); pointList.add(amenity);
} }
return false; return false;
@ -246,7 +389,7 @@ public class TravelObfHelper implements TravelHelper {
} }
String category = amenity.getTagSuffix("category_"); String category = amenity.getTagSuffix("category_");
if (category != null) { if (category != null) {
wptPt.category = Algorithms.capitalizeFirstLetter(category); wptPt.category = capitalizeFirstLetter(category);
} }
return wptPt; return wptPt;
} }
@ -266,7 +409,7 @@ public class TravelObfHelper implements TravelHelper {
for (BinaryMapIndexReader reader : getReaders()) { for (BinaryMapIndexReader reader : getReaders()) {
try { try {
SearchRequest<Amenity> searchRequest = BinaryMapIndexReader.buildSearchPoiRequest(0, 0, searchQuery, SearchRequest<Amenity> searchRequest = BinaryMapIndexReader.buildSearchPoiRequest(0, 0, searchQuery,
0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, getSearchFilter(false), new ResultMatcher<Amenity>() { 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, getSearchFilter(ROUTE_ARTICLE), new ResultMatcher<Amenity>() {
@Override @Override
public boolean publish(Amenity object) { public boolean publish(Amenity object) {
List<String> otherNames = object.getAllNames(false); List<String> otherNames = object.getAllNames(false);
@ -441,7 +584,7 @@ public class TravelObfHelper implements TravelHelper {
for (BinaryMapIndexReader reader : getReaders()) { for (BinaryMapIndexReader reader : getReaders()) {
try { try {
SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest( SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(
0, 0, title, 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, getSearchFilter(false), 0, 0, title, 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, getSearchFilter(ROUTE_ARTICLE),
new ResultMatcher<Amenity>() { new ResultMatcher<Amenity>() {
boolean done = false; boolean done = false;
@ -485,7 +628,7 @@ public class TravelObfHelper implements TravelHelper {
@Nullable @Nullable
private TravelArticle getCachedArticle(@NonNull TravelArticleIdentifier articleId, @NonNull String lang, private TravelArticle getCachedArticle(@NonNull TravelArticleIdentifier articleId, @NonNull String lang,
boolean forceReadPoints, @Nullable GpxReadCallback callback) { boolean readGpx, @Nullable GpxReadCallback callback) {
TravelArticle article = null; TravelArticle article = null;
Map<String, TravelArticle> articles = cachedArticles.get(articleId); Map<String, TravelArticle> articles = cachedArticles.get(articleId);
if (articles != null) { if (articles != null) {
@ -502,9 +645,9 @@ public class TravelObfHelper implements TravelHelper {
} }
} }
if (article == null && articles == null) { if (article == null && articles == null) {
article = findArticleById(articleId, lang, callback); article = findArticleById(articleId, lang, readGpx, callback);
} }
if (article != null && forceReadPoints && !Algorithms.isEmpty(lang)) { if (article != null && readGpx && !Algorithms.isEmpty(lang)) {
readGpxFile(article, callback); readGpxFile(article, callback);
} }
return article; return article;
@ -518,8 +661,8 @@ public class TravelObfHelper implements TravelHelper {
} }
} }
private TravelArticle findArticleById(@NonNull final TravelArticleIdentifier articleId, private synchronized TravelArticle findArticleById(@NonNull final TravelArticleIdentifier articleId,
final String lang, @Nullable GpxReadCallback callback) { final String lang, boolean readGpx, @Nullable GpxReadCallback callback) {
TravelArticle article = null; TravelArticle article = null;
final boolean isDbArticle = articleId.file != null && articleId.file.getName().endsWith(IndexConstants.BINARY_WIKIVOYAGE_MAP_INDEX_EXT); final boolean isDbArticle = articleId.file != null && articleId.file.getName().endsWith(IndexConstants.BINARY_WIKIVOYAGE_MAP_INDEX_EXT);
final List<Amenity> amenities = new ArrayList<>(); final List<Amenity> amenities = new ArrayList<>();
@ -530,12 +673,13 @@ public class TravelObfHelper implements TravelHelper {
} }
SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(0, 0, SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(0, 0,
Algorithms.emptyIfNull(articleId.title), 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, Algorithms.emptyIfNull(articleId.title), 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE,
getSearchFilter(false), new ResultMatcher<Amenity>() { getSearchFilter(ROUTE_ARTICLE), new ResultMatcher<Amenity>() {
boolean done = false; boolean done = false;
@Override @Override
public boolean publish(Amenity amenity) { public boolean publish(Amenity amenity) {
if (Algorithms.stringsEqual(articleId.routeId, Algorithms.emptyIfNull(amenity.getTagContent(Amenity.ROUTE_ID, null))) || isDbArticle) { if (Algorithms.stringsEqual(articleId.routeId,
Algorithms.emptyIfNull(amenity.getTagContent(Amenity.ROUTE_ID))) || isDbArticle) {
amenities.add(amenity); amenities.add(amenity);
done = true; done = true;
} }
@ -562,7 +706,7 @@ public class TravelObfHelper implements TravelHelper {
LOG.error(e.getMessage()); LOG.error(e.getMessage());
} }
if (!Algorithms.isEmpty(amenities)) { if (!Algorithms.isEmpty(amenities)) {
article = cacheTravelArticles(reader.getFile(), amenities.get(0), lang, true, callback); article = cacheTravelArticles(reader.getFile(), amenities.get(0), lang, readGpx, callback);
} }
} }
return article; return article;
@ -585,7 +729,7 @@ public class TravelObfHelper implements TravelHelper {
@Nullable @Nullable
@Override @Override
public TravelArticle getArticleByTitle(@NonNull final String title, @NonNull QuadRect rect, public synchronized TravelArticle getArticleByTitle(@NonNull final String title, @NonNull QuadRect rect,
@NonNull final String lang, boolean readGpx, @Nullable GpxReadCallback callback) { @NonNull final String lang, boolean readGpx, @Nullable GpxReadCallback callback) {
TravelArticle article = null; TravelArticle article = null;
final List<Amenity> amenities = new ArrayList<>(); final List<Amenity> amenities = new ArrayList<>();
@ -606,7 +750,7 @@ public class TravelObfHelper implements TravelHelper {
for (BinaryMapIndexReader reader : getReaders()) { for (BinaryMapIndexReader reader : getReaders()) {
try { try {
SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest( SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(
x, y, title, left, right, top, bottom, getSearchFilter(false), x, y, title, left, right, top, bottom, getSearchFilter(ROUTE_ARTICLE),
new ResultMatcher<Amenity>() { new ResultMatcher<Amenity>() {
boolean done = false; boolean done = false;
@ -693,7 +837,12 @@ public class TravelObfHelper implements TravelHelper {
@NonNull @NonNull
@Override @Override
public File createGpxFile(@NonNull TravelArticle article) { public File createGpxFile(@NonNull TravelArticle article) {
final GPXFile gpx = article.getGpxFile(); final GPXFile gpx;
if (article instanceof TravelGpx) {
gpx = buildTravelGpxFile((TravelGpx) article);
} else {
gpx = article.getGpxFile();
}
File file = app.getAppPath(IndexConstants.GPX_TRAVEL_DIR + getGPXName(article)); File file = app.getAppPath(IndexConstants.GPX_TRAVEL_DIR + getGPXName(article));
writeGpxFile(file, gpx); writeGpxFile(file, gpx);
return file; return file;

View file

@ -20,6 +20,8 @@ import net.osmand.plus.wikivoyage.explore.travelcards.StartEditingTravelCard;
import net.osmand.plus.wikivoyage.explore.travelcards.StartEditingTravelCard.StartEditingTravelVH; import net.osmand.plus.wikivoyage.explore.travelcards.StartEditingTravelCard.StartEditingTravelVH;
import net.osmand.plus.wikivoyage.explore.travelcards.TravelDownloadUpdateCard; import net.osmand.plus.wikivoyage.explore.travelcards.TravelDownloadUpdateCard;
import net.osmand.plus.wikivoyage.explore.travelcards.TravelDownloadUpdateCard.DownloadUpdateVH; import net.osmand.plus.wikivoyage.explore.travelcards.TravelDownloadUpdateCard.DownloadUpdateVH;
import net.osmand.plus.wikivoyage.explore.travelcards.TravelGpxCard;
import net.osmand.plus.wikivoyage.explore.travelcards.TravelGpxCard.TravelGpxVH;
import net.osmand.plus.wikivoyage.explore.travelcards.TravelNeededMapsCard; import net.osmand.plus.wikivoyage.explore.travelcards.TravelNeededMapsCard;
import net.osmand.plus.wikivoyage.explore.travelcards.TravelNeededMapsCard.NeededMapsVH; import net.osmand.plus.wikivoyage.explore.travelcards.TravelNeededMapsCard.NeededMapsVH;
@ -48,6 +50,9 @@ public class ExploreRvAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
case ArticleTravelCard.TYPE: case ArticleTravelCard.TYPE:
return new ArticleTravelVH(inflate(parent, R.layout.wikivoyage_article_card)); return new ArticleTravelVH(inflate(parent, R.layout.wikivoyage_article_card));
case TravelGpxCard.TYPE:
return new TravelGpxVH(inflate(parent, R.layout.wikivoyage_travel_gpx_card));
case TravelDownloadUpdateCard.TYPE: case TravelDownloadUpdateCard.TYPE:
return new DownloadUpdateVH(inflate(parent, R.layout.travel_download_update_card)); return new DownloadUpdateVH(inflate(parent, R.layout.travel_download_update_card));
@ -74,6 +79,10 @@ public class ExploreRvAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
HeaderTravelCard headerTravelCard = (HeaderTravelCard) item; HeaderTravelCard headerTravelCard = (HeaderTravelCard) item;
headerTravelCard.setArticleItemCount(getArticleItemCount()); headerTravelCard.setArticleItemCount(getArticleItemCount());
headerTravelCard.bindViewHolder(viewHolder); headerTravelCard.bindViewHolder(viewHolder);
} else if (viewHolder instanceof ArticleTravelVH && item instanceof TravelGpxCard) {
TravelGpxCard travelGpxCard = (TravelGpxCard) item;
travelGpxCard.setLastItem(position == getLastArticleItemIndex());
travelGpxCard.bindViewHolder(viewHolder);
} else if (viewHolder instanceof ArticleTravelVH && item instanceof ArticleTravelCard) { } else if (viewHolder instanceof ArticleTravelVH && item instanceof ArticleTravelCard) {
ArticleTravelCard articleTravelCard = (ArticleTravelCard) item; ArticleTravelCard articleTravelCard = (ArticleTravelCard) item;
articleTravelCard.setLastItem(position == getLastArticleItemIndex()); articleTravelCard.setLastItem(position == getLastArticleItemIndex());
@ -96,7 +105,7 @@ public class ExploreRvAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
public int getArticleItemCount() { public int getArticleItemCount() {
int count = 0; int count = 0;
for (BaseTravelCard o : items) { for (BaseTravelCard o : items) {
if (o instanceof ArticleTravelCard) { if (o instanceof ArticleTravelCard || o instanceof TravelGpxCard) {
count++; count++;
} }
} }
@ -106,7 +115,7 @@ public class ExploreRvAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
private int getLastArticleItemIndex() { private int getLastArticleItemIndex() {
for (int i = items.size() - 1; i > 0; i--) { for (int i = items.size() - 1; i > 0; i--) {
BaseTravelCard o = items.get(i); BaseTravelCard o = items.get(i);
if (o instanceof ArticleTravelCard) { if (o instanceof ArticleTravelCard || o instanceof TravelGpxCard) {
return i; return i;
} }
} }

View file

@ -29,6 +29,7 @@ import net.osmand.plus.download.DownloadResources;
import net.osmand.plus.download.DownloadValidationManager; import net.osmand.plus.download.DownloadValidationManager;
import net.osmand.plus.download.IndexItem; import net.osmand.plus.download.IndexItem;
import net.osmand.plus.wikivoyage.data.TravelArticle; import net.osmand.plus.wikivoyage.data.TravelArticle;
import net.osmand.plus.wikivoyage.data.TravelGpx;
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.explore.travelcards.ArticleTravelCard; import net.osmand.plus.wikivoyage.explore.travelcards.ArticleTravelCard;
@ -37,6 +38,7 @@ import net.osmand.plus.wikivoyage.explore.travelcards.HeaderTravelCard;
import net.osmand.plus.wikivoyage.explore.travelcards.OpenBetaTravelCard; import net.osmand.plus.wikivoyage.explore.travelcards.OpenBetaTravelCard;
import net.osmand.plus.wikivoyage.explore.travelcards.StartEditingTravelCard; import net.osmand.plus.wikivoyage.explore.travelcards.StartEditingTravelCard;
import net.osmand.plus.wikivoyage.explore.travelcards.TravelDownloadUpdateCard; import net.osmand.plus.wikivoyage.explore.travelcards.TravelDownloadUpdateCard;
import net.osmand.plus.wikivoyage.explore.travelcards.TravelGpxCard;
import net.osmand.plus.wikivoyage.explore.travelcards.TravelNeededMapsCard; import net.osmand.plus.wikivoyage.explore.travelcards.TravelNeededMapsCard;
import java.io.IOException; import java.io.IOException;
@ -173,14 +175,17 @@ public class ExploreTabFragment extends BaseOsmAndFragment implements DownloadEv
if (!Version.isPaidVersion(app) && !OpenBetaTravelCard.isClosed()) { if (!Version.isPaidVersion(app) && !OpenBetaTravelCard.isClosed()) {
items.add(new OpenBetaTravelCard(activity, nightMode)); items.add(new OpenBetaTravelCard(activity, nightMode));
} }
if (app.getTravelHelper().isAnyTravelBookPresent()) {
items.add(new HeaderTravelCard(app, nightMode, getString(R.string.popular_destinations)));
List<TravelArticle> popularArticles = app.getTravelHelper().getPopularArticles(); List<TravelArticle> popularArticles = app.getTravelHelper().getPopularArticles();
if (!popularArticles.isEmpty()) {
items.add(new HeaderTravelCard(app, nightMode, getString(R.string.popular_destinations)));
for (TravelArticle article : popularArticles) { for (TravelArticle article : popularArticles) {
if (article instanceof TravelGpx) {
items.add(new TravelGpxCard(app, nightMode, (TravelGpx) article, getActivity()));
} else {
items.add(new ArticleTravelCard(app, nightMode, article, activity.getSupportFragmentManager())); items.add(new ArticleTravelCard(app, nightMode, article, activity.getSupportFragmentManager()));
} }
} }
}
items.add(new StartEditingTravelCard(activity, nightMode)); items.add(new StartEditingTravelCard(activity, nightMode));
adapter.setItems(items); adapter.setItems(items);
final DownloadIndexesThread downloadThread = app.getDownloadThread(); final DownloadIndexesThread downloadThread = app.getDownloadThread();

View file

@ -7,6 +7,8 @@ import android.view.ViewGroup;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.DrawableRes;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -16,16 +18,20 @@ import com.squareup.picasso.Callback;
import com.squareup.picasso.Picasso; import com.squareup.picasso.Picasso;
import com.squareup.picasso.RequestCreator; import com.squareup.picasso.RequestCreator;
import net.osmand.AndroidUtils;
import net.osmand.PicassoUtils; import net.osmand.PicassoUtils;
import net.osmand.plus.OsmAndFormatter;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.R; import net.osmand.plus.R;
import net.osmand.plus.UiUtilities; import net.osmand.plus.UiUtilities;
import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.widgets.tools.CropCircleTransformation; import net.osmand.plus.widgets.tools.CropCircleTransformation;
import net.osmand.plus.wikipedia.WikiArticleHelper; import net.osmand.plus.wikipedia.WikiArticleHelper;
import net.osmand.plus.wikivoyage.WikivoyageUtils; import net.osmand.plus.wikivoyage.WikivoyageUtils;
import net.osmand.plus.wikivoyage.data.TravelArticle; import net.osmand.plus.wikivoyage.data.TravelArticle;
import net.osmand.plus.wikivoyage.data.TravelGpx;
import net.osmand.plus.wikivoyage.data.TravelLocalDataHelper; import net.osmand.plus.wikivoyage.data.TravelLocalDataHelper;
import net.osmand.plus.wikivoyage.explore.travelcards.TravelGpxCard;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -33,7 +39,8 @@ import java.util.List;
public class SavedArticlesRvAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { public class SavedArticlesRvAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int HEADER_TYPE = 0; private static final int HEADER_TYPE = 0;
private static final int ITEM_TYPE = 1; private static final int ARTICLE_TYPE = 1;
private static final int GPX_TYPE = 2;
private final OsmandApplication app; private final OsmandApplication app;
private final OsmandSettings settings; private final OsmandSettings settings;
@ -45,6 +52,7 @@ public class SavedArticlesRvAdapter extends RecyclerView.Adapter<RecyclerView.Vi
private final Drawable readIcon; private final Drawable readIcon;
private final Drawable deleteIcon; private final Drawable deleteIcon;
private PicassoUtils picasso; private PicassoUtils picasso;
boolean nightMode;
public void setListener(Listener listener) { public void setListener(Listener listener) {
this.listener = listener; this.listener = listener;
@ -54,21 +62,34 @@ public class SavedArticlesRvAdapter extends RecyclerView.Adapter<RecyclerView.Vi
this.app = app; this.app = app;
this.settings = app.getSettings(); this.settings = app.getSettings();
picasso = PicassoUtils.getPicasso(app); picasso = PicassoUtils.getPicasso(app);
nightMode = !app.getSettings().isLightContent();
readIcon = getActiveIcon(R.drawable.ic_action_read_article);
deleteIcon = getActiveIcon(R.drawable.ic_action_read_later_fill);
}
int colorId = settings.isLightContent() private Drawable getActiveIcon(@DrawableRes int iconId) {
? R.color.wikivoyage_active_light : R.color.wikivoyage_active_dark; int colorId = nightMode ? R.color.wikivoyage_active_dark : R.color.wikivoyage_active_light;
UiUtilities ic = app.getUIUtilities(); return app.getUIUtilities().getIcon(iconId, colorId);
readIcon = ic.getIcon(R.drawable.ic_action_read_article, colorId);
deleteIcon = ic.getIcon(R.drawable.ic_action_read_later_fill, colorId);
} }
@NonNull @NonNull
@Override @Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
boolean header = viewType == HEADER_TYPE; switch (viewType) {
int layoutId = header ? R.layout.wikivoyage_list_header : R.layout.wikivoyage_article_card; case HEADER_TYPE:
View itemView = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false); return new HeaderVH(inflate(parent, R.layout.wikivoyage_list_header));
return header ? new HeaderVH(itemView) : new ItemVH(itemView); case ARTICLE_TYPE:
return new ItemVH(inflate(parent, R.layout.wikivoyage_article_card));
case GPX_TYPE:
return new TravelGpxCard.TravelGpxVH(inflate(parent, R.layout.wikivoyage_travel_gpx_card));
default:
throw new RuntimeException("Unsupported view type: " + viewType);
}
}
@NonNull
private View inflate(@NonNull ViewGroup parent, @LayoutRes int layoutId) {
return LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
} }
@Override @Override
@ -77,7 +98,7 @@ public class SavedArticlesRvAdapter extends RecyclerView.Adapter<RecyclerView.Vi
final HeaderVH holder = (HeaderVH) viewHolder; final HeaderVH holder = (HeaderVH) viewHolder;
holder.title.setText((String) getItem(position)); holder.title.setText((String) getItem(position));
holder.description.setText(String.valueOf(items.size() - 1)); holder.description.setText(String.valueOf(items.size() - 1));
} else { } else if (viewHolder instanceof ItemVH) {
final ItemVH holder = (ItemVH) viewHolder; final ItemVH holder = (ItemVH) viewHolder;
TravelArticle article = (TravelArticle) getItem(position); TravelArticle article = (TravelArticle) getItem(position);
final String url = TravelArticle.getImageUrl(article.getImageTitle(), false); final String url = TravelArticle.getImageUrl(article.getImageTitle(), false);
@ -111,6 +132,52 @@ public class SavedArticlesRvAdapter extends RecyclerView.Adapter<RecyclerView.Vi
holder.rightButton.setCompoundDrawablesWithIntrinsicBounds(null, null, deleteIcon, null); holder.rightButton.setCompoundDrawablesWithIntrinsicBounds(null, null, deleteIcon, null);
holder.divider.setVisibility(lastItem ? View.GONE : View.VISIBLE); holder.divider.setVisibility(lastItem ? View.GONE : View.VISIBLE);
holder.shadow.setVisibility(lastItem ? View.VISIBLE : View.GONE); holder.shadow.setVisibility(lastItem ? View.VISIBLE : View.GONE);
} else if (viewHolder instanceof TravelGpxCard.TravelGpxVH) {
final TravelGpx article = (TravelGpx) getItem(position);
final TravelGpxCard.TravelGpxVH holder = (TravelGpxCard.TravelGpxVH) viewHolder;
holder.title.setText(article.getTitle());
Drawable icon = getActiveIcon(R.drawable.ic_action_user_account_16);
holder.user.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
holder.user.setText(WikiArticleHelper.getPartialContent(article.user));
AndroidUtils.setBackground(app, holder.user, nightMode, R.drawable.btn_border_bg_light, R.drawable.btn_border_bg_dark);
holder.distance.setText(OsmAndFormatter.getFormattedDistance(article.totalDistance, app));
holder.diffElevationUp.setText(OsmAndFormatter.getFormattedAlt(article.diffElevationUp, app));
holder.diffElevationDown.setText(OsmAndFormatter.getFormattedAlt(article.diffElevationDown, app));
holder.leftButton.setText(app.getString(R.string.shared_string_view));
View.OnClickListener readClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) {
listener.openArticle(article);
}
}
};
holder.leftButton.setOnClickListener(readClickListener);
holder.itemView.setOnClickListener(readClickListener);
holder.leftButton.setCompoundDrawablesWithIntrinsicBounds(readIcon, null, null, null);
updateSaveButton(holder, article);
}
}
private void updateSaveButton(final TravelGpxCard.TravelGpxVH holder, final TravelGpx article) {
if (article != null) {
final TravelLocalDataHelper helper = app.getTravelHelper().getBookmarksHelper();
final boolean saved = helper.isArticleSaved(article);
Drawable icon = getActiveIcon(saved ? R.drawable.ic_action_read_later_fill : R.drawable.ic_action_read_later);
holder.rightButton.setText(saved ? R.string.shared_string_remove : R.string.shared_string_save);
holder.rightButton.setCompoundDrawablesWithIntrinsicBounds(null, null, icon, null);
holder.rightButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (saved) {
helper.removeArticleFromSaved(article);
} else {
app.getTravelHelper().createGpxFile(article);
helper.addArticleToSaved(article);
}
updateSaveButton(holder, article);
}
});
} }
} }
@ -118,8 +185,10 @@ public class SavedArticlesRvAdapter extends RecyclerView.Adapter<RecyclerView.Vi
public int getItemViewType(int position) { public int getItemViewType(int position) {
if (getItem(position) instanceof String) { if (getItem(position) instanceof String) {
return HEADER_TYPE; return HEADER_TYPE;
} else if (getItem(position) instanceof TravelGpx) {
return GPX_TYPE;
} }
return ITEM_TYPE; return ARTICLE_TYPE;
} }
@Override @Override

View file

@ -8,6 +8,7 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.DiffUtil; import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
@ -17,12 +18,15 @@ import net.osmand.PlatformUtil;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R; import net.osmand.plus.R;
import net.osmand.plus.base.BaseOsmAndFragment; import net.osmand.plus.base.BaseOsmAndFragment;
import net.osmand.plus.track.TrackMenuFragment;
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.TravelGpx;
import net.osmand.plus.wikivoyage.data.TravelLocalDataHelper; import net.osmand.plus.wikivoyage.data.TravelLocalDataHelper;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -48,11 +52,19 @@ public class SavedArticlesTabFragment extends BaseOsmAndFragment implements Trav
adapter.setListener(new SavedArticlesRvAdapter.Listener() { adapter.setListener(new SavedArticlesRvAdapter.Listener() {
@Override @Override
public void openArticle(TravelArticle article) { public void openArticle(TravelArticle article) {
if (article instanceof TravelGpx) {
FragmentActivity activity = getActivity();
if (activity != null) {
File file = app.getTravelHelper().createGpxFile(article);
TrackMenuFragment.openTrack(getActivity(), file, null);
}
} else {
FragmentManager fm = getFragmentManager(); FragmentManager fm = getFragmentManager();
if (fm != null) { if (fm != null) {
WikivoyageArticleDialogFragment.showInstance(app, fm, article.generateIdentifier(), article.getLang()); WikivoyageArticleDialogFragment.showInstance(app, fm, article.generateIdentifier(), article.getLang());
} }
} }
}
}); });
final RecyclerView rv = (RecyclerView) mainView.findViewById(R.id.recycler_view); final RecyclerView rv = (RecyclerView) mainView.findViewById(R.id.recycler_view);

View file

@ -0,0 +1,125 @@
package net.osmand.plus.wikivoyage.explore.travelcards;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity;
import androidx.recyclerview.widget.RecyclerView;
import net.osmand.AndroidUtils;
import net.osmand.plus.OsmAndFormatter;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.track.TrackMenuFragment;
import net.osmand.plus.wikivoyage.data.TravelGpx;
import net.osmand.plus.wikivoyage.data.TravelLocalDataHelper;
import java.io.File;
public class TravelGpxCard extends BaseTravelCard {
public static final int TYPE = 3;
private final TravelGpx article;
private final Drawable readIcon;
private final FragmentActivity activity;
private boolean isLastItem;
public TravelGpxCard(@NonNull OsmandApplication app, boolean nightMode, @NonNull TravelGpx article,
@NonNull FragmentActivity activity) {
super(app, nightMode);
this.article = article;
readIcon = getActiveIcon(R.drawable.ic_action_read_article);
this.activity = activity;
}
@Override
public void bindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder) {
if (viewHolder instanceof TravelGpxVH) {
final TravelGpxVH holder = (TravelGpxVH) viewHolder;
holder.title.setText(article.getTitle());
Drawable icon = getActiveIcon(R.drawable.ic_action_user_account_16);
holder.user.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
holder.user.setText(article.user);
AndroidUtils.setBackground(app, holder.user, nightMode, R.drawable.btn_border_bg_light, R.drawable.btn_border_bg_dark);
holder.distance.setText(OsmAndFormatter.getFormattedDistance(article.totalDistance, app));
holder.diffElevationUp.setText(OsmAndFormatter.getFormattedAlt(article.diffElevationUp, app));
holder.diffElevationDown.setText(OsmAndFormatter.getFormattedAlt(article.diffElevationDown, app));
holder.leftButton.setText(app.getString(R.string.shared_string_view));
View.OnClickListener readClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
if (activity != null) {
File file = app.getTravelHelper().createGpxFile(article);
TrackMenuFragment.openTrack(activity, file, null);
}
}
};
holder.leftButton.setOnClickListener(readClickListener);
holder.itemView.setOnClickListener(readClickListener);
holder.leftButton.setCompoundDrawablesWithIntrinsicBounds(readIcon, null, null, null);
updateSaveButton(holder);
holder.divider.setVisibility(isLastItem ? View.GONE : View.VISIBLE);
holder.shadow.setVisibility(isLastItem ? View.VISIBLE : View.GONE);
}
}
private void updateSaveButton(final TravelGpxVH holder) {
if (article != null) {
final TravelLocalDataHelper helper = app.getTravelHelper().getBookmarksHelper();
final boolean saved = helper.isArticleSaved(article);
Drawable icon = getActiveIcon(saved ? R.drawable.ic_action_read_later_fill : R.drawable.ic_action_read_later);
holder.rightButton.setText(saved ? R.string.shared_string_remove : R.string.shared_string_save);
holder.rightButton.setCompoundDrawablesWithIntrinsicBounds(null, null, icon, null);
holder.rightButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (saved) {
helper.removeArticleFromSaved(article);
} else {
app.getTravelHelper().createGpxFile(article);
helper.addArticleToSaved(article);
}
updateSaveButton(holder);
}
});
}
}
public static class TravelGpxVH extends RecyclerView.ViewHolder {
public final TextView title;
public final TextView user;
public final TextView distance;
public final TextView diffElevationUp;
public final TextView diffElevationDown;
public final TextView leftButton;
public final TextView rightButton;
public final View divider;
public final View shadow;
public TravelGpxVH(final View itemView) {
super(itemView);
title = itemView.findViewById(R.id.title);
user = itemView.findViewById(R.id.user_name);
distance = itemView.findViewById(R.id.distance);
diffElevationUp = itemView.findViewById(R.id.diff_ele_up);
diffElevationDown = itemView.findViewById(R.id.diff_ele_down);
leftButton = itemView.findViewById(R.id.left_button);
rightButton = itemView.findViewById(R.id.right_button);
divider = itemView.findViewById(R.id.divider);
shadow = itemView.findViewById(R.id.shadow);
}
}
public void setLastItem(boolean lastItem) {
isLastItem = lastItem;
}
@Override
public int getCardType() {
return TYPE;
}
}