commit
b34163ba76
27 changed files with 991 additions and 297 deletions
157
GPX.md
157
GPX.md
|
@ -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>
|
|
||||||
```
|
|
|
@ -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);
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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"
|
||||||
|
|
282
OsmAnd/res/layout/wikivoyage_travel_gpx_card.xml
Normal file
282
OsmAnd/res/layout/wikivoyage_travel_gpx_card.xml
Normal 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>
|
|
@ -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);
|
||||||
|
|
|
@ -1200,7 +1200,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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -131,12 +131,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());
|
||||||
|
@ -170,7 +170,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() {
|
||||||
|
|
|
@ -42,9 +42,8 @@ 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);
|
TrackMenuFragment.showInstance(mapActivity, selectedGpxPoint.getSelectedGpxFile());
|
||||||
TrackMenuFragment.showInstance(mapActivity, selectedGpxFile.getGpxFile().path, selectedGpxFile.isShowCurrentTrack());
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
leftTitleButtonController.caption = mapActivity.getString(R.string.shared_string_open_track);
|
leftTitleButtonController.caption = mapActivity.getString(R.string.shared_string_open_track);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
@ -185,13 +186,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 +231,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() {
|
||||||
MapActivity mapActivity = getMapActivity();
|
if (getCurrentMenuState() != MenuState.HEADER_ONLY && isPortrait()) {
|
||||||
if (mapActivity != null) {
|
openMenuHeaderOnly();
|
||||||
mapActivity.launchPrevActivityIntent();
|
} else {
|
||||||
|
dismiss();
|
||||||
|
|
||||||
|
MapActivity mapActivity = getMapActivity();
|
||||||
|
if (mapActivity != null) {
|
||||||
|
MapContextMenu contextMenu = mapActivity.getContextMenu();
|
||||||
|
if (contextMenu.isActive() && contextMenu.getPointDescription() != null
|
||||||
|
&& contextMenu.getPointDescription().isGpxPoint()) {
|
||||||
|
contextMenu.show();
|
||||||
|
} else {
|
||||||
|
mapActivity.launchPrevActivityIntent();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
dismiss();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -249,6 +265,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
|
||||||
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 +275,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 +286,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 +298,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 +444,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 +490,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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -659,7 +686,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 +824,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 +864,8 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
|
||||||
menuType = type;
|
menuType = type;
|
||||||
setupCards();
|
setupCards();
|
||||||
updateHeader();
|
updateHeader();
|
||||||
|
updateCardsLayout();
|
||||||
|
calculateLayoutAndUpdateMenuState();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -845,6 +874,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) {
|
||||||
|
|
|
@ -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,17 +340,20 @@ public class WikivoyageArticleDialogFragment extends WikiArticleBaseDialogFragme
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateTrackButton(boolean processing, @Nullable GPXFile gpxFile) {
|
private void updateTrackButton(boolean processing, @Nullable GPXFile gpxFile) {
|
||||||
if (processing) {
|
Context ctx = getContext();
|
||||||
trackButton.setVisibility(View.GONE);
|
if (ctx != null) {
|
||||||
gpxProgress.setVisibility(View.VISIBLE);
|
if (processing) {
|
||||||
} else {
|
|
||||||
if (gpxFile != null && gpxFile.getPointsSize() > 0) {
|
|
||||||
trackButton.setVisibility(View.VISIBLE);
|
|
||||||
trackButton.setText(getString(R.string.shared_string_gpx_points) + " (" + gpxFile.getPointsSize() + ")");
|
|
||||||
} else {
|
|
||||||
trackButton.setVisibility(View.GONE);
|
trackButton.setVisibility(View.GONE);
|
||||||
|
gpxProgress.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
if (gpxFile != null && gpxFile.getPointsSize() > 0) {
|
||||||
|
trackButton.setVisibility(View.VISIBLE);
|
||||||
|
trackButton.setText(ctx.getString(R.string.shared_string_gpx_points) + " (" + gpxFile.getPointsSize() + ")");
|
||||||
|
} else {
|
||||||
|
trackButton.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
gpxProgress.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
gpxProgress.setVisibility(View.GONE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
14
OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelGpx.java
Normal file
14
OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelGpx.java
Normal 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;
|
||||||
|
}
|
|
@ -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));
|
||||||
|
|
|
@ -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,47 +107,69 @@ 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<>();
|
||||||
|
final LatLon location = app.getMapViewTrackingUtilities().getMapLocation();
|
||||||
|
for (final BinaryMapIndexReader reader : getReaders()) {
|
||||||
try {
|
try {
|
||||||
final LatLon location = app.getMapViewTrackingUtilities().getMapLocation();
|
searchAmenity(amenities, location, reader, POPULAR_ARTICLES_SEARCH_RADIUS, -1, ROUTE_ARTICLE);
|
||||||
SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(
|
searchAmenity(amenities, location, reader, GPX_TRACKS_SEARCH_RADIUS, 15, ROUTE_TRACK);
|
||||||
location, POPULAR_ARTICLES_SEARCH_RADIUS, -1, getSearchFilter(false), null);
|
} catch (Exception e) {
|
||||||
List<Amenity> amenities = reader.searchPoi(req);
|
LOG.error(e.getMessage(), e);
|
||||||
if (amenities.size() > 0) {
|
}
|
||||||
Collections.sort(amenities, new Comparator<Amenity>() {
|
}
|
||||||
@Override
|
|
||||||
public int compare(Amenity a1, Amenity a2) {
|
if (amenities.size() > 0) {
|
||||||
int d1 = (int) (MapUtils.getDistance(a1.getLocation().getLatitude(), a1.getLocation().getLongitude(),
|
Collections.sort(amenities, new Comparator<Pair<File, Amenity>>() {
|
||||||
location.getLatitude(), location.getLongitude()));
|
@Override
|
||||||
int d2 = (int) (MapUtils.getDistance(a2.getLocation().getLatitude(), a2.getLocation().getLongitude(),
|
public int compare(Pair article1, Pair article2) {
|
||||||
location.getLatitude(), location.getLongitude()));
|
int d1 = (int) (MapUtils.getDistance(((Amenity) article1.second).getLocation(), location));
|
||||||
return d1 < d2 ? -1 : (d1 == d2 ? 0 : 1);
|
int d2 = (int) (MapUtils.getDistance(((Amenity) article2.second).getLocation(), location));
|
||||||
}
|
return d1 < d2 ? -1 : (d1 == d2 ? 0 : 1);
|
||||||
});
|
}
|
||||||
for (Amenity amenity : amenities) {
|
});
|
||||||
if (!Algorithms.isEmpty(amenity.getName(lang))) {
|
for (Pair<File, Amenity> amenity : amenities) {
|
||||||
TravelArticle article = cacheTravelArticles(reader.getFile(), amenity, lang, false, null);
|
if (!Algorithms.isEmpty(amenity.second.getName(lang))) {
|
||||||
if (article != null) {
|
TravelArticle article = cacheTravelArticles(amenity.first, amenity.second, lang, false, null);
|
||||||
popularArticles.add(article);
|
if (article != null) {
|
||||||
if (popularArticles.size() >= MAX_POPULAR_ARTICLES_COUNT) {
|
popularArticles.add(article);
|
||||||
break;
|
if (popularArticles.size() >= MAX_POPULAR_ARTICLES_COUNT) {
|
||||||
}
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} 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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,12 +175,15 @@ 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()) {
|
List<TravelArticle> popularArticles = app.getTravelHelper().getPopularArticles();
|
||||||
|
if (!popularArticles.isEmpty()) {
|
||||||
items.add(new HeaderTravelCard(app, nightMode, getString(R.string.popular_destinations)));
|
items.add(new HeaderTravelCard(app, nightMode, getString(R.string.popular_destinations)));
|
||||||
|
|
||||||
List<TravelArticle> popularArticles = app.getTravelHelper().getPopularArticles();
|
|
||||||
for (TravelArticle article : popularArticles) {
|
for (TravelArticle article : popularArticles) {
|
||||||
items.add(new ArticleTravelCard(app, nightMode, article, activity.getSupportFragmentManager()));
|
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 StartEditingTravelCard(activity, nightMode));
|
items.add(new StartEditingTravelCard(activity, nightMode));
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,9 +52,17 @@ 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) {
|
||||||
FragmentManager fm = getFragmentManager();
|
if (article instanceof TravelGpx) {
|
||||||
if (fm != null) {
|
FragmentActivity activity = getActivity();
|
||||||
WikivoyageArticleDialogFragment.showInstance(app, fm, article.generateIdentifier(), article.getLang());
|
if (activity != null) {
|
||||||
|
File file = app.getTravelHelper().createGpxFile(article);
|
||||||
|
TrackMenuFragment.openTrack(getActivity(), file, null);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FragmentManager fm = getFragmentManager();
|
||||||
|
if (fm != null) {
|
||||||
|
WikivoyageArticleDialogFragment.showInstance(app, fm, article.generateIdentifier(), article.getLang());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue