Merge branch 'master' into markers_backup
# Conflicts: # OsmAnd/src/net/osmand/plus/settings/fragments/DuplicatesSettingsAdapter.java
This commit is contained in:
commit
bb10f31f86
65 changed files with 2191 additions and 1318 deletions
|
@ -106,9 +106,11 @@ public class TspAnt {
|
|||
// Allocates all memory.
|
||||
// Adds 1 to edge lengths to ensure no zero length edges.
|
||||
public TspAnt readGraph(List<LatLon> intermediates, LatLon start, LatLon end) {
|
||||
boolean keepEndPoint = end != null;
|
||||
List<LatLon> l = new ArrayList<LatLon>();
|
||||
l.add(start);
|
||||
boolean keepEndPoint = end != null;
|
||||
List<LatLon> l = new ArrayList<LatLon>();
|
||||
if (start != null) {
|
||||
l.add(start);
|
||||
}
|
||||
l.addAll(intermediates);
|
||||
if (keepEndPoint) {
|
||||
l.add(end);
|
||||
|
|
|
@ -267,4 +267,8 @@
|
|||
<string name="location_sharing_status">مشاركة: %1$s</string>
|
||||
<string name="shared_string_enabled">مفعل</string>
|
||||
<string name="duration_ago">%1$s منذ</string>
|
||||
<string name="shared_string_export">تصدير</string>
|
||||
<string name="logcat_buffer">لوجكات العازلة</string>
|
||||
<string name="logcat_buffer_descr">تحقق من السجلات التفصيلية للتطبيق وشاركها</string>
|
||||
<string name="send_report">إرسال تقرير</string>
|
||||
</resources>
|
|
@ -1,25 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<application
|
||||
android:icon="@mipmap/icon_free"
|
||||
android:label="@string/app_name_free"
|
||||
tools:replace="android:icon">
|
||||
<meta-data
|
||||
android:name="com.facebook.sdk.ApplicationId"
|
||||
android:value="fb131313131043971"/>
|
||||
<activity
|
||||
android:name="net.osmand.plus.activities.MapActivity"
|
||||
android:theme="@style/FirstSplashScreenCustom"
|
||||
tools:replace="android:theme"/>
|
||||
<service
|
||||
android:name="net.osmand.plus.NavigationService"
|
||||
android:process="net.osmand.freecustom"
|
||||
tools:replace="android:process"/>
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="net.osmand.freecustom.fileprovider"
|
||||
tools:replace="android:authorities"/>
|
||||
</application>
|
||||
</manifest>
|
|
@ -119,9 +119,6 @@ android {
|
|||
full {
|
||||
java.srcDirs = ["src-google"]
|
||||
}
|
||||
fulldev {
|
||||
java.srcDirs = ["src-google"]
|
||||
}
|
||||
free {
|
||||
java.srcDirs = ["src-google"]
|
||||
manifest.srcFile "AndroidManifest-free.xml"
|
||||
|
@ -130,10 +127,6 @@ android {
|
|||
java.srcDirs = ["src-google"]
|
||||
manifest.srcFile "AndroidManifest-freedev.xml"
|
||||
}
|
||||
freecustom {
|
||||
java.srcDirs = ["src-google"]
|
||||
manifest.srcFile "AndroidManifest-freecustom.xml"
|
||||
}
|
||||
freehuawei {
|
||||
java.srcDirs = ["src-huawei"]
|
||||
manifest.srcFile "AndroidManifest-freehuawei.xml"
|
||||
|
@ -188,38 +181,24 @@ android {
|
|||
dimension "version"
|
||||
applicationId "net.osmand"
|
||||
}
|
||||
freeres {
|
||||
dimension "version"
|
||||
applicationId "net.osmand"
|
||||
resConfig "en"
|
||||
}
|
||||
freecustom {
|
||||
dimension "version"
|
||||
applicationId "net.osmand.freecustom"
|
||||
}
|
||||
full {
|
||||
dimension "version"
|
||||
applicationId "net.osmand.plus"
|
||||
}
|
||||
fulldev {
|
||||
dimension "version"
|
||||
applicationId "net.osmand.plus"
|
||||
resConfig "en"
|
||||
// resConfigs "xxhdpi", "nodpi"
|
||||
}
|
||||
freehuawei {
|
||||
dimension "version"
|
||||
applicationId "net.osmand.huawei"
|
||||
}
|
||||
// CoreVersion
|
||||
// Build that doesn't include 3D OpenGL
|
||||
legacy {
|
||||
dimension "coreversion"
|
||||
}
|
||||
|
||||
// Build that includes 3D OpenGL release
|
||||
qtcore {
|
||||
dimension "coreversion"
|
||||
}
|
||||
|
||||
// Build that includes 3D OpenGL debug
|
||||
qtcoredebug {
|
||||
dimension "coreversion"
|
||||
}
|
||||
|
|
30
OsmAnd/res/drawable/ic_action_device_location.xml
Normal file
30
OsmAnd/res/drawable/ic_action_device_location.xml
Normal file
|
@ -0,0 +1,30 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M8,9.95C8,13.0979 9.799,16.5395 11.3078,18.9347C11.535,19.2955 11.7687,19.6502 12.0081,20H5V4H10.6499C9.0278,5.4317 8,7.5291 8,9.95Z"
|
||||
android:strokeAlpha="0.3"
|
||||
android:fillColor="#727272"
|
||||
android:fillAlpha="0.3"/>
|
||||
<path
|
||||
android:pathData="M10.6499,4H5L5,20H12.0081C12.4759,20.6835 12.9654,21.3479 13.4706,22H5C3.8954,22 3,21.1046 3,20V4C3,2.8954 3.8954,2 5,2H15C15.112,2 15.2218,2.0092 15.3288,2.0269C13.5444,2.1705 11.9161,2.8824 10.6499,4Z"
|
||||
android:strokeAlpha="0.7"
|
||||
android:fillColor="#727272"
|
||||
android:fillAlpha="0.7"/>
|
||||
<path
|
||||
android:pathData="M9.6612,16H8C7.4477,16 7,16.4477 7,17C7,17.5523 7.4477,18 8,18H10.7383C10.3718,17.3767 10.0035,16.7041 9.6612,16Z"
|
||||
android:strokeAlpha="0.7"
|
||||
android:fillColor="#727272"
|
||||
android:fillAlpha="0.7"/>
|
||||
<path
|
||||
android:pathData="M6,6C6,5.4477 6.4477,5 7,5C7.5523,5 8,5.4477 8,6C8,6.5523 7.5523,7 7,7C6.4477,7 6,6.5523 6,6Z"
|
||||
android:strokeAlpha="0.5"
|
||||
android:fillColor="#727272"
|
||||
android:fillAlpha="0.5"/>
|
||||
<path
|
||||
android:pathData="M16,22C16,22 22,15 22,9.95C22,6.55 19.3137,4 16,4C12.6863,4 10,6.55 10,9.95C10,15 16,22 16,22ZM16,12C17.1046,12 18,11.1046 18,10C18,8.8954 17.1046,8 16,8C14.8954,8 14,8.8954 14,10C14,11.1046 14.8954,12 16,12Z"
|
||||
android:fillColor="#727272"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
30
OsmAnd/res/drawable/ic_action_device_location_colored.xml
Normal file
30
OsmAnd/res/drawable/ic_action_device_location_colored.xml
Normal file
|
@ -0,0 +1,30 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M5,4H15V20H5V4Z"
|
||||
android:fillColor="#237BFF"/>
|
||||
<path
|
||||
android:pathData="M15,4H5L5,20H15V4ZM5,2C3.8954,2 3,2.8954 3,4V20C3,21.1046 3.8954,22 5,22H15C16.1046,22 17,21.1046 17,20V4C17,2.8954 16.1046,2 15,2H5Z"
|
||||
android:fillColor="#264573"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M7,18C7,17.4477 7.4477,17 8,17H12C12.5523,17 13,17.4477 13,18C13,18.5523 12.5523,19 12,19H8C7.4477,19 7,18.5523 7,18Z"
|
||||
android:fillColor="#66A3FF"/>
|
||||
<path
|
||||
android:pathData="M6,6C6,5.4477 6.4477,5 7,5C7.5523,5 8,5.4477 8,6C8,6.5523 7.5523,7 7,7C6.4477,7 6,6.5523 6,6Z"
|
||||
android:fillColor="#002357"/>
|
||||
<path
|
||||
android:pathData="M17,5.3265C16.3744,5.1142 15.7013,5 15,5C11.6863,5 9,7.55 9,10.95C9,14.8397 12.441,19.8863 14.0222,22H15C15.4484,22 15.8623,21.8525 16.1958,21.6033C16.4203,21.2762 16.672,20.9017 16.9394,20.4904C16.979,20.3335 17,20.1692 17,20V5.3265Z"
|
||||
android:strokeAlpha="0.1"
|
||||
android:fillColor="#000000"
|
||||
android:fillAlpha="0.1"/>
|
||||
<path
|
||||
android:pathData="M22,9.95C22,13.9575 17.1491,20.7132 16.3199,21.8388C16.243,21.9431 16.1239,22 15.9943,22V22C15.8712,22 15.7575,21.9487 15.6805,21.8526C14.8522,20.8194 10,14.569 10,9.95C10,6.55 12.6863,4 16,4C19.3137,4 22,6.55 22,9.95Z"
|
||||
android:fillColor="#FF8800"/>
|
||||
<path
|
||||
android:pathData="M16,10m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"
|
||||
android:fillColor="#B35F00"/>
|
||||
</vector>
|
|
@ -26,9 +26,9 @@
|
|||
android:id="@+id/common_graphs_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
android:orientation="vertical">
|
||||
tools:visibility="visible">
|
||||
|
||||
<com.github.mikephil.charting.charts.LineChart
|
||||
android:id="@+id/line_chart"
|
||||
|
@ -42,17 +42,22 @@
|
|||
android:id="@+id/custom_graphs_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
android:orientation="vertical">
|
||||
tools:visibility="visible">
|
||||
|
||||
<com.github.mikephil.charting.charts.HorizontalBarChart
|
||||
android:id="@+id/horizontal_chart"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/route_info_chart_height" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/route_items"
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?attr/list_divider" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/route_legend"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
|
@ -65,48 +70,106 @@
|
|||
android:id="@+id/message_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/content_padding"
|
||||
android:layout_marginLeft="@dimen/content_padding"
|
||||
android:layout_marginTop="@dimen/content_padding_small"
|
||||
android:layout_marginEnd="@dimen/content_padding"
|
||||
android:layout_marginRight="@dimen/content_padding"
|
||||
android:layout_marginBottom="@dimen/content_padding_small"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
android:orientation="horizontal">
|
||||
tools:visibility="visible">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/message_icon"
|
||||
android:layout_width="@dimen/standard_icon_size"
|
||||
android:layout_height="@dimen/standard_icon_size"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginEnd="@dimen/content_padding"
|
||||
android:layout_marginRight="@dimen/content_padding"
|
||||
android:tint="?attr/default_icon_color"
|
||||
tools:src="@drawable/ic_action_info_dark" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
android:layout_width="@dimen/card_button_progress_size"
|
||||
android:layout_height="@dimen/card_button_progress_size"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginEnd="@dimen/content_padding"
|
||||
android:layout_marginRight="@dimen/content_padding"
|
||||
android:indeterminate="true"
|
||||
android:visibility="gone" />
|
||||
|
||||
<net.osmand.plus.widgets.TextViewEx
|
||||
android:id="@+id/message_text"
|
||||
android:layout_width="0dp"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:layout_weight="1"
|
||||
android:letterSpacing="@dimen/description_letter_spacing"
|
||||
android:lineSpacingMultiplier="@dimen/bottom_sheet_text_spacing_multiplier"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="@dimen/default_desc_text_size"
|
||||
osmand:typeface="@string/font_roboto_regular"
|
||||
tools:text="Altitude data available only on the roads, you need to calculate a route using “Route between points” to get it." />
|
||||
android:layout_marginStart="@dimen/content_padding"
|
||||
android:layout_marginLeft="@dimen/content_padding"
|
||||
android:layout_marginTop="@dimen/content_padding_small"
|
||||
android:layout_marginEnd="@dimen/content_padding"
|
||||
android:layout_marginRight="@dimen/content_padding"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/message_icon"
|
||||
android:layout_width="@dimen/standard_icon_size"
|
||||
android:layout_height="@dimen/standard_icon_size"
|
||||
android:layout_marginEnd="@dimen/content_padding"
|
||||
android:layout_marginRight="@dimen/content_padding"
|
||||
android:tint="?attr/default_icon_color"
|
||||
tools:src="@drawable/ic_action_info_dark" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
android:layout_width="@dimen/card_button_progress_size"
|
||||
android:layout_height="@dimen/card_button_progress_size"
|
||||
android:layout_marginEnd="@dimen/content_padding"
|
||||
android:layout_marginRight="@dimen/content_padding"
|
||||
android:indeterminate="true"
|
||||
android:visibility="gone" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<net.osmand.plus.widgets.TextViewEx
|
||||
android:id="@+id/message_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:letterSpacing="@dimen/description_letter_spacing"
|
||||
android:lineSpacingMultiplier="@dimen/bottom_sheet_text_spacing_multiplier"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="@dimen/default_desc_text_size"
|
||||
osmand:typeface="@string/font_roboto_regular"
|
||||
tools:text="Altitude data available only on the roads, you need to calculate a route using “Route between points” to get it." />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/btn_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/content_padding"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<View
|
||||
android:layout_width="@dimen/standard_icon_size"
|
||||
android:layout_height="@dimen/standard_icon_size"
|
||||
android:layout_marginLeft="@dimen/content_padding"
|
||||
android:layout_marginStart="@dimen/content_padding"
|
||||
android:layout_marginEnd="@dimen/content_padding"
|
||||
android:layout_marginRight="@dimen/content_padding"
|
||||
android:visibility="invisible"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:minHeight="@dimen/bottom_sheet_list_item_height"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?attr/divider_color_basic" />
|
||||
|
||||
<net.osmand.plus.widgets.TextViewEx
|
||||
android:id="@+id/btn_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:layout_weight="1"
|
||||
android:gravity="start|center_vertical"
|
||||
android:lineSpacingMultiplier="@dimen/bottom_sheet_text_spacing_multiplier"
|
||||
android:textColor="@color/preference_category_title"
|
||||
android:textSize="@dimen/default_list_text_size"
|
||||
osmand:typeface="@string/font_roboto_medium"
|
||||
tools:text="@string/route_between_points" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
|
|
@ -84,26 +84,11 @@
|
|||
|
||||
</ScrollView>
|
||||
|
||||
<net.osmand.plus.widgets.TextViewEx
|
||||
android:id="@+id/inapp_descr"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/list_content_padding_large"
|
||||
android:layout_marginRight="@dimen/list_content_padding_large"
|
||||
android:layout_marginTop="@dimen/title_padding"
|
||||
android:gravity="center"
|
||||
android:text="@string/osm_live_payment_desc"
|
||||
android:textColor="?attr/card_description_text_color"
|
||||
android:textSize="@dimen/default_desc_text_size"
|
||||
osmand:typeface="@string/font_roboto_regular"
|
||||
android:layout_marginStart="@dimen/list_content_padding_large"
|
||||
android:layout_marginEnd="@dimen/list_content_padding_large" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/card_padding"
|
||||
android:layout_marginTop="@dimen/card_padding">
|
||||
android:layout_marginTop="@dimen/title_padding">
|
||||
|
||||
<include layout="@layout/purchase_dialog_card_shadow_button"/>
|
||||
|
|
@ -2218,4 +2218,10 @@
|
|||
<string name="all_previous_segments">Всички предишни сегменти</string>
|
||||
<string name="only_selected_segment_recalc">Само избраният сегмент ще бъде преизчислен съобразно избрания профил.</string>
|
||||
<string name="all_next_segments_will_be_recalc">Всички следващи сегменти ще бъдат преизчислени с помощта на избрания профил.</string>
|
||||
<string name="plan_route_change_route_type_after">Промяна типа на маршрута след</string>
|
||||
<string name="simplified_track">Опростена пътека</string>
|
||||
<string name="simplified_track_description">Само линията на маршрута ще бъде запазена, точките ще бъдат изтрити.</string>
|
||||
<string name="shared_string_file_name">Име на файл</string>
|
||||
<string name="number_of_gpx_files_selected_pattern">Избрани файлове за проследяване: %s</string>
|
||||
<string name="monitoring_control_start">REC</string>
|
||||
</resources>
|
|
@ -226,14 +226,14 @@
|
|||
<string name="poi_pension_fund">صندوق بازنشستگی</string>
|
||||
<string name="poi_migration">سازمان مهاجرت</string>
|
||||
<string name="poi_tax_inspection">اداره مالیات</string>
|
||||
<string name="poi_office_administrative">دفتر اجرایی</string>
|
||||
<string name="poi_office_administrative">اداره حکومتی</string>
|
||||
<string name="poi_customs">گمرک</string>
|
||||
<string name="poi_city">شهر</string>
|
||||
<string name="poi_town">شهر</string>
|
||||
<string name="poi_village">روستا</string>
|
||||
<string name="poi_hamlet">دهکده</string>
|
||||
<string name="poi_isolated_dwelling">سکونتگاه دورافتاده</string>
|
||||
<string name="poi_suburb">حومه شهر</string>
|
||||
<string name="poi_suburb">منطقه شهری</string>
|
||||
<string name="poi_neighbourhood">محله</string>
|
||||
<string name="poi_locality">محل</string>
|
||||
<string name="poi_place_allotments"/>
|
||||
|
@ -346,7 +346,7 @@
|
|||
<string name="poi_artwork">اثر هنری</string>
|
||||
<string name="poi_archaeological_site">مرکز باستان شناسی</string>
|
||||
<string name="poi_battlefield">میدان جنگ</string>
|
||||
<string name="poi_boundary_stone">سنگ یادبود</string>
|
||||
<string name="poi_boundary_stone">سنگ مرز</string>
|
||||
<string name="poi_historic_cannon">توپ تاریخی</string>
|
||||
<string name="poi_castle">قلعه</string>
|
||||
<string name="poi_city_gate">دروازه شهر</string>
|
||||
|
@ -1452,4 +1452,5 @@
|
|||
<string name="poi_access_bus">دسترسی اتوبوس</string>
|
||||
<string name="poi_climbing_crag">بله</string>
|
||||
<string name="poi_protected_area">منطقهٔ حفاظتشده</string>
|
||||
<string name="poi_badminton">بدمینتون</string>
|
||||
</resources>
|
|
@ -222,7 +222,7 @@
|
|||
<string name="version_index_is_not_supported">La versione dell\'indice \'\'{0}\'\' non è supportata</string>
|
||||
<string name="osmand_routing_experimental">Il routing offline di OsmAnd è sperimentale e non funziona per distanze superiori ai 20 km.
|
||||
\n
|
||||
\nNavigazione eseguita temporaneamente tramite servizio online CloudMade.</string>
|
||||
\nNavigazione temporaneamente spostata sul servizio online CloudMade.</string>
|
||||
<string name="specified_dir_doesnt_exist">Impossibile trovare la cartella specificata.</string>
|
||||
<string name="application_dir">Cartella salvataggio dei dati</string>
|
||||
<string name="osmand_net_previously_installed">Tutti i dati offline nella vecchia app verranno supportati dalla nuova, ma i Preferiti devono essere esportati dalla vecchia app e importati nella nuova.</string>
|
||||
|
@ -313,13 +313,13 @@
|
|||
<string name="gps_provider">GPS</string>
|
||||
<string name="int_seconds">secondi</string>
|
||||
<string name="int_min">min.</string>
|
||||
<string name="background_service_int_descr">Intervallo di tempo di risveglio usato dal servizio in background:</string>
|
||||
<string name="background_service_int_descr">Intervallo di risveglio usato dal servizio in background:</string>
|
||||
<string name="background_service_int">Intervallo di risveglio GPS</string>
|
||||
<string name="background_service_provider_descr">Fornitore di posizione utilizzato dal servizio in background:</string>
|
||||
<string name="background_service_provider_descr">Metodo di localizzazione utilizzato dal servizio in background:</string>
|
||||
<string name="background_service_provider">Fornitore di posizionamento</string>
|
||||
<string name="background_router_service_descr">Traccia la posizione mentre lo schermo è spento.</string>
|
||||
<string name="background_router_service">Esegui OsmAnd in background</string>
|
||||
<string name="off_router_service_no_gps_available">Il servizio di navigazione in background di OsmAnd necessita del sistema di posizionamento attivato.</string>
|
||||
<string name="off_router_service_no_gps_available">Il servizio di navigazione in background necessita di un sistema di posizionamento attivo.</string>
|
||||
<string name="hide_poi_filter">Nascondi filtro</string>
|
||||
<string name="show_poi_filter">Visualizza filtro</string>
|
||||
<string name="search_poi_filter">Filtro</string>
|
||||
|
@ -2695,13 +2695,13 @@
|
|||
<string name="wiki_article_search_text">Sto cercando l’articolo Wiki corrispondente</string>
|
||||
<string name="wiki_article_not_found">Articolo non trovato</string>
|
||||
<string name="how_to_open_wiki_title">Come aprire gli articoli di Wikipedia?</string>
|
||||
<string name="osmand_plus_extended_description_part2">Navigazione
|
||||
\n • Funziona online (veloce) o offline (nessuna tariffa di roaming quando sei all\'estero)
|
||||
\n • Guida vocale svolta-dopo-svolta (voci registrate e sintetizzate)
|
||||
\n • Guida sulla corsia opzionale, visualizzazione dei nomi delle strade, e tempo di arrivo stimato
|
||||
\n • Supporto per punti intermedi del tuo itinerario
|
||||
\n • Rielaborazione del percorso automatico ogni volta che si devia
|
||||
\n • Ricerca di posti per indirizzo, per tipo (es: ristorante, albergo, stazione di servizio, museo) o per coordinate geografiche
|
||||
<string name="osmand_plus_extended_description_part2">Navigazione
|
||||
\n • Funziona online (veloce) o offline (nessuna tariffa di roaming quando sei all\'estero)
|
||||
\n • Guida vocale svolta-per-svolta (voci registrate e sintetizzate)
|
||||
\n • Indicazioni di corsia opzionali, visualizzazione dei nomi delle strade, e tempo di arrivo stimato
|
||||
\n • Supporto dei punti intermedi del tuo itinerario
|
||||
\n • Rielaborazione del percorso automatico ogni volta che si devia
|
||||
\n • Ricerca di luoghi per indirizzo, per tipo (es: ristorante, albergo, stazione di servizio, museo) o per coordinate geografiche
|
||||
\n</string>
|
||||
<string name="hide_full_description">Nascondi la descrizione completa</string>
|
||||
<string name="show_full_description">Mostra la descrizione completa</string>
|
||||
|
@ -2743,12 +2743,12 @@
|
|||
\n • Condividi la tua posizione in modo tale che i tuoi amici possano trovarti
|
||||
\n</string>
|
||||
<string name="osmand_plus_extended_description_part6">Funzioni per pedoni e velocipedi
|
||||
\n • Mostra percorsi per pedoni, escursionisti e biciclette, ottimo per le attività all’aperto
|
||||
\n • Mostra percorsi per pedoni, escursionisti e biciclette, ottimo per le attività all\'aperto
|
||||
\n • Navigazione e visualizzazione speciali per pedoni e velocipedi
|
||||
\n • Scegli se mostrare le fermate del trasporto pubblico (bus, tram e treno) inclusi i nomi delle linee
|
||||
\n • Scegli se registrare la tua visita su un file GPX locale oppure su di un servizio online
|
||||
\n • Scegli se mostrare la velocità e l’altitudine
|
||||
\n • Mostra le curve di livello e l’ombreggiatura dei rilievi</string>
|
||||
\n • Mostra le curve di livello e l’ombreggiatura dei rilievi (attraverso il componente aggiuntivo)</string>
|
||||
<string name="unirs_render_descr">Modifica dello stile standard per un maggior contrasto delle strade pedonali e piste ciclabili. Utilizza i colori della versione vecchia di Mapnik.</string>
|
||||
<string name="access_intermediate_arrival_time">Orario di arrivo intermedio</string>
|
||||
<string name="map_widget_intermediate_time">Orario intermedio</string>
|
||||
|
@ -3940,7 +3940,7 @@
|
|||
<string name="icon_group_amenity">Amenità</string>
|
||||
<string name="icon_group_special">Speciale</string>
|
||||
<string name="icon_group_transport">Trasporto</string>
|
||||
<string name="icon_group_service">Servizio</string>
|
||||
<string name="icon_group_service">Servizi</string>
|
||||
<string name="icon_group_symbols">Simboli</string>
|
||||
<string name="icon_group_sport">Sport</string>
|
||||
<string name="icon_group_emergency">Emergenza</string>
|
||||
|
|
|
@ -3675,7 +3675,7 @@
|
|||
<string name="osmand_purchases_item">Zakupy w OsmAnd</string>
|
||||
<string name="osm_live_payment_subscription_management">Opłata zostanie pobrana z twojego konta Google Play przy potwierdzeniu zakupu.
|
||||
\n
|
||||
\nSubskrypcja automatycznie odnawia się, chyba że anulujesz ją przed dniem odnowienia. Twoje konto zostanie obciążone za okres odnowienia (miesiąc/trzy miesiące/rok) dopiero w dniu odnowienia.
|
||||
\nSubskrypcja automatycznie odnawia się, chyba że anulujesz ją przed dniem odnowienia. Twoje konto zostanie obciążone za okres odnowienia (miesiąc/trzy miesiące/rok) dopiero w dniu odnowienia.
|
||||
\n
|
||||
\nMożesz zarządzać subskrypcjami i anulować je w ustawieniach Google Play.</string>
|
||||
<string name="release_3_7">• Nowe mapy stoków offline
|
||||
|
@ -3802,7 +3802,7 @@
|
|||
<string name="use_volume_buttons_as_zoom">Przyciski głośności jako powiększenie</string>
|
||||
<string name="app_mode_wheelchair">Wózek inwalidzki</string>
|
||||
<string name="app_mode_go_cart">Gokart</string>
|
||||
<string name="osm_edit_closed_note">Zamknięta notatka OSM</string>
|
||||
<string name="osm_edit_closed_note">Zamknięta uwaga OSM</string>
|
||||
<string name="set_working_days_to_continue">Aby kontynuować, musisz ustawić dni robocze</string>
|
||||
<string name="route_between_points">Droga pomiędzy punktami</string>
|
||||
<string name="plan_a_route">Zaplanuj trasę</string>
|
||||
|
@ -3844,7 +3844,7 @@
|
|||
<string name="plan_route_exit_dialog_descr">Czy na pewno chcesz odrzucić wszystkie zmiany w zaplanowanej trasie, zamykając ją\?</string>
|
||||
<string name="in_case_of_reverse_direction">W przypadku odwrotnego kierunku</string>
|
||||
<string name="navigate_to_track_descr">Przejdź z mojej lokalizacji na trasę</string>
|
||||
<string name="app_mode_wheelchair_forward">Wózek naprzód</string>
|
||||
<string name="app_mode_wheelchair_forward">Wózek inwalidzki naprzód</string>
|
||||
<string name="show_gpx">Ślady</string>
|
||||
<string name="follow_track">Podążanie za śladem</string>
|
||||
<string name="import_track_descr">Wybierz plik śladu do śledzenia lub zaimportuj go z urządzenia.</string>
|
||||
|
@ -3913,7 +3913,7 @@
|
|||
<string name="sort_name_ascending">Nazwa: A – Z</string>
|
||||
<string name="what_is_new">Co nowego</string>
|
||||
<string name="start_finish_icons">Ikony start/koniec</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 naliczona za wybrany okres. Anuluj ją w AppGallery w dowolnym momencie.</string>
|
||||
<string name="osm_live_payment_subscription_management_hw">Płatność zostanie pobrana z konta AppGallery po potwierdzeniu zakupu.
|
||||
\n
|
||||
|
@ -3935,4 +3935,21 @@
|
|||
<string name="osm_edit_logout_success">Wylogowanie powiodło się</string>
|
||||
<string name="file_already_imported">Plik jest już zaimportowany do OsmAnd</string>
|
||||
<string name="ltr_or_rtl_combine_via_dash">%1$s — %2$s</string>
|
||||
<string name="navigate_point_mgrs">MGRS</string>
|
||||
<string name="navigate_point_format_mgrs">MGRS</string>
|
||||
<string name="use_two_phase_routing">Użyj 2-fazowego algorytmu routingu A *</string>
|
||||
<string name="shared_string_graph">Wykres</string>
|
||||
<string name="message_need_calculate_route_before_show_graph">%1$s dane dostępne tylko na drogach, aby je uzyskać, musisz obliczyć trasę za pomocą opcji „Trasa między punktami”.</string>
|
||||
<string name="message_graph_will_be_available_after_recalculation">Poczekaj na ponowne obliczenie trasy.
|
||||
\nWykres będzie dostępny po ponownym obliczeniu.</string>
|
||||
<string name="shared_string_local_maps">Mapy lokalne</string>
|
||||
<string name="app_mode_gap">Luka</string>
|
||||
<string name="icon_group_amenity">Udogodnienie</string>
|
||||
<string name="icon_group_special">Specjalne</string>
|
||||
<string name="icon_group_transport">Transport</string>
|
||||
<string name="icon_group_service">Usługi</string>
|
||||
<string name="icon_group_symbols">Symbole</string>
|
||||
<string name="icon_group_sport">Sport</string>
|
||||
<string name="icon_group_emergency">Służby ratunkowe</string>
|
||||
<string name="icon_group_travel">Podróże</string>
|
||||
</resources>
|
|
@ -3833,4 +3833,6 @@
|
|||
<string name="poi_parking_sheds">Barracão</string>
|
||||
<string name="poi_parking_rooftop">Telhado</string>
|
||||
<string name="poi_gpx_point">Ponto GPX</string>
|
||||
<string name="poi_radar_tower">Torre de radar</string>
|
||||
<string name="poi_parking_layby">Área de repouso</string>
|
||||
</resources>
|
|
@ -3835,4 +3835,5 @@
|
|||
<string name="poi_fuel_lng">СПГ</string>
|
||||
<string name="poi_parking_sheds">Навесы</string>
|
||||
<string name="poi_gpx_point">Точка GPX</string>
|
||||
<string name="poi_radar_tower">Радиолокационная вышка</string>
|
||||
</resources>
|
|
@ -3937,4 +3937,8 @@
|
|||
<string name="icon_group_sport">Спорт</string>
|
||||
<string name="icon_group_emergency">Экстренные службы</string>
|
||||
<string name="icon_group_travel">Путешествие</string>
|
||||
<string name="navigate_point_mgrs">MGRS</string>
|
||||
<string name="navigate_point_format_mgrs">MGRS</string>
|
||||
<string name="mgrs_format_descr">OsmAnd использует MGRS, который похож на формат UTM NATO.</string>
|
||||
<string name="use_native_pt">Развитие местного общественного транспорта</string>
|
||||
</resources>
|
|
@ -19,6 +19,7 @@
|
|||
<attr name="expandable_list_background" format="color"/>
|
||||
<attr name="bg_color" format="reference" />
|
||||
<attr name="bg_circle" format="reference"/>
|
||||
<attr name="bg_circle_contour" format="reference" />
|
||||
<attr name="btn_round" format="reference" />
|
||||
<attr name="btn_round_border" format="reference" />
|
||||
<attr name="btn_round_border_2" format="reference" />
|
||||
|
|
|
@ -11,6 +11,12 @@
|
|||
Thx - Hardy
|
||||
|
||||
-->
|
||||
<string name="subscription_on_hold_title">OsmAnd Live subscription is on hold</string>
|
||||
<string name="subscription_paused_title">OsmAnd Live subscription has been paused</string>
|
||||
<string name="subscription_expired_title">OsmAnd Live subscription has been expired</string>
|
||||
<string name="subscription_payment_issue_title">There is a problem with your subscription. Click the button to go to the Google Play subscription settings to fix your payment method.</string>
|
||||
<string name="manage_subscription">Manage subscription</string>
|
||||
<string name="message_you_need_add_two_points_to_show_graphs">You must add at least two points.</string>
|
||||
<string name="icon_group_travel">Travel</string>
|
||||
<string name="icon_group_emergency">Emergency</string>
|
||||
<string name="icon_group_sport">Sport</string>
|
||||
|
|
|
@ -92,6 +92,7 @@
|
|||
<item name="actionBarStyle">@style/Widget.Styled.ActionBarLight</item>
|
||||
<item name="bg_color">@color/list_background_color_light</item>
|
||||
<item name="bg_circle">@drawable/circle_background_light</item>
|
||||
<item name="bg_circle_contour">@drawable/circle_contour_bg_light</item>
|
||||
<item name="btn_round">@drawable/btn_round_light</item>
|
||||
<item name="btn_round_border">@drawable/btn_round_border_light</item>
|
||||
<item name="btn_round_border_2">@drawable/btn_round_border_light_2</item>
|
||||
|
@ -394,6 +395,7 @@
|
|||
<item name="actionBarStyle">@style/Widget.Styled.ActionBarDark</item>
|
||||
<item name="bg_color">@color/list_background_color_dark</item>
|
||||
<item name="bg_circle">@drawable/circle_background_dark</item>
|
||||
<item name="bg_circle_contour">@drawable/circle_contour_bg_dark</item>
|
||||
<item name="btn_round">@drawable/btn_round_dark</item>
|
||||
<item name="btn_round_border">@drawable/btn_round_border_dark</item>
|
||||
<item name="btn_round_border_2">@drawable/btn_round_border_dark_2</item>
|
||||
|
|
|
@ -21,8 +21,10 @@ import net.osmand.plus.OsmandPlugin;
|
|||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.inapp.InAppPurchases.InAppPurchase;
|
||||
import net.osmand.plus.inapp.InAppPurchases.InAppSubscription;
|
||||
import net.osmand.plus.inapp.InAppPurchases.InAppSubscription.SubscriptionState;
|
||||
import net.osmand.plus.inapp.InAppPurchasesImpl.InAppPurchaseLiveUpdatesOldSubscription;
|
||||
import net.osmand.plus.inapp.util.BillingManager;
|
||||
import net.osmand.plus.settings.backend.CommonPreference;
|
||||
import net.osmand.plus.settings.backend.OsmandPreference;
|
||||
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||
import net.osmand.plus.srtmplugin.SRTMPlugin;
|
||||
|
@ -33,6 +35,7 @@ import java.text.ParseException;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
|
||||
|
||||
|
@ -139,6 +142,7 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
|
|||
for (Purchase p : purchases) {
|
||||
skuSubscriptions.add(p.getSku());
|
||||
}
|
||||
skuSubscriptions.addAll(subscriptionStateMap.keySet());
|
||||
|
||||
BillingManager billingManager = getBillingManager();
|
||||
// Have we been disposed of in the meantime? If so, quit.
|
||||
|
@ -291,7 +295,7 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
|
|||
}
|
||||
|
||||
// Listener that's called when we finish querying the items and subscriptions we own
|
||||
private SkuDetailsResponseListener mSkuDetailsResponseListener = new SkuDetailsResponseListener() {
|
||||
private final SkuDetailsResponseListener mSkuDetailsResponseListener = new SkuDetailsResponseListener() {
|
||||
|
||||
@NonNull
|
||||
private List<String> getAllOwnedSubscriptionSkus() {
|
||||
|
@ -304,6 +308,15 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
|
|||
}
|
||||
}
|
||||
}
|
||||
for (Entry<String, SubscriptionState> entry : subscriptionStateMap.entrySet()) {
|
||||
SubscriptionState state = entry.getValue();
|
||||
if (state == SubscriptionState.PAUSED || state == SubscriptionState.ON_HOLD) {
|
||||
String sku = entry.getKey();
|
||||
if (!result.contains(sku)) {
|
||||
result.add(sku);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -399,35 +412,28 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
|
|||
// Do we have the live updates?
|
||||
boolean subscribedToLiveUpdates = false;
|
||||
List<Purchase> liveUpdatesPurchases = new ArrayList<>();
|
||||
for (InAppPurchase p : getLiveUpdates().getAllSubscriptions()) {
|
||||
Purchase purchase = getPurchase(p.getSku());
|
||||
if (purchase != null) {
|
||||
liveUpdatesPurchases.add(purchase);
|
||||
for (InAppSubscription s : getLiveUpdates().getAllSubscriptions()) {
|
||||
Purchase purchase = getPurchase(s.getSku());
|
||||
if (purchase != null || s.getState().isActive()) {
|
||||
if (purchase != null) {
|
||||
liveUpdatesPurchases.add(purchase);
|
||||
}
|
||||
if (!subscribedToLiveUpdates) {
|
||||
subscribedToLiveUpdates = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
OsmandPreference<Long> subscriptionCancelledTime = ctx.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_TIME;
|
||||
if (!subscribedToLiveUpdates && ctx.getSettings().LIVE_UPDATES_PURCHASED.get()) {
|
||||
if (subscriptionCancelledTime.get() == 0) {
|
||||
subscriptionCancelledTime.set(System.currentTimeMillis());
|
||||
ctx.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_FIRST_DLG_SHOWN.set(false);
|
||||
ctx.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_SECOND_DLG_SHOWN.set(false);
|
||||
} else if (System.currentTimeMillis() - subscriptionCancelledTime.get() > SUBSCRIPTION_HOLDING_TIME_MSEC) {
|
||||
ctx.getSettings().LIVE_UPDATES_PURCHASED.set(false);
|
||||
if (!isDepthContoursPurchased(ctx)) {
|
||||
ctx.getSettings().getCustomRenderBooleanProperty("depthContours").set(false);
|
||||
}
|
||||
ctx.getSettings().LIVE_UPDATES_PURCHASED.set(false);
|
||||
if (!isDepthContoursPurchased(ctx)) {
|
||||
ctx.getSettings().getCustomRenderBooleanProperty("depthContours").set(false);
|
||||
}
|
||||
} else if (subscribedToLiveUpdates) {
|
||||
subscriptionCancelledTime.set(0L);
|
||||
ctx.getSettings().LIVE_UPDATES_PURCHASED.set(true);
|
||||
}
|
||||
|
||||
lastValidationCheckTime = System.currentTimeMillis();
|
||||
logDebug("User " + (subscribedToLiveUpdates ? "HAS" : "DOES NOT HAVE")
|
||||
+ " live updates purchased.");
|
||||
logDebug("User " + (subscribedToLiveUpdates ? "HAS" : "DOES NOT HAVE") + " live updates purchased.");
|
||||
|
||||
OsmandSettings settings = ctx.getSettings();
|
||||
settings.INAPPS_READ.set(true);
|
||||
|
@ -490,12 +496,24 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper {
|
|||
}
|
||||
}
|
||||
if (inAppPurchase instanceof InAppSubscription) {
|
||||
InAppSubscription s = (InAppSubscription) inAppPurchase;
|
||||
|
||||
SubscriptionState state = subscriptionStateMap.get(inAppPurchase.getSku());
|
||||
s.setState(state == null ? SubscriptionState.UNDEFINED : state);
|
||||
CommonPreference<String> statePref = ctx.getSettings().registerStringPreference(
|
||||
s.getSku() + "_state", SubscriptionState.UNDEFINED.getStateStr()).makeGlobal();
|
||||
s.setPrevState(SubscriptionState.getByStateStr(statePref.get()));
|
||||
statePref.set(s.getState().getStateStr());
|
||||
if (s.getState().isGone() && s.hasStateChanged()) {
|
||||
ctx.getSettings().LIVE_UPDATES_EXPIRED_FIRST_DLG_SHOWN_TIME.set(0L);
|
||||
ctx.getSettings().LIVE_UPDATES_EXPIRED_SECOND_DLG_SHOWN_TIME.set(0L);
|
||||
}
|
||||
|
||||
String introductoryPrice = skuDetails.getIntroductoryPrice();
|
||||
String introductoryPricePeriod = skuDetails.getIntroductoryPricePeriod();
|
||||
String introductoryPriceCycles = skuDetails.getIntroductoryPriceCycles();
|
||||
long introductoryPriceAmountMicros = skuDetails.getIntroductoryPriceAmountMicros();
|
||||
if (!Algorithms.isEmpty(introductoryPrice)) {
|
||||
InAppSubscription s = (InAppSubscription) inAppPurchase;
|
||||
try {
|
||||
s.setIntroductoryInfo(new InAppPurchases.InAppSubscriptionIntroductoryInfo(s, introductoryPrice,
|
||||
introductoryPriceAmountMicros, introductoryPricePeriod, introductoryPriceCycles));
|
||||
|
|
|
@ -134,11 +134,11 @@ public class Version {
|
|||
}
|
||||
|
||||
public static boolean isDeveloperVersion(OsmandApplication ctx){
|
||||
return getAppName(ctx).contains("~") || ctx.getPackageName().equals(FREE_DEV_VERSION_NAME);
|
||||
return false;//getAppName(ctx).contains("~") || ctx.getPackageName().equals(FREE_DEV_VERSION_NAME);
|
||||
}
|
||||
|
||||
public static boolean isDeveloperBuild(OsmandApplication ctx){
|
||||
return getAppName(ctx).contains("~");
|
||||
return false;//getAppName(ctx).contains("~");
|
||||
}
|
||||
|
||||
public static String getVersionForTracker(OsmandApplication ctx) {
|
||||
|
|
|
@ -214,7 +214,7 @@ public class LocalIndexHelper {
|
|||
return result;
|
||||
}
|
||||
|
||||
public void loadVoiceData(File voiceDir, List<LocalIndexInfo> result, boolean backup, AbstractLoadLocalIndexTask loadTask) {
|
||||
private void loadVoiceData(File voiceDir, List<LocalIndexInfo> result, boolean backup, AbstractLoadLocalIndexTask loadTask) {
|
||||
if (voiceDir.canRead()) {
|
||||
//First list TTS files, they are preferred
|
||||
for (File voiceF : listFilesSorted(voiceDir)) {
|
||||
|
|
|
@ -83,7 +83,7 @@ import net.osmand.plus.base.BaseOsmAndFragment;
|
|||
import net.osmand.plus.base.ContextMenuFragment;
|
||||
import net.osmand.plus.base.FailSafeFuntions;
|
||||
import net.osmand.plus.base.MapViewTrackingUtilities;
|
||||
import net.osmand.plus.chooseplan.OsmLiveCancelledDialog;
|
||||
import net.osmand.plus.chooseplan.OsmLiveGoneDialog;
|
||||
import net.osmand.plus.dashboard.DashBaseFragment;
|
||||
import net.osmand.plus.dashboard.DashboardOnMap;
|
||||
import net.osmand.plus.dialogs.CrashBottomSheetDialogFragment;
|
||||
|
@ -845,8 +845,6 @@ public class MapActivity extends OsmandActionBarActivity implements DownloadEven
|
|||
getSupportFragmentManager().beginTransaction()
|
||||
.add(R.id.fragmentContainer, new FirstUsageWelcomeFragment(),
|
||||
FirstUsageWelcomeFragment.TAG).commitAllowingStateLoss();
|
||||
} else if (!isFirstScreenShowing() && OsmLiveCancelledDialog.shouldShowDialog(app)) {
|
||||
OsmLiveCancelledDialog.showInstance(getSupportFragmentManager());
|
||||
} else if (SendAnalyticsBottomSheetDialogFragment.shouldShowDialog(app)) {
|
||||
SendAnalyticsBottomSheetDialogFragment.showInstance(app, getSupportFragmentManager(), null);
|
||||
}
|
||||
|
@ -2290,6 +2288,9 @@ public class MapActivity extends OsmandActionBarActivity implements DownloadEven
|
|||
@Override
|
||||
public void onInAppPurchaseGetItems() {
|
||||
DiscountHelper.checkAndDisplay(this);
|
||||
if (!isFirstScreenShowing() && OsmLiveGoneDialog.shouldShowDialog(app)) {
|
||||
OsmLiveGoneDialog.showInstance(app, getSupportFragmentManager());
|
||||
}
|
||||
}
|
||||
|
||||
public enum ShowQuickSearchMode {
|
||||
|
|
|
@ -1,245 +0,0 @@
|
|||
package net.osmand.plus.chooseplan;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import androidx.annotation.ColorRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
|
||||
import net.osmand.PlatformUtil;
|
||||
import net.osmand.plus.OsmandApplication;
|
||||
import net.osmand.plus.Version;
|
||||
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||
import net.osmand.plus.settings.backend.OsmandPreference;
|
||||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.activities.MapActivity;
|
||||
import net.osmand.plus.base.BaseOsmAndDialogFragment;
|
||||
import net.osmand.plus.chooseplan.ChoosePlanDialogFragment.OsmAndFeature;
|
||||
import net.osmand.plus.inapp.InAppPurchaseHelper;
|
||||
import net.osmand.plus.inapp.InAppPurchaseHelper.InAppPurchaseListener;
|
||||
import net.osmand.plus.inapp.InAppPurchaseHelper.InAppPurchaseTaskType;
|
||||
import net.osmand.plus.widgets.TextViewEx;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import static net.osmand.plus.inapp.InAppPurchaseHelper.SUBSCRIPTION_HOLDING_TIME_MSEC;
|
||||
|
||||
public class OsmLiveCancelledDialog extends BaseOsmAndDialogFragment implements InAppPurchaseListener {
|
||||
public static final String TAG = OsmLiveCancelledDialog.class.getSimpleName();
|
||||
private static final Log LOG = PlatformUtil.getLog(OsmLiveCancelledDialog.class);
|
||||
|
||||
private OsmandApplication app;
|
||||
private InAppPurchaseHelper purchaseHelper;
|
||||
|
||||
private boolean nightMode;
|
||||
private View osmLiveButton;
|
||||
|
||||
private final OsmAndFeature[] osmLiveFeatures = {
|
||||
OsmAndFeature.DAILY_MAP_UPDATES,
|
||||
OsmAndFeature.UNLIMITED_DOWNLOADS,
|
||||
OsmAndFeature.WIKIPEDIA_OFFLINE,
|
||||
OsmAndFeature.WIKIVOYAGE_OFFLINE,
|
||||
OsmAndFeature.CONTOUR_LINES_HILLSHADE_MAPS,
|
||||
OsmAndFeature.SEA_DEPTH_MAPS,
|
||||
OsmAndFeature.UNLOCK_ALL_FEATURES,
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
app = getMyApplication();
|
||||
purchaseHelper = app.getInAppPurchaseHelper();
|
||||
nightMode = isNightMode(getMapActivity() != null);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
Activity ctx = requireActivity();
|
||||
int themeId = nightMode ? R.style.OsmandDarkTheme_DarkActionbar : R.style.OsmandLightTheme_DarkActionbar_LightStatusBar;
|
||||
Dialog dialog = new Dialog(ctx, themeId);
|
||||
Window window = dialog.getWindow();
|
||||
if (window != null) {
|
||||
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||
if (!getSettings().DO_NOT_USE_ANIMATIONS.get()) {
|
||||
window.getAttributes().windowAnimations = R.style.Animations_Alpha;
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
window.setStatusBarColor(ContextCompat.getColor(ctx, getStatusBarColor()));
|
||||
}
|
||||
}
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
Context ctx = getContext();
|
||||
if (ctx == null) {
|
||||
return null;
|
||||
}
|
||||
int themeRes = nightMode ? R.style.OsmandDarkTheme_DarkActionbar : R.style.OsmandLightTheme_DarkActionbar_LightStatusBar;
|
||||
View view = LayoutInflater.from(new ContextThemeWrapper(getContext(), themeRes))
|
||||
.inflate(R.layout.osmlive_cancelled_dialog_fragment, container, false);
|
||||
|
||||
view.findViewById(R.id.button_close).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
TextViewEx infoDescr = (TextViewEx) view.findViewById(R.id.info_description);
|
||||
StringBuilder descr = new StringBuilder();
|
||||
descr.append(getString(R.string.purchase_cancelled_dialog_descr));
|
||||
for (OsmAndFeature feature : osmLiveFeatures) {
|
||||
descr.append("\n").append("— ").append(feature.toHumanString(ctx));
|
||||
}
|
||||
infoDescr.setText(descr);
|
||||
TextViewEx inappDescr = (TextViewEx) view.findViewById(R.id.inapp_descr);
|
||||
inappDescr.setText(Version.isHuawei(app) ? R.string.osm_live_payment_desc_hw : R.string.osm_live_payment_desc);
|
||||
|
||||
osmLiveButton = view.findViewById(R.id.card_button);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public MapActivity getMapActivity() {
|
||||
Activity activity = getActivity();
|
||||
if (activity instanceof MapActivity) {
|
||||
return (MapActivity) activity;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
MapActivity mapActivity = getMapActivity();
|
||||
if (mapActivity != null) {
|
||||
mapActivity.disableDrawer();
|
||||
}
|
||||
|
||||
boolean requestingInventory = purchaseHelper != null && purchaseHelper.getActiveTask() == InAppPurchaseTaskType.REQUEST_INVENTORY;
|
||||
setupOsmLiveButton(requestingInventory);
|
||||
|
||||
OsmandPreference<Boolean> firstTimeShown = app.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_FIRST_DLG_SHOWN;
|
||||
OsmandPreference<Boolean> secondTimeShown = app.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_SECOND_DLG_SHOWN;
|
||||
if (!firstTimeShown.get()) {
|
||||
firstTimeShown.set(true);
|
||||
} else if (!secondTimeShown.get()) {
|
||||
secondTimeShown.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
|
||||
MapActivity mapActivity = getMapActivity();
|
||||
if (mapActivity != null) {
|
||||
mapActivity.enableDrawer();
|
||||
}
|
||||
}
|
||||
|
||||
@ColorRes
|
||||
protected int getStatusBarColor() {
|
||||
return nightMode ? R.color.status_bar_wikivoyage_dark : R.color.status_bar_wikivoyage_light;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(InAppPurchaseTaskType taskType, String error) {
|
||||
if (taskType == InAppPurchaseTaskType.REQUEST_INVENTORY) {
|
||||
setupOsmLiveButton(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGetItems() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemPurchased(String sku, boolean active) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showProgress(InAppPurchaseTaskType taskType) {
|
||||
if (taskType == InAppPurchaseTaskType.REQUEST_INVENTORY) {
|
||||
setupOsmLiveButton(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dismissProgress(InAppPurchaseTaskType taskType) {
|
||||
if (taskType == InAppPurchaseTaskType.REQUEST_INVENTORY) {
|
||||
setupOsmLiveButton(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void setupOsmLiveButton(boolean progress) {
|
||||
if (osmLiveButton != null) {
|
||||
ProgressBar progressBar = (ProgressBar) osmLiveButton.findViewById(R.id.card_button_progress);
|
||||
TextViewEx buttonTitle = (TextViewEx) osmLiveButton.findViewById(R.id.card_button_title);
|
||||
TextViewEx buttonSubtitle = (TextViewEx) osmLiveButton.findViewById(R.id.card_button_subtitle);
|
||||
buttonTitle.setText(getString(R.string.osm_live_plan_pricing));
|
||||
buttonSubtitle.setVisibility(View.GONE);
|
||||
if (progress) {
|
||||
buttonTitle.setVisibility(View.GONE);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
osmLiveButton.setOnClickListener(null);
|
||||
} else {
|
||||
buttonTitle.setVisibility(View.VISIBLE);
|
||||
progressBar.setVisibility(View.GONE);
|
||||
osmLiveButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dismiss();
|
||||
FragmentActivity activity = getActivity();
|
||||
if (activity != null) {
|
||||
ChoosePlanDialogFragment.showOsmLiveInstance(activity.getSupportFragmentManager());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean shouldShowDialog(OsmandApplication app) {
|
||||
OsmandSettings settings = app.getSettings();
|
||||
long cancelledTime = settings.LIVE_UPDATES_PURCHASE_CANCELLED_TIME.get();
|
||||
boolean firstTimeShown = settings.LIVE_UPDATES_PURCHASE_CANCELLED_FIRST_DLG_SHOWN.get();
|
||||
boolean secondTimeShown = settings.LIVE_UPDATES_PURCHASE_CANCELLED_SECOND_DLG_SHOWN.get();
|
||||
return cancelledTime > 0
|
||||
&& (!firstTimeShown
|
||||
|| (System.currentTimeMillis() - cancelledTime > SUBSCRIPTION_HOLDING_TIME_MSEC
|
||||
&& !secondTimeShown));
|
||||
}
|
||||
|
||||
public static void showInstance(@NonNull FragmentManager fm) {
|
||||
try {
|
||||
if (fm.findFragmentByTag(OsmLiveCancelledDialog.TAG) == null) {
|
||||
OsmLiveCancelledDialog fragment = new OsmLiveCancelledDialog();
|
||||
fragment.show(fm, OsmLiveCancelledDialog.TAG);
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
LOG.error("showInstance", e);
|
||||
}
|
||||
}
|
||||
}
|
334
OsmAnd/src/net/osmand/plus/chooseplan/OsmLiveGoneDialog.java
Normal file
334
OsmAnd/src/net/osmand/plus/chooseplan/OsmLiveGoneDialog.java
Normal file
|
@ -0,0 +1,334 @@
|
|||
package net.osmand.plus.chooseplan;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
|
||||
import androidx.annotation.ColorRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
|
||||
import net.osmand.PlatformUtil;
|
||||
import net.osmand.plus.OsmandApplication;
|
||||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.activities.MapActivity;
|
||||
import net.osmand.plus.base.BaseOsmAndDialogFragment;
|
||||
import net.osmand.plus.chooseplan.ChoosePlanDialogFragment.OsmAndFeature;
|
||||
import net.osmand.plus.inapp.InAppPurchaseHelper;
|
||||
import net.osmand.plus.inapp.InAppPurchases.InAppSubscription;
|
||||
import net.osmand.plus.settings.backend.OsmandPreference;
|
||||
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||
import net.osmand.plus.widgets.TextViewEx;
|
||||
import net.osmand.util.Algorithms;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
public abstract class OsmLiveGoneDialog extends BaseOsmAndDialogFragment {
|
||||
public static final String TAG = OsmLiveGoneDialog.class.getName();
|
||||
private static final Log LOG = PlatformUtil.getLog(OsmLiveGoneDialog.class);
|
||||
|
||||
private static final long TIME_BETWEEN_DIALOGS_MSEC = 1000 * 60 * 60 * 24 * 3; // 3 days
|
||||
|
||||
private OsmandApplication app;
|
||||
private boolean nightMode;
|
||||
private View osmLiveButton;
|
||||
|
||||
private final OsmAndFeature[] osmLiveFeatures = {
|
||||
OsmAndFeature.DAILY_MAP_UPDATES,
|
||||
OsmAndFeature.UNLIMITED_DOWNLOADS,
|
||||
OsmAndFeature.WIKIPEDIA_OFFLINE,
|
||||
OsmAndFeature.WIKIVOYAGE_OFFLINE,
|
||||
OsmAndFeature.CONTOUR_LINES_HILLSHADE_MAPS,
|
||||
OsmAndFeature.SEA_DEPTH_MAPS,
|
||||
OsmAndFeature.UNLOCK_ALL_FEATURES,
|
||||
};
|
||||
|
||||
public static class OsmLiveOnHoldDialog extends OsmLiveGoneDialog {
|
||||
public static final String TAG = OsmLiveOnHoldDialog.class.getSimpleName();
|
||||
|
||||
@Override
|
||||
protected OsmLiveButtonType getOsmLiveButtonType() {
|
||||
return OsmLiveButtonType.MANAGE_SUBSCRIPTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return getString(R.string.subscription_on_hold_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSubscriptionDescr() {
|
||||
return getString(R.string.subscription_payment_issue_title);
|
||||
}
|
||||
}
|
||||
|
||||
public static class OsmLivePausedDialog extends OsmLiveGoneDialog {
|
||||
public static final String TAG = OsmLivePausedDialog.class.getSimpleName();
|
||||
|
||||
@Override
|
||||
protected OsmLiveButtonType getOsmLiveButtonType() {
|
||||
return OsmLiveButtonType.MANAGE_SUBSCRIPTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return getString(R.string.subscription_paused_title);
|
||||
}
|
||||
}
|
||||
|
||||
public static class OsmLiveExpiredDialog extends OsmLiveGoneDialog {
|
||||
public static final String TAG = OsmLiveExpiredDialog.class.getSimpleName();
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return getString(R.string.subscription_expired_title);
|
||||
}
|
||||
}
|
||||
|
||||
protected enum OsmLiveButtonType {
|
||||
PURCHASE_SUBSCRIPTION,
|
||||
MANAGE_SUBSCRIPTION
|
||||
}
|
||||
|
||||
protected OsmLiveButtonType getOsmLiveButtonType() {
|
||||
return OsmLiveButtonType.PURCHASE_SUBSCRIPTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
app = getMyApplication();
|
||||
nightMode = isNightMode(getMapActivity() != null);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
Activity ctx = requireActivity();
|
||||
int themeId = nightMode ? R.style.OsmandDarkTheme_DarkActionbar : R.style.OsmandLightTheme_DarkActionbar_LightStatusBar;
|
||||
Dialog dialog = new Dialog(ctx, themeId);
|
||||
Window window = dialog.getWindow();
|
||||
if (window != null) {
|
||||
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||
if (!getSettings().DO_NOT_USE_ANIMATIONS.get()) {
|
||||
window.getAttributes().windowAnimations = R.style.Animations_Alpha;
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
window.setStatusBarColor(ContextCompat.getColor(ctx, getStatusBarColor()));
|
||||
}
|
||||
}
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
Context ctx = getContext();
|
||||
if (ctx == null) {
|
||||
return null;
|
||||
}
|
||||
int themeRes = nightMode ? R.style.OsmandDarkTheme_DarkActionbar : R.style.OsmandLightTheme_DarkActionbar_LightStatusBar;
|
||||
View view = LayoutInflater.from(new ContextThemeWrapper(getContext(), themeRes))
|
||||
.inflate(R.layout.osmlive_gone_dialog_fragment, container, false);
|
||||
|
||||
view.findViewById(R.id.button_close).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
TextViewEx title = (TextViewEx) view.findViewById(R.id.title);
|
||||
title.setText(getTitle());
|
||||
TextViewEx infoDescr = (TextViewEx) view.findViewById(R.id.info_description);
|
||||
StringBuilder descr = new StringBuilder();
|
||||
String subscriptionDescr = getSubscriptionDescr();
|
||||
if (!Algorithms.isEmpty(subscriptionDescr)) {
|
||||
descr.append(subscriptionDescr).append("\n\n");
|
||||
}
|
||||
descr.append(getString(R.string.purchase_cancelled_dialog_descr));
|
||||
for (OsmAndFeature feature : osmLiveFeatures) {
|
||||
descr.append("\n").append("— ").append(feature.toHumanString(ctx));
|
||||
}
|
||||
infoDescr.setText(descr);
|
||||
|
||||
osmLiveButton = view.findViewById(R.id.card_button);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
protected abstract String getTitle();
|
||||
|
||||
protected String getSubscriptionDescr() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public MapActivity getMapActivity() {
|
||||
Activity activity = getActivity();
|
||||
if (activity instanceof MapActivity) {
|
||||
return (MapActivity) activity;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
MapActivity mapActivity = getMapActivity();
|
||||
if (mapActivity != null) {
|
||||
mapActivity.disableDrawer();
|
||||
}
|
||||
|
||||
setupOsmLiveButton();
|
||||
|
||||
OsmandPreference<Long> firstTimeShownTime = app.getSettings().LIVE_UPDATES_EXPIRED_FIRST_DLG_SHOWN_TIME;
|
||||
OsmandPreference<Long> secondTimeShownTime = app.getSettings().LIVE_UPDATES_EXPIRED_SECOND_DLG_SHOWN_TIME;
|
||||
if (firstTimeShownTime.get() == 0) {
|
||||
firstTimeShownTime.set(System.currentTimeMillis());
|
||||
} else if (secondTimeShownTime.get() == 0) {
|
||||
secondTimeShownTime.set(System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
|
||||
MapActivity mapActivity = getMapActivity();
|
||||
if (mapActivity != null) {
|
||||
mapActivity.enableDrawer();
|
||||
}
|
||||
}
|
||||
|
||||
@ColorRes
|
||||
protected int getStatusBarColor() {
|
||||
return nightMode ? R.color.status_bar_wikivoyage_dark : R.color.status_bar_wikivoyage_light;
|
||||
}
|
||||
|
||||
private void setupOsmLiveButton() {
|
||||
if (osmLiveButton != null) {
|
||||
TextViewEx buttonTitle = (TextViewEx) osmLiveButton.findViewById(R.id.card_button_title);
|
||||
TextViewEx buttonSubtitle = (TextViewEx) osmLiveButton.findViewById(R.id.card_button_subtitle);
|
||||
switch (getOsmLiveButtonType()) {
|
||||
case PURCHASE_SUBSCRIPTION:
|
||||
buttonTitle.setText(getString(R.string.osm_live_plan_pricing));
|
||||
osmLiveButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dismiss();
|
||||
FragmentActivity activity = getActivity();
|
||||
if (activity != null) {
|
||||
ChoosePlanDialogFragment.showOsmLiveInstance(activity.getSupportFragmentManager());
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
case MANAGE_SUBSCRIPTION:
|
||||
buttonTitle.setText(getString(R.string.manage_subscription));
|
||||
osmLiveButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dismiss();
|
||||
FragmentActivity activity = getActivity();
|
||||
if (activity != null) {
|
||||
InAppSubscription expiredSubscription = getExpiredSubscription((OsmandApplication) activity.getApplication());
|
||||
if (expiredSubscription != null) {
|
||||
manageSubscription(expiredSubscription.getSku());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
buttonSubtitle.setVisibility(View.GONE);
|
||||
buttonTitle.setVisibility(View.VISIBLE);
|
||||
osmLiveButton.findViewById(R.id.card_button_progress).setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void manageSubscription(@Nullable String sku) {
|
||||
Context ctx = getContext();
|
||||
if (ctx != null) {
|
||||
String url = "https://play.google.com/store/account/subscriptions?package=" + ctx.getPackageName();
|
||||
if (!Algorithms.isEmpty(sku)) {
|
||||
url += "&sku=" + sku;
|
||||
}
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static InAppSubscription getExpiredSubscription(@NonNull OsmandApplication app) {
|
||||
if (!app.getSettings().LIVE_UPDATES_PURCHASED.get()) {
|
||||
InAppPurchaseHelper purchaseHelper = app.getInAppPurchaseHelper();
|
||||
return purchaseHelper.getLiveUpdates().getTopExpiredSubscription();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean shouldShowDialog(@NonNull OsmandApplication app) {
|
||||
InAppSubscription expiredSubscription = getExpiredSubscription(app);
|
||||
if (expiredSubscription == null) {
|
||||
return false;
|
||||
}
|
||||
OsmandSettings settings = app.getSettings();
|
||||
long firstTimeShownTime = settings.LIVE_UPDATES_EXPIRED_FIRST_DLG_SHOWN_TIME.get();
|
||||
long secondTimeShownTime = settings.LIVE_UPDATES_EXPIRED_SECOND_DLG_SHOWN_TIME.get();
|
||||
return firstTimeShownTime == 0
|
||||
|| (System.currentTimeMillis() - firstTimeShownTime > TIME_BETWEEN_DIALOGS_MSEC && secondTimeShownTime == 0);
|
||||
}
|
||||
|
||||
public static void showInstance(@NonNull OsmandApplication app, @NonNull FragmentManager fm) {
|
||||
try {
|
||||
InAppSubscription expiredSubscription = getExpiredSubscription(app);
|
||||
if (expiredSubscription == null) {
|
||||
return;
|
||||
}
|
||||
String tag = null;
|
||||
DialogFragment fragment = null;
|
||||
switch (expiredSubscription.getState()) {
|
||||
case ON_HOLD:
|
||||
tag = OsmLiveOnHoldDialog.TAG;
|
||||
if (fm.findFragmentByTag(tag) == null) {
|
||||
fragment = new OsmLiveOnHoldDialog();
|
||||
}
|
||||
break;
|
||||
case PAUSED:
|
||||
tag = OsmLivePausedDialog.TAG;
|
||||
if (fm.findFragmentByTag(tag) == null) {
|
||||
fragment = new OsmLivePausedDialog();
|
||||
}
|
||||
break;
|
||||
case EXPIRED:
|
||||
tag = OsmLiveExpiredDialog.TAG;
|
||||
if (fm.findFragmentByTag(tag) == null) {
|
||||
fragment = new OsmLiveExpiredDialog();
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (fragment != null) {
|
||||
fragment.show(fm, tag);
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
LOG.error("showInstance", e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ import android.widget.TextView;
|
|||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import net.osmand.AndroidUtils;
|
||||
import net.osmand.data.FavouritePoint;
|
||||
|
@ -32,6 +33,8 @@ import net.osmand.plus.R;
|
|||
import net.osmand.plus.UiUtilities;
|
||||
import net.osmand.plus.activities.FavoritesListFragment.FavouritesAdapter;
|
||||
import net.osmand.plus.activities.MapActivity;
|
||||
import net.osmand.plus.mapcontextmenu.editors.FavoritePointEditor;
|
||||
import net.osmand.plus.mapcontextmenu.editors.FavoritePointEditorFragmentNew;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
|
@ -79,20 +82,24 @@ public class FavoriteDialogs {
|
|||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if(dlgHolder != null && dlgHolder.length > 0 && dlgHolder[0] != null) {
|
||||
if (dlgHolder != null && dlgHolder.length > 0 && dlgHolder[0] != null) {
|
||||
dlgHolder[0].dismiss();
|
||||
}
|
||||
FavouritePoint point = (FavouritePoint) args.getSerializable(KEY_FAVORITE);
|
||||
if (helper.editFavourite(fp, point.getLatitude(), point.getLongitude())) {
|
||||
if (point != null && helper.editFavourite(fp, point.getLatitude(), point.getLongitude())) {
|
||||
helper.deleteFavourite(point);
|
||||
if (activity instanceof MapActivity) {
|
||||
((MapActivity) activity).getContextMenu()
|
||||
MapActivity mapActivity = (MapActivity) activity;
|
||||
Fragment fragment = mapActivity.getSupportFragmentManager()
|
||||
.findFragmentByTag(FavoritePointEditor.TAG);
|
||||
if (fragment instanceof FavoritePointEditorFragmentNew) {
|
||||
((FavoritePointEditorFragmentNew) fragment).exitEditing();
|
||||
}
|
||||
mapActivity.getContextMenu()
|
||||
.show(new LatLon(point.getLatitude(), point.getLongitude()), fp.getPointDescription(activity), fp);
|
||||
mapActivity.getMapView().refreshMap();
|
||||
}
|
||||
}
|
||||
if (activity instanceof MapActivity) {
|
||||
((MapActivity) activity).getMapView().refreshMap();
|
||||
}
|
||||
}
|
||||
});
|
||||
builder.show();
|
||||
|
|
|
@ -342,7 +342,7 @@ public class DownloadActivityType {
|
|||
return FileNameTranslationHelper.getFontName(ctx, getBasename(indexItem));
|
||||
}
|
||||
final String basename = getBasename(indexItem);
|
||||
if (basename.endsWith(FileNameTranslationHelper.WIKI_NAME)){
|
||||
if (basename.endsWith(FileNameTranslationHelper.WIKI_NAME)) {
|
||||
return FileNameTranslationHelper.getWikiName(ctx, basename);
|
||||
}
|
||||
// if (this == HILLSHADE_FILE){
|
||||
|
@ -357,7 +357,7 @@ public class DownloadActivityType {
|
|||
final int ind = basename.indexOf("addresses-nationwide");
|
||||
String downloadName = basename.substring(0, ind - 1) + basename.substring(ind + "addresses-nationwide".length());
|
||||
return osmandRegions.getLocaleName(downloadName, includingParent) +
|
||||
" "+ ctx.getString(R.string.index_item_nation_addresses);
|
||||
" " + ctx.getString(R.string.index_item_nation_addresses);
|
||||
} else if (basename.startsWith("Depth_")) {
|
||||
final int extInd = basename.indexOf("osmand_ext");
|
||||
String downloadName = extInd == -1 ? basename.substring(6, basename.length()).replace('_', ' ')
|
||||
|
@ -438,11 +438,11 @@ public class DownloadActivityType {
|
|||
}
|
||||
if (this == HILLSHADE_FILE) {
|
||||
return fileName.substring(0, fileName.length() - IndexConstants.SQLITE_EXT.length())
|
||||
.replace(FileNameTranslationHelper.HILL_SHADE, "");
|
||||
.replace(FileNameTranslationHelper.HILL_SHADE + "_", "");
|
||||
}
|
||||
if (this == SLOPE_FILE) {
|
||||
return fileName.substring(0, fileName.length() - IndexConstants.SQLITE_EXT.length())
|
||||
.replace(FileNameTranslationHelper.SLOPE, "");
|
||||
.replace(FileNameTranslationHelper.SLOPE + "_", "");
|
||||
}
|
||||
if (fileName.endsWith(IndexConstants.SQLITE_EXT)) {
|
||||
return fileName.substring(0, fileName.length() - IndexConstants.SQLITE_EXT.length());
|
||||
|
|
|
@ -143,9 +143,9 @@ public class IndexItem implements Comparable<IndexItem> {
|
|||
|
||||
public String getTranslatedBasename() {
|
||||
if (type == DownloadActivityType.HILLSHADE_FILE) {
|
||||
return (FileNameTranslationHelper.HILL_SHADE + getBasename()).replace("_", " ");
|
||||
return (FileNameTranslationHelper.HILL_SHADE + "_" + getBasename()).replace("_", " ");
|
||||
} else if (type == DownloadActivityType.SLOPE_FILE) {
|
||||
return (FileNameTranslationHelper.SLOPE + getBasename()).replace('_', ' ');
|
||||
return (FileNameTranslationHelper.SLOPE + "_" + getBasename()).replace('_', ' ');
|
||||
} else {
|
||||
return getBasename();
|
||||
}
|
||||
|
|
|
@ -7,8 +7,6 @@ import android.graphics.drawable.Drawable;
|
|||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.util.TypedValue;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
|
@ -19,7 +17,6 @@ import android.view.ViewGroup;
|
|||
import android.widget.ArrayAdapter;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ExpandableListView;
|
||||
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
@ -45,7 +42,6 @@ import net.osmand.plus.ContextMenuAdapter;
|
|||
import net.osmand.plus.ContextMenuAdapter.ItemClickListener;
|
||||
import net.osmand.plus.ContextMenuItem;
|
||||
import net.osmand.plus.OsmandApplication;
|
||||
import net.osmand.plus.OsmandPlugin;
|
||||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.SQLiteTileSource;
|
||||
import net.osmand.plus.UiUtilities;
|
||||
|
@ -127,19 +123,6 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement
|
|||
if (asyncLoader == null || asyncLoader.getResult() == null) {
|
||||
reloadData();
|
||||
}
|
||||
|
||||
getExpandableListView().setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
|
||||
long packedPos = ((ExpandableListContextMenuInfo) menuInfo).packedPosition;
|
||||
int group = ExpandableListView.getPackedPositionGroup(packedPos);
|
||||
int child = ExpandableListView.getPackedPositionChild(packedPos);
|
||||
if (child >= 0 && group >= 0) {
|
||||
final LocalIndexInfo point = listAdapter.getChild(group, child);
|
||||
showContextMenu(point);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void reloadData() {
|
||||
|
@ -156,62 +139,6 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement
|
|||
}
|
||||
}
|
||||
|
||||
private void showContextMenu(final LocalIndexInfo info) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
final ContextMenuAdapter adapter = new ContextMenuAdapter(getMyApplication());
|
||||
basicFileOperation(info, adapter);
|
||||
OsmandPlugin.onContextMenuActivity(getActivity(), null, info, adapter);
|
||||
|
||||
String[] values = adapter.getItemNames();
|
||||
builder.setItems(values, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
ContextMenuItem item = adapter.getItem(which);
|
||||
if (item.getItemClickListener() != null) {
|
||||
item.getItemClickListener().onContextMenuClick(null,
|
||||
item.getTitleId(), which, false, null);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void basicFileOperation(final LocalIndexInfo info, ContextMenuAdapter adapter) {
|
||||
ItemClickListener listener = new ItemClickListener() {
|
||||
@Override
|
||||
public boolean onContextMenuClick(ArrayAdapter<ContextMenuItem> adapter, int resId, int pos, boolean isChecked, int[] viewCoordinates) {
|
||||
return performBasicOperation(resId, info);
|
||||
}
|
||||
};
|
||||
if (info.getType() == LocalIndexType.MAP_DATA || info.getType() == LocalIndexType.SRTM_DATA ||
|
||||
info.getType() == LocalIndexType.WIKI_DATA ) {
|
||||
if (!info.isBackupedData()) {
|
||||
adapter.addItem(new ContextMenuItem.ItemBuilder()
|
||||
.setTitleId(R.string.local_index_mi_backup, getContext())
|
||||
.setListener(listener)
|
||||
.createItem());
|
||||
}
|
||||
}
|
||||
if (info.isBackupedData()) {
|
||||
adapter.addItem(new ContextMenuItem.ItemBuilder()
|
||||
.setTitleId(R.string.local_index_mi_restore, getContext())
|
||||
.setListener(listener)
|
||||
.createItem());
|
||||
}
|
||||
if (info.getType() != LocalIndexType.TTS_VOICE_DATA && info.getType() != LocalIndexType.VOICE_DATA
|
||||
&& info.getType() != LocalIndexType.FONT_DATA) {
|
||||
adapter.addItem(new ContextMenuItem.ItemBuilder()
|
||||
.setTitleId(R.string.shared_string_rename, getContext())
|
||||
.setListener(listener)
|
||||
.createItem());
|
||||
}
|
||||
adapter.addItem(new ContextMenuItem.ItemBuilder()
|
||||
.setTitleId(R.string.shared_string_delete, getContext())
|
||||
.setListener(listener)
|
||||
.createItem());
|
||||
}
|
||||
|
||||
private boolean performBasicOperation(int resId, final LocalIndexInfo info) {
|
||||
if (resId == R.string.shared_string_rename) {
|
||||
FileUtils.renameFile(getActivity(), new File(info.getPathToData()), new RenameCallback() {
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package net.osmand.plus.helpers;
|
||||
|
||||
import android.graphics.RectF;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.github.mikephil.charting.charts.BarChart;
|
||||
import com.github.mikephil.charting.highlight.Highlight;
|
||||
import com.github.mikephil.charting.renderer.HorizontalBarChartRenderer;
|
||||
|
||||
import net.osmand.AndroidUtils;
|
||||
|
||||
public class CustomBarChartRenderer extends HorizontalBarChartRenderer {
|
||||
private float highlightHalfWidth;
|
||||
|
||||
public CustomBarChartRenderer(@NonNull BarChart chart) {
|
||||
this(chart, AndroidUtils.dpToPx(chart.getContext(), 1f) / 2f);
|
||||
}
|
||||
|
||||
public CustomBarChartRenderer(@NonNull BarChart chart, float highlightHalfWidth) {
|
||||
super(chart, chart.getAnimator(), chart.getViewPortHandler());
|
||||
this.highlightHalfWidth = highlightHalfWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setHighlightDrawPos(Highlight high, RectF bar) {
|
||||
bar.left = high.getDrawX() - highlightHalfWidth;
|
||||
bar.right = high.getDrawX() + highlightHalfWidth;
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ import android.content.Context;
|
|||
import net.osmand.IndexConstants;
|
||||
import net.osmand.PlatformUtil;
|
||||
import net.osmand.map.OsmandRegions;
|
||||
import net.osmand.plus.OsmandApplication;
|
||||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.download.DownloadResources;
|
||||
|
||||
|
@ -18,10 +19,14 @@ import java.lang.reflect.Field;
|
|||
public class FileNameTranslationHelper {
|
||||
private static final Log LOG = PlatformUtil.getLog(FileNameTranslationHelper.class);
|
||||
public static final String WIKI_NAME = "_wiki";
|
||||
public static final String HILL_SHADE = "Hillshade_";
|
||||
public static final String SLOPE = "Slope_";
|
||||
public static final String HILL_SHADE = "Hillshade";
|
||||
public static final String SLOPE = "Slope";
|
||||
public static final String SEA_DEPTH = "Depth_";
|
||||
|
||||
public static String getFileNameWithRegion(OsmandApplication app, String fileName) {
|
||||
return getFileName(app, app.getResourceManager().getOsmandRegions(), fileName);
|
||||
}
|
||||
|
||||
public static String getFileName(Context ctx, OsmandRegions regions, String fileName) {
|
||||
String basename = getBasename(fileName);
|
||||
if (basename.endsWith(WIKI_NAME)) { //wiki files
|
||||
|
@ -30,13 +35,15 @@ public class FileNameTranslationHelper {
|
|||
return getVoiceName(ctx, fileName);
|
||||
} else if (fileName.endsWith(IndexConstants.FONT_INDEX_EXT)) { //otf files
|
||||
return getFontName(ctx, basename);
|
||||
} else if (fileName.startsWith(HILL_SHADE)){
|
||||
} else if (fileName.startsWith(HILL_SHADE)) {
|
||||
basename = basename.replace(HILL_SHADE + " ", "");
|
||||
return getTerrainName(ctx, regions, basename, R.string.download_hillshade_maps);
|
||||
} else if (fileName.startsWith(SLOPE)) {
|
||||
basename = basename.replace(SLOPE + " ", "");
|
||||
return getTerrainName(ctx, regions, basename, R.string.download_slope_maps);
|
||||
} else if (fileName.length() == 2) { //voice recorded files
|
||||
try {
|
||||
Field f = R.string.class.getField("lang_"+fileName);
|
||||
Field f = R.string.class.getField("lang_" + fileName);
|
||||
if (f != null) {
|
||||
Integer in = (Integer) f.get(null);
|
||||
return ctx.getString(in);
|
||||
|
@ -62,9 +69,10 @@ public class FileNameTranslationHelper {
|
|||
|
||||
public static String getTerrainName(Context ctx, OsmandRegions regions, String basename,
|
||||
int terrainNameRes) {
|
||||
String terrain = ctx.getString(terrainNameRes) + " ";
|
||||
basename = basename.replace(" ", "_");
|
||||
String terrain = ctx.getString(terrainNameRes);
|
||||
String locName = regions.getLocaleName(basename.trim(), true);
|
||||
return terrain + locName;
|
||||
return ctx.getString(R.string.ltr_or_rtl_combine_via_space, locName, "(" + terrain + ")");
|
||||
}
|
||||
|
||||
public static String getWikiName(Context ctx, String basename){
|
||||
|
|
|
@ -76,6 +76,8 @@ import net.osmand.plus.ContextMenuAdapter;
|
|||
import net.osmand.plus.ContextMenuItem;
|
||||
import net.osmand.plus.GPXDatabase.GpxDataItem;
|
||||
import net.osmand.plus.GpxDbHelper.GpxDataItemCallback;
|
||||
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem;
|
||||
import net.osmand.plus.GpxSelectionHelper.GpxDisplayGroup;
|
||||
import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
|
||||
import net.osmand.plus.OsmAndConstants;
|
||||
import net.osmand.plus.OsmAndFormatter;
|
||||
|
@ -2045,6 +2047,91 @@ public class GpxUiHelper {
|
|||
return gpx;
|
||||
}
|
||||
|
||||
public enum LineGraphType {
|
||||
ALTITUDE,
|
||||
SLOPE,
|
||||
SPEED;
|
||||
}
|
||||
|
||||
public static List<ILineDataSet> getDataSets(LineChart chart,
|
||||
OsmandApplication app,
|
||||
GPXTrackAnalysis analysis,
|
||||
@NonNull LineGraphType firstType,
|
||||
@Nullable LineGraphType secondType,
|
||||
boolean calcWithoutGaps) {
|
||||
if (app == null || chart == null || analysis == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<ILineDataSet> result = new ArrayList<>();
|
||||
if (secondType == null) {
|
||||
ILineDataSet dataSet = getDataSet(chart, app, analysis, calcWithoutGaps, false, firstType);
|
||||
if (dataSet != null) {
|
||||
result.add(dataSet);
|
||||
}
|
||||
} else {
|
||||
OrderedLineDataSet dataSet1 = getDataSet(chart, app, analysis, calcWithoutGaps, false, firstType);
|
||||
OrderedLineDataSet dataSet2 = getDataSet(chart, app, analysis, calcWithoutGaps, true, secondType);
|
||||
if (dataSet1 == null && dataSet2 == null) {
|
||||
return new ArrayList<>();
|
||||
} else if (dataSet1 == null) {
|
||||
result.add(dataSet2);
|
||||
} else if (dataSet2 == null) {
|
||||
result.add(dataSet1);
|
||||
} else if (dataSet1.getPriority() < dataSet2.getPriority()) {
|
||||
result.add(dataSet2);
|
||||
result.add(dataSet1);
|
||||
} else {
|
||||
result.add(dataSet1);
|
||||
result.add(dataSet2);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static OrderedLineDataSet getDataSet(@NonNull LineChart chart,
|
||||
@NonNull OsmandApplication app,
|
||||
@NonNull GPXTrackAnalysis analysis,
|
||||
boolean calcWithoutGaps,
|
||||
boolean useRightAxis,
|
||||
@NonNull LineGraphType type) {
|
||||
OrderedLineDataSet dataSet = null;
|
||||
switch (type) {
|
||||
case ALTITUDE: {
|
||||
if (analysis.hasElevationData) {
|
||||
dataSet = GpxUiHelper.createGPXElevationDataSet(app, chart,
|
||||
analysis, GPXDataSetAxisType.DISTANCE, useRightAxis, true, calcWithoutGaps);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SLOPE:
|
||||
if (analysis.hasElevationData) {
|
||||
dataSet = GpxUiHelper.createGPXSlopeDataSet(app, chart,
|
||||
analysis, GPXDataSetAxisType.DISTANCE, null, useRightAxis, true, calcWithoutGaps);
|
||||
}
|
||||
break;
|
||||
case SPEED: {
|
||||
if (analysis.hasSpeedData) {
|
||||
dataSet = GpxUiHelper.createGPXSpeedDataSet(app, chart,
|
||||
analysis, GPXDataSetAxisType.DISTANCE, useRightAxis, true, calcWithoutGaps);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return dataSet;
|
||||
}
|
||||
|
||||
public static GpxDisplayItem makeGpxDisplayItem(OsmandApplication app, GPXUtilities.GPXFile gpx) {
|
||||
GpxDisplayItem gpxItem = null;
|
||||
String groupName = app.getString(R.string.current_route);
|
||||
GpxDisplayGroup group = app.getSelectedGpxHelper().buildGpxDisplayGroup(gpx, 0, groupName);
|
||||
if (group != null && group.getModifiableList().size() > 0) {
|
||||
gpxItem = group.getModifiableList().get(0);
|
||||
if (gpxItem != null) {
|
||||
gpxItem.route = true;
|
||||
}
|
||||
}
|
||||
return gpxItem;
|
||||
}
|
||||
|
||||
|
||||
public static class GPXInfo {
|
||||
|
|
|
@ -17,13 +17,12 @@ import net.osmand.AndroidNetworkUtils.OnRequestsResultListener;
|
|||
import net.osmand.AndroidNetworkUtils.RequestResponse;
|
||||
import net.osmand.PlatformUtil;
|
||||
import net.osmand.plus.OsmandApplication;
|
||||
import net.osmand.plus.settings.backend.OsmandSettings;
|
||||
import net.osmand.plus.settings.backend.OsmandPreference;
|
||||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.Version;
|
||||
import net.osmand.plus.inapp.InAppPurchases.InAppPurchase;
|
||||
import net.osmand.plus.inapp.InAppPurchases.InAppPurchase.PurchaseState;
|
||||
import net.osmand.plus.inapp.InAppPurchases.InAppSubscription;
|
||||
import net.osmand.plus.inapp.InAppPurchases.InAppSubscription.SubscriptionState;
|
||||
import net.osmand.plus.inapp.InAppPurchases.InAppSubscriptionList;
|
||||
import net.osmand.plus.liveupdates.CountrySelectionFragment;
|
||||
import net.osmand.plus.liveupdates.CountrySelectionFragment.CountryItem;
|
||||
|
@ -50,11 +49,10 @@ public abstract class InAppPurchaseHelper {
|
|||
private static final String TAG = InAppPurchaseHelper.class.getSimpleName();
|
||||
private boolean mDebugLog = false;
|
||||
|
||||
public static final long SUBSCRIPTION_HOLDING_TIME_MSEC = 1000 * 60 * 60 * 24 * 3; // 3 days
|
||||
|
||||
protected InAppPurchases purchases;
|
||||
protected long lastValidationCheckTime;
|
||||
protected boolean inventoryRequested;
|
||||
protected Map<String, SubscriptionState> subscriptionStateMap = new HashMap<>();
|
||||
|
||||
private static final long PURCHASE_VALIDATION_PERIOD_MSEC = 1000 * 60 * 60 * 24; // daily
|
||||
|
||||
|
@ -375,21 +373,33 @@ public abstract class InAppPurchaseHelper {
|
|||
final String sku, final String payload) throws UnsupportedOperationException;
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private class RequestInventoryTask extends AsyncTask<Void, Void, String> {
|
||||
private class RequestInventoryTask extends AsyncTask<Void, Void, String[]> {
|
||||
|
||||
RequestInventoryTask() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String doInBackground(Void... params) {
|
||||
protected String[] doInBackground(Void... params) {
|
||||
try {
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
parameters.put("androidPackage", ctx.getPackageName());
|
||||
addUserInfo(parameters);
|
||||
return AndroidNetworkUtils.sendRequest(ctx,
|
||||
String activeSubscriptionsIds = AndroidNetworkUtils.sendRequest(ctx,
|
||||
"https://osmand.net/api/subscriptions/active",
|
||||
parameters, "Requesting active subscriptions...", false, false);
|
||||
|
||||
String subscriptionsState = null;
|
||||
String userId = ctx.getSettings().BILLING_USER_ID.get();
|
||||
String userToken = ctx.getSettings().BILLING_USER_TOKEN.get();
|
||||
if (!Algorithms.isEmpty(userId) && !Algorithms.isEmpty(userToken)) {
|
||||
parameters.put("userId", userId);
|
||||
parameters.put("userToken", userToken);
|
||||
subscriptionsState = AndroidNetworkUtils.sendRequest(ctx,
|
||||
"https://osmand.net/api/subscriptions/get",
|
||||
parameters, "Requesting subscriptions state...", false, false);
|
||||
}
|
||||
|
||||
return new String[] { activeSubscriptionsIds, subscriptionsState };
|
||||
} catch (Exception e) {
|
||||
logError("sendRequest Error", e);
|
||||
}
|
||||
|
@ -397,12 +407,14 @@ public abstract class InAppPurchaseHelper {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String response) {
|
||||
logDebug("Response=" + response);
|
||||
if (response != null) {
|
||||
protected void onPostExecute(String[] response) {
|
||||
logDebug("Response=" + Arrays.toString(response));
|
||||
String activeSubscriptionsIdsJson = response[0];
|
||||
String subscriptionsStateJson = response[1];
|
||||
if (activeSubscriptionsIdsJson != null) {
|
||||
inventoryRequested = true;
|
||||
try {
|
||||
JSONObject obj = new JSONObject(response);
|
||||
JSONObject obj = new JSONObject(activeSubscriptionsIdsJson);
|
||||
JSONArray names = obj.names();
|
||||
if (names != null) {
|
||||
for (int i = 0; i < names.length(); i++) {
|
||||
|
@ -418,6 +430,24 @@ public abstract class InAppPurchaseHelper {
|
|||
logError("Json parsing error", e);
|
||||
}
|
||||
}
|
||||
if (subscriptionsStateJson != null) {
|
||||
inventoryRequested = true;
|
||||
Map<String, SubscriptionState> subscriptionStateMap = new HashMap<>();
|
||||
try {
|
||||
JSONArray subArrJson = new JSONArray(subscriptionsStateJson);
|
||||
for (int i = 0; i < subArrJson.length(); i++) {
|
||||
JSONObject subObj = subArrJson.getJSONObject(i);
|
||||
String sku = subObj.getString("sku");
|
||||
String state = subObj.getString("state");
|
||||
if (!Algorithms.isEmpty(sku) && !Algorithms.isEmpty(state)) {
|
||||
subscriptionStateMap.put(sku, SubscriptionState.getByStateStr(state));
|
||||
}
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
logError("Json parsing error", e);
|
||||
}
|
||||
InAppPurchaseHelper.this.subscriptionStateMap = subscriptionStateMap;
|
||||
}
|
||||
exec(InAppPurchaseTaskType.REQUEST_INVENTORY, getRequestInventoryCommand());
|
||||
}
|
||||
}
|
||||
|
@ -467,9 +497,8 @@ public abstract class InAppPurchaseHelper {
|
|||
ctx.getSettings().LIVE_UPDATES_PURCHASED.set(true);
|
||||
ctx.getSettings().getCustomRenderBooleanProperty("depthContours").set(true);
|
||||
|
||||
ctx.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_TIME.set(0L);
|
||||
ctx.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_FIRST_DLG_SHOWN.set(false);
|
||||
ctx.getSettings().LIVE_UPDATES_PURCHASE_CANCELLED_SECOND_DLG_SHOWN.set(false);
|
||||
ctx.getSettings().LIVE_UPDATES_EXPIRED_FIRST_DLG_SHOWN_TIME.set(0L);
|
||||
ctx.getSettings().LIVE_UPDATES_EXPIRED_SECOND_DLG_SHOWN_TIME.set(0L);
|
||||
|
||||
notifyDismissProgress(InAppPurchaseTaskType.PURCHASE_LIVE_UPDATES);
|
||||
notifyItemPurchased(sku, active);
|
||||
|
|
|
@ -24,6 +24,8 @@ import java.text.NumberFormat;
|
|||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Currency;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
@ -190,6 +192,28 @@ public abstract class InAppPurchases {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public InAppSubscription getTopExpiredSubscription() {
|
||||
List<InAppSubscription> expiredSubscriptions = new ArrayList<>();
|
||||
for (InAppSubscription s : getAllSubscriptions()) {
|
||||
if (s.getState().isGone()) {
|
||||
expiredSubscriptions.add(s);
|
||||
}
|
||||
}
|
||||
Collections.sort(expiredSubscriptions, new Comparator<InAppSubscription>() {
|
||||
@Override
|
||||
public int compare(InAppSubscription s1, InAppSubscription s2) {
|
||||
int orderS1 = s1.getState().ordinal();
|
||||
int orderS2 = s2.getState().ordinal();
|
||||
if (orderS1 != orderS2) {
|
||||
return (orderS1 < orderS2) ? -1 : ((orderS1 == orderS2) ? 0 : 1);
|
||||
}
|
||||
return Double.compare(s1.getMonthlyPriceValue(), s2.getMonthlyPriceValue());
|
||||
}
|
||||
});
|
||||
return expiredSubscriptions.isEmpty() ? null : expiredSubscriptions.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class InAppPurchase {
|
||||
|
@ -554,9 +578,49 @@ public abstract class InAppPurchases {
|
|||
private String subscriptionPeriodString;
|
||||
private Period subscriptionPeriod;
|
||||
private boolean upgrade = false;
|
||||
private SubscriptionState state = SubscriptionState.UNDEFINED;
|
||||
private SubscriptionState prevState = SubscriptionState.UNDEFINED;
|
||||
|
||||
private InAppSubscriptionIntroductoryInfo introductoryInfo;
|
||||
|
||||
public enum SubscriptionState {
|
||||
UNDEFINED("undefined"),
|
||||
ACTIVE("active"),
|
||||
CANCELLED("cancelled"),
|
||||
IN_GRACE_PERIOD("in_grace_period"),
|
||||
ON_HOLD("on_hold"),
|
||||
PAUSED("paused"),
|
||||
EXPIRED("expired");
|
||||
|
||||
private final String stateStr;
|
||||
|
||||
SubscriptionState(@NonNull String stateStr) {
|
||||
this.stateStr = stateStr;
|
||||
}
|
||||
|
||||
public String getStateStr() {
|
||||
return stateStr;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static SubscriptionState getByStateStr(@NonNull String stateStr) {
|
||||
for (SubscriptionState state : SubscriptionState.values()) {
|
||||
if (state.stateStr.equals(stateStr)) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
return UNDEFINED;
|
||||
}
|
||||
|
||||
public boolean isGone() {
|
||||
return this == ON_HOLD || this == PAUSED || this == EXPIRED;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return this == ACTIVE || this == CANCELLED || this == IN_GRACE_PERIOD;
|
||||
}
|
||||
}
|
||||
|
||||
InAppSubscription(@NonNull String skuNoVersion, int version) {
|
||||
super(skuNoVersion + "_v" + version);
|
||||
this.skuNoVersion = skuNoVersion;
|
||||
|
@ -592,6 +656,28 @@ public abstract class InAppPurchases {
|
|||
return upgrade;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public SubscriptionState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(@NonNull SubscriptionState state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public SubscriptionState getPrevState() {
|
||||
return prevState;
|
||||
}
|
||||
|
||||
public void setPrevState(@NonNull SubscriptionState prevState) {
|
||||
this.prevState = prevState;
|
||||
}
|
||||
|
||||
public boolean hasStateChanged() {
|
||||
return state != prevState;
|
||||
}
|
||||
|
||||
public boolean isAnyPurchased() {
|
||||
if (isPurchased()) {
|
||||
return true;
|
||||
|
|
|
@ -495,7 +495,7 @@ public abstract class ImageCard extends AbstractCard {
|
|||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
LOG.error(e);
|
||||
}
|
||||
if (listener != null) {
|
||||
listener.onPostProcess(result);
|
||||
|
|
|
@ -942,7 +942,7 @@ public abstract class PointEditorFragmentNew extends BaseOsmAndFragment implemen
|
|||
}
|
||||
}
|
||||
|
||||
private void exitEditing() {
|
||||
public void exitEditing() {
|
||||
cancelled = true;
|
||||
dismiss();
|
||||
}
|
||||
|
|
|
@ -125,8 +125,7 @@ public class TrackDetailsMenu {
|
|||
hidding = true;
|
||||
fragment.dismiss(backPressed);
|
||||
} else {
|
||||
segment = null;
|
||||
trackChartPoints = null;
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -274,8 +273,7 @@ public class TrackDetailsMenu {
|
|||
if (hidding) {
|
||||
hidding = false;
|
||||
visible = false;
|
||||
segment = null;
|
||||
trackChartPoints = null;
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -532,6 +530,11 @@ public class TrackDetailsMenu {
|
|||
fitTrackOnMap(chart, location, forceFit);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
segment = null;
|
||||
trackChartPoints = null;
|
||||
}
|
||||
|
||||
private List<LatLon> getXAxisPoints(LineChart chart) {
|
||||
float[] entries = chart.getXAxis().mEntries;
|
||||
LineData lineData = chart.getLineData();
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
package net.osmand.plus.measurementtool;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
|
@ -16,15 +19,23 @@ import com.github.mikephil.charting.data.ChartData;
|
|||
import com.github.mikephil.charting.data.LineData;
|
||||
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
|
||||
|
||||
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem;
|
||||
import net.osmand.AndroidUtils;
|
||||
import net.osmand.plus.OsmandApplication;
|
||||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.activities.MapActivity;
|
||||
import net.osmand.plus.helpers.GpxUiHelper;
|
||||
import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetAxisType;
|
||||
import net.osmand.plus.helpers.GpxUiHelper.OrderedLineDataSet;
|
||||
import net.osmand.plus.helpers.GpxUiHelper.LineGraphType;
|
||||
import net.osmand.plus.mapcontextmenu.other.HorizontalSelectionAdapter;
|
||||
import net.osmand.plus.mapcontextmenu.other.HorizontalSelectionAdapter.HorizontalSelectionAdapterListener;
|
||||
import net.osmand.plus.mapcontextmenu.other.TrackDetailsMenu;
|
||||
import net.osmand.plus.measurementtool.MeasurementToolFragment.OnUpdateAdditionalInfoListener;
|
||||
import net.osmand.plus.measurementtool.graph.CommonGraphAdapter;
|
||||
import net.osmand.plus.measurementtool.graph.CustomGraphAdapter;
|
||||
import net.osmand.plus.measurementtool.graph.CustomGraphAdapter.LegendViewType;
|
||||
import net.osmand.plus.measurementtool.graph.BaseGraphAdapter;
|
||||
import net.osmand.plus.measurementtool.graph.GraphAdapterHelper;
|
||||
import net.osmand.plus.measurementtool.graph.GraphAdapterHelper.RefreshMapCallback;
|
||||
import net.osmand.plus.routepreparationmenu.RouteDetailsFragment;
|
||||
import net.osmand.plus.routepreparationmenu.cards.BaseCard;
|
||||
import net.osmand.router.RouteSegmentResult;
|
||||
|
@ -32,48 +43,44 @@ import net.osmand.util.Algorithms;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import static net.osmand.GPXUtilities.GPXFile;
|
||||
import static net.osmand.GPXUtilities.GPXTrackAnalysis;
|
||||
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;
|
||||
import static net.osmand.plus.mapcontextmenu.other.HorizontalSelectionAdapter.HorizontalSelectionItem;
|
||||
import static net.osmand.router.RouteStatisticsHelper.RouteStatistics;
|
||||
|
||||
public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListener {
|
||||
|
||||
private static String GRAPH_DATA_GPX_FILE_NAME = "graph_data_tmp";
|
||||
private static int INVALID_ID = -1;
|
||||
|
||||
private MeasurementEditingContext editingCtx;
|
||||
private MeasurementToolFragment fragment;
|
||||
|
||||
private GraphType visibleGraphType;
|
||||
private List<GraphType> graphTypes = new ArrayList<>();
|
||||
private TrackDetailsMenu trackDetailsMenu;
|
||||
private RefreshMapCallback refreshMapCallback;
|
||||
private GPXFile gpxFile;
|
||||
private GPXTrackAnalysis analysis;
|
||||
private GpxDisplayItem gpxItem;
|
||||
|
||||
private View commonGraphContainer;
|
||||
private View customGraphContainer;
|
||||
private View messageContainer;
|
||||
private LineChart commonGraphChart;
|
||||
private HorizontalBarChart customGraphChart;
|
||||
private CommonGraphAdapter commonGraphAdapter;
|
||||
private CustomGraphAdapter customGraphAdapter;
|
||||
private RecyclerView graphTypesMenu;
|
||||
|
||||
private enum CommonGraphType {
|
||||
OVERVIEW(R.string.shared_string_overview, false),
|
||||
ALTITUDE(R.string.altitude, true),
|
||||
SLOPE(R.string.shared_string_slope, true),
|
||||
SPEED(R.string.map_widget_speed, false);
|
||||
private GraphType visibleType;
|
||||
private List<GraphType> graphTypes = new ArrayList<>();
|
||||
|
||||
CommonGraphType(int titleId, boolean canBeCalculated) {
|
||||
this.titleId = titleId;
|
||||
this.canBeCalculated = canBeCalculated;
|
||||
}
|
||||
|
||||
final int titleId;
|
||||
final boolean canBeCalculated;
|
||||
}
|
||||
|
||||
public GraphsCard(@NonNull MapActivity mapActivity, MeasurementToolFragment fragment) {
|
||||
public GraphsCard(@NonNull MapActivity mapActivity,
|
||||
TrackDetailsMenu trackDetailsMenu,
|
||||
MeasurementToolFragment fragment) {
|
||||
super(mapActivity);
|
||||
this.trackDetailsMenu = trackDetailsMenu;
|
||||
this.fragment = fragment;
|
||||
}
|
||||
|
||||
|
@ -82,19 +89,27 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen
|
|||
if (mapActivity == null || fragment == null) return;
|
||||
editingCtx = fragment.getEditingCtx();
|
||||
|
||||
graphTypesMenu = view.findViewById(R.id.graph_types_recycler_view);
|
||||
graphTypesMenu.setLayoutManager(new LinearLayoutManager(mapActivity, RecyclerView.HORIZONTAL, false));
|
||||
commonGraphContainer = view.findViewById(R.id.common_graphs_container);
|
||||
customGraphContainer = view.findViewById(R.id.custom_graphs_container);
|
||||
messageContainer = view.findViewById(R.id.message_container);
|
||||
commonGraphChart = (LineChart) view.findViewById(R.id.line_chart);
|
||||
customGraphChart = (HorizontalBarChart) view.findViewById(R.id.horizontal_chart);
|
||||
updateGraphData();
|
||||
LineChart lineChart = (LineChart) view.findViewById(R.id.line_chart);
|
||||
HorizontalBarChart barChart = (HorizontalBarChart) view.findViewById(R.id.horizontal_chart);
|
||||
commonGraphAdapter = new CommonGraphAdapter(lineChart, true);
|
||||
customGraphAdapter = new CustomGraphAdapter(barChart, true);
|
||||
|
||||
graphTypesMenu = view.findViewById(R.id.graph_types_recycler_view);
|
||||
graphTypesMenu.setLayoutManager(
|
||||
new LinearLayoutManager(mapActivity, RecyclerView.HORIZONTAL, false));
|
||||
customGraphAdapter.setLegendContainer((ViewGroup) view.findViewById(R.id.route_legend));
|
||||
customGraphAdapter.setLayoutChangeListener(new BaseGraphAdapter.LayoutChangeListener() {
|
||||
@Override
|
||||
public void onLayoutChanged() {
|
||||
setLayoutNeeded();
|
||||
}
|
||||
});
|
||||
|
||||
refreshGraphTypesSelectionMenu();
|
||||
updateDataView();
|
||||
GraphAdapterHelper.bindGraphAdapters(commonGraphAdapter, Collections.singletonList((BaseGraphAdapter) customGraphAdapter), (ViewGroup) view);
|
||||
refreshMapCallback = GraphAdapterHelper.bindToMap(commonGraphAdapter, mapActivity, trackDetailsMenu);
|
||||
fullUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -104,15 +119,32 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen
|
|||
|
||||
@Override
|
||||
public void onUpdateAdditionalInfo() {
|
||||
if (!isRouteCalculating()) {
|
||||
updateGraphData();
|
||||
refreshGraphTypesSelectionMenu();
|
||||
if (editingCtx != null) {
|
||||
fullUpdate();
|
||||
}
|
||||
updateDataView();
|
||||
}
|
||||
|
||||
private void refreshGraphTypesSelectionMenu() {
|
||||
graphTypesMenu.removeAllViews();
|
||||
private void fullUpdate() {
|
||||
if (!isRouteCalculating()) {
|
||||
updateData();
|
||||
setupVisibleType();
|
||||
updateMenu();
|
||||
}
|
||||
updateView();
|
||||
updateChartOnMap();
|
||||
}
|
||||
|
||||
private void updateMenu() {
|
||||
if (!editingCtx.isPointsEnoughToCalculateRoute()) {
|
||||
graphTypesMenu.setVisibility(View.GONE);
|
||||
} else {
|
||||
graphTypesMenu.setVisibility(View.VISIBLE);
|
||||
graphTypesMenu.removeAllViews();
|
||||
fillInMenu();
|
||||
}
|
||||
}
|
||||
|
||||
private void fillInMenu() {
|
||||
OsmandApplication app = getMyApplication();
|
||||
int activeColorId = nightMode ? R.color.active_color_primary_dark : R.color.active_color_primary_light;
|
||||
final HorizontalSelectionAdapter adapter = new HorizontalSelectionAdapter(app, nightMode);
|
||||
|
@ -127,16 +159,16 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen
|
|||
}
|
||||
}
|
||||
adapter.setItems(items);
|
||||
String selectedItemKey = visibleGraphType.getTitle();
|
||||
String selectedItemKey = visibleType.getTitle();
|
||||
adapter.setSelectedItemByTitle(selectedItemKey);
|
||||
adapter.setListener(new HorizontalSelectionAdapter.HorizontalSelectionAdapterListener() {
|
||||
adapter.setListener(new HorizontalSelectionAdapterListener() {
|
||||
@Override
|
||||
public void onItemSelected(HorizontalSelectionAdapter.HorizontalSelectionItem item) {
|
||||
public void onItemSelected(HorizontalSelectionItem item) {
|
||||
adapter.setItems(items);
|
||||
adapter.setSelectedItem(item);
|
||||
GraphType chosenGraphType = (GraphType) item.getObject();
|
||||
if (!isCurrentVisibleType(chosenGraphType)) {
|
||||
setupVisibleGraphType(chosenGraphType);
|
||||
GraphType chosenType = (GraphType) item.getObject();
|
||||
if (!isVisibleType(chosenType)) {
|
||||
changeVisibleType(chosenType);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -144,19 +176,19 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen
|
|||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void setupVisibleGraphType(GraphType type) {
|
||||
visibleGraphType = type;
|
||||
updateDataView();
|
||||
private void changeVisibleType(GraphType type) {
|
||||
visibleType = type;
|
||||
updateView();
|
||||
}
|
||||
|
||||
private boolean isCurrentVisibleType(GraphType type) {
|
||||
if (visibleGraphType != null && type != null) {
|
||||
return Algorithms.objectEquals(visibleGraphType.getTitle(), type.getTitle());
|
||||
private boolean isVisibleType(GraphType type) {
|
||||
if (visibleType != null && type != null) {
|
||||
return Algorithms.objectEquals(visibleType.getTitle(), type.getTitle());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private GraphType getFirstAvailableGraphType() {
|
||||
private GraphType getFirstAvailableType() {
|
||||
for (GraphType type : graphTypes) {
|
||||
if (type.isAvailable()) {
|
||||
return type;
|
||||
|
@ -165,208 +197,181 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen
|
|||
return null;
|
||||
}
|
||||
|
||||
private void updateDataView() {
|
||||
if (isRouteCalculating()) {
|
||||
showProgressMessage();
|
||||
} else if (visibleGraphType.hasData()) {
|
||||
private void updateView() {
|
||||
hideAll();
|
||||
if (!editingCtx.isPointsEnoughToCalculateRoute()) {
|
||||
showMessage(app.getString(R.string.message_you_need_add_two_points_to_show_graphs));
|
||||
} else if (isRouteCalculating()) {
|
||||
showMessage(app.getString(R.string.message_graph_will_be_available_after_recalculation), true);
|
||||
} else if (visibleType.hasData()) {
|
||||
showGraph();
|
||||
} else if (visibleGraphType.canBeCalculated()) {
|
||||
showMessage();
|
||||
} else if (visibleType.canBeCalculated()) {
|
||||
showMessage(app.getString(R.string.message_need_calculate_route_before_show_graph,
|
||||
visibleType.getTitle()), R.drawable.ic_action_altitude_average,
|
||||
app.getString(R.string.route_between_points), new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
fragment.startSnapToRoad(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void showProgressMessage() {
|
||||
private void hideAll() {
|
||||
commonGraphContainer.setVisibility(View.GONE);
|
||||
customGraphContainer.setVisibility(View.GONE);
|
||||
messageContainer.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void showMessage(String text) {
|
||||
showMessage(text, INVALID_ID, false, null, null);
|
||||
}
|
||||
|
||||
private void showMessage(String text, @DrawableRes int iconResId, String btnTitle, View.OnClickListener btnListener) {
|
||||
showMessage(text, iconResId, false, btnTitle, btnListener);
|
||||
}
|
||||
|
||||
private void showMessage(String text, boolean showProgressBar) {
|
||||
showMessage(text, INVALID_ID, showProgressBar, null, null);
|
||||
}
|
||||
|
||||
private void showMessage(@NonNull String text,
|
||||
@DrawableRes int iconResId,
|
||||
boolean showProgressBar,
|
||||
String btnTitle,
|
||||
View.OnClickListener btnListener) {
|
||||
messageContainer.setVisibility(View.VISIBLE);
|
||||
TextView tvMessage = messageContainer.findViewById(R.id.message_text);
|
||||
tvMessage.setText(text);
|
||||
ImageView icon = messageContainer.findViewById(R.id.message_icon);
|
||||
if (iconResId != INVALID_ID) {
|
||||
icon.setVisibility(View.VISIBLE);
|
||||
icon.setImageResource(iconResId);
|
||||
} else {
|
||||
icon.setVisibility(View.GONE);
|
||||
}
|
||||
ProgressBar pb = messageContainer.findViewById(R.id.progress_bar);
|
||||
pb.setVisibility(View.VISIBLE);
|
||||
icon.setVisibility(View.GONE);
|
||||
tvMessage.setText(R.string.message_graph_will_be_available_after_recalculation);
|
||||
pb.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
|
||||
View btnContainer = messageContainer.findViewById(R.id.btn_container);
|
||||
if (btnTitle != null) {
|
||||
TextView tvBtnTitle = btnContainer.findViewById(R.id.btn_text);
|
||||
tvBtnTitle.setText(btnTitle);
|
||||
btnContainer.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
btnContainer.setVisibility(View.GONE);
|
||||
}
|
||||
if (btnListener != null) {
|
||||
btnContainer.setOnClickListener(btnListener);
|
||||
}
|
||||
}
|
||||
|
||||
private void showGraph() {
|
||||
if (visibleGraphType.isCustom()) {
|
||||
customGraphChart.clear();
|
||||
commonGraphContainer.setVisibility(View.GONE);
|
||||
if (visibleType.isCustom()) {
|
||||
CustomGraphType customGraphType = (CustomGraphType) visibleType;
|
||||
customGraphContainer.setVisibility(View.VISIBLE);
|
||||
messageContainer.setVisibility(View.GONE);
|
||||
prepareCustomGraphView((BarData) visibleGraphType.getGraphData());
|
||||
customGraphAdapter.setLegendViewType(LegendViewType.ONE_ELEMENT);
|
||||
customGraphAdapter.updateContent(customGraphType.getChartData(), customGraphType.getStatistics());
|
||||
} else {
|
||||
commonGraphChart.clear();
|
||||
CommonGraphType commonGraphType = (CommonGraphType) visibleType;
|
||||
commonGraphContainer.setVisibility(View.VISIBLE);
|
||||
customGraphContainer.setVisibility(View.GONE);
|
||||
messageContainer.setVisibility(View.GONE);
|
||||
prepareCommonGraphView((LineData) visibleGraphType.getGraphData());
|
||||
customGraphAdapter.setLegendViewType(LegendViewType.GONE);
|
||||
commonGraphAdapter.updateContent(commonGraphType.getChartData(), gpxItem);
|
||||
}
|
||||
}
|
||||
|
||||
private void showMessage() {
|
||||
commonGraphContainer.setVisibility(View.GONE);
|
||||
customGraphContainer.setVisibility(View.GONE);
|
||||
messageContainer.setVisibility(View.VISIBLE);
|
||||
TextView tvMessage = messageContainer.findViewById(R.id.message_text);
|
||||
ImageView icon = messageContainer.findViewById(R.id.message_icon);
|
||||
ProgressBar pb = messageContainer.findViewById(R.id.progress_bar);
|
||||
pb.setVisibility(View.GONE);
|
||||
icon.setVisibility(View.VISIBLE);
|
||||
tvMessage.setText(app.getString(
|
||||
R.string.message_need_calculate_route_before_show_graph,
|
||||
visibleGraphType.getTitle()));
|
||||
icon.setImageResource(R.drawable.ic_action_altitude_average);
|
||||
}
|
||||
|
||||
private void prepareCommonGraphView(LineData data) {
|
||||
GpxUiHelper.setupGPXChart(commonGraphChart, 4, 24f, 16f, !nightMode, true);
|
||||
commonGraphChart.setData(data);
|
||||
}
|
||||
|
||||
private void prepareCustomGraphView(BarData data) {
|
||||
OsmandApplication app = getMyApplication();
|
||||
if (app == null) return;
|
||||
|
||||
GpxUiHelper.setupHorizontalGPXChart(app, customGraphChart, 5, 9, 24, true, nightMode);
|
||||
customGraphChart.setExtraRightOffset(16);
|
||||
customGraphChart.setExtraLeftOffset(16);
|
||||
customGraphChart.setData(data);
|
||||
}
|
||||
|
||||
private void updateGraphData() {
|
||||
private void updateData() {
|
||||
graphTypes.clear();
|
||||
OsmandApplication app = getMyApplication();
|
||||
GPXTrackAnalysis analysis = createGpxTrackAnalysis();
|
||||
gpxFile = getGpxFile();
|
||||
analysis = gpxFile != null ? gpxFile.getAnalysis(0) : null;
|
||||
gpxItem = gpxFile != null ? GpxUiHelper.makeGpxDisplayItem(app, gpxFile) : null;
|
||||
if (gpxItem != null) {
|
||||
trackDetailsMenu.setGpxItem(gpxItem);
|
||||
}
|
||||
if (analysis == null) return;
|
||||
|
||||
// update common graph data
|
||||
for (CommonGraphType commonType : CommonGraphType.values()) {
|
||||
List<ILineDataSet> dataSets = getDataSets(commonType, commonGraphChart, analysis);
|
||||
LineData data = null;
|
||||
if (!Algorithms.isEmpty(dataSets)) {
|
||||
data = new LineData(dataSets);
|
||||
}
|
||||
String title = app.getString(commonType.titleId);
|
||||
graphTypes.add(new GraphType(title, false, commonType.canBeCalculated, data));
|
||||
}
|
||||
boolean hasElevationData = analysis.hasElevationData;
|
||||
boolean hasSpeedData = analysis.isSpeedSpecified();
|
||||
addCommonType(R.string.shared_string_overview, true, hasElevationData, ALTITUDE, SLOPE);
|
||||
addCommonType(R.string.altitude, true, hasElevationData, ALTITUDE, null);
|
||||
addCommonType(R.string.shared_string_slope, true, hasElevationData, SLOPE, null);
|
||||
addCommonType(R.string.map_widget_speed, false, hasSpeedData, SPEED, null);
|
||||
|
||||
// update custom graph data
|
||||
List<RouteSegmentResult> routeSegments = editingCtx.getAllRouteSegments();
|
||||
List<RouteStatistics> routeStatistics = calculateRouteStatistics(routeSegments);
|
||||
List<RouteStatistics> routeStatistics = calculateRouteStatistics();
|
||||
if (analysis != null && routeStatistics != null) {
|
||||
for (RouteStatistics statistics : routeStatistics) {
|
||||
String title = AndroidUtils.getStringRouteInfoPropertyValue(app, statistics.name);
|
||||
BarData data = null;
|
||||
if (!Algorithms.isEmpty(statistics.elements)) {
|
||||
data = GpxUiHelper.buildStatisticChart(
|
||||
app, customGraphChart, statistics, analysis, true, nightMode);
|
||||
}
|
||||
graphTypes.add(new GraphType(title, true, false, data));
|
||||
graphTypes.add(new CustomGraphType(title, statistics));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update current visible graph type
|
||||
if (visibleGraphType == null) {
|
||||
visibleGraphType = getFirstAvailableGraphType();
|
||||
private void updateChartOnMap() {
|
||||
if (hasVisibleGraph()) {
|
||||
trackDetailsMenu.reset();
|
||||
refreshMapCallback.refreshMap(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void addCommonType(int titleId,
|
||||
boolean canBeCalculated,
|
||||
boolean hasData,
|
||||
LineGraphType firstType,
|
||||
LineGraphType secondType) {
|
||||
OsmandApplication app = getMyApplication();
|
||||
String title = app.getString(titleId);
|
||||
graphTypes.add(new CommonGraphType(title, canBeCalculated, hasData, firstType, secondType));
|
||||
}
|
||||
|
||||
private void setupVisibleType() {
|
||||
if (visibleType == null) {
|
||||
visibleType = getFirstAvailableType();
|
||||
} else {
|
||||
for (GraphType type : graphTypes) {
|
||||
if (isCurrentVisibleType(type)) {
|
||||
visibleGraphType = type.isAvailable() ? type : getFirstAvailableGraphType();
|
||||
if (isVisibleType(type)) {
|
||||
visibleType = type.isAvailable() ? type : getFirstAvailableType();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<ILineDataSet> getDataSets(CommonGraphType type, LineChart chart, GPXTrackAnalysis analysis) {
|
||||
List<ILineDataSet> dataSets = new ArrayList<>();
|
||||
if (chart != null && analysis != null) {
|
||||
OsmandApplication app = getMyApplication();
|
||||
switch (type) {
|
||||
case OVERVIEW: {
|
||||
List<OrderedLineDataSet> dataList = new ArrayList<>();
|
||||
if (analysis.hasSpeedData) {
|
||||
OrderedLineDataSet speedDataSet = GpxUiHelper.createGPXSpeedDataSet(app, chart,
|
||||
analysis, GPXDataSetAxisType.DISTANCE, true, true, false);
|
||||
dataList.add(speedDataSet);
|
||||
}
|
||||
if (analysis.hasElevationData) {
|
||||
OrderedLineDataSet elevationDataSet = GpxUiHelper.createGPXElevationDataSet(app, chart,
|
||||
analysis, GPXDataSetAxisType.DISTANCE, false, true, false);
|
||||
dataList.add(elevationDataSet);
|
||||
OrderedLineDataSet slopeDataSet = GpxUiHelper.createGPXSlopeDataSet(app, chart,
|
||||
analysis, GPXDataSetAxisType.DISTANCE, null, true, true, false);
|
||||
dataList.add(slopeDataSet);
|
||||
}
|
||||
if (dataList.size() > 0) {
|
||||
Collections.sort(dataList, new Comparator<OrderedLineDataSet>() {
|
||||
@Override
|
||||
public int compare(OrderedLineDataSet o1, OrderedLineDataSet o2) {
|
||||
return Float.compare(o1.getPriority(), o2.getPriority());
|
||||
}
|
||||
});
|
||||
}
|
||||
dataSets.addAll(dataList);
|
||||
break;
|
||||
}
|
||||
case ALTITUDE: {
|
||||
if (analysis.hasElevationData) {
|
||||
OrderedLineDataSet elevationDataSet = GpxUiHelper.createGPXElevationDataSet(app, chart,
|
||||
analysis, GPXDataSetAxisType.DISTANCE, false, true, false);//calcWithoutGaps);
|
||||
dataSets.add(elevationDataSet);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SLOPE:
|
||||
if (analysis.hasElevationData) {
|
||||
OrderedLineDataSet slopeDataSet = GpxUiHelper.createGPXSlopeDataSet(app, chart,
|
||||
analysis, GPXDataSetAxisType.DISTANCE, null, true, true, false);
|
||||
dataSets.add(slopeDataSet);
|
||||
}
|
||||
break;
|
||||
case SPEED: {
|
||||
if (analysis.hasSpeedData) {
|
||||
OrderedLineDataSet speedDataSet = GpxUiHelper.createGPXSpeedDataSet(app, chart,
|
||||
analysis, GPXDataSetAxisType.DISTANCE, false, true, false);//calcWithoutGaps);
|
||||
dataSets.add(speedDataSet);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return dataSets;
|
||||
}
|
||||
|
||||
private GPXTrackAnalysis createGpxTrackAnalysis() {
|
||||
GPXFile gpx;
|
||||
if (editingCtx.getGpxData() != null) {
|
||||
gpx = editingCtx.getGpxData().getGpxFile();
|
||||
private GPXFile getGpxFile() {
|
||||
if (fragment.isTrackReadyToCalculate()) {
|
||||
return editingCtx.exportGpx(GRAPH_DATA_GPX_FILE_NAME);
|
||||
} else {
|
||||
gpx = editingCtx.exportGpx(GRAPH_DATA_GPX_FILE_NAME);
|
||||
GpxData gpxData = editingCtx.getGpxData();
|
||||
return gpxData != null ? gpxData.getGpxFile() : null;
|
||||
}
|
||||
return gpx != null ? gpx.getAnalysis(0) : null;
|
||||
}
|
||||
|
||||
private List<RouteStatistics> calculateRouteStatistics(List<RouteSegmentResult> route) {
|
||||
private List<RouteStatistics> calculateRouteStatistics() {
|
||||
OsmandApplication app = getMyApplication();
|
||||
if (route == null || app == null) return null;
|
||||
return RouteDetailsFragment.calculateRouteStatistics(app, route, nightMode);
|
||||
List<RouteSegmentResult> route = editingCtx.getAllRouteSegments();
|
||||
if (route != null && app != null) {
|
||||
return RouteDetailsFragment.calculateRouteStatistics(app, route, nightMode);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isRouteCalculating() {
|
||||
return fragment.isProgressBarVisible();
|
||||
}
|
||||
|
||||
private static class GraphType {
|
||||
private String title;
|
||||
private boolean isCustom;
|
||||
private boolean canBeCalculated;
|
||||
private ChartData graphData;
|
||||
public boolean hasVisibleGraph() {
|
||||
return (commonGraphContainer != null && commonGraphContainer.getVisibility() == View.VISIBLE)
|
||||
|| (customGraphContainer != null && customGraphContainer.getVisibility() == View.VISIBLE);
|
||||
}
|
||||
|
||||
public GraphType(String title, boolean isCustom, boolean canBeCalculated, ChartData graphData) {
|
||||
private abstract class GraphType<T extends ChartData> {
|
||||
private String title;
|
||||
private boolean canBeCalculated;
|
||||
|
||||
public GraphType(String title, boolean canBeCalculated) {
|
||||
this.title = title;
|
||||
this.isCustom = isCustom;
|
||||
this.canBeCalculated = canBeCalculated;
|
||||
this.graphData = graphData;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
|
@ -374,23 +379,80 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen
|
|||
}
|
||||
|
||||
public boolean isCustom() {
|
||||
return isCustom;
|
||||
return this instanceof CustomGraphType;
|
||||
}
|
||||
|
||||
public boolean isAvailable() {
|
||||
return hasData() || canBeCalculated();
|
||||
return isPointsCountEnoughToCalculateRoute() && (hasData() || canBeCalculated());
|
||||
}
|
||||
|
||||
private boolean isPointsCountEnoughToCalculateRoute() {
|
||||
return editingCtx.getPointsCount() >= 2;
|
||||
}
|
||||
|
||||
public boolean canBeCalculated() {
|
||||
return canBeCalculated;
|
||||
}
|
||||
|
||||
public boolean hasData() {
|
||||
return getGraphData() != null;
|
||||
public abstract boolean hasData();
|
||||
|
||||
public abstract T getChartData();
|
||||
}
|
||||
|
||||
private class CommonGraphType extends GraphType<LineData> {
|
||||
|
||||
private boolean hasData;
|
||||
private LineGraphType firstType;
|
||||
private LineGraphType secondType;
|
||||
|
||||
public CommonGraphType(String title, boolean canBeCalculated, boolean hasData, @NonNull LineGraphType firstType, @Nullable LineGraphType secondType) {
|
||||
super(title, canBeCalculated);
|
||||
this.hasData = hasData;
|
||||
this.firstType = firstType;
|
||||
this.secondType = secondType;
|
||||
}
|
||||
|
||||
public ChartData getGraphData() {
|
||||
return graphData;
|
||||
@Override
|
||||
public boolean hasData() {
|
||||
return hasData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LineData getChartData() {
|
||||
GpxUiHelper.setupGPXChart(commonGraphAdapter.getChart(), 4, 24f, 16f, !nightMode, true);
|
||||
List<ILineDataSet> dataSets = GpxUiHelper.getDataSets(commonGraphAdapter.getChart(),
|
||||
app, analysis, firstType, secondType, false);
|
||||
return !Algorithms.isEmpty(dataSets) ? new LineData(dataSets) : null;
|
||||
}
|
||||
}
|
||||
|
||||
private class CustomGraphType extends GraphType<BarData> {
|
||||
|
||||
private RouteStatistics statistics;
|
||||
|
||||
public CustomGraphType(String title, RouteStatistics statistics) {
|
||||
super(title, false);
|
||||
this.statistics = statistics;
|
||||
}
|
||||
|
||||
public RouteStatistics getStatistics() {
|
||||
return statistics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasData() {
|
||||
return !Algorithms.isEmpty(statistics.elements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BarData getChartData() {
|
||||
GpxUiHelper.setupHorizontalGPXChart(app, customGraphAdapter.getChart(), 5, 9, 24, true, nightMode);
|
||||
BarData data = null;
|
||||
if (!Algorithms.isEmpty(statistics.elements)) {
|
||||
data = GpxUiHelper.buildStatisticChart(app, customGraphAdapter.getChart(),
|
||||
statistics, analysis, true, nightMode);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -398,6 +398,10 @@ public class MeasurementEditingContext {
|
|||
return before.points.size();
|
||||
}
|
||||
|
||||
public boolean isPointsEnoughToCalculateRoute() {
|
||||
return getPointsCount() >= 2;
|
||||
}
|
||||
|
||||
public List<RouteSegmentResult> getAllRouteSegments() {
|
||||
List<RouteSegmentResult> allSegments = new ArrayList<>();
|
||||
for (Pair<WptPt, WptPt> key : getOrderedRoadSegmentDataKeys()) {
|
||||
|
|
|
@ -50,6 +50,7 @@ import net.osmand.plus.activities.TrackActivity;
|
|||
import net.osmand.plus.base.BaseOsmAndFragment;
|
||||
import net.osmand.plus.base.ContextMenuFragment.MenuState;
|
||||
import net.osmand.plus.helpers.AndroidUiHelper;
|
||||
import net.osmand.plus.mapcontextmenu.other.TrackDetailsMenu;
|
||||
import net.osmand.plus.measurementtool.GpxApproximationFragment.GpxApproximationFragmentListener;
|
||||
import net.osmand.plus.measurementtool.OptionsBottomSheetDialogFragment.OptionsFragmentListener;
|
||||
import net.osmand.plus.measurementtool.RouteBetweenPointsBottomSheetDialogFragment.RouteBetweenPointsDialogMode;
|
||||
|
@ -113,8 +114,10 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
|
|||
private TextView distanceToCenterTv;
|
||||
private String pointsSt;
|
||||
private View additionalInfoContainer;
|
||||
private ViewGroup additionalInfoCardsContainer;
|
||||
private BaseCard visibleAdditionalInfoCard;
|
||||
private ViewGroup cardsContainer;
|
||||
private BaseCard visibleCard;
|
||||
private PointsCard pointsCard;
|
||||
private GraphsCard graphsCard;
|
||||
private LinearLayout customRadioButton;
|
||||
private View mainView;
|
||||
private ImageView upDownBtn;
|
||||
|
@ -141,6 +144,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
|
|||
private int cachedMapPosition;
|
||||
|
||||
private MeasurementEditingContext editingCtx = new MeasurementEditingContext();
|
||||
private GraphDetailsMenu detailsMenu;
|
||||
|
||||
private LatLon initialPoint;
|
||||
|
||||
|
@ -160,6 +164,19 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
|
|||
GRAPH
|
||||
}
|
||||
|
||||
private class GraphDetailsMenu extends TrackDetailsMenu {
|
||||
|
||||
@Override
|
||||
protected int getFragmentWidth() {
|
||||
return mainView.getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getFragmentHeight() {
|
||||
return mainView.getHeight();
|
||||
}
|
||||
}
|
||||
|
||||
private void setEditingCtx(MeasurementEditingContext editingCtx) {
|
||||
this.editingCtx = editingCtx;
|
||||
}
|
||||
|
@ -253,8 +270,9 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
|
|||
|
||||
mainView = view.findViewById(R.id.main_view);
|
||||
AndroidUtils.setBackground(mapActivity, mainView, nightMode, R.drawable.bg_bottom_menu_light, R.drawable.bg_bottom_menu_dark);
|
||||
detailsMenu = new GraphDetailsMenu();
|
||||
additionalInfoContainer = mainView.findViewById(R.id.additional_info_container);
|
||||
additionalInfoCardsContainer = mainView.findViewById(R.id.cards_container);
|
||||
cardsContainer = mainView.findViewById(R.id.cards_container);
|
||||
if (portrait) {
|
||||
customRadioButton = mainView.findViewById(R.id.custom_radio_buttons);
|
||||
|
||||
|
@ -278,6 +296,8 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
|
|||
}
|
||||
});
|
||||
}
|
||||
pointsCard = new PointsCard(mapActivity, this);
|
||||
graphsCard = new GraphsCard(mapActivity, detailsMenu, this);
|
||||
|
||||
if (progressBarVisible) {
|
||||
showProgressBar();
|
||||
|
@ -513,29 +533,33 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
|
|||
if (!additionalInfoExpanded || !isCurrentAdditionalInfoType(type)) {
|
||||
MapActivity ma = getMapActivity();
|
||||
if (ma == null) return;
|
||||
currentAdditionalInfoType = type;
|
||||
updateUpDownBtn();
|
||||
|
||||
OsmandApplication app = ma.getMyApplication();
|
||||
BaseCard additionalInfoCard = null;
|
||||
if (AdditionalInfoType.POINTS == type) {
|
||||
additionalInfoCard = new PointsCard(ma, this);
|
||||
visibleCard = pointsCard;
|
||||
UiUtilities.updateCustomRadioButtons(app, customRadioButton, nightMode, START);
|
||||
} else if (AdditionalInfoType.GRAPH == type) {
|
||||
additionalInfoCard = new GraphsCard(ma, this);
|
||||
visibleCard = graphsCard;
|
||||
UiUtilities.updateCustomRadioButtons(app, customRadioButton, nightMode, END);
|
||||
}
|
||||
if (additionalInfoCard != null) {
|
||||
visibleAdditionalInfoCard = additionalInfoCard;
|
||||
additionalInfoCardsContainer.removeAllViews();
|
||||
additionalInfoCardsContainer.addView(additionalInfoCard.build(ma));
|
||||
additionalInfoExpanded = true;
|
||||
}
|
||||
cardsContainer.removeAllViews();
|
||||
View cardView = visibleCard.getView() != null ? visibleCard.getView() : visibleCard.build(ma);
|
||||
cardsContainer.addView(cardView);
|
||||
|
||||
currentAdditionalInfoType = type;
|
||||
additionalInfoExpanded = true;
|
||||
updateUpDownBtn();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAdditionalInfoView() {
|
||||
if (visibleAdditionalInfoCard instanceof OnUpdateAdditionalInfoListener) {
|
||||
((OnUpdateAdditionalInfoListener) visibleAdditionalInfoCard).onUpdateAdditionalInfo();
|
||||
updateAdditionalInfoView(pointsCard);
|
||||
updateAdditionalInfoView(graphsCard);
|
||||
}
|
||||
|
||||
private void updateAdditionalInfoView(OnUpdateAdditionalInfoListener listener) {
|
||||
if (listener != null) {
|
||||
listener.onUpdateAdditionalInfo();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -588,6 +612,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
|
|||
super.onResume();
|
||||
MapActivity mapActivity = getMapActivity();
|
||||
if (mapActivity != null) {
|
||||
detailsMenu.setMapActivity(mapActivity);
|
||||
mapActivity.getMapLayers().getMapControlsLayer().addThemeInfoProviderTag(TAG);
|
||||
mapActivity.getMapLayers().getMapControlsLayer().showMapControlsIfHidden();
|
||||
cachedMapPosition = mapActivity.getMapView().getMapPosition();
|
||||
|
@ -603,6 +628,8 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
|
|||
if (mapActivity != null) {
|
||||
mapActivity.getMapLayers().getMapControlsLayer().removeThemeInfoProviderTag(TAG);
|
||||
}
|
||||
detailsMenu.onDismiss();
|
||||
detailsMenu.setMapActivity(null);
|
||||
setMapPosition(cachedMapPosition);
|
||||
}
|
||||
|
||||
|
@ -669,7 +696,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
|
|||
mainIcon.setImageDrawable(getActiveIcon(gpxData != null ? R.drawable.ic_action_polygom_dark : R.drawable.ic_action_ruler));
|
||||
}
|
||||
|
||||
private void startSnapToRoad(boolean rememberPreviousTitle) {
|
||||
public void startSnapToRoad(boolean rememberPreviousTitle) {
|
||||
MapActivity mapActivity = getMapActivity();
|
||||
if (mapActivity != null) {
|
||||
if (rememberPreviousTitle) {
|
||||
|
@ -1189,7 +1216,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
|
|||
final ApplicationMode appMode = editingCtx.getAppMode();
|
||||
if (mapActivity != null) {
|
||||
Drawable icon;
|
||||
if (!editingCtx.isApproximationNeeded() || editingCtx.isNewData()) {
|
||||
if (isTrackReadyToCalculate()) {
|
||||
if (appMode == MeasurementEditingContext.DEFAULT_APP_MODE) {
|
||||
icon = getActiveIcon(R.drawable.ic_action_split_interval);
|
||||
} else {
|
||||
|
@ -1204,6 +1231,10 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
|
|||
}
|
||||
}
|
||||
|
||||
public boolean isTrackReadyToCalculate() {
|
||||
return !editingCtx.isApproximationNeeded() || editingCtx.isNewData();
|
||||
}
|
||||
|
||||
private void hideSnapToRoadIcon() {
|
||||
MapActivity mapActivity = getMapActivity();
|
||||
if (mapActivity != null) {
|
||||
|
@ -1488,6 +1519,10 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
|
|||
return type.equals(currentAdditionalInfoType);
|
||||
}
|
||||
|
||||
public boolean hasVisibleGraph() {
|
||||
return graphsCard != null && graphsCard.hasVisibleGraph();
|
||||
}
|
||||
|
||||
private String getSuggestedFileName() {
|
||||
GpxData gpxData = editingCtx.getGpxData();
|
||||
String displayedName = null;
|
||||
|
|
|
@ -24,7 +24,9 @@ public class PointsCard extends BaseCard implements OnUpdateAdditionalInfoListen
|
|||
|
||||
@Override
|
||||
public void onUpdateAdditionalInfo() {
|
||||
adapter.notifyDataSetChanged();
|
||||
if (adapter != null) {
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
package net.osmand.plus.measurementtool.graph;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.github.mikephil.charting.charts.Chart;
|
||||
import com.github.mikephil.charting.data.ChartData;
|
||||
import com.github.mikephil.charting.data.Entry;
|
||||
import com.github.mikephil.charting.highlight.Highlight;
|
||||
import com.github.mikephil.charting.listener.ChartTouchListener;
|
||||
|
||||
import net.osmand.plus.OsmandApplication;
|
||||
|
||||
public abstract class BaseGraphAdapter<_Chart extends Chart, _ChartData extends ChartData, _Data> {
|
||||
|
||||
private Highlight lastKnownHighlight;
|
||||
protected _Chart chart;
|
||||
protected _ChartData chartData;
|
||||
protected _Data additionalData;
|
||||
protected boolean usedOnMap;
|
||||
|
||||
public BaseGraphAdapter(_Chart chart, boolean usedOnMap) {
|
||||
this.chart = chart;
|
||||
this.usedOnMap = usedOnMap;
|
||||
prepareChartView();
|
||||
}
|
||||
|
||||
protected void prepareChartView() {
|
||||
chart.setExtraRightOffset(16);
|
||||
chart.setExtraLeftOffset(16);
|
||||
}
|
||||
|
||||
public _Chart getChart() {
|
||||
return chart;
|
||||
}
|
||||
|
||||
protected void updateHighlight() {
|
||||
highlight(lastKnownHighlight);
|
||||
}
|
||||
|
||||
public void highlight(Highlight h) {
|
||||
this.lastKnownHighlight = h;
|
||||
}
|
||||
|
||||
public void updateContent(_ChartData chartData, _Data data) {
|
||||
updateData(chartData, data);
|
||||
updateView();
|
||||
}
|
||||
|
||||
public void updateData(_ChartData chartData, _Data data) {
|
||||
this.chartData = chartData;
|
||||
this.additionalData = data;
|
||||
}
|
||||
|
||||
public abstract void updateView();
|
||||
|
||||
protected boolean isNightMode() {
|
||||
OsmandApplication app = getMyApplication();
|
||||
if (app != null) {
|
||||
return usedOnMap ? app.getDaynightHelper().isNightModeForMapControls()
|
||||
: !app.getSettings().isLightContent();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected OsmandApplication getMyApplication() {
|
||||
return (OsmandApplication) chart.getContext().getApplicationContext();
|
||||
}
|
||||
|
||||
public interface ExternalValueSelectedListener {
|
||||
void onValueSelected(Entry e, Highlight h);
|
||||
void onNothingSelected();
|
||||
}
|
||||
|
||||
public interface ExternalGestureListener {
|
||||
void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture);
|
||||
void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture, boolean hasTranslated);
|
||||
}
|
||||
|
||||
public interface LayoutChangeListener {
|
||||
void onLayoutChanged();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
package net.osmand.plus.measurementtool.graph;
|
||||
|
||||
import android.graphics.Matrix;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
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.listener.ChartTouchListener;
|
||||
import com.github.mikephil.charting.listener.OnChartGestureListener;
|
||||
import com.github.mikephil.charting.listener.OnChartValueSelectedListener;
|
||||
|
||||
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class CommonGraphAdapter extends BaseGraphAdapter<LineChart, LineData, GpxDisplayItem> {
|
||||
|
||||
private Highlight highlight;
|
||||
private Map<String, ExternalValueSelectedListener> externalValueSelectedListeners = new HashMap<>();
|
||||
private ExternalGestureListener externalGestureListener;
|
||||
|
||||
public CommonGraphAdapter(LineChart chart, boolean usedOnMap) {
|
||||
super(chart, usedOnMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareChartView() {
|
||||
super.prepareChartView();
|
||||
|
||||
chart.setOnChartValueSelectedListener(new OnChartValueSelectedListener() {
|
||||
@Override
|
||||
public void onValueSelected(Entry e, Highlight h) {
|
||||
highlight = h;
|
||||
for (ExternalValueSelectedListener listener : externalValueSelectedListeners.values()) {
|
||||
listener.onValueSelected(e, h);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected() {
|
||||
for (ExternalValueSelectedListener listener : externalValueSelectedListeners.values()) {
|
||||
listener.onNothingSelected();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
chart.setOnChartGestureListener(new OnChartGestureListener() {
|
||||
boolean hasTranslated = false;
|
||||
float highlightDrawX = -1;
|
||||
|
||||
@Override
|
||||
public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) {
|
||||
hasTranslated = false;
|
||||
if (chart.getHighlighted() != null && chart.getHighlighted().length > 0) {
|
||||
highlightDrawX = chart.getHighlighted()[0].getDrawX();
|
||||
} else {
|
||||
highlightDrawX = -1;
|
||||
}
|
||||
if (externalGestureListener != null) {
|
||||
externalGestureListener.onChartGestureStart(me, lastPerformedGesture);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) {
|
||||
GpxDisplayItem gpxItem = getGpxItem();
|
||||
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 (externalGestureListener != null) {
|
||||
externalGestureListener.onChartGestureEnd(me, lastPerformedGesture, hasTranslated);
|
||||
}
|
||||
}
|
||||
|
||||
@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) {
|
||||
hasTranslated = true;
|
||||
if (highlightDrawX != -1) {
|
||||
Highlight h = chart.getHighlightByTouchPoint(highlightDrawX, 0f);
|
||||
if (h != null) {
|
||||
chart.highlightValue(h, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void addValueSelectedListener(String key, ExternalValueSelectedListener listener) {
|
||||
this.externalValueSelectedListeners.put(key, listener);
|
||||
}
|
||||
|
||||
public void setExternalGestureListener(ExternalGestureListener listener) {
|
||||
this.externalGestureListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateView() {
|
||||
chart.setData(chartData);
|
||||
updateHighlight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void highlight(Highlight h) {
|
||||
super.highlight(h);
|
||||
chart.highlightValue(highlight);
|
||||
}
|
||||
|
||||
public GpxDisplayItem getGpxItem() {
|
||||
return additionalData;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
package net.osmand.plus.measurementtool.graph;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
|
||||
import com.github.mikephil.charting.charts.HorizontalBarChart;
|
||||
import com.github.mikephil.charting.data.BarData;
|
||||
import com.github.mikephil.charting.data.Entry;
|
||||
import com.github.mikephil.charting.highlight.Highlight;
|
||||
import com.github.mikephil.charting.listener.OnChartValueSelectedListener;
|
||||
|
||||
import net.osmand.AndroidUtils;
|
||||
import net.osmand.plus.OsmAndFormatter;
|
||||
import net.osmand.plus.OsmandApplication;
|
||||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.helpers.CustomBarChartRenderer;
|
||||
import net.osmand.router.RouteStatisticsHelper;
|
||||
import net.osmand.router.RouteStatisticsHelper.RouteStatistics;
|
||||
import net.osmand.router.RouteStatisticsHelper.RouteSegmentAttribute;
|
||||
import net.osmand.util.Algorithms;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static net.osmand.plus.track.ColorsCard.MINIMUM_CONTRAST_RATIO;
|
||||
|
||||
public class CustomGraphAdapter extends BaseGraphAdapter<HorizontalBarChart, BarData, RouteStatistics> {
|
||||
|
||||
private String selectedPropertyName;
|
||||
private ViewGroup legendContainer;
|
||||
private LegendViewType legendViewType;
|
||||
private LayoutChangeListener layoutChangeListener;
|
||||
|
||||
public enum LegendViewType {
|
||||
ONE_ELEMENT,
|
||||
ALL_AS_LIST,
|
||||
GONE
|
||||
}
|
||||
|
||||
public CustomGraphAdapter(HorizontalBarChart chart, boolean usedOnMap) {
|
||||
super(chart, usedOnMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareChartView() {
|
||||
super.prepareChartView();
|
||||
legendViewType = LegendViewType.GONE;
|
||||
chart.setRenderer(new CustomBarChartRenderer(chart));
|
||||
chart.setOnChartValueSelectedListener(new OnChartValueSelectedListener() {
|
||||
@Override
|
||||
public void onValueSelected(Entry e, Highlight h) {
|
||||
if (getStatistics() == null) return;
|
||||
|
||||
List<RouteStatisticsHelper.RouteSegmentAttribute> elems = getStatistics().elements;
|
||||
int i = h.getStackIndex();
|
||||
if (i >= 0 && elems.size() > i) {
|
||||
selectedPropertyName = elems.get(i).getPropertyName();
|
||||
updateLegend();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected() {
|
||||
selectedPropertyName = null;
|
||||
updateLegend();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateView() {
|
||||
chart.setData(chartData);
|
||||
updateHighlight();
|
||||
updateLegend();
|
||||
}
|
||||
|
||||
public void setLegendContainer(ViewGroup legendContainer) {
|
||||
this.legendContainer = legendContainer;
|
||||
}
|
||||
|
||||
public void setLegendViewType(LegendViewType legendViewType) {
|
||||
this.legendViewType = legendViewType;
|
||||
}
|
||||
|
||||
public void setLayoutChangeListener(LayoutChangeListener layoutChangeListener) {
|
||||
this.layoutChangeListener = layoutChangeListener;
|
||||
}
|
||||
|
||||
public void highlight(Highlight h) {
|
||||
super.highlight(h);
|
||||
Highlight bh = h != null ? chart.getHighlighter().getHighlight(1, h.getXPx()) : null;
|
||||
if (bh != null) {
|
||||
bh.setDraw(h.getXPx(), 0);
|
||||
}
|
||||
chart.highlightValue(bh, true);
|
||||
}
|
||||
|
||||
private void updateLegend() {
|
||||
if (legendContainer != null) {
|
||||
legendContainer.removeAllViews();
|
||||
attachLegend();
|
||||
if (layoutChangeListener != null) {
|
||||
layoutChangeListener.onLayoutChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void attachLegend() {
|
||||
if (getSegmentsList() == null) return;
|
||||
|
||||
switch (legendViewType) {
|
||||
case ONE_ELEMENT:
|
||||
for (RouteSegmentAttribute segment : getSegmentsList()) {
|
||||
if (segment.getPropertyName().equals(selectedPropertyName)) {
|
||||
attachLegend(Collections.singletonList(segment), null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ALL_AS_LIST:
|
||||
attachLegend(getSegmentsList(), selectedPropertyName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void attachLegend(List<RouteSegmentAttribute> list,
|
||||
String propertyNameToFullSpan) {
|
||||
OsmandApplication app = getMyApplication();
|
||||
LayoutInflater inflater = LayoutInflater.from(app);
|
||||
for (RouteStatisticsHelper.RouteSegmentAttribute segment : list) {
|
||||
View view = inflater.inflate(R.layout.route_details_legend, legendContainer, false);
|
||||
int segmentColor = segment.getColor();
|
||||
Drawable circle = app.getUIUtilities().getPaintedIcon(R.drawable.ic_action_circle, segmentColor);
|
||||
ImageView legendIcon = (ImageView) view.findViewById(R.id.legend_icon_color);
|
||||
legendIcon.setImageDrawable(circle);
|
||||
double contrastRatio = ColorUtils.calculateContrast(segmentColor,
|
||||
AndroidUtils.getColorFromAttr(app, R.attr.card_and_list_background_basic));
|
||||
if (contrastRatio < MINIMUM_CONTRAST_RATIO) {
|
||||
legendIcon.setBackgroundResource(AndroidUtils.resolveAttribute(app, R.attr.bg_circle_contour));
|
||||
}
|
||||
String propertyName = segment.getUserPropertyName();
|
||||
String name = AndroidUtils.getRenderingStringPropertyName(app, propertyName, propertyName.replaceAll("_", " "));
|
||||
boolean selected = segment.getPropertyName().equals(propertyNameToFullSpan);
|
||||
Spannable text = getSpanLegend(name, segment, selected);
|
||||
TextView legend = (TextView) view.findViewById(R.id.legend_text);
|
||||
legend.setText(text);
|
||||
|
||||
legendContainer.addView(view);
|
||||
}
|
||||
}
|
||||
|
||||
private Spannable getSpanLegend(String title,
|
||||
RouteSegmentAttribute segment,
|
||||
boolean fullSpan) {
|
||||
String formattedDistance = OsmAndFormatter.getFormattedDistance(segment.getDistance(), getMyApplication());
|
||||
title = Algorithms.capitalizeFirstLetter(title);
|
||||
SpannableStringBuilder spannable = new SpannableStringBuilder(title);
|
||||
spannable.append(": ");
|
||||
int startIndex = fullSpan ? -0 : spannable.length();
|
||||
spannable.append(formattedDistance);
|
||||
spannable.setSpan(new StyleSpan(android.graphics.Typeface.BOLD),
|
||||
startIndex, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
return spannable;
|
||||
}
|
||||
|
||||
private List<RouteSegmentAttribute> getSegmentsList() {
|
||||
return getStatistics() != null ? new ArrayList<>(getStatistics().partition.values()) : null;
|
||||
}
|
||||
|
||||
private RouteStatistics getStatistics() {
|
||||
return additionalData;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
package net.osmand.plus.measurementtool.graph;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.github.mikephil.charting.charts.BarChart;
|
||||
import com.github.mikephil.charting.charts.LineChart;
|
||||
import com.github.mikephil.charting.data.Entry;
|
||||
import com.github.mikephil.charting.highlight.Highlight;
|
||||
import com.github.mikephil.charting.listener.ChartTouchListener;
|
||||
|
||||
import net.osmand.plus.OsmandApplication;
|
||||
import net.osmand.plus.activities.MapActivity;
|
||||
import net.osmand.plus.mapcontextmenu.other.TrackDetailsMenu;
|
||||
import net.osmand.plus.measurementtool.graph.BaseGraphAdapter.ExternalValueSelectedListener;
|
||||
import net.osmand.plus.measurementtool.graph.BaseGraphAdapter.ExternalGestureListener;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GraphAdapterHelper {
|
||||
|
||||
public static final String BIND_GRAPH_ADAPTERS_KEY = "bind_graph_adapters_key";
|
||||
public static final String BIND_TO_MAP_KEY = "bind_to_map_key";
|
||||
|
||||
public static void bindGraphAdapters(final CommonGraphAdapter mainGraphAdapter,
|
||||
final List<BaseGraphAdapter> otherGraphAdapters,
|
||||
final ViewGroup mainView) {
|
||||
if (mainGraphAdapter == null || mainGraphAdapter.getChart() == null
|
||||
|| otherGraphAdapters == null || otherGraphAdapters.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
final LineChart mainChart = mainGraphAdapter.getChart();
|
||||
View.OnTouchListener mainChartTouchListener = new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent ev) {
|
||||
if (mainView != null) {
|
||||
mainView.requestDisallowInterceptTouchEvent(true);
|
||||
}
|
||||
for (BaseGraphAdapter adapter : otherGraphAdapters) {
|
||||
if (adapter.getChart() != null) {
|
||||
MotionEvent event = MotionEvent.obtainNoHistory(ev);
|
||||
event.setSource(0);
|
||||
adapter.getChart().dispatchTouchEvent(event);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
mainChart.setOnTouchListener(mainChartTouchListener);
|
||||
|
||||
mainGraphAdapter.addValueSelectedListener(BIND_GRAPH_ADAPTERS_KEY,
|
||||
new ExternalValueSelectedListener() {
|
||||
@Override
|
||||
public void onValueSelected(Entry e, Highlight h) {
|
||||
for (BaseGraphAdapter adapter : otherGraphAdapters) {
|
||||
adapter.highlight(h);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected() {
|
||||
for (BaseGraphAdapter adapter : otherGraphAdapters) {
|
||||
adapter.highlight(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
View.OnTouchListener otherChartsTouchListener = new View.OnTouchListener() {
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent ev) {
|
||||
if (ev.getSource() != 0) {
|
||||
final MotionEvent event = MotionEvent.obtainNoHistory(ev);
|
||||
event.setSource(0);
|
||||
mainChart.dispatchTouchEvent(event);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
for (BaseGraphAdapter adapter : otherGraphAdapters) {
|
||||
if (adapter.getChart() != null) {
|
||||
if (adapter.getChart() instanceof BarChart) {
|
||||
// maybe we should find min and max axis from all charters
|
||||
BarChart barChart = (BarChart) adapter.getChart();
|
||||
barChart.getAxisRight().setAxisMinimum(mainChart.getXChartMin());
|
||||
barChart.getAxisRight().setAxisMaximum(mainChart.getXChartMax());
|
||||
barChart.setHighlightPerDragEnabled(false);
|
||||
barChart.setHighlightPerTapEnabled(false);
|
||||
}
|
||||
adapter.getChart().setOnTouchListener(otherChartsTouchListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static RefreshMapCallback bindToMap(@NonNull final CommonGraphAdapter graphAdapter,
|
||||
@NonNull final MapActivity mapActivity,
|
||||
@NonNull final TrackDetailsMenu detailsMenu) {
|
||||
final RefreshMapCallback refreshMapCallback = new RefreshMapCallback() {
|
||||
@Override
|
||||
public void refreshMap(boolean forceFit) {
|
||||
LineChart chart = graphAdapter.getChart();
|
||||
OsmandApplication app = mapActivity.getMyApplication();
|
||||
if (!app.getRoutingHelper().isFollowingMode()) {
|
||||
detailsMenu.refreshChart(chart, forceFit);
|
||||
mapActivity.refreshMap();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
graphAdapter.addValueSelectedListener(BIND_TO_MAP_KEY,
|
||||
new CommonGraphAdapter.ExternalValueSelectedListener() {
|
||||
|
||||
@Override
|
||||
public void onValueSelected(Entry e, Highlight h) {
|
||||
refreshMapCallback.refreshMap(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected() {
|
||||
}
|
||||
});
|
||||
|
||||
graphAdapter.setExternalGestureListener(new ExternalGestureListener() {
|
||||
@Override
|
||||
public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture, boolean hasTranslated) {
|
||||
if ((lastPerformedGesture == ChartTouchListener.ChartGesture.DRAG && hasTranslated) ||
|
||||
lastPerformedGesture == ChartTouchListener.ChartGesture.X_ZOOM ||
|
||||
lastPerformedGesture == ChartTouchListener.ChartGesture.Y_ZOOM ||
|
||||
lastPerformedGesture == ChartTouchListener.ChartGesture.PINCH_ZOOM ||
|
||||
lastPerformedGesture == ChartTouchListener.ChartGesture.DOUBLE_TAP ||
|
||||
lastPerformedGesture == ChartTouchListener.ChartGesture.ROTATE) {
|
||||
refreshMapCallback.refreshMap(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return refreshMapCallback;
|
||||
}
|
||||
|
||||
public interface RefreshMapCallback {
|
||||
void refreshMap(boolean forceFit);
|
||||
}
|
||||
}
|
|
@ -62,6 +62,7 @@ import net.osmand.plus.activities.TrackActivity;
|
|||
import net.osmand.plus.base.OsmAndListFragment;
|
||||
import net.osmand.plus.helpers.AndroidUiHelper;
|
||||
import net.osmand.plus.helpers.GpxUiHelper;
|
||||
import net.osmand.plus.helpers.GpxUiHelper.LineGraphType;
|
||||
import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetAxisType;
|
||||
import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetType;
|
||||
import net.osmand.plus.helpers.GpxUiHelper.OrderedLineDataSet;
|
||||
|
@ -86,6 +87,10 @@ 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 TrackSegmentFragment extends OsmAndListFragment implements TrackBitmapDrawerListener {
|
||||
|
||||
private OsmandApplication app;
|
||||
|
@ -424,64 +429,17 @@ public class TrackSegmentFragment extends OsmAndListFragment implements TrackBit
|
|||
}
|
||||
}
|
||||
|
||||
private List<ILineDataSet> getDataSets(GPXTabItemType tabType, LineChart chart) {
|
||||
private List<ILineDataSet> getDataSets(LineChart chart,
|
||||
GPXTabItemType tabType,
|
||||
LineGraphType firstType,
|
||||
LineGraphType secondType) {
|
||||
List<ILineDataSet> dataSets = dataSetsMap.get(tabType);
|
||||
if (dataSets == null && chart != null) {
|
||||
dataSets = new ArrayList<>();
|
||||
GPXTrackAnalysis analysis = gpxItem.analysis;
|
||||
GpxDataItem gpxDataItem = getGpxDataItem();
|
||||
boolean calcWithoutGaps = gpxItem.isGeneralTrack() && gpxDataItem != null && !gpxDataItem.isJoinSegments();
|
||||
switch (tabType) {
|
||||
case GPX_TAB_ITEM_GENERAL: {
|
||||
OrderedLineDataSet speedDataSet = null;
|
||||
OrderedLineDataSet elevationDataSet = null;
|
||||
if (analysis.hasSpeedData) {
|
||||
speedDataSet = GpxUiHelper.createGPXSpeedDataSet(app, chart,
|
||||
analysis, GPXDataSetAxisType.DISTANCE, true, true, calcWithoutGaps);
|
||||
}
|
||||
if (analysis.hasElevationData) {
|
||||
elevationDataSet = GpxUiHelper.createGPXElevationDataSet(app, chart,
|
||||
analysis, GPXDataSetAxisType.DISTANCE, false, true, calcWithoutGaps);
|
||||
}
|
||||
if (speedDataSet != null) {
|
||||
dataSets.add(speedDataSet);
|
||||
if (elevationDataSet != null) {
|
||||
dataSets.add(elevationDataSet.getPriority() < speedDataSet.getPriority()
|
||||
? 1 : 0, elevationDataSet);
|
||||
}
|
||||
} else if (elevationDataSet != null) {
|
||||
dataSets.add(elevationDataSet);
|
||||
}
|
||||
dataSetsMap.put(GPXTabItemType.GPX_TAB_ITEM_GENERAL, dataSets);
|
||||
break;
|
||||
}
|
||||
case GPX_TAB_ITEM_ALTITUDE: {
|
||||
OrderedLineDataSet elevationDataSet = GpxUiHelper.createGPXElevationDataSet(app, chart,
|
||||
analysis, GPXDataSetAxisType.DISTANCE, false, true, calcWithoutGaps);
|
||||
if (elevationDataSet != null) {
|
||||
dataSets.add(elevationDataSet);
|
||||
}
|
||||
if (analysis.hasElevationData) {
|
||||
List<Entry> eleValues = elevationDataSet != null && !gpxItem.isGeneralTrack() ? elevationDataSet.getValues() : null;
|
||||
OrderedLineDataSet slopeDataSet = GpxUiHelper.createGPXSlopeDataSet(app, chart,
|
||||
analysis, GPXDataSetAxisType.DISTANCE, eleValues, true, true, calcWithoutGaps);
|
||||
if (slopeDataSet != null) {
|
||||
dataSets.add(slopeDataSet);
|
||||
}
|
||||
}
|
||||
dataSetsMap.put(GPXTabItemType.GPX_TAB_ITEM_ALTITUDE, dataSets);
|
||||
break;
|
||||
}
|
||||
case GPX_TAB_ITEM_SPEED: {
|
||||
OrderedLineDataSet speedDataSet = GpxUiHelper.createGPXSpeedDataSet(app, chart,
|
||||
analysis, GPXDataSetAxisType.DISTANCE, false, true, calcWithoutGaps);
|
||||
if (speedDataSet != null) {
|
||||
dataSets.add(speedDataSet);
|
||||
}
|
||||
dataSetsMap.put(GPXTabItemType.GPX_TAB_ITEM_SPEED, dataSets);
|
||||
break;
|
||||
}
|
||||
}
|
||||
dataSets = GpxUiHelper.getDataSets(chart, app, analysis, firstType, secondType, calcWithoutGaps);
|
||||
dataSetsMap.put(tabType, dataSets);
|
||||
}
|
||||
return dataSets;
|
||||
}
|
||||
|
@ -702,7 +660,7 @@ public class TrackSegmentFragment extends OsmAndListFragment implements TrackBit
|
|||
if (analysis != null) {
|
||||
if (analysis.hasElevationData || analysis.hasSpeedData) {
|
||||
GpxUiHelper.setupGPXChart(app, chart, 4);
|
||||
chart.setData(new LineData(getDataSets(GPXTabItemType.GPX_TAB_ITEM_GENERAL, chart)));
|
||||
chart.setData(new LineData(getDataSets(chart, GPXTabItemType.GPX_TAB_ITEM_GENERAL, ALTITUDE, SPEED)));
|
||||
updateChart(chart);
|
||||
chart.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
|
@ -820,7 +778,7 @@ public class TrackSegmentFragment extends OsmAndListFragment implements TrackBit
|
|||
if (analysis != null) {
|
||||
if (analysis.hasElevationData) {
|
||||
GpxUiHelper.setupGPXChart(app, chart, 4);
|
||||
chart.setData(new LineData(getDataSets(GPXTabItemType.GPX_TAB_ITEM_ALTITUDE, chart)));
|
||||
chart.setData(new LineData(getDataSets(chart, GPXTabItemType.GPX_TAB_ITEM_ALTITUDE, ALTITUDE, SLOPE)));
|
||||
updateChart(chart);
|
||||
chart.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
|
@ -922,7 +880,7 @@ public class TrackSegmentFragment extends OsmAndListFragment implements TrackBit
|
|||
if (analysis != null && analysis.isSpeedSpecified()) {
|
||||
if (analysis.hasSpeedData) {
|
||||
GpxUiHelper.setupGPXChart(app, chart, 4);
|
||||
chart.setData(new LineData(getDataSets(GPXTabItemType.GPX_TAB_ITEM_SPEED, chart)));
|
||||
chart.setData(new LineData(getDataSets(chart, GPXTabItemType.GPX_TAB_ITEM_SPEED, SPEED, null)));
|
||||
updateChart(chart);
|
||||
chart.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
|
@ -1188,7 +1146,7 @@ public class TrackSegmentFragment extends OsmAndListFragment implements TrackBit
|
|||
LatLon location = null;
|
||||
WptPt wpt = null;
|
||||
gpxItem.chartTypes = null;
|
||||
List<ILineDataSet> ds = getDataSets(tabType, null);
|
||||
List<ILineDataSet> ds = getDataSets(null, tabType, null, null);
|
||||
if (ds != null && ds.size() > 0) {
|
||||
gpxItem.chartTypes = new GPXDataSetType[ds.size()];
|
||||
for (int i = 0; i < ds.size(); i++) {
|
||||
|
|
|
@ -2,7 +2,6 @@ package net.osmand.plus.routepreparationmenu;
|
|||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
|
@ -14,11 +13,9 @@ import android.text.TextUtils;
|
|||
import android.text.style.ForegroundColorSpan;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.View.OnLongClickListener;
|
||||
import android.view.View.OnTouchListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
|
@ -30,15 +27,7 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.github.mikephil.charting.animation.ChartAnimator;
|
||||
import com.github.mikephil.charting.charts.HorizontalBarChart;
|
||||
import com.github.mikephil.charting.charts.LineChart;
|
||||
import com.github.mikephil.charting.data.Entry;
|
||||
import com.github.mikephil.charting.highlight.Highlight;
|
||||
import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider;
|
||||
import com.github.mikephil.charting.listener.ChartTouchListener.ChartGesture;
|
||||
import com.github.mikephil.charting.renderer.HorizontalBarChartRenderer;
|
||||
import com.github.mikephil.charting.utils.ViewPortHandler;
|
||||
|
||||
import net.osmand.AndroidUtils;
|
||||
import net.osmand.GPXUtilities.GPXFile;
|
||||
|
@ -68,10 +57,13 @@ import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetType;
|
|||
import net.osmand.plus.helpers.GpxUiHelper.OrderedLineDataSet;
|
||||
import net.osmand.plus.mapcontextmenu.CollapsableView;
|
||||
import net.osmand.plus.mapcontextmenu.other.TrackDetailsMenu;
|
||||
import net.osmand.plus.measurementtool.graph.BaseGraphAdapter;
|
||||
import net.osmand.plus.measurementtool.graph.CommonGraphAdapter;
|
||||
import net.osmand.plus.measurementtool.graph.GraphAdapterHelper;
|
||||
import net.osmand.plus.measurementtool.graph.GraphAdapterHelper.RefreshMapCallback;
|
||||
import net.osmand.plus.render.MapRenderRepositories;
|
||||
import net.osmand.plus.routepreparationmenu.cards.BaseCard;
|
||||
import net.osmand.plus.routepreparationmenu.cards.BaseCard.CardListener;
|
||||
import net.osmand.plus.routepreparationmenu.cards.CardChartListener;
|
||||
import net.osmand.plus.routepreparationmenu.cards.PublicTransportCard;
|
||||
import net.osmand.plus.routepreparationmenu.cards.PublicTransportCard.PublicTransportCardListener;
|
||||
import net.osmand.plus.routepreparationmenu.cards.RouteDirectionsCard;
|
||||
|
@ -98,8 +90,8 @@ import java.lang.ref.WeakReference;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class RouteDetailsFragment extends ContextMenuFragment implements PublicTransportCardListener,
|
||||
CardListener, CardChartListener {
|
||||
public class RouteDetailsFragment extends ContextMenuFragment
|
||||
implements PublicTransportCardListener, CardListener {
|
||||
|
||||
public static final String ROUTE_ID_KEY = "route_id_key";
|
||||
private static final float PAGE_MARGIN = 5f;
|
||||
|
@ -122,6 +114,7 @@ public class RouteDetailsFragment extends ContextMenuFragment implements PublicT
|
|||
private RouteStatisticCard statisticCard;
|
||||
private List<RouteInfoCard> routeInfoCards = new ArrayList<>();
|
||||
private RouteDetailsMenu routeDetailsMenu;
|
||||
private RefreshMapCallback refreshMapCallback;
|
||||
|
||||
public interface RouteDetailsFragmentListener {
|
||||
void onNavigationRequested();
|
||||
|
@ -311,24 +304,7 @@ public class RouteDetailsFragment extends ContextMenuFragment implements PublicT
|
|||
return;
|
||||
}
|
||||
OsmandApplication app = mapActivity.getMyApplication();
|
||||
statisticCard = new RouteStatisticCard(mapActivity, gpx, new OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent ev) {
|
||||
LinearLayout mainView = getMainView();
|
||||
if (mainView != null) {
|
||||
mainView.requestDisallowInterceptTouchEvent(true);
|
||||
}
|
||||
for (RouteInfoCard card : routeInfoCards) {
|
||||
final HorizontalBarChart ch = card.getChart();
|
||||
if (ch != null) {
|
||||
final MotionEvent event = MotionEvent.obtainNoHistory(ev);
|
||||
event.setSource(0);
|
||||
ch.dispatchTouchEvent(event);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}, new OnClickListener() {
|
||||
statisticCard = new RouteStatisticCard(mapActivity, gpx, new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
openDetails();
|
||||
|
@ -336,7 +312,6 @@ public class RouteDetailsFragment extends ContextMenuFragment implements PublicT
|
|||
});
|
||||
statisticCard.setTransparentBackground(true);
|
||||
statisticCard.setListener(this);
|
||||
statisticCard.setChartListener(this);
|
||||
menuCards.add(statisticCard);
|
||||
cardsContainer.addView(statisticCard.build(mapActivity));
|
||||
buildRowDivider(cardsContainer, false);
|
||||
|
@ -359,13 +334,25 @@ public class RouteDetailsFragment extends ContextMenuFragment implements PublicT
|
|||
routeDetailsMenu.setGpxItem(gpxItem);
|
||||
}
|
||||
routeDetailsMenu.setMapActivity(mapActivity);
|
||||
LineChart chart = statisticCard.getChart();
|
||||
if (chart != null) {
|
||||
chart.setExtraRightOffset(16);
|
||||
chart.setExtraLeftOffset(16);
|
||||
|
||||
CommonGraphAdapter mainGraphAdapter = statisticCard.getGraphAdapter();
|
||||
if (mainGraphAdapter != null) {
|
||||
GraphAdapterHelper.bindGraphAdapters(mainGraphAdapter, getRouteInfoCardsGraphAdapters(), getMainView());
|
||||
refreshMapCallback = GraphAdapterHelper.bindToMap(mainGraphAdapter, mapActivity, routeDetailsMenu);
|
||||
}
|
||||
}
|
||||
|
||||
private List<BaseGraphAdapter> getRouteInfoCardsGraphAdapters() {
|
||||
List<BaseGraphAdapter> adapters = new ArrayList<>();
|
||||
for (RouteInfoCard card : routeInfoCards) {
|
||||
BaseGraphAdapter adapter = card.getGraphAdapter();
|
||||
if (adapter != null) {
|
||||
adapters.add(adapter);
|
||||
}
|
||||
}
|
||||
return adapters;
|
||||
}
|
||||
|
||||
public static List<RouteStatistics> calculateRouteStatistics(OsmandApplication app,
|
||||
List<RouteSegmentResult> route,
|
||||
boolean nightMode) {
|
||||
|
@ -384,7 +371,7 @@ public class RouteDetailsFragment extends ContextMenuFragment implements PublicT
|
|||
protected void calculateLayout(View view, boolean initLayout) {
|
||||
super.calculateLayout(view, initLayout);
|
||||
if (!initLayout && getCurrentMenuState() != MenuState.FULL_SCREEN) {
|
||||
refreshChart(false);
|
||||
refreshMapCallback.refreshMap(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -401,48 +388,15 @@ public class RouteDetailsFragment extends ContextMenuFragment implements PublicT
|
|||
buildRowDivider(cardsContainer, false);
|
||||
}
|
||||
|
||||
private OnTouchListener getChartTouchListener() {
|
||||
return new OnTouchListener() {
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent ev) {
|
||||
if (ev.getSource() != 0 && v instanceof HorizontalBarChart) {
|
||||
if (statisticCard != null) {
|
||||
LineChart ch = statisticCard.getChart();
|
||||
if (ch != null) {
|
||||
final MotionEvent event = MotionEvent.obtainNoHistory(ev);
|
||||
event.setSource(0);
|
||||
ch.dispatchTouchEvent(event);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private void addRouteCard(final LinearLayout cardsContainer, RouteInfoCard routeInfoCard) {
|
||||
private void addRouteCard(LinearLayout cardsContainer,
|
||||
RouteInfoCard routeInfoCard) {
|
||||
OsmandApplication app = requireMyApplication();
|
||||
menuCards.add(routeInfoCard);
|
||||
routeInfoCard.setListener(this);
|
||||
cardsContainer.addView(routeInfoCard.build(app));
|
||||
buildRowDivider(cardsContainer, false);
|
||||
|
||||
routeInfoCards.add(routeInfoCard);
|
||||
HorizontalBarChart chart = routeInfoCard.getChart();
|
||||
if (chart != null) {
|
||||
LineChart mainChart = statisticCard.getChart();
|
||||
if (mainChart != null) {
|
||||
chart.getAxisRight().setAxisMinimum(mainChart.getXChartMin());
|
||||
chart.getAxisRight().setAxisMaximum(mainChart.getXChartMax());
|
||||
}
|
||||
chart.setRenderer(new CustomBarChartRenderer(chart, chart.getAnimator(), chart.getViewPortHandler(), AndroidUtils.dpToPx(app, 1f) / 2f));
|
||||
chart.setHighlightPerDragEnabled(false);
|
||||
chart.setHighlightPerTapEnabled(false);
|
||||
chart.setOnTouchListener(getChartTouchListener());
|
||||
}
|
||||
}
|
||||
|
||||
public Drawable getCollapseIcon(boolean collapsed) {
|
||||
|
@ -1487,14 +1441,7 @@ public class RouteDetailsFragment extends ContextMenuFragment implements PublicT
|
|||
private void makeGpx() {
|
||||
OsmandApplication app = requireMyApplication();
|
||||
gpx = GpxUiHelper.makeGpxFromRoute(app.getRoutingHelper().getRoute(), app);
|
||||
String groupName = getString(R.string.current_route);
|
||||
GpxDisplayGroup group = app.getSelectedGpxHelper().buildGpxDisplayGroup(gpx, 0, groupName);
|
||||
if (group != null && group.getModifiableList().size() > 0) {
|
||||
gpxItem = group.getModifiableList().get(0);
|
||||
if (gpxItem != null) {
|
||||
gpxItem.route = true;
|
||||
}
|
||||
}
|
||||
gpxItem = GpxUiHelper.makeGpxDisplayItem(app, gpx);
|
||||
}
|
||||
|
||||
void openDetails() {
|
||||
|
@ -1615,59 +1562,6 @@ public class RouteDetailsFragment extends ContextMenuFragment implements PublicT
|
|||
}
|
||||
}
|
||||
|
||||
private void refreshChart(boolean forceFit) {
|
||||
MapActivity mapActivity = getMapActivity();
|
||||
if (mapActivity != null && routeDetailsMenu != null && statisticCard != null &&
|
||||
!mapActivity.getMyApplication().getRoutingHelper().isFollowingMode()) {
|
||||
LineChart chart = statisticCard.getChart();
|
||||
if (chart != null) {
|
||||
routeDetailsMenu.refreshChart(chart, forceFit);
|
||||
mapActivity.refreshMap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void highlightRouteInfoCharts(@Nullable Highlight h) {
|
||||
for (RouteInfoCard rc : routeInfoCards) {
|
||||
HorizontalBarChart chart = rc.getChart();
|
||||
if (chart != null) {
|
||||
Highlight bh = h != null ? chart.getHighlighter().getHighlight(1, h.getXPx()) : null;
|
||||
if (bh != null) {
|
||||
bh.setDraw(h.getXPx(), 0);
|
||||
}
|
||||
chart.highlightValue(bh, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onValueSelected(BaseCard card, Entry e, Highlight h) {
|
||||
refreshChart(false);
|
||||
highlightRouteInfoCharts(h);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(BaseCard card) {
|
||||
highlightRouteInfoCharts(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChartGestureStart(BaseCard card, MotionEvent me, ChartGesture lastPerformedGesture) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChartGestureEnd(BaseCard card, MotionEvent me, ChartGesture lastPerformedGesture, boolean hasTranslated) {
|
||||
if ((lastPerformedGesture == ChartGesture.DRAG && hasTranslated) ||
|
||||
lastPerformedGesture == ChartGesture.X_ZOOM ||
|
||||
lastPerformedGesture == ChartGesture.Y_ZOOM ||
|
||||
lastPerformedGesture == ChartGesture.PINCH_ZOOM ||
|
||||
lastPerformedGesture == ChartGesture.DOUBLE_TAP ||
|
||||
lastPerformedGesture == ChartGesture.ROTATE) {
|
||||
|
||||
refreshChart(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static class CumulativeInfo {
|
||||
public int distance;
|
||||
public int time;
|
||||
|
@ -1696,20 +1590,4 @@ public class RouteDetailsFragment extends ContextMenuFragment implements PublicT
|
|||
final int timeInSeconds = model.getExpectedTime();
|
||||
return Algorithms.formatDuration(timeInSeconds, app.accessibilityEnabled());
|
||||
}
|
||||
|
||||
private static class CustomBarChartRenderer extends HorizontalBarChartRenderer {
|
||||
|
||||
private float highlightHalfWidth;
|
||||
|
||||
CustomBarChartRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler, float highlightHalfWidth) {
|
||||
super(chart, animator, viewPortHandler);
|
||||
this.highlightHalfWidth = highlightHalfWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setHighlightDrawPos(Highlight high, RectF bar) {
|
||||
bar.left = high.getDrawX() - highlightHalfWidth;
|
||||
bar.right = high.getDrawX() + highlightHalfWidth;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,7 +31,6 @@ public abstract class BaseCard {
|
|||
protected boolean nightMode;
|
||||
|
||||
private CardListener listener;
|
||||
private CardChartListener chartListener;
|
||||
|
||||
public interface CardListener {
|
||||
void onCardLayoutNeeded(@NonNull BaseCard card);
|
||||
|
@ -78,14 +77,6 @@ public abstract class BaseCard {
|
|||
this.listener = listener;
|
||||
}
|
||||
|
||||
public CardChartListener getChartListener() {
|
||||
return chartListener;
|
||||
}
|
||||
|
||||
public void setChartListener(CardChartListener chartListener) {
|
||||
this.chartListener = chartListener;
|
||||
}
|
||||
|
||||
public void setLayoutNeeded() {
|
||||
CardListener listener = this.listener;
|
||||
if (listener != null) {
|
||||
|
|
|
@ -1,54 +1,33 @@
|
|||
package net.osmand.plus.routepreparationmenu.cards;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.view.ContextThemeWrapper;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
|
||||
import com.github.mikephil.charting.charts.HorizontalBarChart;
|
||||
import com.github.mikephil.charting.data.BarData;
|
||||
import com.github.mikephil.charting.data.Entry;
|
||||
import com.github.mikephil.charting.highlight.Highlight;
|
||||
import com.github.mikephil.charting.listener.OnChartValueSelectedListener;
|
||||
|
||||
import net.osmand.AndroidUtils;
|
||||
import net.osmand.GPXUtilities.GPXTrackAnalysis;
|
||||
import net.osmand.plus.OsmAndFormatter;
|
||||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.activities.MapActivity;
|
||||
import net.osmand.plus.helpers.GpxUiHelper;
|
||||
import net.osmand.router.RouteStatisticsHelper.RouteSegmentAttribute;
|
||||
import net.osmand.plus.measurementtool.graph.CustomGraphAdapter;
|
||||
import net.osmand.plus.measurementtool.graph.CustomGraphAdapter.LegendViewType;
|
||||
import net.osmand.router.RouteStatisticsHelper.RouteStatistics;
|
||||
import net.osmand.util.Algorithms;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static net.osmand.plus.track.ColorsCard.MINIMUM_CONTRAST_RATIO;
|
||||
|
||||
public class RouteInfoCard extends BaseCard {
|
||||
|
||||
private RouteStatistics routeStatistics;
|
||||
private RouteStatistics statistics;
|
||||
private GPXTrackAnalysis analysis;
|
||||
private String selectedPropertyName;
|
||||
private CustomGraphAdapter graphAdapter;
|
||||
|
||||
private boolean showLegend;
|
||||
|
||||
public RouteInfoCard(MapActivity mapActivity, RouteStatistics routeStatistics, GPXTrackAnalysis analysis) {
|
||||
public RouteInfoCard(MapActivity mapActivity, RouteStatistics statistics, GPXTrackAnalysis analysis) {
|
||||
super(mapActivity);
|
||||
this.routeStatistics = routeStatistics;
|
||||
this.statistics = statistics;
|
||||
this.analysis = analysis;
|
||||
}
|
||||
|
||||
|
@ -59,112 +38,47 @@ public class RouteInfoCard extends BaseCard {
|
|||
|
||||
@Override
|
||||
protected void updateContent() {
|
||||
updateContent(routeStatistics);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public HorizontalBarChart getChart() {
|
||||
return (HorizontalBarChart) view.findViewById(R.id.chart);
|
||||
}
|
||||
|
||||
private void updateContent(final RouteStatistics routeStatistics) {
|
||||
updateHeader();
|
||||
final HorizontalBarChart chart = (HorizontalBarChart) view.findViewById(R.id.chart);
|
||||
GpxUiHelper.setupHorizontalGPXChart(app, chart, 5, 9, 24, true, nightMode);
|
||||
chart.setExtraRightOffset(16);
|
||||
chart.setExtraLeftOffset(16);
|
||||
BarData barData = GpxUiHelper.buildStatisticChart(app, chart, routeStatistics, analysis, true, nightMode);
|
||||
chart.setData(barData);
|
||||
chart.setOnChartValueSelectedListener(new OnChartValueSelectedListener() {
|
||||
@Override
|
||||
public void onValueSelected(Entry e, Highlight h) {
|
||||
List<RouteSegmentAttribute> elems = routeStatistics.elements;
|
||||
int i = h.getStackIndex();
|
||||
if (i >= 0 && elems.size() > i) {
|
||||
selectedPropertyName = elems.get(i).getPropertyName();
|
||||
if (showLegend) {
|
||||
updateLegend(routeStatistics);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected() {
|
||||
selectedPropertyName = null;
|
||||
if (showLegend) {
|
||||
updateLegend(routeStatistics);
|
||||
}
|
||||
}
|
||||
});
|
||||
LinearLayout container = (LinearLayout) view.findViewById(R.id.route_items);
|
||||
container.removeAllViews();
|
||||
if (showLegend) {
|
||||
attachLegend(container, routeStatistics);
|
||||
}
|
||||
final ImageView iconViewCollapse = (ImageView) view.findViewById(R.id.up_down_icon);
|
||||
iconViewCollapse.setImageDrawable(getCollapseIcon(!showLegend));
|
||||
HorizontalBarChart chart = (HorizontalBarChart) view.findViewById(R.id.chart);
|
||||
GpxUiHelper.setupHorizontalGPXChart(getMyApplication(), chart, 5, 9, 24, true, nightMode);
|
||||
BarData barData = GpxUiHelper.buildStatisticChart(app, chart, statistics, analysis, true, nightMode);
|
||||
graphAdapter = new CustomGraphAdapter(chart, true);
|
||||
graphAdapter.setLegendContainer(container);
|
||||
graphAdapter.updateData(barData, statistics);
|
||||
updateView();
|
||||
|
||||
view.findViewById(R.id.info_type_details_button).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
showLegend = !showLegend;
|
||||
updateContent();
|
||||
updateView();
|
||||
setLayoutNeeded();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void updateLegend(RouteStatistics routeStatistics) {
|
||||
LinearLayout container = (LinearLayout) view.findViewById(R.id.route_items);
|
||||
container.removeAllViews();
|
||||
attachLegend(container, routeStatistics);
|
||||
setLayoutNeeded();
|
||||
}
|
||||
|
||||
private Drawable getCollapseIcon(boolean collapsed) {
|
||||
return collapsed ? getContentIcon(R.drawable.ic_action_arrow_down) : getActiveIcon(R.drawable.ic_action_arrow_up);
|
||||
private void updateView() {
|
||||
updateCollapseIcon();
|
||||
graphAdapter.setLegendViewType(showLegend ? LegendViewType.ALL_AS_LIST : LegendViewType.GONE);
|
||||
graphAdapter.updateView();
|
||||
}
|
||||
|
||||
private void updateHeader() {
|
||||
TextView title = (TextView) view.findViewById(R.id.info_type_title);
|
||||
String name = AndroidUtils.getStringRouteInfoPropertyValue(app, routeStatistics.name);
|
||||
String name = AndroidUtils.getStringRouteInfoPropertyValue(app, statistics.name);
|
||||
title.setText(name);
|
||||
}
|
||||
|
||||
private void attachLegend(ViewGroup container, RouteStatistics routeStatistics) {
|
||||
Map<String, RouteSegmentAttribute> partition = routeStatistics.partition;
|
||||
List<Map.Entry<String, RouteSegmentAttribute>> list = new ArrayList<>(partition.entrySet());
|
||||
ContextThemeWrapper ctx = new ContextThemeWrapper(mapActivity, !nightMode ? R.style.OsmandLightTheme : R.style.OsmandDarkTheme);
|
||||
LayoutInflater inflater = LayoutInflater.from(ctx);
|
||||
for (Map.Entry<String, RouteSegmentAttribute> entry : list) {
|
||||
RouteSegmentAttribute segment = entry.getValue();
|
||||
View view = inflater.inflate(R.layout.route_details_legend, container, false);
|
||||
int segmentColor = segment.getColor();
|
||||
Drawable circle = app.getUIUtilities().getPaintedIcon(R.drawable.ic_action_circle, segmentColor);
|
||||
ImageView legendIcon = (ImageView) view.findViewById(R.id.legend_icon_color);
|
||||
legendIcon.setImageDrawable(circle);
|
||||
double contrastRatio = ColorUtils.calculateContrast(segmentColor, ContextCompat.getColor(app, nightMode ? R.color.card_and_list_background_dark : R.color.card_and_list_background_light));
|
||||
if (contrastRatio < MINIMUM_CONTRAST_RATIO) {
|
||||
legendIcon.setBackgroundResource(nightMode ? R.drawable.circle_contour_bg_dark : R.drawable.circle_contour_bg_light);
|
||||
}
|
||||
String propertyName = segment.getUserPropertyName();
|
||||
String name = AndroidUtils.getRenderingStringPropertyName(app, propertyName, propertyName.replaceAll("_", " "));
|
||||
Spannable text = getSpanLegend(name, segment, segment.getUserPropertyName().equals(selectedPropertyName));
|
||||
TextView legend = (TextView) view.findViewById(R.id.legend_text);
|
||||
legend.setText(text);
|
||||
|
||||
container.addView(view);
|
||||
}
|
||||
private void updateCollapseIcon() {
|
||||
ImageView ivCollapse = (ImageView) view.findViewById(R.id.up_down_icon);
|
||||
Drawable drawable = showLegend ?
|
||||
getContentIcon(R.drawable.ic_action_arrow_down) :
|
||||
getActiveIcon(R.drawable.ic_action_arrow_up);
|
||||
ivCollapse.setImageDrawable(drawable);
|
||||
}
|
||||
|
||||
private Spannable getSpanLegend(String title, RouteSegmentAttribute segment, boolean selected) {
|
||||
String formattedDistance = OsmAndFormatter.getFormattedDistance(segment.getDistance(), getMyApplication());
|
||||
title = Algorithms.capitalizeFirstLetter(title);
|
||||
SpannableStringBuilder spannable = new SpannableStringBuilder(title);
|
||||
spannable.append(": ");
|
||||
int startIndex = selected ? -0 : spannable.length();
|
||||
spannable.append(formattedDistance);
|
||||
spannable.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), startIndex, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
return spannable;
|
||||
public CustomGraphAdapter getGraphAdapter() {
|
||||
return graphAdapter;
|
||||
}
|
||||
}
|
|
@ -1,13 +1,10 @@
|
|||
package net.osmand.plus.routepreparationmenu.cards;
|
||||
|
||||
import android.graphics.Matrix;
|
||||
import android.os.Build;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.View.OnTouchListener;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
@ -16,18 +13,12 @@ import androidx.annotation.Nullable;
|
|||
import androidx.core.content.ContextCompat;
|
||||
|
||||
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.AndroidUtils;
|
||||
import net.osmand.GPXUtilities.GPXFile;
|
||||
import net.osmand.GPXUtilities.GPXTrackAnalysis;
|
||||
import net.osmand.plus.GpxSelectionHelper;
|
||||
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem;
|
||||
import net.osmand.plus.OsmAndFormatter;
|
||||
import net.osmand.plus.OsmandApplication;
|
||||
|
@ -36,6 +27,7 @@ import net.osmand.plus.activities.MapActivity;
|
|||
import net.osmand.plus.helpers.GpxUiHelper;
|
||||
import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetAxisType;
|
||||
import net.osmand.plus.helpers.GpxUiHelper.OrderedLineDataSet;
|
||||
import net.osmand.plus.measurementtool.graph.CommonGraphAdapter;
|
||||
import net.osmand.plus.routing.RoutingHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -52,16 +44,15 @@ public class RouteStatisticCard extends BaseCard {
|
|||
private OrderedLineDataSet slopeDataSet;
|
||||
@Nullable
|
||||
private OrderedLineDataSet elevationDataSet;
|
||||
private OnTouchListener onTouchListener;
|
||||
private OnClickListener onAnalyseClickListener;
|
||||
private CommonGraphAdapter graphAdapter;
|
||||
|
||||
public RouteStatisticCard(MapActivity mapActivity, GPXFile gpx, OnTouchListener onTouchListener,
|
||||
public RouteStatisticCard(MapActivity mapActivity, GPXFile gpx,
|
||||
OnClickListener onAnalyseClickListener) {
|
||||
super(mapActivity);
|
||||
this.gpx = gpx;
|
||||
this.onTouchListener = onTouchListener;
|
||||
this.onAnalyseClickListener = onAnalyseClickListener;
|
||||
makeGpxDisplayItem();
|
||||
this.gpxItem = GpxUiHelper.makeGpxDisplayItem(app, gpx);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
@ -219,26 +210,15 @@ public class RouteStatisticCard extends BaseCard {
|
|||
return elevationDataSet;
|
||||
}
|
||||
|
||||
private void makeGpxDisplayItem() {
|
||||
String groupName = getMyApplication().getString(R.string.current_route);
|
||||
GpxSelectionHelper.GpxDisplayGroup group = getMyApplication().getSelectedGpxHelper().buildGpxDisplayGroup(gpx, 0, groupName);
|
||||
if (group != null && group.getModifiableList().size() > 0) {
|
||||
gpxItem = group.getModifiableList().get(0);
|
||||
if (gpxItem != null) {
|
||||
gpxItem.route = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public LineChart getChart() {
|
||||
return (LineChart) view.findViewById(R.id.chart);
|
||||
public CommonGraphAdapter getGraphAdapter() {
|
||||
return graphAdapter;
|
||||
}
|
||||
|
||||
private void buildHeader(GPXTrackAnalysis analysis) {
|
||||
final LineChart mChart = (LineChart) view.findViewById(R.id.chart);
|
||||
LineChart mChart = (LineChart) view.findViewById(R.id.chart);
|
||||
GpxUiHelper.setupGPXChart(mChart, 4, 24f, 16f, !nightMode, true);
|
||||
mChart.setOnTouchListener(onTouchListener);
|
||||
graphAdapter = new CommonGraphAdapter(mChart, true);
|
||||
|
||||
if (analysis.hasElevationData) {
|
||||
List<ILineDataSet> dataSets = new ArrayList<>();
|
||||
|
@ -256,99 +236,7 @@ public class RouteStatisticCard extends BaseCard {
|
|||
this.elevationDataSet = elevationDataSet;
|
||||
this.slopeDataSet = slopeDataSet;
|
||||
|
||||
LineData data = new LineData(dataSets);
|
||||
mChart.setData(data);
|
||||
|
||||
mChart.setOnChartValueSelectedListener(new OnChartValueSelectedListener() {
|
||||
@Override
|
||||
public void onValueSelected(Entry e, Highlight h) {
|
||||
CardChartListener chartListener = getChartListener();
|
||||
if (chartListener != null) {
|
||||
chartListener.onValueSelected(RouteStatisticCard.this, e, h);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected() {
|
||||
CardChartListener chartListener = getChartListener();
|
||||
if (chartListener != null) {
|
||||
chartListener.onNothingSelected(RouteStatisticCard.this);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mChart.setOnChartGestureListener(new OnChartGestureListener() {
|
||||
boolean hasTranslated = false;
|
||||
float highlightDrawX = -1;
|
||||
|
||||
@Override
|
||||
public void onChartGestureStart(MotionEvent me, ChartGesture lastPerformedGesture) {
|
||||
hasTranslated = false;
|
||||
if (mChart.getHighlighted() != null && mChart.getHighlighted().length > 0) {
|
||||
highlightDrawX = mChart.getHighlighted()[0].getDrawX();
|
||||
} else {
|
||||
highlightDrawX = -1;
|
||||
}
|
||||
CardChartListener chartListener = getChartListener();
|
||||
if (chartListener != null) {
|
||||
chartListener.onChartGestureStart(RouteStatisticCard.this, me, lastPerformedGesture);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChartGestureEnd(MotionEvent me, ChartGesture lastPerformedGesture) {
|
||||
gpxItem.chartMatrix = new Matrix(mChart.getViewPortHandler().getMatrixTouch());
|
||||
Highlight[] highlights = mChart.getHighlighted();
|
||||
if (highlights != null && highlights.length > 0) {
|
||||
gpxItem.chartHighlightPos = highlights[0].getX();
|
||||
} else {
|
||||
gpxItem.chartHighlightPos = -1;
|
||||
}
|
||||
CardChartListener chartListener = getChartListener();
|
||||
if (chartListener != null) {
|
||||
chartListener.onChartGestureEnd(RouteStatisticCard.this, me, lastPerformedGesture, hasTranslated);
|
||||
}
|
||||
}
|
||||
|
||||
@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) {
|
||||
hasTranslated = true;
|
||||
if (highlightDrawX != -1) {
|
||||
Highlight h = mChart.getHighlightByTouchPoint(highlightDrawX, 0f);
|
||||
if (h != null) {
|
||||
/*
|
||||
ILineDataSet set = mChart.getLineData().getDataSetByIndex(h.getDataSetIndex());
|
||||
if (set != null && set.isHighlightEnabled()) {
|
||||
Entry e = set.getEntryForXValue(h.getX(), h.getY());
|
||||
MPPointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelForValues(e.getX(), e.getY());
|
||||
h.setDraw((float) pix.x, (float) pix.y);
|
||||
}
|
||||
*/
|
||||
mChart.highlightValue(h, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
graphAdapter.updateContent(new LineData(dataSets), gpxItem);
|
||||
mChart.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mChart.setVisibility(View.GONE);
|
||||
|
|
|
@ -1104,9 +1104,8 @@ public class OsmandSettings {
|
|||
public final OsmandPreference<Boolean> BILLING_PURCHASE_TOKEN_SENT = new BooleanPreference(this, "billing_purchase_token_sent", false).makeGlobal();
|
||||
public final OsmandPreference<String> BILLING_PURCHASE_TOKENS_SENT = new StringPreference(this, "billing_purchase_tokens_sent", "").makeGlobal();
|
||||
public final OsmandPreference<Boolean> LIVE_UPDATES_PURCHASED = new BooleanPreference(this, "billing_live_updates_purchased", false).makeGlobal();
|
||||
public final OsmandPreference<Long> LIVE_UPDATES_PURCHASE_CANCELLED_TIME = new LongPreference(this, "live_updates_purchase_cancelled_time", 0).makeGlobal();
|
||||
public final OsmandPreference<Boolean> LIVE_UPDATES_PURCHASE_CANCELLED_FIRST_DLG_SHOWN = new BooleanPreference(this, "live_updates_purchase_cancelled_first_dlg_shown", false).makeGlobal();
|
||||
public final OsmandPreference<Boolean> LIVE_UPDATES_PURCHASE_CANCELLED_SECOND_DLG_SHOWN = new BooleanPreference(this, "live_updates_purchase_cancelled_second_dlg_shown", false).makeGlobal();
|
||||
public final OsmandPreference<Long> LIVE_UPDATES_EXPIRED_FIRST_DLG_SHOWN_TIME = new LongPreference(this, "live_updates_expired_first_dlg_shown_time", 0).makeGlobal();
|
||||
public final OsmandPreference<Long> LIVE_UPDATES_EXPIRED_SECOND_DLG_SHOWN_TIME = new LongPreference(this, "live_updates_expired_second_dlg_shown_time", 0).makeGlobal();
|
||||
public final OsmandPreference<Boolean> FULL_VERSION_PURCHASED = new BooleanPreference(this, "billing_full_version_purchased", false).makeGlobal();
|
||||
public final OsmandPreference<Boolean> DEPTH_CONTOURS_PURCHASED = new BooleanPreference(this, "billing_sea_depth_purchased", false).makeGlobal();
|
||||
public final OsmandPreference<Boolean> CONTOUR_LINES_PURCHASED = new BooleanPreference(this, "billing_srtm_purchased", false).makeGlobal();
|
||||
|
|
|
@ -64,7 +64,7 @@ public class DataSettingsItem extends StreamSettingsItem {
|
|||
SettingsItemReader<? extends SettingsItem> getReader() {
|
||||
return new StreamSettingsItemReader(this) {
|
||||
@Override
|
||||
public void readFromStream(@NonNull InputStream inputStream, File destination) throws IOException, IllegalArgumentException {
|
||||
public void readFromStream(@NonNull InputStream inputStream, String entryName) throws IOException, IllegalArgumentException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
int nRead;
|
||||
byte[] data = new byte[SettingsHelper.BUFFER];
|
||||
|
|
|
@ -16,7 +16,6 @@ import net.osmand.plus.R;
|
|||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
@ -146,7 +145,7 @@ public class FavoritesSettingsItem extends CollectionSettingsItem<FavoriteGroup>
|
|||
return new SettingsItemReader<FavoritesSettingsItem>(this) {
|
||||
|
||||
@Override
|
||||
public void readFromStream(@NonNull InputStream inputStream, File destination) throws IllegalArgumentException {
|
||||
public void readFromStream(@NonNull InputStream inputStream, String entryName) throws IllegalArgumentException {
|
||||
GPXFile gpxFile = GPXUtilities.loadGPXFile(inputStream);
|
||||
if (gpxFile.error != null) {
|
||||
warnings.add(app.getString(R.string.settings_item_read_error, String.valueOf(getType())));
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package net.osmand.plus.settings.backend.backup;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
|
@ -23,34 +24,33 @@ import java.util.zip.ZipOutputStream;
|
|||
public class FileSettingsItem extends StreamSettingsItem {
|
||||
|
||||
public enum FileSubtype {
|
||||
UNKNOWN("", null),
|
||||
OTHER("other", ""),
|
||||
ROUTING_CONFIG("routing_config", IndexConstants.ROUTING_PROFILES_DIR),
|
||||
RENDERING_STYLE("rendering_style", IndexConstants.RENDERERS_DIR),
|
||||
WIKI_MAP("wiki_map", IndexConstants.WIKI_INDEX_DIR),
|
||||
SRTM_MAP("srtm_map", IndexConstants.SRTM_INDEX_DIR),
|
||||
OBF_MAP("obf_map", IndexConstants.MAPS_PATH),
|
||||
TILES_MAP("tiles_map", IndexConstants.TILES_INDEX_DIR),
|
||||
GPX("gpx", IndexConstants.GPX_INDEX_DIR),
|
||||
TTS_VOICE("tts_voice", IndexConstants.VOICE_INDEX_DIR),
|
||||
VOICE("voice", IndexConstants.VOICE_INDEX_DIR),
|
||||
TRAVEL("travel", IndexConstants.WIKIVOYAGE_INDEX_DIR),
|
||||
MULTIMEDIA_NOTES("multimedia_notes", IndexConstants.AV_INDEX_DIR);
|
||||
UNKNOWN("", null, R.drawable.ic_type_file),
|
||||
OTHER("other", "", R.drawable.ic_type_file),
|
||||
ROUTING_CONFIG("routing_config", IndexConstants.ROUTING_PROFILES_DIR, R.drawable.ic_action_route_distance),
|
||||
RENDERING_STYLE("rendering_style", IndexConstants.RENDERERS_DIR, R.drawable.ic_action_map_style),
|
||||
WIKI_MAP("wiki_map", IndexConstants.WIKI_INDEX_DIR, R.drawable.ic_plugin_wikipedia),
|
||||
SRTM_MAP("srtm_map", IndexConstants.SRTM_INDEX_DIR, R.drawable.ic_plugin_srtm),
|
||||
OBF_MAP("obf_map", IndexConstants.MAPS_PATH, R.drawable.ic_map),
|
||||
TILES_MAP("tiles_map", IndexConstants.TILES_INDEX_DIR, R.drawable.ic_map),
|
||||
ROAD_MAP("road_map", IndexConstants.ROADS_INDEX_DIR, R.drawable.ic_map),
|
||||
GPX("gpx", IndexConstants.GPX_INDEX_DIR, R.drawable.ic_action_route_distance),
|
||||
TTS_VOICE("tts_voice", IndexConstants.VOICE_INDEX_DIR, R.drawable.ic_action_volume_up),
|
||||
VOICE("voice", IndexConstants.VOICE_INDEX_DIR, R.drawable.ic_action_volume_up),
|
||||
TRAVEL("travel", IndexConstants.WIKIVOYAGE_INDEX_DIR, R.drawable.ic_plugin_wikipedia),
|
||||
MULTIMEDIA_NOTES("multimedia_notes", IndexConstants.AV_INDEX_DIR, R.drawable.ic_action_photo_dark);
|
||||
|
||||
private String subtypeName;
|
||||
private String subtypeFolder;
|
||||
private final String subtypeName;
|
||||
private final String subtypeFolder;
|
||||
private final int iconId;
|
||||
|
||||
FileSubtype(String subtypeName, String subtypeFolder) {
|
||||
FileSubtype(@NonNull String subtypeName, String subtypeFolder, @DrawableRes int iconId) {
|
||||
this.subtypeName = subtypeName;
|
||||
this.subtypeFolder = subtypeFolder;
|
||||
this.iconId = iconId;
|
||||
}
|
||||
|
||||
public boolean isMap() {
|
||||
return this == OBF_MAP || this == WIKI_MAP || this == SRTM_MAP || this == TILES_MAP;
|
||||
}
|
||||
|
||||
public boolean isDirectory() {
|
||||
return this == TTS_VOICE || this == VOICE;
|
||||
return this == OBF_MAP || this == WIKI_MAP || this == SRTM_MAP || this == TILES_MAP || this == ROAD_MAP;
|
||||
}
|
||||
|
||||
public String getSubtypeName() {
|
||||
|
@ -61,6 +61,11 @@ public class FileSettingsItem extends StreamSettingsItem {
|
|||
return subtypeFolder;
|
||||
}
|
||||
|
||||
@DrawableRes
|
||||
public int getIconId() {
|
||||
return iconId;
|
||||
}
|
||||
|
||||
public static FileSubtype getSubtypeByName(@NonNull String name) {
|
||||
for (FileSubtype subtype : FileSubtype.values()) {
|
||||
if (name.equals(subtype.subtypeName)) {
|
||||
|
@ -205,7 +210,12 @@ public class FileSettingsItem extends StreamSettingsItem {
|
|||
}
|
||||
|
||||
public long getSize() {
|
||||
return size;
|
||||
if (size != 0) {
|
||||
return size;
|
||||
} else if (file != null && !file.isDirectory()) {
|
||||
return file.length();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void setSize(long size) {
|
||||
|
@ -227,15 +237,28 @@ public class FileSettingsItem extends StreamSettingsItem {
|
|||
return file.exists();
|
||||
}
|
||||
|
||||
private File renameFile(File file) {
|
||||
private File renameFile(File oldFile) {
|
||||
String oldPath = oldFile.getAbsolutePath();
|
||||
String prefix;
|
||||
if (file.isDirectory()) {
|
||||
prefix = file.getAbsolutePath();
|
||||
} else if (oldPath.endsWith(IndexConstants.BINARY_WIKI_MAP_INDEX_EXT)) {
|
||||
prefix = oldPath.substring(0, oldPath.lastIndexOf(IndexConstants.BINARY_WIKI_MAP_INDEX_EXT));
|
||||
} else if (oldPath.endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT)) {
|
||||
prefix = oldPath.substring(0, oldPath.lastIndexOf(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT));
|
||||
} else if (oldPath.endsWith(IndexConstants.BINARY_ROAD_MAP_INDEX_EXT)) {
|
||||
prefix = oldPath.substring(0, oldPath.lastIndexOf(IndexConstants.BINARY_ROAD_MAP_INDEX_EXT));
|
||||
} else {
|
||||
prefix = oldPath.substring(0, oldPath.lastIndexOf("."));
|
||||
}
|
||||
String suffix = oldPath.replace(prefix, "");
|
||||
int number = 0;
|
||||
String path = file.getAbsolutePath();
|
||||
while (true) {
|
||||
number++;
|
||||
String copyName = path.replaceAll(file.getName(), file.getName().replaceFirst("[.]", "_" + number + "."));
|
||||
File copyFile = new File(copyName);
|
||||
if (!copyFile.exists()) {
|
||||
return copyFile;
|
||||
String newName = prefix + "_" + number + suffix;
|
||||
File newFile = new File(newName);
|
||||
if (!newFile.exists()) {
|
||||
return newFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -245,12 +268,17 @@ public class FileSettingsItem extends StreamSettingsItem {
|
|||
SettingsItemReader<? extends SettingsItem> getReader() {
|
||||
return new StreamSettingsItemReader(this) {
|
||||
@Override
|
||||
public void readFromStream(@NonNull InputStream inputStream, File dest) throws IOException, IllegalArgumentException {
|
||||
public void readFromStream(@NonNull InputStream inputStream, String entryName) throws IOException, IllegalArgumentException {
|
||||
OutputStream output;
|
||||
File dest = FileSettingsItem.this.getFile();
|
||||
if (dest.isDirectory()) {
|
||||
dest = new File(dest, entryName.substring(fileName.length()));
|
||||
}
|
||||
if (dest.exists() && !shouldReplace) {
|
||||
dest = renameFile(dest);
|
||||
}
|
||||
if (dest.getParentFile() != null && !dest.getParentFile().exists()) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
dest.getParentFile().mkdirs();
|
||||
}
|
||||
output = new FileOutputStream(dest);
|
||||
|
@ -271,46 +299,42 @@ public class FileSettingsItem extends StreamSettingsItem {
|
|||
@Nullable
|
||||
@Override
|
||||
public SettingsItemWriter<? extends SettingsItem> getWriter() {
|
||||
try {
|
||||
if (!file.isDirectory()) {
|
||||
if (!file.isDirectory()) {
|
||||
try {
|
||||
setInputStream(new FileInputStream(file));
|
||||
} catch (FileNotFoundException e) {
|
||||
warnings.add(app.getString(R.string.settings_item_read_error, file.getName()));
|
||||
SettingsHelper.LOG.error("Failed to set input stream from file: " + file.getName(), e);
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
warnings.add(app.getString(R.string.settings_item_read_error, file.getName()));
|
||||
SettingsHelper.LOG.error("Failed to set input stream from file: " + file.getName(), e);
|
||||
}
|
||||
return new StreamSettingsItemWriter(this) {
|
||||
return super.getWriter();
|
||||
} else {
|
||||
return new StreamSettingsItemWriter(this) {
|
||||
|
||||
@Override
|
||||
public void writeEntry(String fileName, @NonNull ZipOutputStream zos) throws IOException {
|
||||
if (getSubtype().isDirectory()) {
|
||||
File file = getFile();
|
||||
zipDirsWithFiles(file, zos);
|
||||
} else {
|
||||
super.writeEntry(fileName, zos);
|
||||
@Override
|
||||
public void writeEntry(String fileName, @NonNull ZipOutputStream zos) throws IOException {
|
||||
writeDirWithFiles(file, zos);
|
||||
}
|
||||
}
|
||||
|
||||
public void zipDirsWithFiles(File f, ZipOutputStream zos)
|
||||
throws IOException {
|
||||
if (f == null) {
|
||||
return;
|
||||
}
|
||||
if (f.isDirectory()) {
|
||||
File[] fs = f.listFiles();
|
||||
if (fs != null) {
|
||||
for (File c : fs) {
|
||||
zipDirsWithFiles(c, zos);
|
||||
public void writeDirWithFiles(File file, ZipOutputStream zos) throws IOException {
|
||||
if (file != null) {
|
||||
if (file.isDirectory()) {
|
||||
File[] files = file.listFiles();
|
||||
if (files != null) {
|
||||
for (File subfolderFile : files) {
|
||||
writeDirWithFiles(subfolderFile, zos);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String subtypeFolder = getSubtype().getSubtypeFolder();
|
||||
String zipEntryName = Algorithms.isEmpty(subtypeFolder)
|
||||
? file.getName()
|
||||
: file.getPath().substring(file.getPath().indexOf(subtypeFolder) - 1);
|
||||
setInputStream(new FileInputStream(file));
|
||||
super.writeEntry(zipEntryName, zos);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String zipEntryName = Algorithms.isEmpty(getSubtype().getSubtypeFolder())
|
||||
? f.getName()
|
||||
: f.getPath().substring(f.getPath().indexOf(getSubtype().getSubtypeFolder()) - 1);
|
||||
setInputStream(new FileInputStream(f));
|
||||
super.writeEntry(zipEntryName, zos);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import org.json.JSONException;
|
|||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
@ -30,7 +29,7 @@ public abstract class OsmandSettingsItemReader<T extends OsmandSettingsItem> ext
|
|||
@NonNull JSONObject json) throws JSONException;
|
||||
|
||||
@Override
|
||||
public void readFromStream(@NonNull InputStream inputStream, File destination) throws IOException, IllegalArgumentException {
|
||||
public void readFromStream(@NonNull InputStream inputStream, String entryName) throws IOException, IllegalArgumentException {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
try {
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
|
||||
|
|
|
@ -16,13 +16,19 @@ import java.util.Map;
|
|||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import static net.osmand.plus.settings.backend.backup.SettingsHelper.*;
|
||||
|
||||
class SettingsExporter {
|
||||
|
||||
private Map<String, SettingsItem> items;
|
||||
private Map<String, String> additionalParams;
|
||||
private ExportProgressListener progressListener;
|
||||
|
||||
private boolean cancelled;
|
||||
private boolean exportItemsFiles;
|
||||
|
||||
SettingsExporter(boolean exportItemsFiles) {
|
||||
SettingsExporter(ExportProgressListener progressListener, boolean exportItemsFiles) {
|
||||
this.progressListener = progressListener;
|
||||
this.exportItemsFiles = exportItemsFiles;
|
||||
items = new LinkedHashMap<>();
|
||||
additionalParams = new LinkedHashMap<>();
|
||||
|
@ -35,13 +41,17 @@ class SettingsExporter {
|
|||
items.put(item.getName(), item);
|
||||
}
|
||||
|
||||
public void setCancelled(boolean cancelled) {
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
|
||||
void addAdditionalParam(String key, String value) {
|
||||
additionalParams.put(key, value);
|
||||
}
|
||||
|
||||
void exportSettings(File file) throws JSONException, IOException {
|
||||
JSONObject json = createItemsJson();
|
||||
OutputStream os = new BufferedOutputStream(new FileOutputStream(file), SettingsHelper.BUFFER);
|
||||
OutputStream os = new BufferedOutputStream(new FileOutputStream(file), BUFFER);
|
||||
ZipOutputStream zos = new ZipOutputStream(os);
|
||||
try {
|
||||
ZipEntry entry = new ZipEntry("items.json");
|
||||
|
@ -60,6 +70,7 @@ class SettingsExporter {
|
|||
}
|
||||
|
||||
private void writeItemFiles(ZipOutputStream zos) throws IOException {
|
||||
int progress = 0;
|
||||
for (SettingsItem item : items.values()) {
|
||||
SettingsItemWriter<? extends SettingsItem> writer = item.getWriter();
|
||||
if (writer != null) {
|
||||
|
@ -69,13 +80,22 @@ class SettingsExporter {
|
|||
}
|
||||
writer.writeEntry(fileName, zos);
|
||||
}
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
if (item instanceof FileSettingsItem) {
|
||||
int size = (int) ((FileSettingsItem) item).getSize() / (1 << 20);
|
||||
progress += size;
|
||||
if (progressListener != null) {
|
||||
progressListener.updateProgress(progress);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private JSONObject createItemsJson() throws JSONException {
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("version", SettingsHelper.VERSION);
|
||||
json.put("version", VERSION);
|
||||
for (Map.Entry<String, String> param : additionalParams.entrySet()) {
|
||||
json.put(param.getKey(), param.getValue());
|
||||
}
|
||||
|
|
|
@ -7,7 +7,9 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.Nullable;
|
||||
|
||||
import net.osmand.AndroidUtils;
|
||||
import net.osmand.Collator;
|
||||
import net.osmand.IndexConstants;
|
||||
import net.osmand.OsmAndCollator;
|
||||
import net.osmand.PlatformUtil;
|
||||
import net.osmand.data.LatLon;
|
||||
import net.osmand.map.ITileSource;
|
||||
|
@ -24,6 +26,7 @@ import net.osmand.plus.audionotes.AudioVideoNotesPlugin;
|
|||
import net.osmand.plus.audionotes.AudioVideoNotesPlugin.Recording;
|
||||
import net.osmand.plus.download.ui.AbstractLoadLocalIndexTask;
|
||||
import net.osmand.plus.helpers.AvoidSpecificRoads.AvoidRoadInfo;
|
||||
import net.osmand.plus.helpers.FileNameTranslationHelper;
|
||||
import net.osmand.plus.helpers.GpxUiHelper;
|
||||
import net.osmand.plus.helpers.GpxUiHelper.GPXInfo;
|
||||
import net.osmand.plus.osmedit.OpenstreetmapPoint;
|
||||
|
@ -35,6 +38,7 @@ import net.osmand.plus.quickaction.QuickActionRegistry;
|
|||
import net.osmand.plus.settings.backend.ApplicationMode;
|
||||
import net.osmand.plus.settings.backend.ApplicationMode.ApplicationModeBean;
|
||||
import net.osmand.plus.settings.backend.ExportSettingsType;
|
||||
import net.osmand.util.Algorithms;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.json.JSONException;
|
||||
|
@ -43,14 +47,16 @@ import java.io.File;
|
|||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static net.osmand.IndexConstants.OSMAND_SETTINGS_FILE_EXT;
|
||||
import static net.osmand.plus.settings.backend.backup.FileSettingsItem.*;
|
||||
import static net.osmand.plus.activities.LocalIndexHelper.LocalIndexType;
|
||||
import static net.osmand.plus.settings.backend.backup.FileSettingsItem.FileSubtype;
|
||||
|
||||
/*
|
||||
Usage:
|
||||
|
@ -107,6 +113,8 @@ public class SettingsHelper {
|
|||
|
||||
public interface SettingsExportListener {
|
||||
void onSettingsExportFinished(@NonNull File file, boolean succeed);
|
||||
|
||||
void onSettingsExportProgressUpdate(int value);
|
||||
}
|
||||
|
||||
public enum ImportType {
|
||||
|
@ -135,6 +143,14 @@ public class SettingsHelper {
|
|||
return importTask == null || importTask.isImportDone();
|
||||
}
|
||||
|
||||
public boolean cancelExportForFile(File file) {
|
||||
ExportAsyncTask exportTask = exportAsyncTasks.get(file);
|
||||
if (exportTask != null && (exportTask.getStatus() == AsyncTask.Status.RUNNING)) {
|
||||
return exportTask.cancel(true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isFileExporting(File file) {
|
||||
return exportAsyncTasks.containsKey(file);
|
||||
}
|
||||
|
@ -196,11 +212,15 @@ public class SettingsHelper {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private class ExportAsyncTask extends AsyncTask<Void, Void, Boolean> {
|
||||
public interface ExportProgressListener {
|
||||
void updateProgress(int value);
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
public class ExportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
|
||||
private SettingsExporter exporter;
|
||||
private File file;
|
||||
private SettingsExporter exporter;
|
||||
private SettingsExportListener listener;
|
||||
|
||||
ExportAsyncTask(@NonNull File settingsFile,
|
||||
|
@ -208,7 +228,7 @@ public class SettingsHelper {
|
|||
@NonNull List<SettingsItem> items, boolean exportItemsFiles) {
|
||||
this.file = settingsFile;
|
||||
this.listener = listener;
|
||||
this.exporter = new SettingsExporter(exportItemsFiles);
|
||||
this.exporter = new SettingsExporter(getProgressListener(), exportItemsFiles);
|
||||
for (SettingsItem item : items) {
|
||||
exporter.addSettingsItem(item);
|
||||
}
|
||||
|
@ -227,6 +247,13 @@ public class SettingsHelper {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(Integer... values) {
|
||||
if (listener != null) {
|
||||
listener.onSettingsExportProgressUpdate(values[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean success) {
|
||||
exportAsyncTasks.remove(file);
|
||||
|
@ -234,6 +261,21 @@ public class SettingsHelper {
|
|||
listener.onSettingsExportFinished(file, success);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCancelled() {
|
||||
Algorithms.removeAllFiles(file);
|
||||
}
|
||||
|
||||
private ExportProgressListener getProgressListener() {
|
||||
return new ExportProgressListener() {
|
||||
@Override
|
||||
public void updateProgress(int value) {
|
||||
exporter.setCancelled(isCancelled());
|
||||
publishProgress(value);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
|
@ -538,11 +580,11 @@ public class SettingsHelper {
|
|||
if (!favoriteGroups.isEmpty()) {
|
||||
dataList.put(ExportSettingsType.FAVORITES, favoriteGroups);
|
||||
}
|
||||
List<LocalIndexInfo> localIndexInfoList = getVoiceIndexInfo();
|
||||
List<File> files;
|
||||
files = getFilesByType(localIndexInfoList, LocalIndexType.MAP_DATA, LocalIndexType.TILES_DATA,
|
||||
List<LocalIndexInfo> localIndexInfoList = getLocalIndexData();
|
||||
List<File> files = getFilesByType(localIndexInfoList, LocalIndexType.MAP_DATA, LocalIndexType.TILES_DATA,
|
||||
LocalIndexType.SRTM_DATA, LocalIndexType.WIKI_DATA);
|
||||
if (!files.isEmpty()) {
|
||||
sortLocalFiles(files);
|
||||
dataList.put(ExportSettingsType.OFFLINE_MAPS, files);
|
||||
}
|
||||
files = getFilesByType(localIndexInfoList, LocalIndexType.TTS_VOICE_DATA);
|
||||
|
@ -560,7 +602,7 @@ public class SettingsHelper {
|
|||
return dataList;
|
||||
}
|
||||
|
||||
private List<LocalIndexInfo> getVoiceIndexInfo() {
|
||||
private List<LocalIndexInfo> getLocalIndexData() {
|
||||
return new LocalIndexHelper(app).getLocalIndexData(new AbstractLoadLocalIndexTask() {
|
||||
@Override
|
||||
public void loadFile(LocalIndexInfo... loaded) {
|
||||
|
@ -820,4 +862,18 @@ public class SettingsHelper {
|
|||
}
|
||||
return settingsToOperate;
|
||||
}
|
||||
|
||||
private void sortLocalFiles(List<File> files) {
|
||||
final Collator collator = OsmAndCollator.primaryCollator();
|
||||
Collections.sort(files, new Comparator<File>() {
|
||||
@Override
|
||||
public int compare(File lhs, File rhs) {
|
||||
return collator.compare(getNameToDisplay(lhs), getNameToDisplay(rhs));
|
||||
}
|
||||
|
||||
private String getNameToDisplay(File item) {
|
||||
return FileNameTranslationHelper.getFileNameWithRegion(app, item.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -124,7 +124,7 @@ class SettingsImporter {
|
|||
try {
|
||||
SettingsItemReader<? extends SettingsItem> reader = item.getReader();
|
||||
if (reader != null) {
|
||||
reader.readFromStream(ois, app.getAppPath(fileName));
|
||||
reader.readFromStream(ois, fileName);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
item.warnings.add(app.getString(R.string.settings_item_read_error, item.getName()));
|
||||
|
|
|
@ -169,7 +169,7 @@ public abstract class SettingsItem {
|
|||
SettingsItemReader<? extends SettingsItem> getJsonReader() {
|
||||
return new SettingsItemReader<SettingsItem>(this) {
|
||||
@Override
|
||||
public void readFromStream(@NonNull InputStream inputStream, File destination) throws IOException, IllegalArgumentException {
|
||||
public void readFromStream(@NonNull InputStream inputStream, String entryName) throws IOException, IllegalArgumentException {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
try {
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
|
||||
|
|
|
@ -2,7 +2,6 @@ package net.osmand.plus.settings.backend.backup;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
|
@ -14,5 +13,5 @@ public abstract class SettingsItemReader<T extends SettingsItem> {
|
|||
this.item = item;
|
||||
}
|
||||
|
||||
public abstract void readFromStream(@NonNull InputStream inputStream, File destination) throws IOException, IllegalArgumentException;
|
||||
public abstract void readFromStream(@NonNull InputStream inputStream, String entryName) throws IOException, IllegalArgumentException;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,11 @@ import net.osmand.IndexConstants;
|
|||
import net.osmand.PlatformUtil;
|
||||
import net.osmand.map.ITileSource;
|
||||
import net.osmand.plus.FavouritesDbHelper.FavoriteGroup;
|
||||
import net.osmand.plus.audionotes.AudioVideoNotesPlugin;
|
||||
import net.osmand.plus.helpers.FileNameTranslationHelper;
|
||||
import net.osmand.plus.helpers.GpxUiHelper;
|
||||
import net.osmand.plus.settings.backend.ApplicationMode;
|
||||
import net.osmand.plus.settings.backend.ApplicationMode.ApplicationModeBean;
|
||||
import net.osmand.plus.mapmarkers.MapMarkersGroup;
|
||||
import net.osmand.plus.OsmandApplication;
|
||||
import net.osmand.plus.R;
|
||||
|
@ -35,6 +40,8 @@ import org.apache.commons.logging.Log;
|
|||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import static net.osmand.plus.settings.backend.backup.FileSettingsItem.*;
|
||||
|
||||
public class DuplicatesSettingsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
|
||||
private static final Log LOG = PlatformUtil.getLog(DuplicatesSettingsAdapter.class.getName());
|
||||
|
@ -76,12 +83,11 @@ public class DuplicatesSettingsAdapter extends RecyclerView.Adapter<RecyclerView
|
|||
if (holder instanceof HeaderViewHolder) {
|
||||
HeaderViewHolder headerHolder = (HeaderViewHolder) holder;
|
||||
headerHolder.title.setText((String) currentItem);
|
||||
headerHolder.subTitle.setText(String.format(
|
||||
app.getString(R.string.listed_exist),
|
||||
(String) currentItem));
|
||||
headerHolder.subTitle.setText(String.format(app.getString(R.string.listed_exist), currentItem));
|
||||
headerHolder.divider.setVisibility(View.VISIBLE);
|
||||
} else if (holder instanceof ItemViewHolder) {
|
||||
ItemViewHolder itemHolder = (ItemViewHolder) holder;
|
||||
itemHolder.subTitle.setVisibility(View.GONE);
|
||||
if (currentItem instanceof ApplicationModeBean) {
|
||||
ApplicationModeBean modeBean = (ApplicationModeBean) currentItem;
|
||||
String profileName = modeBean.userProfileName;
|
||||
|
@ -117,19 +123,17 @@ public class DuplicatesSettingsAdapter extends RecyclerView.Adapter<RecyclerView
|
|||
QuickAction action = (QuickAction) currentItem;
|
||||
itemHolder.title.setText(action.getName(app));
|
||||
itemHolder.icon.setImageDrawable(uiUtilities.getIcon(action.getIconRes(), activeColorRes));
|
||||
itemHolder.subTitle.setVisibility(View.GONE);
|
||||
} else if (currentItem instanceof PoiUIFilter) {
|
||||
PoiUIFilter filter = (PoiUIFilter) currentItem;
|
||||
itemHolder.title.setText(filter.getName());
|
||||
int iconRes = RenderingIcons.getBigIconResourceId(filter.getIconId());
|
||||
itemHolder.icon.setImageDrawable(uiUtilities.getIcon(iconRes != 0 ? iconRes : R.drawable.ic_action_user, activeColorRes));
|
||||
itemHolder.subTitle.setVisibility(View.GONE);
|
||||
} else if (currentItem instanceof ITileSource) {
|
||||
itemHolder.title.setText(((ITileSource) currentItem).getName());
|
||||
itemHolder.icon.setImageDrawable(uiUtilities.getIcon(R.drawable.ic_map, activeColorRes));
|
||||
itemHolder.subTitle.setVisibility(View.GONE);
|
||||
} else if (currentItem instanceof File) {
|
||||
File file = (File) currentItem;
|
||||
FileSubtype fileSubtype = FileSubtype.getSubtypeByPath(app, file.getPath());
|
||||
itemHolder.title.setText(file.getName());
|
||||
if (file.getAbsolutePath().contains(IndexConstants.RENDERERS_DIR)) {
|
||||
itemHolder.icon.setImageDrawable(uiUtilities.getIcon(R.drawable.ic_action_map_style, activeColorRes));
|
||||
|
@ -144,16 +148,18 @@ public class DuplicatesSettingsAdapter extends RecyclerView.Adapter<RecyclerView
|
|||
iconId = R.drawable.ic_action_photo_dark;
|
||||
}
|
||||
itemHolder.icon.setImageDrawable(uiUtilities.getIcon(iconId, activeColorRes));
|
||||
} else if (fileSubtype.isMap()
|
||||
|| fileSubtype == FileSubtype.TTS_VOICE
|
||||
|| fileSubtype == FileSubtype.VOICE) {
|
||||
itemHolder.title.setText(FileNameTranslationHelper.getFileNameWithRegion(app, file.getName()));
|
||||
itemHolder.icon.setImageDrawable(uiUtilities.getIcon(fileSubtype.getIconId(), activeColorRes));
|
||||
}
|
||||
itemHolder.subTitle.setVisibility(View.GONE);
|
||||
} else if (currentItem instanceof AvoidRoadInfo) {
|
||||
itemHolder.title.setText(((AvoidRoadInfo) currentItem).name);
|
||||
itemHolder.icon.setImageDrawable(app.getUIUtilities().getIcon(R.drawable.ic_action_alert, activeColorRes));
|
||||
itemHolder.subTitle.setVisibility(View.GONE);
|
||||
} else if (currentItem instanceof FavoriteGroup) {
|
||||
itemHolder.title.setText(((FavoriteGroup) currentItem).getDisplayName(app));
|
||||
itemHolder.icon.setImageDrawable(app.getUIUtilities().getIcon(R.drawable.ic_action_favorite, activeColorRes));
|
||||
itemHolder.subTitle.setVisibility(View.GONE);
|
||||
} else if (currentItem instanceof MapMarkersGroup) {
|
||||
MapMarkersGroup markersGroup = (MapMarkersGroup) currentItem;
|
||||
String groupName = markersGroup.getName();
|
||||
|
@ -162,7 +168,6 @@ public class DuplicatesSettingsAdapter extends RecyclerView.Adapter<RecyclerView
|
|||
}
|
||||
itemHolder.title.setText(groupName);
|
||||
itemHolder.icon.setImageDrawable(app.getUIUtilities().getIcon(R.drawable.ic_action_flag, activeColorRes));
|
||||
itemHolder.subTitle.setVisibility(View.GONE);
|
||||
}
|
||||
itemHolder.divider.setVisibility(shouldShowDivider(position) ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
@ -182,7 +187,7 @@ public class DuplicatesSettingsAdapter extends RecyclerView.Adapter<RecyclerView
|
|||
}
|
||||
}
|
||||
|
||||
private class HeaderViewHolder extends RecyclerView.ViewHolder {
|
||||
private static class HeaderViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView title;
|
||||
TextView subTitle;
|
||||
View divider;
|
||||
|
@ -195,7 +200,7 @@ public class DuplicatesSettingsAdapter extends RecyclerView.Adapter<RecyclerView
|
|||
}
|
||||
}
|
||||
|
||||
private class ItemViewHolder extends RecyclerView.ViewHolder {
|
||||
private static class ItemViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView title;
|
||||
TextView subTitle;
|
||||
ImageView icon;
|
||||
|
@ -219,4 +224,4 @@ public class DuplicatesSettingsAdapter extends RecyclerView.Adapter<RecyclerView
|
|||
return next instanceof String;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -286,21 +286,9 @@ class ExportImportSettingsAdapter extends OsmandBaseExpandableListAdapter {
|
|||
file = (File) currentItem;
|
||||
size = file.length();
|
||||
}
|
||||
title.setText(FileNameTranslationHelper.getFileName(app,
|
||||
app.getResourceManager().getOsmandRegions(),
|
||||
file.getName()));
|
||||
title.setText(FileNameTranslationHelper.getFileNameWithRegion(app, file.getName()));
|
||||
FileSubtype subtype = FileSubtype.getSubtypeByPath(app, file.getPath());
|
||||
switch (subtype) {
|
||||
case SRTM_MAP:
|
||||
iconId = R.drawable.ic_plugin_srtm;
|
||||
break;
|
||||
case WIKI_MAP:
|
||||
iconId = R.drawable.ic_plugin_wikipedia;
|
||||
break;
|
||||
default:
|
||||
iconId = R.drawable.ic_map;
|
||||
}
|
||||
setupIcon(icon, iconId, itemSelected);
|
||||
setupIcon(icon, subtype.getIconId(), itemSelected);
|
||||
subText.setText(AndroidUtils.formatSize(app, size));
|
||||
subText.setVisibility(View.VISIBLE);
|
||||
break;
|
||||
|
@ -312,9 +300,7 @@ class ExportImportSettingsAdapter extends OsmandBaseExpandableListAdapter {
|
|||
case TTS_VOICE:
|
||||
case VOICE:
|
||||
file = (File) currentItem;
|
||||
title.setText(FileNameTranslationHelper.getFileName(app,
|
||||
app.getResourceManager().getOsmandRegions(),
|
||||
file.getName()));
|
||||
title.setText(FileNameTranslationHelper.getFileNameWithRegion(app, file.getName()));
|
||||
setupIcon(icon, R.drawable.ic_action_volume_up, itemSelected);
|
||||
break;
|
||||
case MARKERS:
|
||||
|
|
|
@ -2,6 +2,7 @@ package net.osmand.plus.settings.fragments;
|
|||
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
|
@ -33,6 +34,7 @@ import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem;
|
|||
import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem;
|
||||
import net.osmand.plus.settings.backend.ApplicationMode;
|
||||
import net.osmand.plus.settings.backend.ExportSettingsType;
|
||||
import net.osmand.plus.settings.backend.backup.FileSettingsItem;
|
||||
import net.osmand.plus.settings.backend.backup.GlobalSettingsItem;
|
||||
import net.osmand.plus.settings.backend.backup.ProfileSettingsItem;
|
||||
import net.osmand.plus.settings.backend.backup.SettingsHelper.SettingsExportListener;
|
||||
|
@ -47,6 +49,7 @@ import java.util.ArrayList;
|
|||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
public class ExportProfileBottomSheet extends BasePreferenceBottomSheet {
|
||||
|
@ -60,8 +63,10 @@ public class ExportProfileBottomSheet extends BasePreferenceBottomSheet {
|
|||
private static final String EXPORTING_PROFILE_KEY = "exporting_profile_key";
|
||||
private static final String INCLUDE_ADDITIONAL_DATA_KEY = "include_additional_data_key";
|
||||
private static final String INCLUDE_GLOBAL_SETTINGS_KEY = "include_global_settings_key";
|
||||
private static final String PROGRESS_MAX_KEY = "progress_max_key";
|
||||
private static final String PROGRESS_VALUE_KEY = "progress_value_key";
|
||||
|
||||
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd-MM-yy");
|
||||
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd-MM-yy", Locale.US);
|
||||
|
||||
private OsmandApplication app;
|
||||
private Map<ExportSettingsType, List<?>> dataList = new HashMap<>();
|
||||
|
@ -69,6 +74,8 @@ public class ExportProfileBottomSheet extends BasePreferenceBottomSheet {
|
|||
|
||||
private SettingsExportListener exportListener;
|
||||
private ProgressDialog progress;
|
||||
private int progressMax;
|
||||
private int progressValue;
|
||||
|
||||
private long exportStartTime;
|
||||
|
||||
|
@ -87,6 +94,8 @@ public class ExportProfileBottomSheet extends BasePreferenceBottomSheet {
|
|||
includeAdditionalData = savedInstanceState.getBoolean(INCLUDE_ADDITIONAL_DATA_KEY);
|
||||
includeGlobalSettings = savedInstanceState.getBoolean(INCLUDE_GLOBAL_SETTINGS_KEY);
|
||||
exportStartTime = savedInstanceState.getLong(EXPORT_START_TIME_KEY);
|
||||
progressMax = savedInstanceState.getInt(PROGRESS_MAX_KEY);
|
||||
progressValue = savedInstanceState.getInt(PROGRESS_VALUE_KEY);
|
||||
}
|
||||
dataList = app.getSettingsHelper().getAdditionalData(globalExport);
|
||||
}
|
||||
|
@ -99,6 +108,8 @@ public class ExportProfileBottomSheet extends BasePreferenceBottomSheet {
|
|||
outState.putBoolean(INCLUDE_ADDITIONAL_DATA_KEY, includeAdditionalData);
|
||||
outState.putBoolean(INCLUDE_GLOBAL_SETTINGS_KEY, includeGlobalSettings);
|
||||
outState.putLong(EXPORT_START_TIME_KEY, exportStartTime);
|
||||
outState.putInt(PROGRESS_MAX_KEY, progress.getMax());
|
||||
outState.putInt(PROGRESS_VALUE_KEY, progress.getProgress());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -271,10 +282,22 @@ public class ExportProfileBottomSheet extends BasePreferenceBottomSheet {
|
|||
showExportProgressDialog();
|
||||
File tempDir = FileUtils.getTempDir(app);
|
||||
String fileName = getFileName();
|
||||
app.getSettingsHelper().exportSettings(tempDir, fileName, getSettingsExportListener(), prepareSettingsItemsForExport(), true);
|
||||
List<SettingsItem> items = prepareSettingsItemsForExport();
|
||||
progress.setMax(getMaxProgress(items));
|
||||
app.getSettingsHelper().exportSettings(tempDir, fileName, getSettingsExportListener(), items, true);
|
||||
}
|
||||
}
|
||||
|
||||
private int getMaxProgress(List<SettingsItem> items) {
|
||||
long maxProgress = 0;
|
||||
for (SettingsItem item : items) {
|
||||
if (item instanceof FileSettingsItem) {
|
||||
maxProgress += ((FileSettingsItem) item).getSize();
|
||||
}
|
||||
}
|
||||
return (int) maxProgress / (1 << 20);
|
||||
}
|
||||
|
||||
private String getFileName() {
|
||||
if (globalExport) {
|
||||
if (exportStartTime == 0) {
|
||||
|
@ -295,12 +318,31 @@ public class ExportProfileBottomSheet extends BasePreferenceBottomSheet {
|
|||
progress.dismiss();
|
||||
}
|
||||
progress = new ProgressDialog(context);
|
||||
progress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
|
||||
progress.setCancelable(true);
|
||||
progress.setTitle(app.getString(R.string.shared_string_export));
|
||||
progress.setMessage(app.getString(R.string.shared_string_preparing));
|
||||
progress.setCancelable(false);
|
||||
progress.setButton(DialogInterface.BUTTON_NEGATIVE, app.getString(R.string.shared_string_cancel), new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
cancelExport();
|
||||
}
|
||||
});
|
||||
progress.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
cancelExport();
|
||||
}
|
||||
});
|
||||
progress.show();
|
||||
}
|
||||
|
||||
private void cancelExport() {
|
||||
app.getSettingsHelper().cancelExportForFile(getExportFile());
|
||||
progress.dismiss();
|
||||
dismiss();
|
||||
}
|
||||
|
||||
private SettingsExportListener getSettingsExportListener() {
|
||||
if (exportListener == null) {
|
||||
exportListener = new SettingsExportListener() {
|
||||
|
@ -315,6 +357,11 @@ public class ExportProfileBottomSheet extends BasePreferenceBottomSheet {
|
|||
app.showToastMessage(R.string.export_profile_failed);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSettingsExportProgressUpdate(int value) {
|
||||
progress.setProgress(value);
|
||||
}
|
||||
};
|
||||
}
|
||||
return exportListener;
|
||||
|
@ -326,6 +373,8 @@ public class ExportProfileBottomSheet extends BasePreferenceBottomSheet {
|
|||
boolean fileExporting = app.getSettingsHelper().isFileExporting(file);
|
||||
if (fileExporting) {
|
||||
showExportProgressDialog();
|
||||
progress.setMax(progressMax);
|
||||
progress.setProgress(progressValue);
|
||||
app.getSettingsHelper().updateExportListener(file, getSettingsExportListener());
|
||||
} else if (file.exists()) {
|
||||
dismissExportProgressDialog();
|
||||
|
|
|
@ -32,6 +32,8 @@ import net.osmand.plus.R;
|
|||
import net.osmand.plus.UiUtilities;
|
||||
import net.osmand.plus.base.BaseOsmAndFragment;
|
||||
import net.osmand.plus.helpers.AvoidSpecificRoads.AvoidRoadInfo;
|
||||
import net.osmand.plus.osmedit.OpenstreetmapPoint;
|
||||
import net.osmand.plus.osmedit.OsmNotesPoint;
|
||||
import net.osmand.plus.poi.PoiUIFilter;
|
||||
import net.osmand.plus.quickaction.QuickAction;
|
||||
import net.osmand.plus.settings.backend.ApplicationMode;
|
||||
|
@ -46,10 +48,7 @@ import java.io.File;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static net.osmand.IndexConstants.AV_INDEX_DIR;
|
||||
import static net.osmand.IndexConstants.GPX_INDEX_DIR;
|
||||
import static net.osmand.IndexConstants.RENDERERS_DIR;
|
||||
import static net.osmand.IndexConstants.ROUTING_PROFILES_DIR;
|
||||
import static net.osmand.plus.settings.backend.backup.FileSettingsItem.*;
|
||||
import static net.osmand.plus.settings.fragments.ImportSettingsFragment.IMPORT_SETTINGS_TAG;
|
||||
|
||||
|
||||
|
@ -196,6 +195,11 @@ public class ImportDuplicatesFragment extends BaseOsmAndFragment {
|
|||
List<File> trackFilesList = new ArrayList<>();
|
||||
List<AvoidRoadInfo> avoidRoads = new ArrayList<>();
|
||||
List<FavoriteGroup> favoriteGroups = new ArrayList<>();
|
||||
List<OsmNotesPoint> osmNotesPointList = new ArrayList<>();
|
||||
List<OpenstreetmapPoint> osmEditsPointList = new ArrayList<>();
|
||||
List<File> ttsVoiceFilesList = new ArrayList<>();
|
||||
List<File> voiceFilesList = new ArrayList<>();
|
||||
List<File> mapFilesList = new ArrayList<>();
|
||||
|
||||
for (Object object : duplicatesList) {
|
||||
if (object instanceof ApplicationMode.ApplicationModeBean) {
|
||||
|
@ -208,19 +212,30 @@ public class ImportDuplicatesFragment extends BaseOsmAndFragment {
|
|||
tileSources.add((ITileSource) object);
|
||||
} else if (object instanceof File) {
|
||||
File file = (File) object;
|
||||
if (file.getAbsolutePath().contains(RENDERERS_DIR)) {
|
||||
FileSubtype fileSubtype = FileSubtype.getSubtypeByPath(app, file.getPath());
|
||||
if (fileSubtype == FileSubtype.RENDERING_STYLE) {
|
||||
renderFilesList.add(file);
|
||||
} else if (file.getAbsolutePath().contains(ROUTING_PROFILES_DIR)) {
|
||||
} else if (fileSubtype == FileSubtype.ROUTING_CONFIG) {
|
||||
routingFilesList.add(file);
|
||||
} else if (file.getAbsolutePath().contains(AV_INDEX_DIR)) {
|
||||
} else if (fileSubtype == FileSubtype.MULTIMEDIA_NOTES) {
|
||||
multimediaFilesList.add(file);
|
||||
} else if (file.getAbsolutePath().contains(GPX_INDEX_DIR)) {
|
||||
} else if (fileSubtype == FileSubtype.GPX) {
|
||||
trackFilesList.add(file);
|
||||
} else if (fileSubtype.isMap()) {
|
||||
mapFilesList.add(file);
|
||||
} else if (fileSubtype == FileSubtype.TTS_VOICE) {
|
||||
ttsVoiceFilesList.add(file);
|
||||
} else if (fileSubtype == FileSubtype.VOICE) {
|
||||
voiceFilesList.add(file);
|
||||
}
|
||||
} else if (object instanceof AvoidRoadInfo) {
|
||||
avoidRoads.add((AvoidRoadInfo) object);
|
||||
} else if (object instanceof FavoriteGroup) {
|
||||
favoriteGroups.add((FavoriteGroup) object);
|
||||
} else if (object instanceof OsmNotesPoint) {
|
||||
osmNotesPointList.add((OsmNotesPoint) object);
|
||||
} else if (object instanceof OpenstreetmapPoint) {
|
||||
osmEditsPointList.add((OpenstreetmapPoint) object);
|
||||
}
|
||||
}
|
||||
if (!profiles.isEmpty()) {
|
||||
|
@ -263,6 +278,26 @@ public class ImportDuplicatesFragment extends BaseOsmAndFragment {
|
|||
duplicates.add(getString(R.string.shared_string_favorites));
|
||||
duplicates.addAll(favoriteGroups);
|
||||
}
|
||||
if (!osmNotesPointList.isEmpty()) {
|
||||
duplicates.add(getString(R.string.osm_notes));
|
||||
duplicates.addAll(osmNotesPointList);
|
||||
}
|
||||
if (!osmEditsPointList.isEmpty()) {
|
||||
duplicates.add(getString(R.string.osm_edits));
|
||||
duplicates.addAll(osmEditsPointList);
|
||||
}
|
||||
if (!mapFilesList.isEmpty()) {
|
||||
duplicates.add(getString(R.string.shared_string_maps));
|
||||
duplicates.addAll(mapFilesList);
|
||||
}
|
||||
if (!ttsVoiceFilesList.isEmpty()) {
|
||||
duplicates.add(getString(R.string.local_indexes_cat_tts));
|
||||
duplicates.addAll(ttsVoiceFilesList);
|
||||
}
|
||||
if (!voiceFilesList.isEmpty()) {
|
||||
duplicates.add(getString(R.string.local_indexes_cat_voice));
|
||||
duplicates.addAll(voiceFilesList);
|
||||
}
|
||||
return duplicates;
|
||||
}
|
||||
|
||||
|
|
|
@ -735,6 +735,11 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment {
|
|||
app.showToastMessage(R.string.profile_backup_failed);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSettingsExportProgressUpdate(int value) {
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
return exportListener;
|
||||
|
|
|
@ -26,7 +26,9 @@ import net.osmand.data.QuadRect;
|
|||
import net.osmand.data.RotatedTileBox;
|
||||
import net.osmand.data.TransportStop;
|
||||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.activities.MapActivity;
|
||||
import net.osmand.plus.mapcontextmenu.other.TrackChartPoints;
|
||||
import net.osmand.plus.measurementtool.MeasurementToolFragment;
|
||||
import net.osmand.plus.profiles.LocationIcon;
|
||||
import net.osmand.plus.routing.RouteCalculationResult;
|
||||
import net.osmand.plus.routing.RouteDirectionInfo;
|
||||
|
@ -158,7 +160,8 @@ public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.ICont
|
|||
@Override
|
||||
public void onPrepareBufferImage(Canvas canvas, RotatedTileBox tileBox, DrawSettings settings) {
|
||||
if ((helper.isPublicTransportMode() && transportHelper.getRoutes() != null) ||
|
||||
(helper.getFinalLocation() != null && helper.getRoute().isCalculated())) {
|
||||
(helper.getFinalLocation() != null && helper.getRoute().isCalculated()) ||
|
||||
isPlanRouteGraphsAvailable()) {
|
||||
|
||||
updateAttrs(settings, tileBox);
|
||||
|
||||
|
@ -202,6 +205,17 @@ public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.ICont
|
|||
|
||||
}
|
||||
|
||||
private boolean isPlanRouteGraphsAvailable() {
|
||||
if (view.getContext() instanceof MapActivity) {
|
||||
MapActivity mapActivity = (MapActivity) view.getContext();
|
||||
MeasurementToolFragment fragment = mapActivity.getMeasurementToolFragment();
|
||||
if (fragment != null) {
|
||||
return fragment.hasVisibleGraph();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void updateAttrs(DrawSettings settings, RotatedTileBox tileBox) {
|
||||
boolean updatePaints = attrs.updatePaints(view.getApplication(), settings, tileBox);
|
||||
attrs.isPaint3 = false;
|
||||
|
|
Loading…
Reference in a new issue