commit
04c7208d23
58 changed files with 2983 additions and 1950 deletions
|
@ -1727,13 +1727,12 @@ public class BinaryMapIndexReader {
|
||||||
double half16t = MapUtils.getDistance(lat, MapUtils.getLongitudeFromTile(16, ((int) dx) + 0.5),
|
double half16t = MapUtils.getDistance(lat, MapUtils.getLongitudeFromTile(16, ((int) dx) + 0.5),
|
||||||
lat, MapUtils.getLongitudeFromTile(16, (int) dx));
|
lat, MapUtils.getLongitudeFromTile(16, (int) dx));
|
||||||
double cf31 = ((double) radiusMeters / (half16t * 2)) * (1 << 15);
|
double cf31 = ((double) radiusMeters / (half16t * 2)) * (1 << 15);
|
||||||
int y31 = MapUtils.get31TileNumberY(lat);
|
y = MapUtils.get31TileNumberY(lat);
|
||||||
int x31 = MapUtils.get31TileNumberX(lon);
|
x = MapUtils.get31TileNumberX(lon);
|
||||||
left = (int) (x31 - cf31);
|
left = (int) (x - cf31);
|
||||||
right = (int) (x31 + cf31);
|
right = (int) (x + cf31);
|
||||||
top = (int) (y31 - cf31);
|
top = (int) (y - cf31);
|
||||||
bottom = (int) (y31 + cf31);
|
bottom = (int) (y + cf31);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean publish(T obj) {
|
public boolean publish(T obj) {
|
||||||
|
|
|
@ -764,6 +764,7 @@ public class CommonWords {
|
||||||
addFrequentlyUsed("martiri");
|
addFrequentlyUsed("martiri");
|
||||||
addFrequentlyUsed("verdi");
|
addFrequentlyUsed("verdi");
|
||||||
addFrequentlyUsed("augusta");
|
addFrequentlyUsed("augusta");
|
||||||
|
addFrequentlyUsed("neuburger");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ public class Amenity extends MapObject {
|
||||||
public static final String IS_AGGR_PART = "is_aggr_part";
|
public static final String IS_AGGR_PART = "is_aggr_part";
|
||||||
public static final String CONTENT_JSON = "content_json";
|
public static final String CONTENT_JSON = "content_json";
|
||||||
public static final String ROUTE_ID = "route_id";
|
public static final String ROUTE_ID = "route_id";
|
||||||
|
public static final String ROUTE_SOURCE = "route_source";
|
||||||
|
|
||||||
|
|
||||||
private String subType;
|
private String subType;
|
||||||
|
|
|
@ -16,11 +16,13 @@ import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,10 @@ public class Algorithms {
|
||||||
return map == null || map.size() == 0;
|
return map == null || map.size() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String emptyIfNull(String s) {
|
||||||
|
return s == null ? "" : s;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isEmpty(CharSequence s) {
|
public static boolean isEmpty(CharSequence s) {
|
||||||
return s == null || s.length() == 0;
|
return s == null || s.length() == 0;
|
||||||
}
|
}
|
||||||
|
@ -86,6 +90,10 @@ public class Algorithms {
|
||||||
return s == null || s.trim().length() == 0;
|
return s == null || s.trim().length() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int hash(Object... values) {
|
||||||
|
return Arrays.hashCode(values);
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean stringsEqual(String s1, String s2) {
|
public static boolean stringsEqual(String s1, String s2) {
|
||||||
if (s1 == null && s2 == null) {
|
if (s1 == null && s2 == null) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -109,61 +109,6 @@
|
||||||
android:layout_marginEnd="@dimen/content_padding"
|
android:layout_marginEnd="@dimen/content_padding"
|
||||||
android:layout_marginStart="@dimen/content_padding" />
|
android:layout_marginStart="@dimen/content_padding" />
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/split_color_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:gravity="center_vertical">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/split_interval_view"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1.6"
|
|
||||||
android:minHeight="@dimen/card_row_min_height"
|
|
||||||
android:paddingLeft="@dimen/content_padding"
|
|
||||||
android:paddingStart="@dimen/content_padding"
|
|
||||||
android:paddingRight="0dp"
|
|
||||||
android:paddingEnd="0dp"
|
|
||||||
android:paddingTop="@dimen/content_padding_half"
|
|
||||||
android:paddingBottom="@dimen/content_padding_half"
|
|
||||||
android:background="?attr/selectableItemBackground"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:gravity="center_vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/split_interval_title"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:textColor="?android:textColorPrimary"
|
|
||||||
android:textSize="@dimen/default_list_text_size"
|
|
||||||
android:text="@string/gpx_split_interval"/>
|
|
||||||
|
|
||||||
<net.osmand.plus.widgets.TextViewEx
|
|
||||||
android:id="@+id/split_interval_text"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingLeft="@dimen/content_padding_half"
|
|
||||||
android:paddingRight="@dimen/content_padding_half"
|
|
||||||
android:textColor="?android:textColorPrimary"
|
|
||||||
android:textSize="@dimen/default_list_text_size"
|
|
||||||
osmand:typeface="@string/font_roboto_medium"
|
|
||||||
tools:text="10 km"
|
|
||||||
android:paddingEnd="@dimen/content_padding_half"
|
|
||||||
android:paddingStart="@dimen/content_padding_half" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
|
||||||
android:id="@+id/split_interval_arrow"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
osmand:srcCompat="@drawable/ic_action_arrow_drop_down"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/appearance_view"
|
android:id="@+id/appearance_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
127
OsmAnd/res/layout/track_menu.xml
Normal file
127
OsmAnd/res/layout/track_menu.xml
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout 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="match_parent"
|
||||||
|
android:background="@color/color_transparent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/main_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<include layout="@layout/context_menu_top_shadow" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/route_menu_top_shadow_all"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/list_background_color"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="@dimen/content_padding"
|
||||||
|
android:layout_height="2dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginTop="@dimen/context_menu_padding_margin_tiny"
|
||||||
|
android:layout_marginBottom="@dimen/list_item_button_padding"
|
||||||
|
android:background="?attr/bg_dash_line" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/context_menu_top_shadow_all"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/context_menu_top_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingStart="@dimen/context_menu_padding_margin_default"
|
||||||
|
android:paddingLeft="@dimen/context_menu_padding_margin_default"
|
||||||
|
android:paddingEnd="@dimen/context_menu_padding_margin_default"
|
||||||
|
android:paddingRight="@dimen/context_menu_padding_margin_default">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/context_menu_first_line_top_margin"
|
||||||
|
android:layout_marginEnd="@dimen/context_menu_padding_margin_default"
|
||||||
|
android:layout_marginRight="@dimen/context_menu_padding_margin_default"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
style="@style/TextAppearance.ContextMenuTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:text="@string/search_address_building" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/description"
|
||||||
|
style="@style/TextAppearance.ContextMenuSubtitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
tools:text="@string/amenity_type_finance" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/context_menu_icon_view"
|
||||||
|
android:layout_width="@dimen/map_widget_icon"
|
||||||
|
android:layout_height="@dimen/map_widget_icon"
|
||||||
|
android:layout_marginTop="@dimen/context_menu_padding_margin_default"
|
||||||
|
android:tint="?attr/default_icon_color"
|
||||||
|
osmand:srcCompat="@drawable/ic_action_polygom_dark" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/bottom_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?attr/activity_background_basic"
|
||||||
|
android:foreground="@drawable/bg_contextmenu_shadow"
|
||||||
|
android:foregroundGravity="top|fill_horizontal">
|
||||||
|
|
||||||
|
<net.osmand.plus.LockableScrollView
|
||||||
|
android:id="@+id/route_menu_bottom_scroll"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fillViewport="true">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/route_menu_cards_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingBottom="@dimen/context_menu_action_buttons_height" />
|
||||||
|
|
||||||
|
</net.osmand.plus.LockableScrollView>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
|
android:id="@+id/bottom_navigation"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/context_menu_action_buttons_height"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:background="?attr/wikivoyage_card_bg_color"
|
||||||
|
osmand:itemBackground="?attr/wikivoyage_card_bg_color"
|
||||||
|
osmand:itemIconTint="@color/bottom_navigation_color_selector_light"
|
||||||
|
osmand:itemTextColor="@color/bottom_navigation_color_selector_light"
|
||||||
|
osmand:labelVisibilityMode="labeled"
|
||||||
|
osmand:menu="@menu/track_menu_bottom_navigation" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
6
OsmAnd/res/layout/track_segments_container.xml
Normal file
6
OsmAnd/res/layout/track_segments_container.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical" android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
</LinearLayout>
|
21
OsmAnd/res/menu/track_menu_bottom_navigation.xml
Normal file
21
OsmAnd/res/menu/track_menu_bottom_navigation.xml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- <item-->
|
||||||
|
<!-- android:id="@+id/action_overview"-->
|
||||||
|
<!-- android:icon="@drawable/ic_action_trail_overview"-->
|
||||||
|
<!-- android:title="@string/shared_string_overview" />-->
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_track"
|
||||||
|
android:icon="@drawable/ic_action_polygom_dark"
|
||||||
|
android:title="@string/shared_string_gpx_track" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_points"
|
||||||
|
android:icon="@drawable/ic_action_waypoint"
|
||||||
|
android:title="@string/shared_string_gpx_points" />
|
||||||
|
|
||||||
|
<!-- <item-->
|
||||||
|
<!-- android:id="@+id/action_options"-->
|
||||||
|
<!-- android:icon="@drawable/ic_overflow_menu_white"-->
|
||||||
|
<!-- android:title="@string/shared_string_options" />-->
|
||||||
|
</menu>
|
|
@ -2374,7 +2374,7 @@
|
||||||
<string name="poi_street_cabinet_postal_service">نوع الكبينة/الخزانة: خدمة بريدية</string>
|
<string name="poi_street_cabinet_postal_service">نوع الكبينة/الخزانة: خدمة بريدية</string>
|
||||||
<string name="poi_street_cabinet_gas">نوع الكبينة/الخزانة: غاز</string>
|
<string name="poi_street_cabinet_gas">نوع الكبينة/الخزانة: غاز</string>
|
||||||
<string name="poi_street_cabinet_telecom">نوع الكبينة/الخزانة: اتصالات</string>
|
<string name="poi_street_cabinet_telecom">نوع الكبينة/الخزانة: اتصالات</string>
|
||||||
<string name="poi_street_cabinet_power">نوع الكبينة/الخزانة: طاقة</string>
|
<string name="poi_street_cabinet_power">نوع الكبينة/الخزانة: كهرباء</string>
|
||||||
<string name="poi_in_service_yes">في الخدمة: نعم</string>
|
<string name="poi_in_service_yes">في الخدمة: نعم</string>
|
||||||
<string name="poi_fire_hydrant_style_water_source_cistern">صهريج</string>
|
<string name="poi_fire_hydrant_style_water_source_cistern">صهريج</string>
|
||||||
<string name="poi_water_source_stream">مجرى</string>
|
<string name="poi_water_source_stream">مجرى</string>
|
||||||
|
|
|
@ -740,7 +740,7 @@
|
||||||
<string name="layer_overlay">الخريطة العلوية…</string>
|
<string name="layer_overlay">الخريطة العلوية…</string>
|
||||||
<string name="shared_string_none">بدون</string>
|
<string name="shared_string_none">بدون</string>
|
||||||
<string name="map_overlay">الخريطة العلوية</string>
|
<string name="map_overlay">الخريطة العلوية</string>
|
||||||
<string name="map_overlay_descr">اختيار خريطة التراكب</string>
|
<string name="map_overlay_descr">اختيار خريطة علوية أو سفلية</string>
|
||||||
<string name="poi_dialog_other_tags_message">يتم الاحتفاظ بكافة العلامات الأخرى</string>
|
<string name="poi_dialog_other_tags_message">يتم الاحتفاظ بكافة العلامات الأخرى</string>
|
||||||
<string name="amenity_type_seamark">سيمارك</string>
|
<string name="amenity_type_seamark">سيمارك</string>
|
||||||
<string name="poi_dialog_comment_default">متغير POI</string>
|
<string name="poi_dialog_comment_default">متغير POI</string>
|
||||||
|
@ -2383,7 +2383,7 @@
|
||||||
\n• عرض موقعك والتوجيه
|
\n• عرض موقعك والتوجيه
|
||||||
\n• محاذاة اختيارية للصورة وفق البوصلة أو توجيه الحركة
|
\n• محاذاة اختيارية للصورة وفق البوصلة أو توجيه الحركة
|
||||||
\n• حفظ أهم أماكنك المفضلة
|
\n• حفظ أهم أماكنك المفضلة
|
||||||
\n• عرض النقاط المهمة من حولك (POI)
|
\n• عرض نقاط الاهتمام من حولك
|
||||||
\n• عرض متخصص لبيانات خرائط على الإنترنت، الرؤية من الأقمار الصناعية (من بينج) وتراكب طبقات خرائط مختلفة كالسياحة ومسارات GPX للملاحة وطبقات إضافية مع شفافية قابلة للتعديل
|
\n• عرض متخصص لبيانات خرائط على الإنترنت، الرؤية من الأقمار الصناعية (من بينج) وتراكب طبقات خرائط مختلفة كالسياحة ومسارات GPX للملاحة وطبقات إضافية مع شفافية قابلة للتعديل
|
||||||
\n• عرض اختياري لأسماء الأماكن باللغة الإنكليزية، اللغة المحلية، أو عبر الإملاء الصوتي
|
\n• عرض اختياري لأسماء الأماكن باللغة الإنكليزية، اللغة المحلية، أو عبر الإملاء الصوتي
|
||||||
\n</string>
|
\n</string>
|
||||||
|
|
|
@ -2888,4 +2888,9 @@
|
||||||
<string name="poi_cuisine_cajun">Cajun</string>
|
<string name="poi_cuisine_cajun">Cajun</string>
|
||||||
<string name="poi_cuisine_burrito">Burritos</string>
|
<string name="poi_cuisine_burrito">Burritos</string>
|
||||||
<string name="poi_cuisine_waffle">Gofres</string>
|
<string name="poi_cuisine_waffle">Gofres</string>
|
||||||
|
<string name="poi_motorcycle_clothes_no">Roba de motorista: no</string>
|
||||||
|
<string name="poi_motorcycle_clothes_yes">Roba de motorista</string>
|
||||||
|
<string name="poi_motorcycle_tyres_no">Pneumàtics: no</string>
|
||||||
|
<string name="poi_motorcycle_tyres_yes">Pneumàtics</string>
|
||||||
|
<string name="poi_motorcycle_parts_no">Recanvis: no</string>
|
||||||
</resources>
|
</resources>
|
|
@ -3925,4 +3925,12 @@
|
||||||
\n
|
\n
|
||||||
\n</string>
|
\n</string>
|
||||||
<string name="reverse_all_points">Revertir tots els punts</string>
|
<string name="reverse_all_points">Revertir tots els punts</string>
|
||||||
|
<string name="profile_by_default_description">Seleccioneu el perfil que s\'utilitzarà en iniciar l\'aplicació.</string>
|
||||||
|
<string name="shared_string_last_used">Darrera utilització</string>
|
||||||
|
<string name="routing_attr_prefer_hiking_routes_description">Prioritza les rutes de senderisme</string>
|
||||||
|
<string name="routing_attr_prefer_hiking_routes_name">Prioritza les rutes de senderisme</string>
|
||||||
|
<string name="routing_attr_allow_streams_description">Permet rierols i torrents</string>
|
||||||
|
<string name="routing_attr_allow_streams_name">Permet rierols i torrents</string>
|
||||||
|
<string name="routing_attr_allow_intermittent_description">Permet les vies navegables no permanents</string>
|
||||||
|
<string name="routing_attr_allow_intermittent_name">Permet les vies navegables no permanents</string>
|
||||||
</resources>
|
</resources>
|
|
@ -3901,4 +3901,5 @@
|
||||||
<string name="poi_waste_transfer_station">Müllumladestation</string>
|
<string name="poi_waste_transfer_station">Müllumladestation</string>
|
||||||
<string name="poi_weightbridge">Fahrzeugwaage</string>
|
<string name="poi_weightbridge">Fahrzeugwaage</string>
|
||||||
<string name="poi_ranger_station">Rangerstation</string>
|
<string name="poi_ranger_station">Rangerstation</string>
|
||||||
|
<string name="poi_swimming_area">Schwimmbereich</string>
|
||||||
</resources>
|
</resources>
|
|
@ -3543,18 +3543,18 @@
|
||||||
<string name="shared_string_min">Min</string>
|
<string name="shared_string_min">Min</string>
|
||||||
<string name="n_items_of_z">%1$s de %2$s</string>
|
<string name="n_items_of_z">%1$s de %2$s</string>
|
||||||
<string name="shared_string_terrain">Terrain</string>
|
<string name="shared_string_terrain">Terrain</string>
|
||||||
<string name="hillshade_description">Carte ombrée utilisant des nuances sombres pour visualiser les pistes, les sommets et les plaines.</string>
|
<string name="hillshade_description">Carte ombrée utilisant des nuances sombres pour visualiser les pentes, les sommets et les plaines.</string>
|
||||||
<string name="slope_description">La piste est colorée selon sa pente.</string>
|
<string name="slope_description">La pente est colorée selon l\'inclinaison du terrain.</string>
|
||||||
<string name="terrain_slider_description">Définissez les niveaux de zoom minimum et maximum pour lesquels la couche sera affichée.</string>
|
<string name="terrain_slider_description">Définissez les niveaux de zoom minimum et maximum pour lesquels la couche sera affichée.</string>
|
||||||
<string name="hillshade_download_description">Des cartes supplémentaires sont nécessaires pour afficher l\'ombrage du relief sur la carte.</string>
|
<string name="hillshade_download_description">Des cartes supplémentaires sont nécessaires pour afficher l\'ombrage du relief sur la carte.</string>
|
||||||
<string name="slope_download_description">Des cartes supplémentaires sont nécessaires pour afficher les pistes sur la carte.</string>
|
<string name="slope_download_description">Des cartes supplémentaires sont nécessaires pour afficher les pentes sur la carte.</string>
|
||||||
<string name="slope_read_more">Vous pouvez en apprendre plus sur les pistes dans %1$s.</string>
|
<string name="slope_read_more">Vous pouvez en savoir plus sur les pentes dans %1$s.</string>
|
||||||
<string name="shared_string_transparency">Transparence</string>
|
<string name="shared_string_transparency">Transparence</string>
|
||||||
<string name="shared_string_zoom_levels">Niveaux de zoom</string>
|
<string name="shared_string_zoom_levels">Niveaux de zoom</string>
|
||||||
<string name="shared_string_legend">Légende</string>
|
<string name="shared_string_legend">Légende</string>
|
||||||
<string name="terrain_empty_state_text">Permet d\'afficher l\'ombrage du relief ou les pistes. Vous pouvez en apprendre plus sur ces types de cartes sur notre site.</string>
|
<string name="terrain_empty_state_text">Active l\'affichage de l\'ombrage du relief et l\'inclinaison. Vous pouvez en savoir plus sur ces types de cartes sur notre site.</string>
|
||||||
<string name="shared_string_hillshade">Ombrage du relief</string>
|
<string name="shared_string_hillshade">Ombrage du relief</string>
|
||||||
<string name="download_slope_maps">Pistes</string>
|
<string name="download_slope_maps">Pentes</string>
|
||||||
<string name="quick_action_show_hide_terrain">Affiche ou masque le terrain</string>
|
<string name="quick_action_show_hide_terrain">Affiche ou masque le terrain</string>
|
||||||
<string name="quick_action_terrain_hide">Masquer le terrain</string>
|
<string name="quick_action_terrain_hide">Masquer le terrain</string>
|
||||||
<string name="quick_action_terrain_show">Afficher le terrain</string>
|
<string name="quick_action_terrain_show">Afficher le terrain</string>
|
||||||
|
@ -4004,4 +4004,6 @@
|
||||||
<string name="routing_attr_prefer_hiking_routes_description">Pirivilégier les itinéraires de randonnée</string>
|
<string name="routing_attr_prefer_hiking_routes_description">Pirivilégier les itinéraires de randonnée</string>
|
||||||
<string name="routing_attr_allow_intermittent_description">Autoriser les voies navigables intermittentes</string>
|
<string name="routing_attr_allow_intermittent_description">Autoriser les voies navigables intermittentes</string>
|
||||||
<string name="routing_attr_allow_intermittent_name">Autoriser les voies navigables intermittentes</string>
|
<string name="routing_attr_allow_intermittent_name">Autoriser les voies navigables intermittentes</string>
|
||||||
|
<string name="routing_attr_allow_streams_description">Autoriser les cours d’eau et les drains</string>
|
||||||
|
<string name="routing_attr_allow_streams_name">Autoriser les cours d’eau et les drains</string>
|
||||||
</resources>
|
</resources>
|
|
@ -3890,4 +3890,5 @@
|
||||||
<string name="poi_water_source_tap">Csap</string>
|
<string name="poi_water_source_tap">Csap</string>
|
||||||
<string name="poi_water_source_water_works">Vízmű</string>
|
<string name="poi_water_source_water_works">Vízmű</string>
|
||||||
<string name="poi_water_source_tube_well">Csöves kút</string>
|
<string name="poi_water_source_tube_well">Csöves kút</string>
|
||||||
|
<string name="poi_swimming_area">Fürdésre kijelölt terület</string>
|
||||||
</resources>
|
</resources>
|
|
@ -3943,4 +3943,7 @@
|
||||||
<string name="shared_string_resources">Ressurser</string>
|
<string name="shared_string_resources">Ressurser</string>
|
||||||
<string name="profile_by_default_description">Velg profilen som skal brukes når programmet starter.</string>
|
<string name="profile_by_default_description">Velg profilen som skal brukes når programmet starter.</string>
|
||||||
<string name="shared_string_last_used">Sist brukt</string>
|
<string name="shared_string_last_used">Sist brukt</string>
|
||||||
|
<string name="routing_attr_allow_streams_name">Tillat bekker og avløp</string>
|
||||||
|
<string name="routing_attr_allow_intermittent_name">Tillat periodiske vannveier</string>
|
||||||
|
<string name="routing_attr_allow_intermittent_description">Tillat periodiske vannveier</string>
|
||||||
</resources>
|
</resources>
|
|
@ -3793,7 +3793,7 @@
|
||||||
<string name="please_provide_point_name_error">Podaj nazwę punktu</string>
|
<string name="please_provide_point_name_error">Podaj nazwę punktu</string>
|
||||||
<string name="quick_action_remove_next_destination_descr">Usuwa następny cel na trasie. Jeśli jest to miejsce docelowe, nawigacja zostanie zatrzymana.</string>
|
<string name="quick_action_remove_next_destination_descr">Usuwa następny cel na trasie. Jeśli jest to miejsce docelowe, nawigacja zostanie zatrzymana.</string>
|
||||||
<string name="search_download_wikipedia_maps">Pobierz mapy Wikipedii</string>
|
<string name="search_download_wikipedia_maps">Pobierz mapy Wikipedii</string>
|
||||||
<string name="plugin_wikipedia_description">Uzyskaj informacje o ciekawych miejscach z Wikipedii, kieszonkowego przewodnika offline zawierającego artykuły o miejscach i celach.</string>
|
<string name="plugin_wikipedia_description">Uzyskaj informacje o ciekawych miejscach z Wikipedii, kieszonkowego przewodnika offline zawierającego artykuły o miejscach i celach podróży.</string>
|
||||||
<string name="app_mode_enduro_motorcycle">Motocykl Enduro</string>
|
<string name="app_mode_enduro_motorcycle">Motocykl Enduro</string>
|
||||||
<string name="app_mode_motor_scooter">Skuter</string>
|
<string name="app_mode_motor_scooter">Skuter</string>
|
||||||
<string name="routing_attr_length_description">Określ długość pojazdu dozwoloną na trasach.</string>
|
<string name="routing_attr_length_description">Określ długość pojazdu dozwoloną na trasach.</string>
|
||||||
|
@ -3913,11 +3913,11 @@
|
||||||
<string name="start_finish_icons">Ikony startu i końca</string>
|
<string name="start_finish_icons">Ikony startu i końca</string>
|
||||||
<string name="contour_lines_thanks">Dziękujemy za zakup \"Linii konturowych\"</string>
|
<string name="contour_lines_thanks">Dziękujemy za zakup \"Linii konturowych\"</string>
|
||||||
<string name="osm_live_payment_desc_hw">Subskrypcja jest naliczana za wybrany okres. Anuluj go w AppGallery w dowolnym momencie.</string>
|
<string name="osm_live_payment_desc_hw">Subskrypcja jest naliczana za wybrany okres. Anuluj go w AppGallery w dowolnym momencie.</string>
|
||||||
<string name="osm_live_payment_subscription_management_hw">Płatność zostanie pobrana z konta AppGallery po potwierdzeniu zakupu.
|
<string name="osm_live_payment_subscription_management_hw">Twoje konto AppGallery jest obciążane po potwierdzeniu zakupu.
|
||||||
\n
|
\n
|
||||||
\nSubskrypcja jest automatycznie odnawiana, chyba że zostanie anulowana przed datą odnowienia. Twoje konto zostanie obciążone opłatą za okres odnowienia (miesiąc/trzy miesiące/rok) tylko w dniu odnowienia.
|
\nSubskrypcja przedłuża się automatycznie, chyba że zostanie anulowana przed datą odnowienia. Twoje konto zostanie obciążone za okres odnowienia (miesiąc/trzy miesiące/rok) tylko w dniu odnowienia.
|
||||||
\n
|
\n
|
||||||
\nMożesz zarządzać subskrypcjami i anulować je, przechodząc do ustawień Galerii aplikacji.</string>
|
\nMożesz zarządzać swoimi subskrypcjami i anulować je w ustawieniach AppGallery.</string>
|
||||||
<string name="routing_attr_avoid_footways_description">Unikaj chodników</string>
|
<string name="routing_attr_avoid_footways_description">Unikaj chodników</string>
|
||||||
<string name="routing_attr_avoid_footways_name">Unikaj chodników</string>
|
<string name="routing_attr_avoid_footways_name">Unikaj chodników</string>
|
||||||
<string name="development">Rozwój</string>
|
<string name="development">Rozwój</string>
|
||||||
|
@ -4024,4 +4024,12 @@
|
||||||
<string name="profile_type_osmand_string">Profil OsmAnd</string>
|
<string name="profile_type_osmand_string">Profil OsmAnd</string>
|
||||||
<string name="profile_type_user_string">Profil użytkownika</string>
|
<string name="profile_type_user_string">Profil użytkownika</string>
|
||||||
<string name="reverse_all_points">Odwróć wszystkie punkty</string>
|
<string name="reverse_all_points">Odwróć wszystkie punkty</string>
|
||||||
|
<string name="profile_by_default_description">Wybierz profil, który będzie używany przy uruchomieniu aplikacji.</string>
|
||||||
|
<string name="shared_string_last_used">Ostatnio używane</string>
|
||||||
|
<string name="routing_attr_prefer_hiking_routes_description">Preferowane szlaki turystyczne</string>
|
||||||
|
<string name="routing_attr_prefer_hiking_routes_name">Preferuj szlaki turystyczne</string>
|
||||||
|
<string name="routing_attr_allow_streams_description">Zezwalaj na strumienie i dreny</string>
|
||||||
|
<string name="routing_attr_allow_streams_name">Zezwalaj na strumienie i dreny</string>
|
||||||
|
<string name="routing_attr_allow_intermittent_description">Zezwalaj na przerywane drogi wodne</string>
|
||||||
|
<string name="routing_attr_allow_intermittent_name">Zezwalaj na przerywane drogi wodne</string>
|
||||||
</resources>
|
</resources>
|
|
@ -194,7 +194,7 @@
|
||||||
<string name="poi_fuel_biogas">Biogás</string>
|
<string name="poi_fuel_biogas">Biogás</string>
|
||||||
<string name="poi_fuel_lh2">Hidrogênio líquido</string>
|
<string name="poi_fuel_lh2">Hidrogênio líquido</string>
|
||||||
<string name="poi_fuel_electricity">Eletricidade</string>
|
<string name="poi_fuel_electricity">Eletricidade</string>
|
||||||
<string name="poi_electricity_combined_charging">Eletroposto</string>
|
<string name="poi_electricity_combined_charging">Eletroposto;Estação de carregamento de veículos elétricos; Estação de carregamento de VE; Ponto de recarga elétrica; Ponto de carga; Estação de recarga eletrônica; Equipamento de abastecimento de veículos elétricos</string>
|
||||||
<string name="poi_vehicle_ramp">Rampa de veículo</string>
|
<string name="poi_vehicle_ramp">Rampa de veículo</string>
|
||||||
<string name="poi_compressed_air">Ar comprimido</string>
|
<string name="poi_compressed_air">Ar comprimido</string>
|
||||||
<string name="poi_parking">Estacionamento</string>
|
<string name="poi_parking">Estacionamento</string>
|
||||||
|
@ -3898,4 +3898,5 @@
|
||||||
<string name="poi_ranger_station">Posto de guarda florestal</string>
|
<string name="poi_ranger_station">Posto de guarda florestal</string>
|
||||||
<string name="poi_waste_transfer_station">Estação de transferência de resíduos</string>
|
<string name="poi_waste_transfer_station">Estação de transferência de resíduos</string>
|
||||||
<string name="poi_lavoir">Lavandaria pública</string>
|
<string name="poi_lavoir">Lavandaria pública</string>
|
||||||
|
<string name="poi_swimming_area">Área de natação</string>
|
||||||
</resources>
|
</resources>
|
|
@ -3886,4 +3886,5 @@
|
||||||
<string name="poi_lavoir">Умывальник</string>
|
<string name="poi_lavoir">Умывальник</string>
|
||||||
<string name="poi_waste_transfer_station">Станция перекачки отходов</string>
|
<string name="poi_waste_transfer_station">Станция перекачки отходов</string>
|
||||||
<string name="poi_ranger_station">Станция рейнджеров</string>
|
<string name="poi_ranger_station">Станция рейнджеров</string>
|
||||||
|
<string name="poi_swimming_area">Место для купания</string>
|
||||||
</resources>
|
</resources>
|
|
@ -4001,4 +4001,8 @@
|
||||||
<string name="plan_route_split_after">Разделить после</string>
|
<string name="plan_route_split_after">Разделить после</string>
|
||||||
<string name="profile_type_user_string">Профиль пользователя</string>
|
<string name="profile_type_user_string">Профиль пользователя</string>
|
||||||
<string name="profile_type_osmand_string">Профиль OsmAnd</string>
|
<string name="profile_type_osmand_string">Профиль OsmAnd</string>
|
||||||
|
<string name="profile_by_default_description">Выберите профиль, который будет использоваться при запуске приложения.</string>
|
||||||
|
<string name="shared_string_last_used">Последний раз использовалось</string>
|
||||||
|
<string name="routing_attr_allow_intermittent_description">Разрешить прерывистые водные пути</string>
|
||||||
|
<string name="routing_attr_allow_intermittent_name">Разрешить прерывистые водные пути</string>
|
||||||
</resources>
|
</resources>
|
|
@ -2915,4 +2915,63 @@ Vänligen tillhandahåll fullständig kod</string>
|
||||||
<string name="shared_string_languages">Språk</string>
|
<string name="shared_string_languages">Språk</string>
|
||||||
<string name="shared_string_language">Språk</string>
|
<string name="shared_string_language">Språk</string>
|
||||||
<string name="shared_string_all_languages">Alla språk</string>
|
<string name="shared_string_all_languages">Alla språk</string>
|
||||||
|
<string name="release_3_9">• La till inställning för att exportera och importera all data, inklusive inställningar, resurser, mina platser
|
||||||
|
\n
|
||||||
|
\n • Planera rutt: Grafer för seggraphs for track segments with route, and added the ability to create and edit multiple track segments
|
||||||
|
\n
|
||||||
|
\n • Added OAuth authentication method for OpenStreetMap, improved UI of OSM dialogs
|
||||||
|
\n
|
||||||
|
\n • Support custom colors for favorites and track waypoints
|
||||||
|
\n
|
||||||
|
\n</string>
|
||||||
|
<string name="reverse_all_points">Vänd alla punkter</string>
|
||||||
|
<string name="profile_by_default_description">Välj den profil som ska användas när appen startas.</string>
|
||||||
|
<string name="shared_string_last_used">Senast använd</string>
|
||||||
|
<string name="routing_attr_prefer_hiking_routes_description">Föredra vandringsleder</string>
|
||||||
|
<string name="routing_attr_prefer_hiking_routes_name">Föredra vandringsleder</string>
|
||||||
|
<string name="routing_attr_allow_streams_description">Tillåt strömmar och torrläggningar</string>
|
||||||
|
<string name="routing_attr_allow_streams_name">Tillåt strömmar och torrläggningar</string>
|
||||||
|
<string name="routing_attr_allow_intermittent_description">Tillåt vägar över vatten</string>
|
||||||
|
<string name="routing_attr_allow_intermittent_name">Tillåt vägar över vatten</string>
|
||||||
|
<string name="gpx_upload_private_visibility_descr">\"Privat\" betyder att spåret inte visas i några offentliga listor, men spårpunkter från det i okronologisk ordning är tillgängliga via det offentliga GPS-API utan tidsstämplar.</string>
|
||||||
|
<string name="gpx_upload_identifiable_visibility_descr">\"Identifierbart\" betyder att spårningen kommer att visas offentligt i dina GPS-spår och i offentliga GPS-spårningar, dvs. andra användare kommer att kunna komma åt grundata kring spårningen och associera den med ditt användarnamn. Offentliga tidsstämplade tracepoint-data från GPS-API som serveras via trackpoints API refererar till din ursprungliga spårningssida.</string>
|
||||||
|
<string name="gpx_upload_trackable_visibility_descr">\"Spårbar\" betyder att spåret inte visas i några offentliga listor, men bearbetade spårpunkter tillsammans med respektive tidsstämplar (som inte kan kopplas direkt till dig) görs genom nedladdningar från det offentliga GPS-API: et.</string>
|
||||||
|
<string name="osm_edit_close_note">Stäng OSM Anteckningar</string>
|
||||||
|
<string name="osm_edit_comment_note">Kommentera OSM Anteckningar</string>
|
||||||
|
<string name="osm_login_descr">Du kan logga in antingen med OAuth metoden (högre säkerhet) eller genom att använda ditt användarnamn och lösenord.</string>
|
||||||
|
<string name="shared_string_add_photo">Lägg till bild</string>
|
||||||
|
<string name="register_on_openplacereviews">Registrera dig hos
|
||||||
|
\nOpenPlaceReviews.org</string>
|
||||||
|
<string name="register_on_openplacereviews_desc">Bilder tillhandahålls av OpenPlaceReviews.org\'s öppen data projekt. För att kunna ladda upp dina bilder behöver du registrera dig på deras webbsida.</string>
|
||||||
|
<string name="register_opr_create_new_account">Registrera ett nytt konto</string>
|
||||||
|
<string name="register_opr_have_account">Jag har redan ett konto</string>
|
||||||
|
<string name="shared_string_search_history">Sökhistorik</string>
|
||||||
|
<string name="app_mode_kayak">Kajak</string>
|
||||||
|
<string name="app_mode_motorboat">Motorbåt</string>
|
||||||
|
<string name="cannot_upload_image">Uppladdning av bild misslyckades, vänligen försök igen senare</string>
|
||||||
|
<string name="select_picture">Välj bild</string>
|
||||||
|
<string name="shared_string_resources">Resurser</string>
|
||||||
|
<string name="approximate_file_size">Uppskattad filstorlek</string>
|
||||||
|
<string name="select_data_to_export">Vänligen välj vilken data som önskas exporteras till filen.</string>
|
||||||
|
<string name="file_size_needed_for_import">Erfordras för importering</string>
|
||||||
|
<string name="export_not_enough_space_descr">Din enhet har bara %1$s ledigt lagringsutrymme. Vänligen frigör utrymme alternativt avmarkera några föremål som skall exporteras.</string>
|
||||||
|
<string name="export_not_enough_space">Det finns inte tillräckligt med lagringsutrymme</string>
|
||||||
|
<string name="select_items_for_import">Välj vilka föremål som skall importeras.</string>
|
||||||
|
<string name="select_groups_for_import">Välj vilka grupper som skall importeras.</string>
|
||||||
|
<string name="add_to_mapillary">Lägg till Mapillary</string>
|
||||||
|
<string name="add_to_opr">Lägg till ÖppnaPlattsRecensioner (OPR)</string>
|
||||||
|
<string name="use_dev_url_descr">Byt till att använda dev.openstreetmap.org istället för openstreetmap.org för att testa uppladdning av OSM Anteckningar / Sevärdheter / GPX.</string>
|
||||||
|
<string name="use_dev_url">Använd dev.openstreetmap.org</string>
|
||||||
|
<string name="app_mode_light_aircraft">Mindre flyplan</string>
|
||||||
|
<string name="add_photos_descr">OsmAnd visar bilder från flertalet källor:
|
||||||
|
\nOpenPlaceReviews - Foton av sevärdheter;
|
||||||
|
\nMapillary - Bilder på gatunivå;
|
||||||
|
\nWeb/Wikimedia - Foton av sevärdheter enligt OpenStreetMap-data.</string>
|
||||||
|
<string name="elevation_data">Du kan använda höjddata för att ta hänsyn till upp- / nedstigning på din resa</string>
|
||||||
|
<string name="plan_route_join_segments">Sammanslå segment</string>
|
||||||
|
<string name="plan_route_split_before">Dela innan</string>
|
||||||
|
<string name="plan_route_split_after">Dela efter</string>
|
||||||
|
<string name="plan_route_add_new_segment">Lägg till ett nytt segment</string>
|
||||||
|
<string name="profile_type_osmand_string">OsmAnd profil</string>
|
||||||
|
<string name="profile_type_user_string">Användarprofil</string>
|
||||||
</resources>
|
</resources>
|
|
@ -198,7 +198,7 @@
|
||||||
<string name="poi_fuel_e85">85% Етанолу (Е85)</string>
|
<string name="poi_fuel_e85">85% Етанолу (Е85)</string>
|
||||||
<string name="poi_fuel_lh2">Рідкий водень</string>
|
<string name="poi_fuel_lh2">Рідкий водень</string>
|
||||||
<string name="poi_fuel_electricity">Електроенергія</string>
|
<string name="poi_fuel_electricity">Електроенергія</string>
|
||||||
<string name="poi_electricity_combined_charging">Станція зарядки</string>
|
<string name="poi_electricity_combined_charging">Зарядна станція;Зарядна станція для електромобіля;Зарядна станція для електромобілів;точка підзарядки електроенергією;Точка зарядки; Електронна зарядна станція;Обладнання для електромобілів</string>
|
||||||
<string name="poi_vehicle_ramp">Рампа для огляду/перевірки машин</string>
|
<string name="poi_vehicle_ramp">Рампа для огляду/перевірки машин</string>
|
||||||
<string name="poi_compressed_air">Стиснене повітря</string>
|
<string name="poi_compressed_air">Стиснене повітря</string>
|
||||||
<string name="poi_parking">Стоянка</string>
|
<string name="poi_parking">Стоянка</string>
|
||||||
|
@ -3886,4 +3886,5 @@
|
||||||
<string name="poi_weightbridge">Автомобільні ваги</string>
|
<string name="poi_weightbridge">Автомобільні ваги</string>
|
||||||
<string name="poi_lavoir">Громадська пральня</string>
|
<string name="poi_lavoir">Громадська пральня</string>
|
||||||
<string name="poi_waste_transfer_station">Станція перевезення відходів</string>
|
<string name="poi_waste_transfer_station">Станція перевезення відходів</string>
|
||||||
|
<string name="poi_swimming_area">Плавальний майданчик</string>
|
||||||
</resources>
|
</resources>
|
|
@ -3897,4 +3897,5 @@
|
||||||
<string name="poi_ranger_station">護林員站</string>
|
<string name="poi_ranger_station">護林員站</string>
|
||||||
<string name="poi_lavoir">公共洗衣區</string>
|
<string name="poi_lavoir">公共洗衣區</string>
|
||||||
<string name="poi_waste_transfer_station">垃圾站</string>
|
<string name="poi_waste_transfer_station">垃圾站</string>
|
||||||
|
<string name="poi_swimming_area">游泳區</string>
|
||||||
</resources>
|
</resources>
|
|
@ -384,7 +384,7 @@
|
||||||
<string name="poi_fuel_electricity">Electricity</string>
|
<string name="poi_fuel_electricity">Electricity</string>
|
||||||
<string name="poi_aeroway_fuel">Aircraft fuel station</string>
|
<string name="poi_aeroway_fuel">Aircraft fuel station</string>
|
||||||
<string name="poi_waterway_fuel">Gas station for boats</string>
|
<string name="poi_waterway_fuel">Gas station for boats</string>
|
||||||
<string name="poi_electricity_combined_charging">Charging station</string>
|
<string name="poi_electricity_combined_charging">Charging station;Electric vehicle charging station;EV charging station;Electric recharging point;Charge point;Electronic charging station;Electric vehicle supply equipment</string>
|
||||||
<string name="poi_fuel_wood">Fuel: wood</string>
|
<string name="poi_fuel_wood">Fuel: wood</string>
|
||||||
<string name="poi_fuel_charcoal">Fuel: charcoal</string>
|
<string name="poi_fuel_charcoal">Fuel: charcoal</string>
|
||||||
<string name="poi_fuel_coal">Fuel: coal</string>
|
<string name="poi_fuel_coal">Fuel: coal</string>
|
||||||
|
@ -4326,5 +4326,11 @@
|
||||||
|
|
||||||
<string name="poi_lavoir">Lavoir</string>
|
<string name="poi_lavoir">Lavoir</string>
|
||||||
|
|
||||||
|
<string name="poi_swimming_area">Swimming area</string>
|
||||||
|
|
||||||
|
<string name="poi_wildlife_crossing">Wildlife crossing</string>
|
||||||
|
<string name="poi_wildlife_crossing_bat_bridge">Bat bridge</string>
|
||||||
|
<string name="poi_wildlife_crossing_bat_tunnel">Bat tunnel</string>
|
||||||
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -43,6 +43,7 @@ import net.osmand.plus.myplaces.TrackPointFragment;
|
||||||
import net.osmand.plus.myplaces.TrackSegmentFragment;
|
import net.osmand.plus.myplaces.TrackSegmentFragment;
|
||||||
import net.osmand.plus.settings.backend.OsmAndAppCustomization;
|
import net.osmand.plus.settings.backend.OsmAndAppCustomization;
|
||||||
import net.osmand.plus.settings.backend.OsmandSettings;
|
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||||
|
import net.osmand.plus.track.TrackDisplayHelper;
|
||||||
import net.osmand.plus.views.AddGpxPointBottomSheetHelper.NewGpxPoint;
|
import net.osmand.plus.views.AddGpxPointBottomSheetHelper.NewGpxPoint;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -57,18 +58,14 @@ public class TrackActivity extends TabActivity {
|
||||||
public static final String OPEN_TRACKS_LIST = "OPEN_TRACKS_LIST";
|
public static final String OPEN_TRACKS_LIST = "OPEN_TRACKS_LIST";
|
||||||
public static final String CURRENT_RECORDING = "CURRENT_RECORDING";
|
public static final String CURRENT_RECORDING = "CURRENT_RECORDING";
|
||||||
public static final String SHOW_TEMPORARILY = "SHOW_TEMPORARILY";
|
public static final String SHOW_TEMPORARILY = "SHOW_TEMPORARILY";
|
||||||
protected List<WeakReference<Fragment>> fragList = new ArrayList<>();
|
|
||||||
private OsmandApplication app;
|
private OsmandApplication app;
|
||||||
|
private TrackDisplayHelper displayHelper;
|
||||||
private TrackBitmapDrawer trackBitmapDrawer;
|
private TrackBitmapDrawer trackBitmapDrawer;
|
||||||
|
|
||||||
private File file = null;
|
|
||||||
private GPXFile gpxFile;
|
|
||||||
private GpxDataItem gpxDataItem;
|
|
||||||
private LockableViewPager viewPager;
|
private LockableViewPager viewPager;
|
||||||
private long modifiedTime = -1;
|
private final List<WeakReference<Fragment>> fragList = new ArrayList<>();
|
||||||
|
|
||||||
private List<GpxDisplayGroup> displayGroups;
|
|
||||||
private List<GpxDisplayGroup> originalGroups = new ArrayList<>();
|
|
||||||
private boolean stopped = false;
|
private boolean stopped = false;
|
||||||
private boolean openPointsTab = false;
|
private boolean openPointsTab = false;
|
||||||
private boolean openTracksList = false;
|
private boolean openTracksList = false;
|
||||||
|
@ -85,14 +82,15 @@ public class TrackActivity extends TabActivity {
|
||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
displayHelper = new TrackDisplayHelper(app);
|
||||||
if (intent.hasExtra(TRACK_FILE_NAME)) {
|
if (intent.hasExtra(TRACK_FILE_NAME)) {
|
||||||
file = new File(intent.getStringExtra(TRACK_FILE_NAME));
|
displayHelper.setFile(new File(intent.getStringExtra(TRACK_FILE_NAME)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ActionBar actionBar = getSupportActionBar();
|
ActionBar actionBar = getSupportActionBar();
|
||||||
if (actionBar != null) {
|
if (actionBar != null) {
|
||||||
if (file != null) {
|
if (getFile() != null) {
|
||||||
String fn = file.getName().replace(IndexConstants.GPX_FILE_EXT, "").replace("/", " ").replace("_", " ");
|
String fn = getFile().getName().replace(IndexConstants.GPX_FILE_EXT, "").replace("/", " ").replace("_", " ");
|
||||||
actionBar.setTitle(fn);
|
actionBar.setTitle(fn);
|
||||||
} else {
|
} else {
|
||||||
actionBar.setTitle(getString(R.string.shared_string_currently_recording_track));
|
actionBar.setTitle(getString(R.string.shared_string_currently_recording_track));
|
||||||
|
@ -109,11 +107,19 @@ public class TrackActivity extends TabActivity {
|
||||||
setContentView(R.layout.track_content);
|
setContentView(R.layout.track_content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TrackDisplayHelper getDisplayHelper() {
|
||||||
|
return displayHelper;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public TrackBitmapDrawer getTrackBitmapDrawer() {
|
public TrackBitmapDrawer getTrackBitmapDrawer() {
|
||||||
return trackBitmapDrawer;
|
return trackBitmapDrawer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public File getFile() {
|
||||||
|
return displayHelper.getFile();
|
||||||
|
}
|
||||||
|
|
||||||
public void addPoint(PointDescription pointDescription) {
|
public void addPoint(PointDescription pointDescription) {
|
||||||
Intent currentIntent = getIntent();
|
Intent currentIntent = getIntent();
|
||||||
if (currentIntent != null) {
|
if (currentIntent != null) {
|
||||||
|
@ -122,7 +128,7 @@ public class TrackActivity extends TabActivity {
|
||||||
final OsmandSettings settings = app.getSettings();
|
final OsmandSettings settings = app.getSettings();
|
||||||
GPXFile gpx = getGpx();
|
GPXFile gpx = getGpx();
|
||||||
LatLon location = settings.getLastKnownMapLocation();
|
LatLon location = settings.getLastKnownMapLocation();
|
||||||
QuadRect rect = getRect();
|
QuadRect rect = displayHelper.getRect();
|
||||||
NewGpxPoint newGpxPoint = new NewGpxPoint(gpx, pointDescription, rect);
|
NewGpxPoint newGpxPoint = new NewGpxPoint(gpx, pointDescription, rect);
|
||||||
if (gpx != null && location != null) {
|
if (gpx != null && location != null) {
|
||||||
settings.setMapLocationToShow(location.getLatitude(), location.getLongitude(),
|
settings.setMapLocationToShow(location.getLatitude(), location.getLongitude(),
|
||||||
|
@ -152,53 +158,20 @@ public class TrackActivity extends TabActivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public QuadRect getRect() {
|
|
||||||
if (getGpx() != null) {
|
|
||||||
return getGpx().getRect();
|
|
||||||
} else {
|
|
||||||
return new QuadRect(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void setGpxDataItem(GpxDataItem gpxDataItem) {
|
protected void setGpxDataItem(GpxDataItem gpxDataItem) {
|
||||||
this.gpxDataItem = gpxDataItem;
|
displayHelper.setGpxDataItem(gpxDataItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setGpx(GPXFile result) {
|
protected void setGpx(GPXFile result) {
|
||||||
this.gpxFile = result;
|
displayHelper.setGpx(result);
|
||||||
if (file == null) {
|
|
||||||
this.gpxFile = getMyApplication().getSavingTrackHelper().getCurrentGpx();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<GpxDisplayGroup> getGpxFile(boolean useDisplayGroups) {
|
public List<GpxDisplayGroup> getGpxFile(boolean useDisplayGroups) {
|
||||||
if (gpxFile == null) {
|
return displayHelper.getGpxFile(useDisplayGroups);
|
||||||
return new ArrayList<>();
|
|
||||||
}
|
|
||||||
if (gpxFile.modifiedTime != modifiedTime) {
|
|
||||||
modifiedTime = gpxFile.modifiedTime;
|
|
||||||
GpxSelectionHelper selectedGpxHelper = ((OsmandApplication) getApplication()).getSelectedGpxHelper();
|
|
||||||
displayGroups = selectedGpxHelper.collectDisplayGroups(gpxFile);
|
|
||||||
originalGroups.clear();
|
|
||||||
for (GpxDisplayGroup g : displayGroups) {
|
|
||||||
originalGroups.add(g.cloneInstance());
|
|
||||||
}
|
|
||||||
if (file != null) {
|
|
||||||
SelectedGpxFile sf = selectedGpxHelper.getSelectedFileByPath(gpxFile.path);
|
|
||||||
if (sf != null && file != null && sf.getDisplayGroups(app) != null) {
|
|
||||||
displayGroups = sf.getDisplayGroups(app);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (useDisplayGroups) {
|
|
||||||
return displayGroups;
|
|
||||||
} else {
|
|
||||||
return originalGroups;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttachFragment(Fragment fragment) {
|
public void onAttachFragment(@NonNull Fragment fragment) {
|
||||||
fragList.add(new WeakReference<>(fragment));
|
fragList.add(new WeakReference<>(fragment));
|
||||||
if (trackBitmapDrawer != null && fragment instanceof TrackBitmapDrawerListener) {
|
if (trackBitmapDrawer != null && fragment instanceof TrackBitmapDrawerListener) {
|
||||||
trackBitmapDrawer.addListener((TrackBitmapDrawerListener) fragment);
|
trackBitmapDrawer.addListener((TrackBitmapDrawerListener) fragment);
|
||||||
|
@ -218,7 +191,7 @@ public class TrackActivity extends TabActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onSaveInstanceState(Bundle outState) {
|
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
if (viewPager.getCurrentItem() == 1) {
|
if (viewPager.getCurrentItem() == 1) {
|
||||||
outState.putBoolean(OPEN_POINTS_TAB, true);
|
outState.putBoolean(OPEN_POINTS_TAB, true);
|
||||||
|
@ -266,15 +239,6 @@ public class TrackActivity extends TabActivity {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateSplitView() {
|
|
||||||
for (WeakReference<Fragment> f : fragList) {
|
|
||||||
Fragment frag = f.get();
|
|
||||||
if (frag instanceof TrackSegmentFragment) {
|
|
||||||
((TrackSegmentFragment) frag).updateSplitView();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateHeader(Fragment sender) {
|
public void updateHeader(Fragment sender) {
|
||||||
for (WeakReference<Fragment> f : fragList) {
|
for (WeakReference<Fragment> f : fragList) {
|
||||||
Fragment frag = f.get();
|
Fragment frag = f.get();
|
||||||
|
@ -323,23 +287,23 @@ public class TrackActivity extends TabActivity {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public GPXFile getGpx() {
|
public GPXFile getGpx() {
|
||||||
return gpxFile;
|
return displayHelper.getGpx();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public GpxDataItem getGpxDataItem() {
|
public GpxDataItem getGpxDataItem() {
|
||||||
return gpxDataItem;
|
return displayHelper.getGpxDataItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onGPXFileReady(@Nullable GPXFile gpxFile) {
|
public void onGPXFileReady(@Nullable GPXFile gpxFile) {
|
||||||
setGpx(gpxFile);
|
setGpx(gpxFile);
|
||||||
setGpxDataItem(file != null ? app.getGpxDbHelper().getItem(file) : null);
|
setGpxDataItem(getFile() != null ? app.getGpxDbHelper().getItem(getFile()) : null);
|
||||||
|
|
||||||
WindowManager mgr = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
|
WindowManager mgr = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
|
||||||
if (gpxFile != null && mgr != null) {
|
if (gpxFile != null && mgr != null) {
|
||||||
DisplayMetrics dm = new DisplayMetrics();
|
DisplayMetrics dm = new DisplayMetrics();
|
||||||
mgr.getDefaultDisplay().getMetrics(dm);
|
mgr.getDefaultDisplay().getMetrics(dm);
|
||||||
trackBitmapDrawer = new TrackBitmapDrawer(app, gpxFile, getGpxDataItem(), getRect(), dm.density, dm.widthPixels, AndroidUtils.dpToPx(app, 152f));
|
trackBitmapDrawer = new TrackBitmapDrawer(app, gpxFile, getGpxDataItem(), displayHelper.getRect(), dm.density, dm.widthPixels, AndroidUtils.dpToPx(app, 152f));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (WeakReference<Fragment> f : fragList) {
|
for (WeakReference<Fragment> f : fragList) {
|
||||||
|
@ -406,23 +370,6 @@ public class TrackActivity extends TabActivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean setJoinSegments(boolean joinSegments) {
|
|
||||||
if (gpxDataItem != null) {
|
|
||||||
boolean updated = app.getGpxDbHelper().updateJoinSegments(gpxDataItem, joinSegments);
|
|
||||||
|
|
||||||
SelectedGpxFile selectedGpxFile = app.getSelectedGpxHelper().getSelectedFileByPath(gpxFile.path);
|
|
||||||
if (updated && selectedGpxFile != null) {
|
|
||||||
selectedGpxFile.setJoinSegments(joinSegments);
|
|
||||||
}
|
|
||||||
return updated;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isJoinSegments() {
|
|
||||||
return gpxDataItem != null && gpxDataItem.isJoinSegments();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class GPXFileLoaderTask extends AsyncTask<Void, Void, GPXFile> {
|
private static class GPXFileLoaderTask extends AsyncTask<Void, Void, GPXFile> {
|
||||||
|
|
||||||
private OsmandApplication app;
|
private OsmandApplication app;
|
||||||
|
@ -437,7 +384,7 @@ public class TrackActivity extends TabActivity {
|
||||||
GPXFileLoaderTask(@NonNull TrackActivity activity) {
|
GPXFileLoaderTask(@NonNull TrackActivity activity) {
|
||||||
this.activityRef = new WeakReference<>(activity);
|
this.activityRef = new WeakReference<>(activity);
|
||||||
app = activity.getMyApplication();
|
app = activity.getMyApplication();
|
||||||
file = activity.file;
|
file = activity.getDisplayHelper().getFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onPreExecute() {
|
protected void onPreExecute() {
|
||||||
|
@ -486,11 +433,11 @@ public class TrackActivity extends TabActivity {
|
||||||
if (activity != null) {
|
if (activity != null) {
|
||||||
activity.setSupportProgressBarIndeterminateVisibility(false);
|
activity.setSupportProgressBarIndeterminateVisibility(false);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
final GpxSelectionHelper helper = app.getSelectedGpxHelper();
|
GpxSelectionHelper helper = app.getSelectedGpxHelper();
|
||||||
if (showTemporarily) {
|
if (showTemporarily) {
|
||||||
helper.selectGpxFile(result, false, false);
|
helper.selectGpxFile(result, false, false);
|
||||||
} else {
|
} else {
|
||||||
final SelectedGpxFile selectedGpx = helper.getSelectedFileByPath(result.path);
|
SelectedGpxFile selectedGpx = helper.getSelectedFileByPath(result.path);
|
||||||
if (selectedGpx != null && result.error == null) {
|
if (selectedGpx != null && result.error == null) {
|
||||||
selectedGpx.setGpxFile(result, app);
|
selectedGpx.setGpxFile(result, app);
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ public abstract class ContextMenuFragment extends BaseOsmAndFragment
|
||||||
private OnLayoutChangeListener containerLayoutListener;
|
private OnLayoutChangeListener containerLayoutListener;
|
||||||
private View topShadow;
|
private View topShadow;
|
||||||
private ViewGroup topView;
|
private ViewGroup topView;
|
||||||
private View bottomScrollView;
|
private ViewGroup bottomScrollView;
|
||||||
private LinearLayout cardsContainer;
|
private LinearLayout cardsContainer;
|
||||||
private FrameLayout bottomContainer;
|
private FrameLayout bottomContainer;
|
||||||
|
|
||||||
|
@ -259,7 +259,7 @@ public abstract class ContextMenuFragment extends BaseOsmAndFragment
|
||||||
return bottomContainer;
|
return bottomContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public View getBottomScrollView() {
|
public ViewGroup getBottomScrollView() {
|
||||||
return bottomScrollView;
|
return bottomScrollView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import net.osmand.plus.GpxSelectionHelper;
|
||||||
import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
|
import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
|
||||||
import net.osmand.plus.OsmandApplication;
|
import net.osmand.plus.OsmandApplication;
|
||||||
import net.osmand.plus.R;
|
import net.osmand.plus.R;
|
||||||
|
import net.osmand.plus.Version;
|
||||||
import net.osmand.plus.activities.MapActivity;
|
import net.osmand.plus.activities.MapActivity;
|
||||||
import net.osmand.plus.activities.TrackActivity;
|
import net.osmand.plus.activities.TrackActivity;
|
||||||
import net.osmand.plus.helpers.GpxUiHelper;
|
import net.osmand.plus.helpers.GpxUiHelper;
|
||||||
|
@ -25,6 +26,7 @@ import net.osmand.plus.mapcontextmenu.builders.SelectedGpxMenuBuilder;
|
||||||
import net.osmand.plus.myplaces.SaveCurrentTrackTask;
|
import net.osmand.plus.myplaces.SaveCurrentTrackTask;
|
||||||
import net.osmand.plus.settings.backend.OsmandSettings;
|
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||||
import net.osmand.plus.track.SaveGpxAsyncTask.SaveGpxListener;
|
import net.osmand.plus.track.SaveGpxAsyncTask.SaveGpxListener;
|
||||||
|
import net.osmand.plus.track.TrackMenuFragment;
|
||||||
import net.osmand.util.Algorithms;
|
import net.osmand.util.Algorithms;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -45,15 +47,21 @@ public class SelectedGpxMenuController extends MenuController {
|
||||||
leftTitleButtonController = new TitleButtonController() {
|
leftTitleButtonController = new TitleButtonController() {
|
||||||
@Override
|
@Override
|
||||||
public void buttonPressed() {
|
public void buttonPressed() {
|
||||||
Intent intent = new Intent(mapActivity, mapActivity.getMyApplication().getAppCustomization().getTrackActivity());
|
OsmandApplication app = mapActivity.getMyApplication();
|
||||||
SelectedGpxFile selectedGpxFile = selectedGpxPoint.getSelectedGpxFile();
|
SelectedGpxFile selectedGpxFile = selectedGpxPoint.getSelectedGpxFile();
|
||||||
if (selectedGpxFile.isShowCurrentTrack()) {
|
if (Version.isDeveloperVersion(app)) {
|
||||||
intent.putExtra(TrackActivity.CURRENT_RECORDING, true);
|
mapActivity.getContextMenu().hide(false);
|
||||||
|
TrackMenuFragment.showInstance(mapActivity, selectedGpxFile.getGpxFile().path, selectedGpxFile.isShowCurrentTrack());
|
||||||
} else {
|
} else {
|
||||||
intent.putExtra(TrackActivity.TRACK_FILE_NAME, selectedGpxFile.getGpxFile().path);
|
Intent intent = new Intent(mapActivity, mapActivity.getMyApplication().getAppCustomization().getTrackActivity());
|
||||||
|
if (selectedGpxFile.isShowCurrentTrack()) {
|
||||||
|
intent.putExtra(TrackActivity.CURRENT_RECORDING, true);
|
||||||
|
} else {
|
||||||
|
intent.putExtra(TrackActivity.TRACK_FILE_NAME, selectedGpxFile.getGpxFile().path);
|
||||||
|
}
|
||||||
|
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
|
mapActivity.startActivity(intent);
|
||||||
}
|
}
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
|
||||||
mapActivity.startActivity(intent);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
leftTitleButtonController.caption = mapActivity.getString(R.string.shared_string_open_track);
|
leftTitleButtonController.caption = mapActivity.getString(R.string.shared_string_open_track);
|
||||||
|
|
|
@ -427,7 +427,7 @@ public class MapMarkersGroupsAdapter extends RecyclerView.Adapter<RecyclerView.V
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
if (mapActivity.getSupportFragmentManager() != null) {
|
if (mapActivity.getSupportFragmentManager() != null) {
|
||||||
WikivoyageArticleDialogFragment.showInstanceByTitle(app, mapActivity.getSupportFragmentManager(), article.getTitle(), article.getLang());
|
WikivoyageArticleDialogFragment.showInstance(app, mapActivity.getSupportFragmentManager(), article.generateIdentifier(), article.getLang());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1211,7 +1211,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
|
||||||
SelectedGpxFile selectedGpxFile = mapActivity.getMyApplication().getSelectedGpxHelper()
|
SelectedGpxFile selectedGpxFile = mapActivity.getMyApplication().getSelectedGpxHelper()
|
||||||
.getSelectedFileByPath(gpxFile.path);
|
.getSelectedFileByPath(gpxFile.path);
|
||||||
boolean showOnMap = selectedGpxFile != null;
|
boolean showOnMap = selectedGpxFile != null;
|
||||||
saveExistingGpx(gpxFile, showOnMap, false, true, FinalSaveAction.SHOW_TOAST);
|
saveExistingGpx(gpxFile, showOnMap, false, true, FinalSaveAction.SHOW_IS_SAVED_FRAGMENT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import net.osmand.GPXUtilities.Route;
|
||||||
import net.osmand.GPXUtilities.Track;
|
import net.osmand.GPXUtilities.Track;
|
||||||
import net.osmand.GPXUtilities.TrkSegment;
|
import net.osmand.GPXUtilities.TrkSegment;
|
||||||
import net.osmand.GPXUtilities.WptPt;
|
import net.osmand.GPXUtilities.WptPt;
|
||||||
|
import net.osmand.GPXUtilities.Metadata;
|
||||||
import net.osmand.plus.OsmandApplication;
|
import net.osmand.plus.OsmandApplication;
|
||||||
import net.osmand.plus.R;
|
import net.osmand.plus.R;
|
||||||
import net.osmand.plus.Version;
|
import net.osmand.plus.Version;
|
||||||
|
@ -91,6 +92,10 @@ class SaveGpxRouteAsyncTask extends AsyncTask<Void, Void, Exception> {
|
||||||
backupFile = FileUtils.backupFile(app, outFile);
|
backupFile = FileUtils.backupFile(app, outFile);
|
||||||
String trackName = Algorithms.getFileNameWithoutExtension(outFile);
|
String trackName = Algorithms.getFileNameWithoutExtension(outFile);
|
||||||
GPXFile gpx = generateGpxFile(measurementLayer, editingCtx, trackName, gpxFile);
|
GPXFile gpx = generateGpxFile(measurementLayer, editingCtx, trackName, gpxFile);
|
||||||
|
if (gpxFile.metadata != null) {
|
||||||
|
gpx.metadata = new Metadata();
|
||||||
|
gpx.metadata.getExtensionsToWrite().putAll(gpxFile.metadata.getExtensionsToRead());
|
||||||
|
}
|
||||||
if (!gpx.showCurrentTrack) {
|
if (!gpx.showCurrentTrack) {
|
||||||
res = GPXUtilities.writeGpxFile(outFile, gpx);
|
res = GPXUtilities.writeGpxFile(outFile, gpx);
|
||||||
}
|
}
|
||||||
|
|
88
OsmAnd/src/net/osmand/plus/myplaces/DeletePointsTask.java
Normal file
88
OsmAnd/src/net/osmand/plus/myplaces/DeletePointsTask.java
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
package net.osmand.plus.myplaces;
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
|
import net.osmand.GPXUtilities;
|
||||||
|
import net.osmand.GPXUtilities.GPXFile;
|
||||||
|
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem;
|
||||||
|
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItemType;
|
||||||
|
import net.osmand.plus.OsmandApplication;
|
||||||
|
import net.osmand.plus.activities.SavingTrackHelper;
|
||||||
|
import net.osmand.plus.mapmarkers.MapMarkersGroup;
|
||||||
|
import net.osmand.plus.mapmarkers.MapMarkersHelper;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
class DeletePointsTask extends AsyncTask<Void, Void, Void> {
|
||||||
|
|
||||||
|
private OsmandApplication app;
|
||||||
|
private GPXFile gpx;
|
||||||
|
private Set<GpxDisplayItem> selectedItems;
|
||||||
|
private WeakReference<OnPointsDeleteListener> listenerRef;
|
||||||
|
|
||||||
|
DeletePointsTask(OsmandApplication app, GPXFile gpxFile, Set<GpxDisplayItem> selectedItems, OnPointsDeleteListener listener) {
|
||||||
|
this.app = app;
|
||||||
|
this.gpx = gpxFile;
|
||||||
|
this.selectedItems = selectedItems;
|
||||||
|
this.listenerRef = new WeakReference<>(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
OnPointsDeleteListener listener = listenerRef.get();
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onPointsDeletionStarted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground(Void... params) {
|
||||||
|
SavingTrackHelper savingTrackHelper = app.getSavingTrackHelper();
|
||||||
|
if (gpx != null) {
|
||||||
|
for (GpxDisplayItem item : selectedItems) {
|
||||||
|
if (gpx.showCurrentTrack) {
|
||||||
|
savingTrackHelper.deletePointData(item.locationStart);
|
||||||
|
} else {
|
||||||
|
if (item.group.getType() == GpxDisplayItemType.TRACK_POINTS) {
|
||||||
|
gpx.deleteWptPt(item.locationStart);
|
||||||
|
} else if (item.group.getType() == GpxDisplayItemType.TRACK_ROUTE_POINTS) {
|
||||||
|
gpx.deleteRtePt(item.locationStart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!gpx.showCurrentTrack) {
|
||||||
|
GPXUtilities.writeGpxFile(new File(gpx.path), gpx);
|
||||||
|
boolean selected = app.getSelectedGpxHelper().getSelectedFileByPath(gpx.path) != null;
|
||||||
|
if (selected) {
|
||||||
|
app.getSelectedGpxHelper().setGpxFileToDisplay(gpx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
syncGpx(gpx);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void syncGpx(GPXFile gpxFile) {
|
||||||
|
MapMarkersHelper helper = app.getMapMarkersHelper();
|
||||||
|
MapMarkersGroup group = helper.getMarkersGroup(gpxFile);
|
||||||
|
if (group != null) {
|
||||||
|
helper.runSynchronization(group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Void aVoid) {
|
||||||
|
OnPointsDeleteListener listener = listenerRef.get();
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onPointsDeleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface OnPointsDeleteListener {
|
||||||
|
void onPointsDeletionStarted();
|
||||||
|
|
||||||
|
void onPointsDeleted();
|
||||||
|
}
|
||||||
|
}
|
695
OsmAnd/src/net/osmand/plus/myplaces/GPXItemPagerAdapter.java
Normal file
695
OsmAnd/src/net/osmand/plus/myplaces/GPXItemPagerAdapter.java
Normal file
|
@ -0,0 +1,695 @@
|
||||||
|
package net.osmand.plus.myplaces;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.graphics.Matrix;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.widget.SwitchCompat;
|
||||||
|
import androidx.viewpager.widget.PagerAdapter;
|
||||||
|
|
||||||
|
import com.github.mikephil.charting.charts.LineChart;
|
||||||
|
import com.github.mikephil.charting.data.Entry;
|
||||||
|
import com.github.mikephil.charting.data.LineData;
|
||||||
|
import com.github.mikephil.charting.highlight.Highlight;
|
||||||
|
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
|
||||||
|
import com.github.mikephil.charting.listener.ChartTouchListener.ChartGesture;
|
||||||
|
import com.github.mikephil.charting.listener.OnChartGestureListener;
|
||||||
|
import com.github.mikephil.charting.listener.OnChartValueSelectedListener;
|
||||||
|
|
||||||
|
import net.osmand.GPXUtilities;
|
||||||
|
import net.osmand.GPXUtilities.GPXFile;
|
||||||
|
import net.osmand.GPXUtilities.GPXTrackAnalysis;
|
||||||
|
import net.osmand.GPXUtilities.Track;
|
||||||
|
import net.osmand.GPXUtilities.TrkSegment;
|
||||||
|
import net.osmand.GPXUtilities.WptPt;
|
||||||
|
import net.osmand.plus.GPXDatabase.GpxDataItem;
|
||||||
|
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem;
|
||||||
|
import net.osmand.plus.OsmAndFormatter;
|
||||||
|
import net.osmand.plus.OsmandApplication;
|
||||||
|
import net.osmand.plus.R;
|
||||||
|
import net.osmand.plus.UiUtilities;
|
||||||
|
import net.osmand.plus.helpers.AndroidUiHelper;
|
||||||
|
import net.osmand.plus.helpers.GpxUiHelper;
|
||||||
|
import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetAxisType;
|
||||||
|
import net.osmand.plus.helpers.GpxUiHelper.LineGraphType;
|
||||||
|
import net.osmand.plus.helpers.GpxUiHelper.OrderedLineDataSet;
|
||||||
|
import net.osmand.plus.track.TrackDisplayHelper;
|
||||||
|
import net.osmand.plus.views.controls.PagerSlidingTabStrip;
|
||||||
|
import net.osmand.plus.views.controls.PagerSlidingTabStrip.CustomTabProvider;
|
||||||
|
import net.osmand.plus.views.controls.WrapContentHeightViewPager.ViewAtPositionInterface;
|
||||||
|
import net.osmand.util.Algorithms;
|
||||||
|
import net.osmand.util.MapUtils;
|
||||||
|
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static net.osmand.plus.helpers.GpxUiHelper.LineGraphType.ALTITUDE;
|
||||||
|
import static net.osmand.plus.helpers.GpxUiHelper.LineGraphType.SLOPE;
|
||||||
|
import static net.osmand.plus.helpers.GpxUiHelper.LineGraphType.SPEED;
|
||||||
|
|
||||||
|
public class GPXItemPagerAdapter extends PagerAdapter implements CustomTabProvider, ViewAtPositionInterface {
|
||||||
|
|
||||||
|
private OsmandApplication app;
|
||||||
|
private UiUtilities iconsCache;
|
||||||
|
private TrackDisplayHelper displayHelper;
|
||||||
|
private Map<GPXTabItemType, List<ILineDataSet>> dataSetsMap = new HashMap<>();
|
||||||
|
|
||||||
|
private WptPt selectedWpt;
|
||||||
|
private TrkSegment segment;
|
||||||
|
private GpxDisplayItem gpxItem;
|
||||||
|
private GPXTabItemType[] tabTypes;
|
||||||
|
|
||||||
|
private PagerSlidingTabStrip tabs;
|
||||||
|
private SparseArray<View> views = new SparseArray<>();
|
||||||
|
private SegmentActionsListener actionsListener;
|
||||||
|
|
||||||
|
private boolean chartClicked;
|
||||||
|
|
||||||
|
|
||||||
|
public GPXItemPagerAdapter(@NonNull PagerSlidingTabStrip tabs,
|
||||||
|
@NonNull GpxDisplayItem gpxItem,
|
||||||
|
@NonNull TrackDisplayHelper displayHelper,
|
||||||
|
@NonNull SegmentActionsListener actionsListener) {
|
||||||
|
super();
|
||||||
|
this.tabs = tabs;
|
||||||
|
this.gpxItem = gpxItem;
|
||||||
|
this.displayHelper = displayHelper;
|
||||||
|
this.actionsListener = actionsListener;
|
||||||
|
app = (OsmandApplication) tabs.getContext().getApplicationContext();
|
||||||
|
iconsCache = app.getUIUtilities();
|
||||||
|
fetchTabTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fetchTabTypes() {
|
||||||
|
List<GPXTabItemType> tabTypeList = new ArrayList<>();
|
||||||
|
tabTypeList.add(GPXTabItemType.GPX_TAB_ITEM_GENERAL);
|
||||||
|
if (gpxItem != null && gpxItem.analysis != null) {
|
||||||
|
if (gpxItem.analysis.hasElevationData) {
|
||||||
|
tabTypeList.add(GPXTabItemType.GPX_TAB_ITEM_ALTITUDE);
|
||||||
|
}
|
||||||
|
if (gpxItem.analysis.isSpeedSpecified()) {
|
||||||
|
tabTypeList.add(GPXTabItemType.GPX_TAB_ITEM_SPEED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tabTypes = tabTypeList.toArray(new GPXTabItemType[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ILineDataSet> getDataSets(LineChart chart, GPXTabItemType tabType,
|
||||||
|
LineGraphType firstType, LineGraphType secondType) {
|
||||||
|
List<ILineDataSet> dataSets = dataSetsMap.get(tabType);
|
||||||
|
if (dataSets == null && chart != null) {
|
||||||
|
GPXTrackAnalysis analysis = gpxItem.analysis;
|
||||||
|
GpxDataItem gpxDataItem = displayHelper.getGpxDataItem();
|
||||||
|
boolean calcWithoutGaps = gpxItem.isGeneralTrack() && gpxDataItem != null && !gpxDataItem.isJoinSegments();
|
||||||
|
dataSets = GpxUiHelper.getDataSets(chart, app, analysis, firstType, secondType, calcWithoutGaps);
|
||||||
|
dataSetsMap.put(tabType, dataSets);
|
||||||
|
}
|
||||||
|
return dataSets;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TrkSegment getTrackSegment(LineChart chart) {
|
||||||
|
if (segment == null) {
|
||||||
|
LineData lineData = chart.getLineData();
|
||||||
|
List<ILineDataSet> ds = lineData != null ? lineData.getDataSets() : null;
|
||||||
|
if (ds != null && ds.size() > 0) {
|
||||||
|
for (GPXUtilities.Track t : gpxItem.group.getGpx().tracks) {
|
||||||
|
for (TrkSegment s : t.segments) {
|
||||||
|
if (s.points.size() > 0 && s.points.get(0).equals(gpxItem.analysis.locationStart)) {
|
||||||
|
segment = s;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (segment != null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return segment;
|
||||||
|
}
|
||||||
|
|
||||||
|
private WptPt getPoint(LineChart chart, float pos) {
|
||||||
|
WptPt wpt = null;
|
||||||
|
LineData lineData = chart.getLineData();
|
||||||
|
List<ILineDataSet> ds = lineData != null ? lineData.getDataSets() : null;
|
||||||
|
if (ds != null && ds.size() > 0) {
|
||||||
|
TrkSegment segment = getTrackSegment(chart);
|
||||||
|
OrderedLineDataSet dataSet = (OrderedLineDataSet) ds.get(0);
|
||||||
|
if (gpxItem.chartAxisType == GPXDataSetAxisType.TIME) {
|
||||||
|
float time = pos * 1000;
|
||||||
|
for (WptPt p : segment.points) {
|
||||||
|
if (p.time - gpxItem.analysis.startTime >= time) {
|
||||||
|
wpt = p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
float distance = pos * dataSet.getDivX();
|
||||||
|
double totalDistance = 0;
|
||||||
|
for (int i = 0; i < segment.points.size(); i++) {
|
||||||
|
WptPt currentPoint = segment.points.get(i);
|
||||||
|
if (i != 0) {
|
||||||
|
WptPt previousPoint = segment.points.get(i - 1);
|
||||||
|
totalDistance += MapUtils.getDistance(previousPoint.lat, previousPoint.lon, currentPoint.lat, currentPoint.lon);
|
||||||
|
}
|
||||||
|
if (currentPoint.distance >= distance || Math.abs(totalDistance - distance) < 0.1) {
|
||||||
|
wpt = currentPoint;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return wpt;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return tabTypes.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence getPageTitle(int position) {
|
||||||
|
return tabTypes[position].toHumanString(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Object instantiateItem(@NonNull ViewGroup container, int position) {
|
||||||
|
GPXTabItemType tabType = tabTypes[position];
|
||||||
|
View view = getViewForTab(container, tabType);
|
||||||
|
GPXFile gpxFile = displayHelper.getGpx();
|
||||||
|
if (gpxFile != null && gpxItem != null) {
|
||||||
|
GPXTrackAnalysis analysis = gpxItem.analysis;
|
||||||
|
LineChart chart = view.findViewById(R.id.chart);
|
||||||
|
setupChart(view, chart);
|
||||||
|
|
||||||
|
switch (tabType) {
|
||||||
|
case GPX_TAB_ITEM_GENERAL:
|
||||||
|
setupGeneralTab(view, chart, analysis, gpxFile, position);
|
||||||
|
break;
|
||||||
|
case GPX_TAB_ITEM_ALTITUDE:
|
||||||
|
setupAltitudeTab(view, chart, analysis, gpxFile, position);
|
||||||
|
break;
|
||||||
|
case GPX_TAB_ITEM_SPEED:
|
||||||
|
setupSpeedTab(view, chart, analysis, gpxFile, position);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
container.addView(view, 0);
|
||||||
|
views.put(position, view);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private View getViewForTab(@NonNull ViewGroup container, @NonNull GPXTabItemType tabType) {
|
||||||
|
LayoutInflater inflater = LayoutInflater.from(container.getContext());
|
||||||
|
switch (tabType) {
|
||||||
|
case GPX_TAB_ITEM_ALTITUDE:
|
||||||
|
return inflater.inflate(R.layout.gpx_item_altitude, container, false);
|
||||||
|
case GPX_TAB_ITEM_SPEED:
|
||||||
|
return inflater.inflate(R.layout.gpx_item_speed, container, false);
|
||||||
|
case GPX_TAB_ITEM_GENERAL:
|
||||||
|
default:
|
||||||
|
return inflater.inflate(R.layout.gpx_item_general, container, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupSpeedTab(View view, LineChart chart, GPXTrackAnalysis analysis, GPXFile gpxFile, int position) {
|
||||||
|
if (analysis != null && analysis.isSpeedSpecified()) {
|
||||||
|
if (analysis.hasSpeedData) {
|
||||||
|
GpxUiHelper.setupGPXChart(app, chart, 4);
|
||||||
|
chart.setData(new LineData(getDataSets(chart, GPXTabItemType.GPX_TAB_ITEM_SPEED, SPEED, null)));
|
||||||
|
updateChart(chart);
|
||||||
|
chart.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
chart.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
((ImageView) view.findViewById(R.id.average_icon))
|
||||||
|
.setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_speed));
|
||||||
|
((ImageView) view.findViewById(R.id.max_icon))
|
||||||
|
.setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_max_speed));
|
||||||
|
((ImageView) view.findViewById(R.id.time_moving_icon))
|
||||||
|
.setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_time_span));
|
||||||
|
((ImageView) view.findViewById(R.id.distance_icon))
|
||||||
|
.setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_polygom_dark));
|
||||||
|
|
||||||
|
String avg = OsmAndFormatter.getFormattedSpeed(analysis.avgSpeed, app);
|
||||||
|
String max = OsmAndFormatter.getFormattedSpeed(analysis.maxSpeed, app);
|
||||||
|
|
||||||
|
((TextView) view.findViewById(R.id.average_text)).setText(avg);
|
||||||
|
((TextView) view.findViewById(R.id.max_text)).setText(max);
|
||||||
|
|
||||||
|
view.findViewById(R.id.gpx_join_gaps_container).setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
if (displayHelper.setJoinSegments(!displayHelper.isJoinSegments())) {
|
||||||
|
for (int i = 0; i < getCount(); i++) {
|
||||||
|
View view = getViewAtPosition(i);
|
||||||
|
updateJoinGapsInfo(view, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
chart.setVisibility(View.GONE);
|
||||||
|
view.findViewById(R.id.average_max).setVisibility(View.GONE);
|
||||||
|
view.findViewById(R.id.list_divider).setVisibility(View.GONE);
|
||||||
|
view.findViewById(R.id.time_distance).setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
updateJoinGapsInfo(view, position);
|
||||||
|
view.findViewById(R.id.analyze_on_map).setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
openAnalyzeOnMap(GPXTabItemType.GPX_TAB_ITEM_SPEED);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (gpxFile.showCurrentTrack) {
|
||||||
|
view.findViewById(R.id.split_interval).setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
view.findViewById(R.id.split_interval).setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
openSplitIntervalScreen();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ImageView overflowMenu = view.findViewById(R.id.overflow_menu);
|
||||||
|
if (!gpxItem.group.getTrack().generalTrack) {
|
||||||
|
setupOptionsPopupMenu(overflowMenu, false);
|
||||||
|
} else {
|
||||||
|
overflowMenu.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupOptionsPopupMenu(ImageView overflowMenu, final boolean confirmDeletion) {
|
||||||
|
overflowMenu.setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_overflow_menu_white));
|
||||||
|
overflowMenu.setVisibility(View.VISIBLE);
|
||||||
|
overflowMenu.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
actionsListener.showOptionsPopupMenu(view, getTrkSegment(), confirmDeletion);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupAltitudeTab(View view, LineChart chart, GPXTrackAnalysis analysis, GPXFile gpxFile, int position) {
|
||||||
|
if (analysis != null) {
|
||||||
|
if (analysis.hasElevationData) {
|
||||||
|
GpxUiHelper.setupGPXChart(app, chart, 4);
|
||||||
|
chart.setData(new LineData(getDataSets(chart, GPXTabItemType.GPX_TAB_ITEM_ALTITUDE, ALTITUDE, SLOPE)));
|
||||||
|
updateChart(chart);
|
||||||
|
chart.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
chart.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
((ImageView) view.findViewById(R.id.average_icon))
|
||||||
|
.setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_altitude_average));
|
||||||
|
((ImageView) view.findViewById(R.id.range_icon))
|
||||||
|
.setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_altitude_average));
|
||||||
|
((ImageView) view.findViewById(R.id.ascent_icon))
|
||||||
|
.setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_altitude_ascent));
|
||||||
|
((ImageView) view.findViewById(R.id.descent_icon))
|
||||||
|
.setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_altitude_descent));
|
||||||
|
|
||||||
|
String min = OsmAndFormatter.getFormattedAlt(analysis.minElevation, app);
|
||||||
|
String max = OsmAndFormatter.getFormattedAlt(analysis.maxElevation, app);
|
||||||
|
String asc = OsmAndFormatter.getFormattedAlt(analysis.diffElevationUp, app);
|
||||||
|
String desc = OsmAndFormatter.getFormattedAlt(analysis.diffElevationDown, app);
|
||||||
|
|
||||||
|
((TextView) view.findViewById(R.id.average_text))
|
||||||
|
.setText(OsmAndFormatter.getFormattedAlt(analysis.avgElevation, app));
|
||||||
|
((TextView) view.findViewById(R.id.range_text)).setText(String.format("%s - %s", min, max));
|
||||||
|
((TextView) view.findViewById(R.id.ascent_text)).setText(asc);
|
||||||
|
((TextView) view.findViewById(R.id.descent_text)).setText(desc);
|
||||||
|
|
||||||
|
view.findViewById(R.id.gpx_join_gaps_container).setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
if (displayHelper.setJoinSegments(!displayHelper.isJoinSegments())) {
|
||||||
|
for (int i = 0; i < getCount(); i++) {
|
||||||
|
View view = getViewAtPosition(i);
|
||||||
|
updateJoinGapsInfo(view, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
chart.setVisibility(View.GONE);
|
||||||
|
view.findViewById(R.id.average_range).setVisibility(View.GONE);
|
||||||
|
view.findViewById(R.id.list_divider).setVisibility(View.GONE);
|
||||||
|
view.findViewById(R.id.ascent_descent).setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
updateJoinGapsInfo(view, position);
|
||||||
|
view.findViewById(R.id.analyze_on_map).setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
openAnalyzeOnMap(GPXTabItemType.GPX_TAB_ITEM_ALTITUDE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (gpxFile.showCurrentTrack) {
|
||||||
|
view.findViewById(R.id.split_interval).setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
view.findViewById(R.id.split_interval).setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
openSplitIntervalScreen();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ImageView overflowMenu = view.findViewById(R.id.overflow_menu);
|
||||||
|
if (!gpxItem.group.getTrack().generalTrack) {
|
||||||
|
setupOptionsPopupMenu(overflowMenu, false);
|
||||||
|
} else {
|
||||||
|
overflowMenu.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupGeneralTab(View view, LineChart chart, GPXTrackAnalysis analysis, GPXFile gpxFile, int position) {
|
||||||
|
if (analysis != null) {
|
||||||
|
if (analysis.hasElevationData || analysis.hasSpeedData) {
|
||||||
|
GpxUiHelper.setupGPXChart(app, chart, 4);
|
||||||
|
chart.setData(new LineData(getDataSets(chart, GPXTabItemType.GPX_TAB_ITEM_GENERAL, ALTITUDE, SPEED)));
|
||||||
|
updateChart(chart);
|
||||||
|
chart.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
chart.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
((ImageView) view.findViewById(R.id.distance_icon))
|
||||||
|
.setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_polygom_dark));
|
||||||
|
((ImageView) view.findViewById(R.id.duration_icon))
|
||||||
|
.setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_time_span));
|
||||||
|
((ImageView) view.findViewById(R.id.start_time_icon))
|
||||||
|
.setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_time_start));
|
||||||
|
((ImageView) view.findViewById(R.id.end_time_icon))
|
||||||
|
.setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_time_end));
|
||||||
|
|
||||||
|
view.findViewById(R.id.gpx_join_gaps_container).setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
if (displayHelper.setJoinSegments(!displayHelper.isJoinSegments())) {
|
||||||
|
actionsListener.updateContent();
|
||||||
|
for (int i = 0; i < getCount(); i++) {
|
||||||
|
View view = getViewAtPosition(i);
|
||||||
|
updateJoinGapsInfo(view, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (analysis.timeSpan > 0) {
|
||||||
|
DateFormat tf = SimpleDateFormat.getTimeInstance(DateFormat.SHORT);
|
||||||
|
DateFormat df = SimpleDateFormat.getDateInstance(DateFormat.MEDIUM);
|
||||||
|
|
||||||
|
Date start = new Date(analysis.startTime);
|
||||||
|
((TextView) view.findViewById(R.id.start_time_text)).setText(tf.format(start));
|
||||||
|
((TextView) view.findViewById(R.id.start_date_text)).setText(df.format(start));
|
||||||
|
Date end = new Date(analysis.endTime);
|
||||||
|
((TextView) view.findViewById(R.id.end_time_text)).setText(tf.format(end));
|
||||||
|
((TextView) view.findViewById(R.id.end_date_text)).setText(df.format(end));
|
||||||
|
} else {
|
||||||
|
view.findViewById(R.id.list_divider).setVisibility(View.GONE);
|
||||||
|
view.findViewById(R.id.start_end_time).setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
chart.setVisibility(View.GONE);
|
||||||
|
view.findViewById(R.id.distance_time_span).setVisibility(View.GONE);
|
||||||
|
view.findViewById(R.id.list_divider).setVisibility(View.GONE);
|
||||||
|
view.findViewById(R.id.start_end_time).setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
updateJoinGapsInfo(view, position);
|
||||||
|
view.findViewById(R.id.analyze_on_map).setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
openAnalyzeOnMap(GPXTabItemType.GPX_TAB_ITEM_GENERAL);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (gpxFile.showCurrentTrack) {
|
||||||
|
view.findViewById(R.id.split_interval).setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
view.findViewById(R.id.split_interval).setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
openSplitIntervalScreen();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ImageView overflowMenu = view.findViewById(R.id.overflow_menu);
|
||||||
|
if (!gpxItem.group.getTrack().generalTrack) {
|
||||||
|
setupOptionsPopupMenu(overflowMenu, true);
|
||||||
|
} else {
|
||||||
|
overflowMenu.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupChart(final View view, final LineChart chart) {
|
||||||
|
chart.setHighlightPerDragEnabled(chartClicked);
|
||||||
|
chart.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
if (!chartClicked) {
|
||||||
|
chartClicked = true;
|
||||||
|
if (selectedWpt != null) {
|
||||||
|
actionsListener.onPointSelected(selectedWpt.lat, selectedWpt.lon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
chart.setOnTouchListener(new View.OnTouchListener() {
|
||||||
|
|
||||||
|
private float listViewYPos;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onTouch(View v, MotionEvent event) {
|
||||||
|
if (chartClicked) {
|
||||||
|
actionsListener.onChartTouch();
|
||||||
|
if (!chart.isHighlightPerDragEnabled()) {
|
||||||
|
chart.setHighlightPerDragEnabled(true);
|
||||||
|
}
|
||||||
|
switch (event.getAction()) {
|
||||||
|
case MotionEvent.ACTION_DOWN:
|
||||||
|
listViewYPos = event.getRawY();
|
||||||
|
break;
|
||||||
|
case MotionEvent.ACTION_MOVE:
|
||||||
|
actionsListener.scrollBy(Math.round(listViewYPos - event.getRawY()));
|
||||||
|
listViewYPos = event.getRawY();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
chart.setOnChartValueSelectedListener(new OnChartValueSelectedListener() {
|
||||||
|
@Override
|
||||||
|
public void onValueSelected(Entry e, Highlight h) {
|
||||||
|
WptPt wpt = getPoint(chart, h.getX());
|
||||||
|
selectedWpt = wpt;
|
||||||
|
if (chartClicked && wpt != null) {
|
||||||
|
actionsListener.onPointSelected(wpt.lat, wpt.lon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNothingSelected() {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
chart.setOnChartGestureListener(new OnChartGestureListener() {
|
||||||
|
|
||||||
|
float highlightDrawX = -1;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChartGestureStart(MotionEvent me, ChartGesture lastPerformedGesture) {
|
||||||
|
if (chart.getHighlighted() != null && chart.getHighlighted().length > 0) {
|
||||||
|
highlightDrawX = chart.getHighlighted()[0].getDrawX();
|
||||||
|
} else {
|
||||||
|
highlightDrawX = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChartGestureEnd(MotionEvent me, ChartGesture lastPerformedGesture) {
|
||||||
|
gpxItem.chartMatrix = new Matrix(chart.getViewPortHandler().getMatrixTouch());
|
||||||
|
Highlight[] highlights = chart.getHighlighted();
|
||||||
|
if (highlights != null && highlights.length > 0) {
|
||||||
|
gpxItem.chartHighlightPos = highlights[0].getX();
|
||||||
|
} else {
|
||||||
|
gpxItem.chartHighlightPos = -1;
|
||||||
|
}
|
||||||
|
if (chartClicked) {
|
||||||
|
for (int i = 0; i < getCount(); i++) {
|
||||||
|
View v = getViewAtPosition(i);
|
||||||
|
if (v != view) {
|
||||||
|
updateChart(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChartLongPressed(MotionEvent me) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChartDoubleTapped(MotionEvent me) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChartSingleTapped(MotionEvent me) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChartScale(MotionEvent me, float scaleX, float scaleY) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChartTranslate(MotionEvent me, float dX, float dY) {
|
||||||
|
if (chartClicked && highlightDrawX != -1) {
|
||||||
|
Highlight h = chart.getHighlightByTouchPoint(highlightDrawX, 0f);
|
||||||
|
if (h != null) {
|
||||||
|
chart.highlightValue(h);
|
||||||
|
WptPt wpt = getPoint(chart, h.getX());
|
||||||
|
if (wpt != null) {
|
||||||
|
actionsListener.onPointSelected(wpt.lat, wpt.lon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroyItem(@NonNull ViewGroup collection, int position, @NonNull Object view) {
|
||||||
|
views.remove(position);
|
||||||
|
collection.removeView((View) view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
|
||||||
|
return view == object;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getCustomTabView(@NonNull ViewGroup parent, int position) {
|
||||||
|
View tab = LayoutInflater.from(parent.getContext()).inflate(R.layout.gpx_tab, parent, false);
|
||||||
|
tab.setTag(tabTypes[position].name());
|
||||||
|
deselect(tab);
|
||||||
|
return tab;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void select(View tab) {
|
||||||
|
GPXTabItemType tabType = GPXTabItemType.valueOf((String) tab.getTag());
|
||||||
|
ImageView img = tab.findViewById(R.id.tab_image);
|
||||||
|
switch (tabs.getTabSelectionType()) {
|
||||||
|
case ALPHA:
|
||||||
|
img.setAlpha(tabs.getTabTextSelectedAlpha());
|
||||||
|
break;
|
||||||
|
case SOLID_COLOR:
|
||||||
|
img.setImageDrawable(iconsCache.getPaintedIcon(tabType.getIconId(), tabs.getTextColor()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deselect(View tab) {
|
||||||
|
GPXTabItemType tabType = GPXTabItemType.valueOf((String) tab.getTag());
|
||||||
|
ImageView img = tab.findViewById(R.id.tab_image);
|
||||||
|
switch (tabs.getTabSelectionType()) {
|
||||||
|
case ALPHA:
|
||||||
|
img.setAlpha(tabs.getTabTextAlpha());
|
||||||
|
break;
|
||||||
|
case SOLID_COLOR:
|
||||||
|
img.setImageDrawable(iconsCache.getPaintedIcon(tabType.getIconId(), tabs.getTabInactiveTextColor()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getViewAtPosition(int position) {
|
||||||
|
return views.get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateChart(int position) {
|
||||||
|
View view = getViewAtPosition(position);
|
||||||
|
if (view != null) {
|
||||||
|
updateChart((LineChart) view.findViewById(R.id.chart));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateJoinGapsInfo(View view, int position) {
|
||||||
|
if (view != null) {
|
||||||
|
GPXTrackAnalysis analysis = gpxItem.analysis;
|
||||||
|
GPXTabItemType tabType = tabTypes[position];
|
||||||
|
boolean visible = gpxItem.isGeneralTrack() && analysis != null && tabType.equals(GPXTabItemType.GPX_TAB_ITEM_GENERAL);
|
||||||
|
AndroidUiHelper.updateVisibility(view.findViewById(R.id.gpx_join_gaps_container), visible);
|
||||||
|
boolean joinSegments = displayHelper.isJoinSegments();
|
||||||
|
((SwitchCompat) view.findViewById(R.id.gpx_join_gaps_switch)).setChecked(joinSegments);
|
||||||
|
if (analysis != null) {
|
||||||
|
if (tabType.equals(GPXTabItemType.GPX_TAB_ITEM_GENERAL)) {
|
||||||
|
float totalDistance = !joinSegments && gpxItem.isGeneralTrack() ? analysis.totalDistanceWithoutGaps : analysis.totalDistance;
|
||||||
|
float timeSpan = !joinSegments && gpxItem.isGeneralTrack() ? analysis.timeSpanWithoutGaps : analysis.timeSpan;
|
||||||
|
|
||||||
|
((TextView) view.findViewById(R.id.distance_text)).setText(OsmAndFormatter.getFormattedDistance(totalDistance, app));
|
||||||
|
((TextView) view.findViewById(R.id.duration_text)).setText(Algorithms.formatDuration((int) (timeSpan / 1000), app.accessibilityEnabled()));
|
||||||
|
} else if (tabType.equals(GPXTabItemType.GPX_TAB_ITEM_SPEED)) {
|
||||||
|
long timeMoving = !joinSegments && gpxItem.isGeneralTrack() ? analysis.timeMovingWithoutGaps : analysis.timeMoving;
|
||||||
|
float totalDistanceMoving = !joinSegments && gpxItem.isGeneralTrack() ? analysis.totalDistanceMovingWithoutGaps : analysis.totalDistanceMoving;
|
||||||
|
|
||||||
|
((TextView) view.findViewById(R.id.time_moving_text)).setText(Algorithms.formatDuration((int) (timeMoving / 1000), app.accessibilityEnabled()));
|
||||||
|
((TextView) view.findViewById(R.id.distance_text)).setText(OsmAndFormatter.getFormattedDistance(totalDistanceMoving, app));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateChart(LineChart chart) {
|
||||||
|
if (chart != null && !chart.isEmpty()) {
|
||||||
|
if (gpxItem.chartMatrix != null) {
|
||||||
|
chart.getViewPortHandler().refresh(new Matrix(gpxItem.chartMatrix), chart, true);
|
||||||
|
}
|
||||||
|
if (gpxItem.chartHighlightPos != -1) {
|
||||||
|
chart.highlightValue(gpxItem.chartHighlightPos, 0);
|
||||||
|
} else {
|
||||||
|
chart.highlightValue(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TrkSegment getTrkSegment() {
|
||||||
|
for (Track track : gpxItem.group.getGpx().tracks) {
|
||||||
|
if (!track.generalTrack && !gpxItem.isGeneralTrack() || track.generalTrack && gpxItem.isGeneralTrack()) {
|
||||||
|
for (TrkSegment segment : track.segments) {
|
||||||
|
if (segment.points.size() > 0 && segment.points.get(0).equals(gpxItem.analysis.locationStart)) {
|
||||||
|
return segment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void openAnalyzeOnMap(GPXTabItemType tabType) {
|
||||||
|
List<ILineDataSet> ds = getDataSets(null, tabType, null, null);
|
||||||
|
actionsListener.openAnalyzeOnMap(gpxItem, ds, tabType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openSplitIntervalScreen() {
|
||||||
|
actionsListener.openSplitInterval(gpxItem, getTrkSegment());
|
||||||
|
}
|
||||||
|
}
|
32
OsmAnd/src/net/osmand/plus/myplaces/GPXTabItemType.java
Normal file
32
OsmAnd/src/net/osmand/plus/myplaces/GPXTabItemType.java
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package net.osmand.plus.myplaces;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.annotation.DrawableRes;
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
|
||||||
|
import net.osmand.plus.R;
|
||||||
|
|
||||||
|
public enum GPXTabItemType {
|
||||||
|
|
||||||
|
GPX_TAB_ITEM_GENERAL(R.string.shared_string_overview, R.drawable.ic_action_polygom_dark),
|
||||||
|
GPX_TAB_ITEM_ALTITUDE(R.string.altitude, R.drawable.ic_action_altitude_average),
|
||||||
|
GPX_TAB_ITEM_SPEED(R.string.map_widget_speed, R.drawable.ic_action_speed);
|
||||||
|
|
||||||
|
private final int iconId;
|
||||||
|
private final int titleId;
|
||||||
|
|
||||||
|
GPXTabItemType(@StringRes int titleId, @DrawableRes int iconId) {
|
||||||
|
this.iconId = iconId;
|
||||||
|
this.titleId = titleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@DrawableRes
|
||||||
|
public int getIconId() {
|
||||||
|
return iconId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toHumanString(Context ctx) {
|
||||||
|
return ctx.getString(titleId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package net.osmand.plus.myplaces;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
|
||||||
|
|
||||||
|
import net.osmand.GPXUtilities.TrkSegment;
|
||||||
|
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface SegmentActionsListener {
|
||||||
|
|
||||||
|
void updateContent();
|
||||||
|
|
||||||
|
void onChartTouch();
|
||||||
|
|
||||||
|
void scrollBy(int px);
|
||||||
|
|
||||||
|
void onPointSelected(double lat, double lon);
|
||||||
|
|
||||||
|
void openSplitInterval(GpxDisplayItem gpxItem, TrkSegment trkSegment);
|
||||||
|
|
||||||
|
void showOptionsPopupMenu(View view, TrkSegment trkSegment, boolean confirmDeletion);
|
||||||
|
|
||||||
|
void openAnalyzeOnMap(GpxDisplayItem gpxItem, List<ILineDataSet> dataSets, GPXTabItemType tabType);
|
||||||
|
}
|
91
OsmAnd/src/net/osmand/plus/myplaces/SegmentGPXAdapter.java
Normal file
91
OsmAnd/src/net/osmand/plus/myplaces/SegmentGPXAdapter.java
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
package net.osmand.plus.myplaces;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
|
import net.osmand.AndroidUtils;
|
||||||
|
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem;
|
||||||
|
import net.osmand.plus.OsmandApplication;
|
||||||
|
import net.osmand.plus.R;
|
||||||
|
import net.osmand.plus.UiUtilities;
|
||||||
|
import net.osmand.plus.track.TrackDisplayHelper;
|
||||||
|
import net.osmand.plus.views.controls.PagerSlidingTabStrip;
|
||||||
|
import net.osmand.plus.views.controls.WrapContentHeightViewPager;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class SegmentGPXAdapter extends ArrayAdapter<GpxDisplayItem> {
|
||||||
|
|
||||||
|
private OsmandApplication app;
|
||||||
|
private TrackDisplayHelper displayHelper;
|
||||||
|
private SegmentActionsListener listener;
|
||||||
|
private boolean nightMode;
|
||||||
|
|
||||||
|
public SegmentGPXAdapter(@NonNull Context context, @NonNull List<GpxDisplayItem> items,
|
||||||
|
@NonNull TrackDisplayHelper displayHelper,
|
||||||
|
@NonNull SegmentActionsListener listener,
|
||||||
|
boolean nightMode) {
|
||||||
|
super(context, R.layout.gpx_list_item_tab_content, items);
|
||||||
|
this.app = (OsmandApplication) context.getApplicationContext();
|
||||||
|
this.displayHelper = displayHelper;
|
||||||
|
this.listener = listener;
|
||||||
|
this.nightMode = nightMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
|
||||||
|
View row = convertView;
|
||||||
|
boolean create = false;
|
||||||
|
if (row == null) {
|
||||||
|
create = true;
|
||||||
|
row = createGpxTabsView(displayHelper, parent, listener, nightMode);
|
||||||
|
}
|
||||||
|
GpxDisplayItem item = getItem(position);
|
||||||
|
if (item != null) {
|
||||||
|
WrapContentHeightViewPager pager = row.findViewById(R.id.pager);
|
||||||
|
PagerSlidingTabStrip tabLayout = row.findViewById(R.id.sliding_tabs);
|
||||||
|
|
||||||
|
pager.setAdapter(new GPXItemPagerAdapter(tabLayout, item, displayHelper, listener));
|
||||||
|
if (create) {
|
||||||
|
tabLayout.setViewPager(pager);
|
||||||
|
} else {
|
||||||
|
tabLayout.notifyDataSetChanged(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static View createGpxTabsView(TrackDisplayHelper displayHelper, ViewGroup root,
|
||||||
|
SegmentActionsListener listener, boolean nightMode) {
|
||||||
|
Context context = root.getContext();
|
||||||
|
View row = UiUtilities.getInflater(context, nightMode).inflate(R.layout.gpx_list_item_tab_content, root, false);
|
||||||
|
|
||||||
|
PagerSlidingTabStrip tabLayout = row.findViewById(R.id.sliding_tabs);
|
||||||
|
tabLayout.setTabBackground(R.color.color_transparent);
|
||||||
|
tabLayout.setIndicatorColorResource(nightMode ? R.color.active_color_primary_dark : R.color.active_color_primary_light);
|
||||||
|
tabLayout.setIndicatorBgColorResource(nightMode ? R.color.divider_color_dark : R.color.divider_color_light);
|
||||||
|
tabLayout.setIndicatorHeight(AndroidUtils.dpToPx(context, 1f));
|
||||||
|
if (!nightMode) {
|
||||||
|
tabLayout.setTextColor(tabLayout.getIndicatorColor());
|
||||||
|
tabLayout.setTabInactiveTextColor(ContextCompat.getColor(row.getContext(), R.color.text_color_secondary_light));
|
||||||
|
}
|
||||||
|
tabLayout.setTextSize(AndroidUtils.spToPx(context, 12f));
|
||||||
|
tabLayout.setShouldExpand(true);
|
||||||
|
WrapContentHeightViewPager pager = row.findViewById(R.id.pager);
|
||||||
|
pager.setSwipeable(false);
|
||||||
|
pager.setOffscreenPageLimit(2);
|
||||||
|
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
package net.osmand.plus.myplaces;
|
package net.osmand.plus.myplaces;
|
||||||
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.res.ColorStateList;
|
import android.content.res.ColorStateList;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
|
@ -24,14 +23,18 @@ import android.widget.TextView;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.appcompat.widget.ListPopupWindow;
|
import androidx.appcompat.widget.ListPopupWindow;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import androidx.fragment.app.DialogFragment;
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
import net.osmand.AndroidUtils;
|
import net.osmand.AndroidUtils;
|
||||||
import net.osmand.GPXUtilities;
|
import net.osmand.GPXUtilities;
|
||||||
import net.osmand.GPXUtilities.GPXFile;
|
import net.osmand.GPXUtilities.GPXFile;
|
||||||
import net.osmand.GPXUtilities.GPXTrackAnalysis;
|
import net.osmand.GPXUtilities.GPXTrackAnalysis;
|
||||||
|
import net.osmand.GPXUtilities.TrkSegment;
|
||||||
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;
|
||||||
|
@ -40,8 +43,8 @@ import net.osmand.plus.OsmAndFormatter;
|
||||||
import net.osmand.plus.OsmandApplication;
|
import net.osmand.plus.OsmandApplication;
|
||||||
import net.osmand.plus.R;
|
import net.osmand.plus.R;
|
||||||
import net.osmand.plus.UiUtilities;
|
import net.osmand.plus.UiUtilities;
|
||||||
import net.osmand.plus.activities.TrackActivity;
|
|
||||||
import net.osmand.plus.helpers.FontCache;
|
import net.osmand.plus.helpers.FontCache;
|
||||||
|
import net.osmand.plus.track.TrackDisplayHelper;
|
||||||
import net.osmand.util.Algorithms;
|
import net.osmand.util.Algorithms;
|
||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
|
@ -55,7 +58,9 @@ import gnu.trove.list.array.TIntArrayList;
|
||||||
public class SplitSegmentDialogFragment extends DialogFragment {
|
public class SplitSegmentDialogFragment extends DialogFragment {
|
||||||
|
|
||||||
public final static String TAG = "SPLIT_SEGMENT_DIALOG_FRAGMENT";
|
public final static String TAG = "SPLIT_SEGMENT_DIALOG_FRAGMENT";
|
||||||
|
|
||||||
private OsmandApplication app;
|
private OsmandApplication app;
|
||||||
|
private TrackDisplayHelper displayHelper;
|
||||||
|
|
||||||
private SplitSegmentsAdapter adapter;
|
private SplitSegmentsAdapter adapter;
|
||||||
private View headerView;
|
private View headerView;
|
||||||
|
@ -79,8 +84,8 @@ public class SplitSegmentDialogFragment extends DialogFragment {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
TrackActivity trackActivity = requireTrackActivity();
|
FragmentActivity activity = requireActivity();
|
||||||
app = trackActivity.getMyApplication();
|
app = (OsmandApplication) activity.getApplication();
|
||||||
ic = app.getUIUtilities();
|
ic = app.getUIUtilities();
|
||||||
boolean isLightTheme = app.getSettings().isLightContent();
|
boolean isLightTheme = app.getSettings().isLightContent();
|
||||||
int themeId = isLightTheme ? R.style.OsmandLightTheme : R.style.OsmandDarkTheme;
|
int themeId = isLightTheme ? R.style.OsmandLightTheme : R.style.OsmandDarkTheme;
|
||||||
|
@ -90,9 +95,8 @@ public class SplitSegmentDialogFragment extends DialogFragment {
|
||||||
@Override
|
@Override
|
||||||
public void onActivityCreated(Bundle savedInstanceState) {
|
public void onActivityCreated(Bundle savedInstanceState) {
|
||||||
super.onActivityCreated(savedInstanceState);
|
super.onActivityCreated(savedInstanceState);
|
||||||
TrackActivity trackActivity = requireTrackActivity();
|
|
||||||
listView.setBackgroundColor(getResources().getColor(
|
listView.setBackgroundColor(getResources().getColor(
|
||||||
trackActivity.getMyApplication().getSettings().isLightContent() ? R.color.activity_background_color_light
|
app.getSettings().isLightContent() ? R.color.activity_background_color_light
|
||||||
: R.color.activity_background_color_dark));
|
: R.color.activity_background_color_dark));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +108,7 @@ public class SplitSegmentDialogFragment extends DialogFragment {
|
||||||
minMaxSpeedPaint.setStyle(Paint.Style.FILL);
|
minMaxSpeedPaint.setStyle(Paint.Style.FILL);
|
||||||
minMaxSpeedTextBounds = new Rect();
|
minMaxSpeedTextBounds = new Rect();
|
||||||
|
|
||||||
TrackActivity trackActivity = requireTrackActivity();
|
AppCompatActivity trackActivity = (AppCompatActivity) getActivity();
|
||||||
final View view = trackActivity.getLayoutInflater().inflate(R.layout.split_segments_layout, container, false);
|
final View view = trackActivity.getLayoutInflater().inflate(R.layout.split_segments_layout, container, false);
|
||||||
|
|
||||||
Toolbar toolbar = (Toolbar) view.findViewById(R.id.split_interval_toolbar);
|
Toolbar toolbar = (Toolbar) view.findViewById(R.id.split_interval_toolbar);
|
||||||
|
@ -251,7 +255,7 @@ public class SplitSegmentDialogFragment extends DialogFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateContent() {
|
public void updateContent() {
|
||||||
if (getTrackActivity() != null) {
|
if (getActivity() != null) {
|
||||||
adapter.clear();
|
adapter.clear();
|
||||||
adapter.setNotifyOnChange(false);
|
adapter.setNotifyOnChange(false);
|
||||||
adapter.add(gpxItem);
|
adapter.add(gpxItem);
|
||||||
|
@ -298,18 +302,7 @@ public class SplitSegmentDialogFragment extends DialogFragment {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private GPXFile getGpx() {
|
private GPXFile getGpx() {
|
||||||
TrackActivity trackActivity = getTrackActivity();
|
return displayHelper.getGpx();
|
||||||
return trackActivity != null ? trackActivity.getGpx() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public TrackActivity getTrackActivity() {
|
|
||||||
return (TrackActivity) getActivity();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public TrackActivity requireTrackActivity() {
|
|
||||||
return (TrackActivity) requireActivity();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prepareSplitIntervalAdapterData() {
|
private void prepareSplitIntervalAdapterData() {
|
||||||
|
@ -347,7 +340,7 @@ public class SplitSegmentDialogFragment extends DialogFragment {
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private List<GpxDisplayGroup> getDisplayGroups() {
|
private List<GpxDisplayGroup> getDisplayGroups() {
|
||||||
return filterGroups(true);
|
return displayHelper.getDisplayGroups(filterTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addOptionSplit(int value, boolean distance, List<GpxDisplayGroup> model) {
|
private void addOptionSplit(int value, boolean distance, List<GpxDisplayGroup> model) {
|
||||||
|
@ -375,32 +368,13 @@ public class SplitSegmentDialogFragment extends DialogFragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private List<GpxDisplayGroup> filterGroups(boolean useDisplayGroups) {
|
|
||||||
List<GpxDisplayGroup> groups = new ArrayList<>();
|
|
||||||
if (getTrackActivity() != null) {
|
|
||||||
List<GpxDisplayGroup> result = getTrackActivity().getGpxFile(useDisplayGroups);
|
|
||||||
for (GpxDisplayGroup group : result) {
|
|
||||||
boolean add = hasFilterType(group.getType());
|
|
||||||
if (add) {
|
|
||||||
groups.add(group);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return groups;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private List<GpxDisplayItem> getSplitSegments() {
|
private List<GpxDisplayItem> getSplitSegments() {
|
||||||
TrackActivity trackActivity = getTrackActivity();
|
|
||||||
List<GpxDisplayItem> splitSegments = new ArrayList<>();
|
List<GpxDisplayItem> splitSegments = new ArrayList<>();
|
||||||
if (trackActivity != null) {
|
List<GpxDisplayGroup> result = displayHelper.getGpxFile(true);
|
||||||
List<GpxDisplayGroup> result = trackActivity.getGpxFile(true);
|
if (result != null && result.size() > 0 && trkSegment.points.size() > 0) {
|
||||||
if (result != null && result.size() > 0 && trkSegment.points.size() > 0) {
|
for (GpxDisplayGroup group : result) {
|
||||||
for (GpxDisplayGroup group : result) {
|
splitSegments.addAll(collectDisplayItemsFromGroup(group));
|
||||||
splitSegments.addAll(collectDisplayItemsFromGroup(group));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return splitSegments;
|
return splitSegments;
|
||||||
|
@ -434,24 +408,6 @@ public class SplitSegmentDialogFragment extends DialogFragment {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dismiss() {
|
|
||||||
TrackActivity trackActivity = getTrackActivity();
|
|
||||||
if (trackActivity != null) {
|
|
||||||
trackActivity.updateSplitView();
|
|
||||||
}
|
|
||||||
super.dismiss();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCancel(DialogInterface dialog) {
|
|
||||||
TrackActivity trackActivity = getTrackActivity();
|
|
||||||
if (trackActivity != null) {
|
|
||||||
trackActivity.updateSplitView();
|
|
||||||
}
|
|
||||||
super.onCancel(dialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class SplitSegmentsAdapter extends ArrayAdapter<GpxDisplayItem> {
|
private class SplitSegmentsAdapter extends ArrayAdapter<GpxDisplayItem> {
|
||||||
|
|
||||||
SplitSegmentsAdapter(List<GpxDisplayItem> items) {
|
SplitSegmentsAdapter(List<GpxDisplayItem> items) {
|
||||||
|
@ -464,7 +420,7 @@ public class SplitSegmentDialogFragment extends DialogFragment {
|
||||||
@Override
|
@Override
|
||||||
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
|
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
|
||||||
GpxDisplayItem currentGpxDisplayItem = getItem(position);
|
GpxDisplayItem currentGpxDisplayItem = getItem(position);
|
||||||
TrackActivity trackActivity = requireTrackActivity();
|
FragmentActivity trackActivity = requireActivity();
|
||||||
if (convertView == null) {
|
if (convertView == null) {
|
||||||
convertView = trackActivity.getLayoutInflater().inflate(R.layout.gpx_split_segment_fragment, parent, false);
|
convertView = trackActivity.getLayoutInflater().inflate(R.layout.gpx_split_segment_fragment, parent, false);
|
||||||
}
|
}
|
||||||
|
@ -750,14 +706,15 @@ public class SplitSegmentDialogFragment extends DialogFragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean showInstance(@NonNull TrackActivity trackActivity, @NonNull GpxDisplayItem gpxItem, GPXUtilities.TrkSegment trkSegment) {
|
public static boolean showInstance(@NonNull FragmentManager fragmentManager, @NonNull TrackDisplayHelper displayHelper,
|
||||||
|
@NonNull GpxDisplayItem gpxItem, @NonNull TrkSegment trkSegment) {
|
||||||
try {
|
try {
|
||||||
SplitSegmentDialogFragment fragment = new SplitSegmentDialogFragment();
|
SplitSegmentDialogFragment fragment = new SplitSegmentDialogFragment();
|
||||||
fragment.setGpxItem(gpxItem);
|
fragment.setGpxItem(gpxItem);
|
||||||
fragment.setTrkSegment(trkSegment);
|
fragment.setTrkSegment(trkSegment);
|
||||||
fragment.setRetainInstance(true);
|
fragment.setRetainInstance(true);
|
||||||
fragment.setJoinSegments(trackActivity.isJoinSegments());
|
fragment.displayHelper = displayHelper;
|
||||||
fragment.show(trackActivity.getSupportFragmentManager(), TAG);
|
fragment.show(fragmentManager, TAG);
|
||||||
return true;
|
return true;
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -6,7 +6,6 @@ import android.content.Intent;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
@ -14,9 +13,7 @@ import android.view.Gravity;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.AbsListView;
|
import android.widget.AbsListView;
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.AdapterView.OnItemClickListener;
|
import android.widget.AdapterView.OnItemClickListener;
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.ListAdapter;
|
import android.widget.ListAdapter;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
|
@ -39,16 +36,12 @@ import com.squareup.picasso.RequestCreator;
|
||||||
import net.osmand.AndroidUtils;
|
import net.osmand.AndroidUtils;
|
||||||
import net.osmand.GPXUtilities;
|
import net.osmand.GPXUtilities;
|
||||||
import net.osmand.GPXUtilities.GPXFile;
|
import net.osmand.GPXUtilities.GPXFile;
|
||||||
|
import net.osmand.GPXUtilities.Metadata;
|
||||||
import net.osmand.GPXUtilities.WptPt;
|
import net.osmand.GPXUtilities.WptPt;
|
||||||
import net.osmand.PicassoUtils;
|
import net.osmand.PicassoUtils;
|
||||||
import net.osmand.data.PointDescription;
|
import net.osmand.data.PointDescription;
|
||||||
import net.osmand.data.QuadRect;
|
|
||||||
import net.osmand.plus.GPXDatabase.GpxDataItem;
|
import net.osmand.plus.GPXDatabase.GpxDataItem;
|
||||||
import net.osmand.plus.GpxSelectionHelper;
|
|
||||||
import net.osmand.plus.GpxSelectionHelper.GpxDisplayGroup;
|
|
||||||
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItemType;
|
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItemType;
|
||||||
import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
|
|
||||||
import net.osmand.plus.OsmAndFormatter;
|
|
||||||
import net.osmand.plus.OsmandApplication;
|
import net.osmand.plus.OsmandApplication;
|
||||||
import net.osmand.plus.R;
|
import net.osmand.plus.R;
|
||||||
import net.osmand.plus.activities.MapActivity;
|
import net.osmand.plus.activities.MapActivity;
|
||||||
|
@ -57,9 +50,7 @@ import net.osmand.plus.dialogs.GpxAppearanceAdapter;
|
||||||
import net.osmand.plus.myplaces.TrackBitmapDrawer.TrackBitmapDrawerListener;
|
import net.osmand.plus.myplaces.TrackBitmapDrawer.TrackBitmapDrawerListener;
|
||||||
import net.osmand.plus.settings.backend.CommonPreference;
|
import net.osmand.plus.settings.backend.CommonPreference;
|
||||||
import net.osmand.plus.settings.backend.OsmandSettings;
|
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||||
import net.osmand.plus.track.GpxSplitType;
|
import net.osmand.plus.track.TrackDisplayHelper;
|
||||||
import net.osmand.plus.track.SplitTrackAsyncTask;
|
|
||||||
import net.osmand.plus.track.SplitTrackAsyncTask.SplitTrackListener;
|
|
||||||
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;
|
||||||
|
@ -67,12 +58,8 @@ import net.osmand.plus.wikivoyage.article.WikivoyageArticleDialogFragment;
|
||||||
import net.osmand.plus.wikivoyage.data.TravelArticle;
|
import net.osmand.plus.wikivoyage.data.TravelArticle;
|
||||||
import net.osmand.render.RenderingRulesStorage;
|
import net.osmand.render.RenderingRulesStorage;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import gnu.trove.list.array.TIntArrayList;
|
|
||||||
|
|
||||||
import static net.osmand.plus.dialogs.ConfigureMapMenu.CURRENT_TRACK_COLOR_ATTR;
|
import static net.osmand.plus.dialogs.ConfigureMapMenu.CURRENT_TRACK_COLOR_ATTR;
|
||||||
|
|
||||||
public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
|
public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
|
||||||
|
@ -81,11 +68,8 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
|
||||||
private Fragment fragment;
|
private Fragment fragment;
|
||||||
private ListView listView;
|
private ListView listView;
|
||||||
private GpxDisplayItemType[] filterTypes;
|
private GpxDisplayItemType[] filterTypes;
|
||||||
|
private TrackDisplayHelper displayHelper;
|
||||||
|
|
||||||
private List<String> options = new ArrayList<>();
|
|
||||||
private List<Double> distanceSplit = new ArrayList<>();
|
|
||||||
private TIntArrayList timeSplit = new TIntArrayList();
|
|
||||||
private int selectedSplitInterval;
|
|
||||||
private boolean updateEnable;
|
private boolean updateEnable;
|
||||||
private View headerView;
|
private View headerView;
|
||||||
private SwitchCompat vis;
|
private SwitchCompat vis;
|
||||||
|
@ -108,17 +92,18 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
|
||||||
private View lineTextLayout;
|
private View lineTextLayout;
|
||||||
private View overlayView;
|
private View overlayView;
|
||||||
|
|
||||||
ListPopupWindow splitListPopupWindow;
|
|
||||||
ListPopupWindow colorListPopupWindow;
|
ListPopupWindow colorListPopupWindow;
|
||||||
|
|
||||||
TrackActivityFragmentAdapter(@NonNull OsmandApplication app,
|
TrackActivityFragmentAdapter(@NonNull OsmandApplication app,
|
||||||
@NonNull Fragment fragment,
|
@NonNull Fragment fragment,
|
||||||
@NonNull ListView listView,
|
@NonNull ListView listView,
|
||||||
|
@NonNull TrackDisplayHelper displayHelper,
|
||||||
@NonNull GpxDisplayItemType... filterTypes) {
|
@NonNull GpxDisplayItemType... filterTypes) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
this.fragment = fragment;
|
this.fragment = fragment;
|
||||||
this.listView = listView;
|
this.listView = listView;
|
||||||
this.filterTypes = filterTypes;
|
this.filterTypes = filterTypes;
|
||||||
|
this.displayHelper = displayHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onActivityCreated(Bundle savedInstanceState) {
|
public void onActivityCreated(Bundle savedInstanceState) {
|
||||||
|
@ -206,14 +191,12 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
private GPXFile getGpx() {
|
private GPXFile getGpx() {
|
||||||
TrackActivity activity = getTrackActivity();
|
return displayHelper.getGpx();
|
||||||
return activity != null ? activity.getGpx() : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private GpxDataItem getGpxDataItem() {
|
private GpxDataItem getGpxDataItem() {
|
||||||
TrackActivity activity = getTrackActivity();
|
return displayHelper.getGpxDataItem();
|
||||||
return activity != null ? activity.getGpxDataItem() : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showTrackBitmapProgress() {
|
private void showTrackBitmapProgress() {
|
||||||
|
@ -265,17 +248,15 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
|
||||||
imageView.setOnClickListener(new View.OnClickListener() {
|
imageView.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
showTemporaryObjectOnMap(getRect());
|
showTemporaryObjectOnMap(displayHelper.getRect());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
final View splitColorView = headerView.findViewById(R.id.split_color_view);
|
|
||||||
final View appearanceView = headerView.findViewById(R.id.appearance_view);
|
final View appearanceView = headerView.findViewById(R.id.appearance_view);
|
||||||
final View divider = headerView.findViewById(R.id.divider);
|
final View divider = headerView.findViewById(R.id.divider);
|
||||||
final View splitIntervalView = headerView.findViewById(R.id.split_interval_view);
|
|
||||||
vis = (SwitchCompat) headerView.findViewById(R.id.showOnMapToggle);
|
vis = (SwitchCompat) headerView.findViewById(R.id.showOnMapToggle);
|
||||||
final View bottomDivider = headerView.findViewById(R.id.bottom_divider);
|
final View bottomDivider = headerView.findViewById(R.id.bottom_divider);
|
||||||
GPXFile gpxFile = getGpx();
|
GPXFile gpxFile = getGpx();
|
||||||
boolean gpxFileSelected = isGpxFileSelected(gpxFile);
|
boolean gpxFileSelected = isGpxFileSelected(app, gpxFile);
|
||||||
|
|
||||||
boolean hasPath = gpxFile != null && (gpxFile.tracks.size() > 0 || gpxFile.routes.size() > 0);
|
boolean hasPath = gpxFile != null && (gpxFile.tracks.size() > 0 || gpxFile.routes.size() > 0);
|
||||||
TrackActivity activity = getTrackActivity();
|
TrackActivity activity = getTrackActivity();
|
||||||
|
@ -297,13 +278,7 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
vis.toggle();
|
vis.toggle();
|
||||||
if (!vis.isChecked()) {
|
|
||||||
selectedSplitInterval = 0;
|
|
||||||
}
|
|
||||||
setTrackVisibilityOnMap(vis.isChecked());
|
setTrackVisibilityOnMap(vis.isChecked());
|
||||||
if (!showMapOnly) {
|
|
||||||
updateSplitIntervalView(splitIntervalView);
|
|
||||||
}
|
|
||||||
TrackActivity trackActivity = getTrackActivity();
|
TrackActivity trackActivity = getTrackActivity();
|
||||||
if (trackActivity != null) {
|
if (trackActivity != null) {
|
||||||
trackActivity.updateHeader(fragment);
|
trackActivity.updateHeader(fragment);
|
||||||
|
@ -312,45 +287,12 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
splitColorView.setVisibility(View.GONE);
|
|
||||||
if (showMapOnly) {
|
if (showMapOnly) {
|
||||||
splitIntervalView.setVisibility(View.GONE);
|
|
||||||
appearanceView.setVisibility(View.GONE);
|
appearanceView.setVisibility(View.GONE);
|
||||||
divider.setVisibility(View.GONE);
|
divider.setVisibility(View.GONE);
|
||||||
bottomDivider.setVisibility(View.VISIBLE);
|
bottomDivider.setVisibility(View.VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
bottomDivider.setVisibility(View.GONE);
|
|
||||||
|
|
||||||
if (hasPath) {
|
if (hasPath) {
|
||||||
if (!gpxFile.showCurrentTrack && listItemsCount > 0) {
|
|
||||||
prepareSplitIntervalAdapterData();
|
|
||||||
setupSplitIntervalView(splitIntervalView);
|
|
||||||
updateSplitIntervalView(splitIntervalView);
|
|
||||||
splitIntervalView.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
TrackActivity activity = getTrackActivity();
|
|
||||||
if (activity != null) {
|
|
||||||
ListAdapter adapter = new ArrayAdapter<>(activity, R.layout.popup_list_text_item, options);
|
|
||||||
OnItemClickListener itemClickListener = new OnItemClickListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
|
||||||
selectedSplitInterval = position;
|
|
||||||
setTrackVisibilityOnMap(vis.isChecked());
|
|
||||||
splitListPopupWindow.dismiss();
|
|
||||||
updateSplitIntervalView(splitIntervalView);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
splitListPopupWindow = createPopupWindow(activity, splitIntervalView, adapter, itemClickListener);
|
|
||||||
splitListPopupWindow.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
splitIntervalView.setVisibility(View.VISIBLE);
|
|
||||||
} else {
|
|
||||||
splitIntervalView.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
appearanceView.setOnClickListener(new View.OnClickListener() {
|
appearanceView.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
|
@ -363,6 +305,7 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
|
||||||
appearanceView.setVisibility(View.GONE);
|
appearanceView.setVisibility(View.GONE);
|
||||||
divider.setVisibility(View.GONE);
|
divider.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
bottomDivider.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
updateTrackColor();
|
updateTrackColor();
|
||||||
}
|
}
|
||||||
|
@ -373,7 +316,7 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
|
||||||
GPXFile gpx = getGpx();
|
GPXFile gpx = getGpx();
|
||||||
WptPt pointToShow = gpx != null ? gpx.findPointToShow() : null;
|
WptPt pointToShow = gpx != null ? gpx.findPointToShow() : null;
|
||||||
if (activity != null && pointToShow != null) {
|
if (activity != null && pointToShow != null) {
|
||||||
boolean gpxFileSelected = isGpxFileSelected(gpx);
|
boolean gpxFileSelected = isGpxFileSelected(app, gpx);
|
||||||
if (!gpxFileSelected) {
|
if (!gpxFileSelected) {
|
||||||
Intent intent = activity.getIntent();
|
Intent intent = activity.getIntent();
|
||||||
if (intent != null) {
|
if (intent != null) {
|
||||||
|
@ -422,7 +365,7 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
TravelArticle article = getTravelArticle(gpx.metadata);
|
TravelArticle article = getTravelArticle(gpx);
|
||||||
if (article != null) {
|
if (article != null) {
|
||||||
return createTravelArticleCard(context, article);
|
return createTravelArticleCard(context, article);
|
||||||
}
|
}
|
||||||
|
@ -448,7 +391,7 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private String getMetadataDescription(@NonNull GPXUtilities.Metadata metadata) {
|
private String getMetadataDescription(@NonNull Metadata metadata) {
|
||||||
String descHtml = metadata.desc;
|
String descHtml = metadata.desc;
|
||||||
if (TextUtils.isEmpty(descHtml)) {
|
if (TextUtils.isEmpty(descHtml)) {
|
||||||
Map<String, String> extensions = metadata.getExtensionsToRead();
|
Map<String, String> extensions = metadata.getExtensionsToRead();
|
||||||
|
@ -466,7 +409,7 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private String getMetadataImageLink(@NonNull GPXUtilities.Metadata metadata) {
|
private String getMetadataImageLink(@NonNull Metadata metadata) {
|
||||||
String link = metadata.link;
|
String link = metadata.link;
|
||||||
if (!TextUtils.isEmpty(link)) {
|
if (!TextUtils.isEmpty(link)) {
|
||||||
String lowerCaseLink = link.toLowerCase();
|
String lowerCaseLink = link.toLowerCase();
|
||||||
|
@ -482,11 +425,12 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private TravelArticle getTravelArticle(@NonNull GPXUtilities.Metadata metadata) {
|
private TravelArticle getTravelArticle(@NonNull GPXFile gpx) {
|
||||||
|
Metadata metadata = gpx.metadata;
|
||||||
String title = metadata.getArticleTitle();
|
String title = metadata.getArticleTitle();
|
||||||
String lang = metadata.getArticleLang();
|
String lang = metadata.getArticleLang();
|
||||||
if (!TextUtils.isEmpty(title) && !TextUtils.isEmpty(lang)) {
|
if (!TextUtils.isEmpty(title) && !TextUtils.isEmpty(lang)) {
|
||||||
return app.getTravelHelper().getArticleByTitle(title, lang);
|
return app.getTravelHelper().getArticleByTitle(title, gpx.getRect(), lang);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -506,8 +450,8 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
TrackActivity activity = getTrackActivity();
|
TrackActivity activity = getTrackActivity();
|
||||||
if (activity != null) {
|
if (activity != null) {
|
||||||
WikivoyageArticleDialogFragment.showInstance(app,
|
WikivoyageArticleDialogFragment.showInstance(app, activity.getSupportFragmentManager(),
|
||||||
activity.getSupportFragmentManager(), article.getRouteId(), article.getLang());
|
article.generateIdentifier(), article.getLang());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -585,7 +529,7 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isGpxFileSelected(GPXFile gpxFile) {
|
public static boolean isGpxFileSelected(OsmandApplication app, GPXFile gpxFile) {
|
||||||
return gpxFile != null &&
|
return gpxFile != null &&
|
||||||
((gpxFile.showCurrentTrack && app.getSelectedGpxHelper().getSelectedCurrentRecordingTrack() != null) ||
|
((gpxFile.showCurrentTrack && app.getSelectedGpxHelper().getSelectedCurrentRecordingTrack() != null) ||
|
||||||
(gpxFile.path != null && app.getSelectedGpxHelper().getSelectedFileByPath(gpxFile.path) != null));
|
(gpxFile.path != null && app.getSelectedGpxHelper().getSelectedFileByPath(gpxFile.path) != null));
|
||||||
|
@ -594,26 +538,10 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
|
||||||
private void setTrackVisibilityOnMap(boolean visible) {
|
private void setTrackVisibilityOnMap(boolean visible) {
|
||||||
GPXFile gpxFile = getGpx();
|
GPXFile gpxFile = getGpx();
|
||||||
if (gpxFile != null) {
|
if (gpxFile != null) {
|
||||||
GpxSelectionHelper gpxHelper = app.getSelectedGpxHelper();
|
app.getSelectedGpxHelper().selectGpxFile(gpxFile, visible, false);
|
||||||
SelectedGpxFile sf = gpxHelper.selectGpxFile(gpxFile, visible, false);
|
|
||||||
if (gpxFile.hasTrkPt()) {
|
|
||||||
List<GpxDisplayGroup> groups = getDisplayGroups();
|
|
||||||
if (groups.size() > 0) {
|
|
||||||
updateSplit(groups, visible ? sf : null);
|
|
||||||
if (getGpxDataItem() != null) {
|
|
||||||
updateSplitInDatabase();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private QuadRect getRect() {
|
|
||||||
TrackActivity activity = getTrackActivity();
|
|
||||||
return activity != null ? activity.getRect() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isShowOnMap() {
|
public boolean isShowOnMap() {
|
||||||
return vis != null && vis.isChecked();
|
return vis != null && vis.isChecked();
|
||||||
}
|
}
|
||||||
|
@ -629,88 +557,6 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
|
||||||
menuFab.setVisibility(visible ? View.VISIBLE : View.GONE);
|
menuFab.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public List<GpxDisplayGroup> getOriginalGroups() {
|
|
||||||
return filterGroups(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public List<GpxDisplayGroup> getDisplayGroups() {
|
|
||||||
return filterGroups(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasFilterType(GpxDisplayItemType filterType) {
|
|
||||||
for (GpxDisplayItemType type : filterTypes) {
|
|
||||||
if (type == filterType) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private List<GpxDisplayGroup> filterGroups(boolean useDisplayGroups) {
|
|
||||||
List<GpxDisplayGroup> groups = new ArrayList<>();
|
|
||||||
TrackActivity activity = getTrackActivity();
|
|
||||||
if (activity != null) {
|
|
||||||
List<GpxDisplayGroup> result = activity.getGpxFile(useDisplayGroups);
|
|
||||||
for (GpxDisplayGroup group : result) {
|
|
||||||
boolean add = hasFilterType(group.getType());
|
|
||||||
if (add) {
|
|
||||||
groups.add(group);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return groups;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupSplitIntervalView(View view) {
|
|
||||||
final TextView title = (TextView) view.findViewById(R.id.split_interval_title);
|
|
||||||
final TextView text = (TextView) view.findViewById(R.id.split_interval_text);
|
|
||||||
final ImageView img = (ImageView) view.findViewById(R.id.split_interval_arrow);
|
|
||||||
int colorId;
|
|
||||||
final List<GpxDisplayGroup> groups = getDisplayGroups();
|
|
||||||
if (groups.size() > 0) {
|
|
||||||
colorId = app.getSettings().isLightContent() ?
|
|
||||||
R.color.text_color_primary_light : R.color.text_color_primary_dark;
|
|
||||||
} else {
|
|
||||||
colorId = app.getSettings().isLightContent() ?
|
|
||||||
R.color.text_color_secondary_light : R.color.text_color_secondary_dark;
|
|
||||||
}
|
|
||||||
int color = app.getResources().getColor(colorId);
|
|
||||||
title.setTextColor(color);
|
|
||||||
text.setTextColor(color);
|
|
||||||
img.setImageDrawable(app.getUIUtilities().getIcon(R.drawable.ic_action_arrow_drop_down, colorId));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateSplitIntervalView(View view) {
|
|
||||||
final TextView text = (TextView) view.findViewById(R.id.split_interval_text);
|
|
||||||
selectedSplitInterval = getSelectedSplitInterval();
|
|
||||||
if (selectedSplitInterval == 0) {
|
|
||||||
text.setText(app.getString(R.string.shared_string_none));
|
|
||||||
} else {
|
|
||||||
text.setText(options.get(selectedSplitInterval));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getSelectedSplitInterval() {
|
|
||||||
if (getGpxDataItem() == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int splitType = getGpxDataItem().getSplitType();
|
|
||||||
double splitInterval = getGpxDataItem().getSplitInterval();
|
|
||||||
int position = 0;
|
|
||||||
|
|
||||||
if (splitType == GpxSplitType.DISTANCE.getType()) {
|
|
||||||
position = distanceSplit.indexOf(splitInterval);
|
|
||||||
} else if (splitType == GpxSplitType.TIME.getType()) {
|
|
||||||
position = timeSplit.indexOf((int) splitInterval);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Math.max(position, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateTrackColor() {
|
private void updateTrackColor() {
|
||||||
int color = getGpxDataItem() != null ? getGpxDataItem().getColor() : 0;
|
int color = getGpxDataItem() != null ? getGpxDataItem().getColor() : 0;
|
||||||
GPXFile gpxFile = getGpx();
|
GPXFile gpxFile = getGpx();
|
||||||
|
@ -732,154 +578,6 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<GpxSelectionHelper.GpxDisplayItem> flatten(List<GpxDisplayGroup> groups) {
|
|
||||||
ArrayList<GpxSelectionHelper.GpxDisplayItem> list = new ArrayList<>();
|
|
||||||
for (GpxDisplayGroup g : groups) {
|
|
||||||
list.addAll(g.getModifiableList());
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void prepareSplitIntervalAdapterData() {
|
|
||||||
final List<GpxDisplayGroup> groups = getDisplayGroups();
|
|
||||||
|
|
||||||
options.add(app.getString(R.string.shared_string_none));
|
|
||||||
distanceSplit.add(-1d);
|
|
||||||
timeSplit.add(-1);
|
|
||||||
addOptionSplit(30, true, groups); // 50 feet, 20 yards, 20
|
|
||||||
// m
|
|
||||||
addOptionSplit(60, true, groups); // 100 feet, 50 yards,
|
|
||||||
// 50 m
|
|
||||||
addOptionSplit(150, true, groups); // 200 feet, 100 yards,
|
|
||||||
// 100 m
|
|
||||||
addOptionSplit(300, true, groups); // 500 feet, 200 yards,
|
|
||||||
// 200 m
|
|
||||||
addOptionSplit(600, true, groups); // 1000 feet, 500 yards,
|
|
||||||
// 500 m
|
|
||||||
addOptionSplit(1500, true, groups); // 2000 feet, 1000 yards, 1 km
|
|
||||||
addOptionSplit(3000, true, groups); // 1 mi, 2 km
|
|
||||||
addOptionSplit(6000, true, groups); // 2 mi, 5 km
|
|
||||||
addOptionSplit(15000, true, groups); // 5 mi, 10 km
|
|
||||||
|
|
||||||
addOptionSplit(15, false, groups);
|
|
||||||
addOptionSplit(30, false, groups);
|
|
||||||
addOptionSplit(60, false, groups);
|
|
||||||
addOptionSplit(120, false, groups);
|
|
||||||
addOptionSplit(150, false, groups);
|
|
||||||
addOptionSplit(300, false, groups);
|
|
||||||
addOptionSplit(600, false, groups);
|
|
||||||
addOptionSplit(900, false, groups);
|
|
||||||
addOptionSplit(1800, false, groups);
|
|
||||||
addOptionSplit(3600, false, groups);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateSplit(@NonNull List<GpxDisplayGroup> groups, @Nullable final SelectedGpxFile selectedGpx) {
|
|
||||||
GPXFile gpxFile = getGpx();
|
|
||||||
TrackActivity activity = getTrackActivity();
|
|
||||||
GpxSplitType gpxSplitType = getGpxSplitType();
|
|
||||||
if (activity != null && gpxSplitType != null && gpxFile != null) {
|
|
||||||
int timeSplit = 0;
|
|
||||||
double distanceSplit = 0;
|
|
||||||
if (gpxSplitType != GpxSplitType.NO_SPLIT && !gpxFile.showCurrentTrack) {
|
|
||||||
timeSplit = this.timeSplit.get(selectedSplitInterval);
|
|
||||||
distanceSplit = this.distanceSplit.get(selectedSplitInterval);
|
|
||||||
}
|
|
||||||
SplitTrackListener splitTrackListener = new SplitTrackListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void trackSplittingStarted() {
|
|
||||||
TrackActivity activity = getTrackActivity();
|
|
||||||
if (activity != null) {
|
|
||||||
activity.setSupportProgressBarIndeterminateVisibility(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void trackSplittingFinished() {
|
|
||||||
TrackActivity activity = getTrackActivity();
|
|
||||||
if (activity != null) {
|
|
||||||
if (AndroidUtils.isActivityNotDestroyed(activity)) {
|
|
||||||
activity.setSupportProgressBarIndeterminateVisibility(false);
|
|
||||||
}
|
|
||||||
if (selectedGpx != null) {
|
|
||||||
List<GpxDisplayGroup> groups = getDisplayGroups();
|
|
||||||
selectedGpx.setDisplayGroups(groups, app);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
new SplitTrackAsyncTask(app, gpxSplitType, groups, splitTrackListener, activity.isJoinSegments(),
|
|
||||||
timeSplit, distanceSplit).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private GpxSplitType getGpxSplitType() {
|
|
||||||
if (selectedSplitInterval == 0) {
|
|
||||||
return GpxSplitType.NO_SPLIT;
|
|
||||||
} else if (distanceSplit.get(selectedSplitInterval) > 0) {
|
|
||||||
return GpxSplitType.DISTANCE;
|
|
||||||
} else if (timeSplit.get(selectedSplitInterval) > 0) {
|
|
||||||
return GpxSplitType.TIME;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addOptionSplit(int value, boolean distance, @NonNull List<GpxDisplayGroup> model) {
|
|
||||||
if (model.size() > 0) {
|
|
||||||
if (distance) {
|
|
||||||
double dvalue = OsmAndFormatter.calculateRoundedDist(value, app);
|
|
||||||
options.add(OsmAndFormatter.getFormattedDistanceInterval(app, value));
|
|
||||||
distanceSplit.add(dvalue);
|
|
||||||
timeSplit.add(-1);
|
|
||||||
if (Math.abs(model.get(0).getSplitDistance() - dvalue) < 1) {
|
|
||||||
selectedSplitInterval = distanceSplit.size() - 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
options.add(OsmAndFormatter.getFormattedTimeInterval(app, value));
|
|
||||||
distanceSplit.add(-1d);
|
|
||||||
timeSplit.add(value);
|
|
||||||
if (model.get(0).getSplitTime() == value) {
|
|
||||||
selectedSplitInterval = distanceSplit.size() - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateSplitInDatabase() {
|
|
||||||
double splitInterval = 0;
|
|
||||||
GpxSplitType splitType = null;
|
|
||||||
if (selectedSplitInterval == 0) {
|
|
||||||
splitType = GpxSplitType.NO_SPLIT;
|
|
||||||
splitInterval = 0;
|
|
||||||
} else if (distanceSplit.get(selectedSplitInterval) > 0) {
|
|
||||||
splitType = GpxSplitType.DISTANCE;
|
|
||||||
splitInterval = distanceSplit.get(selectedSplitInterval);
|
|
||||||
} else if (timeSplit.get(selectedSplitInterval) > 0) {
|
|
||||||
splitType = GpxSplitType.TIME;
|
|
||||||
splitInterval = timeSplit.get(selectedSplitInterval);
|
|
||||||
}
|
|
||||||
GpxDataItem item = getGpxDataItem();
|
|
||||||
if (item != null && splitType != null) {
|
|
||||||
app.getGpxDbHelper().updateSplit(item, splitType, splitInterval);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateSplitView() {
|
|
||||||
GPXFile gpxFile = getGpx();
|
|
||||||
if (gpxFile != null) {
|
|
||||||
SelectedGpxFile sf = app.getSelectedGpxHelper().selectGpxFile(gpxFile,
|
|
||||||
((SwitchCompat) headerView.findViewById(R.id.showOnMapToggle)).isChecked(), false);
|
|
||||||
final List<GpxDisplayGroup> groups = getDisplayGroups();
|
|
||||||
if (groups.size() > 0) {
|
|
||||||
updateSplit(groups, ((SwitchCompat) headerView.findViewById(R.id.showOnMapToggle)).isChecked() ? sf : null);
|
|
||||||
if (getGpxDataItem() != null) {
|
|
||||||
updateSplitInDatabase();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateSplitIntervalView(headerView.findViewById(R.id.split_interval_view));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void hideTransparentOverlay() {
|
public void hideTransparentOverlay() {
|
||||||
overlayView.setVisibility(View.GONE);
|
overlayView.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,6 @@ import com.google.android.material.snackbar.Snackbar;
|
||||||
|
|
||||||
import net.osmand.AndroidUtils;
|
import net.osmand.AndroidUtils;
|
||||||
import net.osmand.Collator;
|
import net.osmand.Collator;
|
||||||
import net.osmand.GPXUtilities;
|
|
||||||
import net.osmand.GPXUtilities.GPXFile;
|
import net.osmand.GPXUtilities.GPXFile;
|
||||||
import net.osmand.GPXUtilities.WptPt;
|
import net.osmand.GPXUtilities.WptPt;
|
||||||
import net.osmand.OsmAndCollator;
|
import net.osmand.OsmAndCollator;
|
||||||
|
@ -59,7 +58,6 @@ import net.osmand.plus.UiUtilities;
|
||||||
import net.osmand.plus.activities.MapActivity;
|
import net.osmand.plus.activities.MapActivity;
|
||||||
import net.osmand.plus.activities.OsmandActionBarActivity;
|
import net.osmand.plus.activities.OsmandActionBarActivity;
|
||||||
import net.osmand.plus.activities.OsmandBaseExpandableListAdapter;
|
import net.osmand.plus.activities.OsmandBaseExpandableListAdapter;
|
||||||
import net.osmand.plus.activities.SavingTrackHelper;
|
|
||||||
import net.osmand.plus.activities.TrackActivity;
|
import net.osmand.plus.activities.TrackActivity;
|
||||||
import net.osmand.plus.base.OsmandExpandableListFragment;
|
import net.osmand.plus.base.OsmandExpandableListFragment;
|
||||||
import net.osmand.plus.base.PointImageDrawable;
|
import net.osmand.plus.base.PointImageDrawable;
|
||||||
|
@ -67,14 +65,16 @@ import net.osmand.plus.helpers.GpxUiHelper;
|
||||||
import net.osmand.plus.mapmarkers.CoordinateInputDialogFragment;
|
import net.osmand.plus.mapmarkers.CoordinateInputDialogFragment;
|
||||||
import net.osmand.plus.mapmarkers.MapMarkersGroup;
|
import net.osmand.plus.mapmarkers.MapMarkersGroup;
|
||||||
import net.osmand.plus.mapmarkers.MapMarkersHelper;
|
import net.osmand.plus.mapmarkers.MapMarkersHelper;
|
||||||
|
import net.osmand.plus.myplaces.DeletePointsTask.OnPointsDeleteListener;
|
||||||
import net.osmand.plus.myplaces.TrackBitmapDrawer.TrackBitmapDrawerListener;
|
import net.osmand.plus.myplaces.TrackBitmapDrawer.TrackBitmapDrawerListener;
|
||||||
import net.osmand.plus.settings.backend.OsmandSettings;
|
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||||
import net.osmand.plus.track.SaveGpxAsyncTask.SaveGpxListener;
|
import net.osmand.plus.track.SaveGpxAsyncTask.SaveGpxListener;
|
||||||
|
import net.osmand.plus.track.TrackDisplayHelper;
|
||||||
|
import net.osmand.plus.track.TrackMenuFragment;
|
||||||
import net.osmand.plus.widgets.TextViewEx;
|
import net.osmand.plus.widgets.TextViewEx;
|
||||||
import net.osmand.util.Algorithms;
|
import net.osmand.util.Algorithms;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
@ -88,7 +88,7 @@ import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
||||||
public class TrackPointFragment extends OsmandExpandableListFragment implements TrackBitmapDrawerListener {
|
public class TrackPointFragment extends OsmandExpandableListFragment implements TrackBitmapDrawerListener, OnPointsDeleteListener {
|
||||||
|
|
||||||
public static final int SEARCH_ID = -1;
|
public static final int SEARCH_ID = -1;
|
||||||
public static final int DELETE_ID = 2;
|
public static final int DELETE_ID = 2;
|
||||||
|
@ -105,6 +105,8 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
|
||||||
private OsmandApplication app;
|
private OsmandApplication app;
|
||||||
private TrackActivityFragmentAdapter fragmentAdapter;
|
private TrackActivityFragmentAdapter fragmentAdapter;
|
||||||
final private PointGPXAdapter adapter = new PointGPXAdapter();
|
final private PointGPXAdapter adapter = new PointGPXAdapter();
|
||||||
|
private GpxDisplayItemType[] filterTypes = new GpxDisplayItemType[] {GpxDisplayItemType.TRACK_POINTS, GpxDisplayItemType.TRACK_ROUTE_POINTS};
|
||||||
|
private TrackDisplayHelper displayHelper;
|
||||||
|
|
||||||
private boolean selectionMode;
|
private boolean selectionMode;
|
||||||
private LinkedHashMap<GpxDisplayItemType, Set<GpxDisplayItem>> selectedItems = new LinkedHashMap<>();
|
private LinkedHashMap<GpxDisplayItemType, Set<GpxDisplayItem>> selectedItems = new LinkedHashMap<>();
|
||||||
|
@ -120,6 +122,13 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
this.app = getMyApplication();
|
this.app = getMyApplication();
|
||||||
|
|
||||||
|
FragmentActivity activity = getActivity();
|
||||||
|
if (activity instanceof TrackActivity) {
|
||||||
|
displayHelper = ((TrackActivity) activity).getDisplayHelper();
|
||||||
|
} else if (getTargetFragment() instanceof TrackMenuFragment) {
|
||||||
|
displayHelper = ((TrackMenuFragment) getTargetFragment()).getDisplayHelper();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -135,8 +144,7 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
|
||||||
mainView = inflater.inflate(R.layout.track_points_tree, container, false);
|
mainView = inflater.inflate(R.layout.track_points_tree, container, false);
|
||||||
ExpandableListView listView = (ExpandableListView) mainView.findViewById(android.R.id.list);
|
ExpandableListView listView = (ExpandableListView) mainView.findViewById(android.R.id.list);
|
||||||
|
|
||||||
fragmentAdapter = new TrackActivityFragmentAdapter(app, this, listView,
|
fragmentAdapter = new TrackActivityFragmentAdapter(app, this, listView, displayHelper, filterTypes);
|
||||||
GpxDisplayItemType.TRACK_POINTS, GpxDisplayItemType.TRACK_ROUTE_POINTS);
|
|
||||||
fragmentAdapter.setShowMapOnly(true);
|
fragmentAdapter.setShowMapOnly(true);
|
||||||
fragmentAdapter.setTrackBitmapSelectionSupported(false);
|
fragmentAdapter.setTrackBitmapSelectionSupported(false);
|
||||||
fragmentAdapter.setShowDescriptionCard(true);
|
fragmentAdapter.setShowDescriptionCard(true);
|
||||||
|
@ -165,13 +173,8 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
|
||||||
if (optionsMenu != null) {
|
if (optionsMenu != null) {
|
||||||
optionsMenu.close();
|
optionsMenu.close();
|
||||||
}
|
}
|
||||||
if (fragmentAdapter != null) {
|
if (fragmentAdapter != null && fragmentAdapter.colorListPopupWindow != null) {
|
||||||
if (fragmentAdapter.splitListPopupWindow != null) {
|
fragmentAdapter.colorListPopupWindow.dismiss();
|
||||||
fragmentAdapter.splitListPopupWindow.dismiss();
|
|
||||||
}
|
|
||||||
if (fragmentAdapter.colorListPopupWindow != null) {
|
|
||||||
fragmentAdapter.colorListPopupWindow.dismiss();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,7 +195,6 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private int getSelectedItemsCount() {
|
private int getSelectedItemsCount() {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (Set<GpxDisplayItem> set : selectedItems.values()) {
|
for (Set<GpxDisplayItem> set : selectedItems.values()) {
|
||||||
|
@ -213,21 +215,14 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public TrackActivity getTrackActivity() {
|
|
||||||
return (TrackActivity) getActivity();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private GPXFile getGpx() {
|
private GPXFile getGpx() {
|
||||||
TrackActivity activity = getTrackActivity();
|
return displayHelper.getGpx();
|
||||||
return activity != null ? activity.getGpx() : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private GpxDataItem getGpxDataItem() {
|
private GpxDataItem getGpxDataItem() {
|
||||||
TrackActivity activity = getTrackActivity();
|
return displayHelper.getGpxDataItem();
|
||||||
return activity != null ? activity.getGpxDataItem() : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void expandAllGroups() {
|
private void expandAllGroups() {
|
||||||
|
@ -250,7 +245,7 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private List<GpxDisplayGroup> getOriginalGroups() {
|
private List<GpxDisplayGroup> getOriginalGroups() {
|
||||||
return fragmentAdapter != null ? fragmentAdapter.getOriginalGroups() : null;
|
return displayHelper.getOriginalGroups(filterTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setContent() {
|
public void setContent() {
|
||||||
|
@ -495,13 +490,13 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deleteItems() {
|
private void deleteItems() {
|
||||||
new DeletePointsTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
new DeletePointsTask(app, getGpx(), getSelectedItems(), this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addOrRemoveMapMarkersSyncGroup() {
|
private void addOrRemoveMapMarkersSyncGroup() {
|
||||||
final MapMarkersHelper markersHelper = app.getMapMarkersHelper();
|
final MapMarkersHelper markersHelper = app.getMapMarkersHelper();
|
||||||
|
|
||||||
TrackActivity activity = getTrackActivity();
|
FragmentActivity activity = getActivity();
|
||||||
if (activity == null) {
|
if (activity == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -536,7 +531,7 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
|
||||||
.setAction(R.string.shared_string_undo, new View.OnClickListener() {
|
.setAction(R.string.shared_string_undo, new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
TrackActivity trackActivity = getTrackActivity();
|
FragmentActivity trackActivity = getActivity();
|
||||||
if (trackActivity != null) {
|
if (trackActivity != null) {
|
||||||
if (markersRemoved) {
|
if (markersRemoved) {
|
||||||
if (gpxFile != null) {
|
if (gpxFile != null) {
|
||||||
|
@ -606,7 +601,7 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selectFavoritesImpl() {
|
private void selectFavoritesImpl() {
|
||||||
TrackActivity activity = getTrackActivity();
|
FragmentActivity activity = getActivity();
|
||||||
if (activity != null && getSelectedItemsCount() > 0) {
|
if (activity != null && getSelectedItemsCount() > 0) {
|
||||||
AlertDialog.Builder b = new AlertDialog.Builder(activity);
|
AlertDialog.Builder b = new AlertDialog.Builder(activity);
|
||||||
final EditText editText = new EditText(activity);
|
final EditText editText = new EditText(activity);
|
||||||
|
@ -684,14 +679,11 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
|
||||||
} else {
|
} else {
|
||||||
GPXFile gpx = item.group.getGpx();
|
GPXFile gpx = item.group.getGpx();
|
||||||
if (gpx != null) {
|
if (gpx != null) {
|
||||||
TrackActivity trackActivity = getTrackActivity();
|
FragmentActivity activity = getActivity();
|
||||||
if (trackActivity != null && fragmentAdapter != null) {
|
if (activity != null && !TrackActivityFragmentAdapter.isGpxFileSelected(app, gpx)) {
|
||||||
boolean gpxFileSelected = fragmentAdapter.isGpxFileSelected(gpx);
|
Intent intent = activity.getIntent();
|
||||||
if (!gpxFileSelected) {
|
if (intent != null) {
|
||||||
Intent intent = trackActivity.getIntent();
|
intent.putExtra(TrackActivity.SHOW_TEMPORARILY, true);
|
||||||
if (intent != null) {
|
|
||||||
intent.putExtra(TrackActivity.SHOW_TEMPORARILY, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
app.getSelectedGpxHelper().setGpxFileToDisplay(gpx);
|
app.getSelectedGpxHelper().setGpxFileToDisplay(gpx);
|
||||||
|
@ -741,6 +733,23 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPointsDeletionStarted() {
|
||||||
|
showProgressBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPointsDeleted() {
|
||||||
|
selectedItems.clear();
|
||||||
|
selectedGroups.clear();
|
||||||
|
|
||||||
|
hideProgressBar();
|
||||||
|
List<GpxDisplayGroup> groups = getOriginalGroups();
|
||||||
|
if (groups != null) {
|
||||||
|
adapter.synchronizeGroups(groups);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class PointGPXAdapter extends OsmandBaseExpandableListAdapter implements Filterable {
|
class PointGPXAdapter extends OsmandBaseExpandableListAdapter implements Filterable {
|
||||||
|
|
||||||
Map<GpxDisplayGroup, List<GpxDisplayItem>> itemGroups = new LinkedHashMap<>();
|
Map<GpxDisplayGroup, List<GpxDisplayItem>> itemGroups = new LinkedHashMap<>();
|
||||||
|
@ -1199,9 +1208,6 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
|
||||||
|
|
||||||
public class PointsFilter extends Filter {
|
public class PointsFilter extends Filter {
|
||||||
|
|
||||||
PointsFilter() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected FilterResults performFiltering(CharSequence constraint) {
|
protected FilterResults performFiltering(CharSequence constraint) {
|
||||||
FilterResults results = new FilterResults();
|
FilterResults results = new FilterResults();
|
||||||
|
@ -1243,77 +1249,4 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
|
||||||
expandAllGroups();
|
expandAllGroups();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class DeletePointsTask extends AsyncTask<Void, Void, Void> {
|
|
||||||
|
|
||||||
private OsmandApplication app;
|
|
||||||
private WeakReference<TrackPointFragment> fragmentRef;
|
|
||||||
private GPXFile gpx;
|
|
||||||
private Set<GpxDisplayItem> selectedItems;
|
|
||||||
|
|
||||||
DeletePointsTask(TrackPointFragment fragment) {
|
|
||||||
this.app = fragment.getMyApplication();
|
|
||||||
this.fragmentRef = new WeakReference<>(fragment);
|
|
||||||
this.gpx = fragment.getGpx();
|
|
||||||
this.selectedItems = fragment.getSelectedItems();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPreExecute() {
|
|
||||||
TrackPointFragment fragment = fragmentRef.get();
|
|
||||||
if (fragment != null) {
|
|
||||||
fragment.showProgressBar();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Void doInBackground(Void... params) {
|
|
||||||
SavingTrackHelper savingTrackHelper = app.getSavingTrackHelper();
|
|
||||||
if (gpx != null) {
|
|
||||||
for (GpxDisplayItem item : selectedItems) {
|
|
||||||
if (gpx.showCurrentTrack) {
|
|
||||||
savingTrackHelper.deletePointData(item.locationStart);
|
|
||||||
} else {
|
|
||||||
if (item.group.getType() == GpxDisplayItemType.TRACK_POINTS) {
|
|
||||||
gpx.deleteWptPt(item.locationStart);
|
|
||||||
} else if (item.group.getType() == GpxDisplayItemType.TRACK_ROUTE_POINTS) {
|
|
||||||
gpx.deleteRtePt(item.locationStart);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!gpx.showCurrentTrack) {
|
|
||||||
GPXUtilities.writeGpxFile(new File(gpx.path), gpx);
|
|
||||||
boolean selected = app.getSelectedGpxHelper().getSelectedFileByPath(gpx.path) != null;
|
|
||||||
if (selected) {
|
|
||||||
app.getSelectedGpxHelper().setGpxFileToDisplay(gpx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
syncGpx(gpx);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Void aVoid) {
|
|
||||||
TrackPointFragment fragment = fragmentRef.get();
|
|
||||||
if (fragment != null) {
|
|
||||||
fragment.selectedItems.clear();
|
|
||||||
fragment.selectedGroups.clear();
|
|
||||||
|
|
||||||
fragment.hideProgressBar();
|
|
||||||
List<GpxDisplayGroup> groups = fragment.getOriginalGroups();
|
|
||||||
if (groups != null) {
|
|
||||||
fragment.adapter.synchronizeGroups(groups);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void syncGpx(GPXFile gpxFile) {
|
|
||||||
MapMarkersHelper helper = app.getMapMarkersHelper();
|
|
||||||
MapMarkersGroup group = helper.getMarkersGroup(gpxFile);
|
|
||||||
if (group != null) {
|
|
||||||
helper.runSynchronization(group);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -43,7 +43,7 @@ public class CurrentStreetName {
|
||||||
String rf = n.directionInfo.getRef();
|
String rf = n.directionInfo.getRef();
|
||||||
String dn = n.directionInfo.getDestinationName();
|
String dn = n.directionInfo.getDestinationName();
|
||||||
isSet = !(Algorithms.isEmpty(nm) && Algorithms.isEmpty(rf) && Algorithms.isEmpty(dn));
|
isSet = !(Algorithms.isEmpty(nm) && Algorithms.isEmpty(rf) && Algorithms.isEmpty(dn));
|
||||||
streetName.text = RoutingHelperUtils.formatStreetName(nm, null, dn, "»");
|
streetName.text = RoutingHelperUtils.formatStreetName(nm, rf, dn, "»");
|
||||||
streetName.turnType = n.directionInfo.getTurnType();
|
streetName.turnType = n.directionInfo.getTurnType();
|
||||||
streetName.shieldObject = n.directionInfo.getRouteDataObject();
|
streetName.shieldObject = n.directionInfo.getRouteDataObject();
|
||||||
if (streetName.turnType == null) {
|
if (streetName.turnType == null) {
|
||||||
|
@ -62,7 +62,8 @@ public class CurrentStreetName {
|
||||||
if (rs != null) {
|
if (rs != null) {
|
||||||
streetName.text = getRouteSegmentStreetName(routingHelper, rs, false);
|
streetName.text = getRouteSegmentStreetName(routingHelper, rs, false);
|
||||||
if (Algorithms.isEmpty(streetName.text)) {
|
if (Algorithms.isEmpty(streetName.text)) {
|
||||||
isSet = !Algorithms.isEmpty(getRouteSegmentStreetName(routingHelper, rs, true));
|
streetName.text = getRouteSegmentStreetName(routingHelper, rs, true);
|
||||||
|
isSet = !Algorithms.isEmpty(streetName.text);
|
||||||
} else {
|
} else {
|
||||||
isSet = true;
|
isSet = true;
|
||||||
}
|
}
|
||||||
|
|
57
OsmAnd/src/net/osmand/plus/track/SegmentsCard.java
Normal file
57
OsmAnd/src/net/osmand/plus/track/SegmentsCard.java
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
package net.osmand.plus.track;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem;
|
||||||
|
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItemType;
|
||||||
|
import net.osmand.plus.R;
|
||||||
|
import net.osmand.plus.activities.MapActivity;
|
||||||
|
import net.osmand.plus.myplaces.GPXItemPagerAdapter;
|
||||||
|
import net.osmand.plus.myplaces.SegmentActionsListener;
|
||||||
|
import net.osmand.plus.myplaces.SegmentGPXAdapter;
|
||||||
|
import net.osmand.plus.routepreparationmenu.cards.BaseCard;
|
||||||
|
import net.osmand.plus.views.controls.PagerSlidingTabStrip;
|
||||||
|
import net.osmand.plus.views.controls.WrapContentHeightViewPager;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class SegmentsCard extends BaseCard {
|
||||||
|
|
||||||
|
private TrackDisplayHelper displayHelper;
|
||||||
|
private GpxDisplayItemType[] filterTypes = new GpxDisplayItemType[] {GpxDisplayItemType.TRACK_SEGMENT};
|
||||||
|
private SegmentGPXAdapter adapter;
|
||||||
|
private SegmentActionsListener listener;
|
||||||
|
|
||||||
|
public SegmentsCard(@NonNull MapActivity mapActivity, @NonNull TrackDisplayHelper displayHelper,
|
||||||
|
@NonNull SegmentActionsListener listener) {
|
||||||
|
super(mapActivity);
|
||||||
|
this.displayHelper = displayHelper;
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCardLayoutId() {
|
||||||
|
return R.layout.track_segments_container;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateContent() {
|
||||||
|
ViewGroup container = (ViewGroup) view;
|
||||||
|
container.removeAllViews();
|
||||||
|
List<GpxDisplayItem> items = TrackDisplayHelper.flatten(displayHelper.getOriginalGroups(filterTypes));
|
||||||
|
for (GpxDisplayItem displayItem : items) {
|
||||||
|
View segmentView = SegmentGPXAdapter.createGpxTabsView(displayHelper, container, listener, nightMode);
|
||||||
|
|
||||||
|
WrapContentHeightViewPager pager = segmentView.findViewById(R.id.pager);
|
||||||
|
PagerSlidingTabStrip tabLayout = segmentView.findViewById(R.id.sliding_tabs);
|
||||||
|
|
||||||
|
pager.setAdapter(new GPXItemPagerAdapter(tabLayout, displayItem, displayHelper, listener));
|
||||||
|
tabLayout.setViewPager(pager);
|
||||||
|
|
||||||
|
container.addView(segmentView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
179
OsmAnd/src/net/osmand/plus/track/TrackDisplayHelper.java
Normal file
179
OsmAnd/src/net/osmand/plus/track/TrackDisplayHelper.java
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
package net.osmand.plus.track;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import net.osmand.GPXUtilities.GPXFile;
|
||||||
|
import net.osmand.GPXUtilities.WptPt;
|
||||||
|
import net.osmand.data.LatLon;
|
||||||
|
import net.osmand.data.PointDescription;
|
||||||
|
import net.osmand.data.QuadRect;
|
||||||
|
import net.osmand.plus.GPXDatabase.GpxDataItem;
|
||||||
|
import net.osmand.plus.GpxSelectionHelper;
|
||||||
|
import net.osmand.plus.GpxSelectionHelper.GpxDisplayGroup;
|
||||||
|
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem;
|
||||||
|
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItemType;
|
||||||
|
import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
|
||||||
|
import net.osmand.plus.OsmandApplication;
|
||||||
|
import net.osmand.plus.R;
|
||||||
|
import net.osmand.plus.activities.MapActivity;
|
||||||
|
import net.osmand.plus.measurementtool.GpxData;
|
||||||
|
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TrackDisplayHelper {
|
||||||
|
|
||||||
|
private final OsmandApplication app;
|
||||||
|
|
||||||
|
private File file;
|
||||||
|
private GPXFile gpxFile;
|
||||||
|
private GpxDataItem gpxDataItem;
|
||||||
|
|
||||||
|
private long modifiedTime = -1;
|
||||||
|
private List<GpxDisplayGroup> displayGroups;
|
||||||
|
private List<GpxDisplayGroup> originalGroups = new ArrayList<>();
|
||||||
|
|
||||||
|
public TrackDisplayHelper(OsmandApplication app) {
|
||||||
|
this.app = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public GPXFile getGpx() {
|
||||||
|
return gpxFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public GpxDataItem getGpxDataItem() {
|
||||||
|
return gpxDataItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getFile() {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFile(@Nullable File file) {
|
||||||
|
this.file = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGpx(@NonNull GPXFile result) {
|
||||||
|
this.gpxFile = result;
|
||||||
|
if (file == null) {
|
||||||
|
this.gpxFile = app.getSavingTrackHelper().getCurrentGpx();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGpxDataItem(GpxDataItem gpxDataItem) {
|
||||||
|
this.gpxDataItem = gpxDataItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QuadRect getRect() {
|
||||||
|
if (getGpx() != null) {
|
||||||
|
return getGpx().getRect();
|
||||||
|
} else {
|
||||||
|
return new QuadRect(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setJoinSegments(boolean joinSegments) {
|
||||||
|
if (gpxDataItem != null) {
|
||||||
|
boolean updated = app.getGpxDbHelper().updateJoinSegments(gpxDataItem, joinSegments);
|
||||||
|
|
||||||
|
SelectedGpxFile selectedGpxFile = app.getSelectedGpxHelper().getSelectedFileByPath(gpxFile.path);
|
||||||
|
if (updated && selectedGpxFile != null) {
|
||||||
|
selectedGpxFile.setJoinSegments(joinSegments);
|
||||||
|
}
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isJoinSegments() {
|
||||||
|
return gpxDataItem != null && gpxDataItem.isJoinSegments();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<GpxDisplayGroup> getGpxFile(boolean useDisplayGroups) {
|
||||||
|
if (gpxFile == null) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
if (gpxFile.modifiedTime != modifiedTime) {
|
||||||
|
modifiedTime = gpxFile.modifiedTime;
|
||||||
|
GpxSelectionHelper selectedGpxHelper = app.getSelectedGpxHelper();
|
||||||
|
displayGroups = selectedGpxHelper.collectDisplayGroups(gpxFile);
|
||||||
|
originalGroups.clear();
|
||||||
|
for (GpxSelectionHelper.GpxDisplayGroup g : displayGroups) {
|
||||||
|
originalGroups.add(g.cloneInstance());
|
||||||
|
}
|
||||||
|
if (file != null) {
|
||||||
|
SelectedGpxFile sf = selectedGpxHelper.getSelectedFileByPath(gpxFile.path);
|
||||||
|
if (sf != null && file != null && sf.getDisplayGroups(app) != null) {
|
||||||
|
displayGroups = sf.getDisplayGroups(app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (useDisplayGroups) {
|
||||||
|
return displayGroups;
|
||||||
|
} else {
|
||||||
|
return originalGroups;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public List<GpxDisplayGroup> getOriginalGroups(GpxDisplayItemType[] filterTypes) {
|
||||||
|
return filterGroups(false, filterTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public List<GpxDisplayGroup> getDisplayGroups(GpxDisplayItemType[] filterTypes) {
|
||||||
|
return filterGroups(true, filterTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasFilterType(GpxDisplayItemType filterType, GpxDisplayItemType[] filterTypes) {
|
||||||
|
for (GpxDisplayItemType type : filterTypes) {
|
||||||
|
if (type == filterType) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private List<GpxDisplayGroup> filterGroups(boolean useDisplayGroups, GpxDisplayItemType[] filterTypes) {
|
||||||
|
List<GpxDisplayGroup> groups = new ArrayList<>();
|
||||||
|
for (GpxDisplayGroup group : getGpxFile(useDisplayGroups)) {
|
||||||
|
if (hasFilterType(group.getType(), filterTypes)) {
|
||||||
|
groups.add(group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<GpxDisplayItem> flatten(List<GpxDisplayGroup> groups) {
|
||||||
|
ArrayList<GpxDisplayItem> list = new ArrayList<>();
|
||||||
|
for (GpxDisplayGroup g : groups) {
|
||||||
|
list.addAll(g.getModifiableList());
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addNewGpxData(Activity activity) {
|
||||||
|
GPXFile gpxFile = getGpx();
|
||||||
|
GpxData gpxData = new GpxData(gpxFile);
|
||||||
|
WptPt pointToShow = gpxFile != null ? gpxFile.findPointToShow() : null;
|
||||||
|
if (pointToShow != null) {
|
||||||
|
LatLon location = new LatLon(pointToShow.getLatitude(), pointToShow.getLongitude());
|
||||||
|
final OsmandSettings settings = app.getSettings();
|
||||||
|
settings.setMapLocationToShow(location.getLatitude(), location.getLongitude(),
|
||||||
|
settings.getLastKnownMapZoom(),
|
||||||
|
new PointDescription(PointDescription.POINT_TYPE_WPT, activity.getString(R.string.add_line)),
|
||||||
|
false,
|
||||||
|
gpxData
|
||||||
|
);
|
||||||
|
MapActivity.launchMapActivityMoveToTop(activity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
596
OsmAnd/src/net/osmand/plus/track/TrackMenuFragment.java
Normal file
596
OsmAnd/src/net/osmand/plus/track/TrackMenuFragment.java
Normal file
|
@ -0,0 +1,596 @@
|
||||||
|
package net.osmand.plus.track;
|
||||||
|
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.res.ColorStateList;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.DrawableRes;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
|
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
|
||||||
|
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||||
|
|
||||||
|
import net.osmand.AndroidUtils;
|
||||||
|
import net.osmand.GPXUtilities.GPXFile;
|
||||||
|
import net.osmand.GPXUtilities.Track;
|
||||||
|
import net.osmand.GPXUtilities.TrkSegment;
|
||||||
|
import net.osmand.GPXUtilities.WptPt;
|
||||||
|
import net.osmand.PlatformUtil;
|
||||||
|
import net.osmand.data.QuadRect;
|
||||||
|
import net.osmand.data.RotatedTileBox;
|
||||||
|
import net.osmand.plus.GPXDatabase.GpxDataItem;
|
||||||
|
import net.osmand.plus.GpxDbHelper;
|
||||||
|
import net.osmand.plus.GpxSelectionHelper.GpxDisplayGroup;
|
||||||
|
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem;
|
||||||
|
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItemType;
|
||||||
|
import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
|
||||||
|
import net.osmand.plus.OsmandApplication;
|
||||||
|
import net.osmand.plus.R;
|
||||||
|
import net.osmand.plus.activities.MapActivity;
|
||||||
|
import net.osmand.plus.base.ContextMenuFragment;
|
||||||
|
import net.osmand.plus.base.ContextMenuScrollFragment;
|
||||||
|
import net.osmand.plus.helpers.AndroidUiHelper;
|
||||||
|
import net.osmand.plus.helpers.GpxUiHelper;
|
||||||
|
import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetType;
|
||||||
|
import net.osmand.plus.helpers.GpxUiHelper.OrderedLineDataSet;
|
||||||
|
import net.osmand.plus.mapcontextmenu.other.TrackDetailsMenu;
|
||||||
|
import net.osmand.plus.measurementtool.GpxData;
|
||||||
|
import net.osmand.plus.measurementtool.MeasurementEditingContext;
|
||||||
|
import net.osmand.plus.measurementtool.MeasurementToolFragment;
|
||||||
|
import net.osmand.plus.myplaces.GPXTabItemType;
|
||||||
|
import net.osmand.plus.myplaces.SegmentActionsListener;
|
||||||
|
import net.osmand.plus.myplaces.SplitSegmentDialogFragment;
|
||||||
|
import net.osmand.plus.myplaces.TrackActivityFragmentAdapter;
|
||||||
|
import net.osmand.plus.routepreparationmenu.cards.BaseCard;
|
||||||
|
import net.osmand.plus.routepreparationmenu.cards.BaseCard.CardListener;
|
||||||
|
import net.osmand.plus.track.SaveGpxAsyncTask.SaveGpxListener;
|
||||||
|
import net.osmand.plus.widgets.IconPopupMenu;
|
||||||
|
import net.osmand.util.Algorithms;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static net.osmand.plus.activities.TrackActivity.CURRENT_RECORDING;
|
||||||
|
import static net.osmand.plus.activities.TrackActivity.TRACK_FILE_NAME;
|
||||||
|
|
||||||
|
public class TrackMenuFragment extends ContextMenuScrollFragment implements CardListener, SegmentActionsListener {
|
||||||
|
|
||||||
|
public static final String TAG = TrackMenuFragment.class.getName();
|
||||||
|
private static final Log log = PlatformUtil.getLog(TrackMenuFragment.class);
|
||||||
|
|
||||||
|
private OsmandApplication app;
|
||||||
|
private TrackDisplayHelper displayHelper;
|
||||||
|
|
||||||
|
private GpxDataItem gpxDataItem;
|
||||||
|
private SelectedGpxFile selectedGpxFile;
|
||||||
|
|
||||||
|
private View routeMenuTopShadowAll;
|
||||||
|
private BottomNavigationView bottomNav;
|
||||||
|
private TrackMenuType menuType = TrackMenuType.TRACK;
|
||||||
|
private SegmentsCard segmentsCard;
|
||||||
|
|
||||||
|
private int menuTitleHeight;
|
||||||
|
|
||||||
|
public enum TrackMenuType {
|
||||||
|
TRACK(R.id.action_track, R.string.shared_string_gpx_tracks),
|
||||||
|
POINTS(R.id.action_points, R.string.shared_string_gpx_points);
|
||||||
|
|
||||||
|
TrackMenuType(@DrawableRes int iconId, @StringRes int titleId) {
|
||||||
|
this.iconId = iconId;
|
||||||
|
this.titleId = titleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int iconId;
|
||||||
|
public final int titleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMainLayoutId() {
|
||||||
|
return R.layout.track_menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getHeaderViewHeight() {
|
||||||
|
return menuTitleHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isHeaderViewDetached() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getToolbarHeight() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getMiddleStateKoef() {
|
||||||
|
return 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSupportedMenuStatesPortrait() {
|
||||||
|
return MenuState.HEADER_ONLY | MenuState.HALF_SCREEN | MenuState.FULL_SCREEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TrackDisplayHelper getDisplayHelper() {
|
||||||
|
return displayHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
app = requireMyApplication();
|
||||||
|
GpxDbHelper gpxDbHelper = app.getGpxDbHelper();
|
||||||
|
displayHelper = new TrackDisplayHelper(app);
|
||||||
|
|
||||||
|
String gpxFilePath = "";
|
||||||
|
boolean currentRecording = false;
|
||||||
|
Bundle arguments = getArguments();
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
gpxFilePath = savedInstanceState.getString(TRACK_FILE_NAME);
|
||||||
|
currentRecording = savedInstanceState.getBoolean(CURRENT_RECORDING, false);
|
||||||
|
} else if (arguments != null) {
|
||||||
|
gpxFilePath = arguments.getString(TRACK_FILE_NAME);
|
||||||
|
currentRecording = arguments.getBoolean(CURRENT_RECORDING, false);
|
||||||
|
}
|
||||||
|
if (currentRecording) {
|
||||||
|
selectedGpxFile = app.getSavingTrackHelper().getCurrentTrack();
|
||||||
|
} else {
|
||||||
|
File file = new File(gpxFilePath);
|
||||||
|
displayHelper.setFile(file);
|
||||||
|
gpxDataItem = gpxDbHelper.getItem(file);
|
||||||
|
selectedGpxFile = app.getSelectedGpxHelper().getSelectedFileByPath(gpxFilePath);
|
||||||
|
}
|
||||||
|
displayHelper.setGpxDataItem(gpxDataItem);
|
||||||
|
displayHelper.setGpx(selectedGpxFile.getGpxFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
public GPXFile getGpx() {
|
||||||
|
return displayHelper.getGpx();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
View view = super.onCreateView(inflater, container, savedInstanceState);
|
||||||
|
if (view != null) {
|
||||||
|
bottomNav = view.findViewById(R.id.bottom_navigation);
|
||||||
|
routeMenuTopShadowAll = view.findViewById(R.id.route_menu_top_shadow_all);
|
||||||
|
TextView title = view.findViewById(R.id.title);
|
||||||
|
String fileName = Algorithms.getFileWithoutDirs(getGpx().path);
|
||||||
|
title.setText(GpxUiHelper.getGpxTitle(fileName));
|
||||||
|
|
||||||
|
if (isPortrait()) {
|
||||||
|
updateCardsLayout();
|
||||||
|
}
|
||||||
|
setupCards();
|
||||||
|
setupButtons(view);
|
||||||
|
enterTrackAppearanceMode();
|
||||||
|
runLayoutListener();
|
||||||
|
}
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupCards() {
|
||||||
|
MapActivity mapActivity = getMapActivity();
|
||||||
|
if (mapActivity != null) {
|
||||||
|
ViewGroup cardsContainer = getCardsContainer();
|
||||||
|
cardsContainer.removeAllViews();
|
||||||
|
if (menuType == TrackMenuType.TRACK) {
|
||||||
|
if (segmentsCard != null) {
|
||||||
|
cardsContainer.addView(segmentsCard.getView());
|
||||||
|
} else {
|
||||||
|
segmentsCard = new SegmentsCard(mapActivity, displayHelper, this);
|
||||||
|
cardsContainer.addView(segmentsCard.build(mapActivity));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void calculateLayout(View view, boolean initLayout) {
|
||||||
|
menuTitleHeight = routeMenuTopShadowAll.getHeight()
|
||||||
|
+ bottomNav.getHeight();
|
||||||
|
super.calculateLayout(view, initLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setViewY(int y, boolean animated, boolean adjustMapPos) {
|
||||||
|
super.setViewY(y, animated, adjustMapPos);
|
||||||
|
updateStatusBarColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateMainViewLayout(int posY) {
|
||||||
|
super.updateMainViewLayout(posY);
|
||||||
|
updateStatusBarColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldShowMapControls(int menuState) {
|
||||||
|
return menuState == MenuState.HEADER_ONLY || menuState == MenuState.HALF_SCREEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
adjustMapPosition(getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
exitTrackAppearanceMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enterTrackAppearanceMode() {
|
||||||
|
MapActivity mapActivity = getMapActivity();
|
||||||
|
if (mapActivity != null) {
|
||||||
|
boolean portrait = AndroidUiHelper.isOrientationPortrait(mapActivity);
|
||||||
|
AndroidUiHelper.setVisibility(mapActivity, portrait ? View.INVISIBLE : View.GONE,
|
||||||
|
R.id.map_left_widgets_panel,
|
||||||
|
R.id.map_right_widgets_panel,
|
||||||
|
R.id.map_center_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void exitTrackAppearanceMode() {
|
||||||
|
MapActivity mapActivity = getMapActivity();
|
||||||
|
if (mapActivity != null) {
|
||||||
|
AndroidUiHelper.setVisibility(mapActivity, View.VISIBLE,
|
||||||
|
R.id.map_left_widgets_panel,
|
||||||
|
R.id.map_right_widgets_panel,
|
||||||
|
R.id.map_center_info,
|
||||||
|
R.id.map_search_button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
|
outState.putString(TRACK_FILE_NAME, getGpx().path);
|
||||||
|
outState.putBoolean(CURRENT_RECORDING, selectedGpxFile.isShowCurrentTrack());
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getStatusBarColorId() {
|
||||||
|
View view = getView();
|
||||||
|
if (view != null) {
|
||||||
|
boolean nightMode = isNightMode();
|
||||||
|
if (getViewY() <= getFullScreenTopPosY() || !isPortrait()) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 23 && !nightMode) {
|
||||||
|
view.setSystemUiVisibility(view.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
|
||||||
|
}
|
||||||
|
return nightMode ? R.color.divider_color_dark : R.color.divider_color_light;
|
||||||
|
} else {
|
||||||
|
if (Build.VERSION.SDK_INT >= 23 && !nightMode) {
|
||||||
|
view.setSystemUiVisibility(view.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateStatusBarColor() {
|
||||||
|
MapActivity mapActivity = getMapActivity();
|
||||||
|
if (mapActivity != null) {
|
||||||
|
mapActivity.updateStatusBarColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getThemeInfoProviderTag() {
|
||||||
|
return TAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCardLayoutNeeded(@NonNull BaseCard card) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCardPressed(@NonNull BaseCard card) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCardButtonPressed(@NonNull BaseCard card, int buttonIndex) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int applyPosY(int currentY, boolean needCloseMenu, boolean needMapAdjust, int previousMenuState, int newMenuState, int dZoom, boolean animated) {
|
||||||
|
int y = super.applyPosY(currentY, needCloseMenu, needMapAdjust, previousMenuState, newMenuState, dZoom, animated);
|
||||||
|
if (needMapAdjust) {
|
||||||
|
adjustMapPosition(y);
|
||||||
|
}
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onHeaderClick() {
|
||||||
|
adjustMapPosition(getViewY());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void adjustMapPosition(int y) {
|
||||||
|
MapActivity mapActivity = getMapActivity();
|
||||||
|
if (mapActivity != null && mapActivity.getMapView() != null) {
|
||||||
|
GPXFile gpxFile = getGpx();
|
||||||
|
QuadRect r = gpxFile.getRect();
|
||||||
|
|
||||||
|
RotatedTileBox tb = mapActivity.getMapView().getCurrentRotatedTileBox().copy();
|
||||||
|
int tileBoxWidthPx = 0;
|
||||||
|
int tileBoxHeightPx = 0;
|
||||||
|
|
||||||
|
if (!isPortrait()) {
|
||||||
|
tileBoxWidthPx = tb.getPixWidth() - getWidth();
|
||||||
|
} else {
|
||||||
|
int fHeight = getViewHeight() - y - AndroidUtils.getStatusBarHeight(mapActivity);
|
||||||
|
tileBoxHeightPx = tb.getPixHeight() - fHeight;
|
||||||
|
}
|
||||||
|
if (r.left != 0 && r.right != 0) {
|
||||||
|
mapActivity.getMapView().fitRectToMap(r.left, r.right, r.top, r.bottom, tileBoxWidthPx, tileBoxHeightPx, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateCardsLayout() {
|
||||||
|
View mainView = getMainView();
|
||||||
|
if (mainView != null) {
|
||||||
|
View topShadow = getTopShadow();
|
||||||
|
FrameLayout bottomContainer = getBottomContainer();
|
||||||
|
if (getCurrentMenuState() == MenuState.HEADER_ONLY) {
|
||||||
|
topShadow.setVisibility(View.INVISIBLE);
|
||||||
|
bottomContainer.setBackgroundDrawable(null);
|
||||||
|
} else {
|
||||||
|
topShadow.setVisibility(View.VISIBLE);
|
||||||
|
AndroidUtils.setBackground(mainView.getContext(), bottomContainer, isNightMode(), R.color.list_background_color_light, R.color.list_background_color_dark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupButtons(View view) {
|
||||||
|
ColorStateList navColorStateList = AndroidUtils.createBottomNavColorStateList(getContext(), isNightMode());
|
||||||
|
BottomNavigationView bottomNav = view.findViewById(R.id.bottom_navigation);
|
||||||
|
bottomNav.setItemIconTintList(navColorStateList);
|
||||||
|
bottomNav.setItemTextColor(navColorStateList);
|
||||||
|
bottomNav.setSelectedItemId(R.id.action_track);
|
||||||
|
bottomNav.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
|
||||||
|
for (TrackMenuType type : TrackMenuType.values()) {
|
||||||
|
if (type.iconId == item.getItemId()) {
|
||||||
|
menuType = type;
|
||||||
|
setupCards();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateContent() {
|
||||||
|
if (segmentsCard != null) {
|
||||||
|
segmentsCard.updateContent();
|
||||||
|
}
|
||||||
|
setupCards();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChartTouch() {
|
||||||
|
getBottomScrollView().requestDisallowInterceptTouchEvent(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void scrollBy(int px) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPointSelected(double lat, double lon) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void openSplitInterval(GpxDisplayItem gpxItem, TrkSegment trkSegment) {
|
||||||
|
FragmentManager fragmentManager = getFragmentManager();
|
||||||
|
if (fragmentManager != null) {
|
||||||
|
SplitSegmentDialogFragment.showInstance(fragmentManager, displayHelper, gpxItem, trkSegment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void openAnalyzeOnMap(GpxDisplayItem gpxItem, List<ILineDataSet> dataSets, GPXTabItemType tabType) {
|
||||||
|
WptPt wpt = null;
|
||||||
|
gpxItem.chartTypes = null;
|
||||||
|
if (dataSets != null && dataSets.size() > 0) {
|
||||||
|
gpxItem.chartTypes = new GPXDataSetType[dataSets.size()];
|
||||||
|
for (int i = 0; i < dataSets.size(); i++) {
|
||||||
|
OrderedLineDataSet orderedDataSet = (OrderedLineDataSet) dataSets.get(i);
|
||||||
|
gpxItem.chartTypes[i] = orderedDataSet.getDataSetType();
|
||||||
|
}
|
||||||
|
if (gpxItem.chartHighlightPos != -1) {
|
||||||
|
TrkSegment segment = null;
|
||||||
|
for (Track t : gpxItem.group.getGpx().tracks) {
|
||||||
|
for (TrkSegment s : t.segments) {
|
||||||
|
if (s.points.size() > 0 && s.points.get(0).equals(gpxItem.analysis.locationStart)) {
|
||||||
|
segment = s;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (segment != null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (segment != null) {
|
||||||
|
OrderedLineDataSet dataSet = (OrderedLineDataSet) dataSets.get(0);
|
||||||
|
float distance = gpxItem.chartHighlightPos * dataSet.getDivX();
|
||||||
|
for (WptPt p : segment.points) {
|
||||||
|
if (p.distance >= distance) {
|
||||||
|
wpt = p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (wpt != null) {
|
||||||
|
gpxItem.locationOnMap = wpt;
|
||||||
|
} else {
|
||||||
|
gpxItem.locationOnMap = gpxItem.locationStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
TrackDetailsMenu trackDetailsMenu = getMapActivity().getTrackDetailsMenu();
|
||||||
|
trackDetailsMenu.setGpxItem(gpxItem);
|
||||||
|
trackDetailsMenu.show();
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void showOptionsPopupMenu(View view, final TrkSegment segment, final boolean confirmDeletion) {
|
||||||
|
FragmentActivity activity = getActivity();
|
||||||
|
if (activity != null) {
|
||||||
|
IconPopupMenu optionsPopupMenu = new IconPopupMenu(activity, view.findViewById(R.id.overflow_menu));
|
||||||
|
Menu menu = optionsPopupMenu.getMenu();
|
||||||
|
optionsPopupMenu.getMenuInflater().inflate(R.menu.track_segment_menu, menu);
|
||||||
|
menu.findItem(R.id.action_edit).setIcon(app.getUIUtilities().getThemedIcon(R.drawable.ic_action_edit_dark));
|
||||||
|
menu.findItem(R.id.action_delete).setIcon(app.getUIUtilities().getThemedIcon(R.drawable.ic_action_remove_dark));
|
||||||
|
optionsPopupMenu.setOnMenuItemClickListener(new IconPopupMenu.OnMenuItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onMenuItemClick(MenuItem item) {
|
||||||
|
int i = item.getItemId();
|
||||||
|
if (i == R.id.action_edit) {
|
||||||
|
editSegment(segment);
|
||||||
|
return true;
|
||||||
|
} else if (i == R.id.action_delete) {
|
||||||
|
FragmentActivity activity = getActivity();
|
||||||
|
if (!confirmDeletion) {
|
||||||
|
deleteAndSaveSegment(segment);
|
||||||
|
} else if (activity != null) {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||||
|
builder.setMessage(R.string.recording_delete_confirm);
|
||||||
|
builder.setPositiveButton(R.string.shared_string_yes, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
deleteAndSaveSegment(segment);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.setNegativeButton(R.string.shared_string_cancel, null);
|
||||||
|
builder.show();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
optionsPopupMenu.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void editSegment(TrkSegment segment) {
|
||||||
|
GPXFile gpxFile = getGpx();
|
||||||
|
openPlanRoute(new GpxData(gpxFile));
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void openPlanRoute(GpxData gpxData) {
|
||||||
|
QuadRect qr = gpxData.getRect();
|
||||||
|
getMapActivity().getMapView().fitRectToMap(qr.left, qr.right, qr.top, qr.bottom, (int) qr.width(), (int) qr.height(), 0);
|
||||||
|
MeasurementEditingContext editingContext = new MeasurementEditingContext();
|
||||||
|
editingContext.setGpxData(gpxData);
|
||||||
|
MeasurementToolFragment.showInstance(getFragmentManager(), editingContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteAndSaveSegment(TrkSegment segment) {
|
||||||
|
if (deleteSegment(segment)) {
|
||||||
|
GPXFile gpx = displayHelper.getGpx();
|
||||||
|
if (gpx != null) {
|
||||||
|
boolean showOnMap = TrackActivityFragmentAdapter.isGpxFileSelected(app, gpx);
|
||||||
|
SelectedGpxFile selectedGpxFile = app.getSelectedGpxHelper().selectGpxFile(gpx, showOnMap, false);
|
||||||
|
saveGpx(showOnMap ? selectedGpxFile : null, gpx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean deleteSegment(TrkSegment segment) {
|
||||||
|
if (segment != null) {
|
||||||
|
GPXFile gpx = displayHelper.getGpx();
|
||||||
|
if (gpx != null) {
|
||||||
|
return gpx.removeTrkSegment(segment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveGpx(final SelectedGpxFile selectedGpxFile, GPXFile gpxFile) {
|
||||||
|
new SaveGpxAsyncTask(new File(gpxFile.path), gpxFile, new SaveGpxListener() {
|
||||||
|
@Override
|
||||||
|
public void gpxSavingStarted() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void gpxSavingFinished(Exception errorMessage) {
|
||||||
|
if (selectedGpxFile != null) {
|
||||||
|
List<GpxDisplayGroup> groups = displayHelper.getDisplayGroups(new GpxDisplayItemType[] {GpxDisplayItemType.TRACK_SEGMENT});
|
||||||
|
if (groups != null) {
|
||||||
|
selectedGpxFile.setDisplayGroups(groups, app);
|
||||||
|
selectedGpxFile.processPoints(app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateContent();
|
||||||
|
}
|
||||||
|
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void close() {
|
||||||
|
try {
|
||||||
|
MapActivity mapActivity = getMapActivity();
|
||||||
|
if (mapActivity != null) {
|
||||||
|
FragmentManager fragmentManager = mapActivity.getSupportFragmentManager();
|
||||||
|
fragmentManager.beginTransaction().remove(this).commitAllowingStateLoss();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean showInstance(@NonNull MapActivity mapActivity, String path, boolean showCurrentTrack) {
|
||||||
|
try {
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putString(TRACK_FILE_NAME, path);
|
||||||
|
args.putBoolean(CURRENT_RECORDING, showCurrentTrack);
|
||||||
|
args.putInt(ContextMenuFragment.MENU_STATE_KEY, MenuState.HALF_SCREEN);
|
||||||
|
|
||||||
|
TrackMenuFragment fragment = new TrackMenuFragment();
|
||||||
|
fragment.setArguments(args);
|
||||||
|
|
||||||
|
mapActivity.getSupportFragmentManager()
|
||||||
|
.beginTransaction()
|
||||||
|
.replace(R.id.fragmentContainer, fragment, fragment.getFragmentTag())
|
||||||
|
.addToBackStack(fragment.getFragmentTag())
|
||||||
|
.commitAllowingStateLoss();
|
||||||
|
return true;
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
import net.osmand.AndroidUtils;
|
import net.osmand.AndroidUtils;
|
||||||
import net.osmand.GPXUtilities;
|
import net.osmand.GPXUtilities;
|
||||||
|
import net.osmand.GPXUtilities.WptPt;
|
||||||
import net.osmand.data.LatLon;
|
import net.osmand.data.LatLon;
|
||||||
import net.osmand.data.PointDescription;
|
import net.osmand.data.PointDescription;
|
||||||
import net.osmand.plus.OsmandApplication;
|
import net.osmand.plus.OsmandApplication;
|
||||||
|
@ -22,6 +23,7 @@ import net.osmand.plus.settings.backend.OsmandSettings;
|
||||||
import net.osmand.plus.wikipedia.WikiArticleHelper;
|
import net.osmand.plus.wikipedia.WikiArticleHelper;
|
||||||
import net.osmand.plus.wikivoyage.article.WikivoyageArticleDialogFragment;
|
import net.osmand.plus.wikivoyage.article.WikivoyageArticleDialogFragment;
|
||||||
import net.osmand.plus.wikivoyage.data.TravelArticle;
|
import net.osmand.plus.wikivoyage.data.TravelArticle;
|
||||||
|
import net.osmand.plus.wikivoyage.data.TravelArticle.TravelArticleIdentifier;
|
||||||
import net.osmand.plus.wikivoyage.explore.WikivoyageExploreActivity;
|
import net.osmand.plus.wikivoyage.explore.WikivoyageExploreActivity;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -67,8 +69,8 @@ public class WikivoyageWebViewClient extends WebViewClient {
|
||||||
if (url.contains(WIKIVOYAGE_DOMAIN) && isWebPage) {
|
if (url.contains(WIKIVOYAGE_DOMAIN) && isWebPage) {
|
||||||
String lang = WikiArticleHelper.getLang(url);
|
String lang = WikiArticleHelper.getLang(url);
|
||||||
String articleName = WikiArticleHelper.getArticleNameFromUrl(url, lang);
|
String articleName = WikiArticleHelper.getArticleNameFromUrl(url, lang);
|
||||||
String articleId = app.getTravelHelper().getArticleId(articleName, lang);
|
TravelArticleIdentifier articleId = app.getTravelHelper().getArticleId(articleName, lang);
|
||||||
if (!articleId.isEmpty()) {
|
if (articleId != null) {
|
||||||
WikivoyageArticleDialogFragment.showInstance(app, fragmentManager, articleId, lang);
|
WikivoyageArticleDialogFragment.showInstance(app, fragmentManager, articleId, lang);
|
||||||
} else {
|
} else {
|
||||||
WikiArticleHelper.warnAboutExternalLoad(url, activity, nightMode);
|
WikiArticleHelper.warnAboutExternalLoad(url, activity, nightMode);
|
||||||
|
@ -83,8 +85,8 @@ public class WikivoyageWebViewClient extends WebViewClient {
|
||||||
WikiArticleHelper.warnAboutExternalLoad(url, activity, nightMode);
|
WikiArticleHelper.warnAboutExternalLoad(url, activity, nightMode);
|
||||||
} else if (url.startsWith(PREFIX_GEO)) {
|
} else if (url.startsWith(PREFIX_GEO)) {
|
||||||
if (article != null) {
|
if (article != null) {
|
||||||
List<GPXUtilities.WptPt> points = article.getGpxFile().getPoints();
|
List<WptPt> points = article.getGpxFile().getPoints();
|
||||||
GPXUtilities.WptPt gpxPoint = null;
|
WptPt gpxPoint = null;
|
||||||
String coordinates = url.replace(PREFIX_GEO, "");
|
String coordinates = url.replace(PREFIX_GEO, "");
|
||||||
double lat;
|
double lat;
|
||||||
double lon;
|
double lon;
|
||||||
|
@ -96,7 +98,7 @@ public class WikivoyageWebViewClient extends WebViewClient {
|
||||||
Log.w(TAG, e.getMessage(), e);
|
Log.w(TAG, e.getMessage(), e);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
for (GPXUtilities.WptPt point : points) {
|
for (WptPt point : points) {
|
||||||
if (point.getLatitude() == lat && point.getLongitude() == lon) {
|
if (point.getLatitude() == lat && point.getLongitude() == lon) {
|
||||||
gpxPoint = point;
|
gpxPoint = point;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -30,17 +30,18 @@ import net.osmand.AndroidUtils;
|
||||||
import net.osmand.IndexConstants;
|
import net.osmand.IndexConstants;
|
||||||
import net.osmand.plus.OsmandApplication;
|
import net.osmand.plus.OsmandApplication;
|
||||||
import net.osmand.plus.OsmandPlugin;
|
import net.osmand.plus.OsmandPlugin;
|
||||||
import net.osmand.plus.UiUtilities;
|
|
||||||
import net.osmand.plus.settings.backend.OsmandSettings;
|
|
||||||
import net.osmand.plus.R;
|
import net.osmand.plus.R;
|
||||||
|
import net.osmand.plus.UiUtilities;
|
||||||
import net.osmand.plus.activities.TrackActivity;
|
import net.osmand.plus.activities.TrackActivity;
|
||||||
import net.osmand.plus.development.OsmandDevelopmentPlugin;
|
import net.osmand.plus.development.OsmandDevelopmentPlugin;
|
||||||
import net.osmand.plus.helpers.FileNameTranslationHelper;
|
import net.osmand.plus.helpers.FileNameTranslationHelper;
|
||||||
|
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||||
import net.osmand.plus.wikipedia.WikiArticleBaseDialogFragment;
|
import net.osmand.plus.wikipedia.WikiArticleBaseDialogFragment;
|
||||||
import net.osmand.plus.wikipedia.WikiArticleHelper;
|
import net.osmand.plus.wikipedia.WikiArticleHelper;
|
||||||
import net.osmand.plus.wikivoyage.WikivoyageShowPicturesDialogFragment;
|
import net.osmand.plus.wikivoyage.WikivoyageShowPicturesDialogFragment;
|
||||||
import net.osmand.plus.wikivoyage.WikivoyageWebViewClient;
|
import net.osmand.plus.wikivoyage.WikivoyageWebViewClient;
|
||||||
import net.osmand.plus.wikivoyage.data.TravelArticle;
|
import net.osmand.plus.wikivoyage.data.TravelArticle;
|
||||||
|
import net.osmand.plus.wikivoyage.data.TravelArticle.TravelArticleIdentifier;
|
||||||
import net.osmand.plus.wikivoyage.data.TravelHelper;
|
import net.osmand.plus.wikivoyage.data.TravelHelper;
|
||||||
import net.osmand.plus.wikivoyage.data.TravelLocalDataHelper;
|
import net.osmand.plus.wikivoyage.data.TravelLocalDataHelper;
|
||||||
import net.osmand.util.Algorithms;
|
import net.osmand.util.Algorithms;
|
||||||
|
@ -58,15 +59,15 @@ public class WikivoyageArticleDialogFragment extends WikiArticleBaseDialogFragme
|
||||||
public static final String TAG = "WikivoyageArticleDialogFragment";
|
public static final String TAG = "WikivoyageArticleDialogFragment";
|
||||||
|
|
||||||
|
|
||||||
private static final String ROUTE_ID_KEY = "route_id_key";
|
private static final String ARTICLE_ID_KEY = "article_id";
|
||||||
private static final String LANGS_KEY = "langs_key";
|
private static final String LANGS_KEY = "langs";
|
||||||
private static final String SELECTED_LANG_KEY = "selected_lang_key";
|
private static final String SELECTED_LANG_KEY = "selected_lang";
|
||||||
|
|
||||||
private static final String EMPTY_URL = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d4//";
|
private static final String EMPTY_URL = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d4//";
|
||||||
|
|
||||||
private static final int MENU_ITEM_SHARE = 0;
|
private static final int MENU_ITEM_SHARE = 0;
|
||||||
|
|
||||||
private String routeId = "";
|
private TravelArticleIdentifier articleId;
|
||||||
private ArrayList<String> langs;
|
private ArrayList<String> langs;
|
||||||
private String selectedLang;
|
private String selectedLang;
|
||||||
private TravelArticle article;
|
private TravelArticle article;
|
||||||
|
@ -192,15 +193,17 @@ public class WikivoyageArticleDialogFragment extends WikiArticleBaseDialogFragme
|
||||||
if (requestCode == WikivoyageArticleContentsFragment.SHOW_CONTENT_ITEM_REQUEST_CODE) {
|
if (requestCode == WikivoyageArticleContentsFragment.SHOW_CONTENT_ITEM_REQUEST_CODE) {
|
||||||
String link = data.getStringExtra(WikivoyageArticleContentsFragment.CONTENT_ITEM_LINK_KEY);
|
String link = data.getStringExtra(WikivoyageArticleContentsFragment.CONTENT_ITEM_LINK_KEY);
|
||||||
String title = data.getStringExtra(WikivoyageArticleContentsFragment.CONTENT_ITEM_TITLE_KEY);
|
String title = data.getStringExtra(WikivoyageArticleContentsFragment.CONTENT_ITEM_TITLE_KEY);
|
||||||
moveToAnchor(link, title);
|
if (title != null) {
|
||||||
|
moveToAnchor(link, title);
|
||||||
|
}
|
||||||
} else if (requestCode == WikivoyageShowPicturesDialogFragment.SHOW_PICTURES_CHANGED_REQUEST_CODE) {
|
} else if (requestCode == WikivoyageShowPicturesDialogFragment.SHOW_PICTURES_CHANGED_REQUEST_CODE) {
|
||||||
updateWebSettings();
|
updateWebSettings();
|
||||||
populateArticle();
|
populateArticle();
|
||||||
} else if (requestCode == WikivoyageArticleNavigationFragment.OPEN_ARTICLE_REQUEST_CODE) {
|
} else if (requestCode == WikivoyageArticleNavigationFragment.OPEN_ARTICLE_REQUEST_CODE) {
|
||||||
String tripId = data.getStringExtra(WikivoyageArticleNavigationFragment.ROUTE_ID_KEY);
|
TravelArticleIdentifier articleId = data.getParcelableExtra(WikivoyageArticleNavigationFragment.ARTICLE_ID_KEY);
|
||||||
String selectedLang = data.getStringExtra(WikivoyageArticleNavigationFragment.SELECTED_LANG_KEY);
|
String selectedLang = data.getStringExtra(WikivoyageArticleNavigationFragment.SELECTED_LANG_KEY);
|
||||||
if (!Algorithms.isEmpty(tripId) && !TextUtils.isEmpty(selectedLang)) {
|
if (articleId != null && !TextUtils.isEmpty(selectedLang)) {
|
||||||
this.routeId = tripId;
|
this.articleId = articleId;
|
||||||
this.selectedLang = selectedLang;
|
this.selectedLang = selectedLang;
|
||||||
populateArticle();
|
populateArticle();
|
||||||
}
|
}
|
||||||
|
@ -286,21 +289,21 @@ public class WikivoyageArticleDialogFragment extends WikiArticleBaseDialogFragme
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void populateArticle() {
|
protected void populateArticle() {
|
||||||
if (Algorithms.isEmpty(routeId) || langs == null) {
|
if (articleId == null || langs == null) {
|
||||||
Bundle args = getArguments();
|
Bundle args = getArguments();
|
||||||
if (args != null) {
|
if (args != null) {
|
||||||
routeId = args.getString(ROUTE_ID_KEY);
|
articleId = args.getParcelable(ARTICLE_ID_KEY);
|
||||||
langs = args.getStringArrayList(LANGS_KEY);
|
langs = args.getStringArrayList(LANGS_KEY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Algorithms.isEmpty(routeId) || langs == null || langs.isEmpty()) {
|
if (articleId == null || langs == null || langs.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (selectedLang == null) {
|
if (selectedLang == null) {
|
||||||
selectedLang = langs.get(0);
|
selectedLang = langs.get(0);
|
||||||
}
|
}
|
||||||
articleToolbarText.setText("");
|
articleToolbarText.setText("");
|
||||||
article = getMyApplication().getTravelHelper().getArticleById(routeId, selectedLang);
|
article = getMyApplication().getTravelHelper().getArticleById(articleId, selectedLang);
|
||||||
if (article == null) {
|
if (article == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -366,34 +369,34 @@ public class WikivoyageArticleDialogFragment extends WikiArticleBaseDialogFragme
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean showInstanceByTitle(@NonNull OsmandApplication app,
|
public static boolean showInstanceByTitle(@NonNull OsmandApplication app,
|
||||||
@NonNull FragmentManager fm,
|
@NonNull FragmentManager fm,
|
||||||
@NonNull String title,
|
@NonNull String title,
|
||||||
@NonNull String lang) {
|
@NonNull String lang) {
|
||||||
String articleId = app.getTravelHelper().getArticleId(title, lang);
|
TravelArticleIdentifier articleId = app.getTravelHelper().getArticleId(title, lang);
|
||||||
return showInstance(app, fm, articleId, lang);
|
return showInstance(app, fm, articleId, lang);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean showInstance(@NonNull OsmandApplication app,
|
public static boolean showInstance(@NonNull OsmandApplication app,
|
||||||
@NonNull FragmentManager fm,
|
@NonNull FragmentManager fm,
|
||||||
@NonNull String routeId,
|
@NonNull TravelArticleIdentifier articleId,
|
||||||
@Nullable String selectedLang) {
|
@Nullable String selectedLang) {
|
||||||
ArrayList<String> langs = app.getTravelHelper().getArticleLangs(routeId);
|
ArrayList<String> langs = app.getTravelHelper().getArticleLangs(articleId);
|
||||||
return showInstance(fm, routeId, langs, selectedLang);
|
return showInstance(fm, articleId, langs, selectedLang);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean showInstance(@NonNull FragmentManager fm,
|
public static boolean showInstance(@NonNull FragmentManager fm,
|
||||||
String routeId,
|
@NonNull TravelArticleIdentifier articleId,
|
||||||
@NonNull ArrayList<String> langs) {
|
@NonNull ArrayList<String> langs) {
|
||||||
return showInstance(fm, routeId, langs, null);
|
return showInstance(fm, articleId, langs, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean showInstance(@NonNull FragmentManager fm,
|
private static boolean showInstance(@NonNull FragmentManager fm,
|
||||||
String routeId,
|
@NonNull TravelArticleIdentifier articleId,
|
||||||
@NonNull ArrayList<String> langs,
|
@NonNull ArrayList<String> langs,
|
||||||
@Nullable String selectedLang) {
|
@Nullable String selectedLang) {
|
||||||
try {
|
try {
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
args.putString(ROUTE_ID_KEY, routeId);
|
args.putParcelable(ARTICLE_ID_KEY, articleId);
|
||||||
args.putStringArrayList(LANGS_KEY, langs);
|
args.putStringArrayList(LANGS_KEY, langs);
|
||||||
if (langs.contains(selectedLang)) {
|
if (langs.contains(selectedLang)) {
|
||||||
args.putString(SELECTED_LANG_KEY, selectedLang);
|
args.putString(SELECTED_LANG_KEY, selectedLang);
|
||||||
|
@ -416,7 +419,7 @@ public class WikivoyageArticleDialogFragment extends WikiArticleBaseDialogFragme
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
WikivoyageArticleNavigationFragment.showInstance(fm,
|
WikivoyageArticleNavigationFragment.showInstance(fm,
|
||||||
WikivoyageArticleDialogFragment.this, routeId, selectedLang);
|
WikivoyageArticleDialogFragment.this, articleId, selectedLang);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import net.osmand.plus.base.MenuBottomSheetDialogFragment;
|
||||||
import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem;
|
import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem;
|
||||||
import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem;
|
import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem;
|
||||||
import net.osmand.plus.wikivoyage.data.TravelArticle;
|
import net.osmand.plus.wikivoyage.data.TravelArticle;
|
||||||
|
import net.osmand.plus.wikivoyage.data.TravelArticle.TravelArticleIdentifier;
|
||||||
import net.osmand.plus.wikivoyage.data.WikivoyageSearchResult;
|
import net.osmand.plus.wikivoyage.data.WikivoyageSearchResult;
|
||||||
import net.osmand.util.Algorithms;
|
import net.osmand.util.Algorithms;
|
||||||
|
|
||||||
|
@ -38,14 +39,14 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr
|
||||||
|
|
||||||
public static final String TAG = WikivoyageArticleNavigationFragment.class.getSimpleName();
|
public static final String TAG = WikivoyageArticleNavigationFragment.class.getSimpleName();
|
||||||
|
|
||||||
public static final String ROUTE_ID_KEY = "route_id_key";
|
public static final String ARTICLE_ID_KEY = "article_id";
|
||||||
public static final String SELECTED_LANG_KEY = "selected_lang_key";
|
public static final String SELECTED_LANG_KEY = "selected_lang";
|
||||||
|
|
||||||
public static final int OPEN_ARTICLE_REQUEST_CODE = 2;
|
public static final int OPEN_ARTICLE_REQUEST_CODE = 2;
|
||||||
|
|
||||||
private static final long UNDEFINED = -1;
|
private static final long UNDEFINED = -1;
|
||||||
|
|
||||||
private String routeId = "";
|
private TravelArticleIdentifier articleId;
|
||||||
private String selectedLang;
|
private String selectedLang;
|
||||||
private TravelArticle article;
|
private TravelArticle article;
|
||||||
private List<String> parentsList;
|
private List<String> parentsList;
|
||||||
|
@ -61,26 +62,27 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
selectedLang = savedInstanceState.getString(SELECTED_LANG_KEY);
|
selectedLang = savedInstanceState.getString(SELECTED_LANG_KEY);
|
||||||
routeId = savedInstanceState.getString(ROUTE_ID_KEY);
|
articleId = savedInstanceState.getParcelable(ARTICLE_ID_KEY);
|
||||||
} else {
|
} else {
|
||||||
Bundle args = getArguments();
|
Bundle args = getArguments();
|
||||||
if (args != null) {
|
if (args != null) {
|
||||||
selectedLang = args.getString(SELECTED_LANG_KEY);
|
selectedLang = args.getString(SELECTED_LANG_KEY);
|
||||||
routeId = args.getString(ROUTE_ID_KEY);
|
articleId = args.getParcelable(ARTICLE_ID_KEY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Algorithms.isEmpty(routeId) || TextUtils.isEmpty(selectedLang)) {
|
if (articleId == null || TextUtils.isEmpty(selectedLang)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
article = getMyApplication().getTravelHelper().getArticleById(routeId, selectedLang);
|
article = getMyApplication().getTravelHelper().getArticleById(articleId, selectedLang);
|
||||||
if (article == null) {
|
if (article == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
parentsList = new ArrayList<>(Arrays.asList(article.getAggregatedPartOf().split(",")));
|
parentsList = new ArrayList<>(Arrays.asList(article.getAggregatedPartOf().split(",")));
|
||||||
|
|
||||||
Map<WikivoyageSearchResult, List<WikivoyageSearchResult>> navigationMap = getMyApplication().getTravelHelper().getNavigationMap(article);
|
Map<WikivoyageSearchResult, List<WikivoyageSearchResult>> navigationMap
|
||||||
|
= getMyApplication().getTravelHelper().getNavigationMap(article);
|
||||||
|
|
||||||
items.add(new TitleItem(getString(R.string.shared_string_navigation)));
|
items.add(new TitleItem(getString(R.string.shared_string_navigation)));
|
||||||
|
|
||||||
|
@ -102,7 +104,7 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr
|
||||||
public boolean onChildClick(ExpandableListView parent, View v,
|
public boolean onChildClick(ExpandableListView parent, View v,
|
||||||
int groupPosition, int childPosition, long id) {
|
int groupPosition, int childPosition, long id) {
|
||||||
WikivoyageSearchResult articleItem = listAdapter.getArticleItem(groupPosition, childPosition);
|
WikivoyageSearchResult articleItem = listAdapter.getArticleItem(groupPosition, childPosition);
|
||||||
sendResults(articleItem.getRouteId());
|
sendResults(articleItem.getArticleId());
|
||||||
dismiss();
|
dismiss();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -111,10 +113,10 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr
|
||||||
@Override
|
@Override
|
||||||
public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
|
public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
|
||||||
WikivoyageSearchResult articleItem = (WikivoyageSearchResult) listAdapter.getGroup(groupPosition);
|
WikivoyageSearchResult articleItem = (WikivoyageSearchResult) listAdapter.getGroup(groupPosition);
|
||||||
if (Algorithms.isEmpty(articleItem.getRouteId())) {
|
if (Algorithms.isEmpty(articleItem.getArticleRouteId())) {
|
||||||
Toast.makeText(getContext(), R.string.wiki_article_not_found, Toast.LENGTH_LONG).show();
|
Toast.makeText(getContext(), R.string.wiki_article_not_found, Toast.LENGTH_LONG).show();
|
||||||
} else {
|
} else {
|
||||||
sendResults(articleItem.getRouteId());
|
sendResults(articleItem.getArticleId());
|
||||||
dismiss();
|
dismiss();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -134,7 +136,7 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr
|
||||||
@Override
|
@Override
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
outState.putString(ROUTE_ID_KEY, routeId);
|
outState.putParcelable(ARTICLE_ID_KEY, articleId);
|
||||||
outState.putString(SELECTED_LANG_KEY, selectedLang);
|
outState.putString(SELECTED_LANG_KEY, selectedLang);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,17 +150,17 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr
|
||||||
return nightMode ? R.color.wikivoyage_bottom_bar_bg_dark : R.color.list_background_color_light;
|
return nightMode ? R.color.wikivoyage_bottom_bar_bg_dark : R.color.list_background_color_light;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendResults(String routeId) {
|
private void sendResults(TravelArticleIdentifier articleId) {
|
||||||
WikivoyageArticleDialogFragment.showInstance(getMyApplication(), getFragmentManager(), routeId, selectedLang);
|
WikivoyageArticleDialogFragment.showInstance(getMyApplication(), getFragmentManager(), articleId, selectedLang);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean showInstance(@NonNull FragmentManager fm,
|
public static boolean showInstance(@NonNull FragmentManager fm,
|
||||||
@Nullable Fragment targetFragment,
|
@Nullable Fragment targetFragment,
|
||||||
String routeId,
|
@NonNull TravelArticleIdentifier articleId,
|
||||||
@NonNull String selectedLang) {
|
@NonNull String selectedLang) {
|
||||||
try {
|
try {
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
args.putString(ROUTE_ID_KEY, routeId);
|
args.putParcelable(ARTICLE_ID_KEY, articleId);
|
||||||
args.putString(SELECTED_LANG_KEY, selectedLang);
|
args.putString(SELECTED_LANG_KEY, selectedLang);
|
||||||
WikivoyageArticleNavigationFragment fragment = new WikivoyageArticleNavigationFragment();
|
WikivoyageArticleNavigationFragment fragment = new WikivoyageArticleNavigationFragment();
|
||||||
if (targetFragment != null) {
|
if (targetFragment != null) {
|
||||||
|
@ -204,7 +206,7 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getChild(int groupPosition, int childPosititon) {
|
public Object getChild(int groupPosition, int childPosititon) {
|
||||||
return getArticleItem(groupPosition, childPosititon).getArticleTitles().get(0);
|
return getArticleItem(groupPosition, childPosititon).getArticleTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -236,8 +238,8 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr
|
||||||
public View getChildView(int groupPosition, final int childPosition,
|
public View getChildView(int groupPosition, final int childPosition,
|
||||||
boolean isLastChild, View convertView, ViewGroup parent) {
|
boolean isLastChild, View convertView, ViewGroup parent) {
|
||||||
WikivoyageSearchResult articleItem = getArticleItem(groupPosition, childPosition);
|
WikivoyageSearchResult articleItem = getArticleItem(groupPosition, childPosition);
|
||||||
String childTitle = articleItem.getArticleTitles().get(0);
|
String childTitle = articleItem.getArticleTitle();
|
||||||
boolean selected = Algorithms.stringsEqual(routeId, articleItem.getRouteId()) || parentsList.contains(childTitle);
|
boolean selected = articleItem.getArticleId().equals(articleId) || parentsList.contains(childTitle);
|
||||||
|
|
||||||
if (convertView == null) {
|
if (convertView == null) {
|
||||||
convertView = LayoutInflater.from(context)
|
convertView = LayoutInflater.from(context)
|
||||||
|
@ -263,7 +265,7 @@ public class WikivoyageArticleNavigationFragment extends MenuBottomSheetDialogFr
|
||||||
@Override
|
@Override
|
||||||
public View getGroupView(final int groupPosition, final boolean isExpanded,
|
public View getGroupView(final int groupPosition, final boolean isExpanded,
|
||||||
View convertView, ViewGroup parent) {
|
View convertView, ViewGroup parent) {
|
||||||
String groupTitle = ((WikivoyageSearchResult) getGroup(groupPosition)).getArticleTitles().get(0);
|
String groupTitle = ((WikivoyageSearchResult) getGroup(groupPosition)).getArticleTitle();
|
||||||
boolean selected = parentsList.contains(groupTitle) || article.getTitle().equals(groupTitle);
|
boolean selected = parentsList.contains(groupTitle) || article.getTitle().equals(groupTitle);
|
||||||
if (convertView == null) {
|
if (convertView == null) {
|
||||||
convertView = LayoutInflater.from(context)
|
convertView = LayoutInflater.from(context)
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package net.osmand.plus.wikivoyage.data;
|
package net.osmand.plus.wikivoyage.data;
|
||||||
|
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
@ -7,13 +9,21 @@ import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.Size;
|
import androidx.annotation.Size;
|
||||||
|
|
||||||
import net.osmand.GPXUtilities.GPXFile;
|
import net.osmand.GPXUtilities.GPXFile;
|
||||||
|
import net.osmand.Location;
|
||||||
|
import net.osmand.aidl.search.SearchResult;
|
||||||
|
import net.osmand.data.LatLon;
|
||||||
|
import net.osmand.util.Algorithms;
|
||||||
|
import net.osmand.util.MapUtils;
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.Hex;
|
import org.apache.commons.codec.binary.Hex;
|
||||||
import org.apache.commons.codec.digest.DigestUtils;
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public class TravelArticle {
|
public class TravelArticle {
|
||||||
|
|
||||||
|
@ -21,20 +31,30 @@ public class TravelArticle {
|
||||||
private static final String THUMB_PREFIX = "320px-";
|
private static final String THUMB_PREFIX = "320px-";
|
||||||
private static final String REGULAR_PREFIX = "1280px-";//1280, 1024, 800
|
private static final String REGULAR_PREFIX = "1280px-";//1280, 1024, 800
|
||||||
|
|
||||||
|
File file;
|
||||||
String title;
|
String title;
|
||||||
String content;
|
String content;
|
||||||
String isPartOf;
|
String isPartOf;
|
||||||
double lat;
|
double lat = Double.NaN;
|
||||||
double lon;
|
double lon = Double.NaN;
|
||||||
String imageTitle;
|
String imageTitle;
|
||||||
GPXFile gpxFile;
|
GPXFile gpxFile;
|
||||||
String routeId;
|
String routeId;
|
||||||
|
String routeSource;
|
||||||
long originalId;
|
long originalId;
|
||||||
String lang;
|
String lang;
|
||||||
String contentsJson;
|
String contentsJson;
|
||||||
String aggregatedPartOf;
|
String aggregatedPartOf;
|
||||||
String fullContent;
|
String fullContent;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public TravelArticleIdentifier generateIdentifier() {
|
||||||
|
return new TravelArticleIdentifier(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getFile() {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
public String getTitle() {
|
public String getTitle() {
|
||||||
return title;
|
return title;
|
||||||
|
@ -68,6 +88,10 @@ public class TravelArticle {
|
||||||
return routeId;
|
return routeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getRouteSource() {
|
||||||
|
return routeSource;
|
||||||
|
}
|
||||||
|
|
||||||
public long getOriginalId() {
|
public long getOriginalId() {
|
||||||
return originalId;
|
return originalId;
|
||||||
}
|
}
|
||||||
|
@ -127,4 +151,92 @@ public class TravelArticle {
|
||||||
String md5 = new String(Hex.encodeHex(DigestUtils.md5(s)));
|
String md5 = new String(Hex.encodeHex(DigestUtils.md5(s)));
|
||||||
return new String[]{md5.substring(0, 1), md5.substring(0, 2)};
|
return new String[]{md5.substring(0, 1), md5.substring(0, 2)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class TravelArticleIdentifier implements Parcelable {
|
||||||
|
@Nullable File file;
|
||||||
|
double lat;
|
||||||
|
double lon;
|
||||||
|
@Nullable String title;
|
||||||
|
@Nullable String routeId;
|
||||||
|
@Nullable String routeSource;
|
||||||
|
|
||||||
|
public static final Creator<TravelArticleIdentifier> CREATOR = new Creator<TravelArticleIdentifier>() {
|
||||||
|
@Override
|
||||||
|
public TravelArticleIdentifier createFromParcel(Parcel in) {
|
||||||
|
return new TravelArticleIdentifier(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TravelArticleIdentifier[] newArray(int size) {
|
||||||
|
return new TravelArticleIdentifier[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private TravelArticleIdentifier(@NonNull Parcel in) {
|
||||||
|
readFromParcel(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TravelArticleIdentifier(@NonNull TravelArticle article) {
|
||||||
|
file = article.file;
|
||||||
|
lat = article.lat;
|
||||||
|
lon = article.lon;
|
||||||
|
title = article.title;
|
||||||
|
routeId = article.routeId;
|
||||||
|
routeSource = article.routeSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel out, int flags) {
|
||||||
|
out.writeDouble(lat);
|
||||||
|
out.writeDouble(lon);
|
||||||
|
out.writeString(title);
|
||||||
|
out.writeString(routeId);
|
||||||
|
out.writeString(routeSource);
|
||||||
|
out.writeString(file != null ? file.getAbsolutePath() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readFromParcel(Parcel in) {
|
||||||
|
lat = in.readDouble();
|
||||||
|
lon = in.readDouble();
|
||||||
|
title = in.readString();
|
||||||
|
routeId = in.readString();
|
||||||
|
routeSource = in.readString();
|
||||||
|
String filePath = in.readString();
|
||||||
|
if (!Algorithms.isEmpty(filePath)) {
|
||||||
|
file = new File(filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
TravelArticleIdentifier that = (TravelArticleIdentifier) o;
|
||||||
|
return areLatLonEqual(that.lat, that.lon, lat, lon) &&
|
||||||
|
Algorithms.objectEquals(file, that.file) &&
|
||||||
|
Algorithms.stringsEqual(title, that.title) &&
|
||||||
|
Algorithms.stringsEqual(routeId, that.routeId) &&
|
||||||
|
Algorithms.stringsEqual(routeSource, that.routeSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Algorithms.hash(file, lat, lon, title, routeId, routeSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean areLatLonEqual(double lat1, double lon1, double lat2, double lon2) {
|
||||||
|
boolean latEqual = (Double.isNaN(lat1) && Double.isNaN(lat2)) || Math.abs(lat1 - lat2) < 0.00001;
|
||||||
|
boolean lonEqual = (Double.isNaN(lon1) && Double.isNaN(lon2)) || Math.abs(lon1 - lon2) < 0.00001;
|
||||||
|
return latEqual && lonEqual;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,10 +16,12 @@ import net.osmand.OsmAndCollator;
|
||||||
import net.osmand.PlatformUtil;
|
import net.osmand.PlatformUtil;
|
||||||
import net.osmand.data.Amenity;
|
import net.osmand.data.Amenity;
|
||||||
import net.osmand.data.LatLon;
|
import net.osmand.data.LatLon;
|
||||||
|
import net.osmand.data.QuadRect;
|
||||||
import net.osmand.plus.OsmandApplication;
|
import net.osmand.plus.OsmandApplication;
|
||||||
import net.osmand.plus.R;
|
import net.osmand.plus.R;
|
||||||
import net.osmand.plus.api.SQLiteAPI.SQLiteConnection;
|
import net.osmand.plus.api.SQLiteAPI.SQLiteConnection;
|
||||||
import net.osmand.plus.api.SQLiteAPI.SQLiteCursor;
|
import net.osmand.plus.api.SQLiteAPI.SQLiteCursor;
|
||||||
|
import net.osmand.plus.wikivoyage.data.TravelArticle.TravelArticleIdentifier;
|
||||||
import net.osmand.util.Algorithms;
|
import net.osmand.util.Algorithms;
|
||||||
import net.osmand.util.MapUtils;
|
import net.osmand.util.MapUtils;
|
||||||
|
|
||||||
|
@ -251,12 +253,15 @@ public class TravelDbHelper implements TravelHelper {
|
||||||
if (cursor != null) {
|
if (cursor != null) {
|
||||||
if (cursor.moveToFirst()) {
|
if (cursor.moveToFirst()) {
|
||||||
do {
|
do {
|
||||||
WikivoyageSearchResult rs = new WikivoyageSearchResult();
|
String routeId = cursor.getLong(0) + "";
|
||||||
rs.routeId = cursor.getLong(0) + "";
|
String articleTitle = cursor.getString(1);
|
||||||
rs.articleTitles.add(cursor.getString(1));
|
String lang = cursor.getString(2);
|
||||||
rs.langs.add(cursor.getString(2));
|
String isPartOf = cursor.getString(3);
|
||||||
rs.isPartOf.add(cursor.getString(3));
|
String imageTitle = cursor.getString(4);
|
||||||
rs.imageTitle = cursor.getString(4);
|
List<String> langs = new ArrayList<>();
|
||||||
|
langs.add(lang);
|
||||||
|
WikivoyageSearchResult rs = new WikivoyageSearchResult(routeId, articleTitle,
|
||||||
|
isPartOf, imageTitle, langs);
|
||||||
res.add(rs);
|
res.add(rs);
|
||||||
} while (cursor.moveToNext());
|
} while (cursor.moveToNext());
|
||||||
}
|
}
|
||||||
|
@ -392,12 +397,12 @@ public class TravelDbHelper implements TravelHelper {
|
||||||
Collections.sort(list, new Comparator<WikivoyageSearchResult>() {
|
Collections.sort(list, new Comparator<WikivoyageSearchResult>() {
|
||||||
@Override
|
@Override
|
||||||
public int compare(WikivoyageSearchResult o1, WikivoyageSearchResult o2) {
|
public int compare(WikivoyageSearchResult o1, WikivoyageSearchResult o2) {
|
||||||
boolean c1 = CollatorStringMatcher.cmatches(collator, searchQuery, o1.articleTitles.get(0),
|
boolean c1 = CollatorStringMatcher.cmatches(collator, searchQuery, o1.getArticleTitle(),
|
||||||
StringMatcherMode.CHECK_ONLY_STARTS_WITH);
|
StringMatcherMode.CHECK_ONLY_STARTS_WITH);
|
||||||
boolean c2 = CollatorStringMatcher.cmatches(collator, searchQuery, o2.articleTitles.get(0),
|
boolean c2 = CollatorStringMatcher.cmatches(collator, searchQuery, o2.getArticleTitle(),
|
||||||
StringMatcherMode.CHECK_ONLY_STARTS_WITH);
|
StringMatcherMode.CHECK_ONLY_STARTS_WITH);
|
||||||
if (c1 == c2) {
|
if (c1 == c2) {
|
||||||
return collator.compare(o1.articleTitles.get(0), o2.articleTitles.get(0));
|
return collator.compare(o1.getArticleTitle(), o2.getArticleTitle());
|
||||||
} else if (c1) {
|
} else if (c1) {
|
||||||
return -1;
|
return -1;
|
||||||
} else if (c2) {
|
} else if (c2) {
|
||||||
|
@ -428,28 +433,27 @@ public class TravelDbHelper implements TravelHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Collection<WikivoyageSearchResult> groupSearchResultsByRouteId(List<WikivoyageSearchResult> res) {
|
private Collection<WikivoyageSearchResult> groupSearchResultsByRouteId(List<WikivoyageSearchResult> res) {
|
||||||
String baseLng = application.getLanguage();
|
String baseLng = application.getLanguage();
|
||||||
Map<String, WikivoyageSearchResult> wikivoyage = new HashMap<>();
|
Map<String, WikivoyageSearchResult> wikivoyage = new HashMap<>();
|
||||||
for (WikivoyageSearchResult rs : res) {
|
for (WikivoyageSearchResult rs : res) {
|
||||||
WikivoyageSearchResult prev = wikivoyage.get(rs.routeId);
|
WikivoyageSearchResult prev = wikivoyage.get(rs.getArticleRouteId());
|
||||||
if (prev != null) {
|
if (prev != null) {
|
||||||
int insInd = prev.langs.size();
|
boolean matchLang = false;
|
||||||
if (rs.langs.get(0).equals(baseLng)) {
|
if (rs.langs.get(0).equals(baseLng)) {
|
||||||
insInd = 0;
|
matchLang = true;
|
||||||
} else if (rs.langs.get(0).equals("en")) {
|
} else if (rs.langs.get(0).equals("en")) {
|
||||||
if (!prev.langs.get(0).equals(baseLng)) {
|
if (!prev.langs.get(0).equals(baseLng)) {
|
||||||
insInd = 0;
|
matchLang = true;
|
||||||
} else {
|
|
||||||
insInd = 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
prev.articleTitles.add(insInd, rs.articleTitles.get(0));
|
if (matchLang) {
|
||||||
prev.langs.add(insInd, rs.langs.get(0));
|
prev.articleId.title = rs.getArticleTitle();
|
||||||
prev.isPartOf.add(insInd, rs.isPartOf.get(0));
|
prev.isPartOf = rs.getIsPartOf();
|
||||||
|
}
|
||||||
|
prev.langs.add(matchLang ? 0 : 1, rs.langs.get(0));
|
||||||
} else {
|
} else {
|
||||||
wikivoyage.put(rs.routeId, rs);
|
wikivoyage.put(rs.getArticleRouteId(), rs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return wikivoyage.values();
|
return wikivoyage.values();
|
||||||
|
@ -457,12 +461,11 @@ public class TravelDbHelper implements TravelHelper {
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public LinkedHashMap<WikivoyageSearchResult, List<WikivoyageSearchResult>> getNavigationMap(
|
public Map<WikivoyageSearchResult, List<WikivoyageSearchResult>> getNavigationMap(@NonNull final TravelArticle article) {
|
||||||
@NonNull final TravelArticle article) {
|
|
||||||
String lang = article.getLang();
|
String lang = article.getLang();
|
||||||
String title = article.getTitle();
|
String title = article.getTitle();
|
||||||
if (TextUtils.isEmpty(lang) || TextUtils.isEmpty(title)) {
|
if (TextUtils.isEmpty(lang) || TextUtils.isEmpty(title)) {
|
||||||
return new LinkedHashMap<>();
|
return Collections.emptyMap();
|
||||||
}
|
}
|
||||||
String[] parts = null;
|
String[] parts = null;
|
||||||
if (!TextUtils.isEmpty(article.getAggregatedPartOf())) {
|
if (!TextUtils.isEmpty(article.getAggregatedPartOf())) {
|
||||||
|
@ -504,20 +507,20 @@ public class TravelDbHelper implements TravelHelper {
|
||||||
SQLiteCursor cursor = conn.rawQuery(query.toString(), params.toArray(new String[0]));
|
SQLiteCursor cursor = conn.rawQuery(query.toString(), params.toArray(new String[0]));
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
do {
|
do {
|
||||||
WikivoyageSearchResult rs = new WikivoyageSearchResult();
|
String routeId = cursor.getLong(0) + "";
|
||||||
rs.routeId = cursor.getLong(0) + "";
|
String articleTitle = cursor.getString(1);
|
||||||
rs.articleTitles.add(cursor.getString(1));
|
String articleLang = cursor.getString(2);
|
||||||
rs.langs.add(cursor.getString(2));
|
String isPartOf = cursor.getString(3);
|
||||||
rs.isPartOf.add(cursor.getString(3));
|
WikivoyageSearchResult rs = new WikivoyageSearchResult(routeId, articleTitle,
|
||||||
List<WikivoyageSearchResult> l = navMap.get(rs.isPartOf.get(0));
|
isPartOf, null, Collections.singletonList(articleLang));
|
||||||
|
List<WikivoyageSearchResult> l = navMap.get(rs.isPartOf);
|
||||||
if (l == null) {
|
if (l == null) {
|
||||||
l = new ArrayList<>();
|
l = new ArrayList<>();
|
||||||
navMap.put(rs.isPartOf.get(0), l);
|
navMap.put(rs.isPartOf, l);
|
||||||
}
|
}
|
||||||
l.add(rs);
|
l.add(rs);
|
||||||
String key = rs.getArticleTitles().get(0);
|
if (headers != null && headers.contains(articleTitle)) {
|
||||||
if (headers != null && headers.contains(key)) {
|
headerObjs.put(articleTitle, rs);
|
||||||
headerObjs.put(key, rs);
|
|
||||||
}
|
}
|
||||||
} while (cursor.moveToNext());
|
} while (cursor.moveToNext());
|
||||||
}
|
}
|
||||||
|
@ -533,12 +536,10 @@ public class TravelDbHelper implements TravelHelper {
|
||||||
Collections.sort(results, new Comparator<WikivoyageSearchResult>() {
|
Collections.sort(results, new Comparator<WikivoyageSearchResult>() {
|
||||||
@Override
|
@Override
|
||||||
public int compare(WikivoyageSearchResult o1, WikivoyageSearchResult o2) {
|
public int compare(WikivoyageSearchResult o1, WikivoyageSearchResult o2) {
|
||||||
return collator.compare(o1.articleTitles.get(0), o2.articleTitles.get(0));
|
return collator.compare(o1.getArticleTitle(), o2.getArticleTitle());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
WikivoyageSearchResult emptyResult = new WikivoyageSearchResult();
|
WikivoyageSearchResult emptyResult = new WikivoyageSearchResult("", header, null, null, null);
|
||||||
emptyResult.articleTitles.add(header);
|
|
||||||
emptyResult.routeId = "";
|
|
||||||
searchResult = searchResult != null ? searchResult : emptyResult;
|
searchResult = searchResult != null ? searchResult : emptyResult;
|
||||||
res.put(searchResult, results);
|
res.put(searchResult, results);
|
||||||
}
|
}
|
||||||
|
@ -548,9 +549,10 @@ public class TravelDbHelper implements TravelHelper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public TravelArticle getArticleById(@NonNull String routeId, @NonNull String lang) {
|
public TravelArticle getArticleById(@NonNull TravelArticleIdentifier articleId, @NonNull String lang) {
|
||||||
TravelArticle res = null;
|
TravelArticle res = null;
|
||||||
SQLiteConnection conn = openConnection();
|
SQLiteConnection conn = openConnection();
|
||||||
|
String routeId = articleId.routeId;
|
||||||
if (conn != null && !Algorithms.isEmpty(routeId)) {
|
if (conn != null && !Algorithms.isEmpty(routeId)) {
|
||||||
SQLiteCursor cursor = conn.rawQuery(ARTICLES_TABLE_SELECT + " WHERE " + ARTICLES_COL_TRIP_ID + " = ? AND "
|
SQLiteCursor cursor = conn.rawQuery(ARTICLES_TABLE_SELECT + " WHERE " + ARTICLES_COL_TRIP_ID + " = ? AND "
|
||||||
+ ARTICLES_COL_LANG + " = ?", new String[] { routeId, lang });
|
+ ARTICLES_COL_LANG + " = ?", new String[] { routeId, lang });
|
||||||
|
@ -564,9 +566,21 @@ public class TravelDbHelper implements TravelHelper {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public TravelArticle getArticleByTitle(@NonNull String title, @NonNull String lang) {
|
@Override
|
||||||
|
public TravelArticle getArticleByTitle(@NonNull final String title, @NonNull final String lang) {
|
||||||
|
return getArticleByTitle(title, new QuadRect(), lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public TravelArticle getArticleByTitle(@NonNull final String title, @NonNull LatLon latLon, @NonNull final String lang) {
|
||||||
|
return getArticleByTitle(title, new QuadRect(), lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public TravelArticle getArticleByTitle(@NonNull final String title, @NonNull QuadRect rect, @NonNull final String lang) {
|
||||||
TravelArticle res = null;
|
TravelArticle res = null;
|
||||||
SQLiteConnection conn = openConnection();
|
SQLiteConnection conn = openConnection();
|
||||||
if (conn != null) {
|
if (conn != null) {
|
||||||
|
@ -582,33 +596,32 @@ public class TravelDbHelper implements TravelHelper {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public String getArticleId(@NonNull String title, @NonNull String lang) {
|
public TravelArticleIdentifier getArticleId(@NonNull String title, @NonNull String lang) {
|
||||||
String res = "";
|
TravelArticle article = null;
|
||||||
SQLiteConnection conn = openConnection();
|
SQLiteConnection conn = openConnection();
|
||||||
if (conn != null) {
|
if (conn != null) {
|
||||||
SQLiteCursor cursor = conn.rawQuery("SELECT " + ARTICLES_COL_TRIP_ID + " FROM "
|
SQLiteCursor cursor = conn.rawQuery(ARTICLES_TABLE_SELECT + " WHERE " + ARTICLES_COL_TITLE + " = ? AND "
|
||||||
+ ARTICLES_TABLE_NAME + " WHERE " + ARTICLES_COL_TITLE + " = ? AND "
|
|
||||||
+ ARTICLES_COL_LANG + " = ?", new String[]{title, lang});
|
+ ARTICLES_COL_LANG + " = ?", new String[]{title, lang});
|
||||||
if (cursor != null) {
|
if (cursor != null) {
|
||||||
if (cursor.moveToFirst()) {
|
if (cursor.moveToFirst()) {
|
||||||
res = cursor.getLong(0) + "";
|
article = readArticle(cursor);
|
||||||
}
|
}
|
||||||
cursor.close();
|
cursor.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res;
|
return article != null ? article.generateIdentifier() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public ArrayList<String> getArticleLangs(@NonNull String routeId) {
|
public ArrayList<String> getArticleLangs(@NonNull TravelArticleIdentifier articleId) {
|
||||||
ArrayList<String> res = new ArrayList<>();
|
ArrayList<String> res = new ArrayList<>();
|
||||||
SQLiteConnection conn = openConnection();
|
SQLiteConnection conn = openConnection();
|
||||||
if (conn != null) {
|
if (conn != null) {
|
||||||
SQLiteCursor cursor = conn.rawQuery("SELECT " + ARTICLES_COL_LANG + " FROM " + ARTICLES_TABLE_NAME
|
SQLiteCursor cursor = conn.rawQuery("SELECT " + ARTICLES_COL_LANG + " FROM " + ARTICLES_TABLE_NAME
|
||||||
+ " WHERE " + ARTICLES_COL_TRIP_ID + " = ?", new String[]{routeId});
|
+ " WHERE " + ARTICLES_COL_TRIP_ID + " = ?", new String[]{articleId.routeId});
|
||||||
if (cursor != null) {
|
if (cursor != null) {
|
||||||
if (cursor.moveToFirst()) {
|
if (cursor.moveToFirst()) {
|
||||||
String baseLang = application.getLanguage();
|
String baseLang = application.getLanguage();
|
||||||
|
|
|
@ -3,6 +3,10 @@ package net.osmand.plus.wikivoyage.data;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import net.osmand.data.LatLon;
|
||||||
|
import net.osmand.data.QuadRect;
|
||||||
|
import net.osmand.plus.wikivoyage.data.TravelArticle.TravelArticleIdentifier;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -28,16 +32,22 @@ public interface TravelHelper {
|
||||||
Map<WikivoyageSearchResult, List<WikivoyageSearchResult>> getNavigationMap(@NonNull final TravelArticle article);
|
Map<WikivoyageSearchResult, List<WikivoyageSearchResult>> getNavigationMap(@NonNull final TravelArticle article);
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
TravelArticle getArticleById(@NonNull String routeId, @NonNull String lang);
|
TravelArticle getArticleById(@NonNull TravelArticleIdentifier articleId, @NonNull String lang);
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
TravelArticle getArticleByTitle(@NonNull String title, @NonNull String lang);
|
public TravelArticle getArticleByTitle(@NonNull final String title, @NonNull final String lang);
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public TravelArticle getArticleByTitle(@NonNull final String title, @NonNull LatLon latLon, @NonNull final String lang);
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
TravelArticle getArticleByTitle(@NonNull String title, @NonNull QuadRect rect, @NonNull String lang);
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
TravelArticleIdentifier getArticleId(@NonNull String title, @NonNull String lang);
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
String getArticleId(@NonNull String title, @NonNull String lang);
|
ArrayList<String> getArticleLangs(@NonNull TravelArticleIdentifier articleId);
|
||||||
|
|
||||||
@NonNull
|
|
||||||
ArrayList<String> getArticleLangs(@NonNull String routeId);
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
String getGPXName(@NonNull final TravelArticle article);
|
String getGPXName(@NonNull final TravelArticle article);
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
package net.osmand.plus.wikivoyage.data;
|
package net.osmand.plus.wikivoyage.data;
|
||||||
|
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import net.osmand.Collator;
|
import net.osmand.Collator;
|
||||||
import net.osmand.CollatorStringMatcher;
|
|
||||||
import net.osmand.GPXUtilities;
|
import net.osmand.GPXUtilities;
|
||||||
import net.osmand.GPXUtilities.GPXFile;
|
import net.osmand.GPXUtilities.GPXFile;
|
||||||
import net.osmand.IndexConstants;
|
import net.osmand.IndexConstants;
|
||||||
|
@ -13,10 +14,13 @@ import net.osmand.PlatformUtil;
|
||||||
import net.osmand.ResultMatcher;
|
import net.osmand.ResultMatcher;
|
||||||
import net.osmand.binary.BinaryMapIndexReader;
|
import net.osmand.binary.BinaryMapIndexReader;
|
||||||
import net.osmand.binary.BinaryMapIndexReader.SearchPoiTypeFilter;
|
import net.osmand.binary.BinaryMapIndexReader.SearchPoiTypeFilter;
|
||||||
|
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
|
||||||
import net.osmand.data.Amenity;
|
import net.osmand.data.Amenity;
|
||||||
import net.osmand.data.LatLon;
|
import net.osmand.data.LatLon;
|
||||||
|
import net.osmand.data.QuadRect;
|
||||||
import net.osmand.osm.PoiCategory;
|
import net.osmand.osm.PoiCategory;
|
||||||
import net.osmand.plus.OsmandApplication;
|
import net.osmand.plus.OsmandApplication;
|
||||||
|
import net.osmand.plus.wikivoyage.data.TravelArticle.TravelArticleIdentifier;
|
||||||
import net.osmand.util.Algorithms;
|
import net.osmand.util.Algorithms;
|
||||||
import net.osmand.util.MapUtils;
|
import net.osmand.util.MapUtils;
|
||||||
|
|
||||||
|
@ -25,35 +29,40 @@ import org.apache.commons.logging.Log;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import static net.osmand.CollatorStringMatcher.StringMatcherMode.CHECK_EQUALS_FROM_SPACE;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
public class TravelObfHelper implements TravelHelper {
|
public class TravelObfHelper implements TravelHelper {
|
||||||
|
|
||||||
private static final Log LOG = PlatformUtil.getLog(TravelObfHelper.class);
|
private static final Log LOG = PlatformUtil.getLog(TravelObfHelper.class);
|
||||||
|
|
||||||
private static final String WORLD_WIKIVOYAGE_FILE_NAME = "World_wikivoyage.travel.obf";
|
private static final String WORLD_WIKIVOYAGE_FILE_NAME = "World_wikivoyage.travel.obf";
|
||||||
private static final String ROUTE_ARTICLE = "route_article";
|
public static final String ROUTE_ARTICLE = "route_article";
|
||||||
private static final int SEARCH_RADIUS = 100000;
|
public static final int POPULAR_ARTICLES_SEARCH_RADIUS = 100000;
|
||||||
|
public static final int ARTICLE_SEARCH_RADIUS = 50000;
|
||||||
|
public static final int MAX_POPULAR_ARTICLES_COUNT = 100;
|
||||||
|
|
||||||
private final OsmandApplication app;
|
private final OsmandApplication app;
|
||||||
private final Collator collator;
|
private final Collator collator;
|
||||||
|
|
||||||
private List<TravelArticle> popularArticles = new ArrayList<>();
|
private List<TravelArticle> popularArticles = new ArrayList<>();
|
||||||
private final Map<String, TravelArticle> cachedArticles;
|
private Map<TravelArticleIdentifier, Map<String, TravelArticle>> cachedArticles = new ConcurrentHashMap<>();
|
||||||
private final TravelLocalDataHelper localDataHelper;
|
private final TravelLocalDataHelper localDataHelper;
|
||||||
|
|
||||||
public TravelObfHelper(OsmandApplication app) {
|
public TravelObfHelper(OsmandApplication app) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
collator = OsmAndCollator.primaryCollator();
|
collator = OsmAndCollator.primaryCollator();
|
||||||
localDataHelper = new TravelLocalDataHelper(app);
|
localDataHelper = new TravelLocalDataHelper(app);
|
||||||
cachedArticles = new HashMap<>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -73,22 +82,23 @@ public class TravelObfHelper implements TravelHelper {
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public List<TravelArticle> loadPopularArticles() {
|
public List<TravelArticle> loadPopularArticles() {
|
||||||
String language = app.getLanguage();
|
String lang = app.getLanguage();
|
||||||
List<TravelArticle> popularArticles = new ArrayList<>();
|
List<TravelArticle> popularArticles = new ArrayList<>();
|
||||||
for (BinaryMapIndexReader travelBookReader : getTravelBookReaders()) {
|
for (BinaryMapIndexReader reader : getReaders()) {
|
||||||
try {
|
try {
|
||||||
final LatLon location = app.getMapViewTrackingUtilities().getMapLocation();
|
final LatLon location = app.getMapViewTrackingUtilities().getMapLocation();
|
||||||
BinaryMapIndexReader.SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(
|
SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(
|
||||||
location, SEARCH_RADIUS, -1, getSearchRouteArticleFilter(), null);
|
location, POPULAR_ARTICLES_SEARCH_RADIUS, -1, getSearchRouteArticleFilter(), null);
|
||||||
List<Amenity> amenities = travelBookReader.searchPoi(req);
|
List<Amenity> amenities = reader.searchPoi(req);
|
||||||
if (amenities.size() > 0) {
|
if (amenities.size() > 0) {
|
||||||
for (Amenity a : amenities) {
|
for (Amenity amenity : amenities) {
|
||||||
if (!Algorithms.isEmpty(a.getName(language))) {
|
if (!Algorithms.isEmpty(amenity.getName(lang))) {
|
||||||
TravelArticle article = readArticle(a, language);
|
TravelArticle article = cacheTravelArticles(reader.getFile(), amenity, lang);
|
||||||
popularArticles.add(article);
|
if (article != null) {
|
||||||
cachedArticles.put(article.routeId, article);
|
popularArticles.add(article);
|
||||||
if (popularArticles.size() >= 100) {
|
if (popularArticles.size() >= MAX_POPULAR_ARTICLES_COUNT) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -104,13 +114,25 @@ public class TravelObfHelper implements TravelHelper {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.error(e.getMessage());
|
LOG.error(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.popularArticles = popularArticles;
|
this.popularArticles = popularArticles;
|
||||||
return popularArticles;
|
return popularArticles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private TravelArticle cacheTravelArticles(File file, Amenity amenity, String lang) {
|
||||||
|
TravelArticle article = null;
|
||||||
|
Map<String, TravelArticle> articles = readArticles(file, amenity);
|
||||||
|
if (!Algorithms.isEmpty(articles)) {
|
||||||
|
TravelArticleIdentifier newArticleId = articles.values().iterator().next().generateIdentifier();
|
||||||
|
cachedArticles.put(newArticleId, articles);
|
||||||
|
article = getCachedArticle(newArticleId, lang);
|
||||||
|
}
|
||||||
|
return article;
|
||||||
|
}
|
||||||
|
|
||||||
SearchPoiTypeFilter getSearchRouteArticleFilter() {
|
SearchPoiTypeFilter getSearchRouteArticleFilter() {
|
||||||
return new SearchPoiTypeFilter() {
|
return new SearchPoiTypeFilter() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -125,23 +147,29 @@ public class TravelObfHelper implements TravelHelper {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private TravelArticle readArticle(@NonNull Amenity amenity, @Nullable String lang) {
|
@NonNull
|
||||||
TravelArticle res = new TravelArticle();
|
private Map<String, TravelArticle> readArticles(@NonNull File file, @NonNull Amenity amenity) {
|
||||||
String title = Algorithms.isEmpty(amenity.getName(lang)) ? amenity.getName() : amenity.getName(lang);
|
Map<String, TravelArticle> articles = new HashMap<>();
|
||||||
if (Algorithms.isEmpty(title)) {
|
Set<String> langs = getLanguages(amenity);
|
||||||
Map<String, String> namesMap = amenity.getNamesMap(true);
|
for (String lang : langs) {
|
||||||
if (!namesMap.isEmpty()) {
|
articles.put(lang, readArticle(file, amenity, lang));
|
||||||
lang = namesMap.keySet().iterator().next();
|
|
||||||
title = amenity.getName(lang);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
res.title = title;
|
return articles;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private TravelArticle readArticle(@NonNull File file, @NonNull Amenity amenity, @Nullable String lang) {
|
||||||
|
TravelArticle res = new TravelArticle();
|
||||||
|
res.file = file;
|
||||||
|
String title = amenity.getName(lang);
|
||||||
|
res.title = Algorithms.isEmpty(title) ? amenity.getName() : title;
|
||||||
res.content = amenity.getDescription(lang);
|
res.content = amenity.getDescription(lang);
|
||||||
res.isPartOf = emptyIfNull(amenity.getTagContent(Amenity.IS_PART, lang));
|
res.isPartOf = emptyIfNull(amenity.getTagContent(Amenity.IS_PART, lang));
|
||||||
res.lat = amenity.getLocation().getLatitude();
|
res.lat = amenity.getLocation().getLatitude();
|
||||||
res.lon = amenity.getLocation().getLongitude();
|
res.lon = amenity.getLocation().getLongitude();
|
||||||
res.imageTitle = emptyIfNull(amenity.getTagContent(Amenity.IMAGE_TITLE, lang));
|
res.imageTitle = emptyIfNull(amenity.getTagContent(Amenity.IMAGE_TITLE, null));
|
||||||
res.routeId = getRouteId(amenity);
|
res.routeId = emptyIfNull(amenity.getTagContent(Amenity.ROUTE_ID, null));
|
||||||
|
res.routeSource = emptyIfNull(amenity.getTagContent(Amenity.ROUTE_SOURCE, null));
|
||||||
res.originalId = 0;
|
res.originalId = 0;
|
||||||
res.lang = lang;
|
res.lang = lang;
|
||||||
res.contentsJson = emptyIfNull(amenity.getTagContent(Amenity.CONTENT_JSON, lang));
|
res.contentsJson = emptyIfNull(amenity.getTagContent(Amenity.CONTENT_JSON, lang));
|
||||||
|
@ -153,85 +181,72 @@ public class TravelObfHelper implements TravelHelper {
|
||||||
return text == null ? "" : text;
|
return text == null ? "" : text;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getRouteId(Amenity amenity) {
|
|
||||||
return amenity.getTagContent(Amenity.ROUTE_ID, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isAnyTravelBookPresent() {
|
public boolean isAnyTravelBookPresent() {
|
||||||
return !Algorithms.isEmpty(getTravelBookReaders());
|
return !Algorithms.isEmpty(getReaders());
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public List<WikivoyageSearchResult> search(@NonNull String searchQuery) {
|
public List<WikivoyageSearchResult> search(@NonNull String searchQuery) {
|
||||||
List<WikivoyageSearchResult> res = new ArrayList<>();
|
List<WikivoyageSearchResult> res = new ArrayList<>();
|
||||||
List<Amenity> searchObjects = null;
|
Map<File, List<Amenity>> amenityMap = new HashMap<>();
|
||||||
for (BinaryMapIndexReader reader : app.getResourceManager().getTravelRepositories()) {
|
for (BinaryMapIndexReader reader : getReaders()) {
|
||||||
try {
|
try {
|
||||||
BinaryMapIndexReader.SearchRequest<Amenity> searchRequest = BinaryMapIndexReader.
|
SearchRequest<Amenity> searchRequest = BinaryMapIndexReader.buildSearchPoiRequest(0, 0, searchQuery,
|
||||||
buildSearchPoiRequest(0, 0, searchQuery,
|
|
||||||
0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, getSearchRouteArticleFilter(), null, null);
|
0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, getSearchRouteArticleFilter(), null, null);
|
||||||
|
|
||||||
searchObjects = reader.searchPoiByName(searchRequest);
|
List<Amenity> amenities = reader.searchPoiByName(searchRequest);
|
||||||
|
if (!Algorithms.isEmpty(amenities)) {
|
||||||
|
amenityMap.put(reader.getFile(), amenities);
|
||||||
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.error(e);
|
LOG.error(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!Algorithms.isEmpty(searchObjects)) {
|
if (!Algorithms.isEmpty(amenityMap)) {
|
||||||
String baseLng = app.getLanguage();
|
String appLang = app.getLanguage();
|
||||||
for (Amenity obj : searchObjects) {
|
for (Entry<File, List<Amenity>> entry : amenityMap.entrySet()) {
|
||||||
WikivoyageSearchResult r = new WikivoyageSearchResult();
|
File file = entry.getKey();
|
||||||
TravelArticle article = readArticle(obj, baseLng);
|
for (Amenity amenity : entry.getValue()) {
|
||||||
r.articleTitles = new ArrayList<>(Collections.singletonList(article.title));
|
Set<String> nameLangs = getLanguages(amenity);
|
||||||
r.imageTitle = article.imageTitle;
|
if (nameLangs.contains(appLang)) {
|
||||||
r.routeId = article.routeId;
|
TravelArticle article = readArticle(file, amenity, appLang);
|
||||||
r.isPartOf = new ArrayList<>(Collections.singletonList(article.isPartOf));
|
WikivoyageSearchResult r = new WikivoyageSearchResult(article, new ArrayList<>(nameLangs));
|
||||||
r.langs = new ArrayList<>(Collections.singletonList(baseLng));
|
res.add(r);
|
||||||
res.add(r);
|
}
|
||||||
cachedArticles.put(article.routeId, article);
|
}
|
||||||
}
|
}
|
||||||
res = new ArrayList<>(groupSearchResultsByRouteId(res));
|
|
||||||
sortSearchResults(res);
|
sortSearchResults(res);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sortSearchResults(@NonNull List<WikivoyageSearchResult> list) {
|
private Set<String> getLanguages(@NonNull Amenity amenity) {
|
||||||
Collections.sort(list, new Comparator<WikivoyageSearchResult>() {
|
Set<String> langs = new HashSet<>();
|
||||||
|
String descrStart = Amenity.DESCRIPTION + ":";
|
||||||
@Override
|
String partStart = Amenity.IS_PART + ":";
|
||||||
public int compare(WikivoyageSearchResult res1, WikivoyageSearchResult res2) {
|
for (String infoTag : amenity.getAdditionalInfoKeys()) {
|
||||||
return collator.compare(res1.articleTitles.get(0), res2.articleTitles.get(0));
|
if (infoTag.startsWith(descrStart)) {
|
||||||
}
|
if (infoTag.length() > descrStart.length()) {
|
||||||
});
|
langs.add(infoTag.substring(descrStart.length()));
|
||||||
}
|
}
|
||||||
|
} else if (infoTag.startsWith(partStart)) {
|
||||||
@NonNull
|
if (infoTag.length() > partStart.length()) {
|
||||||
private Collection<WikivoyageSearchResult> groupSearchResultsByRouteId(@NonNull List<WikivoyageSearchResult> res) {
|
langs.add(infoTag.substring(partStart.length()));
|
||||||
String baseLng = app.getLanguage();
|
|
||||||
Map<String, WikivoyageSearchResult> wikivoyage = new HashMap<>();
|
|
||||||
for (WikivoyageSearchResult rs : res) {
|
|
||||||
WikivoyageSearchResult prev = wikivoyage.get(rs.routeId);
|
|
||||||
if (prev != null) {
|
|
||||||
int insInd = prev.langs.size();
|
|
||||||
if (rs.langs.get(0).equals(baseLng)) {
|
|
||||||
insInd = 0;
|
|
||||||
} else if (rs.langs.get(0).equals("en")) {
|
|
||||||
if (!prev.langs.get(0).equals(baseLng)) {
|
|
||||||
insInd = 0;
|
|
||||||
} else {
|
|
||||||
insInd = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
prev.articleTitles.add(insInd, rs.articleTitles.get(0));
|
|
||||||
prev.langs.add(insInd, rs.langs.get(0));
|
|
||||||
prev.isPartOf.add(insInd, rs.isPartOf.get(0));
|
|
||||||
} else {
|
|
||||||
wikivoyage.put(rs.routeId, rs);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return wikivoyage.values();
|
return langs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sortSearchResults(@NonNull List<WikivoyageSearchResult> list) {
|
||||||
|
Collections.sort(list, new Comparator<WikivoyageSearchResult>() {
|
||||||
|
@Override
|
||||||
|
public int compare(WikivoyageSearchResult res1, WikivoyageSearchResult res2) {
|
||||||
|
return collator.compare(res1.articleId.title, res2.articleId.title);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
@ -243,74 +258,153 @@ public class TravelObfHelper implements TravelHelper {
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Map<WikivoyageSearchResult, List<WikivoyageSearchResult>> getNavigationMap(@NonNull final TravelArticle article) {
|
public Map<WikivoyageSearchResult, List<WikivoyageSearchResult>> getNavigationMap(@NonNull final TravelArticle article) {
|
||||||
return Collections.emptyMap();
|
final String lang = article.getLang();
|
||||||
}
|
final String title = article.getTitle();
|
||||||
|
if (TextUtils.isEmpty(lang) || TextUtils.isEmpty(title)) {
|
||||||
@Override
|
return Collections.emptyMap();
|
||||||
public TravelArticle getArticleById(@NonNull String routeId, @NonNull String lang) {
|
|
||||||
TravelArticle article = cachedArticles.get(routeId);
|
|
||||||
if (article != null) {
|
|
||||||
return article;
|
|
||||||
}
|
}
|
||||||
article = getArticleByIdFromTravelBooks(routeId, lang);
|
final String[] parts;
|
||||||
if (article != null) {
|
if (!TextUtils.isEmpty(article.getAggregatedPartOf())) {
|
||||||
return getArticleByIdFromTravelBooks(routeId, lang);
|
String[] originalParts = article.getAggregatedPartOf().split(",");
|
||||||
|
if (originalParts.length > 1) {
|
||||||
|
parts = new String[originalParts.length];
|
||||||
|
for (int i = 0; i < originalParts.length; i++) {
|
||||||
|
parts[i] = originalParts[originalParts.length - i - 1];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parts = originalParts;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parts = null;
|
||||||
}
|
}
|
||||||
return localDataHelper.getSavedArticle(routeId, lang);
|
Map<String, List<WikivoyageSearchResult>> navMap = new HashMap<>();
|
||||||
}
|
Set<String> headers = new LinkedHashSet<String>();
|
||||||
|
Map<String, WikivoyageSearchResult> headerObjs = new HashMap<>();
|
||||||
private TravelArticle getArticleByIdFromTravelBooks(final String routeId, final String lang) {
|
Map<File, List<Amenity>> amenityMap = new HashMap<>();
|
||||||
TravelArticle article = null;
|
for (BinaryMapIndexReader reader : getReaders()) {
|
||||||
final List<Amenity> amenities = new ArrayList<>();
|
|
||||||
for (BinaryMapIndexReader travelBookReader : getTravelBookReaders()) {
|
|
||||||
try {
|
try {
|
||||||
BinaryMapIndexReader.SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(
|
SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(0,
|
||||||
0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, -1, getSearchRouteArticleFilter(),
|
Integer.MAX_VALUE, 0, Integer.MAX_VALUE, -1, getSearchRouteArticleFilter(), new ResultMatcher<Amenity>() {
|
||||||
new ResultMatcher<Amenity>() {
|
|
||||||
boolean done = false;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean publish(Amenity amenity) {
|
public boolean publish(Amenity amenity) {
|
||||||
if (getRouteId(amenity).equals(routeId)) {
|
String isPartOf = amenity.getTagContent(Amenity.IS_PART, lang);
|
||||||
amenities.add(amenity);
|
if (Algorithms.stringsEqual(title, isPartOf)) {
|
||||||
done = true;
|
return true;
|
||||||
|
} else if (parts != null && parts.length > 0) {
|
||||||
|
String title = amenity.getName(lang);
|
||||||
|
title = Algorithms.isEmpty(title) ? amenity.getName() : title;
|
||||||
|
for (int i = 0; i < parts.length; i++) {
|
||||||
|
String part = parts[i];
|
||||||
|
if (i == 0 && Algorithms.stringsEqual(part, title) || Algorithms.stringsEqual(part, isPartOf)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isCancelled() {
|
public boolean isCancelled() {
|
||||||
return done;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
List<Amenity> amenities = reader.searchPoi(req);
|
||||||
travelBookReader.searchPoi(req);
|
if (!Algorithms.isEmpty(amenities)) {
|
||||||
} catch (IOException e) {
|
amenityMap.put(reader.getFile(), amenities);
|
||||||
LOG.error(e.getMessage());
|
}
|
||||||
}
|
} catch (Exception e) {
|
||||||
if (!amenities.isEmpty()) {
|
LOG.error(e.getMessage(), e);
|
||||||
article = readArticle(amenities.get(0), lang);
|
|
||||||
cachedArticles.put(article.routeId, article);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return article;
|
if (parts != null && parts.length > 0) {
|
||||||
|
headers.addAll(Arrays.asList(parts));
|
||||||
|
headers.add(title);
|
||||||
|
}
|
||||||
|
if (!Algorithms.isEmpty(amenityMap)) {
|
||||||
|
for (Entry<File, List<Amenity>> entry : amenityMap.entrySet()) {
|
||||||
|
File file = entry.getKey();
|
||||||
|
for (Amenity amenity : entry.getValue()) {
|
||||||
|
Set<String> nameLangs = getLanguages(amenity);
|
||||||
|
if (nameLangs.contains(lang)) {
|
||||||
|
TravelArticle a = readArticle(file, amenity, lang);
|
||||||
|
WikivoyageSearchResult rs = new WikivoyageSearchResult(a, new ArrayList<>(nameLangs));
|
||||||
|
List<WikivoyageSearchResult> l = navMap.get(rs.isPartOf);
|
||||||
|
if (l == null) {
|
||||||
|
l = new ArrayList<>();
|
||||||
|
navMap.put(rs.isPartOf, l);
|
||||||
|
}
|
||||||
|
l.add(rs);
|
||||||
|
if (headers != null && headers.contains(a.getTitle())) {
|
||||||
|
headerObjs.put(a.getTitle(), rs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LinkedHashMap<WikivoyageSearchResult, List<WikivoyageSearchResult>> res = new LinkedHashMap<>();
|
||||||
|
for (String header : headers) {
|
||||||
|
WikivoyageSearchResult searchResult = headerObjs.get(header);
|
||||||
|
List<WikivoyageSearchResult> results = navMap.get(header);
|
||||||
|
if (results != null) {
|
||||||
|
Collections.sort(results, new Comparator<WikivoyageSearchResult>() {
|
||||||
|
@Override
|
||||||
|
public int compare(WikivoyageSearchResult o1, WikivoyageSearchResult o2) {
|
||||||
|
return collator.compare(o1.getArticleTitle(), o2.getArticleTitle());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
WikivoyageSearchResult emptyResult = new WikivoyageSearchResult("", header, null, null, null);
|
||||||
|
searchResult = searchResult != null ? searchResult : emptyResult;
|
||||||
|
res.put(searchResult, results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TravelArticle getArticleById(@NonNull TravelArticleIdentifier articleId, @NonNull String lang) {
|
||||||
|
TravelArticle article = getCachedArticle(articleId, lang);
|
||||||
|
return article == null ? findArticleById(articleId, lang) : article;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
private TravelArticle getCachedArticle(@NonNull TravelArticleIdentifier articleId, @NonNull String lang) {
|
||||||
public TravelArticle getArticleByTitle(@NonNull final String title, @NonNull final String lang) {
|
TravelArticle article = null;
|
||||||
|
Map<String, TravelArticle> articles = cachedArticles.get(articleId);
|
||||||
|
if (articles != null) {
|
||||||
|
if (Algorithms.isEmpty(lang)) {
|
||||||
|
Collection<TravelArticle> ac = articles.values();
|
||||||
|
if (!ac.isEmpty()) {
|
||||||
|
article = ac.iterator().next();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
article = articles.get(lang);
|
||||||
|
if (article == null) {
|
||||||
|
article = articles.get("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return article == null ? findArticleById(articleId, lang) : article;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TravelArticle findArticleById(@NonNull final TravelArticleIdentifier articleId, final String lang) {
|
||||||
TravelArticle article = null;
|
TravelArticle article = null;
|
||||||
final List<Amenity> amenities = new ArrayList<>();
|
final List<Amenity> amenities = new ArrayList<>();
|
||||||
for (BinaryMapIndexReader travelBookReader : getTravelBookReaders()) {
|
for (BinaryMapIndexReader reader : getReaders()) {
|
||||||
try {
|
try {
|
||||||
BinaryMapIndexReader.SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(
|
if (articleId.file != null && !articleId.file.equals(reader.getFile())) {
|
||||||
0, 0, title, 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, getSearchRouteArticleFilter(),
|
continue;
|
||||||
new ResultMatcher<Amenity>() {
|
}
|
||||||
|
SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(0, 0,
|
||||||
|
Algorithms.emptyIfNull(articleId.title), 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE,
|
||||||
|
getSearchRouteArticleFilter(), new ResultMatcher<Amenity>() {
|
||||||
boolean done = false;
|
boolean done = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean publish(Amenity amenity) {
|
public boolean publish(Amenity amenity) {
|
||||||
if (CollatorStringMatcher.cmatches(collator, title, amenity.getName(lang), CHECK_EQUALS_FROM_SPACE)) {
|
if (Algorithms.stringsEqual(articleId.routeId, Algorithms.emptyIfNull(amenity.getTagContent(Amenity.ROUTE_ID, null)))
|
||||||
|
&& Algorithms.stringsEqual(articleId.routeSource, Algorithms.emptyIfNull(amenity.getTagContent(Amenity.ROUTE_SOURCE, null)))) {
|
||||||
amenities.add(amenity);
|
amenities.add(amenity);
|
||||||
done = true;
|
done = true;
|
||||||
}
|
}
|
||||||
|
@ -323,19 +417,74 @@ public class TravelObfHelper implements TravelHelper {
|
||||||
}
|
}
|
||||||
}, null);
|
}, null);
|
||||||
|
|
||||||
travelBookReader.searchPoiByName(req);
|
if (!Double.isNaN(articleId.lat)) {
|
||||||
|
req.setBBoxRadius(articleId.lat, articleId.lon, ARTICLE_SEARCH_RADIUS);
|
||||||
|
if (!Algorithms.isEmpty(articleId.title)) {
|
||||||
|
reader.searchPoiByName(req);
|
||||||
|
} else {
|
||||||
|
reader.searchPoi(req);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reader.searchPoi(req);
|
||||||
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.error(e.getMessage());
|
LOG.error(e.getMessage());
|
||||||
}
|
}
|
||||||
if (!amenities.isEmpty()) {
|
if (!amenities.isEmpty()) {
|
||||||
article = readArticle(amenities.get(0), lang);
|
article = cacheTravelArticles(reader.getFile(), amenities.get(0), lang);
|
||||||
cachedArticles.put(article.routeId, article);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return article;
|
return article;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<BinaryMapIndexReader> getTravelBookReaders() {
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public TravelArticle getArticleByTitle(@NonNull final String title, @NonNull final String lang) {
|
||||||
|
return getArticleByTitle(title, new QuadRect(), lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public TravelArticle getArticleByTitle(@NonNull final String title, @NonNull LatLon latLon, @NonNull final String lang) {
|
||||||
|
QuadRect rect = latLon != null ? MapUtils.calculateLatLonBbox(latLon.getLatitude(), latLon.getLongitude(), ARTICLE_SEARCH_RADIUS) : new QuadRect();
|
||||||
|
return getArticleByTitle(title, rect, lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public TravelArticle getArticleByTitle(@NonNull final String title, @NonNull QuadRect rect, @NonNull final String lang) {
|
||||||
|
TravelArticle article = null;
|
||||||
|
List<Amenity> amenities = null;
|
||||||
|
int x = 0;
|
||||||
|
int y = 0;
|
||||||
|
int left = 0;
|
||||||
|
int right = Integer.MAX_VALUE;
|
||||||
|
int top = 0;
|
||||||
|
int bottom = Integer.MAX_VALUE;
|
||||||
|
if (rect.height() > 0 && rect.width() > 0) {
|
||||||
|
x = (int) rect.centerX();
|
||||||
|
y = (int) rect.centerY();
|
||||||
|
left = (int) rect.left;
|
||||||
|
right = (int) rect.right;
|
||||||
|
top = (int) rect.top;
|
||||||
|
bottom = (int) rect.bottom;
|
||||||
|
}
|
||||||
|
for (BinaryMapIndexReader reader : getReaders()) {
|
||||||
|
try {
|
||||||
|
SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(
|
||||||
|
x, y, title, left, right, top, bottom, getSearchRouteArticleFilter(), null, null);
|
||||||
|
amenities = reader.searchPoiByName(req);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error(e.getMessage());
|
||||||
|
}
|
||||||
|
if (!Algorithms.isEmpty(amenities)) {
|
||||||
|
article = cacheTravelArticles(reader.getFile(), amenities.get(0), lang);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return article;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<BinaryMapIndexReader> getReaders() {
|
||||||
if (!app.isApplicationInitializing()) {
|
if (!app.isApplicationInitializing()) {
|
||||||
return app.getResourceManager().getTravelRepositories();
|
return app.getResourceManager().getTravelRepositories();
|
||||||
} else {
|
} else {
|
||||||
|
@ -343,14 +492,16 @@ public class TravelObfHelper implements TravelHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public String getArticleId(@NonNull String title, @NonNull String lang) {
|
public TravelArticleIdentifier getArticleId(@NonNull String title, @NonNull String lang) {
|
||||||
TravelArticle a = null;
|
TravelArticle a = null;
|
||||||
for (TravelArticle article : cachedArticles.values()) {
|
for (Map<String, TravelArticle> articles : cachedArticles.values()) {
|
||||||
if (article.getTitle().equals(title)) {
|
for (TravelArticle article : articles.values()) {
|
||||||
a = article;
|
if (article.getTitle().equals(title)) {
|
||||||
break;
|
a = article;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (a == null) {
|
if (a == null) {
|
||||||
|
@ -359,17 +510,18 @@ public class TravelObfHelper implements TravelHelper {
|
||||||
a = article;
|
a = article;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return a != null && a.getRouteId() != null ? a.getRouteId() : "";
|
return a != null ? a.generateIdentifier() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public ArrayList<String> getArticleLangs(@NonNull String routeId) {
|
public ArrayList<String> getArticleLangs(@NonNull TravelArticleIdentifier articleId) {
|
||||||
ArrayList<String> res = new ArrayList<>();
|
ArrayList<String> res = new ArrayList<>();
|
||||||
res.add("en");
|
TravelArticle article = getArticleById(articleId, "");
|
||||||
for (TravelArticle article : popularArticles) {
|
if (article != null) {
|
||||||
if (article.getRouteId().equals(routeId)) {
|
Map<String, TravelArticle> articles = cachedArticles.get(articleId);
|
||||||
res.add(article.getLang());
|
if (articles != null) {
|
||||||
|
res.addAll(articles.keySet());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
package net.osmand.plus.wikivoyage.data;
|
package net.osmand.plus.wikivoyage.data;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import net.osmand.plus.wikivoyage.data.TravelArticle.TravelArticleIdentifier;
|
||||||
import net.osmand.util.Algorithms;
|
import net.osmand.util.Algorithms;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -9,25 +13,52 @@ public class WikivoyageSearchResult {
|
||||||
|
|
||||||
private static final int SHOW_LANGS = 3;
|
private static final int SHOW_LANGS = 3;
|
||||||
|
|
||||||
String routeId;
|
TravelArticleIdentifier articleId;
|
||||||
List<String> articleTitles = new ArrayList<>();
|
|
||||||
List<String> langs = new ArrayList<>();
|
|
||||||
List<String> isPartOf = new ArrayList<>();
|
|
||||||
String imageTitle;
|
|
||||||
|
|
||||||
public String getRouteId() {
|
String imageTitle;
|
||||||
return routeId;
|
String isPartOf;
|
||||||
|
|
||||||
|
List<String> langs = new ArrayList<>();
|
||||||
|
|
||||||
|
public WikivoyageSearchResult(@NonNull TravelArticle article, @Nullable List<String> langs) {
|
||||||
|
articleId = article.generateIdentifier();
|
||||||
|
imageTitle = article.imageTitle;
|
||||||
|
isPartOf = article.isPartOf;
|
||||||
|
if (langs != null) {
|
||||||
|
this.langs = langs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getArticleTitles() {
|
public WikivoyageSearchResult(String routeId, String articleTitle, String isPartOf, String imageTitle, @Nullable List<String> langs) {
|
||||||
return articleTitles;
|
TravelArticle article = new TravelArticle();
|
||||||
|
article.routeId = routeId;
|
||||||
|
article.title = articleTitle;
|
||||||
|
|
||||||
|
this.articleId = article.generateIdentifier();
|
||||||
|
this.imageTitle = imageTitle;
|
||||||
|
this.isPartOf = isPartOf;
|
||||||
|
if (langs != null) {
|
||||||
|
this.langs = langs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TravelArticleIdentifier getArticleId() {
|
||||||
|
return articleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getArticleTitle() {
|
||||||
|
return articleId.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getArticleRouteId() {
|
||||||
|
return articleId.routeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getLangs() {
|
public List<String> getLangs() {
|
||||||
return langs;
|
return langs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getIsPartOf() {
|
public String getIsPartOf() {
|
||||||
return isPartOf;
|
return isPartOf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ public class SavedArticlesTabFragment extends BaseOsmAndFragment implements Trav
|
||||||
public void openArticle(TravelArticle article) {
|
public void openArticle(TravelArticle article) {
|
||||||
FragmentManager fm = getFragmentManager();
|
FragmentManager fm = getFragmentManager();
|
||||||
if (fm != null) {
|
if (fm != null) {
|
||||||
WikivoyageArticleDialogFragment.showInstance(app, fm, article.getRouteId(), article.getLang());
|
WikivoyageArticleDialogFragment.showInstance(app, fm, article.generateIdentifier(), article.getLang());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -37,6 +37,7 @@ import net.osmand.plus.download.DownloadIndexesThread.DownloadEvents;
|
||||||
import net.osmand.plus.wikipedia.WikiArticleHelper;
|
import net.osmand.plus.wikipedia.WikiArticleHelper;
|
||||||
import net.osmand.plus.wikivoyage.article.WikivoyageArticleDialogFragment;
|
import net.osmand.plus.wikivoyage.article.WikivoyageArticleDialogFragment;
|
||||||
import net.osmand.plus.wikivoyage.data.TravelArticle;
|
import net.osmand.plus.wikivoyage.data.TravelArticle;
|
||||||
|
import net.osmand.plus.wikivoyage.data.TravelArticle.TravelArticleIdentifier;
|
||||||
import net.osmand.plus.wikivoyage.data.TravelHelper;
|
import net.osmand.plus.wikivoyage.data.TravelHelper;
|
||||||
import net.osmand.plus.wikivoyage.data.TravelLocalDataHelper;
|
import net.osmand.plus.wikivoyage.data.TravelLocalDataHelper;
|
||||||
import net.osmand.plus.wikivoyage.search.WikivoyageSearchDialogFragment;
|
import net.osmand.plus.wikivoyage.search.WikivoyageSearchDialogFragment;
|
||||||
|
@ -50,8 +51,8 @@ public class WikivoyageExploreActivity extends TabActivity implements DownloadEv
|
||||||
TravelLocalDataHelper.Listener {
|
TravelLocalDataHelper.Listener {
|
||||||
|
|
||||||
private static final String TAB_SELECTED = "tab_selected";
|
private static final String TAB_SELECTED = "tab_selected";
|
||||||
private static final String ROUTE_ID_KEY = "route_id_key";
|
private static final String ARTICLE_ID_KEY = "article_id";
|
||||||
private static final String SELECTED_LANG_KEY = "selected_lang_key";
|
private static final String SELECTED_LANG_KEY = "selected_lang";
|
||||||
|
|
||||||
private static final int EXPLORE_POSITION = 0;
|
private static final int EXPLORE_POSITION = 0;
|
||||||
private static final int SAVED_ARTICLES_POSITION = 1;
|
private static final int SAVED_ARTICLES_POSITION = 1;
|
||||||
|
@ -182,9 +183,9 @@ public class WikivoyageExploreActivity extends TabActivity implements DownloadEv
|
||||||
BottomNavigationView bottomNav = (BottomNavigationView) findViewById(R.id.bottom_navigation);
|
BottomNavigationView bottomNav = (BottomNavigationView) findViewById(R.id.bottom_navigation);
|
||||||
bottomNav.setSelectedItemId(R.id.action_saved_articles);
|
bottomNav.setSelectedItemId(R.id.action_saved_articles);
|
||||||
}
|
}
|
||||||
String articleId = intent.getStringExtra(ROUTE_ID_KEY);
|
TravelArticleIdentifier articleId = intent.getParcelableExtra(ARTICLE_ID_KEY);
|
||||||
String selectedLang = intent.getStringExtra(SELECTED_LANG_KEY);
|
String selectedLang = intent.getStringExtra(SELECTED_LANG_KEY);
|
||||||
if (!Algorithms.isEmpty(articleId)) {
|
if (articleId != null) {
|
||||||
WikivoyageArticleDialogFragment.showInstance(app, getSupportFragmentManager(), articleId, selectedLang);
|
WikivoyageArticleDialogFragment.showInstance(app, getSupportFragmentManager(), articleId, selectedLang);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -201,8 +202,8 @@ public class WikivoyageExploreActivity extends TabActivity implements DownloadEv
|
||||||
String title = WikiArticleHelper.decodeTitleFromTravelUrl(data.getQueryParameter("title"));
|
String title = WikiArticleHelper.decodeTitleFromTravelUrl(data.getQueryParameter("title"));
|
||||||
String selectedLang = data.getQueryParameter("lang");
|
String selectedLang = data.getQueryParameter("lang");
|
||||||
if (!Algorithms.isEmpty(title) && !Algorithms.isEmpty(selectedLang)) {
|
if (!Algorithms.isEmpty(title) && !Algorithms.isEmpty(selectedLang)) {
|
||||||
String articleId = app.getTravelHelper().getArticleId(title, selectedLang);
|
TravelArticleIdentifier articleId = app.getTravelHelper().getArticleId(title, selectedLang);
|
||||||
if (!articleId.isEmpty()) {
|
if (articleId != null) {
|
||||||
WikivoyageArticleDialogFragment.showInstance(app, getSupportFragmentManager(), articleId, selectedLang);
|
WikivoyageArticleDialogFragment.showInstance(app, getSupportFragmentManager(), articleId, selectedLang);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -279,7 +280,7 @@ public class WikivoyageExploreActivity extends TabActivity implements DownloadEv
|
||||||
|
|
||||||
private void applyIntentParameters(Intent intent, TravelArticle article) {
|
private void applyIntentParameters(Intent intent, TravelArticle article) {
|
||||||
intent.putExtra(TAB_SELECTED, viewPager.getCurrentItem());
|
intent.putExtra(TAB_SELECTED, viewPager.getCurrentItem());
|
||||||
intent.putExtra(ROUTE_ID_KEY, article.getRouteId());
|
intent.putExtra(ARTICLE_ID_KEY, article.generateIdentifier());
|
||||||
intent.putExtra(SELECTED_LANG_KEY, article.getLang());
|
intent.putExtra(SELECTED_LANG_KEY, article.getLang());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,8 @@ public class ArticleTravelCard extends BaseTravelCard {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
if (fragmentManager != null) {
|
if (fragmentManager != null) {
|
||||||
WikivoyageArticleDialogFragment.showInstance(app, fragmentManager, article.getRouteId(), article.getLang());
|
WikivoyageArticleDialogFragment.showInstance(app, fragmentManager,
|
||||||
|
article.generateIdentifier(), article.getLang());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,6 +7,7 @@ import androidx.annotation.NonNull;
|
||||||
import net.osmand.GPXUtilities.GPXFile;
|
import net.osmand.GPXUtilities.GPXFile;
|
||||||
import net.osmand.GPXUtilities.Metadata;
|
import net.osmand.GPXUtilities.Metadata;
|
||||||
import net.osmand.GPXUtilities.WptPt;
|
import net.osmand.GPXUtilities.WptPt;
|
||||||
|
import net.osmand.data.LatLon;
|
||||||
import net.osmand.data.PointDescription;
|
import net.osmand.data.PointDescription;
|
||||||
import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
|
import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
|
||||||
import net.osmand.plus.R;
|
import net.osmand.plus.R;
|
||||||
|
@ -14,12 +15,13 @@ import net.osmand.plus.activities.MapActivity;
|
||||||
import net.osmand.plus.mapcontextmenu.controllers.WptPtMenuController;
|
import net.osmand.plus.mapcontextmenu.controllers.WptPtMenuController;
|
||||||
import net.osmand.plus.wikivoyage.article.WikivoyageArticleDialogFragment;
|
import net.osmand.plus.wikivoyage.article.WikivoyageArticleDialogFragment;
|
||||||
import net.osmand.plus.wikivoyage.data.TravelArticle;
|
import net.osmand.plus.wikivoyage.data.TravelArticle;
|
||||||
|
import net.osmand.plus.wikivoyage.data.TravelArticle.TravelArticleIdentifier;
|
||||||
|
|
||||||
public class WikivoyageWptPtMenuController extends WptPtMenuController {
|
public class WikivoyageWptPtMenuController extends WptPtMenuController {
|
||||||
|
|
||||||
private WikivoyageWptPtMenuController(@NonNull MapActivity mapActivity, @NonNull PointDescription pointDescription, @NonNull WptPt wpt, @NonNull TravelArticle article) {
|
private WikivoyageWptPtMenuController(@NonNull MapActivity mapActivity, @NonNull PointDescription pointDescription, @NonNull WptPt wpt, @NonNull TravelArticle article) {
|
||||||
super(new WikivoyageWptPtMenuBuilder(mapActivity, wpt), mapActivity, pointDescription, wpt);
|
super(new WikivoyageWptPtMenuBuilder(mapActivity, wpt), mapActivity, pointDescription, wpt);
|
||||||
final String tripId = article.getRouteId();
|
final TravelArticleIdentifier articleId = article.generateIdentifier();
|
||||||
final String lang = article.getLang();
|
final String lang = article.getLang();
|
||||||
leftTitleButtonController = new TitleButtonController() {
|
leftTitleButtonController = new TitleButtonController() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -27,7 +29,7 @@ public class WikivoyageWptPtMenuController extends WptPtMenuController {
|
||||||
MapActivity mapActivity = getMapActivity();
|
MapActivity mapActivity = getMapActivity();
|
||||||
if (mapActivity != null) {
|
if (mapActivity != null) {
|
||||||
WikivoyageArticleDialogFragment.showInstance(mapActivity.getMyApplication(),
|
WikivoyageArticleDialogFragment.showInstance(mapActivity.getMyApplication(),
|
||||||
mapActivity.getSupportFragmentManager(), tripId, lang);
|
mapActivity.getSupportFragmentManager(), articleId, lang);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -42,7 +44,7 @@ public class WikivoyageWptPtMenuController extends WptPtMenuController {
|
||||||
String title = metadata != null ? metadata.getArticleTitle() : null;
|
String title = metadata != null ? metadata.getArticleTitle() : null;
|
||||||
String lang = metadata != null ? metadata.getArticleLang() : null;
|
String lang = metadata != null ? metadata.getArticleLang() : null;
|
||||||
if (!TextUtils.isEmpty(title) && !TextUtils.isEmpty(lang)) {
|
if (!TextUtils.isEmpty(title) && !TextUtils.isEmpty(lang)) {
|
||||||
return mapActivity.getMyApplication().getTravelHelper().getArticleByTitle(title, lang);
|
return mapActivity.getMyApplication().getTravelHelper().getArticleByTitle(title, new LatLon(wpt.lat, wpt.lon), lang);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,8 +83,8 @@ public class SearchRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView
|
||||||
rc.transform(new CropCircleTransformation())
|
rc.transform(new CropCircleTransformation())
|
||||||
.placeholder(placeholder)
|
.placeholder(placeholder)
|
||||||
.into(holder.icon);
|
.into(holder.icon);
|
||||||
holder.title.setText(searchRes.getArticleTitles().get(0));
|
holder.title.setText(searchRes.getArticleTitle());
|
||||||
holder.leftDescr.setText(searchRes.getIsPartOf().get(0));
|
holder.leftDescr.setText(searchRes.getIsPartOf());
|
||||||
holder.rightDescr.setText(searchRes.getFirstLangsString());
|
holder.rightDescr.setText(searchRes.getFirstLangsString());
|
||||||
} else {
|
} else {
|
||||||
WikivoyageSearchHistoryItem historyItem = (WikivoyageSearchHistoryItem) item;
|
WikivoyageSearchHistoryItem historyItem = (WikivoyageSearchHistoryItem) item;
|
||||||
|
|
|
@ -112,8 +112,7 @@ public class WikivoyageSearchDialogFragment extends WikiBaseDialogFragment {
|
||||||
Object item = adapter.getItem(pos);
|
Object item = adapter.getItem(pos);
|
||||||
if (item instanceof WikivoyageSearchResult) {
|
if (item instanceof WikivoyageSearchResult) {
|
||||||
WikivoyageSearchResult res = (WikivoyageSearchResult) item;
|
WikivoyageSearchResult res = (WikivoyageSearchResult) item;
|
||||||
WikivoyageArticleDialogFragment
|
WikivoyageArticleDialogFragment.showInstance(fm, res.getArticleId(), new ArrayList<>(res.getLangs()));
|
||||||
.showInstance(fm, res.getRouteId(), new ArrayList<>(res.getLangs()));
|
|
||||||
} else if (item instanceof WikivoyageSearchHistoryItem) {
|
} else if (item instanceof WikivoyageSearchHistoryItem) {
|
||||||
WikivoyageSearchHistoryItem historyItem = (WikivoyageSearchHistoryItem) item;
|
WikivoyageSearchHistoryItem historyItem = (WikivoyageSearchHistoryItem) item;
|
||||||
WikivoyageArticleDialogFragment
|
WikivoyageArticleDialogFragment
|
||||||
|
|
Loading…
Reference in a new issue