diff --git a/OsmAnd-java/src/main/java/com/jwetherell/openmap/common/MGRSPoint.java b/OsmAnd-java/src/main/java/com/jwetherell/openmap/common/MGRSPoint.java index 6e99eec8b5..519fc9d171 100644 --- a/OsmAnd-java/src/main/java/com/jwetherell/openmap/common/MGRSPoint.java +++ b/OsmAnd-java/src/main/java/com/jwetherell/openmap/common/MGRSPoint.java @@ -17,6 +17,7 @@ package com.jwetherell.openmap.common; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Locale; public class MGRSPoint extends ZonedUTMPoint { @@ -697,10 +698,9 @@ public class MGRSPoint extends ZonedUTMPoint { all.set(1, shiftChar(all.get(1), eastShift, northShift)); String zero = ""; } - - - all.add(String.format("%0" + accuracy + "d", roundedEasting)); - all.add(String.format("%0" + accuracy + "d", roundedNorthing)); + + all.add(String.format(Locale.US,"%0" + accuracy + "d", roundedEasting)); + all.add(String.format(Locale.US,"%0" + accuracy + "d", roundedNorthing)); break; } } diff --git a/OsmAnd-java/src/main/java/net/osmand/Period.java b/OsmAnd-java/src/main/java/net/osmand/Period.java index d6bd911257..b529fdb6db 100644 --- a/OsmAnd-java/src/main/java/net/osmand/Period.java +++ b/OsmAnd-java/src/main/java/net/osmand/Period.java @@ -1,20 +1,23 @@ package net.osmand; import java.text.ParseException; +import java.util.Calendar; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Period { public enum PeriodUnit { - YEAR("Y"), - MONTH("M"), - WEEK("W"), - DAY("D"); + YEAR("Y", Calendar.YEAR), + MONTH("M", Calendar.MONTH), + WEEK("W", Calendar.WEEK_OF_YEAR), + DAY("D", Calendar.DATE); private String unitStr; + private int calendarIdx; - PeriodUnit(String unitStr) { + PeriodUnit(String unitStr, int calendarIdx) { + this.calendarIdx = calendarIdx; this.unitStr = unitStr; } @@ -22,6 +25,10 @@ public class Period { return unitStr; } + public int getCalendarIdx() { + return calendarIdx; + } + public double getMonthsValue() { switch (this) { case YEAR: diff --git a/OsmAnd-java/src/main/java/net/osmand/binary/RouteDataObject.java b/OsmAnd-java/src/main/java/net/osmand/binary/RouteDataObject.java index b6dc2f2176..6e063b8ecb 100644 --- a/OsmAnd-java/src/main/java/net/osmand/binary/RouteDataObject.java +++ b/OsmAnd-java/src/main/java/net/osmand/binary/RouteDataObject.java @@ -38,7 +38,7 @@ public class RouteDataObject { public int[] nameIds; // mixed array [0, height, cumulative_distance height, cumulative_distance, height, ...] - length is length(points)*2 public float[] heightDistanceArray = null; - public float heightByCurrentLocation; + public float heightByCurrentLocation = Float.NaN; private static final Log LOG = PlatformUtil.getLog(RouteDataObject.class); public RouteDataObject(RouteRegion region) { @@ -185,7 +185,8 @@ public class RouteDataObject { heightDistanceArray = new float[2 * getPointsLength()]; double plon = 0; double plat = 0; - float prevHeight = heightByCurrentLocation = startHeight; + float prevHeight = startHeight; + heightByCurrentLocation = Float.NaN; double prevDistance = 0; for (int k = 0; k < getPointsLength(); k++) { double lon = MapUtils.get31LongitudeX(getPoint31XTile(k)); @@ -583,7 +584,7 @@ public class RouteDataObject { String nonCondTag = rtr.getTag(); int ks; for (ks = 0; ks < pointTypes[i].length; ks++) { - RouteTypeRule toReplace = region.quickGetEncodingRule(pointTypes[i][j]); + RouteTypeRule toReplace = region.quickGetEncodingRule(pointTypes[i][ks]); if (toReplace != null && toReplace.getTag().contentEquals(nonCondTag)) { break; } diff --git a/OsmAnd/res/drawable/bg_osmand_live_active.xml b/OsmAnd/res/drawable/bg_osmand_live_active.xml new file mode 100644 index 0000000000..8544f60004 --- /dev/null +++ b/OsmAnd/res/drawable/bg_osmand_live_active.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/OsmAnd/res/drawable/bg_osmand_live_cancelled.xml b/OsmAnd/res/drawable/bg_osmand_live_cancelled.xml new file mode 100644 index 0000000000..2774bc263a --- /dev/null +++ b/OsmAnd/res/drawable/bg_osmand_live_cancelled.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/OsmAnd/res/drawable/btn_solid_border_dark.xml b/OsmAnd/res/drawable/btn_solid_border_dark.xml new file mode 100644 index 0000000000..5f0ecb368b --- /dev/null +++ b/OsmAnd/res/drawable/btn_solid_border_dark.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/drawable/btn_solid_border_light.xml b/OsmAnd/res/drawable/btn_solid_border_light.xml new file mode 100644 index 0000000000..528f174597 --- /dev/null +++ b/OsmAnd/res/drawable/btn_solid_border_light.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout-land/fragment_measurement_tool.xml b/OsmAnd/res/layout-land/fragment_measurement_tool.xml index 4ed0e425e3..505e680262 100644 --- a/OsmAnd/res/layout-land/fragment_measurement_tool.xml +++ b/OsmAnd/res/layout-land/fragment_measurement_tool.xml @@ -160,7 +160,9 @@ android:paddingRight="@dimen/measurement_tool_text_button_padding_small" android:text="@string/shared_string_options" android:textColor="?attr/color_dialog_buttons" - osmand:typeface="@string/font_roboto_medium" /> + osmand:typeface="@string/font_roboto_medium" + android:paddingEnd="@dimen/measurement_tool_text_button_padding_small" + android:paddingStart="@dimen/measurement_tool_text_button_padding_small" /> + osmand:srcCompat="@drawable/ic_action_user_account" + android:layout_marginStart="@dimen/content_padding" + android:layout_marginEnd="@dimen/content_padding" /> + android:orientation="vertical" + android:layout_marginStart="@dimen/content_padding" + android:layout_marginEnd="@dimen/content_padding"> + osmand:typeface="@string/font_roboto_medium" + android:paddingEnd="@dimen/content_padding" + android:paddingStart="@dimen/content_padding" /> + osmand:typeface="@string/font_roboto_medium" + android:paddingStart="@dimen/content_padding" + android:paddingEnd="@dimen/content_padding" /> + android:paddingBottom="@dimen/content_padding" + android:layout_marginStart="@dimen/content_padding_small" + android:layout_marginEnd="@dimen/content_padding_small" /> \ No newline at end of file diff --git a/OsmAnd/res/layout/bottom_sheet_item_description_with_padding.xml b/OsmAnd/res/layout/bottom_sheet_item_description_with_padding.xml index b6516e85ff..9a5aec71e8 100644 --- a/OsmAnd/res/layout/bottom_sheet_item_description_with_padding.xml +++ b/OsmAnd/res/layout/bottom_sheet_item_description_with_padding.xml @@ -8,7 +8,9 @@ android:paddingBottom="@dimen/content_padding_small" android:minHeight="@dimen/card_row_min_height" android:layout_width="match_parent" - android:layout_height="wrap_content"> + android:layout_height="wrap_content" + android:paddingEnd="@dimen/content_padding" + android:paddingStart="@dimen/content_padding"> + android:layout_marginRight="@dimen/content_padding" + android:layout_marginStart="@dimen/content_padding" + android:layout_marginEnd="@dimen/content_padding"> + android:paddingBottom="@dimen/content_padding_small" + android:paddingEnd="@dimen/content_padding" + android:paddingStart="@dimen/content_padding" /> diff --git a/OsmAnd/res/layout/bottom_sheet_item_slider_with_two_text.xml b/OsmAnd/res/layout/bottom_sheet_item_slider_with_two_text.xml index f386e8c59f..fc8cb6a41e 100644 --- a/OsmAnd/res/layout/bottom_sheet_item_slider_with_two_text.xml +++ b/OsmAnd/res/layout/bottom_sheet_item_slider_with_two_text.xml @@ -11,7 +11,9 @@ android:layout_height="wrap_content" android:gravity="center_vertical" android:paddingLeft="@dimen/content_padding" - android:paddingRight="@dimen/content_padding"> + android:paddingRight="@dimen/content_padding" + android:paddingStart="@dimen/content_padding" + android:paddingEnd="@dimen/content_padding"> + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/content_padding" + android:layout_marginEnd="@dimen/content_padding" /> \ No newline at end of file diff --git a/OsmAnd/res/layout/bottom_sheet_item_with_descr_radio_and_icon_btn.xml b/OsmAnd/res/layout/bottom_sheet_item_with_descr_radio_and_icon_btn.xml index c4dc6d4265..c0cacc0b79 100644 --- a/OsmAnd/res/layout/bottom_sheet_item_with_descr_radio_and_icon_btn.xml +++ b/OsmAnd/res/layout/bottom_sheet_item_with_descr_radio_and_icon_btn.xml @@ -90,7 +90,9 @@ android:paddingLeft="@dimen/content_padding" android:paddingRight="@dimen/content_padding" android:layout_width="wrap_content" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:paddingEnd="@dimen/content_padding" + android:paddingStart="@dimen/content_padding"> + tools:src="@drawable/ic_action_info_dark" + android:layout_marginEnd="@dimen/content_padding" + android:layout_marginStart="@dimen/content_padding" /> diff --git a/OsmAnd/res/layout/contact_support.xml b/OsmAnd/res/layout/contact_support.xml new file mode 100644 index 0000000000..30e45c4ea4 --- /dev/null +++ b/OsmAnd/res/layout/contact_support.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/custom_color_picker.xml b/OsmAnd/res/layout/custom_color_picker.xml index f2133e410b..c48205c25e 100644 --- a/OsmAnd/res/layout/custom_color_picker.xml +++ b/OsmAnd/res/layout/custom_color_picker.xml @@ -6,7 +6,9 @@ android:layout_height="wrap_content" android:orientation="vertical" android:paddingLeft="@dimen/content_padding" - android:paddingRight="@dimen/content_padding"> + android:paddingRight="@dimen/content_padding" + android:paddingStart="@dimen/content_padding" + android:paddingEnd="@dimen/content_padding"> + tools:visibility="gone" + android:layout_marginEnd="@dimen/multi_selection_menu_padding_top" + android:layout_marginStart="@dimen/multi_selection_menu_padding_top" /> + app:srcCompat="@drawable/ic_configure_screen_dark" + android:layout_marginEnd="@dimen/multi_selection_menu_padding_top" + android:layout_marginStart="@dimen/multi_selection_menu_padding_top" /> + app:srcCompat="@drawable/ic_action_done" + android:layout_marginEnd="@dimen/multi_selection_menu_padding_top" + android:layout_marginStart="@dimen/multi_selection_menu_padding_top" /> + app:srcCompat="@drawable/ic_sort_waypoint_dark" + android:layout_marginStart="@dimen/multi_selection_menu_padding_top" + android:layout_marginEnd="@dimen/multi_selection_menu_padding_top" /> + android:visibility="gone" + android:layout_marginEnd="@dimen/multi_selection_menu_padding_top" + android:layout_marginStart="@dimen/multi_selection_menu_padding_top" /> + android:visibility="gone" + android:layout_marginEnd="@dimen/multi_selection_menu_padding_top" + android:layout_marginStart="@dimen/multi_selection_menu_padding_top" /> diff --git a/OsmAnd/res/layout/dialog_button_with_icon.xml b/OsmAnd/res/layout/dialog_button_with_icon.xml index 264f884495..6a80553e49 100644 --- a/OsmAnd/res/layout/dialog_button_with_icon.xml +++ b/OsmAnd/res/layout/dialog_button_with_icon.xml @@ -13,7 +13,8 @@ android:layout_marginRight="@dimen/content_padding" android:layout_marginStart="@dimen/content_padding" android:layout_marginBottom="@dimen/content_padding_small" - tools:ignore="UselessParent"> + tools:ignore="UselessParent" + android:layout_marginEnd="@dimen/content_padding"> \ No newline at end of file + tools:text="@string/read_more" + android:paddingStart="@dimen/content_padding_half" + android:paddingEnd="@dimen/content_padding_half" /> \ No newline at end of file diff --git a/OsmAnd/res/layout/enough_space_warning_card.xml b/OsmAnd/res/layout/enough_space_warning_card.xml index 3fcbb69bac..6b85dcb27c 100644 --- a/OsmAnd/res/layout/enough_space_warning_card.xml +++ b/OsmAnd/res/layout/enough_space_warning_card.xml @@ -20,7 +20,9 @@ android:layout_marginLeft="@dimen/content_padding" android:layout_marginTop="@dimen/content_padding" android:layout_marginRight="@dimen/content_padding" - app:srcCompat="@drawable/ic_action_sdcard_warning_colored" /> + app:srcCompat="@drawable/ic_action_sdcard_warning_colored" + android:layout_marginStart="@dimen/content_padding" + android:layout_marginEnd="@dimen/content_padding" /> + android:orientation="vertical" + android:layout_marginStart="@dimen/content_padding" + android:layout_marginEnd="@dimen/content_padding"> + tools:listitem="@layout/point_editor_icon_category_item" + android:paddingStart="@dimen/content_padding" + android:paddingEnd="@dimen/content_padding" /> + tools:visibility="visible" + android:paddingEnd="@dimen/content_padding" + android:paddingStart="@dimen/content_padding"> + android:layout_marginBottom="@dimen/content_padding_half" + android:layout_marginEnd="@dimen/content_padding_half" + android:layout_marginStart="@dimen/content_padding_half"> - - - - - - - - - - - - + android:orientation="vertical" /> + android:paddingBottom="@dimen/bottom_sheet_list_item_height" + tools:context=".liveupdates.LiveUpdatesFragment" /> diff --git a/OsmAnd/res/layout/fragment_measurement_tool.xml b/OsmAnd/res/layout/fragment_measurement_tool.xml index 3903b445ca..71bf4671fe 100644 --- a/OsmAnd/res/layout/fragment_measurement_tool.xml +++ b/OsmAnd/res/layout/fragment_measurement_tool.xml @@ -164,7 +164,9 @@ android:paddingTop="@dimen/measurement_tool_button_margin" android:paddingBottom="@dimen/measurement_tool_button_margin" android:paddingLeft="@dimen/measurement_tool_button_margin" - android:paddingRight="@dimen/measurement_tool_button_margin"> + android:paddingRight="@dimen/measurement_tool_button_margin" + android:paddingStart="@dimen/measurement_tool_button_margin" + android:paddingEnd="@dimen/measurement_tool_button_margin"> + app:typeface="@string/font_roboto_medium" + android:layout_marginStart="@dimen/content_padding" + android:layout_marginEnd="@dimen/content_padding" /> + app:typeface="@string/font_roboto_regular" + android:layout_marginEnd="@dimen/content_padding" + android:layout_marginStart="@dimen/content_padding" /> @@ -73,7 +77,9 @@ android:layout_marginTop="@dimen/content_padding_small" android:layout_marginRight="@dimen/content_padding" android:layout_marginBottom="@dimen/content_padding_small" - android:orientation="vertical"> + android:orientation="vertical" + android:layout_marginStart="@dimen/content_padding" + android:layout_marginEnd="@dimen/content_padding"> + android:stepSize="1" + android:layout_marginStart="@dimen/content_padding" + android:layout_marginEnd="@dimen/content_padding" /> + android:stepSize="1" + android:layout_marginStart="@dimen/content_padding" + android:layout_marginEnd="@dimen/content_padding" /> + tools:text="@string/routing_settings_2" + android:layout_marginStart="@dimen/content_padding" + android:layout_marginEnd="@dimen/content_padding" /> + android:paddingBottom="@dimen/content_padding_half" + android:paddingEnd="@dimen/content_padding" + android:paddingStart="@dimen/content_padding"> @@ -221,7 +221,7 @@ android:layout_width="wrap_content" android:layout_height="@dimen/poi_icon_size" android:layout_marginTop="2dp" - osmand:srcCompat="@drawable/ic_action_time_span" /> + osmand:srcCompat="@drawable/ic_action_time_moving_16" /> diff --git a/OsmAnd/res/layout/gpx_list_item_tab_content.xml b/OsmAnd/res/layout/gpx_list_item_tab_content.xml index 5fa3d3ab29..37874a7b9c 100644 --- a/OsmAnd/res/layout/gpx_list_item_tab_content.xml +++ b/OsmAnd/res/layout/gpx_list_item_tab_content.xml @@ -21,7 +21,9 @@ android:layout_marginLeft="@dimen/content_padding" android:layout_marginRight="@dimen/content_padding" android:minHeight="@dimen/dialog_button_height" - android:paddingTop="@dimen/content_padding" /> + android:paddingTop="@dimen/content_padding" + android:layout_marginStart="@dimen/content_padding" + android:layout_marginEnd="@dimen/content_padding" /> + tools:text="Amsterdam is the Netherlands' capital and financial, cultural and creative centre with more" + android:layout_marginEnd="@dimen/content_padding" + android:layout_marginStart="@dimen/content_padding" /> + tools:text="@string/plugin_disabled_descr" + android:paddingEnd="@dimen/content_padding" + android:paddingStart="@dimen/content_padding" /> + android:orientation="vertical" + android:layout_marginStart="@dimen/content_padding_half" + android:layout_marginEnd="@dimen/content_padding_half" /> diff --git a/OsmAnd/res/layout/import_track_card.xml b/OsmAnd/res/layout/import_track_card.xml index 24ead3b9a6..108676e545 100644 --- a/OsmAnd/res/layout/import_track_card.xml +++ b/OsmAnd/res/layout/import_track_card.xml @@ -19,7 +19,9 @@ android:text="@string/import_track_descr" android:textAppearance="@style/TextAppearance.ContextMenuSubtitle" android:textColor="?android:textColorSecondary" - osmand:typeface="@string/font_roboto_regular" /> + osmand:typeface="@string/font_roboto_regular" + android:paddingEnd="@dimen/content_padding" + android:paddingStart="@dimen/content_padding" /> diff --git a/OsmAnd/res/layout/item_header_export_expand_list.xml b/OsmAnd/res/layout/item_header_export_expand_list.xml index a77881d33a..09fc11ad23 100644 --- a/OsmAnd/res/layout/item_header_export_expand_list.xml +++ b/OsmAnd/res/layout/item_header_export_expand_list.xml @@ -22,7 +22,9 @@ android:text="@string/export_profile_dialog_description" android:textColor="?android:textColorPrimary" android:textSize="@dimen/default_list_text_size" - app:typeface="@string/font_roboto_regular" /> + app:typeface="@string/font_roboto_regular" + android:paddingStart="@dimen/content_padding" + android:paddingEnd="@dimen/content_padding" /> + tools:text="@string/plugin_disabled_descr" + android:paddingStart="@dimen/content_padding" + android:paddingEnd="@dimen/content_padding" /> diff --git a/OsmAnd/res/layout/list_item_triple_row_icon_and_menu.xml b/OsmAnd/res/layout/list_item_triple_row_icon_and_menu.xml index 1f179b8178..c030d97625 100644 --- a/OsmAnd/res/layout/list_item_triple_row_icon_and_menu.xml +++ b/OsmAnd/res/layout/list_item_triple_row_icon_and_menu.xml @@ -84,12 +84,12 @@ \ No newline at end of file diff --git a/OsmAnd/res/layout/map_hud_bottom.xml b/OsmAnd/res/layout/map_hud_bottom.xml index 629f9350a1..30acd574e9 100644 --- a/OsmAnd/res/layout/map_hud_bottom.xml +++ b/OsmAnd/res/layout/map_hud_bottom.xml @@ -76,7 +76,9 @@ android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginLeft="@dimen/dashPadding" - android:layout_marginRight="@dimen/dashPadding"/> + android:layout_marginRight="@dimen/dashPadding" + android:layout_marginStart="@dimen/dashPadding" + android:layout_marginEnd="@dimen/dashPadding" /> diff --git a/OsmAnd/res/layout/map_marker_item_header.xml b/OsmAnd/res/layout/map_marker_item_header.xml index ab0a14e08b..14e5f8e9f4 100644 --- a/OsmAnd/res/layout/map_marker_item_header.xml +++ b/OsmAnd/res/layout/map_marker_item_header.xml @@ -50,7 +50,9 @@ android:gravity="center" android:visibility="gone" android:background="?android:selectableItemBackground" - android:textColor="?attr/active_color_basic" /> + android:textColor="?attr/active_color_basic" + android:paddingEnd="@dimen/bottom_sheet_content_margin" + android:paddingStart="@dimen/bottom_sheet_content_margin" /> + osmand:typeface="@string/font_roboto_medium" + android:paddingEnd="@dimen/content_padding" + android:paddingStart="@dimen/content_padding" /> + android:text="@string/navigation_profile" + android:layout_marginEnd="@dimen/content_padding" + android:layout_marginStart="@dimen/content_padding" /> + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/no_purchases_card.xml b/OsmAnd/res/layout/no_purchases_card.xml new file mode 100644 index 0000000000..7bb787d4ab --- /dev/null +++ b/OsmAnd/res/layout/no_purchases_card.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/online_routing_preference_segment.xml b/OsmAnd/res/layout/online_routing_preference_segment.xml index 3e5373aa6a..9af281f86f 100644 --- a/OsmAnd/res/layout/online_routing_preference_segment.xml +++ b/OsmAnd/res/layout/online_routing_preference_segment.xml @@ -16,7 +16,9 @@ android:paddingRight="@dimen/content_padding" android:paddingBottom="@dimen/content_padding_half" tools:visibility="visible" - android:visibility="gone"> + android:visibility="gone" + android:paddingStart="@dimen/content_padding" + android:paddingEnd="@dimen/content_padding"> + android:visibility="gone" + android:paddingEnd="@dimen/content_padding" + android:paddingStart="@dimen/content_padding" /> + android:visibility="gone" + android:paddingEnd="@dimen/content_padding" + android:paddingStart="@dimen/content_padding"> + android:text="@string/add_photos_descr" + android:layout_marginEnd="@dimen/content_padding" + android:layout_marginStart="@dimen/content_padding" /> \ No newline at end of file diff --git a/OsmAnd/res/layout/osm_live_banner_list_item.xml b/OsmAnd/res/layout/osm_live_banner_list_item.xml index 05d83d8da1..12f59c3ef0 100644 --- a/OsmAnd/res/layout/osm_live_banner_list_item.xml +++ b/OsmAnd/res/layout/osm_live_banner_list_item.xml @@ -29,13 +29,14 @@ android:layout_weight="1" android:orientation="vertical"> - + android:textSize="@dimen/default_list_text_size" + osmand:typeface="@string/font_roboto_medium" /> - + android:textSize="@dimen/default_desc_text_size" + osmand:typeface="@string/font_roboto_medium" /> + android:paddingRight="@dimen/content_padding" + android:paddingEnd="@dimen/content_padding" + android:paddingStart="@dimen/content_padding"> + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/content_padding" + android:layout_marginStart="@dimen/content_padding" /> diff --git a/OsmAnd/res/layout/point_editor_fragment_new.xml b/OsmAnd/res/layout/point_editor_fragment_new.xml index 6beccf0dd7..b146b2ea1e 100644 --- a/OsmAnd/res/layout/point_editor_fragment_new.xml +++ b/OsmAnd/res/layout/point_editor_fragment_new.xml @@ -58,7 +58,9 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/content_padding" - android:layout_marginRight="@dimen/content_padding"> + android:layout_marginRight="@dimen/content_padding" + android:layout_marginEnd="@dimen/content_padding" + android:layout_marginStart="@dimen/content_padding"> + app:hintAnimationEnabled="false" + android:layout_marginEnd="@dimen/content_padding" + android:layout_marginStart="@dimen/content_padding"> + android:background="?attr/dashboard_divider" + android:layout_marginStart="@dimen/content_padding" + android:layout_marginEnd="@dimen/content_padding" /> + app:hintAnimationEnabled="false" + android:layout_marginStart="@dimen/content_padding" + android:layout_marginEnd="@dimen/content_padding"> + osmand:typeface="@string/font_roboto_medium" + android:paddingEnd="@dimen/content_padding" + android:paddingStart="@dimen/content_padding" /> + osmand:typeface="@string/font_roboto_medium" + android:paddingEnd="@dimen/content_padding" + android:paddingStart="@dimen/content_padding" /> + osmand:typeface="@string/font_roboto_medium" + android:paddingStart="@dimen/content_padding" + android:paddingEnd="@dimen/content_padding" /> + osmand:typeface="@string/font_roboto_medium" + android:paddingStart="@dimen/content_padding" + android:paddingEnd="@dimen/content_padding" /> + osmand:typeface="@string/font_roboto_medium" + android:paddingEnd="@dimen/content_padding" + android:paddingStart="@dimen/content_padding" /> + osmand:typeface="@string/font_roboto_medium" + android:paddingStart="@dimen/content_padding" + android:paddingEnd="@dimen/content_padding" /> + osmand:srcCompat="@drawable/ic_action_arrow_down" + android:layout_marginEnd="@dimen/content_padding" + android:layout_marginStart="@dimen/content_padding" /> + android:orientation="vertical" + android:layout_marginEnd="@dimen/content_padding" + android:layout_marginStart="@dimen/content_padding"> + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/quick_action_add_dialog_header.xml b/OsmAnd/res/layout/quick_action_add_dialog_header.xml index 981955a3a2..e2a34a23a8 100644 --- a/OsmAnd/res/layout/quick_action_add_dialog_header.xml +++ b/OsmAnd/res/layout/quick_action_add_dialog_header.xml @@ -17,7 +17,9 @@ android:layout_marginLeft="@dimen/content_padding" android:layout_marginRight="@dimen/content_padding" tools:text="Screen 1" - osmand:typeface="@string/font_roboto_medium" /> + osmand:typeface="@string/font_roboto_medium" + android:layout_marginStart="@dimen/content_padding" + android:layout_marginEnd="@dimen/content_padding" /> + android:orientation="horizontal" + android:paddingStart="@dimen/content_padding" + android:paddingEnd="@dimen/content_padding"> + android:padding="@dimen/content_padding_small" + android:layout_marginEnd="@dimen/dashCardMargin" + android:layout_marginStart="@dimen/dashCardMargin"> + android:padding="@dimen/content_padding_small" + android:layout_marginEnd="@dimen/dashCardMargin" + android:layout_marginStart="@dimen/dashCardMargin"> + tools:text="@string/pass_whole_track_descr" + android:paddingStart="@dimen/content_padding" + android:paddingEnd="@dimen/content_padding" /> diff --git a/OsmAnd/res/layout/read_wikipedia_ofline_banner.xml b/OsmAnd/res/layout/read_wikipedia_ofline_banner.xml index 1093ad41f1..4e27debe49 100644 --- a/OsmAnd/res/layout/read_wikipedia_ofline_banner.xml +++ b/OsmAnd/res/layout/read_wikipedia_ofline_banner.xml @@ -16,7 +16,9 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_marginLeft="@dimen/content_padding" - android:layout_marginRight="@dimen/content_padding"> + android:layout_marginRight="@dimen/content_padding" + android:layout_marginStart="@dimen/content_padding" + android:layout_marginEnd="@dimen/content_padding"> + android:visibility="invisible" + android:layout_marginEnd="@dimen/content_padding" + android:layout_marginStart="@dimen/content_padding"> + android:paddingBottom="@dimen/content_padding" + android:layout_marginEnd="@dimen/content_padding_small" + android:layout_marginStart="@dimen/content_padding_small" /> diff --git a/OsmAnd/res/layout/restore_purchases_button.xml b/OsmAnd/res/layout/restore_purchases_button.xml new file mode 100644 index 0000000000..a4a34bcd6c --- /dev/null +++ b/OsmAnd/res/layout/restore_purchases_button.xml @@ -0,0 +1,34 @@ + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/select_folder_row.xml b/OsmAnd/res/layout/select_folder_row.xml index 1d855ff70f..dccba6c9b8 100644 --- a/OsmAnd/res/layout/select_folder_row.xml +++ b/OsmAnd/res/layout/select_folder_row.xml @@ -20,7 +20,9 @@ android:text="@string/select_folder" android:textColor="?android:textColorSecondary" android:textSize="@dimen/default_desc_text_size" - osmand:typeface="@string/font_roboto_regular" /> + osmand:typeface="@string/font_roboto_regular" + android:paddingStart="@dimen/content_padding" + android:paddingEnd="@dimen/content_padding" /> + osmand:typeface="@string/font_roboto_regular" + android:paddingStart="@dimen/content_padding" + android:paddingEnd="@dimen/content_padding" /> + tools:text="@string/gpx_visibility_txt" + android:paddingStart="@dimen/content_padding" + android:paddingEnd="@dimen/content_padding" /> @@ -138,7 +142,9 @@ android:paddingBottom="@dimen/context_menu_first_line_top_margin" android:textColor="?android:textColorPrimary" android:textSize="@dimen/default_desc_text_size" - osmand:typeface="@string/font_roboto_regular" /> + osmand:typeface="@string/font_roboto_regular" + android:paddingEnd="@dimen/content_padding" + android:paddingStart="@dimen/content_padding" /> + osmand:typeface="@string/font_roboto_regular" + android:paddingStart="@dimen/content_padding_small" + android:paddingEnd="@dimen/content_padding_small" /> diff --git a/OsmAnd/res/layout/send_poi_fragment.xml b/OsmAnd/res/layout/send_poi_fragment.xml index 751c0b1fe8..538cc55e0b 100644 --- a/OsmAnd/res/layout/send_poi_fragment.xml +++ b/OsmAnd/res/layout/send_poi_fragment.xml @@ -57,7 +57,9 @@ android:text="@string/close_changeset" android:textColor="?android:textColorPrimary" android:textSize="@dimen/default_list_text_size" - osmand:typeface="@string/font_roboto_regular" /> + osmand:typeface="@string/font_roboto_regular" + android:paddingStart="@dimen/content_padding_small" + android:paddingEnd="@dimen/content_padding_small" /> diff --git a/OsmAnd/res/layout/settings_group_title.xml b/OsmAnd/res/layout/settings_group_title.xml index 456a3aace1..7d4fb811cd 100644 --- a/OsmAnd/res/layout/settings_group_title.xml +++ b/OsmAnd/res/layout/settings_group_title.xml @@ -70,7 +70,9 @@ android:paddingLeft="@dimen/content_padding" android:paddingTop="@dimen/content_padding_small" android:paddingRight="@dimen/content_padding" - android:paddingBottom="@dimen/content_padding_small"> + android:paddingBottom="@dimen/content_padding_small" + android:paddingStart="@dimen/content_padding" + android:paddingEnd="@dimen/content_padding"> + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OsmAnd/res/layout/subscriptions_card.xml b/OsmAnd/res/layout/subscriptions_card.xml new file mode 100644 index 0000000000..b4deb45ffc --- /dev/null +++ b/OsmAnd/res/layout/subscriptions_card.xml @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/subscriptions_list_card.xml b/OsmAnd/res/layout/subscriptions_list_card.xml new file mode 100644 index 0000000000..54c8480c4e --- /dev/null +++ b/OsmAnd/res/layout/subscriptions_list_card.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/OsmAnd/res/layout/track_appearance.xml b/OsmAnd/res/layout/track_appearance.xml index 3356a7f3f3..8d9f0d2f13 100644 --- a/OsmAnd/res/layout/track_appearance.xml +++ b/OsmAnd/res/layout/track_appearance.xml @@ -22,7 +22,9 @@ android:minHeight="@dimen/bottom_sheet_title_height" android:orientation="vertical" android:paddingLeft="@dimen/content_padding" - android:paddingRight="@dimen/content_padding"> + android:paddingRight="@dimen/content_padding" + android:paddingEnd="@dimen/content_padding" + android:paddingStart="@dimen/content_padding"> + tools:visibility="visible" + android:layout_marginStart="@dimen/content_padding_half" + android:layout_marginEnd="@dimen/content_padding_half"> + android:stepSize="1" + android:layout_marginEnd="@dimen/content_padding" + android:layout_marginStart="@dimen/content_padding" /> + android:stepSize="1" + android:layout_marginStart="@dimen/content_padding" + android:layout_marginEnd="@dimen/content_padding" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/wikivoyage_travel_gpx_card.xml b/OsmAnd/res/layout/wikivoyage_travel_gpx_card.xml index 963c27c9f3..de516d1ffd 100644 --- a/OsmAnd/res/layout/wikivoyage_travel_gpx_card.xml +++ b/OsmAnd/res/layout/wikivoyage_travel_gpx_card.xml @@ -176,7 +176,9 @@ android:textSize="@dimen/default_desc_text_size" osmand:typeface="@string/font_roboto_medium" tools:drawableTint="?attr/wikivoyage_active_color" - tools:text="@string/mapillary_menu_title_username" /> + tools:text="@string/mapillary_menu_title_username" + android:paddingStart="@dimen/content_padding_small_half" + android:paddingEnd="@dimen/content_padding_small_half" /> + tools:text="@string/shared_string_profiles" + android:paddingStart="@dimen/content_padding_small_half" + android:paddingEnd="@dimen/content_padding_small_half" /> diff --git a/OsmAnd/res/layout/zoom_levels_with_descr.xml b/OsmAnd/res/layout/zoom_levels_with_descr.xml index 5cfbcde5e9..3d5b16a7df 100644 --- a/OsmAnd/res/layout/zoom_levels_with_descr.xml +++ b/OsmAnd/res/layout/zoom_levels_with_descr.xml @@ -78,7 +78,9 @@ android:layout_marginLeft="@dimen/content_padding" android:layout_marginRight="@dimen/content_padding" android:layout_weight="1" - android:stepSize="1" /> + android:stepSize="1" + android:layout_marginEnd="@dimen/content_padding" + android:layout_marginStart="@dimen/content_padding" /> الجيوديسيا (شكل الأرض ومساحتها) المرجع المحلي الزحلقه + التدريب: تصفيف الشعر + التدريب: الطيران + التدريب: اليوغا + التدريب: الحاسوب + التدريب: فن + التدريب: رياضة + نصب تذكاري بوذي + ممر زحلقة الشمالي \ No newline at end of file diff --git a/OsmAnd/res/values-ar/strings.xml b/OsmAnd/res/values-ar/strings.xml index 475178ebc4..4cb4b38f94 100644 --- a/OsmAnd/res/values-ar/strings.xml +++ b/OsmAnd/res/values-ar/strings.xml @@ -1010,7 +1010,7 @@ المفضلة تم حفظها بنجاح إلى {0} لم يتم العثور على نقاط أماكن مفضلة للقيام بالاحتفاظ بها تم تعديل النقطة المفضلة - لا وجود لأية نقطة مفضلة + لا يوجد نقاط مفضلة تعديل المفضلة حذف المفضلة إزالة النقطة المفضلة \'%s\' ؟ @@ -3536,7 +3536,7 @@ مربع دائرة تحديد الشكل - تحديد المجموعة + تحديد مجموعة إضافة وصف حذف الوصف زر لإظهار طبقة التضاريس أو إخفائها على الخريطة. @@ -4119,4 +4119,17 @@ سيتم فقدان كافة البيانات غير المحفوظة. إظهار مربع حوار البدء حفظ ومتابعة + تخصيص خط المسار + خط المسار + سيستخدم خط الطريق %1$s المحدد في نمط الخريطة المحدد: %2$s. + حدد لونًا لوضع الخريطة: %1$s. + • تم نقل تحديثات OsmAnd Live إلى \"التنزيلات> التحديثات\" +\n +\n • يمكن تلوين المسارات الآن حسب الارتفاع، السرعة أو المنحدر. +\n +\n • خيار مضاف لتغيير مظهر خط مسار الملاحة +\n +\n • تحديث مربع حوار \"تسجيل الرحلة\" +\n +\n \ No newline at end of file diff --git a/OsmAnd/res/values-de/strings.xml b/OsmAnd/res/values-de/strings.xml index 4be82a75ce..1f6d915238 100644 --- a/OsmAnd/res/values-de/strings.xml +++ b/OsmAnd/res/values-de/strings.xml @@ -4054,4 +4054,16 @@ Alle nicht gespeicherten Daten gehen verloren. Startdialog anzeigen Wenn deaktiviert, beginnt die Aufnahme direkt nach dem Tippen auf das Widget oder den Menüpunkt und überspringt den Bestätigungsdialog. + Routenlinie anpassen + Routenlinie + Legen Sie die Farbe für den Kartenmodus fest: %1$s. + • OsmAnd Live-Updates nach \"Downloads > Updates\" verschoben +\n +\n• Tracks können nun nach Höhe, Geschwindigkeit oder Steigung eingefärbt werden. +\n +\n• Option zugefügt, um das Aussehen der Navigationsrouten-Linie zu ändern +\n +\n• Dialog \"Streckenaufzeichnung\" aktualisiert +\n +\n \ No newline at end of file diff --git a/OsmAnd/res/values-eo/phrases.xml b/OsmAnd/res/values-eo/phrases.xml index 51c2b11059..36abea3e77 100644 --- a/OsmAnd/res/values-eo/phrases.xml +++ b/OsmAnd/res/values-eo/phrases.xml @@ -3894,4 +3894,5 @@ Loka referenco Oficejo de geodeziisto Konferenca centro + Loko por kampadveturilo/tendo \ No newline at end of file diff --git a/OsmAnd/res/values-eo/strings.xml b/OsmAnd/res/values-eo/strings.xml index 0eb1180e72..1063075114 100644 --- a/OsmAnd/res/values-eo/strings.xml +++ b/OsmAnd/res/values-eo/strings.xml @@ -4050,4 +4050,17 @@ Konservi kaj daŭrigi Se malŝaltita, registrado komenciĝos tuj post frapeto sur la fenestraĵo aŭ menuero, preterlasante la konfirman dialogon. Montri komencan dialogon + Elektu koloron por map‑reĝimo: %1$s. + Alĝustigi linion de kurso + Linio de kurso + Por la linio de kurso estos uzata %1$s difinita por la map‑stilo: %2$s. + • ĝisdatigoj OsmAnd Live movitaj al “elŝutoj” → “ĝisdatigoj” +\n +\n• eblo kolorigi spurojn laŭ altitudo, rapido aŭ dekliveco +\n +\n• eblo ŝanĝi aspekton de linio de la navigata kurso +\n +\n• plibonigita ekrano “registri spuron” +\n +\n \ No newline at end of file diff --git a/OsmAnd/res/values-es-rAR/phrases.xml b/OsmAnd/res/values-es-rAR/phrases.xml index 7d21230273..0c361ffa33 100644 --- a/OsmAnd/res/values-es-rAR/phrases.xml +++ b/OsmAnd/res/values-es-rAR/phrases.xml @@ -3894,4 +3894,5 @@ Referencia local Geodésico Centro de conferencias + Parcela de campamento \ No newline at end of file diff --git a/OsmAnd/res/values-es-rAR/strings.xml b/OsmAnd/res/values-es-rAR/strings.xml index 720dccf045..177cc6c449 100644 --- a/OsmAnd/res/values-es-rAR/strings.xml +++ b/OsmAnd/res/values-es-rAR/strings.xml @@ -1394,7 +1394,7 @@ Descargando lista de regiones disponibles… No se pudo obtener la lista de regiones desde https://osmand.net. Punto favorito editado - Sin puntos de Favoritos + No existen puntos favoritos Reemplazar Mostrar ruta Iniciar la guía @@ -4052,4 +4052,17 @@ Se perderán todos los datos no guardados. Mostrar el diálogo inicial Si se desactiva, la grabación será iniciada al pulsar el widget o el elemento del menú, saltándose el diálogo de confirmación. + Personalizar línea de ruta + Línea de ruta + La línea de la ruta sería el uso %1$s especificado en el estilo de mapa seleccionado: %2$s. + Define el color para el modo de mapa: %1$s. + • Las actualizaciones de OsmAnd Live se movieron a «Descargas > Actualizaciones» +\n +\n • Las trazas ahora se pueden colorear por altitud, velocidad o pendiente. +\n +\n • Se añade opción para cambiar la apariencia de la línea de ruta de navegación +\n +\n • Se actualiza el cuadro de diálogo «Grabación de viaje» +\n +\n \ No newline at end of file diff --git a/OsmAnd/res/values-fa/strings.xml b/OsmAnd/res/values-fa/strings.xml index 5196513604..c7a92d5c8a 100644 --- a/OsmAnd/res/values-fa/strings.xml +++ b/OsmAnd/res/values-fa/strings.xml @@ -594,8 +594,8 @@ ویرایش OpenStreetMap رد جاری را همین حالا در قالب فایل GPX ذخیره کن. ذخیرهٔ رد جاری - بازهٔ زمانی ثبت هنگام ناوبری - مشخص کنید برای ضبط رد در هنگام ناوبری، فاصلهٔ زمانی ردنگاری چقدر باشد + تناوب ردنگاری هنگام ناوبری + مشخص کنید برای ضبط رد در هنگام ناوبری، تناوب ردنگاری چقدر باشد هنگام ناوبری، یک رد GPX به‌صورت خودکار ضبط و در پوشهٔ ردها (tracks) ذخیره می‌شود. تنظیمات لازم برای ارتباط با (OpenStreetMap.org (OSM را مشخص کنید. زبان را مشخص و داده را دانلود/دوباره بار کنید. @@ -615,7 +615,7 @@ قفل قفل قفل‌گشایی - تنظیم فواصل بیدارباش: + تنظیم تناوب بیدارباش: سرعت شبیه‌سازی مسیر: کل حافظهٔ native اختصاص‌یافته به برنامه %1$s مگابایت (دالویک %2$s مگابایت، بقیه %3$s مگابایت). \nحافظهٔ proportional ‏%4$s مگابایت (حد اندروید %5$s مگابایت، دالویک %6$s مگابایت). @@ -680,7 +680,7 @@ \n {1} مگابایت (از {2} مگابایت) استفاده خواهد شد. پوستهٔ شفاف کتابخانهٔ native بر روی این دستگاه پشتیبانی نمی‌شود. - مقداردهی اولیهٔ کتابخانهٔ native… + در حال مقداردهی اولیهٔ کتابخانهٔ native… نمای نقشه خودکار-مرکز مدت‌زمانی که طول می‌کشد تا نقشه با موقعیت کنونی همگام شود. ناوبری فقط با حالت خودکار-مرکز @@ -706,7 +706,7 @@ آیا از حذف %1$d علاقه‌مندی و %2$d گروه علاقه‌مندی مطمئن هستید؟ نقشه پایهٔ جهان را دانلود کنید تا نمایی از سراسر جهان در زوم‌های کم داشته باشید. نسخهٔ محلی - گشودن دادهٔ جدید… + در حال گشودن دادهٔ جدید… ناوبری آنلاین به‌طور آفلاین کار نمی‌کند. داده وجود ندارد برعکس‌کردن جهت رد @@ -744,7 +744,7 @@ هم‌رسانی مکان در حال نمایه‌کردن نشانی… در حال نمایه‌کردن نقشه… - نمایه‌کردن POI‏… + در حال نمایه‌کردن نقاط توجه… در حال نمایه‌کردن حمل‌ونقل… خطای ورودی/خروجی km @@ -787,8 +787,8 @@ در حال تبدیل اسامی محلی/انگلیسی… ناموفق اجازهٔ دسترسی احراز نشد - بستن بستهٔ تغییر… - بازکردن بستهٔ تغییر… + در حال بستن بستهٔ تغییر… + در حال بازکردن بستهٔ تغییر… دریافت لیست مناطق از https://osmand.net ناموفق بود. در حال دانلود لیست مناطق موجود… دادهٔ آفلاینِ {0} از قبل وجود دارد ({1}). آن را روزآمد می‌کنید ({2})؟ @@ -835,8 +835,8 @@ کارت حافظه در حالت فقط‌خواندنی است. \nفقط می‌توانید نقشه‌هایی را ببینید که قبلاً دریافت شده‌اند و نمی‌توانید مناطق جدیدی را دانلود کنید. خدمات ناوبری OsmAnd - بازهٔ بیدارباش سرویس پس‌زمینه: - بازهٔ زمانی بیدارباش GPS + تناوب بیدارباش برای سرویس پس‌زمینه: + تناوب بیدارباش GPS شیوهٔ موقعیت‌یابی سرویس پس‌زمینه: ارائه‌دهندهٔ موقعیت زمانی که صفحه خاموش است موقعیت شما را ردگیری می‌کند. @@ -913,8 +913,8 @@ لایه‌های شب‌نما یادداشت‌ها یا POIهای OSMای ذخیره‌شده روی دستگاه مشاهده و مدیریت POIها یا یادداشت‌های OSMای که روی دستگاه شما قرار دارند. - بازهٔ زمانی ردیابی آنلاین را مشخص کنید. - بازهٔ زمانی ردیابی آنلاین + تناوب ردیابی آنلاین را مشخص کنید. + تناوب ردیابی آنلاین برنامهٔ OsmAnd (مخفف OSM Automated Navigation Directions به‌معنی راهنمای خودکار ناوبری بر پایهٔ OSM) یک نرم‌افزار ناوبری متن‌باز است که به گسترهٔ وسیعی از دادهٔ جهانی OSM دسترسی دارد. \n \nتمام دادهٔ نقشه (بُرداری یا کاشی) را می‌توانید برای استفادهٔ آفلاین روی کارت حافظه ذخیره کنید. @@ -1198,7 +1198,7 @@ هم‌رسانی مسیر در قالب فایل GPX مسیر از طریق OsmAnd هم‌رسانی شد دستی (روی فِلِش بزنید) - فرمان‌های ناوبری در بازه‌های منظم بازگو می‌شود. + فرمان‌های ناوبری در فاصله‌های زمانی منظم بازگو می‌شود. تکرار فرمان‌های ناوبری قالب نامعتبر: %s اعلام رسیدن @@ -1258,7 +1258,7 @@ نگه‌داشتن توقف فعال‌کردن حالت پس‌زمینهٔ GPS - بازهٔ بیدارباش GPS + تناوب بیدارباش GPS حالت پس‌زمینهٔ GPS متوقف شود؟ زبان ارجح برای نقشه سواحیلی @@ -1306,9 +1306,9 @@ کیفیت راه سطح راه مسیرهای دوچرخه - بازهٔ زمانی ثبت (سراسری) + تناوب ردنگاری عمومی می‌توانید با استفاده از ابزارک ضبط GPX روی نقشه، ردنگاری در فایل GPX را خاموش یا روشن کنید. - بازهٔ زمانی ثبت + تناوب ردنگاری این افزونه یک ابزارک ضبط GPX روی نقشه اضافه می‌کند و با انتخاب آن می‌توانید رد خود را ضبط و در قالب فایل GPX ذخیره کنید. همچنین این امکان را فراهم می‌کند که هنگام مسیریابی، به‌طور خودکار این کار برای شما انجام شود. \n \nردهای ضبط‌شده را با دوستان یا برای استفاده در مشارکت‌های OSM هم‌رسانی کنید. @@ -1633,7 +1633,7 @@ صرف‌نظر از دانلود نقشه‌ها نقشهٔ آفلاینی نصب نکرده‌اید. نقشه‌ای را از لیست انتخاب نمایید یا بعداً از طریق «منو ← %1$s»، آن را دانلود کنید. منطقهٔ دیگری را انتخاب کنید - جست‌وجوی نقشه‌ها… + در حال جست‌وجوی نقشه‌ها… اجازه دهید برنامه موقعیت شما را پیدا کند تا نقشهٔ آنجا را برای دانلود پیشنهاد بدهد. موقعیت پیدا نشد اتصال اینترنت برقرار نیست @@ -1689,7 +1689,7 @@ اعلام هوشمند خودکار فقط اگر جهتم از سمت نقطهٔ مقصد منحرف شد، اعلام شود. بازهٔ زمانی اعلام خودکار - بازهٔ زمانی کمینی بین اعلام پیام‌ها. + تناوب زمانی کمینی بین اعلام پیام‌ها. رنگ پیش‌فرض انتخاب دسته نام @@ -2375,7 +2375,7 @@ درون‌برد فایل GPX ورود فوری مختصات نمایش خطوط جهت‌دار - به نمایشش روی نقشه ادامه بده + روی نقشه نمایان بماند ذخیره به‌عنوان نقاط مسیر نقطهٔ مسیر میزان اُفت‌وخیز ارتفاع را انتخاب کنید @@ -2824,7 +2824,7 @@ پیاده طول مسیر نقاط توجه (POI) - محاسبهٔ مسیر… + در حال محاسبهٔ مسیر… حمل‌ونقل عمومی جاده‌ای که می‌خواهید هنگام ناوبری از آن بپرهیزید، روی نقشه یا از لیست زیر انتخاب کنید: در امتداد مسیر نشان بده @@ -2947,7 +2947,7 @@ محاسبهٔ مسیر پیاده نوع حمل‌ونقل ارسال گزارش - جست‌وجوی GPS‏ + در حال جست‌وجوی GPS‏ ابزارک مختصات لطفاً در گوگل‌پلی دربارهٔ کار ما نظر و امتیاز بدهید. سیاست حریم خصوصی @@ -3374,7 +3374,7 @@ اثرات جانبی: بازه‌های توقف یا اصلاً ضبط نخواهند شد یا فقط یک نقطه به‌ازای هر کدام ضبط می‌شود. جابه‌جایی‌های واقعیِ کوچک احتمالاً حذف می‌شود. فایل نهایی اطلاعات کمتری برای پس‌پردازش خواهد داشت و همچنین با حذف نقاط زائد در هنگام ضبط و حفظ بالقوهٔ نقاط غیرواقعی (ناشی از گیرایی ضعیف یا اثرات چیپست GPS) اطلاعات آماریِ فایل بدتر می‌شود. توصیه: اگر به جزئیات بیشتر از ۵ متر نیاز ندارید و در زمان توقف نمی‌خواهید داده ضبط کنید، تنظیم حدود ۵ متر احتمالاً خوب جواب بدهد. مدت حافظهٔ تأخیری - بازهٔ زمانی ردیابی + تناوب ردیابی نشانی وب نشانی اینترنتی را با این پارامترها تعریف کنید: \nlat={0}, lon={1}, timestamp={2}, hdop={3}, altitude={4}, speed={5}, bearing={6} @@ -3813,7 +3813,7 @@ %s فایل رد انتخاب شده است ضبط هنگامی که برنامه را از طریق برنامه‌های اخیر ببندید ضبط رد به‌طور موقت می‌ایستد. (نشانگر اجرای پس‌زمینه از نوار اعلان حذف می‌شود.) - بازهٔ زمانی برای ضبط رد را انتخاب کنید (که از طریق ابزارک ضبط سفر روی نقشه روشن می‌شود). + تناوب ردنگاری عمومی را انتخاب کنید (که از طریق ابزارک ضبط سفر روی نقشه روشن می‌شود). نگه‌داشتن ضبط سفر ازسرگیری ضبط سفر اسکیت این‌لاین @@ -4061,7 +4061,7 @@ آمادگی خروج از مسیر رسیدن به مقصد - بازه‌های زمانی و مسافتی + تناوب زمانی و مسافتی زمان اعلام پیام‌های گفتاری به نوع پیام، سرعت ناوبری جاری و سرعت ناوبری پیش‌فرض بستگی دارد. زمان اعلام پیام شروع دوباره @@ -4073,4 +4073,18 @@ نمایش کادر آغاز اگر غیرفعال باشد، بلافاصله پس از لمس ابزارک یا آیتم منو، ضبط آغاز می‌شود و کادر تأیید ظاهر نمی‌شود. تمام دادهٔ ذخیره‌نشده از دست خواهد رفت. + برای خط مسیر از %1$s استفاده می‌شود که در سبک انتخاب‌شده برای نقشه (%2$s) مشخص شده است. + رنگ حالت نقشهٔ %1$s را مشخص کنید. + تناوب ردنگاری مشخص می‌کند OsmAnd هر چند وقت یک بار دادهٔ مربوط به موقعیت جاری را درخواست کند. + سفارشی‌سازی خط مسیر + خط مسیر + • روزآمدسازه‌های OsmAnd Live منتقل شد به Downloads ‏> Updates +\n +\n• اکنون می‌توانید رنگ‌بندی ردها را بر اساس ارتفاع، سرعت یا شیب انجام دهید. +\n +\n• گزینه‌ای برای تغییر ظاهر خط مسیر هنگام ناوبری اضافه شد. +\n +\n• کادر «ضبط سفر» روزآمد شد. +\n +\n \ No newline at end of file diff --git a/OsmAnd/res/values-fr/strings.xml b/OsmAnd/res/values-fr/strings.xml index 5dc5f5fc88..89e6c2730b 100644 --- a/OsmAnd/res/values-fr/strings.xml +++ b/OsmAnd/res/values-fr/strings.xml @@ -715,7 +715,7 @@ Enregistrez l\'emplacement de stationnement de votre véhicule et, si besoin, mesurez la durée de stationnement restante. \nCes informations sont visibles sur le Tableau de bord comme sur la carte. Un rappel peut même être ajouté à votre calendrier. Stationnement - Marquer comme stationnement + Stationnement Supprimer l\'emplacement de stationnement Point de départ trop éloigné de la route la plus proche. Lieu partagé @@ -1217,7 +1217,7 @@ \nPoints de passage %2$s %1$s \nPoints %1$s \nTrace %2$s - Itinéraire + Point de passage Heure actuelle Chargement de %1$s… Enregistrer comme groupe de favoris @@ -3722,7 +3722,7 @@ Saisissez l\'adresse Ajouter à un fichier de trace Ajouter des fichiers de trace - Ajouter un point de passage + Ajouter une étape Ajouter un point de passage à la trace Début de la trace Déplacer sur les routes @@ -4040,4 +4040,17 @@ Toutes les données non enregistrées seront perdues. Afficher la boîte de dialogue de démarrage Si désactivé, l\'enregistrement débutera dès appui sur le gadget ou dans le menu (sans demande de confirmation). + - Les mises à jour d\'OsmAnd Live ont été déplacées vers \"Téléchargements > Mises à jour\" +\n +\n - Les traces peuvent maintenant être colorées par altitude, vitesse ou pente. +\n +\n - Ajout d\'une option pour modifier l\'apparence de la ligne d\'itinéraire en navigation +\n +\n - Mise à jour de la fenêtre \"Enregistrement du trajet\". +\n +\n + La ligne d\'itinéraire utilisera le %1$s pour le style de carte : %2$s. + Personnaliser la ligne d\'itinéraire + Ligne d\'itinéraire + Définissez une couleur pour le mode de carte : %1$s. \ No newline at end of file diff --git a/OsmAnd/res/values-hu/phrases.xml b/OsmAnd/res/values-hu/phrases.xml index 8176745fa7..525e77d214 100644 --- a/OsmAnd/res/values-hu/phrases.xml +++ b/OsmAnd/res/values-hu/phrases.xml @@ -270,7 +270,7 @@ Búvárfelszerelés Barkácsáruház Horgászfelszerelés - Képkeretbolt + Képkeretező Bútorbolt Egyéb bolt Üveges @@ -3892,4 +3892,5 @@ Vágány / kocsiállás Földmérő Konferencia-központ + Sátorhely \ No newline at end of file diff --git a/OsmAnd/res/values-hu/strings.xml b/OsmAnd/res/values-hu/strings.xml index e95128dd82..9796e64f4c 100644 --- a/OsmAnd/res/values-hu/strings.xml +++ b/OsmAnd/res/values-hu/strings.xml @@ -4044,4 +4044,16 @@ Minden nem mentett adat törlődni fog. Kezdő párbeszéd megjelenítése Ha le van tiltva, akkor a felvétel közvetlenül a widget vagy a menüelem megérintése után elindul, kihagyva a megerősítő párbeszédpanelt. + • Az OsmAnd Live frissítések átköltöztek a „Letöltések> Frissítések” helyre +\n +\n• A nyomvonalak színezhetők magasság, sebesség vagy lejtés szerint. +\n +\n• Módoítható a navigációs útvonal megjelenése +\n +\n• Frissült az „Utazás felvétele” párbeszédpanel +\n +\n + Útvonal + Útvonal testreszabása + Szín meghatározása a következő térképmódhoz: %1$s. \ No newline at end of file diff --git a/OsmAnd/res/values-it/strings.xml b/OsmAnd/res/values-it/strings.xml index 240b7e3156..56bc8eff8e 100644 --- a/OsmAnd/res/values-it/strings.xml +++ b/OsmAnd/res/values-it/strings.xml @@ -2070,7 +2070,7 @@ Spessore linee isoipse Spessore linee isoipse Acqua - Non visualizzare l\'acqua + Nascondi l\'acqua Densità linee isoipse Azione rapida Azione %d diff --git a/OsmAnd/res/values-iw/strings.xml b/OsmAnd/res/values-iw/strings.xml index c320b44aa6..456c757f3c 100644 --- a/OsmAnd/res/values-iw/strings.xml +++ b/OsmAnd/res/values-iw/strings.xml @@ -4053,4 +4053,16 @@ כל הנתונים שלא נשמרו יאבדו. להציג חלונית פתיחה אם האפשרות מושבתת, ההקלטה תתחיל מיד לאחר הנגיעה בווידג׳ט או בפריט שבתפריט תוך דילוג על חלונית האישור. + להתאים קו מסלול אישית + קו מסלול + לציין צבע למצב מפה: %1$s. + • העדכונים החיים של OsmAnd הועברו אל „הורדות > עדכונים” +\n +\n • אפשר לצבוע מסלולים לפי גובה, מהירות או שיפוע. +\n +\n • נוספה אפשרות לשנות את מראה קו מסלול הניווט +\n +\n • החלונית „הקלטת מסלול” עודכנה +\n +\n \ No newline at end of file diff --git a/OsmAnd/res/values-ja/strings.xml b/OsmAnd/res/values-ja/strings.xml index 9820354ce7..8e969c342f 100644 --- a/OsmAnd/res/values-ja/strings.xml +++ b/OsmAnd/res/values-ja/strings.xml @@ -556,7 +556,7 @@ POIの更新は利用できません %1$d ファイルが残っています あと%1$d個のファイルがダウンロード可能です フルバージョン - ルートをキャンセル + ルートを破棄しますか? ナビゲーションの停止 選択した目的地、経由地点を削除します、よろしいですか? Wi-Fiに接続されていません。現在の接続を利用してインターネットからダウンロードを行いますか? @@ -1941,7 +1941,7 @@ POIの更新は利用できません 進行方向を振動で示して案内します。 OsmAnd Live更新でのナビゲーションを有効化します。 OsmAnd Liveナビゲーション - 目的地が設定されていません + アクセシビリティプラグイン: ターゲットが設定されていません 指定の道を外れた後にルート再計算を行わない 反対方向へ向かった場合ルートを変更しない ルートと反対方向に移動しても、自動的にルートの再計算をしないようにします。 @@ -2294,13 +2294,20 @@ POIの更新は利用できません  • GPX経路情報をアプリから直接OSMにアップロードできます  • POIを追加してOSMに直接アップロードできます(オフラインの場合は後で行うことができます) " - OsmAndはオープンソースで積極的に開発されています。バグを報告したり、翻訳を改善したり、新しい機能をコーディングすることで、誰もがアプリケーションの開発に貢献できます。このような開発者とユーザーとのやりとりによって、プロジェクトは継続的に改善されています。プロジェクトの進捗状況は、資金調達と新しい機能のテストに大きく左右されます。 -\n -\n地図のおおよそのエリアとその品質(※注 *の数が多いほど充実しています): -\n • 西ヨーロッパ: ****  • 東ヨーロッパ: ***  • ロシア: ***  • 北アメリカ: ***  • 南アメリカ: **  • アジア: **  • 日本、韓国: ***  • 中東: **  • アフリカ: **  • 南極: * -\n -\n 世界中のほとんどの国の地図がダウンロード可能です! -\n フランス、ドイツ、メキシコ、イギリス、スペイン、オランダ、アメリカ、ロシア、ブラジルなどの国の便利なナビゲーターを入手しましょう。 + OsmAndはオープンソースで現在も積極的に開発されています。バグを報告したり、翻訳を改善したり、新機能をコーディングしたりすることで、誰もがアプリの開発に貢献できます。またこのプロジェクトは、新機能のコーディングとテストに要する金銭面での貢献によっても支えられています。 +\n地図のおおよその範囲と品質(※注 *の数が多いほど内容が充実しています): +\n• 西ヨーロッパ:**** +\n• 東ヨーロッパ:*** +\n• ロシア:*** +\n• 北アメリカ:*** +\n• 南アメリカ:** +\n• アジア:** +\n• 日本、韓国:*** +\n• 中東:** +\n• アフリカ:** +\n• 南極大陸:* +\n世界中のほとんどの国がダウンロード可能です! +\nフランス、ドイツ、メキシコ、イギリス、スペイン、オランダ、アメリカ、ロシア、ブラジルなど、各国において頼りとなるナビゲーターを手に入れましょう。 OsmAnd(OSM Automated Navigation Directions~自動ナビ案内)は、『無償利用可、ワールドワイド対応、高品質のOSM』データを使用した、地図&ナビゲーションアプリです。 \n音声と視覚による案内、POI(Point Of Interest~興味的地点)表示、GPX経路の作成と管理、等高線を使った地形情報(要プラグイン)、ドライブ、サイクリング、徒歩移動各移動手段別のモード、OSMの編集等々に対応。 \n OsmAnd+は有料のアプリケーションです。購入することで同プロジェクトの資金、新機能開発への援助となり、最新アップデートも利用可能になります。 @@ -2347,7 +2354,7 @@ POIの更新は利用できません マップ画面でのOSMメモの表示/非表示を切り替えるボタンです。 距離順で並べ替え お気に入りで検索 - 標高の高低をグラデーション表示するには、陰影起伏図(Hillshade Overlay)マップのダウンロードが必要です。 + 標高の高低をグラデーション表示するには、\'Hillshade(陰影起伏図)\'マップのダウンロードが必要です。 等高線プラグイン『Contour lines plugin — OsmAnd』をインストールすることで、陰影起伏図(高低差を陰影で表したもの)をマップ上に表示できます。 ズームレベルを非表示 この地域用の等高線マップをダウンロードします。 @@ -2369,20 +2376,20 @@ POIの更新は利用できません 検索範囲を拡大 再検索 配色 - " 地図のおおよそのエリアとその品質(※注 *の数が多いほど充実しています): -\n • 西ヨーロッパ: **** -\n • 東ヨーロッパ: *** -\n • ロシア: *** -\n • 北米: *** -\n • 南アメリカ: ** -\n • アジア: ** -\n • 日本、韓国: *** -\n • 中東: ** -\n • アフリカ: ** -\n • 南極: * -\n -\n 世界中のほとんどの国の地図がダウンロード可能です! -\n アフガニスタンからジンバブエ、オーストラリアからアメリカ、 アルゼンチン、ブラジル、カナダ、フランス、ドイツ、メキシコ、イギリス、スペイン、... " + 地図のおおよその範囲と品質:(※注 *の数が多いほど内容が充実しています): +\n• 西ヨーロッパ:**** +\n• 東ヨーロッパ:*** +\n• ロシア:*** +\n• 北アメリカ:*** +\n• 南アメリカ: ** +\n• アジア:** +\n• 日本、韓国:*** +\n• 中東:** +\n•アフリカ:** +\n•南極大陸:* +\n世界中のほとんどの国がダウンロード可能です! +\nアフガニスタンからジンバブエへ、オーストラリアからアメリカへも…またアルゼンチン、ブラジル、カナダ、フランス、ドイツ、メキシコ、イギリス、スペイン等々… +\n " 直接OSMに寄与 • データ、バグの報告ができます @@ -2812,7 +2819,7 @@ POIの更新は利用できません By OsmAnd プランと料金 月間 - 3ヶ月間 + 3ヶ月毎 年間 %1$s / 月 %1$.2f %2$s / 月 @@ -3080,9 +3087,9 @@ POIの更新は利用できません 経路%sを保存しました 一時的な制限の検討 『クイックアクション』のリストに最低ひとつは項目を設定する必要があります - アルペン/ダウンヒルスキー + アルペンスキーとダウンヒルスキー アルペンスキーまたはダウンヒルスキーに用の斜面(ゲレンデ)やスキーリフトへの道 - クロスカントリー/ノルディックスキー + クロスカントリースキーとノルディックスキー ノルディックスキーやクロスカントリースキー用コースやそれらに用いられる小道などです。 スキーツーリング スキーツーリング用ルートです。 @@ -3354,9 +3361,9 @@ POIの更新は利用できません ルート再計算 案内 ユーザー名とパスワード - これらの設定はアプリ全般に関するもので、すべてのプロファイルに適用されます + これらのプラグイン設定はアプリ全般に関するもので、すべてのプロファイルに適用されます OSM編集 - まだアップロードしていない編集やOSMバグを%1$sで表示できます。アップロードされた後にはOsmAndに表示されません。 + まだアップロードしていない編集やOSMバグを%1$sで表示できます。アップロードされた変更部分は表示されなくなります。 OSM ナビゲーションまたは移動中に表示されるアイコンです。 静止時に表示されるアイコンです。 @@ -3489,9 +3496,9 @@ POIの更新は利用できません 四角形 最小 マップ内公共交通機関の表示/非表示を切り替えるボタンです。 - POIの作成/編集 + POIの作成・編集 駐車位置 - お気に入りの追加/編集 + お気に入りの追加・編集 アイテム順序を初期状態に戻します 編集に戻る 選択したプロファイルを切り替えるボタンです。 @@ -3518,7 +3525,7 @@ POIの更新は利用できません %1$s / %2$s お支払いは、購入の確認後にGoogle Playアカウントに請求されます。 \n -\nサブスクリプションは更新日より前にキャンセルしない限り、自動的に更新されます。アカウントの更新期間(月/ 3ヶ月/年)に準じた更新日に請求されます。 +\nサブスクリプションは更新日前にキャンセルしない限り、自動的に延長されます。アカウントには更新日に更新期間(月/ 3ヶ月/年)に応じた金額が請求されます。 \n \nサブスクリプションの管理およびキャンセルはGoogle Playの設定からおこなえます。 POIのタイプで検索 @@ -3661,7 +3668,7 @@ POIの更新は利用できません 車高を入力してください。一部のルートにおいては、車高の高い車両では通行できない場合があります。 車両の重量を入力してください。一部のルートにおいては、大型車両では通行できない場合があります。 車の長さを入力してください。一部のルートにおいては、長い車両では通行できない場合があります。 - 常に + 常時 画面の制御 現在無効。 \'画面を表示する時間\'の下にある\'画面を常に表示\'への設定が必要です。 画面オフを維持 @@ -3725,9 +3732,9 @@ POIの更新は利用できません インラインスケート 最も近い目的地を削除 地点の名前を入力してください - ルート上の現在の目的地点が削除されます。目的地に着いた場合はナビは停止します。 + ルート上の次の目的地を削除します。それが最終目的地の場合は、ナビゲーションが停止します。 Wikipediaマップのダウンロード - Wikipediaから興味のあるポイントに関する情報を取得します。それがあなたのモバイルオフラインガイドになります - Wikipediaプラグインを有効にして、周囲の名所に関する記事を楽しんでください。 + 場所や目的地に関する記事が掲載されたWikipediaのオフラインポケットガイドを活用し、ランドマークや観光名所の詳しい情報を入手しましょう。 追加した地点はマップ上に表示されません。選択したグループは非表示になっているため、\"%s\"から見つけることができます。 新しい経路ファイルとして保存 経路ファイルを追加 @@ -3762,8 +3769,8 @@ POIの更新は利用できません 次年度以降 %1$s 初%2$s度 %1$s 初%2$s度 %1$s - ルート記録を一時停止 - ルート記録を続行 + 旅程の記録を一時停止 + 旅程の記録を続行 システムのデフォルト 以後のすべてのセグメントは、選択したプロファイルを使用して再計算されます。 以後のすべてのセグメント @@ -3776,7 +3783,7 @@ POIの更新は利用できません 繰り返し • ルート計画機能を更新 : セグメントごとに異なるナビゲーションタイプを使用し、経路として反映することが可能に \n -\n• 経路の新しい外観オプション : 色、太さ、表示方向矢印、開始/終了アイコン選択 +\n• 経路の新しい外観オプション : 色、太さ、表示方向矢印、開始と終了アイコンの選択 \n \n• 自転車ノードの視認性を向上 \n @@ -3794,13 +3801,13 @@ POIの更新は利用できません %s個の経路ファイルが選択されました ファイル名 (最近使用したアプリを介して) OsmAndを終了すると、GPXの記録が一時停止されます(バックグラウンドサービスインジケーターがAndroid通知バーから消えます。) - 一般的な経路記録の記録間隔を定義します(マップ画面の\'GPX\'ボタンを使用) + 一般的な経路記録の記録間隔を指定します(マップウィジェットの\'旅程の記録\'ボタンでONにします) 保存されました 最終更新日 名称: 降順(Z-A) 名称: 昇順(A-Z) - Java(セーフモード)での公共交通機関ルーティング計算に切り替えます - OsmEdit機能を利用するには、OAuthでのログインが必要です。 + Java(セーフモード)での公共交通機関ルート計算に切り替えます + OsmEdit機能を利用するには、OAuth方式でのログインが必要です。 OAuthでログイン OpenStreetMapOAuthトークンを消去する ログアウトしました @@ -3832,20 +3839,20 @@ POIの更新は利用できません ユーザー名とパスワードを使用する ログイン サブスクリプションの管理 - サブスクリプションに際し問題が発生しています。ボタンをタップしてGooglePlayサブスクリプション設定に移動し、お支払い方法を確認してください。 + ボタンをタップしてGoogle Playで支払い方法を設定し、サブスクリプションを修正します。 OsmAndLiveサブスクリプションの有効期限が切れました OsmAndLiveサブスクリプションが一時停止されました OsmAndLiveサブスクリプションは保留中です マーカーの履歴 GPXファイルをOpenStreetMapに送信 タグはカンマで区切って入力してください。 - パブリック状態は、経路がGPS経路およびGPS経路リストに公開を目的として表示されることを示唆しています。 APIを介して提供されるデータは、経路ページを参照していません。経路ポイントのタイムスタンプは、パブリックGPS APIからは入手できず、各地点は時系列で並べられることはありません。ただし他のユーザーは、公開経路リストとその中のタイムスタンプから生の経路データをダウンロードすることができます。 - 識別可能状態は、経路がGPS経路およびパブリックGPS経路リストに表示されることを示唆しています。他のユーザーは現在の経路をダウンロードして、ユーザー名とリンクできます。 trackpoints APIを介して配信されるデータは、元の経路追跡ページを参照します。経路ポイントのタイムスタンプは、パブリックGPSAPIを介して入手できます。 - プライベート状態は、経路がパブリックリストに表示されないことを示唆しています。経路ポイントは、タイムスタンプなしでパブリックGPS APIを介して引き続き利用できますが、時系列で並べられることはありません。 - 追跡可能状態にある場合は経路が公開リストに表示されないことを示唆していますが、経路ポイントは自体はタイムスタンプ付きの公開GPSAPIを介して引き続き利用できます。他のユーザーからはあなたに直接関連付けることはできず、自身の処理済み経路ポイントのみをダウンロードできます。 + \"公開 \"状態は、追跡機能にてユーザーのGPS追跡、公開GPS追跡リスト、および生データのタイムスタンプ付き公開追跡リストに公開されることを意味します。APIを介して提供されるデータはユーザーの追跡ページを参照しません。追跡ポイントのタイムスタンプはパブリックGPS APIでは利用できず、また追跡ポイントは時系列に並んでいません。 + \"識別可能\"状態は、経路がGPS経路およびパブリックGPS経路リストに表示されることを示唆しています。他のユーザーは現在の経路をダウンロードして、ユーザー名とリンクできます。経路ポイント APIを介して配信されるデータは、元の経路追跡ページを参照します。経路ポイントのタイムスタンプは、公開されているGPS APIを通じて入手できます。 + \"プライベート\"状態は、経路が公開リストに表示されないことを示唆しています。経路ポイントはタイムスタンプなしでパブリックGPS APIを介して引き続き利用できますが、時系列で並べられることはありません。 + \"追跡可能\"状態は、経路が公開リストに表示されることはありませんが、公開されているGPS APIからのダウンロードを通じて、タイムスタンプ付きの経路ポイント(個人と直接関連付けることはできません)を処理出来ることを意味します。 アカウント - ルートが再計算されるのを待ちます。 -\nグラフは再計算後に利用できるようになります。 + しばらくお待ち下さい。 +\nグラフはルートが再計算された後に利用できるようになります。 OSM通知/ POI / GPXのアップロードをテストする場合、openstreetmap.orgではなくdev.openstreetmap.orgを使用するよう切り替えます。 dev.openstreetmap.orgを使用 次々回案内(中距離) @@ -4034,4 +4041,41 @@ POIの更新は利用できません フォルダーの変更 開始ダイアログを表示 無効にした場合、ウィジェットまたはメニュー項目をタップするとすぐに記録が始まり、確認のダイアログは表示されません。 + ネイティブ公共交通機関モードの展開 + グラフ + %1$sデータは道路でのみ利用可能です。グラフを表示するには、\"地点間ルート\"を使用してルートを計算する必要があります。 + ギャップ + 音声アナウンスの間隔 + ルートラインのカスタマイズ + ルートライン + ルートライン指定は%1$s、選択したマップスタイルは:%2$s + マップモードの色指定: %1$s + • OsmAndLiveのアップデートは\"ダウンロード\">\"更新\"に移動 +\n +\n• 経路は、高度、速度、または傾斜によって色付けされる可能性あり +\n +\n• ナビゲーションルートラインの外観を変更するオプションが追加 +\n +\n• \"旅程記録\"ダイアログを更新 +\n +\n + 開始・終了アイコン + \'等高線(Contour lines)\'をご購入いただきありがとうございます + サブスクリプションは選択した期間に応じて課金されます。 AppGalleryからいつでもキャンセルできます。 + お支払いは、購入の確認後にAppGalleryアカウントへ請求されます。 +\n +\nサブスクリプションは更新日前にキャンセルされない限り自動的に延長されます。アカウントには更新日に更新期間(月/ 3ヶ月/年)に応じた金額が請求されます。 +\n +\nサブスクリプションの管理およびキャンセルはAppGalleryの設定からおこなえます。 + ドイツ語(話し言葉) + MGRS(Military Grid Reference System) + 新着情報 + スノーモービルレジャー用の専用道路や通路です。 + MGRS(Military Grid Reference System) + OsmAndは、UTM NATO形式類似のMGRSを使用します。 + 凡例 + %2$sの%1$s + 基本前進のみの車椅子 + 通常色 + 逆方向の場合 \ No newline at end of file diff --git a/OsmAnd/res/values-nb/strings.xml b/OsmAnd/res/values-nb/strings.xml index c902064e98..c0254ccc92 100644 --- a/OsmAnd/res/values-nb/strings.xml +++ b/OsmAnd/res/values-nb/strings.xml @@ -3941,4 +3941,8 @@ Ruting kan unngå kraftige oppoverbakker. Lagre og fortsett All ulagret data vil gå tapt. + Vis startdialog + Tilpass rutelinje + Rutelinje + Angi farge for kartmodus: %1$s. \ No newline at end of file diff --git a/OsmAnd/res/values-nl/strings.xml b/OsmAnd/res/values-nl/strings.xml index 07c4aef6f7..bad32e0a9e 100644 --- a/OsmAnd/res/values-nl/strings.xml +++ b/OsmAnd/res/values-nl/strings.xml @@ -2884,7 +2884,7 @@ Wissel tussen dag-/nachtmodus Oppervlakte Kwaliteit wegdek - Steilte + Helling U gebruikt {0} kaart geleverd door OsmAnd. Wilt u de \"OsmAnd full\" versie openen\? Open OsmAnd\? Eenheid hoeken diff --git a/OsmAnd/res/values-pt-rBR/phrases.xml b/OsmAnd/res/values-pt-rBR/phrases.xml index 560f405680..2b9315b6d7 100644 --- a/OsmAnd/res/values-pt-rBR/phrases.xml +++ b/OsmAnd/res/values-pt-rBR/phrases.xml @@ -3894,4 +3894,5 @@ Centro de conferências Geodesia Geodesia + Campo de acampamento \ No newline at end of file diff --git a/OsmAnd/res/values-pt-rBR/strings.xml b/OsmAnd/res/values-pt-rBR/strings.xml index 0c4060b65a..30546270a5 100644 --- a/OsmAnd/res/values-pt-rBR/strings.xml +++ b/OsmAnd/res/values-pt-rBR/strings.xml @@ -4045,4 +4045,17 @@ O intervalo de registro define o período de tempo no qual OsmAnd solicitará os dados de posição de localização atual. Mostrar caixa de diálogo inicial Se desativado, a gravação começará logo após tocar no widget ou item de menu, pulando a caixa de diálogo de confirmação. + Personalizar linha de rota + Linha de rota + A linha de rota seria usada %1$s especificado no estilo de mapa selecionado: %2$s. + Especifique a cor para o modo de mapa: %1$s. + "• As atualizações do OsmAnd Live foram movidas para \"Downloads > Atualizações\" +\n +\n • As trilhas agora podem ser coloridas por altitude, velocidade ou inclinação. +\n +\n • Adicionada opção para alterar a aparência da linha da rota de navegação +\n +\n • Caixa de diálogo \"Gravação de viagem\" atualizada +\n +\n" \ No newline at end of file diff --git a/OsmAnd/res/values-ru/phrases.xml b/OsmAnd/res/values-ru/phrases.xml index 7b2f28b784..5735fbfac4 100644 --- a/OsmAnd/res/values-ru/phrases.xml +++ b/OsmAnd/res/values-ru/phrases.xml @@ -3886,4 +3886,7 @@ Название трассы Лыжный трамплин По лесу: да + Туннель летучих мышей + Мост летучих мышей + Конференц-центр \ No newline at end of file diff --git a/OsmAnd/res/values-ru/strings.xml b/OsmAnd/res/values-ru/strings.xml index 8ed1ef86a1..ada07660d2 100644 --- a/OsmAnd/res/values-ru/strings.xml +++ b/OsmAnd/res/values-ru/strings.xml @@ -597,7 +597,7 @@ Чтение кешированных растровых карт… Недостаточно памяти для локальной карты «{0}» Версия локальной карты «{0}» не поддерживается - Офлайн-навигация OsmAnd — это экспериментальная функция, и она не работает на дистанциях больше 20 км. + Локальная навигация OsmAnd — это экспериментальная функция, и она не работает на дистанциях больше 20 км. \n \nНавигация временно переключена на онлайн-сервис CloudMade. Невозможно найти указанную папку. @@ -1528,7 +1528,7 @@ Линии метро Продолжить навигацию Приостановить навигацию - Отрисовка дорог cогласно шкале SAC. + Отрисовка дорог согласно шкале SAC. Отрисовка дорог согласно трассам OSMC. Раннее По умолчанию @@ -1580,7 +1580,10 @@ Размер текста Нажмите на любой элемент списка для просмотра более подробной информации, долго удерживайте, чтобы поместить в архив или удалить. Текущие данные на устройстве (%1$s свободно): Анонимный пользователь - Анонимному пользователю недоступно:\n- создание групп;\n- синхронизация групп и устройств с сервером;\n- управление группами и устройствами в личном кабинете. + Анонимному пользователю недоступно: +\n— создание групп; +\n— синхронизация групп и устройств с сервером; +\n— управление группами и устройствами в личном кабинете. Вы вошли как %1$s Маршруты Детали @@ -1894,7 +1897,7 @@ Пропустить OsmAnd Плагины - Офлайн-карты + Локальные карты \nи навигация Номер дома Тип лыжной трассы @@ -2385,7 +2388,7 @@ Дорога Показывать направляющие линии Тёмно-жёлтый - OsmAnd — это активно развивающийся проект с открытым исходным кодом. Благодаря открытости, каждый может внести свой вклад в приложение, сообщая об ошибках, совершенствуя перевод или разрабатывая новые возможности. Кроме того, проект финансируется за счет денежных взносов с целью разработки и тестирования новых функций. + OsmAnd — это активно развивающийся проект с открытым исходным кодом. Благодаря открытости, каждый может внести свой вклад в приложение, сообщая об ошибках, совершенствуя перевод или разрабатывая новые возможности. Кроме того, проект финансируется за счёт денежных взносов с целью разработки и тестирования новых функций. \nПримерное покрытие и качество карт: \n • Западная Европа: **** \n • Восточная Европа: *** @@ -2398,7 +2401,7 @@ \n • Африка: ** \n • Антарктида: \nДля скачивания доступно большинство стран мира! -\n Получите надежный навигатор в своей стране — будь то Франция, Германия, Мексика, Великобритания, Испания, Нидерланды, США, Россия, Бразилия или любая другая. +\n Получите надёжный навигатор в своей стране — будь то Франция, Германия, Мексика, Великобритания, Испания, Нидерланды, США, Россия, Бразилия или любая другая. Примерная карта покрытия и качество: \n • Западная Европа: **** \n • Восточная Европа: *** @@ -2519,11 +2522,11 @@ \n• Просмотр вашей скорости и высоты над уровнем моря \n• Функция записи GPX позволяет вам записывать ваше путешествие и делиться им \n• Через приложение вы можете включить отображение контурных линий и затемнение рельефа - Прогулки, пеший туризм, экскурсии -\n• Карта показывает пешеходные и треккинговые тропы -\n• Википедия на предпочитаемом вами языке может многое рассказать вам во время экскурсии по городу -\n• Остановки общественного транспорта (автобус, трамвай, поезд), включая названия маршрутов, помогут ориентироваться в новом городе -\n• GPS навигация в пешеходном режиме высчитывает маршрут, используя пешеходные тропы + Прогулки, пеший туризм, экскурсии +\n• Карта показывает пешеходные и трекинговые тропы +\n• Википедия на предпочитаемом вами языке может многое рассказать вам во время экскурсии по городу +\n• Остановки общественного транспорта (автобус, трамвай, поезд), включая названия маршрутов, помогут ориентироваться в новом городе +\n• GPS навигация в пешеходном режиме высчитывает маршрут, используя пешеходные тропы \n• Отправьте и следуйте по маршруту GPX или запишите и поделитесь своим собственным \n Внесение вклада в OSM @@ -2944,7 +2947,7 @@ Без паромов Исключить паромы Отправить журнал - К сожалению OsmAnd не смог найти подходящий для вас маршрут. + К сожалению, OsmAnd не смог найти подходящий для вас маршрут. Попробуйте пройти навигацию пешком. Попробуйте изменить настройки. Рассчитать маршрут ходьбы @@ -3772,7 +3775,7 @@ Все предыдущие сегменты будут пересчитаны с использованием выбранного профиля. Открыть сохранённый трек Остановка записи GPX при принудительном закрытии (через последние приложения). (Из панели уведомлений Android исчезнет значок фонового режима.) - сохранен + сохранён Добавьте хотя бы две точки. Повторить • Обновлённая функция планирования маршрута позволяет применять к сегментам разные режимы навигации и настраивать привязку к дорогам @@ -3794,7 +3797,7 @@ Имя: Я - А Имя: А - Я Значки старта и финиша - Спасибо за покупку \'Контурных линий\' + Спасибо за покупку «Контурных линий» Избегать пешеходных дорожек Избегать пешеходных дорожек Подписка оплачивается за выбранный период. Отмените её в AppGallery в любое время. @@ -3863,7 +3866,7 @@ Добавить в OpenPlaceReviews Добавить в Mapillary OsmAnd показывает фотографии из нескольких источников: -\nOpenPlaceReviews — фотогорафии POI; +\nOpenPlaceReviews — фотографии POI; \nMapillary — изображения улиц; \nWeb / Wikimedia — фотографии POI по данным OpenStreetMap. Ресурсы @@ -3885,7 +3888,7 @@ Каяк %1$s * %2$s Немецкий (повседневный) - Легкий самолет + Лёгкий самолёт Добавить новый сегмент Объединить сегменты Разделить до @@ -4008,7 +4011,7 @@ Обновления карт будут проверяться каждый день. Следующий раз %1$s в %2$s. Обновления карт будут проверяться каждый час. Следующий раз %1$s в %2$s. Удалить обновления - Вы уверены, что хотите удалить все live обновления для %s\? + Вы уверены, что хотите удалить все live-обновления для %s\? Покупки Показать/скрыть Природные границы @@ -4029,9 +4032,9 @@ Сохранить и остановить запись Запись трека остановлена Вы уверены, что хотите остановить запись\? -\nВсе несохраненные данные будут потеряны. +\nВсе несохранённые данные будут потеряны. На паузе - Позволяет избегать больших подъемов при навигации. + Позволяет избегать больших подъёмов при навигации. Переключатель для отображения или скрытия виджета координат. Выберите категорию или добавьте новую Копировать имя POI @@ -4044,8 +4047,8 @@ Выберите цель, чтобы проложить к ней короткий, быстрый или безопасный маршрут Удалить %1$d файлов\? Сохранить и продолжить - Все несохраненные данные будут потеряны. + Все несохранённые данные будут потеряны. Показывать начальный диалог - Если выключено, запись начнется сразу после нажатия на виджет или пункт меню, пропуская диалоговое окно подтверждения. + Если выключено, запись начнётся сразу после нажатия на виджет или пункт меню, пропуская диалоговое окно подтверждения. Интервал записи трека указывает период времени, через который OsmAnd будет запрашивать данные о текущем местоположении. \ No newline at end of file diff --git a/OsmAnd/res/values-sc/strings.xml b/OsmAnd/res/values-sc/strings.xml index f2fdc6c6d8..c11864da5a 100644 --- a/OsmAnd/res/values-sc/strings.xml +++ b/OsmAnd/res/values-sc/strings.xml @@ -844,7 +844,7 @@ Domo Amigos Logos - Àteru + Àteros Nùmene Categoria No, gràtzias @@ -4042,4 +4042,8 @@ Ischerta un\'àtera casta de coloratzione. Còmporas Distàntzia pro tocu + Sarva e sighi + Totu sos datos non sarvados s\'ant a pèrdere. + Ammustra su diàlogu de incumintzu + Dislinda unu colore pro sa modalidade pro sa mapa: %1$s. \ No newline at end of file diff --git a/OsmAnd/res/values-sk/strings.xml b/OsmAnd/res/values-sk/strings.xml index 6c12a3f3b0..4d855b855a 100644 --- a/OsmAnd/res/values-sk/strings.xml +++ b/OsmAnd/res/values-sk/strings.xml @@ -4045,4 +4045,17 @@ Všetky neuložené údaje budú stratené. Zobraziť úvodné okno Ak je vypnuté, záznam začne hneď po stlačení nástroja alebo položky v menu a preskočí okno nastavenia. + • Aktualizácie OsmAnd Live presunuté do \"Sťahovania > Aktualizácie\" +\n +\n • Stopy je teraz možné vyfarbiť podľa nadmorskej výšky, rýchlosti alebo sklonu svahu. +\n +\n • Pridaná možnosť zmeny vzhľadu navigačnej trasy +\n +\n • Vylepšené okno \"Záznam výletu\" +\n +\n + Upraviť navigačnú trasu + Navigačná trasa + Navigačná trasa použije %1$s, zadané na zvolenom štýle mapy: %2$s. + Zadajte farbu pre režim mapy: %1$s. \ No newline at end of file diff --git a/OsmAnd/res/values-sr/phrases.xml b/OsmAnd/res/values-sr/phrases.xml index f261e4e3f9..fae32d16dc 100644 --- a/OsmAnd/res/values-sr/phrases.xml +++ b/OsmAnd/res/values-sr/phrases.xml @@ -3863,4 +3863,8 @@ Сирена Медицинска сестра Мобилни агент за новац + Језеро + Река + Цепљење: Ковид 19 + Цепљење \ No newline at end of file diff --git a/OsmAnd/res/values-sr/strings.xml b/OsmAnd/res/values-sr/strings.xml index e23eae86cc..f3e3dd9e2b 100644 --- a/OsmAnd/res/values-sr/strings.xml +++ b/OsmAnd/res/values-sr/strings.xml @@ -1604,7 +1604,7 @@ Просек %1$d од %2$d Успон/Спуст - Време кретања + Време у покрету Макс/Мин Мин/Макс Туре @@ -3861,7 +3861,7 @@ Спорт Хитна помоћ Путовање - Треба да додате најмање две тачке + Додајте најмање две тачке Пријавите се на OpenStreetMap.org Пријавите се на OpenStreetMap.org Пријавите се помоћу ОпенСтритМап @@ -3979,4 +3979,13 @@ Сегмент %1$d Отпремљено %1$d од %2$d Одаберите измене за отпремање + Подврста + Возило + Бицикл + Аутомобил + Празно + Уреди путању + Преименуј путању + Сачувај и настави + Сви несачувани подаци ће бити изгубљени. \ No newline at end of file diff --git a/OsmAnd/res/values-sv/phrases.xml b/OsmAnd/res/values-sv/phrases.xml index e209110762..0a5b1b8c4d 100644 --- a/OsmAnd/res/values-sv/phrases.xml +++ b/OsmAnd/res/values-sv/phrases.xml @@ -2557,7 +2557,7 @@ Explosion: enhet Husnamn Plantskola - JA + Ja Tryckluft: nej Ja Ja @@ -3393,4 +3393,506 @@ Uttag: NEMA 5-20 Uttag: NEMA 5-15R: utgång Uttag: NEMA 5-15R: ström + Lägerplats + Lokal referens + Geolog + Konferenscenter + Stoppläge för det mobila biblioteket + Toppregister: nej + Toppregister: ja + Piststatus: stängd + Piststatus: öppen + Patrullerat: ja + I skogen: ja + Pistnamn + Skidhopp + Tunnel för fladdermöss + Fladdermusbro + Korsning för vilda djur + Badplats + Lavoir + Avfallsöverföringsstation + Viktbro + Ranger-station + Sjö + Flod + Brunn + Driven pump + Vattentank + Kran + Vattenverk + Rörbrunn + Vaccination: COVID19 + Vaccination + Livräddarbas + Siren + Sjuksköterska + Agent för mobila pengar + Subnationellt + Representationskontor + Kontor + Honorärkonsulat + Generalkonsulat + Konsulat + Konsulär byrå + Leds av en konsul + Bostad + Nuntiatur + Uppdrag + Intresseavdelning + Hög provision + Delegation + Filial + Leds av en ambassadör + Förbindelse + Ambassad + Radartorn + Parkeringsficka + Skjul + Tak + GPX-punkt + LNG + Nötbutik + Bikupa + Tidtabell + Realtid + Fördröjning + Hiss + Kvarter + Kommun + Ge låda + Vibration: nej + Pumpstatus: saknad stråle + Sugning + Trycksatt + Grundvatten + Rör + Nätverk för påfyllning av dricksvatten + Påfyllning av dricksvatten: nej + Hinder + Vattennivå: över medelvattennivå + Vattennivå: under medelvattennivå + Vattennivå: flytande + Vattennivå: översvämning + Vattennivå: överflödig + Vattennivå: täcker + Vattennivå: torr + Vattennivå: nedsäkt + Vattennivå: delvis nedsänkt + Felaktig + Primitiv + Kontrastfärgad + Endast tillåtet när du går + Internetåtkomst: kunder + Bås typ + Bås + Dykcenter + Jaktbas + Referensummer för pist + Bowlingcenter + Säkerhetsbutik + Fjällräddning + Paintball + Spökcykel + Antal utbrott + Inaktiv + Aktiv + Vilande + Utdöd + Senaste utbrottet + Lera + Lavakupol + Caldera + Maar + Sköld + Stratovulkan + Scoria + URL + Parkeringsplats + Grav + Kön: bladat + Kön: man + Kön: kvinna + Turistläger + Klostertyp: kontorister regelbundet + Klostertyp: eremitage + Klostertyp: kanoneri + Klostertyp: kloster + Klostertyp: kloster + Fotbad + Sjö + Flod + Termisk + Hammam + Patrullerad: nej + Avgångstavla: nej + Nej + Nej + Nej + Pil: nej + Nej + Nej + Nej + Ja + Ja + Ja + Ja + Ja + Ja + Ja + Ja + Ja + Ja + Onsen + Varm källa + Postbank + Girokort + Migros bank + Postfinanskort + Kontantuttag: utländska kort + Kontantuttag: minsta köp + Kontantuttagsavgift: nej + Kontantuttagsavgift: ja + Kontantuttag: inget köp krävs + Kontantuttag: köp krävs + Kontantuttagvaluta + Kontantuttag gräns + Kontantuttag typ: självuttag + Kontantuttag typ: kassa + Kontantuttagoperatör + Kontantuttag + Kontantuttag: ja + Djurskötsel + Avgift + Snickare + Bageri + Golvare + Snickare + Bränneri + Byggare + Hälsokost + Källaringång + Anvisad + Ja + Anvisad + Ja + Leverans + Anvisad + Tillåtande + Destination + Ja + Anvisad + Ja + Anvisad + Ja + Anvisad + Desination + Tillåtande + Ja + Anvisad + Ja + Anvisad + Ja + Anvisad + Ja + Anvisad + Ja + Tillåtande + Anvisad + Ja + Tillgänglighet för funktionshindrade: nej + Taxiåtkomst: nej + Taxiåtkomst: anvisad + Taxiåtkomst: ja + Tillträde för jordbruksfordon: nej + Tillträde för jordbruksfordon: ja + Snöskoteråtkomst: nej + Snöskoteråtkomst: privat + Skidåtkomst: ja + Skidåtkomst: ja + Bussåtkomst: nej + Bussåtkomst: ja + Turistbussåtkomst: nej + Turistbussåtkomst: utsedd + Turistbussåtkomst: ja + Bussåtkomst: nej + PSV-åtkomst: nej + PSV-åtkomst: utsedd + PSV-åtkomst: ja + Släpvansåtkomst: nej + Husbilsåtkomst: nej + Husvangsåtkomst: nej + Fotgängareåtkomst: kunder + Fotgängareåtkomst: tillåtet + Fotgängareåtkomst: destination + Fotgängareåtkomst: nej + Fotgängareåtkomst: privat + Fotgängareåtkomst: ja + Häståtkomst: skogsbruk + Häståtkomst: tillåtet + Häståtkomst: destination + Häståtkomst: privat + Cykelåtkomst: kunder + Cykelåtkomst: tillåtet + Cyckelåtkomst: desitnation + Cykelåtkomst: gående + Cykelåtkomst: privat + Mofa-åtkomst: nej + Mopedåtkomst: nej + Motorcykelåtkomst: nej + Motorcykelåtkomst: privat + Åtkomst för lätta lastbilar: nej + HGV-åtkomst: olämpligt + HGV-åtkomst: avråds + HGV-åtkomst: jordbruk + HGV-åtkomst: nej + HGV-åtkomst: privat + Motorfordonåtkomst: jordbruk + Motorfordonåtkomst: skogsbruk + Motorfordonåtkomst: levernas + Motorfordonåtkomst: militär + Motorfordonåtkomst: kunder + Motorfordonåtkomst: tillåtet + Motorfordonåtkomst: desination + Motorfordonåtkomst: nej + Motorfordonåtkomst: privat + Motorfordonåtkomst: ja + Bilåtkomst: skogsbruk + Bilåtkomst: kunder + Bilåtkomst: tillåtet + Bilåtkomst: destination + Bilåtkomst: nej + Bilåtkomst: privat + Bilåtkomst: ja + Fordonsåtkomst: skogsbruk + Fordonsåtkomst: leverans + Fordonsåtkomst: militär + Fordonsåtkomst: kunder + Fordonsåtkomst: tillåtet + Fordonsåtkomst: destination + Fordonsåtkomst: nej + Fordonsåtkomst: privat + Fordonåtkomst: ja + Taxikontor + Vattenpipa longue + Kolhög + Avgift för skötbord: nej + Avgift för skötbord: ja + Antal skötbord + Plats för skötbord: unisextoalett + Plats för skötbord: rum + Plats för skötbord: toalett för män + Plats för skötbord: toalett för kvinnor + Skötbord: begränsat + Skötbord: nej + Skötbord: ja + Plats + Fara: kontaminering + Fara: minfält + Fara: lavin + Fara: översvämmning + Fara: nukleär + Fara: stenfall + Fara: erosion + Räddningslåda + Borttaget objekt + Gasfackla + 3B* + 3B + 3A* + 3A + 2B* + 2B + 2A* + 2A + 1B* + 1B + 1A* + 1A + n/c* + n/c + Färja + Spårvagn + HOV + Tunnelbana + Buss + Tåg + Ja + Landmärke + Naturligt monument + Barnomsorg + Vägtull + Korallö + Barn + Akademisk + Religion + Antikvarisk + Serie + Blodgivning + Medicinskt laboratorium + Partihandel + Konfektyrproduktion + Fältsamling + Inspektionsmätare + Ventilgrupp + Ventil + Mått + Kompession + Ersättning + Omvandlare + Dragkraft + Övergång + Industriell + Mindre distribution + Distribution + Överföring + Rörledningsstation + Bara + Ja + Skoreparation + Kulle + Licensklasser + Ruiner + Minfält + Översvämningsrisk + Hal väg + Lavinfara + Erosionsrisk + Kärnkraftfara + Fara + Kontaktlöst accepteras inte + Kontaktlöst + Utskjutning + Vitvarubutik + Slutdatum + Sten + Kurs + Elektronisk + Nålstift + Anteckningsbok + Kod + Stämplingspunkt + Kontrollpunkt för vandring + Genomsnittlig lutning + Lägsta punkt + Högsta punkt + Istunga + Rester + Hylla + Sten + Isfall + Hängande + Berg + Tidvatten + Utlopp + Dal + Platå + Isfält + Istäcket + Svårighetsgrad + Kabelnummer + Via ferrata + Klättringsäventyr + Zip line + Däck + Försäkring + Motor + Reparation av växellådan + Justering + Ljuddämpare + Lastbilsreparation + Glas + Hjul + Elektrisk + Reparation av kaross + Luftkonditionering + Batterier + Bildelar + Diagnostik + Försäljning av nya bilar + Bromsar + Försäljning av begagnade bilar + Oljebyte + Bilverkstad + Spelcenter för vuxna + Spelhall + Vattenverks kontor + Keramik + Golvbutik + Låg + Medium + Hög + Låg + Medium + Hög + Låg + Medium + Hög + Låg + Medium + Hög + Låg + Medium + Hög + Låg + Medium + Hög + AS/NZS 3112 + BS 1363 + Schuko + NEMA 14-50 + NEMA 14-30 + NEMA 5-20 + NEMA 5-15R + Tesla Roadster + Tesla Supercharger + Telsa-standard + CHAdeMO + Typ 3 + Typ 2 kombo + Typ 2 + Typ 1 kombo + Typ 1 + CEE röd 125A + CEE röd 64A + CEE röd 32A + CEE röd 16A + CEE blå + Cannabisbutik + Båtbutik + Butik för eldstäder + Jurdbruksbutik + Fryst mat + Typ: betesmark + Typ: övergång + Typ: evig + Typ: jordbruk + Kronans diameter + Omkrets + Klättervägar + Loggbok för toppbestigning: nej + Loggbok för toppbestigning: ja + Väggens orientering: NV + Väggens orientering: V + Väggens orientering: SV + Väggens orientering: S + Väggens orientering: SÖ + Väggens orientering: Ö + Väggens orientering: NÖ + Väggens orientering: N + Fasta ankare: nej + Fasta ankare: ja + Klätterstenkvalitet: bräcklig + Klätterstenkvalitet: solid + Klättersten: porfyr + Klättersten: gnejs + Klättersten: kvarsit + Klättersten: sandsten + Klättersten: granit + Klättersten: kalksten + Maximal klättringslängd + Minsta klättringslängd + Klättringslängd + Djupvatten solo: nej + Djupvatten solo: ja \ No newline at end of file diff --git a/OsmAnd/res/values-uk/phrases.xml b/OsmAnd/res/values-uk/phrases.xml index 015cddb2a8..578640f3c5 100644 --- a/OsmAnd/res/values-uk/phrases.xml +++ b/OsmAnd/res/values-uk/phrases.xml @@ -1209,7 +1209,7 @@ Скайп Ютюб Інстаграм - Вконтактє + VKontakte Гугле+ Мобільний телефон Час обслуговування @@ -3894,4 +3894,5 @@ Локальне посилання Геодезист Конференц-центр + Місце для табору \ No newline at end of file diff --git a/OsmAnd/res/values-uk/strings.xml b/OsmAnd/res/values-uk/strings.xml index 6e7fab1d5f..c679111b1e 100644 --- a/OsmAnd/res/values-uk/strings.xml +++ b/OsmAnd/res/values-uk/strings.xml @@ -545,7 +545,7 @@ Завантажується список доступних регіонів… Не вдалося отримати список регіонів з https://osmand.net. Збережена точка закладки була змінена - Точки Закладок відсутні + Немає закладених точок Замінити Показати маршрут Почати супровід @@ -878,7 +878,7 @@ Показувати попередження… Аварійні служби Швидка побудова маршруту (можливо неоптимальна) - Налаштування попередження про дорожній рух (обмеження швидкості, вимушені зупинки, штучні нерівності, тунелі), попередження про камери контролю швидкості, відомості про смуги руху. + Налаштування попереджень про дорожній рух (обмеження швидкості, вимушені зупинки, штучні нерівності, тунелі), попередження про камери контролю швидкості, відомості про смуги руху. Уник. автомагістралей Прив’язуватися до доріг Перегляд та навігація в автономному та мережевому режимах мапами OSM @@ -977,7 +977,7 @@ Використовувати магнітометр Налаштуйте, як записувати свої поїздки. Завантаження… - Вилучити точку + Видалити точку час точність швидкість @@ -1348,7 +1348,7 @@ Ви увійшли як %1$s Допустиме значення перевищення швидкості Оберіть допустиме значення перевищення швидкості, при перевищенні якого Ви отримаєте голосове попередження. - Назву Закладки було змінено на %1$s, щоб мати можливість зберігати рядки зі смайликами у файл. + Закладку перейменовано на «%1$s», щоб зберегти у файл рядок, що містить смайлики. Друк маршруту Дублювання назви закладки Вказана назва Закладки вже існує, тому її було змінено на %1$s, щоб уникнути появи дублікатів. @@ -1900,7 +1900,7 @@ Сповіщати лише, коли змінюється напрямок до цільової точки. Період сповіщеннь Найменший час між оголошеннями. - Усталений колір + Типовий колір Оберіть категорію Додайте назву Додайте категорію @@ -2031,7 +2031,7 @@ Втулок для необмеженого доступу до мап, їх оновлень та отримання відомостей з Wikipedia. Милі/метри Пропустити завантаження мап - У Вас немає завантажених мап. Ви можете вибрати мапу зі списку або завантажити її пізніше за допомогою меню — %1$s. + У вас немає завантажених мап. Ви можете вибрати мапу зі списку або завантажити її пізніше за допомогою меню — %1$s. Виберіть інший регіон Пошук мапи… OsmAnd визначить ваше розташування і запропонує завантажити мапи для цього регіону. @@ -2395,7 +2395,7 @@ Поділитися своїми вуличними видами цієї місцевості через Mapillary. Віджет Mapillary Дозволяє швидко зробити внесок в Mapillary. - Світлини вулиць в мережі для всіх. Відкрийте для себе місця, діліться та знимкуйте весь світ. + Світлини вулиць в мережі для всіх. Відкрийте для себе місця, діліться та захоплюйте весь світ. Вуличні світлини для усіх. Відкривайте для себе місця, співпрацюйте, знімкуйте світ. Мережеві світлини Тут нема світлин. @@ -2979,7 +2979,7 @@ Виберіть профіль для початку Служба завантаження OsmAnd Пурпуровий - Значок + Піктограма Дані зібрано Натисніть ще раз для зміни орієнтації мапи Мінімальна швидкість @@ -3077,7 +3077,7 @@ Зовнішні пристрої введення Оберіть пристрій на зразок звичайної клавіатури чи WunderLINQ для зовнішнього керування. Немає - Звичайна клавіатура + Клавіатура WundеrLINQ Parrot Увімкніть принаймні один профіль застосунку, щоб скористатися цим параметром. @@ -3253,7 +3253,7 @@ Надавати перевагу ґрунтовим дорогам Надати перевагу незаасфальтованим над асфальтованими дорогами для прокладки. Правки OSM - Увімк/вимк показ горизонталей. + Увімк/вимк показ горизонталей на мапі. Кнопка, що показує чи приховує горизонталі на мапі. Не вдається запустити рушій мовлення. Відтворити свою позицію за допомогою записаного треку GPX. @@ -3370,7 +3370,7 @@ Додати власну категорію Показувати лише вночі Усі налаштування втулка відновлено до усталеного стану. - Усі налаштування профілю відновлено до усталеного стану. + Усі налаштування профілю відновлено до типових. %1$s/%2$s Захід сонця о %1$s Схід сонця о %1$s @@ -3436,7 +3436,7 @@ Нічого не вибрано Швидкі дії Профілі - Перелічені %1$s вже існують у OsmAnd. + Перелічені %1$s вже існують в OsmAnd. Поточні елементи будуть замінені на елементи з файлу Замінити все Зберегти обидва @@ -3464,7 +3464,7 @@ Маршрут буде перераховано, якщо відстань від маршруту до поточного місця розташування перевищує вибране значення. Виберіть відстань, після якого маршрут буде перераховано. Перерахунок маршруту у випадку відхилення - Хв + Мін Рельєф Мапа пагорбів використовує темні відтінки для показу схилів, вершин і низин. У режимі Схил, рельєф місцевості візуалізується за допомогою кольорів. @@ -3485,7 +3485,7 @@ Вибрати групу Вибрати фігуру Коло - Площа + Квадрат Восьмикутник Замінити іншу точку цією. Гірськолижний туризм @@ -4049,4 +4049,17 @@ Усі незбережені дані буде втрачено. Показати діалогове вікно запуску Якщо вимкнено, запис почнеться відразу після торкання до віджета або пункту меню, пропускаючи діалогове вікно підтвердження. + Налаштувати лінію маршруту + Лінія маршруту + Лінія маршруту застосовуватиме %1$s, вказаний у вибраному стилі мапи: %2$s. + Вкажіть колір для режиму мапи: %1$s. + • Оновлення OsmAnd Live переміщено до «Завантаження >Оновлення» +\n +\n• Тепер треки можуть бути забарвлені за висотою, швидкістю або нахилом. +\n +\n• Додано можливість зміни зовнішнього вигляду лінії навігації маршрутом +\n +\n• Оновлено діалогове вікно «Запис поїздки» +\n +\n \ No newline at end of file diff --git a/OsmAnd/res/values-zh-rTW/phrases.xml b/OsmAnd/res/values-zh-rTW/phrases.xml index 56516b9292..55a13b5e8c 100644 --- a/OsmAnd/res/values-zh-rTW/phrases.xml +++ b/OsmAnd/res/values-zh-rTW/phrases.xml @@ -3894,4 +3894,5 @@ 月台/站牌編號 土地測量師 會議中心 + 營地 \ No newline at end of file diff --git a/OsmAnd/res/values-zh-rTW/strings.xml b/OsmAnd/res/values-zh-rTW/strings.xml index a40440e789..a12ea721bc 100644 --- a/OsmAnd/res/values-zh-rTW/strings.xml +++ b/OsmAnd/res/values-zh-rTW/strings.xml @@ -4043,4 +4043,17 @@ 所有未儲存的資料都將會遺失。 顯示開始對話框 若停用,則在點擊小工具或選單項目後將跳過確認對話框,立刻開始。 + 自訂路線 + 路線 + 路線將會使用 %1$s 在選定的地圖樣式上指定的:%2$s。 + 指定地圖模式的顏色:%1$s。 + • OsmAnd Live 更新移動至「下載 > 更新」 +\n +\n • 軌跡現在可以使用海拔、速度或坡度來填色 +\n +\n • 新增了變更導航路線外觀的選項 +\n +\n • 更新「旅程錄製」對話框 +\n +\n \ No newline at end of file diff --git a/OsmAnd/res/values/attrs.xml b/OsmAnd/res/values/attrs.xml index f75867c8e8..4b3348ef53 100644 --- a/OsmAnd/res/values/attrs.xml +++ b/OsmAnd/res/values/attrs.xml @@ -124,6 +124,7 @@ + diff --git a/OsmAnd/res/values/colors.xml b/OsmAnd/res/values/colors.xml index 410723dc79..8d9f9dc24a 100644 --- a/OsmAnd/res/values/colors.xml +++ b/OsmAnd/res/values/colors.xml @@ -75,6 +75,8 @@ #d28521 #237bff #d28521 + #14CC70 + #EE5622 #505050 diff --git a/OsmAnd/res/values/phrases.xml b/OsmAnd/res/values/phrases.xml index 675077e65c..bfe9be1e13 100644 --- a/OsmAnd/res/values/phrases.xml +++ b/OsmAnd/res/values/phrases.xml @@ -4347,4 +4347,36 @@ Local ref + Camp pitch + + Hoops + Karate + Jiu-jitsu + Pilates + Shot put + Snooker + Speedway + Sumo + Table soccer + Taekwondo + Ultimate + Wakeboarding + Weightlifting + Wrestling + Zurkhaneh sport + Water polo + Water ski + Aikido + Biathlon + Bobsleigh + Bullfighting + Cliff diving + Cockfighting + Crossfit + Curling + Cycle polo + Fencing + Horseshoes + Kickboxing + diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index bfb443020a..a716c8b4f7 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -12,12 +12,30 @@ --> + Update all maps added to %1$s? • OsmAnd Live updates moved to \"Downloads > Updates\"\n\n • Tracks now could be colorizing by altitude, speed, or slope.\n\n • Added option to change the appearance of the navigation route line\n\n • Updated \"Trip recording\" dialog\n\n + Expired + On hold + In grace period + Renew subscription + Cancelled + Next billing date: %1$s + Three months subscription + Monthly subscription + Annual subscription + OsmAnd Live + Please follow this link if you any issues with purchases. + Troubleshooting + Contact support + If your purchases don\'t show up here, tap on “%1$s”, or contact our support team. + If you have any questions, please contact us at %1$s. + New device / new account + You don\'t have any purchases Specify color for map mode: %1$s. Route line would be use %1$s specified on selected map style: %2$s. Route line diff --git a/OsmAnd/res/values/styles.xml b/OsmAnd/res/values/styles.xml index 7f16bc4586..113454ae88 100644 --- a/OsmAnd/res/values/styles.xml +++ b/OsmAnd/res/values/styles.xml @@ -218,6 +218,7 @@ @color/divider_color_light @color/text_color_primary_light @color/active_color_primary_light + @color/profile_icon_color_green_light @color/card_and_list_background_light @color/activity_background_light @drawable/pages_bg_light @@ -519,6 +520,7 @@ @color/divider_color_dark @color/text_color_primary_dark @color/active_color_primary_dark + @color/profile_icon_color_green_dark @color/card_and_list_background_dark @color/activity_background_dark @drawable/pages_bg_dark diff --git a/OsmAnd/res/xml/settings_main_screen.xml b/OsmAnd/res/xml/settings_main_screen.xml index ba31622bf4..849a687964 100644 --- a/OsmAnd/res/xml/settings_main_screen.xml +++ b/OsmAnd/res/xml/settings_main_screen.xml @@ -13,6 +13,13 @@ app:fragment="net.osmand.plus.settings.fragments.GlobalSettingsFragment" tools:icon="@drawable/ic_action_settings" /> + + diff --git a/OsmAnd/src-google/net/osmand/plus/inapp/InAppPurchasesImpl.java b/OsmAnd/src-google/net/osmand/plus/inapp/InAppPurchasesImpl.java index a2a0f8d680..9c7ee92758 100644 --- a/OsmAnd/src-google/net/osmand/plus/inapp/InAppPurchasesImpl.java +++ b/OsmAnd/src-google/net/osmand/plus/inapp/InAppPurchasesImpl.java @@ -2,15 +2,15 @@ package net.osmand.plus.inapp; import android.content.Context; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - import com.android.billingclient.api.SkuDetails; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.Version; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + public class InAppPurchasesImpl extends InAppPurchases { private static final InAppPurchase FULL_VERSION = new InAppPurchaseFullVersion(); @@ -285,6 +285,11 @@ public class InAppPurchasesImpl extends InAppPurchases { this.details = details; } + @Override + public int getPeriodTypeString() { + return R.string.monthly_subscription; + } + @Override public String getDefaultPrice(Context ctx) { return ""; diff --git a/OsmAnd/src-huawei/net/osmand/plus/inapp/InAppPurchasesImpl.java b/OsmAnd/src-huawei/net/osmand/plus/inapp/InAppPurchasesImpl.java index 4ed0021b6f..59d97daed0 100644 --- a/OsmAnd/src-huawei/net/osmand/plus/inapp/InAppPurchasesImpl.java +++ b/OsmAnd/src-huawei/net/osmand/plus/inapp/InAppPurchasesImpl.java @@ -165,6 +165,11 @@ public class InAppPurchasesImpl extends InAppPurchases { this.info = info; } + @Override + public int getPeriodTypeString() { + return R.string.monthly_subscription; + } + @Override public String getDefaultPrice(Context ctx) { return ""; diff --git a/OsmAnd/src/net/osmand/data/FavouritePoint.java b/OsmAnd/src/net/osmand/data/FavouritePoint.java index f87083363f..414299e575 100644 --- a/OsmAnd/src/net/osmand/data/FavouritePoint.java +++ b/OsmAnd/src/net/osmand/data/FavouritePoint.java @@ -45,7 +45,7 @@ public class FavouritePoint implements Serializable, LocationPoint { private boolean visible = true; private SpecialPointType specialPointType = null; private BackgroundType backgroundType = null; - private double altitude; + private double altitude = Double.NaN; private long timestamp; public FavouritePoint() { diff --git a/OsmAnd/src/net/osmand/plus/OsmAndFormatter.java b/OsmAnd/src/net/osmand/plus/OsmAndFormatter.java index 883cc908c5..383128095b 100644 --- a/OsmAnd/src/net/osmand/plus/OsmAndFormatter.java +++ b/OsmAnd/src/net/osmand/plus/OsmAndFormatter.java @@ -3,6 +3,9 @@ package net.osmand.plus; import android.content.Context; import android.text.format.DateUtils; +import androidx.core.text.TextUtilsCompat; +import androidx.core.view.ViewCompat; + import com.jwetherell.openmap.common.LatLonPoint; import com.jwetherell.openmap.common.MGRSPoint; import com.jwetherell.openmap.common.UTMPoint; @@ -37,7 +40,7 @@ public class OsmAndFormatter { public final static float METERS_IN_KILOMETER = 1000f; public final static float METERS_IN_ONE_MILE = 1609.344f; // 1609.344 public final static float METERS_IN_ONE_NAUTICALMILE = 1852f; // 1852 - + public final static float YARDS_IN_ONE_METER = 1.0936f; public final static float FEET_IN_ONE_METER = YARDS_IN_ONE_METER * 3f; private static final DecimalFormat fixed2 = new DecimalFormat("0.00"); @@ -175,7 +178,7 @@ public class OsmAndFormatter { } else { generator *= 2; } - + if (point == mainUnitInMeter && metersInSecondUnit * mainUnitInMeter * 0.9f <= generator) { point = 1 / metersInSecondUnit; generator = 1; @@ -198,14 +201,14 @@ public class OsmAndFormatter { } return roundDist; } - + public static String getFormattedRoundDistanceKm(float meters, int digits, OsmandApplication ctx) { int mainUnitStr = R.string.km; float mainUnitInMeters = METERS_IN_KILOMETER; if (digits == 0) { return (int) (meters / mainUnitInMeters + 0.5) + " " + ctx.getString(mainUnitStr); //$NON-NLS-1$ } else if (digits == 1) { - return fixed1.format(((float) meters) / mainUnitInMeters) + " " + ctx.getString(mainUnitStr); + return fixed1.format(((float) meters) / mainUnitInMeters) + " " + ctx.getString(mainUnitStr); } else { return fixed2.format(((float) meters) / mainUnitInMeters) + " " + ctx.getString(mainUnitStr); } @@ -225,10 +228,10 @@ public class OsmAndFormatter { } public static String getFormattedAzimuth(float bearing, AngularConstants angularConstant) { - while(bearing < -180.0) { + while (bearing < -180.0) { bearing += 360; } - while(bearing > 360.0) { + while (bearing > 360.0) { bearing -= 360; } switch (angularConstant) { @@ -315,7 +318,7 @@ public class OsmAndFormatter { return ((int) (alt * FEET_IN_ONE_METER + 0.5)) + " " + ctx.getString(R.string.foot); } } - + public static String getFormattedSpeed(float metersperseconds, OsmandApplication ctx) { OsmandSettings settings = ctx.getSettings(); SpeedConstants mc = settings.SPEED_SYSTEM.get(); @@ -376,22 +379,22 @@ public class OsmAndFormatter { return (kmh10 / 10f) + " " + SpeedConstants.METERS_PER_SECOND.toShortString(ctx); } } - - + + public static String toPublicString(CityType t, Context ctx) { switch (t) { - case CITY: - return ctx.getString(R.string.city_type_city); - case HAMLET: - return ctx.getString(R.string.city_type_hamlet); - case TOWN: - return ctx.getString(R.string.city_type_town); - case VILLAGE: - return ctx.getString(R.string.city_type_village); - case SUBURB: - return ctx.getString(R.string.city_type_suburb); - default: - break; + case CITY: + return ctx.getString(R.string.city_type_city); + case HAMLET: + return ctx.getString(R.string.city_type_hamlet); + case TOWN: + return ctx.getString(R.string.city_type_town); + case VILLAGE: + return ctx.getString(R.string.city_type_village); + case SUBURB: + return ctx.getString(R.string.city_type_suburb); + default: + break; } return ""; } @@ -402,7 +405,7 @@ public class OsmAndFormatter { String typeName = amenity.getSubType(); if (pt != null) { typeName = pt.getTranslation(); - } else if(typeName != null){ + } else if (typeName != null) { typeName = Algorithms.capitalizeFirstLetterAndLowercase(typeName.replace('_', ' ')); } String localName = amenity.getName(locale, transliterate); @@ -506,18 +509,22 @@ public class OsmAndFormatter { if (outputFormat == FORMAT_DEGREES_SHORT) { result.append(formatCoordinate(lat, outputFormat)).append(" ").append(formatCoordinate(lon, outputFormat)); } else if (outputFormat == FORMAT_DEGREES || outputFormat == FORMAT_MINUTES || outputFormat == FORMAT_SECONDS) { + boolean isLeftToRight = TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == ViewCompat.LAYOUT_DIRECTION_LTR; + String rtlCoordinates = isLeftToRight ? "" : "\u200f"; + String rtlCoordinatesPunctuation = isLeftToRight ? ", " : " ,"; result - .append(formatCoordinate(lat, outputFormat)).append(" ") - .append(lat > 0 ? NORTH : SOUTH).append(", ") - .append(formatCoordinate(lon, outputFormat)).append(" ") - .append(lon > 0 ? EAST : WEST); - } else if (outputFormat == UTM_FORMAT) { + .append(rtlCoordinates) + .append(formatCoordinate(lat, outputFormat)).append(rtlCoordinates).append(" ").append(rtlCoordinates) + .append(lat > 0 ? NORTH : SOUTH).append(rtlCoordinates).append(rtlCoordinatesPunctuation).append(rtlCoordinates) + .append(formatCoordinate(lon, outputFormat)).append(rtlCoordinates).append(" ").append(rtlCoordinates) + .append(lon > 0 ? EAST : WEST); + } else if (outputFormat == UTM_FORMAT) { UTMPoint pnt = new UTMPoint(new LatLonPoint(lat, lon)); result - .append(pnt.zone_number) - .append(pnt.zone_letter).append(" ") - .append((long) pnt.easting).append(" ") - .append((long) pnt.northing); + .append(pnt.zone_number) + .append(pnt.zone_letter).append(" ") + .append((long) pnt.easting).append(" ") + .append((long) pnt.northing); } else if (outputFormat == OLC_FORMAT) { String r; try { @@ -536,21 +543,21 @@ public class OsmAndFormatter { } return result.toString(); } - + private static String formatCoordinate(double coordinate, int outputType) { - + if (coordinate < -180.0 || coordinate > 180.0 || Double.isNaN(coordinate)) { return "Error. Wrong coordinates data!"; } if ((outputType != FORMAT_DEGREES) && (outputType != FORMAT_MINUTES) && (outputType - != FORMAT_SECONDS) && (outputType != FORMAT_DEGREES_SHORT)) { - return "Unknown Output Format!"; + != FORMAT_SECONDS) && (outputType != FORMAT_DEGREES_SHORT)) { + return "Unknown Output Format!"; } DecimalFormat degDf = new DecimalFormat("##0.00000", new DecimalFormatSymbols(Locale.US)); DecimalFormat minDf = new DecimalFormat("00.000", new DecimalFormatSymbols(Locale.US)); DecimalFormat secDf = new DecimalFormat("00.0", new DecimalFormatSymbols(Locale.US)); - + StringBuilder sb = new StringBuilder(); if (coordinate < 0) { @@ -566,11 +573,11 @@ public class OsmAndFormatter { sb.append(degDf.format(coordinate)).append(DELIMITER_DEGREES); } else if (outputType == FORMAT_MINUTES) { sb.append(minDf.format(formatCoordinate(coordinate, sb, DELIMITER_DEGREES))) - .append(DELIMITER_MINUTES); + .append(DELIMITER_MINUTES); } else { sb.append(secDf.format(formatCoordinate( - formatCoordinate(coordinate, sb, DELIMITER_DEGREES), sb, DELIMITER_MINUTES))) - .append(DELIMITER_SECONDS); + formatCoordinate(coordinate, sb, DELIMITER_DEGREES), sb, DELIMITER_MINUTES))) + .append(DELIMITER_SECONDS); } return sb.toString(); } diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivity.java b/OsmAnd/src/net/osmand/plus/activities/MapActivity.java index 953691b272..c4c63e0376 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivity.java @@ -140,6 +140,7 @@ import net.osmand.plus.views.layers.MapControlsLayer; import net.osmand.plus.views.layers.MapInfoLayer; import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarController; import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarControllerType; +import net.osmand.plus.views.mapwidgets.WidgetsVisibilityHelper; import net.osmand.render.RenderingRulesStorage; import net.osmand.router.GeneralRouter; import net.osmand.util.Algorithms; @@ -210,6 +211,7 @@ public class MapActivity extends OsmandActionBarActivity implements DownloadEven private MapActivityActions mapActions; private MapActivityLayers mapLayers; + private WidgetsVisibilityHelper mapWidgetsVisibilityHelper; // App variables private OsmandApplication app; @@ -305,6 +307,7 @@ public class MapActivity extends OsmandActionBarActivity implements DownloadEven } mapActions = new MapActivityActions(this); mapLayers = new MapActivityLayers(this); + mapWidgetsVisibilityHelper = new WidgetsVisibilityHelper(this); dashboardOnMap.createDashboardView(); checkAppInitialization(); @@ -1597,6 +1600,10 @@ public class MapActivity extends OsmandActionBarActivity implements DownloadEven return mapLayers; } + public WidgetsVisibilityHelper getWidgetsVisibilityHelper() { + return mapWidgetsVisibilityHelper; + } + public static void launchMapActivityMoveToTop(Context activity, Bundle prevIntentParams, Uri intentData, Bundle intentParams) { if (activity instanceof MapActivity) { if (((MapActivity) activity).getDashboard().isVisible()) { diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java b/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java index 4ef54303ea..35d88627c2 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java @@ -1030,22 +1030,6 @@ public class MapActivityActions implements DialogProvider { }).createItem()); */ - if (Version.isGooglePlayEnabled() || Version.isHuawei() || Version.isDeveloperVersion(app)) { - optionsMenuHelper.addItem(new ItemBuilder().setTitleId(R.string.purchases, mapActivity) - .setId(DRAWER_OSMAND_LIVE_ID) - .setIcon(R.drawable.ic_action_purchases) - .setListener(new ItemClickListener() { - @Override - public boolean onContextMenuClick(ArrayAdapter adapter, int itemId, int pos, boolean isChecked, int[] viewCoordinates) { - app.logEvent("drawer_osm_live_open"); - Intent intent = new Intent(mapActivity, OsmLiveActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - mapActivity.startActivity(intent); - return true; - } - }).createItem()); - } - optionsMenuHelper.addItem(new ItemBuilder().setTitleId(R.string.shared_string_help, mapActivity) .setId(DRAWER_HELP_ID) .setIcon(R.drawable.ic_action_help) diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivityLayers.java b/OsmAnd/src/net/osmand/plus/activities/MapActivityLayers.java index ccdef9484e..8c26c705f8 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivityLayers.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivityLayers.java @@ -13,7 +13,6 @@ import android.widget.Toast; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.view.ContextThemeWrapper; -import androidx.core.content.ContextCompat; import net.osmand.CallbackWithObject; import net.osmand.GPXUtilities.GPXFile; @@ -184,7 +183,7 @@ public class MapActivityLayers { mapControlsLayer = new MapControlsLayer(activity); mapView.addLayer(mapControlsLayer, 11); // 12. quick actions layer - mapQuickActionLayer = new MapQuickActionLayer(activity, contextMenuLayer); + mapQuickActionLayer = new MapQuickActionLayer(activity); mapView.addLayer(mapQuickActionLayer, 12); contextMenuLayer.setMapQuickActionLayer(mapQuickActionLayer); mapControlsLayer.setMapQuickActionLayer(mapQuickActionLayer); diff --git a/OsmAnd/src/net/osmand/plus/chooseplan/ChoosePlanDialogFragment.java b/OsmAnd/src/net/osmand/plus/chooseplan/ChoosePlanDialogFragment.java index 8488daf0f5..814fe15871 100644 --- a/OsmAnd/src/net/osmand/plus/chooseplan/ChoosePlanDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/chooseplan/ChoosePlanDialogFragment.java @@ -22,17 +22,6 @@ import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; -import androidx.annotation.ColorRes; -import androidx.annotation.DrawableRes; -import androidx.annotation.LayoutRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.AppCompatImageView; -import androidx.core.content.ContextCompat; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentActivity; -import androidx.fragment.app.FragmentManager; - import net.osmand.AndroidUtils; import net.osmand.PlatformUtil; import net.osmand.plus.OsmandApplication; @@ -59,6 +48,17 @@ import org.apache.commons.logging.Log; import java.util.List; +import androidx.annotation.ColorRes; +import androidx.annotation.DrawableRes; +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.widget.AppCompatImageView; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.FragmentManager; + public abstract class ChoosePlanDialogFragment extends BaseOsmAndDialogFragment implements InAppPurchaseListener { public static final String TAG = ChoosePlanDialogFragment.class.getSimpleName(); private static final Log LOG = PlatformUtil.getLog(ChoosePlanDialogFragment.class); @@ -489,14 +489,18 @@ public abstract class ChoosePlanDialogFragment extends BaseOsmAndDialogFragment buttonView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - subscribe(s.getSku()); + if (getActivity() != null) { + subscribe(app, getActivity(), purchaseHelper, s.getSku()); + } } }); } else { buttonExView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - subscribe(s.getSku()); + if (getActivity() != null) { + subscribe(app, getActivity(), purchaseHelper, s.getSku()); + } } }); } @@ -526,14 +530,14 @@ public abstract class ChoosePlanDialogFragment extends BaseOsmAndDialogFragment } } - private void subscribe(String sku) { + public static void subscribe(@NonNull OsmandApplication app, Activity activity, + InAppPurchaseHelper purchaseHelper, String sku) { if (!app.getSettings().isInternetConnectionAvailable(true)) { Toast.makeText(app, R.string.internet_not_available, Toast.LENGTH_LONG).show(); } else { - FragmentActivity ctx = getActivity(); - if (ctx != null && purchaseHelper != null) { + if (activity != null && purchaseHelper != null) { OsmandSettings settings = app.getSettings(); - purchaseHelper.purchaseLiveUpdates(ctx, sku, + purchaseHelper.purchaseLiveUpdates(activity, sku, settings.BILLING_USER_EMAIL.get(), settings.BILLING_USER_NAME.get(), settings.BILLING_USER_COUNTRY_DOWNLOAD_NAME.get(), diff --git a/OsmAnd/src/net/osmand/plus/download/ui/UpdatesIndexFragment.java b/OsmAnd/src/net/osmand/plus/download/ui/UpdatesIndexFragment.java index f6cf6b03cc..ae0ab9b560 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/UpdatesIndexFragment.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/UpdatesIndexFragment.java @@ -38,8 +38,9 @@ import net.osmand.plus.OsmandApplication; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.LocalIndexInfo; import net.osmand.plus.helpers.AndroidUiHelper; -import net.osmand.plus.liveupdates.LiveUpdatesClearDialogFragment.RefreshLiveUpdates; -import net.osmand.plus.liveupdates.LiveUpdatesFragmentNew; +import net.osmand.plus.liveupdates.LiveUpdatesClearBottomSheet.RefreshLiveUpdates; +import net.osmand.plus.liveupdates.LiveUpdatesFragment; +import net.osmand.plus.liveupdates.LiveUpdatesHelper.LiveUpdateListener; import net.osmand.plus.liveupdates.LoadLiveMapsTask; import net.osmand.plus.liveupdates.LoadLiveMapsTask.LocalIndexInfoAdapter; import net.osmand.plus.chooseplan.ChoosePlanDialogFragment.ChoosePlanDialogType; @@ -58,10 +59,10 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import static net.osmand.plus.liveupdates.LiveUpdatesFragmentNew.showUpdateDialog; -import static net.osmand.plus.liveupdates.LiveUpdatesFragmentNew.updateCountEnabled; +import static net.osmand.plus.liveupdates.LiveUpdatesFragment.showUpdateDialog; +import static net.osmand.plus.liveupdates.LiveUpdatesFragment.updateCountEnabled; -public class UpdatesIndexFragment extends OsmAndListFragment implements DownloadEvents, RefreshLiveUpdates { +public class UpdatesIndexFragment extends OsmAndListFragment implements DownloadEvents, RefreshLiveUpdates, LiveUpdateListener { private static final int RELOAD_ID = 5; private UpdateIndexAdapter listAdapter; private String errorMessage; @@ -236,7 +237,7 @@ public class UpdatesIndexFragment extends OsmAndListFragment implements Download DownloadActivity activity = getMyActivity(); if (activity != null) { if (!listAdapter.isShowOsmLivePurchaseBanner()) { - LiveUpdatesFragmentNew.showInstance(activity.getSupportFragmentManager(), this); + LiveUpdatesFragment.showInstance(activity.getSupportFragmentManager(), this); } } } else { @@ -397,8 +398,7 @@ public class UpdatesIndexFragment extends OsmAndListFragment implements Download @Override public void onClick(View v) { if (!listAdapter.isShowOsmLivePurchaseBanner()) { - showUpdateDialog(getActivity(), getMyApplication().getSettings(), - listAdapter.mapsList, listAdapter.countEnabled, null); + showUpdateDialog(getActivity(), getFragmentManager(), UpdatesIndexFragment.this); } } }); @@ -416,9 +416,17 @@ public class UpdatesIndexFragment extends OsmAndListFragment implements Download } } + @Override + public void processFinish() { + } + + @Override + public List getMapsToUpdate() { + return LiveUpdatesFragment.getMapsToUpdate(listAdapter.mapsList, settings); + } + @ColorRes public static int getDefaultIconColorId(boolean nightMode) { return nightMode ? R.color.icon_color_default_dark : R.color.icon_color_default_light; } - } diff --git a/OsmAnd/src/net/osmand/plus/inapp/InAppPurchaseHelper.java b/OsmAnd/src/net/osmand/plus/inapp/InAppPurchaseHelper.java index d511b7be85..5136cd1f73 100644 --- a/OsmAnd/src/net/osmand/plus/inapp/InAppPurchaseHelper.java +++ b/OsmAnd/src/net/osmand/plus/inapp/InAppPurchaseHelper.java @@ -8,9 +8,6 @@ import android.os.AsyncTask; import android.text.TextUtils; import android.util.Log; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - import net.osmand.AndroidNetworkUtils; import net.osmand.AndroidNetworkUtils.OnRequestResultListener; import net.osmand.AndroidNetworkUtils.OnRequestsResultListener; @@ -43,6 +40,9 @@ import java.util.List; import java.util.Map; import java.util.Set; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + public abstract class InAppPurchaseHelper { // Debug tag, for logging protected static final org.apache.commons.logging.Log LOG = PlatformUtil.getLog(InAppPurchaseHelper.class); @@ -198,6 +198,18 @@ public abstract class InAppPurchaseHelper { isDeveloperVersion = Version.isDeveloperVersion(ctx); } + @NonNull + public List getEverMadeSubscriptions() { + List subscriptions = new ArrayList<>(); + for (InAppSubscription subscription : getLiveUpdates().getVisibleSubscriptions()) { + SubscriptionState state = subscription.getState(); + if (state != SubscriptionState.UNDEFINED) { + subscriptions.add(subscription); + } + } + return subscriptions; + } + public abstract void isInAppPurchaseSupported(@NonNull final Activity activity, @Nullable final InAppPurchaseInitCallback callback); public boolean hasInventory() { diff --git a/OsmAnd/src/net/osmand/plus/inapp/InAppPurchases.java b/OsmAnd/src/net/osmand/plus/inapp/InAppPurchases.java index e6ec431187..27d359630e 100644 --- a/OsmAnd/src/net/osmand/plus/inapp/InAppPurchases.java +++ b/OsmAnd/src/net/osmand/plus/inapp/InAppPurchases.java @@ -7,10 +7,6 @@ import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.style.ForegroundColorSpan; -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - import net.osmand.AndroidUtils; import net.osmand.Period; import net.osmand.Period.PeriodUnit; @@ -32,6 +28,12 @@ import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import androidx.annotation.ColorInt; +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; + public abstract class InAppPurchases { protected InAppPurchase fullVersion; @@ -580,24 +582,40 @@ public abstract class InAppPurchases { 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"); + UNDEFINED("undefined", 0, 0), + ACTIVE("active", R.string.osm_live_active, R.drawable.bg_osmand_live_active), + CANCELLED("cancelled", R.string.osmand_live_cancelled, R.drawable.bg_osmand_live_cancelled), + IN_GRACE_PERIOD("in_grace_period", R.string.in_grace_period, R.drawable.bg_osmand_live_active), + ON_HOLD("on_hold", R.string.on_hold, R.drawable.bg_osmand_live_cancelled), + PAUSED("paused", R.string.shared_string_paused, R.drawable.bg_osmand_live_cancelled), + EXPIRED("expired", R.string.expired, R.drawable.bg_osmand_live_cancelled); private final String stateStr; + @StringRes + private final int stringRes; + @DrawableRes + private final int backgroundRes; - SubscriptionState(@NonNull String stateStr) { + SubscriptionState(@NonNull String stateStr, @StringRes int stringRes, @DrawableRes int backgroundRes) { this.stateStr = stateStr; + this.stringRes = stringRes; + this.backgroundRes = backgroundRes; } public String getStateStr() { return stateStr; } + @StringRes + public int getStringRes() { + return stringRes; + } + + @DrawableRes + public int getBackgroundRes() { + return backgroundRes; + } + @NonNull public static SubscriptionState getByStateStr(@NonNull String stateStr) { for (SubscriptionState state : SubscriptionState.values()) { @@ -627,6 +645,9 @@ public abstract class InAppPurchases { this.skuNoVersion = sku; } + @StringRes + public abstract int getPeriodTypeString(); + @NonNull private List getUpgrades() { return new ArrayList<>(upgrades.values()); @@ -817,6 +838,11 @@ public abstract class InAppPurchases { this(sku, false); } + @Override + public int getPeriodTypeString() { + return R.string.monthly_subscription; + } + @Override public void setPriceValue(double priceValue) { super.setPriceValue(priceValue); @@ -865,6 +891,11 @@ public abstract class InAppPurchases { super(sku, false); } + @Override + public int getPeriodTypeString() { + return R.string.three_months_subscription; + } + @Override public void setPriceValue(double priceValue) { super.setPriceValue(priceValue); @@ -908,6 +939,11 @@ public abstract class InAppPurchases { super(sku, false); } + @Override + public int getPeriodTypeString() { + return R.string.annual_subscription; + } + @Override public void setPriceValue(double priceValue) { super.setPriceValue(priceValue); diff --git a/OsmAnd/src/net/osmand/plus/liveupdates/CountrySelectionFragment.java b/OsmAnd/src/net/osmand/plus/liveupdates/CountrySelectionFragment.java index f070559121..9c29e9762b 100644 --- a/OsmAnd/src/net/osmand/plus/liveupdates/CountrySelectionFragment.java +++ b/OsmAnd/src/net/osmand/plus/liveupdates/CountrySelectionFragment.java @@ -35,6 +35,8 @@ import java.util.List; public class CountrySelectionFragment extends BaseOsmAndDialogFragment { + public static final String TAG = CountrySelectionFragment.class.getSimpleName(); + private List countryItems = new ArrayList<>(); private OnFragmentInteractionListener mListener; diff --git a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesAlarmReceiver.java b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesAlarmReceiver.java index 78d84bf220..a51140fbab 100644 --- a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesAlarmReceiver.java +++ b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesAlarmReceiver.java @@ -32,7 +32,8 @@ public class LiveUpdatesAlarmReceiver extends BroadcastReceiver { final OsmandSettings settings = application.getSettings(); if (!preferenceDownloadViaWiFi(localIndexInfoFile, settings).get() || wifi.isWifiEnabled()) { - new PerformLiveUpdateAsyncTask(context, localIndexInfoFile, false, null).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, fileName); + new PerformLiveUpdateAsyncTask(context, localIndexInfoFile, false) + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, fileName); } else { PerformLiveUpdateAsyncTask.tryRescheduleDownload(context, settings, localIndexInfoFile); } diff --git a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesClearDialogFragment.java b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesClearBottomSheet.java similarity index 93% rename from OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesClearDialogFragment.java rename to OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesClearBottomSheet.java index c54daada1a..dc199d61de 100644 --- a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesClearDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesClearBottomSheet.java @@ -31,10 +31,10 @@ import static net.osmand.plus.liveupdates.LiveUpdatesHelper.getNameToDisplay; import static net.osmand.plus.liveupdates.LiveUpdatesHelper.preferenceLastCheck; import static net.osmand.plus.liveupdates.LiveUpdatesHelper.preferenceLatestUpdateAvailable; -public class LiveUpdatesClearDialogFragment extends MenuBottomSheetDialogFragment { +public class LiveUpdatesClearBottomSheet extends MenuBottomSheetDialogFragment { - public static final String TAG = LiveUpdatesClearDialogFragment.class.getSimpleName(); - private static final Log LOG = PlatformUtil.getLog(LiveUpdatesClearDialogFragment.class); + public static final String TAG = LiveUpdatesClearBottomSheet.class.getSimpleName(); + private static final Log LOG = PlatformUtil.getLog(LiveUpdatesClearBottomSheet.class); private static final String LOCAL_INDEX_FILE_NAME = "local_index_file_name"; private OsmandApplication app; @@ -44,7 +44,7 @@ public class LiveUpdatesClearDialogFragment extends MenuBottomSheetDialogFragmen public static void showInstance(@NonNull FragmentManager fragmentManager, Fragment target, String fileName) { if (!fragmentManager.isStateSaved()) { - LiveUpdatesClearDialogFragment fragment = new LiveUpdatesClearDialogFragment(); + LiveUpdatesClearBottomSheet fragment = new LiveUpdatesClearBottomSheet(); fragment.setTargetFragment(target, 0); fragment.fileName = fileName; fragment.show(fragmentManager, TAG); diff --git a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesFragment.java b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesFragment.java index 02bdad0a43..9f91391af3 100644 --- a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesFragment.java +++ b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesFragment.java @@ -4,149 +4,172 @@ import android.app.Activity; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.Context; -import android.content.Intent; -import android.content.res.Resources; +import android.content.DialogInterface; +import android.graphics.Typeface; +import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.AsyncTask; +import android.os.Build; import android.os.Bundle; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.Button; import android.widget.CompoundButton; import android.widget.ExpandableListView; -import android.widget.ImageButton; +import android.widget.FrameLayout; import android.widget.ImageView; -import android.widget.ProgressBar; import android.widget.TextView; -import androidx.annotation.DrawableRes; +import androidx.annotation.ColorRes; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; +import androidx.appcompat.content.res.AppCompatResources; +import androidx.appcompat.widget.AppCompatImageButton; +import androidx.appcompat.widget.AppCompatImageView; import androidx.appcompat.widget.SwitchCompat; +import androidx.appcompat.widget.Toolbar; import androidx.core.content.ContextCompat; -import androidx.fragment.app.FragmentActivity; +import androidx.core.graphics.drawable.DrawableCompat; +import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; +import com.google.android.material.appbar.AppBarLayout; + +import net.osmand.AndroidNetworkUtils; +import net.osmand.AndroidUtils; +import net.osmand.PlatformUtil; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.chooseplan.ChoosePlanDialogFragment.ChoosePlanDialogType; -import net.osmand.plus.settings.backend.CommonPreference; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; -import net.osmand.plus.Version; -import net.osmand.plus.activities.LocalIndexHelper; +import net.osmand.plus.UiUtilities; +import net.osmand.plus.UiUtilities.CompoundButtonType; import net.osmand.plus.activities.LocalIndexInfo; import net.osmand.plus.activities.OsmandBaseExpandableListAdapter; -import net.osmand.plus.activities.OsmandInAppPurchaseActivity; -import net.osmand.plus.base.BaseOsmAndFragment; -import net.osmand.plus.chooseplan.ChoosePlanDialogFragment; -import net.osmand.plus.download.ui.AbstractLoadLocalIndexTask; +import net.osmand.plus.base.BaseOsmAndDialogFragment; +import net.osmand.plus.helpers.AndroidUiHelper; +import net.osmand.plus.helpers.FontCache; import net.osmand.plus.inapp.InAppPurchaseHelper; -import net.osmand.plus.inapp.InAppPurchaseHelper.InAppPurchaseListener; -import net.osmand.plus.inapp.InAppPurchaseHelper.InAppPurchaseTaskType; import net.osmand.plus.inapp.InAppPurchases.InAppSubscription; -import net.osmand.plus.resources.IncrementalChangesManager; +import net.osmand.plus.liveupdates.LiveUpdatesClearBottomSheet.RefreshLiveUpdates; +import net.osmand.plus.liveupdates.LiveUpdatesHelper.LiveUpdateListener; +import net.osmand.plus.liveupdates.LiveUpdatesHelper.TimeOfDay; +import net.osmand.plus.liveupdates.LiveUpdatesHelper.UpdateFrequency; +import net.osmand.plus.liveupdates.LiveUpdatesSettingsBottomSheet.OnLiveUpdatesForLocalChange; +import net.osmand.plus.liveupdates.LoadLiveMapsTask.LocalIndexInfoAdapter; +import net.osmand.plus.settings.backend.CommonPreference; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.widgets.TextViewEx; +import net.osmand.plus.wikipedia.WikipediaDialogFragment; import net.osmand.util.Algorithms; -import java.io.File; +import org.apache.commons.logging.Log; + +import java.lang.ref.WeakReference; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; -import java.util.HashSet; +import java.util.Date; import java.util.List; -import java.util.Set; +import java.util.Locale; +import java.util.TimeZone; -import static net.osmand.plus.liveupdates.LiveUpdatesFragmentNew.showUpdateDialog; -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.DEFAULT_LAST_CHECK; -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.TimeOfDay; -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.UpdateFrequency; -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.formatDateTime; +import static net.osmand.AndroidUtils.getSecondaryTextColorId; +import static net.osmand.plus.liveupdates.LiveUpdatesHelper.formatShortDateTime; import static net.osmand.plus.liveupdates.LiveUpdatesHelper.getNameToDisplay; import static net.osmand.plus.liveupdates.LiveUpdatesHelper.getPendingIntent; +import static net.osmand.plus.liveupdates.LiveUpdatesHelper.preferenceForLocalIndex; import static net.osmand.plus.liveupdates.LiveUpdatesHelper.preferenceLastCheck; -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.preferenceLiveUpdatesOn; +import static net.osmand.plus.liveupdates.LiveUpdatesHelper.preferenceLatestUpdateAvailable; import static net.osmand.plus.liveupdates.LiveUpdatesHelper.preferenceTimeOfDayToUpdate; import static net.osmand.plus.liveupdates.LiveUpdatesHelper.preferenceUpdateFrequency; +import static net.osmand.plus.liveupdates.LiveUpdatesHelper.runLiveUpdate; import static net.osmand.plus.liveupdates.LiveUpdatesHelper.setAlarmForPendingIntent; +import static net.osmand.plus.liveupdates.LiveUpdatesSettingsBottomSheet.getTertiaryTextColorId; +import static net.osmand.plus.monitoring.TripRecordingBottomSheet.getActiveTextColorId; +import static net.osmand.plus.monitoring.TripRecordingBottomSheet.getOsmandIconColorId; +import static net.osmand.plus.monitoring.TripRecordingBottomSheet.getSecondaryIconColorId; -public class LiveUpdatesFragment extends BaseOsmAndFragment implements InAppPurchaseListener { - public static final int TITLE = R.string.live_updates; - private static final int SUBSCRIPTION_SETTINGS = 5; - public static final Comparator LOCAL_INDEX_INFO_COMPARATOR = new Comparator() { - @Override - public int compare(LocalIndexInfo lhs, LocalIndexInfo rhs) { - return lhs.getName().compareTo(rhs.getName()); - } - }; +public class LiveUpdatesFragment extends BaseOsmAndDialogFragment implements OnLiveUpdatesForLocalChange, LiveUpdateListener { + + public static final String URL = "https://osmand.net/api/osmlive_status"; + public static final String TAG = LiveUpdatesFragment.class.getSimpleName(); + private final static Log LOG = PlatformUtil.getLog(LiveUpdatesFragment.class); + private static final String SUBSCRIPTION_URL = "https://osmand.net/features/subscription"; private OsmandApplication app; - private View subscriptionHeader; + private OsmandSettings settings; + private boolean nightMode; + + private View toolbarSwitchContainer; private ExpandableListView listView; - private LocalIndexesAdapter adapter; - private AsyncTask> loadLocalIndexesTask; - private boolean showSettingsOnly; + private TextViewEx descriptionTime; + private LiveMapsAdapter adapter; - private ProgressBar progressBar; - private boolean processing; + private GetLastUpdateDateTask getLastUpdateDateTask; + private LoadLiveMapsTask loadLiveMapsTask; - @Nullable - public InAppPurchaseHelper getInAppPurchaseHelper() { - Activity activity = getActivity(); - if (activity instanceof OsmandInAppPurchaseActivity) { - return ((OsmandInAppPurchaseActivity) activity).getPurchaseHelper(); - } else { - return null; + public static void showInstance(@NonNull FragmentManager fragmentManager, Fragment target) { + if (!fragmentManager.isStateSaved()) { + LiveUpdatesFragment fragment = new LiveUpdatesFragment(); + fragment.setTargetFragment(target, 0); + fragment.show(fragmentManager, TAG); } } - private boolean isProcessing() { - return processing; + public static void showUpdateDialog(Activity context, FragmentManager fragmentManager, final LiveUpdateListener listener) { + List mapsToUpdate = listener.getMapsToUpdate(); + if (!Algorithms.isEmpty(mapsToUpdate)) { + int countEnabled = listener.getMapsToUpdate().size(); + if (countEnabled == 1) { + runLiveUpdate(context, mapsToUpdate.get(0).getFileName(), false, new Runnable() { + @Override + public void run() { + listener.processFinish(); + } + }); + } else if (countEnabled > 1) { + Fragment target = null; + if (listener instanceof Fragment) { + target = (Fragment) listener; + } + LiveUpdatesUpdateAllBottomSheet.showInstance(fragmentManager, target); + } + } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setHasOptionsMenu(true); app = getMyApplication(); - if (getActivity() instanceof OsmLiveActivity) { - showSettingsOnly = ((OsmLiveActivity) getActivity()).isShowSettingOnly(); - } + settings = getSettings(); + nightMode = isNightMode(false); + setHasOptionsMenu(true); } @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_live_updates, container, false); - listView = (ExpandableListView) view.findViewById(android.R.id.list); + createToolbar((ViewGroup) view.findViewById(R.id.app_bar)); - boolean nightMode = !app.getSettings().isLightContent(); - final SwipeRefreshLayout swipeRefresh = view.findViewById(R.id.swipe_refresh); - int swipeColor = ContextCompat.getColor(app, nightMode ? R.color.osmand_orange_dark : R.color.osmand_orange); - swipeRefresh.setColorSchemeColors(swipeColor); - swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { - @Override - public void onRefresh() { - showUpdateDialog(getActivity(), getSettings(), adapter.dataShouldUpdate, adapter.dataShouldUpdate.size(), null); - swipeRefresh.setRefreshing(false); - } - }); + listView = (ExpandableListView) view.findViewById(android.R.id.list); + adapter = new LiveMapsAdapter(); + listView.setAdapter(adapter); + expandAllGroups(); View bottomShadowView = inflater.inflate(R.layout.card_bottom_divider, listView, false); - if (!showSettingsOnly) { - listView.addFooterView(bottomShadowView); - } - adapter = new LocalIndexesAdapter(this); + listView.addFooterView(bottomShadowView); listView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() { @Override public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { - if (!processing && InAppPurchaseHelper.isSubscribedToLiveUpdates(app)) { - final FragmentManager fragmentManager = getChildFragmentManager(); - LiveUpdatesSettingsDialogFragment - .createInstance(adapter.getChild(groupPosition, childPosition).getFileName()) - .show(fragmentManager, "settings"); + if (InAppPurchaseHelper.isSubscribedToLiveUpdates(app) && settings.IS_LIVE_UPDATES_ON.get()) { + if (getFragmentManager() != null) { + LiveUpdatesSettingsBottomSheet + .showInstance(getFragmentManager(), LiveUpdatesFragment.this, + adapter.getChild(groupPosition, childPosition).getFileName()); + } return true; } else { return false; @@ -154,305 +177,184 @@ public class LiveUpdatesFragment extends BaseOsmAndFragment implements InAppPurc } }); - progressBar = (ProgressBar) view.findViewById(R.id.progress); + final SwipeRefreshLayout swipeRefresh = view.findViewById(R.id.swipe_refresh); + int swipeColor = ContextCompat.getColor(app, nightMode ? R.color.osmand_orange_dark : R.color.osmand_orange); + swipeRefresh.setColorSchemeColors(swipeColor); + swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { + @Override + public void onRefresh() { + if (settings.IS_LIVE_UPDATES_ON.get()) { + showUpdateDialog(getActivity(), getFragmentManager(), LiveUpdatesFragment.this); + startUpdateDateAsyncTask(); + } + swipeRefresh.setRefreshing(false); + } + }); - if (!Version.isDeveloperVersion(app)) { - subscriptionHeader = inflater.inflate(R.layout.live_updates_header, listView, false); - updateSubscriptionHeader(); - listView.addHeaderView(subscriptionHeader, "subscriptionHeader", false); + View headerView = inflater.inflate(R.layout.list_item_import, listView, false); + View timeContainer = headerView.findViewById(R.id.item_import_container); + AndroidUtils.setListItemBackground(app, timeContainer, nightMode); + AndroidUiHelper.setVisibility(View.VISIBLE, headerView.findViewById(R.id.bottom_divider)); + listView.addHeaderView(headerView); + + AppCompatImageView descriptionIcon = timeContainer.findViewById(R.id.icon); + Drawable icon = UiUtilities.createTintedDrawable(app, R.drawable.ic_action_time, + ContextCompat.getColor(app, nightMode ? R.color.icon_color_default_dark : R.color.icon_color_default_light)); + descriptionIcon.setImageDrawable(icon); + + TextViewEx title = timeContainer.findViewById(R.id.title); + AndroidUtils.setTextSecondaryColor(app, title, nightMode); + title.setText(R.string.latest_openstreetmap_update); + title.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimensionPixelSize(R.dimen.default_desc_text_size)); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + title.setLetterSpacing(AndroidUtils.getFloatValueFromRes(app, R.dimen.description_letter_spacing)); } - listView.setAdapter(adapter); - if (!showSettingsOnly) { - loadLocalIndexesTask = new LoadLocalIndexTask(adapter, this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + descriptionTime = timeContainer.findViewById(R.id.sub_title); + AndroidUtils.setTextPrimaryColor(app, descriptionTime, nightMode); + Typeface typeface = FontCache.getFont(app, getString(R.string.font_roboto_medium)); + descriptionTime.setTypeface(typeface); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + descriptionTime.setLetterSpacing(AndroidUtils.getFloatValueFromRes(app, R.dimen.description_letter_spacing)); } return view; } - public void updateSubscriptionHeader() { - if (getActivity() instanceof OsmLiveActivity && subscriptionHeader != null) { - View subscriptionBanner = subscriptionHeader.findViewById(R.id.subscription_banner); - View subscriptionInfo = subscriptionHeader.findViewById(R.id.subscription_info); - if (InAppPurchaseHelper.isSubscribedToLiveUpdates(app)) { - ImageView statusIcon = (ImageView) subscriptionHeader.findViewById(R.id.statusIcon); - TextView statusTextView = (TextView) subscriptionHeader.findViewById(R.id.statusTextView); - TextView regionNameHeaderTextView = (TextView) subscriptionHeader.findViewById(R.id.regionHeaderTextView); - TextView regionNameTextView = (TextView) subscriptionHeader.findViewById(R.id.regionTextView); - statusTextView.setText(getString(R.string.osm_live_active)); - statusIcon.setImageDrawable(app.getUIUtilities().getThemedIcon(R.drawable.ic_action_done)); - - regionNameHeaderTextView.setText(R.string.osm_live_support_region); - String countryName = app.getSettings().BILLING_USER_COUNTRY.get(); - InAppPurchaseHelper purchaseHelper = getInAppPurchaseHelper(); - if (purchaseHelper != null) { - InAppSubscription monthlyPurchased = purchaseHelper.getPurchasedMonthlyLiveUpdates(); - if (monthlyPurchased != null && monthlyPurchased.isDonationSupported()) { - if (Algorithms.isEmpty(countryName)) { - if (app.getSettings().BILLING_USER_COUNTRY_DOWNLOAD_NAME.get().equals(OsmandSettings.BILLING_USER_DONATION_NONE_PARAMETER)) { - regionNameHeaderTextView.setText(R.string.default_buttons_support); - countryName = getString(R.string.osmand_team); - } else { - countryName = getString(R.string.shared_string_world); - } - } - } else { - regionNameHeaderTextView.setText(R.string.default_buttons_support); - countryName = getString(R.string.osmand_team); - } - } else { - regionNameHeaderTextView.setText(R.string.default_buttons_support); - countryName = getString(R.string.osmand_team); - } - regionNameTextView.setText(countryName); - - View subscriptionsButton = subscriptionHeader.findViewById(R.id.button_subscriptions); - View settingsButtonContainer = subscriptionHeader.findViewById(R.id.button_settings_container); - View settingsButton = subscriptionHeader.findViewById(R.id.button_settings); - subscriptionsButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - ChoosePlanDialogFragment.showDialogInstance(app, getActivity().getSupportFragmentManager(), ChoosePlanDialogType.OSM_LIVE); - } - }); - if (isDonationSupported()) { - settingsButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - showDonationSettings(); - } - }); - settingsButtonContainer.setVisibility(View.VISIBLE); - } else { - settingsButton.setOnClickListener(null); - settingsButtonContainer.setVisibility(View.GONE); - } - - subscriptionBanner.setVisibility(View.GONE); - subscriptionInfo.setVisibility(View.VISIBLE); - } else { - Button readMoreBtn = (Button) subscriptionHeader.findViewById(R.id.read_more_button); - readMoreBtn.setEnabled(!processing); - readMoreBtn.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Uri uri = Uri.parse("https://osmand.net/osm_live"); - Intent goToOsmLive = new Intent(Intent.ACTION_VIEW, uri); - startActivity(goToOsmLive); - } - }); - Button subscriptionButton = (Button) subscriptionHeader.findViewById(R.id.subscription_button); - subscriptionButton.setEnabled(!processing); - subscriptionButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - FragmentActivity activity = getActivity(); - if (activity != null) { - ChoosePlanDialogFragment.showDialogInstance(app, activity.getSupportFragmentManager(), ChoosePlanDialogType.OSM_LIVE); - } - } - }); - - subscriptionBanner.setVisibility(View.VISIBLE); - subscriptionInfo.setVisibility(View.GONE); - } - } - } - @Override public void onResume() { super.onResume(); - InAppPurchaseHelper purchaseHelper = getInAppPurchaseHelper(); - if (purchaseHelper != null) { - if (purchaseHelper.getActiveTask() == InAppPurchaseTaskType.REQUEST_INVENTORY) { - enableProgress(); - } - } + startUpdateDateAsyncTask(); + startLoadLiveMapsAsyncTask(); } @Override - public void onDestroyView() { - super.onDestroyView(); - if (loadLocalIndexesTask != null) { - loadLocalIndexesTask.cancel(true); + public void onPause() { + super.onPause(); + stopUpdateDateAsyncTask(); + stopLoadLiveMapsAsyncTask(); + } + + @Override + public void onDismiss(@NonNull DialogInterface dialog) { + super.onDismiss(dialog); + Fragment target = getTargetFragment(); + if (target instanceof RefreshLiveUpdates) { + ((RefreshLiveUpdates) target).onUpdateStates(app); } } - public void notifyLiveUpdatesChanged() { - if (getActivity() != null && adapter != null) { - adapter.notifyLiveUpdatesChanged(); + private void startUpdateDateAsyncTask() { + getLastUpdateDateTask = new GetLastUpdateDateTask(this); + getLastUpdateDateTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + private void stopUpdateDateAsyncTask() { + if (getLastUpdateDateTask != null) { + getLastUpdateDateTask.cancel(true); } } - private boolean isDonationSupported() { - InAppPurchaseHelper purchaseHelper = getInAppPurchaseHelper(); - if (purchaseHelper != null) { - InAppSubscription monthlyPurchased = purchaseHelper.getPurchasedMonthlyLiveUpdates(); - return monthlyPurchased != null && monthlyPurchased.isDonationSupported(); + private void startLoadLiveMapsAsyncTask() { + if (loadLiveMapsTask == null) { + loadLiveMapsTask = new LoadLiveMapsTask(adapter, app); + loadLiveMapsTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } - return false; } - private void showDonationSettings() { - SubscriptionFragment subscriptionFragment = new SubscriptionFragment(); - subscriptionFragment.show(getChildFragmentManager(), SubscriptionFragment.TAG); + private void stopLoadLiveMapsAsyncTask() { + if (loadLiveMapsTask != null && loadLiveMapsTask.getStatus() == AsyncTask.Status.RUNNING) { + loadLiveMapsTask.cancel(false); + } } - protected class LocalIndexesAdapter extends OsmandBaseExpandableListAdapter { - public static final int SHOULD_UPDATE_GROUP_POSITION = 0; - public static final int SHOULD_NOT_UPDATE_GROUP_POSITION = 1; - final ArrayList dataShouldUpdate = new ArrayList<>(); - final ArrayList dataShouldNotUpdate = new ArrayList<>(); - final LiveUpdatesFragment fragment; - final Context ctx; + protected void createToolbar(ViewGroup appBar) { + AppBarLayout appBarLayout = (AppBarLayout) UiUtilities.getInflater(getActivity(), nightMode) + .inflate(R.layout.global_preferences_toolbar_with_switch, appBar); - public LocalIndexesAdapter(LiveUpdatesFragment fragment) { - this.fragment = fragment; - ctx = fragment.getActivity(); - } + Toolbar toolbar = (Toolbar) appBarLayout.findViewById(R.id.toolbar); + TextViewEx toolbarTitle = (TextViewEx) toolbar.findViewById(R.id.toolbar_title); + toolbarTitle.setText(R.string.osm_live); - public void add(LocalIndexInfo info) { - CommonPreference preference = preferenceLiveUpdatesOn( - info.getFileName(), app.getSettings()); - if (preference.get()) { - dataShouldUpdate.add(info); - } else { - dataShouldNotUpdate.add(info); + View closeButton = toolbar.findViewById(R.id.close_button); + closeButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + dismiss(); } - } + }); - public void notifyLiveUpdatesChanged() { - Set changedSet = new HashSet<>(); - for (LocalIndexInfo localIndexInfo : dataShouldUpdate) { - CommonPreference preference = - preferenceLiveUpdatesOn(localIndexInfo.getFileName(), getSettings()); - if (!preference.get()) { - changedSet.add(localIndexInfo); + FrameLayout iconHelpContainer = toolbar.findViewById(R.id.action_button); + int iconColorResId = nightMode ? R.color.active_buttons_and_links_text_dark : R.color.active_buttons_and_links_text_light; + AppCompatImageButton iconHelp = toolbar.findViewById(R.id.action_button_icon); + Drawable helpDrawable = app.getUIUtilities().getIcon(R.drawable.ic_action_help_online, iconColorResId); + iconHelp.setImageDrawable(helpDrawable); + iconHelpContainer.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Activity activity = getActivity(); + if (activity != null) { + WikipediaDialogFragment.showFullArticle(activity, Uri.parse(SUBSCRIPTION_URL), nightMode); } } - dataShouldUpdate.removeAll(changedSet); - dataShouldNotUpdate.addAll(changedSet); - changedSet.clear(); - for (LocalIndexInfo localIndexInfo : dataShouldNotUpdate) { - CommonPreference preference = - preferenceLiveUpdatesOn(localIndexInfo.getFileName(), getSettings()); - if (preference.get()) { - changedSet.add(localIndexInfo); - } - } - dataShouldUpdate.addAll(changedSet); - dataShouldNotUpdate.removeAll(changedSet); - notifyDataSetChanged(); - expandAllGroups(); - } + }); - public void sort() { - Collections.sort(dataShouldUpdate, LOCAL_INDEX_INFO_COMPARATOR); - Collections.sort(dataShouldNotUpdate, LOCAL_INDEX_INFO_COMPARATOR); - } + toolbarSwitchContainer = appBarLayout.findViewById(R.id.toolbar_switch_container); + updateToolbarSwitch(settings.IS_LIVE_UPDATES_ON.get()); + } - @Override - public LocalIndexInfo getChild(int groupPosition, int childPosition) { - if (groupPosition == 0) { - return dataShouldUpdate.get(childPosition); - } else if (groupPosition == 1) { - return dataShouldNotUpdate.get(childPosition); - } else { - throw new IllegalArgumentException("unexpected group position:" + groupPosition); - } - } + private void updateToolbarSwitch(final boolean isChecked) { + int switchColor = ContextCompat.getColor(app, + isChecked ? getActiveTextColorId(nightMode) : getSecondaryTextColorId(nightMode)); + AndroidUtils.setBackground(toolbarSwitchContainer, new ColorDrawable(switchColor)); - @Override - public long getChildId(int groupPosition, int childPosition) { - // it would be unusable to have 10000 local indexes - return groupPosition * 10000 + childPosition; - } + SwitchCompat switchView = toolbarSwitchContainer.findViewById(R.id.switchWidget); + switchView.setChecked(isChecked); + UiUtilities.setupCompoundButton(switchView, nightMode, CompoundButtonType.TOOLBAR); - @Override - public View getChildView(final int groupPosition, final int childPosition, - boolean isLastChild, View convertView, ViewGroup parent) { - LocalFullMapsViewHolder viewHolder; - if (convertView == null) { - LayoutInflater inflater = LayoutInflater.from(ctx); - convertView = inflater.inflate(R.layout.local_index_live_updates_list_item, parent, false); - viewHolder = new LocalFullMapsViewHolder(convertView, fragment); - convertView.setTag(viewHolder); - } else { - viewHolder = (LocalFullMapsViewHolder) convertView.getTag(); - } - viewHolder.bindLocalIndexInfo(getChild(groupPosition, childPosition).getFileName(), isLastChild); - return convertView; - } - - @Override - public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { - View view = convertView; - String group = getGroup(groupPosition); - if (view == null) { - LayoutInflater inflater = LayoutInflater.from(ctx); - view = inflater.inflate(R.layout.list_group_title_with_switch, parent, false); - } - TextView nameView = ((TextView) view.findViewById(R.id.title)); - nameView.setText(group); - - view.setOnClickListener(null); - - final SwitchCompat liveUpdatesSwitch = (SwitchCompat) view.findViewById(R.id.toggle_item); - View topShadowView = view.findViewById(R.id.bottomShadowView); - if (groupPosition == SHOULD_UPDATE_GROUP_POSITION) { - topShadowView.setVisibility(View.GONE); - liveUpdatesSwitch.setVisibility(View.VISIBLE); - OsmandApplication application = (OsmandApplication) ctx.getApplicationContext(); - final OsmandSettings settings = application.getSettings(); - liveUpdatesSwitch.setChecked(settings.IS_LIVE_UPDATES_ON.get()); - liveUpdatesSwitch.setEnabled(!processing); - liveUpdatesSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - if (isChecked) { - if (InAppPurchaseHelper.isSubscribedToLiveUpdates(app)) { - switchOnLiveUpdates(settings); - } else { - liveUpdatesSwitch.setChecked(false); - app.showToastMessage(getString(R.string.osm_live_ask_for_purchase)); - } - } else { - settings.IS_LIVE_UPDATES_ON.set(false); - enableLiveUpdates(false); - } + toolbarSwitchContainer.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + boolean visible = !isChecked; + if (visible) { + if (InAppPurchaseHelper.isSubscribedToLiveUpdates(app)) { + switchOnLiveUpdates(); + updateToolbarSwitch(true); + } else { + app.showToastMessage(getString(R.string.osm_live_ask_for_purchase)); + updateToolbarSwitch(false); } - - - }); - } else { - topShadowView.setVisibility(View.VISIBLE); - liveUpdatesSwitch.setVisibility(View.GONE); + } else { + settings.IS_LIVE_UPDATES_ON.set(false); + enableLiveUpdates(false); + updateToolbarSwitch(false); + } + updateList(); } + }); - View divider = view.findViewById(R.id.divider); - if (getChildrenCount(groupPosition) == 0) { - divider.setVisibility(View.GONE); - } else { - divider.setVisibility(View.VISIBLE); - } - return view; - } + TextView title = toolbarSwitchContainer.findViewById(R.id.switchButtonText); + title.setText(isChecked ? R.string.shared_string_enabled : R.string.shared_string_disabled); + } - private void switchOnLiveUpdates(final OsmandSettings settings) { - settings.IS_LIVE_UPDATES_ON.set(true); - enableLiveUpdates(true); - showUpdateDialog(getActivity(), getSettings(), adapter.dataShouldUpdate, adapter.dataShouldUpdate.size(), null); - } + private void switchOnLiveUpdates() { + settings.IS_LIVE_UPDATES_ON.set(true); + enableLiveUpdates(true); + showUpdateDialog(getMyActivity(), getFragmentManager(), this); + startUpdateDateAsyncTask(); + } - private void enableLiveUpdates(boolean enable) { - AlarmManager alarmMgr = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE); - for (LocalIndexInfo li : dataShouldUpdate) { + private void enableLiveUpdates(boolean enable) { + if (!Algorithms.isEmpty(adapter.mapsList)) { + AlarmManager alarmMgr = (AlarmManager) app.getSystemService(Context.ALARM_SERVICE); + List mapsToUpdate = getMapsToUpdate(adapter.mapsList, settings); + for (LocalIndexInfo li : mapsToUpdate) { String fileName = li.getFileName(); - PendingIntent alarmIntent = getPendingIntent(ctx, fileName); + PendingIntent alarmIntent = getPendingIntent(app, fileName); if (enable) { final CommonPreference updateFrequencyPreference = - preferenceUpdateFrequency(fileName, getSettings()); + preferenceUpdateFrequency(fileName, settings); final CommonPreference timeOfDayPreference = - preferenceTimeOfDayToUpdate(fileName, getSettings()); + preferenceTimeOfDayToUpdate(fileName, settings); UpdateFrequency updateFrequency = UpdateFrequency.values()[updateFrequencyPreference.get()]; TimeOfDay timeOfDayToUpdate = TimeOfDay.values()[timeOfDayPreference.get()]; setAlarmForPendingIntent(alarmIntent, alarmMgr, updateFrequency, timeOfDayToUpdate); @@ -461,38 +363,126 @@ public class LiveUpdatesFragment extends BaseOsmAndFragment implements InAppPurc } } } + } + + private void expandAllGroups() { + for (int i = 0; i < adapter.getGroupCount(); i++) { + listView.expandGroup(i); + } + } + + public static int updateCountEnabled(TextView countView, List mapsList, OsmandSettings settings) { + int countEnabled = getMapsToUpdate(mapsList, settings).size(); + if (countView != null) { + String countText = countEnabled + "/" + mapsList.size(); + countView.setText(countText); + } + return countEnabled; + } + + public static List getMapsToUpdate(List mapsList, OsmandSettings settings) { + List listToUpdate = new ArrayList<>(); + for (LocalIndexInfo mapToUpdate : mapsList) { + CommonPreference preference = preferenceForLocalIndex(mapToUpdate.getFileName(), settings); + if (preference.get()) { + listToUpdate.add(mapToUpdate); + } + } + return listToUpdate; + } + + protected class LiveMapsAdapter extends OsmandBaseExpandableListAdapter implements LocalIndexInfoAdapter { + private final ArrayList mapsList = new ArrayList<>(); + + @Override + public void addData(LocalIndexInfo info) { + mapsList.add(info); + } + + @Override + public void clearData() { + mapsList.clear(); + } + + @Override + public void onDataUpdated() { + sort(); + } + + public void sort() { + Collections.sort(mapsList, new Comparator() { + @Override + public int compare(LocalIndexInfo o1, LocalIndexInfo o2) { + CommonPreference preference1 = preferenceForLocalIndex(o1.getFileName(), getSettings()); + CommonPreference preference2 = preferenceForLocalIndex(o2.getFileName(), getSettings()); + int prefSort = preference2.get().compareTo(preference1.get()); + if (prefSort != 0) { + return prefSort; + } + return o1.compareTo(o2); + } + }); + notifyDataSetInvalidated(); + } + + @Override + public LocalIndexInfo getChild(int groupPosition, int childPosition) { + return mapsList.get(childPosition); + } + + @Override + public long getChildId(int groupPosition, int childPosition) { + return groupPosition * 10000 + childPosition; // it would be unusable to have 10000 local indexes + } + + @Override + public View getChildView(final int groupPosition, final int childPosition, + boolean isLastChild, View convertView, ViewGroup parent) { + LayoutInflater inflater = UiUtilities.getInflater(app, nightMode); + convertView = inflater.inflate(R.layout.list_item_triple_row_icon_and_menu, parent, false); + LiveMapsViewHolder viewHolder = new LiveMapsViewHolder(convertView); + convertView.setTag(viewHolder); + viewHolder.bindLocalIndexInfo(getChild(groupPosition, childPosition).getFileName()); + return convertView; + } + + @Override + public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { + View view = convertView; + if (view == null) { + LayoutInflater inflater = UiUtilities.getInflater(app, nightMode); + view = inflater.inflate(R.layout.list_group_title_with_right_descr, parent, false); + } + view.setOnClickListener(null); + View topShadowView = view.findViewById(R.id.bottomShadowView); + if (groupPosition == 0) { + topShadowView.setVisibility(View.GONE); + } else { + topShadowView.setVisibility(View.VISIBLE); + } + + TextViewEx titleView = ((TextViewEx) view.findViewById(R.id.title)); + titleView.setText(getGroup(groupPosition)); + + TextViewEx countView = ((TextViewEx) view.findViewById(R.id.description)); + AndroidUtils.setTextSecondaryColor(app, countView, nightMode); + + return view; + } @Override public int getChildrenCount(int groupPosition) { - if (showSettingsOnly) { - return 0; - } else if (groupPosition == SHOULD_UPDATE_GROUP_POSITION) { - return dataShouldUpdate.size(); - } else if (groupPosition == SHOULD_NOT_UPDATE_GROUP_POSITION) { - return dataShouldNotUpdate.size(); - } else { - throw new IllegalArgumentException("unexpected group position:" + groupPosition); - } + return mapsList.size(); } @Override public String getGroup(int groupPosition) { - if (groupPosition == SHOULD_UPDATE_GROUP_POSITION) { - return getString(R.string.download_live_updates); - } else if (groupPosition == SHOULD_NOT_UPDATE_GROUP_POSITION) { - return getString(R.string.available_maps); - } else { - throw new IllegalArgumentException("unexpected group position:" + groupPosition); - } + return getString(R.string.available_maps); } @Override public int getGroupCount() { - if (showSettingsOnly) { - return 0; - } else { - return dataShouldNotUpdate.size() == 0 ? 1 : 2; - } + return 1; } @Override @@ -509,226 +499,240 @@ public class LiveUpdatesFragment extends BaseOsmAndFragment implements InAppPurc public boolean isChildSelectable(int groupPosition, int childPosition) { return true; } - - } - private void expandAllGroups() { - for (int i = 0; i < adapter.getGroupCount(); i++) { - listView.expandGroup(i); - } - } + private class LiveMapsViewHolder { + private final ImageView statusIcon; + private final TextView title; + private final TextView subTitle; + private final TextView description; + private final CompoundButton compoundButton; - private static class LocalFullMapsViewHolder { - private final ImageView icon; - private final TextView nameTextView; - private final TextView subheaderTextView; - private final TextView descriptionTextView; - private final ImageButton options; - private final LiveUpdatesFragment fragment; - private final int secondaryColor; - private final View divider; - - private LocalFullMapsViewHolder(View view, LiveUpdatesFragment context) { - icon = (ImageView) view.findViewById(R.id.icon); - nameTextView = (TextView) view.findViewById(R.id.nameTextView); - subheaderTextView = (TextView) view.findViewById(R.id.subheaderTextView); - descriptionTextView = (TextView) view.findViewById(R.id.descriptionTextView); - options = (ImageButton) view.findViewById(R.id.options); - this.fragment = context; - - TypedValue typedValue = new TypedValue(); - Resources.Theme theme = context.getActivity().getTheme(); - theme.resolveAttribute(android.R.attr.textColorSecondary, typedValue, true); - secondaryColor = typedValue.data; - divider = view.findViewById(R.id.divider); + private LiveMapsViewHolder(View view) { + statusIcon = (AppCompatImageView) view.findViewById(R.id.icon); + title = (TextView) view.findViewById(R.id.title); + subTitle = (TextView) view.findViewById(R.id.sub_title); + description = (TextView) view.findViewById(R.id.description); + compoundButton = (CompoundButton) view.findViewById(R.id.compound_button); } - public void bindLocalIndexInfo(@NonNull final String item, boolean isLastChild) { - OsmandApplication context = fragment.getMyActivity().getMyApplication(); - final CommonPreference shouldUpdatePreference = - preferenceLiveUpdatesOn(item, fragment.getSettings()); - IncrementalChangesManager changesManager = context.getResourceManager().getChangesManager(); + public void bindLocalIndexInfo(@NonNull final String item) { + boolean liveUpdateOn = settings.IS_LIVE_UPDATES_ON.get(); + CommonPreference localUpdateOn = preferenceForLocalIndex(item, settings); +// IncrementalChangesManager changesManager = app.getResourceManager().getChangesManager(); + compoundButton.setChecked(localUpdateOn.get()); + if (!liveUpdateOn && localUpdateOn.get()) { + UiUtilities.setupCompoundButton(nightMode, ContextCompat.getColor(app, getTertiaryTextColorId(nightMode)), compoundButton); + } else { + UiUtilities.setupCompoundButton(compoundButton, nightMode, CompoundButtonType.GLOBAL); + } - nameTextView.setText(getNameToDisplay(item, context)); - if (shouldUpdatePreference.get()) { - final Integer frequencyId = preferenceUpdateFrequency(item, fragment.getSettings()).get(); - final Integer timeOfDateToUpdateId = preferenceTimeOfDayToUpdate(item, fragment.getSettings()).get(); + title.setText(getNameToDisplay(item, app)); + + AndroidUiHelper.updateVisibility(subTitle, localUpdateOn.get()); + if (localUpdateOn.get()) { + int frequencyId = preferenceUpdateFrequency(item, settings).get(); final UpdateFrequency frequency = UpdateFrequency.values()[frequencyId]; + String subTitleText = getString(frequency.getLocalizedId()); + /*int timeOfDateToUpdateId = preferenceTimeOfDayToUpdate(item, settings).get(); final TimeOfDay timeOfDay = TimeOfDay.values()[timeOfDateToUpdateId]; - subheaderTextView.setVisibility(View.VISIBLE); - String subheaderText = fragment.getString(frequency.getLocalizedId()); if (frequency != UpdateFrequency.HOURLY) { - subheaderText += " • " + fragment.getString(timeOfDay.getLocalizedId()); - } - subheaderTextView.setText(subheaderText); - subheaderTextView.setTextColor(fragment.getActivity().getResources() - .getColor(R.color.osmand_orange)); - icon.setImageDrawable(fragment.getIcon(R.drawable.ic_map, R.color.osmand_orange)); - options.setImageDrawable(getSecondaryColorPaintedIcon(R.drawable.ic_overflow_menu_white)); - } else { - subheaderTextView.setVisibility(View.GONE); - icon.setImageDrawable(getSecondaryColorPaintedIcon(R.drawable.ic_map)); - options.setImageDrawable(getSecondaryColorPaintedIcon(R.drawable.ic_action_plus)); + subTitleText += " • " + getString(timeOfDay.getLocalizedId()); + }*/ + subTitle.setText(subTitleText); + subTitle.setTextColor(ContextCompat.getColor(app, liveUpdateOn + ? getActiveTextColorId(nightMode) : getSecondaryTextColorId(nightMode))); + Typeface typeface = FontCache.getFont(app, getString(R.string.font_roboto_medium)); + subTitle.setTypeface(typeface); } - final String fileNameWithoutExtension = - Algorithms.getFileNameWithoutExtension(new File(item)); - final long timestamp = changesManager.getTimestamp(fileNameWithoutExtension); - final long lastCheck = preferenceLastCheck(item, fragment.getSettings()).get(); - CommonPreference liveUpdateOn = preferenceLiveUpdatesOn(item, fragment.getSettings()); - if (liveUpdateOn.get() && lastCheck != DEFAULT_LAST_CHECK) { - String lastCheckString = formatDateTime(fragment.getActivity(), lastCheck); - descriptionTextView.setText(context.getString(R.string.last_update, lastCheckString)); - } else { - String lastCheckString = formatDateTime(fragment.getActivity(), timestamp); - descriptionTextView.setText(context.getString(R.string.last_map_change, lastCheckString)); + Drawable statusDrawable = AppCompatResources.getDrawable(app, R.drawable.ic_map); + int resColorId = !localUpdateOn.get() ? getSecondaryIconColorId(nightMode) : + !liveUpdateOn ? getDefaultIconColorId(nightMode) : getOsmandIconColorId(nightMode); + int statusColor = ContextCompat.getColor(app, resColorId); + if (statusDrawable != null) { + DrawableCompat.setTint(statusDrawable, statusColor); } + statusIcon.setImageDrawable(statusDrawable); - if (!fragment.isProcessing() && InAppPurchaseHelper.isSubscribedToLiveUpdates(context)) { - final View.OnClickListener clickListener = new View.OnClickListener() { + description.setText(getLastCheckString(item, app)); + + if (InAppPurchaseHelper.isSubscribedToLiveUpdates(app)) { + compoundButton.setEnabled(liveUpdateOn); + compoundButton.setOnCheckedChangeListener(new SwitchCompat.OnCheckedChangeListener() { @Override - public void onClick(View v) { - final FragmentManager fragmentManager = fragment.getChildFragmentManager(); - LiveUpdatesSettingsDialogFragment.createInstance(item).show(fragmentManager, "settings"); + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + onUpdateLocalIndex(item, isChecked, new Runnable() { + @Override + public void run() { + runSort(); + } + }); } - }; - options.setOnClickListener(clickListener); - options.setEnabled(true); + }); } else { - options.setEnabled(false); + compoundButton.setEnabled(false); } - - if (isLastChild) { - divider.setVisibility(View.GONE); - } else { - divider.setVisibility(View.VISIBLE); - } - } - - private Drawable getSecondaryColorPaintedIcon(@DrawableRes int drawable) { - return fragment.getMyActivity().getMyApplication().getUIUtilities() - .getPaintedIcon(drawable, secondaryColor); } } - public static class LoadLocalIndexTask - extends AsyncTask> - implements AbstractLoadLocalIndexTask { + public static class GetLastUpdateDateTask extends AsyncTask { - //private List result; - private LocalIndexesAdapter adapter; - private LiveUpdatesFragment fragment; - private LocalIndexHelper helper; + private final OsmandApplication app; + private final WeakReference fragment; - public LoadLocalIndexTask(LocalIndexesAdapter adapter, - LiveUpdatesFragment fragment) { - this.adapter = adapter; - this.fragment = fragment; - helper = new LocalIndexHelper(fragment.getMyActivity().getMyApplication()); + GetLastUpdateDateTask(LiveUpdatesFragment fragment) { + this.fragment = new WeakReference<>(fragment); + app = fragment.getMyApplication(); } @Override - protected List doInBackground(Void... params) { - return helper.getLocalFullMaps(this); + protected String doInBackground(Void... params) { + try { + return AndroidNetworkUtils.sendRequest(app, URL, null, + "Requesting map updates info...", false, false); + } catch (Exception e) { + LOG.error("Error: " + "Requesting map updates info error", e); + return null; + } } @Override - public void loadFile(LocalIndexInfo... loaded) { - publishProgress(loaded); - } - - @Override - protected void onProgressUpdate(LocalIndexInfo... values) { - String fileNameL; - for (LocalIndexInfo localIndexInfo : values) { - fileNameL = localIndexInfo.getFileName().toLowerCase(); - if (localIndexInfo.getType() == LocalIndexHelper.LocalIndexType.MAP_DATA - && !fileNameL.contains("world") && !fileNameL.startsWith("depth_")) { - adapter.add(localIndexInfo); + protected void onPostExecute(String response) { + LiveUpdatesFragment f = fragment.get(); + if (response != null && f != null) { + TextViewEx descriptionTime = f.descriptionTime; + if (descriptionTime != null) { + SimpleDateFormat source = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US); + source.setTimeZone(TimeZone.getTimeZone("UTC")); + SimpleDateFormat dest = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US); + dest.setTimeZone(TimeZone.getDefault()); + try { + LOG.debug("response = " + response); + Date parsed = source.parse(response); + if (parsed != null) { + long dateTime = parsed.getTime(); + LOG.debug("dateTime = " + dateTime); + descriptionTime.setText(dest.format(parsed)); + } + } catch (ParseException e) { + LOG.error(e.getMessage()); + } } } + } + } + + protected static String getLastCheckString(String fileName, OsmandApplication app) { + return getLastCheckString(fileName, app, false); + } + + protected static String getLastCheckString(String fileName, OsmandApplication app, boolean lastTimeChecked) { + OsmandSettings settings = app.getSettings(); + + final long lastUpdate = preferenceLatestUpdateAvailable(fileName, settings).get(); + String lastUpdateString = formatShortDateTime(app, lastUpdate); + String description = app.getString(R.string.updated, lastUpdateString); + + if (lastTimeChecked) { + final long lastCheck = preferenceLastCheck(fileName, settings).get(); + String lastCheckString = formatShortDateTime(app, lastCheck); + if (!lastUpdateString.equals(app.getString(R.string.shared_string_never))) { + description = description.concat("\n" + app.getString(R.string.last_time_checked, lastCheckString)); + } + } + return description; + } + + @Override + public void processFinish() { + adapter.notifyDataSetChanged(); + } + + @Override + public List getMapsToUpdate() { + return getMapsToUpdate(adapter.mapsList, settings); + } + + @Override + public boolean onUpdateLocalIndex(String fileName, boolean newValue, final Runnable callback) { + int frequencyId = preferenceUpdateFrequency(fileName, settings).get(); + int timeOfDateToUpdateId = preferenceTimeOfDayToUpdate(fileName, settings).get(); + final AlarmManager alarmManager = (AlarmManager) app.getSystemService(Context.ALARM_SERVICE); + final PendingIntent alarmIntent = getPendingIntent(app, fileName); + + final CommonPreference liveUpdatePreference = preferenceForLocalIndex(fileName, settings); + liveUpdatePreference.set(newValue); + if (settings.IS_LIVE_UPDATES_ON.get() && liveUpdatePreference.get()) { + runLiveUpdate(getActivity(), fileName, true, callback); + UpdateFrequency updateFrequency = UpdateFrequency.values()[frequencyId]; + TimeOfDay timeOfDayToUpdate = TimeOfDay.values()[timeOfDateToUpdateId]; + setAlarmForPendingIntent(alarmIntent, alarmManager, updateFrequency, timeOfDayToUpdate); + } else { + alarmManager.cancel(alarmIntent); + runSort(); + } + return true; + } + + @Override + public void forceUpdateLocal(String fileName, boolean userRequested, final Runnable callback) { + if (settings.IS_LIVE_UPDATES_ON.get()) { + runLiveUpdate(getActivity(), fileName, userRequested, callback); + } + } + + @Override + public void runSort() { + if (adapter != null) { + adapter.onDataUpdated(); + } + } + + @Override + public void updateList() { + if (adapter != null) { adapter.notifyDataSetChanged(); - fragment.expandAllGroups(); - } - - @Override - protected void onPostExecute(List result) { - //this.result = result; - adapter.sort(); - adapter.notifyLiveUpdatesChanged(); - adapter.notifyDataSetInvalidated(); } } - private void enableProgress() { - processing = true; - progressBar.setVisibility(View.VISIBLE); - updateSubscriptionHeader(); - adapter.notifyDataSetChanged(); + public static String getSupportRegionName(OsmandApplication app, InAppPurchaseHelper purchaseHelper) { + OsmandSettings settings = app.getSettings(); + String countryName = settings.BILLING_USER_COUNTRY.get(); + if (purchaseHelper != null) { + List subscriptions = purchaseHelper.getLiveUpdates().getVisibleSubscriptions(); + boolean donationSupported = false; + for (InAppSubscription s : subscriptions) { + if (s.isDonationSupported()) { + donationSupported = true; + break; + } + } + if (donationSupported) { + if (Algorithms.isEmpty(countryName)) { + if (OsmandSettings.BILLING_USER_DONATION_NONE_PARAMETER.equals(settings.BILLING_USER_COUNTRY_DOWNLOAD_NAME.get())) { + countryName = app.getString(R.string.osmand_team); + } else { + countryName = app.getString(R.string.shared_string_world); + } + } + } else { + countryName = app.getString(R.string.osmand_team); + } + } else { + countryName = app.getString(R.string.osmand_team); + } + return countryName; } - private void disableProgress() { - processing = false; - progressBar.setVisibility(View.INVISIBLE); - updateSubscriptionHeader(); - adapter.notifyDataSetChanged(); + public static String getSupportRegionHeader(OsmandApplication app, String supportRegion) { + return supportRegion.equals(app.getString(R.string.osmand_team)) ? + app.getString(R.string.default_buttons_support) : + app.getString(R.string.osm_live_support_region); } - @Override - public void onError(InAppPurchaseTaskType taskType, String error) { - disableProgress(); - - OsmandInAppPurchaseActivity activity = getInAppPurchaseActivity(); - if (activity != null) { - activity.fireInAppPurchaseErrorOnFragments(getChildFragmentManager(), taskType, error); - } - } - - @Override - public void onGetItems() { - if (!InAppPurchaseHelper.isSubscribedToLiveUpdates(app)) { - app.getSettings().IS_LIVE_UPDATES_ON.set(false); - adapter.enableLiveUpdates(false); - } - disableProgress(); - - OsmandInAppPurchaseActivity activity = getInAppPurchaseActivity(); - if (activity != null) { - activity.fireInAppPurchaseGetItemsOnFragments(getChildFragmentManager()); - } - } - - @Override - public void onItemPurchased(String sku, boolean active) { - InAppPurchaseHelper purchaseHelper = getInAppPurchaseHelper(); - if (purchaseHelper != null && purchaseHelper.getLiveUpdates().containsSku(sku)) { - updateSubscriptionHeader(); - } - - OsmandInAppPurchaseActivity activity = getInAppPurchaseActivity(); - if (activity != null) { - activity.fireInAppPurchaseItemPurchasedOnFragments(getChildFragmentManager(), sku, active); - } - } - - @Override - public void showProgress(InAppPurchaseTaskType taskType) { - enableProgress(); - - OsmandInAppPurchaseActivity activity = getInAppPurchaseActivity(); - if (activity != null) { - activity.fireInAppPurchaseShowProgressOnFragments(getChildFragmentManager(), taskType); - } - } - - @Override - public void dismissProgress(InAppPurchaseTaskType taskType) { - disableProgress(); - - OsmandInAppPurchaseActivity activity = getInAppPurchaseActivity(); - if (activity != null) { - activity.fireInAppPurchaseDismissProgressOnFragments(getChildFragmentManager(), taskType); - } + @ColorRes + public static int getDefaultIconColorId(boolean nightMode) { + return nightMode ? R.color.icon_color_default_dark : R.color.icon_color_default_light; } } diff --git a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesFragmentNew.java b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesFragmentNew.java deleted file mode 100644 index 243362ea98..0000000000 --- a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesFragmentNew.java +++ /dev/null @@ -1,706 +0,0 @@ -package net.osmand.plus.liveupdates; - -import android.app.Activity; -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.graphics.Typeface; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; -import android.util.TypedValue; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CompoundButton; -import android.widget.ExpandableListView; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.annotation.ColorRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.content.res.AppCompatResources; -import androidx.appcompat.widget.AppCompatImageView; -import androidx.appcompat.widget.SwitchCompat; -import androidx.appcompat.widget.Toolbar; -import androidx.core.content.ContextCompat; -import androidx.core.graphics.drawable.DrawableCompat; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; - -import net.osmand.AndroidNetworkUtils; -import net.osmand.AndroidUtils; -import net.osmand.PlatformUtil; -import net.osmand.plus.OsmandApplication; -import net.osmand.plus.R; -import net.osmand.plus.UiUtilities; -import net.osmand.plus.UiUtilities.CompoundButtonType; -import net.osmand.plus.activities.LocalIndexInfo; -import net.osmand.plus.activities.OsmandBaseExpandableListAdapter; -import net.osmand.plus.base.BaseOsmAndDialogFragment; -import net.osmand.plus.helpers.AndroidUiHelper; -import net.osmand.plus.helpers.FontCache; -import net.osmand.plus.inapp.InAppPurchaseHelper; -import net.osmand.plus.liveupdates.LiveUpdatesClearDialogFragment.RefreshLiveUpdates; -import net.osmand.plus.liveupdates.LiveUpdatesHelper.TimeOfDay; -import net.osmand.plus.liveupdates.LiveUpdatesHelper.UpdateFrequency; -import net.osmand.plus.liveupdates.LiveUpdatesSettingsDialogFragmentNew.OnLiveUpdatesForLocalChange; -import net.osmand.plus.liveupdates.LoadLiveMapsTask.LocalIndexInfoAdapter; -import net.osmand.plus.liveupdates.PerformLiveUpdateAsyncTask.LiveUpdateListener; -import net.osmand.plus.settings.backend.CommonPreference; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.widgets.TextViewEx; -import net.osmand.util.Algorithms; - -import org.apache.commons.logging.Log; - -import java.lang.ref.WeakReference; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.Date; -import java.util.Locale; -import java.util.TimeZone; - -import static net.osmand.AndroidUtils.getSecondaryTextColorId; -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.formatShortDateTime; -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.getNameToDisplay; -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.getPendingIntent; -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.preferenceForLocalIndex; -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.preferenceLastCheck; -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.preferenceLatestUpdateAvailable; -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.preferenceTimeOfDayToUpdate; -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.preferenceUpdateFrequency; -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.runLiveUpdate; -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.setAlarmForPendingIntent; -import static net.osmand.plus.liveupdates.LiveUpdatesSettingsDialogFragmentNew.getTertiaryTextColorId; -import static net.osmand.plus.monitoring.TripRecordingBottomSheet.getActiveTextColorId; -import static net.osmand.plus.monitoring.TripRecordingBottomSheet.getOsmandIconColorId; -import static net.osmand.plus.monitoring.TripRecordingBottomSheet.getSecondaryIconColorId; - -public class LiveUpdatesFragmentNew extends BaseOsmAndDialogFragment implements OnLiveUpdatesForLocalChange { - - public static final String URL = "https://osmand.net/api/osmlive_status"; - public static final String TAG = LiveUpdatesFragmentNew.class.getSimpleName(); - private final static Log LOG = PlatformUtil.getLog(LiveUpdatesFragmentNew.class); - private static final String SUBSCRIPTION_URL = "https://osmand.net/features/subscription"; - - private OsmandApplication app; - private OsmandSettings settings; - private boolean nightMode; - - private View toolbarSwitchContainer; - private ExpandableListView listView; - private TextViewEx descriptionTime; - private LiveMapsAdapter adapter; - - private GetLastUpdateDateTask getLastUpdateDateTask; - private LoadLiveMapsTask loadLiveMapsTask; - - private final LiveUpdateListener liveUpdateListener = new LiveUpdateListener() { - @Override - public void processFinish() { - adapter.notifyDataSetChanged(); - } - }; - - public static void showInstance(@NonNull FragmentManager fragmentManager, Fragment target) { - if (!fragmentManager.isStateSaved()) { - LiveUpdatesFragmentNew fragment = new LiveUpdatesFragmentNew(); - fragment.setTargetFragment(target, 0); - fragment.show(fragmentManager, TAG); - } - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - app = getMyApplication(); - settings = getSettings(); - nightMode = isNightMode(false); - setHasOptionsMenu(true); - } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_live_updates, container, false); - - Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar); - toolbar.setTitle(R.string.osm_live); - int iconColorResId = nightMode ? R.color.active_buttons_and_links_text_dark : R.color.active_buttons_and_links_text_light; - Drawable icBack = app.getUIUtilities().getIcon(AndroidUtils.getNavigationIconResId(app), iconColorResId); - DrawableCompat.setTint(icBack, ContextCompat.getColor(app, iconColorResId)); - toolbar.setNavigationIcon(icBack); - toolbar.setNavigationContentDescription(R.string.access_shared_string_navigate_up); - toolbar.setNavigationOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - dismiss(); - } - }); - ImageButton iconHelp = toolbar.findViewById(R.id.toolbar_action); - Drawable helpDrawable = app.getUIUtilities().getIcon(R.drawable.ic_action_help, iconColorResId); - iconHelp.setImageDrawable(helpDrawable); - iconHelp.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(SUBSCRIPTION_URL)); - if (AndroidUtils.isIntentSafe(app, intent)) { - startActivity(intent); - } - } - }); - - listView = (ExpandableListView) view.findViewById(android.R.id.list); - adapter = new LiveMapsAdapter(); - listView.setAdapter(adapter); - expandAllGroups(); - - View bottomShadowView = inflater.inflate(R.layout.card_bottom_divider, listView, false); - listView.addFooterView(bottomShadowView); - listView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() { - @Override - public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { - if (InAppPurchaseHelper.isSubscribedToLiveUpdates(app) && settings.IS_LIVE_UPDATES_ON.get()) { - if (getFragmentManager() != null) { - LiveUpdatesSettingsDialogFragmentNew - .showInstance(getFragmentManager(), LiveUpdatesFragmentNew.this, - adapter.getChild(groupPosition, childPosition).getFileName()); - } - return true; - } else { - return false; - } - } - }); - - final SwipeRefreshLayout swipeRefresh = view.findViewById(R.id.swipe_refresh); - int swipeColor = ContextCompat.getColor(app, nightMode ? R.color.osmand_orange_dark : R.color.osmand_orange); - swipeRefresh.setColorSchemeColors(swipeColor); - swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { - @Override - public void onRefresh() { - if (settings.IS_LIVE_UPDATES_ON.get()) { - showUpdateDialog(getActivity(), settings, adapter.mapsList, adapter.countEnabled, liveUpdateListener); - startUpdateDateAsyncTask(); - } - swipeRefresh.setRefreshing(false); - } - }); - - toolbarSwitchContainer = view.findViewById(R.id.toolbar_switch_container); - updateToolbarSwitch(settings.IS_LIVE_UPDATES_ON.get()); - - View timeContainer = view.findViewById(R.id.item_import_container); - AndroidUtils.setListItemBackground(app, timeContainer, nightMode); - - AppCompatImageView descriptionIcon = timeContainer.findViewById(R.id.icon); - Drawable icon = UiUtilities.createTintedDrawable(app, R.drawable.ic_action_time, - ContextCompat.getColor(app, nightMode ? R.color.icon_color_default_dark : R.color.icon_color_default_light)); - descriptionIcon.setImageDrawable(icon); - - TextViewEx title = timeContainer.findViewById(R.id.title); - AndroidUtils.setTextSecondaryColor(app, title, nightMode); - title.setText(R.string.latest_openstreetmap_update); - title.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimensionPixelSize(R.dimen.default_desc_text_size)); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - title.setLetterSpacing(AndroidUtils.getFloatValueFromRes(app, R.dimen.description_letter_spacing)); - } - - descriptionTime = timeContainer.findViewById(R.id.sub_title); - AndroidUtils.setTextPrimaryColor(app, descriptionTime, nightMode); - Typeface typeface = FontCache.getFont(app, getString(R.string.font_roboto_medium)); - descriptionTime.setTypeface(typeface); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - descriptionTime.setLetterSpacing(AndroidUtils.getFloatValueFromRes(app, R.dimen.description_letter_spacing)); - } - - return view; - } - - @Override - public void onResume() { - super.onResume(); - startUpdateDateAsyncTask(); - startLoadLiveMapsAsyncTask(); - } - - @Override - public void onPause() { - super.onPause(); - stopUpdateDateAsyncTask(); - stopLoadLiveMapsAsyncTask(); - } - - @Override - public void onDismiss(@NonNull DialogInterface dialog) { - super.onDismiss(dialog); - Fragment target = getTargetFragment(); - if (target instanceof RefreshLiveUpdates) { - ((RefreshLiveUpdates) target).onUpdateStates(app); - } - } - - private void startUpdateDateAsyncTask() { - getLastUpdateDateTask = new GetLastUpdateDateTask(this); - getLastUpdateDateTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - - private void stopUpdateDateAsyncTask() { - if (getLastUpdateDateTask != null) { - getLastUpdateDateTask.cancel(true); - } - } - - private void startLoadLiveMapsAsyncTask() { - if (loadLiveMapsTask == null) { - loadLiveMapsTask = new LoadLiveMapsTask(adapter, app); - loadLiveMapsTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - } - - private void stopLoadLiveMapsAsyncTask() { - if (loadLiveMapsTask != null && loadLiveMapsTask.getStatus() == AsyncTask.Status.RUNNING) { - loadLiveMapsTask.cancel(false); - } - } - - private void updateToolbarSwitch(final boolean isChecked) { - int switchColor = ContextCompat.getColor(app, - isChecked ? getActiveTextColorId(nightMode) : getSecondaryTextColorId(nightMode)); - AndroidUtils.setBackground(toolbarSwitchContainer, new ColorDrawable(switchColor)); - - SwitchCompat switchView = toolbarSwitchContainer.findViewById(R.id.switchWidget); - switchView.setChecked(isChecked); - UiUtilities.setupCompoundButton(switchView, nightMode, CompoundButtonType.TOOLBAR); - - toolbarSwitchContainer.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - boolean visible = !isChecked; - if (visible) { - if (InAppPurchaseHelper.isSubscribedToLiveUpdates(app)) { - switchOnLiveUpdates(); - updateToolbarSwitch(true); - } else { - app.showToastMessage(getString(R.string.osm_live_ask_for_purchase)); - updateToolbarSwitch(false); - } - } else { - settings.IS_LIVE_UPDATES_ON.set(false); - enableLiveUpdates(false); - updateToolbarSwitch(false); - } - updateList(); - } - }); - - TextView title = toolbarSwitchContainer.findViewById(R.id.switchButtonText); - title.setText(isChecked ? R.string.shared_string_enabled : R.string.shared_string_disabled); - } - - private void switchOnLiveUpdates() { - settings.IS_LIVE_UPDATES_ON.set(true); - enableLiveUpdates(true); - showUpdateDialog(getMyActivity(), settings, adapter.mapsList, adapter.countEnabled, liveUpdateListener); - startUpdateDateAsyncTask(); - } - - public static void showUpdateDialog(final Activity context, final OsmandSettings settings, - final ArrayList mapsList, int countEnabled, - @Nullable final LiveUpdateListener listener) { - if (!Algorithms.isEmpty(mapsList)) { - if (countEnabled == 1) { - LocalIndexInfo li = mapsList.get(0); - runLiveUpdate(context, li.getFileName(), false, listener); - } else if (countEnabled > 1) { - AlertDialog.Builder bld = new AlertDialog.Builder(context); - bld.setMessage(R.string.update_all_maps_now); - bld.setPositiveButton(R.string.shared_string_yes, new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - for (LocalIndexInfo li : mapsList) { - CommonPreference localUpdateOn = preferenceForLocalIndex(li.getFileName(), settings); - if (localUpdateOn.get()) { - runLiveUpdate(context, li.getFileName(), false, listener); - } - } - } - }); - bld.setNegativeButton(R.string.shared_string_no, null); - bld.show(); - } - } - } - - private void enableLiveUpdates(boolean enable) { - if (!Algorithms.isEmpty(adapter.mapsList)) { - AlarmManager alarmMgr = (AlarmManager) app.getSystemService(Context.ALARM_SERVICE); - for (LocalIndexInfo li : adapter.mapsList) { - CommonPreference localUpdateOn = preferenceForLocalIndex(li.getFileName(), settings); - if (localUpdateOn.get()) { - String fileName = li.getFileName(); - PendingIntent alarmIntent = getPendingIntent(app, fileName); - if (enable) { - final CommonPreference updateFrequencyPreference = - preferenceUpdateFrequency(fileName, settings); - final CommonPreference timeOfDayPreference = - preferenceTimeOfDayToUpdate(fileName, settings); - UpdateFrequency updateFrequency = UpdateFrequency.values()[updateFrequencyPreference.get()]; - TimeOfDay timeOfDayToUpdate = TimeOfDay.values()[timeOfDayPreference.get()]; - setAlarmForPendingIntent(alarmIntent, alarmMgr, updateFrequency, timeOfDayToUpdate); - } else { - alarmMgr.cancel(alarmIntent); - } - } - } - } - } - - private void expandAllGroups() { - for (int i = 0; i < adapter.getGroupCount(); i++) { - listView.expandGroup(i); - } - } - - public static int updateCountEnabled(TextView countView, ArrayList mapsList, OsmandSettings settings) { - int countEnabled = 0; - if (countView != null) { - for (LocalIndexInfo map : mapsList) { - CommonPreference preference = preferenceForLocalIndex(map.getFileName(), settings); - if (preference.get()) { - countEnabled++; - } - } - String countText = countEnabled + "/" + mapsList.size(); - countView.setText(countText); - } - return countEnabled; - } - - protected class LiveMapsAdapter extends OsmandBaseExpandableListAdapter implements LocalIndexInfoAdapter { - private final ArrayList mapsList = new ArrayList<>(); - private int countEnabled = 0; - private TextViewEx countView; - - @Override - public void addData(LocalIndexInfo info) { - mapsList.add(info); - } - - @Override - public void clearData() { - mapsList.clear(); - } - - @Override - public void onDataUpdated() { - sort(); - countEnabled = updateCountEnabled(countView, mapsList, settings); - } - - public void sort() { - Collections.sort(mapsList); - Collections.sort(mapsList, new Comparator() { - @Override - public int compare(LocalIndexInfo o1, LocalIndexInfo o2) { - CommonPreference preference1 = preferenceForLocalIndex(o1.getFileName(), getSettings()); - CommonPreference preference2 = preferenceForLocalIndex(o2.getFileName(), getSettings()); - return preference2.get().compareTo(preference1.get()); - } - }); - notifyDataSetInvalidated(); - } - - @Override - public LocalIndexInfo getChild(int groupPosition, int childPosition) { - return mapsList.get(childPosition); - } - - @Override - public long getChildId(int groupPosition, int childPosition) { - return groupPosition * 10000 + childPosition; // it would be unusable to have 10000 local indexes - } - - @Override - public View getChildView(final int groupPosition, final int childPosition, - boolean isLastChild, View convertView, ViewGroup parent) { - LayoutInflater inflater = UiUtilities.getInflater(app, nightMode); - convertView = inflater.inflate(R.layout.list_item_triple_row_icon_and_menu, parent, false); - LiveMapsViewHolder viewHolder = new LiveMapsViewHolder(convertView); - convertView.setTag(viewHolder); - viewHolder.bindLocalIndexInfo(getChild(groupPosition, childPosition).getFileName()); - return convertView; - } - - @Override - public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { - View view = convertView; - if (view == null) { - LayoutInflater inflater = UiUtilities.getInflater(app, nightMode); - view = inflater.inflate(R.layout.list_group_title_with_right_descr, parent, false); - } - view.setOnClickListener(null); - View topShadowView = view.findViewById(R.id.bottomShadowView); - if (groupPosition == 0) { - topShadowView.setVisibility(View.GONE); - } else { - topShadowView.setVisibility(View.VISIBLE); - } - - TextViewEx titleView = ((TextViewEx) view.findViewById(R.id.title)); - titleView.setText(getGroup(groupPosition)); - - countView = ((TextViewEx) view.findViewById(R.id.description)); - AndroidUtils.setTextSecondaryColor(app, countView, nightMode); - - return view; - } - - @Override - public int getChildrenCount(int groupPosition) { - return mapsList.size(); - } - - @Override - public String getGroup(int groupPosition) { - return getString(R.string.available_maps); - } - - @Override - public int getGroupCount() { - return 1; - } - - @Override - public long getGroupId(int groupPosition) { - return groupPosition; - } - - @Override - public boolean hasStableIds() { - return false; - } - - @Override - public boolean isChildSelectable(int groupPosition, int childPosition) { - return true; - } - } - - private class LiveMapsViewHolder { - private final ImageView statusIcon; - private final TextView title; - private final TextView subTitle; - private final TextView description; - private final CompoundButton option; - - private LiveMapsViewHolder(View view) { - statusIcon = (AppCompatImageView) view.findViewById(R.id.icon); - title = (TextView) view.findViewById(R.id.title); - subTitle = (TextView) view.findViewById(R.id.sub_title); - description = (TextView) view.findViewById(R.id.description); - option = (CompoundButton) view.findViewById(R.id.compound_button); - } - - public void bindLocalIndexInfo(@NonNull final String item) { - boolean liveUpdateOn = settings.IS_LIVE_UPDATES_ON.get(); - CommonPreference localUpdateOn = preferenceForLocalIndex(item, settings); -// IncrementalChangesManager changesManager = app.getResourceManager().getChangesManager(); - option.setChecked(localUpdateOn.get()); - if (!liveUpdateOn && localUpdateOn.get()) { - UiUtilities.setupCompoundButton(nightMode, ContextCompat.getColor(app, getTertiaryTextColorId(nightMode)), option); - } else { - UiUtilities.setupCompoundButton(option, nightMode, CompoundButtonType.GLOBAL); - } - - title.setText(getNameToDisplay(item, app)); - - AndroidUiHelper.updateVisibility(subTitle, localUpdateOn.get()); - if (localUpdateOn.get()) { - int frequencyId = preferenceUpdateFrequency(item, settings).get(); - final UpdateFrequency frequency = UpdateFrequency.values()[frequencyId]; - String subTitleText = getString(frequency.getLocalizedId()); - /*int timeOfDateToUpdateId = preferenceTimeOfDayToUpdate(item, settings).get(); - final TimeOfDay timeOfDay = TimeOfDay.values()[timeOfDateToUpdateId]; - if (frequency != UpdateFrequency.HOURLY) { - subTitleText += " • " + getString(timeOfDay.getLocalizedId()); - }*/ - subTitle.setText(subTitleText); - subTitle.setTextColor(ContextCompat.getColor(app, liveUpdateOn - ? getActiveTextColorId(nightMode) : getSecondaryTextColorId(nightMode))); - Typeface typeface = FontCache.getFont(app, getString(R.string.font_roboto_medium)); - subTitle.setTypeface(typeface); - } - - Drawable statusDrawable = AppCompatResources.getDrawable(app, R.drawable.ic_map); - int resColorId = !localUpdateOn.get() ? getSecondaryIconColorId(nightMode) : - !liveUpdateOn ? getDefaultIconColorId(nightMode) : getOsmandIconColorId(nightMode); - int statusColor = ContextCompat.getColor(app, resColorId); - if (statusDrawable != null) { - DrawableCompat.setTint(statusDrawable, statusColor); - } - statusIcon.setImageDrawable(statusDrawable); - - description.setText(getLastCheckString(item, app)); - - if (InAppPurchaseHelper.isSubscribedToLiveUpdates(app)) { - option.setEnabled(liveUpdateOn); - option.setOnCheckedChangeListener(new SwitchCompat.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - onUpdateLocalIndex(item, isChecked, null); - } - }); - } else { - option.setEnabled(false); - } - } - } - - public static class GetLastUpdateDateTask extends AsyncTask { - - private final OsmandApplication app; - private final WeakReference fragment; - - GetLastUpdateDateTask(LiveUpdatesFragmentNew fragment) { - this.fragment = new WeakReference<>(fragment); - app = fragment.getMyApplication(); - } - - @Override - protected String doInBackground(Void... params) { - try { - return AndroidNetworkUtils.sendRequest(app, URL, null, - "Requesting map updates info...", false, false); - } catch (Exception e) { - LOG.error("Error: " + "Requesting map updates info error", e); - return null; - } - } - - @Override - protected void onPostExecute(String response) { - LiveUpdatesFragmentNew f = fragment.get(); - if (response != null && f != null) { - TextViewEx descriptionTime = f.descriptionTime; - if (descriptionTime != null) { - SimpleDateFormat source = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US); - source.setTimeZone(TimeZone.getTimeZone("UTC")); - SimpleDateFormat dest = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US); - dest.setTimeZone(TimeZone.getDefault()); - try { - LOG.debug("response = " + response); - Date parsed = source.parse(response); - if (parsed != null) { - long dateTime = parsed.getTime(); - LOG.debug("dateTime = " + dateTime); - descriptionTime.setText(dest.format(parsed)); - } - } catch (ParseException e) { - LOG.error(e.getMessage()); - } - } - } - } - } - - protected static String getLastCheckString(String fileName, OsmandApplication app) { - return getLastCheckString(fileName, app, false); - } - - protected static String getLastCheckString(String fileName, OsmandApplication app, boolean lastTimeChecked) { - OsmandSettings settings = app.getSettings(); - - final long lastUpdate = preferenceLatestUpdateAvailable(fileName, settings).get(); - String lastUpdateString = formatShortDateTime(app, lastUpdate); - String description = app.getString(R.string.updated, lastUpdateString); - - if (lastTimeChecked) { - final long lastCheck = preferenceLastCheck(fileName, settings).get(); - String lastCheckString = formatShortDateTime(app, lastCheck); - if (!lastUpdateString.equals(app.getString(R.string.shared_string_never))) { - description = description.concat("\n" + app.getString(R.string.last_time_checked, lastCheckString)); - } - } - return description; - } - - @Override - public boolean onUpdateLocalIndex(String fileName, boolean newValue, final Runnable callback) { - - int frequencyId = preferenceUpdateFrequency(fileName, settings).get(); - int timeOfDateToUpdateId = preferenceTimeOfDayToUpdate(fileName, settings).get(); - final AlarmManager alarmManager = (AlarmManager) app.getSystemService(Context.ALARM_SERVICE); - final PendingIntent alarmIntent = getPendingIntent(app, fileName); - - final CommonPreference liveUpdatePreference = preferenceForLocalIndex(fileName, settings); - liveUpdatePreference.set(newValue); - if (settings.IS_LIVE_UPDATES_ON.get() && liveUpdatePreference.get()) { - runLiveUpdate(getActivity(), fileName, true, new LiveUpdateListener() { - @Override - public void processFinish() { - runSort(); - if (callback != null) { - callback.run(); - } - } - }); - UpdateFrequency updateFrequency = UpdateFrequency.values()[frequencyId]; - TimeOfDay timeOfDayToUpdate = TimeOfDay.values()[timeOfDateToUpdateId]; - setAlarmForPendingIntent(alarmIntent, alarmManager, updateFrequency, timeOfDayToUpdate); - } else { - alarmManager.cancel(alarmIntent); - runSort(); - } - - return true; - } - - @Override - public void forceUpdateLocal(String fileName, boolean userRequested, final Runnable callback) { - if (settings.IS_LIVE_UPDATES_ON.get()) { - runLiveUpdate(getActivity(), fileName, userRequested, new LiveUpdateListener() { - @Override - public void processFinish() { - updateList(); - if (callback != null) { - callback.run(); - } - } - }); - } - } - - @Override - public void runSort() { - if (adapter != null) { - adapter.onDataUpdated(); - } - } - - @Override - public void updateList() { - if (adapter != null) { - adapter.notifyDataSetChanged(); - } - } - - @ColorRes - public static int getDefaultIconColorId(boolean nightMode) { - return nightMode ? R.color.icon_color_default_dark : R.color.icon_color_default_light; - } - -} diff --git a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesHelper.java b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesHelper.java index afcb313217..1833cd397c 100644 --- a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesHelper.java +++ b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesHelper.java @@ -9,9 +9,10 @@ import android.text.format.DateUtils; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.liveupdates.PerformLiveUpdateAsyncTask.LiveUpdateListener; +import net.osmand.plus.activities.LocalIndexInfo; import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; @@ -20,6 +21,7 @@ import net.osmand.util.Algorithms; import java.io.File; import java.util.Calendar; +import java.util.List; import java.util.concurrent.TimeUnit; public class LiveUpdatesHelper { @@ -254,8 +256,27 @@ public class LiveUpdatesHelper { } } - public static void runLiveUpdate(Context context, final String fileName, boolean userRequested, @Nullable final LiveUpdateListener listener) { + public static void runLiveUpdate(Context context, final String fileName, boolean userRequested, @Nullable final Runnable runOnSuccess) { final String fnExt = Algorithms.getFileNameWithoutExtension(new File(fileName)); - new PerformLiveUpdateAsyncTask(context, fileName, userRequested, listener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, fnExt); + PerformLiveUpdateAsyncTask task = new PerformLiveUpdateAsyncTask(context, fileName, userRequested); + task.setRunOnSuccess(runOnSuccess); + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, fnExt); + } + + public static void runLiveUpdate(Context context, boolean userRequested, final LiveUpdateListener listener) { + for (LocalIndexInfo mapToUpdate : listener.getMapsToUpdate()) { + runLiveUpdate(context, mapToUpdate.getFileName(), userRequested, new Runnable() { + @Override + public void run() { + listener.processFinish(); + } + }); + } + } + + public interface LiveUpdateListener { + void processFinish(); + + List getMapsToUpdate(); } } diff --git a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesSettingsDialogFragmentNew.java b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesSettingsBottomSheet.java similarity index 72% rename from OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesSettingsDialogFragmentNew.java rename to OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesSettingsBottomSheet.java index 776368e6a7..a40d0e956c 100644 --- a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesSettingsDialogFragmentNew.java +++ b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesSettingsBottomSheet.java @@ -36,7 +36,7 @@ import net.osmand.plus.base.bottomsheetmenu.simpleitems.DividerSpaceItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.ShortDescriptionItem; import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.helpers.FontCache; -import net.osmand.plus.liveupdates.LiveUpdatesClearDialogFragment.RefreshLiveUpdates; +import net.osmand.plus.liveupdates.LiveUpdatesClearBottomSheet.RefreshLiveUpdates; import net.osmand.plus.liveupdates.LiveUpdatesHelper.TimeOfDay; import net.osmand.plus.liveupdates.LiveUpdatesHelper.UpdateFrequency; import net.osmand.plus.resources.IncrementalChangesManager; @@ -68,28 +68,30 @@ import static net.osmand.plus.liveupdates.LiveUpdatesHelper.preferenceUpdateFreq import static net.osmand.plus.settings.bottomsheets.BooleanPreferenceBottomSheet.getCustomButtonView; import static net.osmand.plus.settings.bottomsheets.BooleanPreferenceBottomSheet.updateCustomButtonView; -public class LiveUpdatesSettingsDialogFragmentNew extends MenuBottomSheetDialogFragment implements RefreshLiveUpdates { +public class LiveUpdatesSettingsBottomSheet extends MenuBottomSheetDialogFragment implements RefreshLiveUpdates { - public static final String TAG = LiveUpdatesSettingsDialogFragmentNew.class.getSimpleName(); - private static final Log LOG = PlatformUtil.getLog(LiveUpdatesSettingsDialogFragmentNew.class); + public static final String TAG = LiveUpdatesSettingsBottomSheet.class.getSimpleName(); + private static final Log LOG = PlatformUtil.getLog(LiveUpdatesSettingsBottomSheet.class); private static final String LOCAL_INDEX_FILE_NAME = "local_index_file_name"; private OsmandApplication app; private OsmandSettings settings; - private OnLiveUpdatesForLocalChange onLiveUpdatesForLocalChange; + + private BaseBottomSheetItem itemTitle; + private BaseBottomSheetItem itemLastCheck; + private BaseBottomSheetItem itemSwitchLiveUpdate; + private BaseBottomSheetItem itemFrequencyHelpMessage; + private BaseBottomSheetItem itemClear; + private BaseBottomSheetItem itemViaWiFi; private MultiStateToggleButton frequencyToggleButton; private MultiStateToggleButton timeOfDayToggleButton; private String fileName; - private int indexLastCheckItem = -1; - private int indexSwitchLiveUpdateItem = -1; - private int indexFrequencyHelpMessageItem = -1; - private int indexClearItem = -1; - private int indexViaWiFiItem = -1; + private OnLiveUpdatesForLocalChange onLiveUpdatesForLocalChange; public static void showInstance(@NonNull FragmentManager fragmentManager, Fragment target, String fileName) { if (!fragmentManager.isStateSaved()) { - LiveUpdatesSettingsDialogFragmentNew fragment = new LiveUpdatesSettingsDialogFragmentNew(); + LiveUpdatesSettingsBottomSheet fragment = new LiveUpdatesSettingsBottomSheet(); fragment.setTargetFragment(target, 0); fragment.fileName = fileName; fragment.show(fragmentManager, TAG); @@ -111,31 +113,33 @@ public class LiveUpdatesSettingsDialogFragmentNew extends MenuBottomSheetDialogF CommonPreference downloadViaWiFiPreference = preferenceDownloadViaWiFi(fileName, settings); CommonPreference frequencyPreference = preferenceUpdateFrequency(fileName, settings); CommonPreference timeOfDayPreference = preferenceTimeOfDayToUpdate(fileName, settings); + int dp4 = getDimen(R.dimen.list_item_button_padding); int dp12 = getDimen(R.dimen.content_padding_small); int dp16 = getDimen(R.dimen.content_padding); - int dp40 = getDimen(R.dimen.list_header_height); + int dp36 = getDimen(R.dimen.dialog_button_height); int dp48 = getDimen(R.dimen.context_menu_buttons_bottom_height); - items.add(new SimpleBottomSheetItem.Builder() + itemTitle = new SimpleBottomSheetItem.Builder() .setTitle(getNameToDisplay(fileName, app)) .setTitleColorId(getPrimaryTextColorId(nightMode)) .setLayoutId(R.layout.bottom_sheet_item_title_big) - .create()); + .create(); + items.add(itemTitle); - items.add(new ShortDescriptionItem.Builder() + itemLastCheck = new ShortDescriptionItem.Builder() .setDescription(getLastCheckString()) .setDescriptionColorId(getSecondaryTextColorId(nightMode)) .setDescriptionMaxLines(2) .setLayoutId(R.layout.bottom_sheet_item_description) - .create()); - indexLastCheckItem = items.size() - 1; + .create(); + items.add(itemLastCheck); View itemLiveUpdate = getCustomButtonView(app, null, localUpdatePreference.get(), nightMode); View itemLiveUpdateButton = itemLiveUpdate.findViewById(R.id.button_container); CompoundButton button = (CompoundButton) itemLiveUpdateButton.findViewById(R.id.compound_button); UiUtilities.setupCompoundButton(button, nightMode, TOOLBAR); itemLiveUpdateButton.setMinimumHeight(getDimen(R.dimen.bottom_sheet_selected_item_title_height)); - items.add(new BottomSheetItemWithCompoundButton.Builder() + itemSwitchLiveUpdate = new BottomSheetItemWithCompoundButton.Builder() .setChecked(localUpdatePreference.get()) .setTitle(getStateText(localUpdatePreference.get())) .setTitleColorId(getActiveTabTextColorId(nightMode)) @@ -143,45 +147,84 @@ public class LiveUpdatesSettingsDialogFragmentNew extends MenuBottomSheetDialogF .setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - if (indexSwitchLiveUpdateItem != -1) { - BottomSheetItemWithCompoundButton button = (BottomSheetItemWithCompoundButton) items.get(indexSwitchLiveUpdateItem); - button.setChecked(!button.isChecked()); - if (onLiveUpdatesForLocalChange != null - && onLiveUpdatesForLocalChange.onUpdateLocalIndex(fileName, button.isChecked(), new Runnable() { - @Override - public void run() { - updateLastCheck(); - updateFrequencyHelpMessage(); - updateFileSize(); + BottomSheetItemWithCompoundButton item = (BottomSheetItemWithCompoundButton) itemSwitchLiveUpdate; + boolean checked = item.isChecked(); + item.setChecked(!checked); + if (onLiveUpdatesForLocalChange != null + && onLiveUpdatesForLocalChange.onUpdateLocalIndex(fileName, !checked, new Runnable() { + @Override + public void run() { + updateLastCheck(); + updateFrequencyHelpMessage(); + updateFileSize(); + Fragment target = getTargetFragment(); + if (target instanceof LiveUpdatesFragment) { + ((LiveUpdatesFragment) target).runSort(); } - })) { - if (indexSwitchLiveUpdateItem != -1 && items.size() > 0) { - button.setTitle(getStateText(button.isChecked())); - updateCustomButtonView(app, null, button.getView(), button.isChecked(), nightMode); - } - CommonPreference localUpdatePreference = preferenceForLocalIndex(fileName, settings); - frequencyToggleButton.updateView(localUpdatePreference.get()); - timeOfDayToggleButton.updateView(localUpdatePreference.get()); - setStateViaWiFiButton(localUpdatePreference); } + })) { + item.setTitle(getStateText(!checked)); + updateCustomButtonView(app, null, item.getView(), !checked, nightMode); + CommonPreference localUpdatePreference = preferenceForLocalIndex(fileName, settings); + frequencyToggleButton.updateView(localUpdatePreference.get()); + timeOfDayToggleButton.updateView(localUpdatePreference.get()); + setStateViaWiFiButton(localUpdatePreference); } } }) + .create(); + items.add(itemSwitchLiveUpdate); + + Typeface typefaceRegular = FontCache.getRobotoRegular(app); + TextViewEx timeOfDayTitle = (TextViewEx) inflater.inflate(R.layout.bottom_sheet_item_title, null); + timeOfDayTitle.setHeight(dp48); + timeOfDayTitle.setMinimumHeight(dp48); + timeOfDayTitle.setText(R.string.update_time); + timeOfDayTitle.setTypeface(typefaceRegular); + AndroidUtils.setPadding(timeOfDayTitle, timeOfDayTitle.getPaddingLeft(), dp4, timeOfDayTitle.getPaddingRight(), dp4); + AndroidUtils.setTextPrimaryColor(app, timeOfDayTitle, nightMode); + items.add(new BaseBottomSheetItem.Builder() + .setCustomView(timeOfDayTitle) .create()); - indexSwitchLiveUpdateItem = items.size() - 1; + + LinearLayout itemTimeOfDayButtons = (LinearLayout) inflater.inflate(R.layout.custom_radio_buttons, null); + LinearLayout.MarginLayoutParams itemTimeOfDayParams = new LinearLayout.MarginLayoutParams( + LinearLayout.MarginLayoutParams.MATCH_PARENT, LinearLayout.MarginLayoutParams.WRAP_CONTENT); + AndroidUtils.setMargins(itemTimeOfDayParams, dp16, 0, dp16, 0); + itemTimeOfDayButtons.setLayoutParams(itemTimeOfDayParams); + + String morning = getString(R.string.morning); + String night = getString(R.string.night); + RadioItem morningButton = new RadioItem(morning); + RadioItem nightButton = new RadioItem(night); + timeOfDayToggleButton = new MultiStateToggleButton(app, itemTimeOfDayButtons, nightMode); + timeOfDayToggleButton.setItems(morningButton, nightButton); + setSelectedRadioItem(timeOfDayToggleButton, timeOfDayPreference.get(), morningButton, nightButton); + timeOfDayToggleButton.updateView(localUpdatePreference.get()); + refreshTimeOfDayLayout(frequencyPreference.get(), itemTimeOfDayButtons, timeOfDayTitle); + + morningButton.setOnClickListener(getTimeOfDayButtonListener(TimeOfDay.MORNING)); + nightButton.setOnClickListener(getTimeOfDayButtonListener(TimeOfDay.NIGHT)); + + items.add(new BaseBottomSheetItem.Builder() + .setCustomView(itemTimeOfDayButtons) + .create() + ); TextViewEx frequencyTitle = (TextViewEx) inflater.inflate(R.layout.bottom_sheet_item_title, null); frequencyTitle.setHeight(dp48); frequencyTitle.setMinimumHeight(dp48); frequencyTitle.setText(R.string.update_frequency); - AndroidUtils.setPadding(frequencyTitle, frequencyTitle.getPaddingLeft(), 0, frequencyTitle.getPaddingRight(), 0); + frequencyTitle.setTypeface(typefaceRegular); + AndroidUtils.setPadding(frequencyTitle, frequencyTitle.getPaddingLeft(), dp4, frequencyTitle.getPaddingRight(), dp4); AndroidUtils.setTextPrimaryColor(app, frequencyTitle, nightMode); items.add(new BaseBottomSheetItem.Builder() .setCustomView(frequencyTitle) .create()); LinearLayout itemFrequencyButtons = (LinearLayout) inflater.inflate(R.layout.custom_radio_buttons, null); - LinearLayout.MarginLayoutParams itemFrequencyParams = new LinearLayout.MarginLayoutParams(LinearLayout.MarginLayoutParams.MATCH_PARENT, LinearLayout.MarginLayoutParams.WRAP_CONTENT); + LinearLayout.MarginLayoutParams itemFrequencyParams = new LinearLayout.MarginLayoutParams( + LinearLayout.MarginLayoutParams.MATCH_PARENT, LinearLayout.MarginLayoutParams.WRAP_CONTENT); AndroidUtils.setMargins(itemFrequencyParams, dp16, 0, dp16, dp12); itemFrequencyButtons.setLayoutParams(itemFrequencyParams); @@ -200,58 +243,27 @@ public class LiveUpdatesSettingsDialogFragmentNew extends MenuBottomSheetDialogF .setCustomView(itemFrequencyButtons) .create()); - TextViewEx timeOfDayTitle = (TextViewEx) inflater.inflate(R.layout.bottom_sheet_item_title, null); - timeOfDayTitle.setHeight(dp40); - timeOfDayTitle.setMinimumHeight(dp40); - timeOfDayTitle.setText(R.string.update_time); - AndroidUtils.setPadding(timeOfDayTitle, timeOfDayTitle.getPaddingLeft(), 0, timeOfDayTitle.getPaddingRight(), 0); - AndroidUtils.setTextPrimaryColor(app, timeOfDayTitle, nightMode); - items.add(new BaseBottomSheetItem.Builder() - .setCustomView(timeOfDayTitle) - .create()); - - LinearLayout itemTimeOfDayButtons = (LinearLayout) inflater.inflate(R.layout.custom_radio_buttons, null); - LinearLayout.MarginLayoutParams itemTimeOfDayParams = new LinearLayout.MarginLayoutParams(LinearLayout.MarginLayoutParams.MATCH_PARENT, LinearLayout.MarginLayoutParams.WRAP_CONTENT); - AndroidUtils.setMargins(itemTimeOfDayParams, dp16, 0, dp16, getDimen(R.dimen.context_menu_padding_margin_medium)); - itemTimeOfDayButtons.setLayoutParams(itemTimeOfDayParams); - - String morning = getString(R.string.morning); - String night = getString(R.string.night); - RadioItem morningButton = new RadioItem(morning); - RadioItem nightButton = new RadioItem(night); - timeOfDayToggleButton = new MultiStateToggleButton(app, itemTimeOfDayButtons, nightMode); - timeOfDayToggleButton.setItems(morningButton, nightButton); - setSelectedRadioItem(timeOfDayToggleButton, timeOfDayPreference.get(), morningButton, nightButton); - timeOfDayToggleButton.updateView(localUpdatePreference.get()); - refreshTimeOfDayLayout(frequencyPreference.get(), itemTimeOfDayButtons, timeOfDayTitle); - hourlyButton.setOnClickListener(getFrequencyButtonListener(UpdateFrequency.HOURLY, itemTimeOfDayButtons, timeOfDayTitle)); dailyButton.setOnClickListener(getFrequencyButtonListener(UpdateFrequency.DAILY, itemTimeOfDayButtons, timeOfDayTitle)); weeklyButton.setOnClickListener(getFrequencyButtonListener(UpdateFrequency.WEEKLY, itemTimeOfDayButtons, timeOfDayTitle)); - morningButton.setOnClickListener(getTimeOfDayButtonListener(TimeOfDay.MORNING)); - nightButton.setOnClickListener(getTimeOfDayButtonListener(TimeOfDay.NIGHT)); - items.add(new BaseBottomSheetItem.Builder() - .setCustomView(itemTimeOfDayButtons) - .create() - ); - - items.add(new ShortDescriptionItem.Builder() + itemFrequencyHelpMessage = new ShortDescriptionItem.Builder() .setDescription(getFrequencyHelpMessage()) .setDescriptionColorId(getSecondaryTextColorId(nightMode)) .setLayoutId(R.layout.bottom_sheet_item_description) - .create()); - indexFrequencyHelpMessageItem = items.size() - 1; + .create(); + items.add(itemFrequencyHelpMessage); - LinearLayout itemUpdateNowButton = (LinearLayout) inflater.inflate(R.layout.bottom_sheet_button_with_icon_center, null); + LinearLayout itemUpdateNowButton = + (LinearLayout) inflater.inflate(R.layout.bottom_sheet_button_with_icon_center, null); LinearLayout.MarginLayoutParams itemUpdateNowParams = new LinearLayout.MarginLayoutParams( - LinearLayout.MarginLayoutParams.MATCH_PARENT, getDimen(R.dimen.measurement_tool_button_height)); - AndroidUtils.setMargins(itemUpdateNowParams, dp12, dp12, dp16, dp12); + LinearLayout.MarginLayoutParams.MATCH_PARENT, dp36); + AndroidUtils.setMargins(itemUpdateNowParams, dp12, 0, dp16, dp12); itemUpdateNowButton.setLayoutParams(itemUpdateNowParams); ((AppCompatImageView) itemUpdateNowButton.findViewById(R.id.button_icon)).setImageDrawable( AppCompatResources.getDrawable(app, R.drawable.ic_action_update)); UiUtilities.setupDialogButton(nightMode, itemUpdateNowButton, UiUtilities.DialogButtonType.SECONDARY, getString(R.string.update_now)); - itemUpdateNowButton.setMinimumHeight(AndroidUtils.dpToPx(app, app.getResources().getDimension(R.dimen.dialog_button_height))); + itemUpdateNowButton.setMinimumHeight(AndroidUtils.dpToPx(app, dp36)); items.add(new BaseBottomSheetItem.Builder() .setCustomView(itemUpdateNowButton) @@ -268,6 +280,10 @@ public class LiveUpdatesSettingsDialogFragmentNew extends MenuBottomSheetDialogF updateLastCheck(); updateFrequencyHelpMessage(); updateFileSize(); + Fragment target = getTargetFragment(); + if (target instanceof LiveUpdatesFragment) { + ((LiveUpdatesFragment) target).updateList(); + } } }); } @@ -281,51 +297,47 @@ public class LiveUpdatesSettingsDialogFragmentNew extends MenuBottomSheetDialogF int iconDeleteColor = ContextCompat.getColor(app, R.color.color_osm_edit_delete); Drawable iconDelete = AppCompatResources.getDrawable(app, R.drawable.ic_action_delete_dark); - items.add( - new BottomSheetItemWithDescription.Builder() - .setDescription(getUpdatesSizeStr()) - .setIcon(UiUtilities.tintDrawable(iconDelete, iconDeleteColor)) - .setTitle(getString(R.string.updates_size)) - .setLayoutId(R.layout.bottom_sheet_item_with_descr_icon_right) - .setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (getUpdatesSize() > 0) { - if (getFragmentManager() != null) { - LiveUpdatesClearDialogFragment - .showInstance(getFragmentManager(), - LiveUpdatesSettingsDialogFragmentNew.this, fileName); - } - } + itemClear = new BottomSheetItemWithDescription.Builder() + .setDescription(getUpdatesSizeStr()) + .setIcon(UiUtilities.tintDrawable(iconDelete, iconDeleteColor)) + .setTitle(getString(R.string.updates_size)) + .setLayoutId(R.layout.bottom_sheet_item_with_descr_icon_right) + .setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (getUpdatesSize() > 0) { + if (getFragmentManager() != null) { + LiveUpdatesClearBottomSheet.showInstance(getFragmentManager(), + LiveUpdatesSettingsBottomSheet.this, fileName); } - }) - .create() - ); - indexClearItem = items.size() - 1; + } + } + }) + .create(); + items.add(itemClear); items.add(createDividerItem()); - items.add( - new BottomSheetItemWithCompoundButton.Builder() - .setChecked(downloadViaWiFiPreference.get()) - .setDescription(getStateText(downloadViaWiFiPreference.get())) - .setIconHidden(true) - .setTitle(getString(R.string.only_download_over_wifi)) - .setLayoutId(R.layout.bottom_sheet_item_with_descr_and_switch_56dp) - .setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (preferenceForLocalIndex(fileName, settings).get() && indexViaWiFiItem != -1 && items.size() > 0) { - BottomSheetItemWithCompoundButton button = (BottomSheetItemWithCompoundButton) items.get(indexViaWiFiItem); - button.setChecked(!button.isChecked()); - button.setDescription(getStateText(button.isChecked())); - preferenceDownloadViaWiFi(fileName, settings).set(button.isChecked()); - } - } - }) - .create() - ); - indexViaWiFiItem = items.size() - 1; + itemViaWiFi = new BottomSheetItemWithCompoundButton.Builder() + .setChecked(downloadViaWiFiPreference.get()) + .setDescription(getStateText(downloadViaWiFiPreference.get())) + .setIconHidden(true) + .setTitle(getString(R.string.only_download_over_wifi)) + .setLayoutId(R.layout.bottom_sheet_item_with_descr_and_switch_56dp) + .setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (preferenceForLocalIndex(fileName, settings).get()) { + BottomSheetItemWithCompoundButton item = (BottomSheetItemWithCompoundButton) itemViaWiFi; + boolean checked = item.isChecked(); + item.setChecked(!checked); + item.setDescription(getStateText(!checked)); + preferenceDownloadViaWiFi(fileName, settings).set(!checked); + } + } + }) + .create(); + items.add(itemViaWiFi); items.add(new DividerSpaceItem(app, getDimen(R.dimen.context_menu_padding_margin_large))); } @@ -334,6 +346,24 @@ public class LiveUpdatesSettingsDialogFragmentNew extends MenuBottomSheetDialogF @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { View view = super.onCreateView(inflater, parent, savedInstanceState); + + int titleHeight = getDimen(R.dimen.bottom_sheet_descr_height); + TextViewEx titleView = (TextViewEx) itemTitle.getView(); + titleView.setMinimumHeight(titleHeight); + titleView.getLayoutParams().height = titleHeight; + titleView.setPadding(titleView.getPaddingLeft(), getDimen(R.dimen.content_padding_small), + titleView.getPaddingRight(), getDimen(R.dimen.list_item_button_padding)); + + int descriptionHeight = getDimen(R.dimen.bottom_sheet_title_height); + TextView descriptionView = (TextView) itemLastCheck.getView(); + descriptionView.setMinimumHeight(descriptionHeight); + descriptionView.getLayoutParams().height = descriptionHeight; + + int frequencyHelpMessageHeight = getDimen(R.dimen.context_menu_progress_min_height); + TextView frequencyHelpMessageView = (TextView) itemFrequencyHelpMessage.getView(); + frequencyHelpMessageView.setMinimumHeight(frequencyHelpMessageHeight); + frequencyHelpMessageView.getLayoutParams().height = frequencyHelpMessageHeight; + CommonPreference localUpdatePreference = preferenceForLocalIndex(fileName, settings); setStateViaWiFiButton(localUpdatePreference); return view; @@ -346,12 +376,12 @@ public class LiveUpdatesSettingsDialogFragmentNew extends MenuBottomSheetDialogF } private void setStateViaWiFiButton(CommonPreference localUpdatePreference) { - if (indexViaWiFiItem != -1 && items.size() > 0) { - BottomSheetItemWithCompoundButton button = (BottomSheetItemWithCompoundButton) items.get(indexViaWiFiItem); - if (button.getView() != null) { - TextView title = button.getView().findViewById(R.id.title); - TextView description = button.getView().findViewById(R.id.description); - CompoundButton compoundButton = button.getView().findViewById(R.id.compound_button); + if (itemViaWiFi != null) { + BottomSheetItemWithCompoundButton item = (BottomSheetItemWithCompoundButton) itemViaWiFi; + if (item.getView() != null) { + TextView title = item.getView().findViewById(R.id.title); + TextView description = item.getView().findViewById(R.id.description); + CompoundButton compoundButton = item.getView().findViewById(R.id.compound_button); if (localUpdatePreference.get()) { AndroidUtils.setTextPrimaryColor(app, title, nightMode); AndroidUtils.setTextSecondaryColor(app, description, nightMode); @@ -366,23 +396,20 @@ public class LiveUpdatesSettingsDialogFragmentNew extends MenuBottomSheetDialogF } private void updateLastCheck() { - if (indexLastCheckItem != -1 && items.size() > 0) { - ((BottomSheetItemWithDescription) items.get(indexLastCheckItem)) - .setDescription(getLastCheckString()); + if (itemLastCheck != null) { + ((BottomSheetItemWithDescription) itemLastCheck).setDescription(getLastCheckString()); } } private void updateFrequencyHelpMessage() { - if (indexFrequencyHelpMessageItem != -1 && items.size() > 0) { - ((BottomSheetItemWithDescription) items.get(indexFrequencyHelpMessageItem)) - .setDescription(getFrequencyHelpMessage()); + if (itemFrequencyHelpMessage != null) { + ((BottomSheetItemWithDescription) itemFrequencyHelpMessage).setDescription(getFrequencyHelpMessage()); } } private void updateFileSize() { - if (indexClearItem != -1 && items.size() > 0) { - ((BottomSheetItemWithDescription) items.get(indexClearItem)) - .setDescription(getUpdatesSizeStr()); + if (itemClear != null) { + ((BottomSheetItemWithDescription) itemClear).setDescription(getUpdatesSizeStr()); } } @@ -414,7 +441,8 @@ public class LiveUpdatesSettingsDialogFragmentNew extends MenuBottomSheetDialogF CommonPreference updateFrequency = preferenceUpdateFrequency(fileName, settings); CommonPreference timeOfDayToUpdate = preferenceTimeOfDayToUpdate(fileName, settings); final long lastUpdate = preferenceLatestUpdateAvailable(fileName, settings).get(); - return formatHelpDateTime(app, UpdateFrequency.values()[updateFrequency.get()], TimeOfDay.values()[timeOfDayToUpdate.get()], lastUpdate); + return formatHelpDateTime(app, UpdateFrequency.values()[updateFrequency.get()], + TimeOfDay.values()[timeOfDayToUpdate.get()], lastUpdate); } private long getUpdatesSize() { @@ -433,8 +461,8 @@ public class LiveUpdatesSettingsDialogFragmentNew extends MenuBottomSheetDialogF private BaseBottomSheetItem createDividerItem() { DividerItem dividerItem = new DividerItem(app); - int start = app.getResources().getDimensionPixelSize(R.dimen.content_padding); - int vertical = getResources().getDimensionPixelSize(R.dimen.content_padding_small_half); + int start = getDimen(R.dimen.content_padding); + int vertical = getDimen(R.dimen.content_padding_small_half); dividerItem.setMargins(start, vertical, 0, vertical); return dividerItem; } @@ -459,7 +487,8 @@ public class LiveUpdatesSettingsDialogFragmentNew extends MenuBottomSheetDialogF } } - private OnRadioItemClickListener getFrequencyButtonListener(@NonNull final UpdateFrequency type, final View... timeOfDayLayouts) { + private OnRadioItemClickListener getFrequencyButtonListener( + @NonNull final UpdateFrequency type, final View... timeOfDayLayouts) { return new OnRadioItemClickListener() { @Override public boolean onRadioItemClick(RadioItem radioItem, View view) { @@ -544,5 +573,4 @@ public class LiveUpdatesSettingsDialogFragmentNew extends MenuBottomSheetDialogF protected int getDismissButtonTextId() { return R.string.shared_string_close; } - } diff --git a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesSettingsDialogFragment.java b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesSettingsDialogFragment.java deleted file mode 100644 index 242f44f83a..0000000000 --- a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesSettingsDialogFragment.java +++ /dev/null @@ -1,288 +0,0 @@ -package net.osmand.plus.liveupdates; - -import android.app.AlarmManager; -import android.app.Dialog; -import android.app.PendingIntent; -import android.content.Context; -import android.content.DialogInterface; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.Spinner; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.widget.SwitchCompat; -import androidx.fragment.app.DialogFragment; - -import net.osmand.AndroidUtils; -import net.osmand.PlatformUtil; -import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.CommonPreference; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.R; -import net.osmand.plus.download.AbstractDownloadActivity; -import net.osmand.plus.liveupdates.LiveUpdatesHelper.TimeOfDay; -import net.osmand.plus.liveupdates.LiveUpdatesHelper.UpdateFrequency; -import net.osmand.plus.resources.IncrementalChangesManager; -import net.osmand.util.Algorithms; - -import org.apache.commons.logging.Log; - -import java.io.File; - -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.DEFAULT_LAST_CHECK; -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.formatDateTime; -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.getNameToDisplay; -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.getPendingIntent; -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.preferenceDownloadViaWiFi; -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.preferenceForLocalIndex; -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.preferenceLastCheck; -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.preferenceLiveUpdatesOn; -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.preferenceTimeOfDayToUpdate; -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.preferenceUpdateFrequency; -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.runLiveUpdate; -import static net.osmand.plus.liveupdates.LiveUpdatesHelper.setAlarmForPendingIntent; - -public class LiveUpdatesSettingsDialogFragment extends DialogFragment { - private static final Log LOG = PlatformUtil.getLog(LiveUpdatesSettingsDialogFragment.class); - private static final String LOCAL_INDEX_FILE_NAME = "local_index_file_name"; - - private TextView sizeTextView; - - private String fileName; - private String fileNameWithoutExtension; - - @NonNull - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - fileName = getArguments().getString(LOCAL_INDEX_FILE_NAME); - assert fileName != null; - - View view = LayoutInflater.from(getActivity()) - .inflate(R.layout.dialog_live_updates_item_settings, null); - final TextView regionNameTextView = (TextView) view.findViewById(R.id.regionNameTextView); - final TextView lastMapChangeTextView = (TextView) view.findViewById(R.id.lastMapChangeTextView); - final TextView lastUpdateTextView = (TextView) view.findViewById(R.id.lastUpdateTextView); - final SwitchCompat liveUpdatesSwitch = (SwitchCompat) view.findViewById(R.id.toggle_item); - final CheckBox downloadOverWiFiCheckBox = (CheckBox) view.findViewById(R.id.downloadOverWiFiSwitch); - final Spinner updateFrequencySpinner = (Spinner) view.findViewById(R.id.updateFrequencySpinner); - final Spinner updateTimesOfDaySpinner = (Spinner) view.findViewById(R.id.updateTimesOfDaySpinner); - final View updateTimesOfDayLayout = view.findViewById(R.id.updateTimesOfDayLayout); - sizeTextView = (TextView) view.findViewById(R.id.sizeTextView); - - regionNameTextView.setText(getNameToDisplay(fileName, getMyActivity().getMyApplication())); - fileNameWithoutExtension = Algorithms.getFileNameWithoutExtension(new File(fileName)); - final IncrementalChangesManager changesManager = getMyApplication().getResourceManager().getChangesManager(); - final long timestamp = changesManager.getTimestamp(fileNameWithoutExtension); - String lastUpdateDate = formatDateTime(getActivity(), timestamp); - lastMapChangeTextView.setText(getString(R.string.last_map_change, lastUpdateDate)); - final long lastCheck = preferenceLastCheck(fileName, getSettings()).get(); - - - CommonPreference preference = preferenceLiveUpdatesOn(fileName, - getSettings()); - if (preference.get() && lastCheck != DEFAULT_LAST_CHECK) { - String lastCheckString = formatDateTime(getActivity(), lastCheck); - lastUpdateTextView.setText(getString(R.string.last_update, lastCheckString)); - } else { - lastUpdateTextView.setVisibility(View.GONE); - } - - final CommonPreference liveUpdatePreference = - preferenceForLocalIndex(fileName, getSettings()); - final CommonPreference downloadViaWiFiPreference = - preferenceDownloadViaWiFi(fileName, getSettings()); - final CommonPreference updateFrequencyPreference = - preferenceUpdateFrequency(fileName, getSettings()); - final CommonPreference timeOfDayPreference = - preferenceTimeOfDayToUpdate(fileName, getSettings()); - - downloadOverWiFiCheckBox.setChecked(!liveUpdatePreference.get() || downloadViaWiFiPreference.get()); - - sizeTextView.setText(getUpdatesSize(getMyActivity(), fileNameWithoutExtension, changesManager)); - - TimeOfDay[] timeOfDays = TimeOfDay.values(); - String[] timeOfDaysStrings = new String[timeOfDays.length]; - for (int i = 0; i < timeOfDays.length; i++) { - timeOfDaysStrings[i] = getString(timeOfDays[i].getLocalizedId()); - } - updateTimesOfDaySpinner.setAdapter(new ArrayAdapter<>(getActivity(), - R.layout.action_spinner_item, timeOfDaysStrings)); - updateTimesOfDaySpinner.setSelection(timeOfDayPreference.get()); - - UpdateFrequency[] updateFrequencies = UpdateFrequency.values(); - String[] updateFrequenciesStrings = new String[updateFrequencies.length]; - for (int i = 0; i < updateFrequencies.length; i++) { - updateFrequenciesStrings[i] = getString(updateFrequencies[i].getLocalizedId()); - } - - refreshTimeOfDayLayout(UpdateFrequency.values()[updateFrequencyPreference.get()], - updateTimesOfDayLayout); - updateFrequencySpinner.setAdapter(new ArrayAdapter<>(getActivity(), - R.layout.action_spinner_item, updateFrequenciesStrings)); - updateFrequencySpinner.setSelection(updateFrequencyPreference.get()); - updateFrequencySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - refreshTimeOfDayLayout(UpdateFrequency.values()[position], updateTimesOfDayLayout); - } - - @Override - public void onNothingSelected(AdapterView parent) { - - } - }); - - builder.setView(view) - .setPositiveButton(R.string.shared_string_ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (liveUpdatePreference.get() != liveUpdatesSwitch.isChecked()) { - liveUpdatePreference.set(liveUpdatesSwitch.isChecked()); - if (!liveUpdatesSwitch.isChecked()) { - long updatesSize = changesManager.getUpdatesSize(fileNameWithoutExtension); - if (updatesSize != 0) { - ClearUpdatesDialogFragment.createInstance(fileName) - .show(getParentFragment().getChildFragmentManager(), null); - } - } - } - downloadViaWiFiPreference.set(downloadOverWiFiCheckBox.isChecked()); - - final int updateFrequencyInt = updateFrequencySpinner.getSelectedItemPosition(); - updateFrequencyPreference.set(updateFrequencyInt); - - AlarmManager alarmMgr = (AlarmManager) getActivity() - .getSystemService(Context.ALARM_SERVICE); - PendingIntent alarmIntent = getPendingIntent(getActivity(), fileName); - - final int timeOfDayInt = updateTimesOfDaySpinner.getSelectedItemPosition(); - timeOfDayPreference.set(timeOfDayInt); - - if (liveUpdatesSwitch.isChecked() && getSettings().IS_LIVE_UPDATES_ON.get()) { - runLiveUpdate(getActivity(), fileName, false, null); - UpdateFrequency updateFrequency = UpdateFrequency.values()[updateFrequencyInt]; - TimeOfDay timeOfDayToUpdate = TimeOfDay.values()[timeOfDayInt]; - setAlarmForPendingIntent(alarmIntent, alarmMgr, updateFrequency, timeOfDayToUpdate); - } else { - alarmMgr.cancel(alarmIntent); - } - getLiveUpdatesFragment().notifyLiveUpdatesChanged(); - } - }) - .setNegativeButton(R.string.shared_string_cancel, null) - .setNeutralButton(R.string.update_now, null); - return builder.create(); - } - - @Override - public void onResume() { - super.onResume(); - final AlertDialog dialog = (AlertDialog) getDialog(); - if (dialog != null) { - Button neutralButton = (Button) dialog.getButton(Dialog.BUTTON_NEUTRAL); - neutralButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (!getSettings().isInternetConnectionAvailable()) { - getMyApplication().showShortToastMessage(R.string.no_internet_connection); - } else { - runLiveUpdate(getActivity(), fileName, true, null); - final IncrementalChangesManager changesManager = getMyApplication().getResourceManager().getChangesManager(); - sizeTextView.setText(getUpdatesSize(getMyActivity(), fileNameWithoutExtension, changesManager)); - dialog.dismiss(); - } - } - }); - } - } - - private void refreshTimeOfDayLayout(UpdateFrequency updateFrequency, View updateTimesOfDayLayout) { - switch (updateFrequency) { - case HOURLY: - updateTimesOfDayLayout.setVisibility(View.GONE); - break; - case DAILY: - case WEEKLY: - updateTimesOfDayLayout.setVisibility(View.VISIBLE); - break; - } - } - - private static String getUpdatesSize(Context ctx, String fileNameWithoutExtension, - IncrementalChangesManager changesManager) { - long updatesSize = changesManager.getUpdatesSize(fileNameWithoutExtension); - return AndroidUtils.formatSize(ctx, updatesSize); - } - - private LiveUpdatesFragment getLiveUpdatesFragment() { - return (LiveUpdatesFragment) getParentFragment(); - } - - private OsmandSettings getSettings() { - return getMyApplication().getSettings(); - } - - private OsmandApplication getMyApplication() { - return getMyActivity().getMyApplication(); - } - - private AbstractDownloadActivity getMyActivity() { - return (AbstractDownloadActivity) this.getActivity(); - } - - public static LiveUpdatesSettingsDialogFragment createInstance(String fileName) { - LiveUpdatesSettingsDialogFragment fragment = new LiveUpdatesSettingsDialogFragment(); - Bundle args = new Bundle(); - args.putString(LOCAL_INDEX_FILE_NAME, fileName); - fragment.setArguments(args); - return fragment; - } - - public static class ClearUpdatesDialogFragment extends DialogFragment { - @NonNull - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final String localIndexInfo = getArguments().getString(LOCAL_INDEX_FILE_NAME); - assert localIndexInfo != null; - - final IncrementalChangesManager changesManager = - getMyApplication().getResourceManager().getChangesManager(); - final String fileNameWithoutExtension = - Algorithms.getFileNameWithoutExtension(new File(localIndexInfo)); - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setMessage( - getString(R.string.ltr_or_rtl_combine_via_space, - getString(R.string.clear_updates_proposition_message), - getUpdatesSize(getContext(), fileNameWithoutExtension, changesManager))) - .setPositiveButton(R.string.shared_string_ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - changesManager.deleteUpdates(fileNameWithoutExtension); - preferenceLastCheck(localIndexInfo, getMyApplication().getSettings()).resetToDefault(); - } - }) - .setNegativeButton(R.string.shared_string_cancel, null); - return builder.create(); - } - - private OsmandApplication getMyApplication() { - return (OsmandApplication) getActivity().getApplication(); - } - - public static ClearUpdatesDialogFragment createInstance(String fileName) { - ClearUpdatesDialogFragment fragment = new ClearUpdatesDialogFragment(); - Bundle args = new Bundle(); - args.putString(LOCAL_INDEX_FILE_NAME, fileName); - fragment.setArguments(args); - return fragment; - } - } -} diff --git a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesUpdateAllBottomSheet.java b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesUpdateAllBottomSheet.java new file mode 100644 index 0000000000..a8adc5e9ce --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesUpdateAllBottomSheet.java @@ -0,0 +1,103 @@ +package net.osmand.plus.liveupdates; + +import android.os.Bundle; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; + +import net.osmand.PlatformUtil; +import net.osmand.plus.R; +import net.osmand.plus.UiUtilities.DialogButtonType; +import net.osmand.plus.base.MenuBottomSheetDialogFragment; +import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; +import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem; +import net.osmand.plus.base.bottomsheetmenu.simpleitems.LongDescriptionItem; +import net.osmand.plus.liveupdates.LiveUpdatesHelper.LiveUpdateListener; +import net.osmand.plus.widgets.TextViewEx; + +import org.apache.commons.logging.Log; + +import static net.osmand.AndroidUtils.getPrimaryTextColorId; +import static net.osmand.plus.liveupdates.LiveUpdatesHelper.runLiveUpdate; + +public class LiveUpdatesUpdateAllBottomSheet extends MenuBottomSheetDialogFragment { + + public static final String TAG = LiveUpdatesUpdateAllBottomSheet.class.getSimpleName(); + private static final Log LOG = PlatformUtil.getLog(LiveUpdatesUpdateAllBottomSheet.class); + + private BaseBottomSheetItem itemTitle; + private BaseBottomSheetItem itemDescription; + + public static void showInstance(@NonNull FragmentManager fragmentManager, Fragment target) { + if (!fragmentManager.isStateSaved()) { + LiveUpdatesUpdateAllBottomSheet fragment = new LiveUpdatesUpdateAllBottomSheet(); + fragment.setTargetFragment(target, 0); + fragment.show(fragmentManager, TAG); + } + } + + @Override + public void createMenuItems(Bundle savedInstanceState) { + updateBottomButtons(); + + itemTitle = new SimpleBottomSheetItem.Builder() + .setTitle(getString(R.string.update_all_maps_now)) + .setTitleColorId(getPrimaryTextColorId(nightMode)) + .setLayoutId(R.layout.bottom_sheet_item_title) + .create(); + items.add(itemTitle); + + String osmAndLive = "\"" + getString(R.string.osm_live) + "\""; + itemDescription = new LongDescriptionItem.Builder() + .setDescription(getString(R.string.update_all_maps_added, osmAndLive)) + .setDescriptionMaxLines(5) + .setDescriptionColorId(getPrimaryTextColorId(nightMode)) + .setLayoutId(R.layout.bottom_sheet_item_description_long) + .create(); + items.add(itemDescription); + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { + View view = super.onCreateView(inflater, parent, savedInstanceState); + ((TextViewEx) itemTitle.getView()).setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimensionPixelSize(R.dimen.default_list_text_size)); + TextView textDescription = (TextView) itemDescription.getView(); + textDescription.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimensionPixelSize(R.dimen.default_list_text_size)); + textDescription.setMinHeight(getResources().getDimensionPixelSize(R.dimen.context_menu_sub_info_height)); + return view; + } + + private void updateAll() { + Fragment target = getTargetFragment(); + if (target instanceof LiveUpdateListener) { + runLiveUpdate(getActivity(), false, (LiveUpdateListener) target); + } + } + + @Override + protected void onRightBottomButtonClick() { + updateAll(); + dismiss(); + } + + @Override + protected int getDismissButtonTextId() { + return R.string.shared_string_cancel; + } + + @Override + protected int getRightBottomButtonTextId() { + return R.string.update_now; + } + + @Override + protected DialogButtonType getRightBottomButtonType() { + return DialogButtonType.PRIMARY; + } +} diff --git a/OsmAnd/src/net/osmand/plus/liveupdates/PerformLiveUpdateAsyncTask.java b/OsmAnd/src/net/osmand/plus/liveupdates/PerformLiveUpdateAsyncTask.java index d148e8c697..9729cd9b3a 100644 --- a/OsmAnd/src/net/osmand/plus/liveupdates/PerformLiveUpdateAsyncTask.java +++ b/OsmAnd/src/net/osmand/plus/liveupdates/PerformLiveUpdateAsyncTask.java @@ -6,7 +6,6 @@ import android.content.Context; import android.os.AsyncTask; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import net.osmand.AndroidNetworkUtils; import net.osmand.AndroidNetworkUtils.OnRequestResultListener; @@ -46,20 +45,18 @@ public class PerformLiveUpdateAsyncTask @NonNull private final String localIndexFileName; private final boolean userRequested; - private final LiveUpdateListener listener; - - public interface LiveUpdateListener { - void processFinish(); - } + private Runnable runOnSuccess; public PerformLiveUpdateAsyncTask(@NonNull Context context, @NonNull String localIndexFileName, - boolean userRequested, - @Nullable LiveUpdateListener listener) { + boolean userRequested) { this.context = context; this.localIndexFileName = localIndexFileName; this.userRequested = userRequested; - this.listener = listener; + } + + public void setRunOnSuccess(Runnable runOnSuccess) { + this.runOnSuccess = runOnSuccess; } @Override @@ -149,9 +146,6 @@ public class PerformLiveUpdateAsyncTask ((DownloadIndexesThread.DownloadEvents) context).downloadInProgress(); } updateLatestAvailability(application, localIndexFileName); - if (listener != null) { - listener.processFinish(); - } } else { LOG.debug("onPostExecute: Not enough space for updates"); } @@ -163,9 +157,6 @@ public class PerformLiveUpdateAsyncTask ((DownloadIndexesThread.DownloadEvents) context).downloadInProgress(); if (userRequested && context instanceof DownloadActivity) { updateLatestAvailability(application, localIndexFileName); - if (listener != null) { - listener.processFinish(); - } application.showShortToastMessage(R.string.no_updates_available); } } @@ -201,7 +192,7 @@ public class PerformLiveUpdateAsyncTask private void updateLatestAvailability(OsmandApplication app, @NonNull final String localIndexFileName) { final OsmandSettings settings = app.getSettings(); AndroidNetworkUtils.sendRequestAsync( - app, LiveUpdatesFragmentNew.URL, null, "Requesting map updates info...", false, false, new OnRequestResultListener() { + app, LiveUpdatesFragment.URL, null, "Requesting map updates info...", false, false, new OnRequestResultListener() { @Override public void onResult(String result) { if (!Algorithms.isEmpty(result)) { @@ -213,6 +204,9 @@ public class PerformLiveUpdateAsyncTask long dateTime = parsed.getTime(); preferenceLatestUpdateAvailable(settings).set(dateTime); preferenceLatestUpdateAvailable(localIndexFileName, settings).set(dateTime); + if (runOnSuccess != null) { + runOnSuccess.run(); + } } } catch (ParseException e) { long dateTime = preferenceLatestUpdateAvailable(settings).get(); diff --git a/OsmAnd/src/net/osmand/plus/liveupdates/SubscriptionFragment.java b/OsmAnd/src/net/osmand/plus/liveupdates/SubscriptionFragment.java index a2afb3d933..ba8990f64f 100644 --- a/OsmAnd/src/net/osmand/plus/liveupdates/SubscriptionFragment.java +++ b/OsmAnd/src/net/osmand/plus/liveupdates/SubscriptionFragment.java @@ -177,7 +177,7 @@ public class SubscriptionFragment extends BaseOsmAndDialogFragment implements In CountrySelectionFragment countryCountrySelectionFragment = countrySelectionFragment; countryCountrySelectionFragment - .show(getChildFragmentManager(), "CountriesSearchSelectionFragment"); + .show(getChildFragmentManager(), CountrySelectionFragment.TAG); } return false; } @@ -228,12 +228,6 @@ public class SubscriptionFragment extends BaseOsmAndDialogFragment implements In } String preferredCountry = obj.getString("preferredCountry"); app.getSettings().BILLING_USER_COUNTRY_DOWNLOAD_NAME.set(preferredCountry); - - Fragment parent = getParentFragment(); - if (parent instanceof LiveUpdatesFragment) { - ((LiveUpdatesFragment) parent).updateSubscriptionHeader(); - } - dismiss(); } else { app.showToastMessage("Error: " + obj.getString("error")); diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/MapContextMenu.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/MapContextMenu.java index 38bf3feb78..cf032a5393 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/MapContextMenu.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/MapContextMenu.java @@ -1036,7 +1036,7 @@ public class MapContextMenu extends MenuTitleController implements StateChangedL title = ""; } String originObjectName = ""; - double altitude = 0; + double altitude = Double.NaN; long timestamp = System.currentTimeMillis(); Object object = getObject(); if (object != null) { diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/SelectedGpxMenuBuilder.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/SelectedGpxMenuBuilder.java index 6005ab7215..e4c2bc71ed 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/SelectedGpxMenuBuilder.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/SelectedGpxMenuBuilder.java @@ -70,7 +70,7 @@ public class SelectedGpxMenuBuilder extends MenuBuilder { String timeSpan = Algorithms.formatDuration((int) (analysis.timeSpan / 1000), app.accessibilityEnabled()); String timeMoving = Algorithms.formatDuration((int) (analysis.timeMoving / 1000), app.accessibilityEnabled()); - String timeSpanTitle = app.getString(R.string.shared_string_time_span) + " / " + app.getString(R.string.shared_string_time_moving); + String timeSpanTitle = app.getString(R.string.shared_string_time_span) + " / " + app.getString(R.string.moving_time); buildRow(view, getThemedIcon(R.drawable.ic_action_time_span), null, timeSpanTitle, timeSpan + " / " + timeMoving, 0, null, false, null, false, 0, false, false, false, null, false); diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/editors/PointEditorFragmentNew.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/editors/PointEditorFragmentNew.java index b8fb261f09..b00ff19efd 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/editors/PointEditorFragmentNew.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/editors/PointEditorFragmentNew.java @@ -619,6 +619,27 @@ public abstract class PointEditorFragmentNew extends BaseOsmAndFragment implemen } private void createIconForCategory() { + createIconList(); + final HorizontalSelectionAdapter horizontalSelectionAdapter = new HorizontalSelectionAdapter(app, nightMode); + horizontalSelectionAdapter.setTitledItems(new ArrayList<>(iconCategories.keySet())); + horizontalSelectionAdapter.setSelectedItemByTitle(selectedIconCategory); + horizontalSelectionAdapter.setListener(new HorizontalSelectionAdapter.HorizontalSelectionAdapterListener() { + @Override + public void onItemSelected(HorizontalSelectionAdapter.HorizontalSelectionItem item) { + selectedIconCategory = item.getTitle(); + createIconList(); + updateIconSelector(selectedIcon, PointEditorFragmentNew.this.view); + horizontalSelectionAdapter.notifyDataSetChanged(); + } + }); + RecyclerView iconCategoriesRecyclerView = view.findViewById(R.id.group_name_recycler_view); + iconCategoriesRecyclerView.setAdapter(horizontalSelectionAdapter); + iconCategoriesRecyclerView.setLayoutManager(new LinearLayoutManager(app, RecyclerView.HORIZONTAL, false)); + iconCategoriesRecyclerView.scrollToPosition(horizontalSelectionAdapter.getItemPositionByTitle(selectedIconCategory)); + } + + + private void createIconList() { FlowLayout selectIcon = view.findViewById(R.id.select_icon); selectIcon.removeAllViews(); JSONArray iconJsonArray = iconCategories.get(selectedIconCategory); @@ -631,22 +652,6 @@ public abstract class PointEditorFragmentNew extends BaseOsmAndFragment implemen e.printStackTrace(); } } - HorizontalSelectionAdapter horizontalSelectionAdapter = new HorizontalSelectionAdapter(app, nightMode); - horizontalSelectionAdapter.setTitledItems(new ArrayList<>(iconCategories.keySet())); - horizontalSelectionAdapter.setSelectedItemByTitle(selectedIconCategory); - horizontalSelectionAdapter.setListener(new HorizontalSelectionAdapter.HorizontalSelectionAdapterListener() { - @Override - public void onItemSelected(HorizontalSelectionAdapter.HorizontalSelectionItem item) { - selectedIconCategory = item.getTitle(); - createIconForCategory(); - updateIconSelector(selectedIcon, PointEditorFragmentNew.this.view); - } - }); - RecyclerView iconCategoriesRecyclerView = view.findViewById(R.id.group_name_recycler_view); - iconCategoriesRecyclerView.setAdapter(horizontalSelectionAdapter); - iconCategoriesRecyclerView.setLayoutManager(new LinearLayoutManager(app, RecyclerView.HORIZONTAL, false)); - horizontalSelectionAdapter.notifyDataSetChanged(); - iconCategoriesRecyclerView.smoothScrollToPosition(horizontalSelectionAdapter.getItemPositionByTitle(selectedIconCategory)); for (String name : iconNameList) { int minimalPaddingBetweenIcon = app.getResources().getDimensionPixelSize(R.dimen.favorites_select_icon_button_right_padding); selectIcon.addView(createIconItemView(name, selectIcon), new FlowLayout.LayoutParams(minimalPaddingBetweenIcon, 0)); diff --git a/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingBottomSheet.java b/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingBottomSheet.java index 480e5affa8..89734b7415 100644 --- a/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingBottomSheet.java @@ -48,9 +48,9 @@ import net.osmand.plus.activities.SavingTrackHelper; import net.osmand.plus.base.MenuBottomSheetDialogFragment; import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; import net.osmand.plus.helpers.AndroidUiHelper; -import net.osmand.plus.helpers.GpxUiHelper; import net.osmand.plus.mapcontextmenu.other.TrackChartPoints; import net.osmand.plus.myplaces.GPXItemPagerAdapter; +import net.osmand.plus.myplaces.GPXTabItemType; import net.osmand.plus.myplaces.SegmentActionsListener; import net.osmand.plus.myplaces.SegmentGPXAdapter; import net.osmand.plus.settings.backend.OsmandSettings; @@ -71,18 +71,19 @@ import java.util.List; import static net.osmand.AndroidUtils.getSecondaryTextColorId; import static net.osmand.AndroidUtils.setPadding; import static net.osmand.plus.UiUtilities.CompoundButtonType.GLOBAL; -import static net.osmand.plus.track.GpxBlockStatisticsBuilder.INIT_BLOCKS_ALTITUDE; -import static net.osmand.plus.track.GpxBlockStatisticsBuilder.INIT_BLOCKS_GENERAL; -import static net.osmand.plus.track.GpxBlockStatisticsBuilder.INIT_BLOCKS_SPEED; +import static net.osmand.plus.myplaces.GPXTabItemType.GPX_TAB_ITEM_ALTITUDE; +import static net.osmand.plus.myplaces.GPXTabItemType.GPX_TAB_ITEM_GENERAL; +import static net.osmand.plus.myplaces.GPXTabItemType.GPX_TAB_ITEM_SPEED; public class TripRecordingBottomSheet extends MenuBottomSheetDialogFragment implements SegmentActionsListener { public static final String TAG = TripRecordingBottomSheet.class.getSimpleName(); private static final Log LOG = PlatformUtil.getLog(TripRecordingBottomSheet.class); public static final String UPDATE_TRACK_ICON = "update_track_icon"; + public static final String UPDATE_DYNAMIC_ITEMS = "update_dynamic_items"; private static final int GPS_UPDATE_INTERVAL = 1000; - private static final String[] INIT_BLOCKS_KEYS = - new String[]{INIT_BLOCKS_GENERAL, INIT_BLOCKS_ALTITUDE, INIT_BLOCKS_SPEED}; + public static final GPXTabItemType[] INIT_TAB_ITEMS = + new GPXTabItemType[]{GPX_TAB_ITEM_GENERAL, GPX_TAB_ITEM_ALTITUDE, GPX_TAB_ITEM_SPEED}; private OsmandApplication app; private OsmandSettings settings; @@ -91,16 +92,17 @@ public class TripRecordingBottomSheet extends MenuBottomSheetDialogFragment impl private View statusContainer; private AppCompatImageView trackAppearanceIcon; - private LinearLayout segmentsContainer; - private TrackDisplayHelper displayHelper; private TrackChartPoints trackChartPoints; private GPXItemPagerAdapter graphsAdapter; + private int graphTabPosition = 0; + private ViewGroup segmentsTabs; private GpxBlockStatisticsBuilder blockStatisticsBuilder; private SelectedGpxFile selectedGpxFile; private final Handler handler = new Handler(); private Runnable updatingGPS; + private Runnable updatingGraph; private GPXFile getGPXFile() { return selectedGpxFile.getGpxFile(); @@ -153,14 +155,14 @@ public class TripRecordingBottomSheet extends MenuBottomSheetDialogFragment impl } }); - segmentsContainer = itemView.findViewById(R.id.segments_container); - createSegmentsTabs(segmentsContainer); + segmentsTabs = itemView.findViewById(R.id.segments_container); + createSegmentsTabs(segmentsTabs); RecyclerView statBlocks = itemView.findViewById(R.id.block_statistics); blockStatisticsBuilder = new GpxBlockStatisticsBuilder(app, selectedGpxFile, nightMode); blockStatisticsBuilder.setBlocksView(statBlocks); blockStatisticsBuilder.setBlocksClickable(false); - blockStatisticsBuilder.setInitBlocksKey(INIT_BLOCKS_GENERAL); + blockStatisticsBuilder.setTabItem(GPX_TAB_ITEM_GENERAL); blockStatisticsBuilder.initStatBlocks(null, ContextCompat.getColor(app, getActiveTextColorId(nightMode))); @@ -181,8 +183,10 @@ public class TripRecordingBottomSheet extends MenuBottomSheetDialogFragment impl boolean wasTrackMonitored = !wasTrackMonitored(); if (!wasTrackMonitored) { blockStatisticsBuilder.stopUpdatingStatBlocks(); + stopUpdatingGraph(); } else { blockStatisticsBuilder.runUpdatingStatBlocksIfNeeded(); + runUpdatingGraph(); } settings.SAVE_GLOBAL_TRACK_TO_GPX.set(wasTrackMonitored); updateStatus(); @@ -222,6 +226,7 @@ public class TripRecordingBottomSheet extends MenuBottomSheetDialogFragment impl super.onResume(); blockStatisticsBuilder.runUpdatingStatBlocksIfNeeded(); runUpdatingGPS(); + runUpdatingGraph(); MapActivity mapActivity = getMapActivity(); if (mapActivity != null) { mapActivity.getMapLayers().getGpxLayer().setTrackChartPoints(trackChartPoints); @@ -233,6 +238,7 @@ public class TripRecordingBottomSheet extends MenuBottomSheetDialogFragment impl super.onPause(); blockStatisticsBuilder.stopUpdatingStatBlocks(); stopUpdatingGPS(); + stopUpdatingGraph(); MapActivity mapActivity = getMapActivity(); if (mapActivity != null) { mapActivity.getMapLayers().getGpxLayer().setTrackChartPoints(null); @@ -248,6 +254,10 @@ public class TripRecordingBottomSheet extends MenuBottomSheetDialogFragment impl if (key.equals(UPDATE_TRACK_ICON)) { updateTrackIcon(app, trackAppearanceIcon); } + if (key.equals(UPDATE_DYNAMIC_ITEMS)) { + blockStatisticsBuilder.restartUpdatingStatBlocks(); + restartUpdatingGraph(); + } } } @@ -258,11 +268,11 @@ public class TripRecordingBottomSheet extends MenuBottomSheetDialogFragment impl } } - public void stopUpdatingGPS() { + private void stopUpdatingGPS() { handler.removeCallbacks(updatingGPS); } - public void runUpdatingGPS() { + private void runUpdatingGPS() { updatingGPS = new Runnable() { @Override public void run() { @@ -274,27 +284,30 @@ public class TripRecordingBottomSheet extends MenuBottomSheetDialogFragment impl handler.post(updatingGPS); } - private void recreateStatBlocks(String initBlocksKey) { - blockStatisticsBuilder.stopUpdatingStatBlocks(); - blockStatisticsBuilder.setInitBlocksKey(initBlocksKey); - blockStatisticsBuilder.runUpdatingStatBlocksIfNeeded(); + private void stopUpdatingGraph() { + handler.removeCallbacks(updatingGraph); } - private void setupDisplayHelper() { - displayHelper = new TrackDisplayHelper(app); - if (!selectedGpxFile.isShowCurrentTrack()) { - File file = new File(getGPXFile().path); - displayHelper.setFile(file); - displayHelper.setGpxDataItem(app.getGpxDbHelper().getItem(file)); - } - displayHelper.setGpx(getGPXFile()); + private void runUpdatingGraph() { + updatingGraph = new Runnable() { + @Override + public void run() { + int interval = app.getSettings().SAVE_GLOBAL_TRACK_INTERVAL.get(); + graphsAdapter.updateGraph(graphTabPosition); + AndroidUiHelper.updateVisibility(segmentsTabs, graphsAdapter.isTabsVisible()); + handler.postDelayed(this, Math.max(GPS_UPDATE_INTERVAL, interval)); + } + }; + handler.post(updatingGraph); + } + + private void restartUpdatingGraph() { + stopUpdatingGraph(); + runUpdatingGraph(); } private void createSegmentsTabs(ViewGroup viewGroup) { - viewGroup.removeAllViews(); - setupDisplayHelper(); - - View segmentView = SegmentGPXAdapter.createGpxTabsView(displayHelper, viewGroup, this, nightMode); + View segmentView = SegmentGPXAdapter.createGpxTabsView(null, viewGroup, this, nightMode); AndroidUiHelper.setVisibility(View.GONE, segmentView.findViewById(R.id.list_item_divider)); WrapContentHeightViewPager pager = segmentView.findViewById(R.id.pager); PagerSlidingTabStrip tabLayout = segmentView.findViewById(R.id.sliding_tabs); @@ -304,17 +317,26 @@ public class TripRecordingBottomSheet extends MenuBottomSheetDialogFragment impl tabLayout.setOnTabReselectedListener(new PagerSlidingTabStrip.OnTabReselectedListener() { @Override public void onTabSelected(int position) { - recreateStatBlocks(INIT_BLOCKS_KEYS[position]); + graphTabPosition = position; + blockStatisticsBuilder.setTabItem(INIT_TAB_ITEMS[graphTabPosition]); + blockStatisticsBuilder.restartUpdatingStatBlocks(); } @Override public void onTabReselected(int position) { - recreateStatBlocks(INIT_BLOCKS_KEYS[position]); + graphTabPosition = position; + blockStatisticsBuilder.setTabItem(INIT_TAB_ITEMS[graphTabPosition]); } }); - graphsAdapter = new GPXItemPagerAdapter(app, GpxUiHelper.makeGpxDisplayItem(app, - displayHelper.getGpx()), displayHelper, nightMode, this, true); + TrackDisplayHelper displayHelper = new TrackDisplayHelper(app); + GPXFile gpxFile = getGPXFile(); + File file = new File(gpxFile.path); + displayHelper.setFile(file); + displayHelper.setGpxDataItem(app.getGpxDbHelper().getItem(file)); + displayHelper.setGpx(gpxFile); + + graphsAdapter = new GPXItemPagerAdapter(app, null, displayHelper, nightMode, this, true); graphsAdapter.setChartHMargin(getResources().getDimensionPixelSize(R.dimen.content_padding)); pager.setAdapter(graphsAdapter); @@ -596,7 +618,7 @@ public class TripRecordingBottomSheet extends MenuBottomSheetDialogFragment impl public void onPointSelected(TrkSegment segment, double lat, double lon) { if (trackChartPoints == null) { trackChartPoints = new TrackChartPoints(); - trackChartPoints.setGpx(displayHelper.getGpx()); + trackChartPoints.setGpx(getGPXFile()); } MapActivity mapActivity = getMapActivity(); if (mapActivity != null) { diff --git a/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingClearDataBottomSheet.java b/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingClearDataBottomSheet.java index 88814e4819..aeb33abf93 100644 --- a/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingClearDataBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingClearDataBottomSheet.java @@ -11,6 +11,7 @@ import net.osmand.plus.base.MenuBottomSheetDialogFragment; import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithDescription; import net.osmand.plus.base.bottomsheetmenu.simpleitems.DividerSpaceItem; +import net.osmand.plus.monitoring.TripRecordingBottomSheet.DismissTargetFragment; import net.osmand.plus.monitoring.TripRecordingBottomSheet.ItemType; import androidx.annotation.NonNull; @@ -18,8 +19,9 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import static net.osmand.AndroidUtils.getPrimaryTextColorId; +import static net.osmand.plus.monitoring.TripRecordingOptionsBottomSheet.ACTION_CLEAR_DATA; -public class TripRecordingClearDataBottomSheet extends MenuBottomSheetDialogFragment implements TripRecordingBottomSheet.DismissTargetFragment { +public class TripRecordingClearDataBottomSheet extends MenuBottomSheetDialogFragment implements DismissTargetFragment { public static final String TAG = TripRecordingClearDataBottomSheet.class.getSimpleName(); @@ -111,6 +113,14 @@ public class TripRecordingClearDataBottomSheet extends MenuBottomSheetDialogFrag public void dismissTarget() { Fragment target = getTargetFragment(); if (target instanceof TripRecordingOptionsBottomSheet) { + Bundle args = target.getArguments(); + if (args != null) { + args.putBoolean(ACTION_CLEAR_DATA, true); + } else { + args = new Bundle(); + args.putBoolean(ACTION_CLEAR_DATA, true); + target.setArguments(args); + } ((TripRecordingOptionsBottomSheet) target).dismiss(); } } diff --git a/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingDiscardBottomSheet.java b/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingDiscardBottomSheet.java index cff99d9daa..6739187152 100644 --- a/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingDiscardBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingDiscardBottomSheet.java @@ -12,6 +12,7 @@ import net.osmand.plus.base.MenuBottomSheetDialogFragment; import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithDescription; import net.osmand.plus.base.bottomsheetmenu.simpleitems.DividerSpaceItem; +import net.osmand.plus.monitoring.TripRecordingBottomSheet.DismissTargetFragment; import net.osmand.plus.monitoring.TripRecordingBottomSheet.ItemType; import androidx.annotation.NonNull; @@ -21,7 +22,7 @@ import androidx.fragment.app.FragmentManager; import static net.osmand.AndroidUtils.getPrimaryTextColorId; import static net.osmand.plus.monitoring.TripRecordingOptionsBottomSheet.ACTION_STOP_AND_DISMISS; -public class TripRecordingDiscardBottomSheet extends MenuBottomSheetDialogFragment implements TripRecordingBottomSheet.DismissTargetFragment { +public class TripRecordingDiscardBottomSheet extends MenuBottomSheetDialogFragment implements DismissTargetFragment { public static final String TAG = TripRecordingDiscardBottomSheet.class.getSimpleName(); @@ -67,10 +68,15 @@ public class TripRecordingDiscardBottomSheet extends MenuBottomSheetDialogFragme dismiss(); Fragment target = getTargetFragment(); - if (target != null) { - Bundle args = new Bundle(); - args.putBoolean(ACTION_STOP_AND_DISMISS, true); - target.setArguments(args); + if (target instanceof TripRecordingOptionsBottomSheet) { + Bundle args = target.getArguments(); + if (args != null) { + args.putBoolean(ACTION_STOP_AND_DISMISS, true); + } else { + args = new Bundle(); + args.putBoolean(ACTION_STOP_AND_DISMISS, true); + target.setArguments(args); + } } dismissTarget(); } diff --git a/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingOptionsBottomSheet.java b/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingOptionsBottomSheet.java index 994b3034d9..6004adb74a 100644 --- a/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingOptionsBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingOptionsBottomSheet.java @@ -26,7 +26,7 @@ import net.osmand.plus.base.MenuBottomSheetDialogFragment; import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.DividerSpaceItem; -import net.osmand.plus.helpers.AndroidUiHelper; +import net.osmand.plus.monitoring.TripRecordingBottomSheet.DismissTargetFragment; import net.osmand.plus.monitoring.TripRecordingBottomSheet.ItemType; import net.osmand.plus.myplaces.SaveCurrentTrackTask; import net.osmand.plus.settings.backend.OsmandSettings; @@ -34,11 +34,13 @@ import net.osmand.plus.track.SaveGpxAsyncTask.SaveGpxListener; import net.osmand.util.Algorithms; import static net.osmand.AndroidUtils.getPrimaryTextColorId; +import static net.osmand.plus.monitoring.TripRecordingBottomSheet.UPDATE_DYNAMIC_ITEMS; -public class TripRecordingOptionsBottomSheet extends MenuBottomSheetDialogFragment implements TripRecordingBottomSheet.DismissTargetFragment { +public class TripRecordingOptionsBottomSheet extends MenuBottomSheetDialogFragment implements DismissTargetFragment { public static final String TAG = TripRecordingOptionsBottomSheet.class.getSimpleName(); public static final String ACTION_STOP_AND_DISMISS = "action_stop_and_discard"; + public static final String ACTION_CLEAR_DATA = "action_clear_data"; private static final int SAVE_UPDATE_INTERVAL = 1000; private OsmandApplication app; @@ -210,8 +212,10 @@ public class TripRecordingOptionsBottomSheet extends MenuBottomSheetDialogFragme @Override public void run() { String time = getTimeTrackSaved(); - TripRecordingBottomSheet.createItem(app, nightMode, buttonSave, ItemType.SAVE, hasDataToSave(), !Algorithms.isEmpty(time) ? time : null); - TripRecordingBottomSheet.createItem(app, nightMode, buttonClear, ItemType.CLEAR_DATA, hasDataToSave(), null); + TripRecordingBottomSheet.createItem(app, nightMode, buttonSave, ItemType.SAVE, + hasDataToSave(), !Algorithms.isEmpty(time) ? time : null); + TripRecordingBottomSheet.createItem(app, nightMode, buttonClear, ItemType.CLEAR_DATA, + hasDataToSave(), null); handler.postDelayed(this, SAVE_UPDATE_INTERVAL); } }; @@ -255,9 +259,14 @@ public class TripRecordingOptionsBottomSheet extends MenuBottomSheetDialogFragme if (mapActivity != null && plugin != null) { stopUpdatingTimeTrackSaved(); plugin.saveCurrentTrack(null, mapActivity, false, true); - Bundle args = new Bundle(); - args.putBoolean(ACTION_STOP_AND_DISMISS, true); - setArguments(args); + Bundle args = getArguments(); + if (args != null) { + args.putBoolean(ACTION_STOP_AND_DISMISS, true); + } else { + args = new Bundle(); + args.putBoolean(ACTION_STOP_AND_DISMISS, true); + setArguments(args); + } dismiss(); dismissTarget(); } @@ -273,12 +282,22 @@ public class TripRecordingOptionsBottomSheet extends MenuBottomSheetDialogFragme return false; } + private boolean isCleared() { + Bundle args = getArguments(); + if (args != null) { + return args.getBoolean(ACTION_CLEAR_DATA); + } + return false; + } + @Override public void dismissTarget() { Fragment target = getTargetFragment(); if (target instanceof TripRecordingBottomSheet) { if (isDiscard()) { ((TripRecordingBottomSheet) target).dismiss(); + } else if (isCleared()) { + ((TripRecordingBottomSheet) target).show(UPDATE_DYNAMIC_ITEMS); } else { ((TripRecordingBottomSheet) target).show(); } diff --git a/OsmAnd/src/net/osmand/plus/myplaces/AvailableGPXFragment.java b/OsmAnd/src/net/osmand/plus/myplaces/AvailableGPXFragment.java index b5f33884ac..a2eaae19d0 100644 --- a/OsmAnd/src/net/osmand/plus/myplaces/AvailableGPXFragment.java +++ b/OsmAnd/src/net/osmand/plus/myplaces/AvailableGPXFragment.java @@ -1290,13 +1290,14 @@ public class AvailableGPXFragment extends OsmandExpandableListFragment implement // local_indexes_cat_gpx now obsolete in new UI screen which shows only GPX data // if (Algorithms.objectEquals(getActivity().getString(R.string.local_indexes_cat_gpx) + " " + // g.subfolder, cat)) { - if (objectEquals("" + g.subfolder, cat)) { + if (objectEquals(g.subfolder, cat)) { found = i; break; } } if (found != -1) { data.get(category.get(found)).remove(g); + selected.remove(g); } } } @@ -1507,17 +1508,15 @@ public class AvailableGPXFragment extends OsmandExpandableListFragment implement int total = 0; for (GpxInfo info : params) { if (!isCancelled() && (info.gpx == null || !info.gpx.showCurrentTrack)) { - boolean successfull; - successfull = removeAllFiles(info.file); - app.getGpxDbHelper().remove(info.file); + boolean successful = FileUtils.removeGpxFile(app, info.file); total++; - if (successfull) { + if (successful) { count++; publishProgress(info); } } } - return app.getString(R.string.local_index_items_deleted, count, total); + return getString(R.string.local_index_items_deleted, count, total); } @Override diff --git a/OsmAnd/src/net/osmand/plus/myplaces/GPXItemPagerAdapter.java b/OsmAnd/src/net/osmand/plus/myplaces/GPXItemPagerAdapter.java index 34232a8c6d..6f5083d8b2 100644 --- a/OsmAnd/src/net/osmand/plus/myplaces/GPXItemPagerAdapter.java +++ b/OsmAnd/src/net/osmand/plus/myplaces/GPXItemPagerAdapter.java @@ -12,6 +12,7 @@ import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.appcompat.widget.SwitchCompat; import androidx.viewpager.widget.PagerAdapter; @@ -33,6 +34,7 @@ import net.osmand.GPXUtilities.TrkSegment; import net.osmand.GPXUtilities.WptPt; import net.osmand.plus.GPXDatabase.GpxDataItem; import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem; +import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; @@ -50,6 +52,7 @@ import net.osmand.plus.views.controls.WrapContentHeightViewPager.ViewAtPositionI import net.osmand.util.Algorithms; import net.osmand.util.MapUtils; +import java.io.File; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -63,10 +66,13 @@ 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.myplaces.GPXTabItemType.GPX_TAB_ITEM_ALTITUDE; +import static net.osmand.plus.myplaces.GPXTabItemType.GPX_TAB_ITEM_GENERAL; import static net.osmand.plus.myplaces.GPXTabItemType.GPX_TAB_ITEM_SPEED; public class GPXItemPagerAdapter extends PagerAdapter implements CustomTabProvider, ViewAtPositionInterface { + private static final int CHART_LABEL_COUNT = 4; + private OsmandApplication app; private UiUtilities iconsCache; private TrackDisplayHelper displayHelper; @@ -75,6 +81,7 @@ public class GPXItemPagerAdapter extends PagerAdapter implements CustomTabProvid private WptPt selectedWpt; private TrkSegment segment; private GpxDisplayItem gpxItem; + private GPXTrackAnalysis analysis; private GPXTabItemType[] tabTypes; private SparseArray views = new SparseArray<>(); @@ -89,8 +96,12 @@ public class GPXItemPagerAdapter extends PagerAdapter implements CustomTabProvid this.chartHMargin = chartHMargin; } + private boolean isShowCurrentTrack() { + return displayHelper.getGpx() != null && displayHelper.getGpx().showCurrentTrack; + } + public GPXItemPagerAdapter(@NonNull OsmandApplication app, - @NonNull GpxDisplayItem gpxItem, + @Nullable GpxDisplayItem gpxItem, @NonNull TrackDisplayHelper displayHelper, boolean nightMode, @NonNull SegmentActionsListener actionsListener, @@ -98,22 +109,45 @@ public class GPXItemPagerAdapter extends PagerAdapter implements CustomTabProvid super(); this.app = app; this.gpxItem = gpxItem; - this.nightMode = nightMode; this.displayHelper = displayHelper; + this.nightMode = nightMode; this.actionsListener = actionsListener; this.onlyGraphs = onlyGraphs; iconsCache = app.getUIUtilities(); + + updateAnalysis(); fetchTabTypes(); } + private void updateAnalysis() { + analysis = null; + if (isShowCurrentTrack()) { + GPXFile gpxFile = displayHelper.getGpx(); + if (gpxFile != null && !gpxFile.isEmpty()) { + analysis = gpxFile.getAnalysis(0); + gpxItem = GpxUiHelper.makeGpxDisplayItem(app, gpxFile); + } + } else { + if (gpxItem != null) { + analysis = gpxItem.analysis; + } + } + } + private void fetchTabTypes() { List tabTypeList = new ArrayList<>(); - tabTypeList.add(GPXTabItemType.GPX_TAB_ITEM_GENERAL); - if (gpxItem != null && gpxItem.analysis != null) { - if (gpxItem.analysis.hasElevationData) { + if (isShowCurrentTrack()) { + if (analysis != null && (analysis.hasElevationData || analysis.hasSpeedData)) { + tabTypeList.add(GPXTabItemType.GPX_TAB_ITEM_GENERAL); + } + } else { + tabTypeList.add(GPXTabItemType.GPX_TAB_ITEM_GENERAL); + } + if (analysis != null) { + if (analysis.hasElevationData) { tabTypeList.add(GPX_TAB_ITEM_ALTITUDE); } - if (gpxItem.analysis.isSpeedSpecified()) { + if (analysis.isSpeedSpecified()) { tabTypeList.add(GPX_TAB_ITEM_SPEED); } } @@ -123,11 +157,20 @@ public class GPXItemPagerAdapter extends PagerAdapter implements CustomTabProvid private List getDataSets(LineChart chart, GPXTabItemType tabType, LineGraphType firstType, LineGraphType secondType) { List dataSets = dataSetsMap.get(tabType); - if (dataSets == null && chart != null) { - GPXTrackAnalysis analysis = gpxItem.analysis; + boolean withoutGaps = true; + if (isShowCurrentTrack()) { + GPXFile gpxFile = displayHelper.getGpx(); + withoutGaps = !app.getSavingTrackHelper().getCurrentTrack().isJoinSegments() && gpxFile != null + && (Algorithms.isEmpty(gpxFile.tracks) || gpxFile.tracks.get(0).generalTrack); + } else if (gpxItem != null) { GpxDataItem gpxDataItem = displayHelper.getGpxDataItem(); - boolean calcWithoutGaps = gpxItem.isGeneralTrack() && gpxDataItem != null && !gpxDataItem.isJoinSegments(); - dataSets = GpxUiHelper.getDataSets(chart, app, analysis, firstType, secondType, calcWithoutGaps); + withoutGaps = gpxItem.isGeneralTrack() && gpxDataItem != null && !gpxDataItem.isJoinSegments(); + } + if (chart != null && analysis != null) { + dataSets = GpxUiHelper.getDataSets(chart, app, analysis, firstType, secondType, withoutGaps); + if (!Algorithms.isEmpty(dataSets)) { + dataSetsMap.remove(tabType); + } dataSetsMap.put(tabType, dataSets); } return dataSets; @@ -140,7 +183,7 @@ public class GPXItemPagerAdapter extends PagerAdapter implements CustomTabProvid if (ds != null && ds.size() > 0) { for (GPXUtilities.Track t : gpxItem.group.getGpx().tracks) { for (TrkSegment s : t.segments) { - if (s.points.size() > 0 && s.points.get(0).equals(gpxItem.analysis.locationStart)) { + if (s.points.size() > 0 && s.points.get(0).equals(analysis.locationStart)) { segment = s; break; } @@ -164,7 +207,7 @@ public class GPXItemPagerAdapter extends PagerAdapter implements CustomTabProvid if (gpxItem.chartAxisType == GPXDataSetAxisType.TIME) { float time = pos * 1000; for (WptPt p : segment.points) { - if (p.time - gpxItem.analysis.startTime >= time) { + if (p.time - analysis.startTime >= time) { wpt = p; break; } @@ -203,23 +246,20 @@ public class GPXItemPagerAdapter extends PagerAdapter implements CustomTabProvid public Object instantiateItem(@NonNull ViewGroup container, int position) { GPXTabItemType tabType = tabTypes[position]; View view = getViewForTab(container, tabType); - GPXFile gpxFile = displayHelper.getGpx(); - if (gpxFile != null && gpxItem != null) { - GPXTrackAnalysis analysis = gpxItem.analysis; - LineChart chart = view.findViewById(R.id.chart); - ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) chart.getLayoutParams(); - AndroidUtils.setMargins(lp, chartHMargin, lp.topMargin, chartHMargin, lp.bottomMargin); + LineChart chart = view.findViewById(R.id.chart); + ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) chart.getLayoutParams(); + AndroidUtils.setMargins(lp, chartHMargin, lp.topMargin, chartHMargin, lp.bottomMargin); + if (analysis != null) { setupChart(view, chart); - switch (tabType) { case GPX_TAB_ITEM_GENERAL: - setupGeneralTab(view, chart, analysis, gpxFile, position); + setupGeneralTab(view, chart, position); break; case GPX_TAB_ITEM_ALTITUDE: - setupAltitudeTab(view, chart, analysis, gpxFile, position); + setupAltitudeTab(view, chart, position); break; case GPX_TAB_ITEM_SPEED: - setupSpeedTab(view, chart, analysis, gpxFile, position); + setupSpeedTab(view, chart, position); break; } } @@ -252,10 +292,10 @@ public class GPXItemPagerAdapter extends PagerAdapter implements CustomTabProvid return view; } - private void setupSpeedTab(View view, LineChart chart, GPXTrackAnalysis analysis, GPXFile gpxFile, int position) { + private void setupSpeedTab(View view, LineChart chart, int position) { if (analysis != null && analysis.isSpeedSpecified()) { if (analysis.hasSpeedData) { - GpxUiHelper.setupGPXChart(app, chart, 4); + GpxUiHelper.setupGPXChart(app, chart, CHART_LABEL_COUNT); chart.setData(new LineData(getDataSets(chart, GPX_TAB_ITEM_SPEED, SPEED, null))); updateChart(chart); chart.setVisibility(View.VISIBLE); @@ -268,7 +308,7 @@ public class GPXItemPagerAdapter extends PagerAdapter implements CustomTabProvid ((ImageView) view.findViewById(R.id.max_icon)) .setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_max_speed)); ((ImageView) view.findViewById(R.id.time_moving_icon)) - .setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_time_span)); + .setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_time_moving_16)); ((ImageView) view.findViewById(R.id.distance_icon)) .setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_polygom_dark)); @@ -324,10 +364,10 @@ public class GPXItemPagerAdapter extends PagerAdapter implements CustomTabProvid }); } - private void setupAltitudeTab(View view, LineChart chart, GPXTrackAnalysis analysis, GPXFile gpxFile, int position) { + private void setupAltitudeTab(View view, LineChart chart, int position) { if (analysis != null) { if (analysis.hasElevationData) { - GpxUiHelper.setupGPXChart(app, chart, 4); + GpxUiHelper.setupGPXChart(app, chart, CHART_LABEL_COUNT); chart.setData(new LineData(getDataSets(chart, GPX_TAB_ITEM_ALTITUDE, ALTITUDE, SLOPE))); updateChart(chart); chart.setVisibility(View.VISIBLE); @@ -391,10 +431,10 @@ public class GPXItemPagerAdapter extends PagerAdapter implements CustomTabProvid } } - private void setupGeneralTab(View view, LineChart chart, GPXTrackAnalysis analysis, GPXFile gpxFile, int position) { + private void setupGeneralTab(View view, LineChart chart, int position) { if (analysis != null) { if (analysis.hasElevationData || analysis.hasSpeedData) { - GpxUiHelper.setupGPXChart(app, chart, 4); + GpxUiHelper.setupGPXChart(app, chart, CHART_LABEL_COUNT); chart.setData(new LineData(getDataSets(chart, GPXTabItemType.GPX_TAB_ITEM_GENERAL, ALTITUDE, SPEED))); updateChart(chart); chart.setVisibility(View.VISIBLE); @@ -632,10 +672,12 @@ public class GPXItemPagerAdapter extends PagerAdapter implements CustomTabProvid @Override public void tabStylesUpdated(View tabsContainer, int currentPosition) { - ViewGroup.MarginLayoutParams params = (MarginLayoutParams) tabsContainer.getLayoutParams(); - params.height = app.getResources().getDimensionPixelSize(!onlyGraphs ? R.dimen.dialog_button_height : R.dimen.context_menu_buttons_bottom_height); - tabsContainer.setLayoutParams(params); - UiUtilities.updateCustomRadioButtons(app, tabsContainer, nightMode, getCustomRadioButtonType(currentPosition)); + if (getCount() > 0) { + ViewGroup.MarginLayoutParams params = (MarginLayoutParams) tabsContainer.getLayoutParams(); + params.height = app.getResources().getDimensionPixelSize(!onlyGraphs ? R.dimen.dialog_button_height : R.dimen.context_menu_buttons_bottom_height); + tabsContainer.setLayoutParams(params); + UiUtilities.updateCustomRadioButtons(app, tabsContainer, nightMode, getCustomRadioButtonType(currentPosition)); + } } private CustomRadioButtonType getCustomRadioButtonType(int index) { @@ -667,22 +709,22 @@ public class GPXItemPagerAdapter extends PagerAdapter implements CustomTabProvid void updateJoinGapsInfo(View view, int position) { if (view != null) { - GPXTrackAnalysis analysis = gpxItem.analysis; GPXTabItemType tabType = tabTypes[position]; - boolean visible = gpxItem.isGeneralTrack() && analysis != null && tabType.equals(GPXTabItemType.GPX_TAB_ITEM_GENERAL); - AndroidUiHelper.updateVisibility(view.findViewById(R.id.gpx_join_gaps_container), visible); + boolean generalTrack = gpxItem.isGeneralTrack(); boolean joinSegments = displayHelper.isJoinSegments(); + boolean visible = generalTrack && analysis != null && tabType.equals(GPXTabItemType.GPX_TAB_ITEM_GENERAL); + AndroidUiHelper.updateVisibility(view.findViewById(R.id.gpx_join_gaps_container), visible); ((SwitchCompat) view.findViewById(R.id.gpx_join_gaps_switch)).setChecked(joinSegments); if (analysis != null) { if (tabType.equals(GPXTabItemType.GPX_TAB_ITEM_GENERAL)) { - float totalDistance = !joinSegments && gpxItem.isGeneralTrack() ? analysis.totalDistanceWithoutGaps : analysis.totalDistance; - float timeSpan = !joinSegments && gpxItem.isGeneralTrack() ? analysis.timeSpanWithoutGaps : analysis.timeSpan; + float totalDistance = !joinSegments && generalTrack ? analysis.totalDistanceWithoutGaps : analysis.totalDistance; + float timeSpan = !joinSegments && generalTrack ? analysis.timeSpanWithoutGaps : analysis.timeSpan; ((TextView) view.findViewById(R.id.distance_text)).setText(OsmAndFormatter.getFormattedDistance(totalDistance, app)); ((TextView) view.findViewById(R.id.duration_text)).setText(Algorithms.formatDuration((int) (timeSpan / 1000), app.accessibilityEnabled())); } else if (tabType.equals(GPX_TAB_ITEM_SPEED)) { - long timeMoving = !joinSegments && gpxItem.isGeneralTrack() ? analysis.timeMovingWithoutGaps : analysis.timeMoving; - float totalDistanceMoving = !joinSegments && gpxItem.isGeneralTrack() ? analysis.totalDistanceMovingWithoutGaps : analysis.totalDistanceMoving; + long timeMoving = !joinSegments && generalTrack ? analysis.timeMovingWithoutGaps : analysis.timeMoving; + float totalDistanceMoving = !joinSegments && generalTrack ? analysis.totalDistanceMovingWithoutGaps : analysis.totalDistanceMoving; ((TextView) view.findViewById(R.id.time_moving_text)).setText(Algorithms.formatDuration((int) (timeMoving / 1000), app.accessibilityEnabled())); ((TextView) view.findViewById(R.id.distance_text)).setText(OsmAndFormatter.getFormattedDistance(totalDistanceMoving, app)); @@ -693,17 +735,60 @@ public class GPXItemPagerAdapter extends PagerAdapter implements CustomTabProvid void updateChart(LineChart chart) { if (chart != null && !chart.isEmpty()) { - if (gpxItem.chartMatrix != null) { - chart.getViewPortHandler().refresh(new Matrix(gpxItem.chartMatrix), chart, true); - } - if (gpxItem.chartHighlightPos != -1) { - chart.highlightValue(gpxItem.chartHighlightPos, 0); + if (gpxItem != null) { + if (gpxItem.chartMatrix != null) { + chart.getViewPortHandler().refresh(new Matrix(gpxItem.chartMatrix), chart, true); + } + if (gpxItem.chartHighlightPos != -1) { + chart.highlightValue(gpxItem.chartHighlightPos, 0); + } } else { chart.highlightValue(null); } } } + public boolean isTabsVisible() { + GPXFile gpxFile = displayHelper.getGpx(); + if (gpxFile != null && getCount() > 0 && views.size() > 0) { + for (int i = 0; i < getCount(); i++) { + LineChart lc = getViewAtPosition(i).findViewById(R.id.chart); + if (!lc.isEmpty() && !gpxFile.isEmpty()) { + return true; + } + } + } + return false; + } + + public void updateGraph(int position) { + updateAnalysis(); + fetchTabTypes(); + if (getCount() > 0 && views.size() > 0) { + LineGraphType firstType = tabTypes[position] == GPX_TAB_ITEM_SPEED ? SPEED : ALTITUDE; + LineGraphType secondType = null; + if (tabTypes[position] == GPX_TAB_ITEM_ALTITUDE) { + secondType = SLOPE; + } else if (tabTypes[position] == GPX_TAB_ITEM_GENERAL) { + secondType = SPEED; + } + LineChart chart = getViewAtPosition(position).findViewById(R.id.chart); + List dataSets = getDataSets(chart, tabTypes[position], firstType, secondType); + boolean isEmptyDataSets = Algorithms.isEmpty(dataSets); + AndroidUiHelper.updateVisibility(chart, !isEmptyDataSets); + chart.clear(); + if (!isEmptyDataSets) { + chart.setData(new LineData(dataSets)); + } + if (chart.getAxisRight().getLabelCount() != CHART_LABEL_COUNT + || chart.getAxisLeft().getLabelCount() != CHART_LABEL_COUNT) { + GpxUiHelper.setupGPXChart(app, chart, CHART_LABEL_COUNT); + } + updateChart(chart); + } + notifyDataSetChanged(); + } + private TrkSegment getTrkSegment() { for (Track track : gpxItem.group.getGpx().tracks) { if (!track.generalTrack && !gpxItem.isGeneralTrack() || track.generalTrack && gpxItem.isGeneralTrack()) { diff --git a/OsmAnd/src/net/osmand/plus/quickaction/QuickActionListFragment.java b/OsmAnd/src/net/osmand/plus/quickaction/QuickActionListFragment.java index ec282deb4c..28981e5db6 100644 --- a/OsmAnd/src/net/osmand/plus/quickaction/QuickActionListFragment.java +++ b/OsmAnd/src/net/osmand/plus/quickaction/QuickActionListFragment.java @@ -593,7 +593,13 @@ public class QuickActionListFragment extends BaseOsmAndFragment List actions = getQuickActions(); int actionGlobalPosition = actions.indexOf(action); int actionPosition = actionGlobalPosition % ITEMS_IN_GROUP + 1; - h.title.setText(action.getName(app)); + String name = action.getName(app); + if (action.getActionNameRes() != 0 && !name.contains(getString(action.getActionNameRes()))) { + String prefAction = getString(action.getActionNameRes()); + h.title.setText(getString(R.string.ltr_or_rtl_combine_via_dash, prefAction, action.getName(app))); + } else { + h.title.setText(name); + } h.subTitle.setText(getResources().getString(R.string.quick_action_item_action, actionPosition)); h.icon.setImageDrawable(getContentIcon(action.getIconRes(app))); diff --git a/OsmAnd/src/net/osmand/plus/rastermaps/OsmandRasterMapsPlugin.java b/OsmAnd/src/net/osmand/plus/rastermaps/OsmandRasterMapsPlugin.java index b43171e632..be88b777a7 100644 --- a/OsmAnd/src/net/osmand/plus/rastermaps/OsmandRasterMapsPlugin.java +++ b/OsmAnd/src/net/osmand/plus/rastermaps/OsmandRasterMapsPlugin.java @@ -369,8 +369,10 @@ public class OsmandRasterMapsPlugin extends OsmandPlugin { settings.MAP_OVERLAY_PREVIOUS.set(null); } if (underlayLayer.getMap() == null) { + settings.MAP_UNDERLAY.removeListener(underlayListener); settings.MAP_UNDERLAY.set(null); settings.MAP_UNDERLAY_PREVIOUS.set(null); + settings.MAP_UNDERLAY.addListener(underlayListener); } String overlayMapDescr = settings.MAP_OVERLAY.get(); if (overlayMapDescr!=null && overlayMapDescr.contains(".sqlitedb")) { diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/MapRouteInfoMenuFragment.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/MapRouteInfoMenuFragment.java index bd1d382677..b60074f0b4 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/MapRouteInfoMenuFragment.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/MapRouteInfoMenuFragment.java @@ -16,6 +16,7 @@ import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import net.osmand.AndroidUtils; +import net.osmand.GPXUtilities; import net.osmand.data.QuadRect; import net.osmand.data.RotatedTileBox; import net.osmand.plus.OsmandApplication; @@ -23,11 +24,16 @@ import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.base.ContextMenuFragment; import net.osmand.plus.helpers.AndroidUiHelper; +import net.osmand.plus.routing.GPXRouteParams; import net.osmand.plus.routing.RoutingHelper; import net.osmand.plus.settings.backend.ApplicationMode; +import net.osmand.plus.track.TrackMenuFragment; +import net.osmand.plus.track.TrackSelectSegmentBottomSheet; import net.osmand.plus.widgets.TextViewExProgress; -public class MapRouteInfoMenuFragment extends ContextMenuFragment { +import static net.osmand.plus.track.TrackMenuFragment.startNavigationForGPX; + +public class MapRouteInfoMenuFragment extends ContextMenuFragment implements TrackSelectSegmentBottomSheet.OnSegmentSelectedListener { public static final String TAG = MapRouteInfoMenuFragment.class.getName(); @Nullable @@ -95,13 +101,13 @@ public class MapRouteInfoMenuFragment extends ContextMenuFragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { + Bundle savedInstanceState) { MapActivity mapActivity = requireMapActivity(); menu = mapActivity.getMapRouteInfoMenu(); View view = super.onCreateView(inflater, container, savedInstanceState); if (view != null) { - bottomContainer = (FrameLayout) view.findViewById(R.id.bottom_container); + bottomContainer = view.findViewById(R.id.bottom_container); modesLayoutToolbar = view.findViewById(R.id.modes_layout_toolbar); modesLayoutToolbarContainer = view.findViewById(R.id.modes_layout_toolbar_container); modesLayoutListContainer = view.findViewById(R.id.modes_layout_list_container); @@ -121,7 +127,7 @@ public class MapRouteInfoMenuFragment extends ContextMenuFragment { int widthNoShadow = getLandscapeNoShadowWidth(); modesLayoutToolbar.setLayoutParams(new FrameLayout.LayoutParams(widthNoShadow, ViewGroup.LayoutParams.WRAP_CONTENT)); FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(widthNoShadow, ViewGroup.LayoutParams.WRAP_CONTENT); - params.gravity = Gravity.BOTTOM|Gravity.START; + params.gravity = Gravity.BOTTOM | Gravity.START; view.findViewById(R.id.control_buttons).setLayoutParams(params); View appModesView = view.findViewById(R.id.app_modes); AndroidUtils.setPadding(appModesView, 0, 0, appModesView.getPaddingRight(), 0); @@ -182,15 +188,15 @@ public class MapRouteInfoMenuFragment extends ContextMenuFragment { @Override protected void updateMenuState(int currentMenuState, int newMenuState) { - if(getMyApplication().getRoutingHelper().isRouteCalculated() ) { + if (getMyApplication().getRoutingHelper().isRouteCalculated()) { ApplicationMode mV = getMyApplication().getRoutingHelper().getAppMode(); if (newMenuState == MenuState.HEADER_ONLY && currentMenuState == MenuState.HALF_SCREEN) { - getSettings().OPEN_ONLY_HEADER_STATE_ROUTE_CALCULATED.setModeValue(mV,true); + getSettings().OPEN_ONLY_HEADER_STATE_ROUTE_CALCULATED.setModeValue(mV, true); } else if (currentMenuState == MenuState.HEADER_ONLY && newMenuState == MenuState.HALF_SCREEN) { getSettings().OPEN_ONLY_HEADER_STATE_ROUTE_CALCULATED.resetModeToDefault(mV); } } - } + } @Override protected void setViewY(int y, boolean animated, boolean adjustMapPos) { @@ -347,7 +353,7 @@ public class MapRouteInfoMenuFragment extends ContextMenuFragment { OsmandApplication app = getMyApplication(); return app != null && app.getRoutingHelper().isOsmandRouting(); } - + public void updateRouteCalculationProgress(int progress) { MapActivity mapActivity = getMapActivity(); View mainView = getMainView(); @@ -356,7 +362,7 @@ public class MapRouteInfoMenuFragment extends ContextMenuFragment { return; } boolean indeterminate = isPublicTransportMode() || !isOsmandRouting(); - ProgressBar progressBar = (ProgressBar) mainView.findViewById(R.id.progress_bar); + ProgressBar progressBar = mainView.findViewById(R.id.progress_bar); if (progressBar != null) { if (progress == 0) { progressBar.setIndeterminate(indeterminate); @@ -366,14 +372,14 @@ public class MapRouteInfoMenuFragment extends ContextMenuFragment { } progressBar.setProgress(progress); } - ProgressBar progressBarButton = (ProgressBar) view.findViewById(R.id.progress_bar_button); + ProgressBar progressBarButton = view.findViewById(R.id.progress_bar_button); if (progressBarButton != null) { if (progressBarButton.getVisibility() != View.VISIBLE) { progressBarButton.setVisibility(View.VISIBLE); } progressBarButton.setProgress(indeterminate ? 0 : progress); } - TextViewExProgress textViewExProgress = (TextViewExProgress) view.findViewById(R.id.start_button_descr); + TextViewExProgress textViewExProgress = view.findViewById(R.id.start_button_descr); textViewExProgress.percent = indeterminate ? 0 : progress / 100f; textViewExProgress.invalidate(); } @@ -390,11 +396,11 @@ public class MapRouteInfoMenuFragment extends ContextMenuFragment { if (progressBar != null) { progressBar.setVisibility(View.GONE); } - ProgressBar progressBarButton = (ProgressBar) view.findViewById(R.id.progress_bar_button); + ProgressBar progressBarButton = view.findViewById(R.id.progress_bar_button); if (progressBarButton != null) { progressBarButton.setProgress(0); } - TextViewExProgress textViewExProgress = (TextViewExProgress) view.findViewById(R.id.start_button_descr); + TextViewExProgress textViewExProgress = view.findViewById(R.id.start_button_descr); textViewExProgress.percent = 0; } @@ -501,4 +507,19 @@ public class MapRouteInfoMenuFragment extends ContextMenuFragment { return false; } } + + @Override + public void onSegmentSelect(GPXUtilities.GPXFile gpxFile, int selectedSegment) { + MapActivity mapActivity = getMapActivity(); + if (mapActivity != null) { + mapActivity.getMyApplication().getSettings().GPX_ROUTE_SEGMENT.set(selectedSegment); + startNavigationForGPX(gpxFile, mapActivity.getMapActions(), mapActivity); + GPXRouteParams.GPXRouteParamsBuilder paramsBuilder = getMyApplication().getRoutingHelper().getCurrentGPXRoute(); + if (paramsBuilder != null) { + paramsBuilder.setSelectedSegment(selectedSegment); + getMyApplication().getRoutingHelper().onSettingsChanged(true); + } + dismiss(); + } + } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/BaseCard.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/BaseCard.java index d4101e58ec..fc24f9bace 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/BaseCard.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/BaseCard.java @@ -39,9 +39,13 @@ public abstract class BaseCard { } public BaseCard(@NonNull MapActivity mapActivity) { + this(mapActivity, true); + } + + public BaseCard(@NonNull MapActivity mapActivity, boolean usedOnMap) { this.mapActivity = mapActivity; this.app = mapActivity.getMyApplication(); - nightMode = mapActivity.getMyApplication().getDaynightHelper().isNightModeForMapControls(); + nightMode = usedOnMap ? app.getDaynightHelper().isNightModeForMapControls() : !app.getSettings().isLightContent(); } public abstract int getCardLayoutId(); diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/TracksCard.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/TracksCard.java index 5becff9bad..fe09174683 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/TracksCard.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/TracksCard.java @@ -10,6 +10,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.appcompat.view.ContextThemeWrapper; import androidx.core.content.ContextCompat; +import androidx.fragment.app.Fragment; import net.osmand.AndroidUtils; import net.osmand.GPXUtilities; @@ -20,7 +21,9 @@ import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.helpers.GpxUiHelper; import net.osmand.plus.helpers.GpxUiHelper.GPXInfo; +import net.osmand.plus.routepreparationmenu.MapRouteInfoMenuFragment; import net.osmand.plus.settings.backend.ApplicationMode; +import net.osmand.plus.track.TrackSelectSegmentBottomSheet; import java.io.File; import java.util.ArrayList; @@ -30,7 +33,7 @@ import java.util.List; public class TracksCard extends BaseCard { - private List gpxFiles; + private final List gpxFiles; private boolean showLimited = true; private static class GpxItem { @@ -76,7 +79,7 @@ public class TracksCard extends BaseCard { } }); - LinearLayout tracks = (LinearLayout) view.findViewById(R.id.items); + LinearLayout tracks = view.findViewById(R.id.items); tracks.removeAllViews(); int minCardHeight = app.getResources().getDimensionPixelSize(R.dimen.route_info_card_item_height); @@ -110,10 +113,10 @@ public class TracksCard extends BaseCard { ((TextView) v.findViewById(R.id.points_count)).setTextColor(descriptionColor); ((TextView) v.findViewById(R.id.time)).setTextColor(descriptionColor); - ImageView img = (ImageView) v.findViewById(R.id.icon); + ImageView img = v.findViewById(R.id.icon); img.setImageDrawable(getActiveIcon(R.drawable.ic_action_polygom_dark)); img.setVisibility(View.VISIBLE); - LinearLayout container = (LinearLayout) v.findViewById(R.id.container); + LinearLayout container = v.findViewById(R.id.container); container.setMinimumHeight(minCardHeight); AndroidUtils.setPadding(container, listContentPadding, 0, 0, 0); v.setOnClickListener(new View.OnClickListener() { @@ -127,8 +130,13 @@ public class TracksCard extends BaseCard { app.initVoiceCommandPlayer(mapActivity, mode, true, null, false, false, true); } } - mapActivity.getMapActions().setGPXRouteParams(item.file); - app.getTargetPointsHelper().updateRouteAndRefresh(true); + if (item.file.getNonEmptySegmentsCount() > 1) { + Fragment targetFragment = mapActivity.getSupportFragmentManager().findFragmentByTag(MapRouteInfoMenuFragment.TAG); + TrackSelectSegmentBottomSheet.showInstance(mapActivity.getSupportFragmentManager(), item.file, targetFragment); + } else { + mapActivity.getMapActions().setGPXRouteParams(item.file); + app.getTargetPointsHelper().updateRouteAndRefresh(true); + } } }); tracks.addView(v); diff --git a/OsmAnd/src/net/osmand/plus/routing/RouteLineDrawInfo.java b/OsmAnd/src/net/osmand/plus/routing/RouteLineDrawInfo.java index 838e553b5b..88a033edae 100644 --- a/OsmAnd/src/net/osmand/plus/routing/RouteLineDrawInfo.java +++ b/OsmAnd/src/net/osmand/plus/routing/RouteLineDrawInfo.java @@ -175,8 +175,8 @@ public class RouteLineDrawInfo { RouteLineDrawInfo that = (RouteLineDrawInfo) o; - if (!Algorithms.objectEquals(colorDay, that.colorDay)) return false; - if (!Algorithms.objectEquals(colorNight, that.colorNight)) return false; + if (!Algorithms.objectEquals(getColor(false), that.getColor(false))) return false; + if (!Algorithms.objectEquals(getColor(true), that.getColor(true))) return false; return Algorithms.objectEquals(width, that.width); } diff --git a/OsmAnd/src/net/osmand/plus/routing/data/AnnounceTimeDistances.java b/OsmAnd/src/net/osmand/plus/routing/data/AnnounceTimeDistances.java index e2e515f889..a0f1ca0cd8 100644 --- a/OsmAnd/src/net/osmand/plus/routing/data/AnnounceTimeDistances.java +++ b/OsmAnd/src/net/osmand/plus/routing/data/AnnounceTimeDistances.java @@ -5,6 +5,9 @@ import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.style.StyleSpan; +import androidx.core.text.TextUtilsCompat; +import androidx.core.view.ViewCompat; + import net.osmand.Location; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; @@ -12,6 +15,8 @@ import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.voice.AbstractPrologCommandPlayer; +import java.util.Locale; + public class AnnounceTimeDistances { // Avoids false negatives: Pre-pone close announcements by this distance to allow for the possible over-estimation of the 'true' lead distance due to positioning error. // A smaller value will increase the timing precision, but at the risk of missing prompts which do not meet the precision limit. @@ -36,12 +41,12 @@ public class AnnounceTimeDistances { private float OFF_ROUTE_DISTANCE; private float TURN_NOW_SPEED; - private int PREPARE_LONG_DISTANCE; + private final int PREPARE_LONG_DISTANCE; private int PREPARE_LONG_DISTANCE_END; - private int PREPARE_DISTANCE; + private final int PREPARE_DISTANCE; private int PREPARE_DISTANCE_END; - private int TURN_IN_DISTANCE; - private int TURN_IN_DISTANCE_END; + private final int TURN_IN_DISTANCE; + private final int TURN_IN_DISTANCE_END; private int TURN_NOW_DISTANCE; private int LONG_PNT_ANNOUNCE_RADIUS; private int SHORT_PNT_ANNOUNCE_RADIUS; @@ -73,13 +78,13 @@ public class AnnounceTimeDistances { PREPARE_DISTANCE_END = (int) (DEFAULT_SPEED * 90); // 22 s: car 275 m, bicycle 61 m, pedestrian 24 m - TURN_IN_DISTANCE = (int) (DEFAULT_SPEED * 22); + TURN_IN_DISTANCE = (int) (DEFAULT_SPEED * 22); // 15 s: car 189 m, bicycle 42 m, pedestrian 17 m TURN_IN_DISTANCE_END = (int) (DEFAULT_SPEED * 15); // Do not play prepare: for pedestrian and slow transport // same check as speed < 150/(90-22) m/s = 2.2 m/s = 8 km/h - // if (DEFAULT_SPEED < 2.3) { + // if (DEFAULT_SPEED < 2.3) { if (PREPARE_DISTANCE_END - TURN_IN_DISTANCE < 150) { PREPARE_DISTANCE_END = PREPARE_DISTANCE * 2; } @@ -108,7 +113,7 @@ public class AnnounceTimeDistances { TURN_NOW_SPEED = TURN_NOW_DISTANCE / TURN_NOW_TIME; // 5 s: car 63 m, bicycle 14 m, pedestrian 6 m -> 12 m (capped by POSITIONING_TOLERANCE) - ARRIVAL_DISTANCE = (int) (Math.max(POSITIONING_TOLERANCE, DEFAULT_SPEED * 5.) * arrivalDistanceFactor); + ARRIVAL_DISTANCE = (int) (Math.max(POSITIONING_TOLERANCE, DEFAULT_SPEED * 5.) * arrivalDistanceFactor); // 20 s: car 250 m, bicycle 56 m, pedestrian 22 m OFF_ROUTE_DISTANCE = DEFAULT_SPEED * 20 * arrivalDistanceFactor; // 20 seconds @@ -175,17 +180,14 @@ public class AnnounceTimeDistances { } private boolean isDistanceLess(float currentSpeed, double dist, double etalon, float defSpeed) { - // Check triggers: + // Check triggers: // (1) distance < etalon? if (dist - voicePromptDelayTimeSec * currentSpeed <= etalon) { return true; } // (2) time_with_current_speed < etalon_time_with_default_speed? // check only if speed > 0 - if (currentSpeed > 0 && (dist / currentSpeed - voicePromptDelayTimeSec) <= etalon / defSpeed) { - return true; - } - return false; + return currentSpeed > 0 && (dist / currentSpeed - voicePromptDelayTimeSec) <= etalon / defSpeed; } public float getSpeed(Location loc) { @@ -208,18 +210,21 @@ public class AnnounceTimeDistances { return (int) (dist - voicePromptDelayTimeSec * speed); } - private void appendTurnDesc(SpannableStringBuilder builder, String name, int dist, String meter, String second) { - appendTurnDesc(builder, name, dist, DEFAULT_SPEED, meter, second); + private void appendTurnDesc(OsmandApplication app, SpannableStringBuilder builder, String name, int dist, String meter, String second) { + appendTurnDesc(app, builder, name, dist, DEFAULT_SPEED, meter, second); } - private void appendTurnDesc(SpannableStringBuilder builder, String name, int dist, float speed, String meter, String second) { + private void appendTurnDesc(OsmandApplication app, SpannableStringBuilder builder, String name, int dist, float speed, String meter, String second) { int minDist = (dist / 5) * 5; int time = (int) (dist / speed); if (time > 15) { // round to 5 time = (time / 5) * 5; } - builder.append(String.format("\n%s: %d - %d %s, %d %s.", name, minDist, minDist + 5, meter, time, second)); + String distStr = String.format("\n%s: %d - %d %s", name, minDist, minDist + 5, meter); + String timeStr = String.format("%d %s.", time, second); + builder.append(app.getString(R.string.ltr_or_rtl_combine_via_comma, distStr, timeStr)); + } public Spannable getIntervalsDescription(OsmandApplication app) { @@ -245,35 +250,35 @@ public class AnnounceTimeDistances { builder.append(turn); makeBold(builder, turn); if (PREPARE_DISTANCE_END <= PREPARE_DISTANCE) { - appendTurnDesc(builder, prepare, PREPARE_DISTANCE, meter, second); + appendTurnDesc(app, builder, prepare, PREPARE_DISTANCE, meter, second); } if (PREPARE_LONG_DISTANCE_END <= PREPARE_LONG_DISTANCE) { - appendTurnDesc(builder, longPrepare, PREPARE_LONG_DISTANCE, meter, second); + appendTurnDesc(app, builder, longPrepare, PREPARE_LONG_DISTANCE, meter, second); } - appendTurnDesc(builder, approach, TURN_IN_DISTANCE, meter, second); - appendTurnDesc(builder, passing, TURN_NOW_DISTANCE, TURN_NOW_SPEED, meter, second); + appendTurnDesc(app, builder, approach, TURN_IN_DISTANCE, meter, second); + appendTurnDesc(app, builder, passing, TURN_NOW_DISTANCE, TURN_NOW_SPEED, meter, second); // Arrive at destination - appendTurnDesc(builder, arrive, (int) (getArrivalDistance()), meter, second); + appendTurnDesc(app, builder, arrive, (int) (getArrivalDistance()), meter, second); makeBoldFormatted(builder, arrive); // Off-route if (getOffRouteDistance() > 0) { - appendTurnDesc(builder, offRoute, (int) getOffRouteDistance(), meter, second); + appendTurnDesc(app, builder, offRoute, (int) getOffRouteDistance(), meter, second); makeBoldFormatted(builder, offRoute); } // Traffic warnings builder.append(traffic); makeBold(builder, traffic); - appendTurnDesc(builder, approach, LONG_ALARM_ANNOUNCE_RADIUS, meter, second); - appendTurnDesc(builder, passing, SHORT_ALARM_ANNOUNCE_RADIUS, meter, second); + appendTurnDesc(app, builder, approach, LONG_ALARM_ANNOUNCE_RADIUS, meter, second); + appendTurnDesc(app, builder, passing, SHORT_ALARM_ANNOUNCE_RADIUS, meter, second); // Waypoint / Favorite / POI builder.append(point); makeBold(builder, point); - appendTurnDesc(builder, approach, LONG_PNT_ANNOUNCE_RADIUS, meter, second); - appendTurnDesc(builder, passing, SHORT_PNT_ANNOUNCE_RADIUS, meter, second); + appendTurnDesc(app, builder, approach, LONG_PNT_ANNOUNCE_RADIUS, meter, second); + appendTurnDesc(app, builder, passing, SHORT_PNT_ANNOUNCE_RADIUS, meter, second); return builder; } diff --git a/OsmAnd/src/net/osmand/plus/settings/bottomsheets/BooleanPreferenceBottomSheet.java b/OsmAnd/src/net/osmand/plus/settings/bottomsheets/BooleanPreferenceBottomSheet.java index 2f4ca39f84..858e32c962 100644 --- a/OsmAnd/src/net/osmand/plus/settings/bottomsheets/BooleanPreferenceBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/settings/bottomsheets/BooleanPreferenceBottomSheet.java @@ -33,7 +33,7 @@ import net.osmand.plus.settings.preferences.SwitchPreferenceEx; import org.apache.commons.logging.Log; -import static net.osmand.plus.liveupdates.LiveUpdatesSettingsDialogFragmentNew.getActivePrimaryColorId; +import static net.osmand.plus.liveupdates.LiveUpdatesSettingsBottomSheet.getActivePrimaryColorId; import static net.osmand.plus.monitoring.TripRecordingBottomSheet.getSecondaryIconColorId; public class BooleanPreferenceBottomSheet extends BasePreferenceBottomSheet { diff --git a/OsmAnd/src/net/osmand/plus/settings/datastorage/DataStorageFragment.java b/OsmAnd/src/net/osmand/plus/settings/datastorage/DataStorageFragment.java index 7fd96b8185..41c58ebd7f 100644 --- a/OsmAnd/src/net/osmand/plus/settings/datastorage/DataStorageFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/datastorage/DataStorageFragment.java @@ -6,7 +6,9 @@ import android.content.Context; import android.content.DialogInterface; import android.graphics.drawable.Drawable; import android.os.AsyncTask; +import android.os.Build; import android.os.Bundle; +import android.text.BidiFormatter; import android.view.View; import android.widget.ImageView; import android.widget.TextView; @@ -24,12 +26,12 @@ import androidx.preference.PreferenceViewHolder; import net.osmand.AndroidUtils; import net.osmand.FileUtils; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.OsmandActionBarActivity; import net.osmand.plus.download.DownloadActivity; +import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.settings.bottomsheets.ChangeDataStorageBottomSheet; import net.osmand.plus.settings.bottomsheets.SelectFolderBottomSheet; import net.osmand.plus.settings.datastorage.item.MemoryItem; @@ -43,19 +45,20 @@ import java.io.File; import java.text.DecimalFormat; import java.util.ArrayList; -import static net.osmand.plus.settings.datastorage.DataStorageHelper.INTERNAL_STORAGE; -import static net.osmand.plus.settings.datastorage.DataStorageHelper.MANUALLY_SPECIFIED; -import static net.osmand.plus.settings.datastorage.DataStorageHelper.OTHER_MEMORY; -import static net.osmand.plus.settings.datastorage.DataStorageHelper.TILES_MEMORY; import static net.osmand.plus.settings.bottomsheets.ChangeDataStorageBottomSheet.CHOSEN_DIRECTORY; import static net.osmand.plus.settings.bottomsheets.ChangeDataStorageBottomSheet.MOVE_DATA; import static net.osmand.plus.settings.bottomsheets.SelectFolderBottomSheet.NEW_PATH; import static net.osmand.plus.settings.bottomsheets.SelectFolderBottomSheet.PATH_CHANGED; +import static net.osmand.plus.settings.datastorage.DataStorageHelper.INTERNAL_STORAGE; +import static net.osmand.plus.settings.datastorage.DataStorageHelper.MANUALLY_SPECIFIED; +import static net.osmand.plus.settings.datastorage.DataStorageHelper.OTHER_MEMORY; +import static net.osmand.plus.settings.datastorage.DataStorageHelper.SHARED_STORAGE; +import static net.osmand.plus.settings.datastorage.DataStorageHelper.TILES_MEMORY; public class DataStorageFragment extends BaseSettingsFragment implements DataStorageHelper.UpdateMemoryInfoUIAdapter { public final static int PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 500; public final static int UI_REFRESH_TIME_MS = 500; - + private final static String CHANGE_DIRECTORY_BUTTON = "change_directory"; private final static String OSMAND_USAGE = "osmand_usage"; @@ -67,7 +70,7 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto private String tmpManuallySpecifiedPath; private DataStorageHelper dataStorageHelper; private boolean calculateTilesBtnPressed; - + private RefreshUsedMemoryTask calculateMemoryTask; private RefreshUsedMemoryTask calculateTilesMemoryTask; @@ -84,10 +87,10 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto } super.onCreate(savedInstanceState); } - + @Override protected void setupPreferences() { - + PreferenceScreen screen = getPreferenceScreen(); if (screen == null || dataStorageHelper == null) { @@ -202,13 +205,13 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto int primaryTextColorResId = isNightMode() ? R.color.text_color_primary_dark : R.color.text_color_primary_light; int primaryTextColor = ContextCompat.getColor(app, primaryTextColorResId); - String[] memoryUnitsFormats = new String[] { + String[] memoryUnitsFormats = new String[]{ getString(R.string.shared_string_memory_kb_desc), getString(R.string.shared_string_memory_mb_desc), getString(R.string.shared_string_memory_gb_desc), getString(R.string.shared_string_memory_tb_desc) }; - + final View itemView = holder.itemView; if (preference instanceof CheckBoxPreference) { StorageItem item = dataStorageHelper.getStorage(key); @@ -226,7 +229,7 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto int defaultIconColor = isNightMode() ? R.color.icon_color_default_dark : R.color.icon_color_default_light; int chosenIconColor = isNightMode() ? R.color.icon_color_osmand_dark : R.color.icon_color_osmand_light; - Drawable icon = app.getUIUtilities().getIcon(item.getIconResId(), + Drawable icon = app.getUIUtilities().getIcon(item.getIconResId(), isCurrent ? chosenIconColor : defaultIconColor); ivIcon.setImageDrawable(icon); @@ -249,6 +252,13 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto } if (currentKey.equals(INTERNAL_STORAGE)) { tvAdditionalDescription.setText(item.getDescription()); + } else if (currentKey.equals(SHARED_STORAGE)) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + BidiFormatter rtlFormatter = BidiFormatter.getInstance(); + tvAdditionalDescription.setText(rtlFormatter.unicodeWrap(item.getDirectory())); + } else { + tvAdditionalDescription.setText(String.format("\u200E%s", item.getDirectory())); + } } else { tvAdditionalDescription.setText(item.getDirectory()); } @@ -261,7 +271,7 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto AndroidUtils.setBackground(itemView, drawable); icon.setVisibility(View.INVISIBLE); title.setText(R.string.shared_string_change); - } else if(key.equals(OSMAND_USAGE)) { + } else if (key.equals(OSMAND_USAGE)) { long totalUsageBytes = dataStorageHelper.getTotalUsedBytes(); TextView tvSummary = itemView.findViewById(R.id.summary); tvSummary.setText(DataStorageHelper.getFormattedMemoryInfo(totalUsageBytes, memoryUnitsFormats)); @@ -424,7 +434,7 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto refreshDataInfo(); updateAllSettings(); } - + private void refreshDataInfo() { calculateTilesBtnPressed = false; dataStorageHelper = new DataStorageHelper(app); @@ -460,7 +470,7 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto public void onMemoryInfoUpdate() { updateAllSettings(); } - + @Override public void onFinishUpdating(String tag) { updateAllSettings(); diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/CoordinatesFormatFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/CoordinatesFormatFragment.java index fd62f6df3c..2af8ac55b8 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/CoordinatesFormatFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/CoordinatesFormatFragment.java @@ -146,7 +146,7 @@ public class CoordinatesFormatFragment extends BaseSettingsFragment { return spannableBuilder; } - return getString(R.string.ltr_or_rtl_combine_via_colon, getString(R.string.shared_string_example), formattedCoordinates); + return formattedCoordinates; } @Override diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/MainSettingsFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/MainSettingsFragment.java index 225ed389e7..521758e6fc 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/MainSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/MainSettingsFragment.java @@ -6,7 +6,6 @@ import android.os.Bundle; import android.view.View; import androidx.annotation.ColorRes; -import androidx.core.content.ContextCompat; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import androidx.preference.Preference; @@ -44,6 +43,7 @@ public class MainSettingsFragment extends BaseSettingsFragment implements OnSele private static final String CONFIGURE_PROFILE = "configure_profile"; private static final String APP_PROFILES = "app_profiles"; + private static final String PURCHASES_SETTINGS = "purchases_settings"; private static final String SELECTED_PROFILE = "selected_profile"; private static final String CREATE_PROFILE = "create_profile"; private static final String IMPORT_PROFILE = "import_profile"; @@ -70,10 +70,12 @@ public class MainSettingsFragment extends BaseSettingsFragment implements OnSele availableAppModes = new LinkedHashSet<>(ApplicationMode.values(getMyApplication())); Preference globalSettings = findPreference("global_settings"); globalSettings.setIcon(getContentIcon(R.drawable.ic_action_settings)); - PreferenceCategory selectedProfile = (PreferenceCategory) findPreference(SELECTED_PROFILE); + Preference purchasesSettings = findPreference(PURCHASES_SETTINGS); + purchasesSettings.setIcon(getContentIcon(R.drawable.ic_action_purchases)); + PreferenceCategory selectedProfile = findPreference(SELECTED_PROFILE); selectedProfile.setIconSpaceReserved(false); setupConfigureProfilePref(); - PreferenceCategory appProfiles = (PreferenceCategory) findPreference(APP_PROFILES); + PreferenceCategory appProfiles = findPreference(APP_PROFILES); appProfiles.setIconSpaceReserved(false); setupAppProfiles(appProfiles); profileManagementPref(); @@ -148,7 +150,14 @@ public class MainSettingsFragment extends BaseSettingsFragment implements OnSele FragmentManager fragmentManager = mapActivity.getSupportFragmentManager(); ExportSettingsFragment.showInstance(fragmentManager, mode, true); } + } else if (PURCHASES_SETTINGS.equals(prefId)) { + MapActivity mapActivity = getMapActivity(); + if (mapActivity != null) { + FragmentManager fragmentManager = mapActivity.getSupportFragmentManager(); + PurchasesFragment.showInstance(fragmentManager); + } } + return super.onPreferenceClick(preference); } diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/PurchasesFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/PurchasesFragment.java new file mode 100644 index 0000000000..e3bbb85f04 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/PurchasesFragment.java @@ -0,0 +1,208 @@ +package net.osmand.plus.settings.fragments; + +import android.app.Activity; +import android.content.Context; +import android.net.Uri; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.TextView; + +import com.google.android.material.appbar.AppBarLayout; + +import net.osmand.AndroidUtils; +import net.osmand.PlatformUtil; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.plus.UiUtilities; +import net.osmand.plus.Version; +import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.activities.OsmandInAppPurchaseActivity; +import net.osmand.plus.base.BaseOsmAndFragment; +import net.osmand.plus.inapp.InAppPurchaseHelper; +import net.osmand.plus.inapp.InAppPurchaseHelper.InAppPurchaseListener; +import net.osmand.plus.liveupdates.CountrySelectionFragment; +import net.osmand.plus.liveupdates.CountrySelectionFragment.OnFragmentInteractionListener; +import net.osmand.plus.wikipedia.WikipediaDialogFragment; +import net.osmand.util.Algorithms; + +import org.apache.commons.logging.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.FragmentManager; + +public class PurchasesFragment extends BaseOsmAndFragment implements InAppPurchaseListener, OnFragmentInteractionListener { + + private static final Log log = PlatformUtil.getLog(PurchasesFragment.class); + public static final String TAG = PurchasesFragment.class.getName(); + + public static final String KEY_IS_SUBSCRIBER = "action_is_new"; + + private static final String OSMAND_PURCHASES_URL = "https://docs.osmand.net/en/main@latest/osmand/purchases"; + + private OsmandApplication app; + private InAppPurchaseHelper purchaseHelper; + + private ViewGroup cardsContainer; + private SubscriptionsCard subscriptionsCard; + + private boolean nightMode; + private Boolean isPaidVersion; + + public static boolean showInstance(FragmentManager fragmentManager) { + try { + PurchasesFragment fragment = new PurchasesFragment(); + fragmentManager.beginTransaction() + .add(R.id.fragmentContainer, fragment, TAG) + .addToBackStack(TAG) + .commitAllowingStateLoss(); + return true; + } catch (Exception e) { + log.error(e); + return false; + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + app = getMyApplication(); + isPaidVersion = Version.isPaidVersion(app); + nightMode = !app.getSettings().isLightContent(); + LayoutInflater themedInflater = UiUtilities.getInflater(getContext(), nightMode); + + View mainView = themedInflater.inflate(R.layout.purchases_layout, container, false); + AndroidUtils.addStatusBarPadding21v(getActivity(), mainView); + createToolbar(mainView, nightMode); + cardsContainer = mainView.findViewById(R.id.cards_container); + + return mainView; + } + + @Override + public void onResume() { + super.onResume(); + updateCards(); + } + + private void updateCards() { + MapActivity mapActivity = getMapActivity(); + purchaseHelper = getInAppPurchaseHelper(); + cardsContainer.removeAllViews(); + if (mapActivity == null || purchaseHelper == null) { + return; + } + + boolean hasSubscriptions = !Algorithms.isEmpty(purchaseHelper.getEverMadeSubscriptions()); + if (hasSubscriptions) { + subscriptionsCard = new SubscriptionsCard(mapActivity, this, purchaseHelper); + cardsContainer.addView(subscriptionsCard.build(mapActivity)); + } + + cardsContainer.addView(new TroubleshootingOrPurchasingCard(mapActivity, purchaseHelper, isPaidVersion) + .build(mapActivity)); + } + + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + super.onSaveInstanceState(outState); + outState.putBoolean(KEY_IS_SUBSCRIBER, isPaidVersion); + } + + @Override + public void onViewStateRestored(@Nullable Bundle savedInstanceState) { + super.onViewStateRestored(savedInstanceState); + if (savedInstanceState != null) { + isPaidVersion = savedInstanceState.getBoolean(KEY_IS_SUBSCRIBER); + } + } + + @Nullable + public InAppPurchaseHelper getInAppPurchaseHelper() { + Activity activity = getActivity(); + if (activity instanceof OsmandInAppPurchaseActivity) { + return ((OsmandInAppPurchaseActivity) activity).getPurchaseHelper(); + } else { + return null; + } + } + + private void createToolbar(View mainView, final boolean nightMode) { + AppBarLayout appbar = mainView.findViewById(R.id.appbar); + View toolbar = UiUtilities.getInflater(getContext(), nightMode).inflate(R.layout.profile_preference_toolbar_with_icon, appbar, false); + + View iconToolbarContainer = toolbar.findViewById(R.id.icon_toolbar); + ImageView icon = iconToolbarContainer.findViewById(R.id.profile_icon); + icon.setImageResource(R.drawable.ic_action_help_online); + icon.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (getContext() != null) { + WikipediaDialogFragment.showFullArticle(getContext(), Uri.parse(OSMAND_PURCHASES_URL), nightMode); + } + } + }); + ImageButton backButton = toolbar.findViewById(R.id.close_button); + backButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + FragmentActivity fragmentActivity = getActivity(); + if (fragmentActivity != null) { + fragmentActivity.onBackPressed(); + } + } + }); + + TextView toolbarTitle = toolbar.findViewById(R.id.toolbar_title); + toolbarTitle.setText(getString(R.string.purchases)); + appbar.addView(toolbar); + } + + @Override + public void onError(InAppPurchaseHelper.InAppPurchaseTaskType taskType, String error) { + } + + @Override + public void onGetItems() { + if (app != null) { + isPaidVersion = Version.isPaidVersion(app); + } + updateCards(); + } + + @Override + public void onItemPurchased(String sku, boolean active) { + isPaidVersion = true; + if (purchaseHelper != null) { + purchaseHelper.requestInventory(); + } + } + + @Override + public void showProgress(InAppPurchaseHelper.InAppPurchaseTaskType taskType) { + } + + @Override + public void dismissProgress(InAppPurchaseHelper.InAppPurchaseTaskType taskType) { + } + + @Override + public void onSearchResult(CountrySelectionFragment.CountryItem name) { + if (subscriptionsCard != null) { + subscriptionsCard.onSupportRegionSelected(name); + } + } + + private MapActivity getMapActivity() { + return (MapActivity) getActivity(); + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/SubscriptionsCard.java b/OsmAnd/src/net/osmand/plus/settings/fragments/SubscriptionsCard.java new file mode 100644 index 0000000000..6b7ba26980 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/SubscriptionsCard.java @@ -0,0 +1,145 @@ +package net.osmand.plus.settings.fragments; + +import android.content.Intent; +import android.net.Uri; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; + +import net.osmand.AndroidUtils; +import net.osmand.plus.R; +import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.helpers.AndroidUiHelper; +import net.osmand.plus.inapp.InAppPurchaseHelper; +import net.osmand.plus.liveupdates.CountrySelectionFragment; +import net.osmand.plus.liveupdates.LiveUpdatesFragment; +import net.osmand.plus.liveupdates.OsmLiveActivity; +import net.osmand.plus.routepreparationmenu.cards.BaseCard; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.util.Algorithms; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; + +public class SubscriptionsCard extends BaseCard { + + private static final String PLAY_STORE_SUBSCRIPTION_URL = "https://play.google.com/store/account/subscriptions"; + private static final String PLAY_STORE_SUBSCRIPTION_DEEPLINK_URL = "https://play.google.com/store/account/subscriptions?sku=%s&package=%s"; + + private Fragment target; + private CountrySelectionFragment countrySelectionFragment = new CountrySelectionFragment(); + private SubscriptionsListCard subscriptionsListCard; + + private InAppPurchaseHelper purchaseHelper; + + @Override + public int getCardLayoutId() { + return R.layout.subscriptions_card; + } + + public SubscriptionsCard(@NonNull MapActivity mapActivity, @NonNull Fragment target, @NonNull InAppPurchaseHelper purchaseHelper) { + super(mapActivity, false); + this.target = target; + this.purchaseHelper = purchaseHelper; + } + + @Override + protected void updateContent() { + if (purchaseHelper == null || Algorithms.isEmpty(purchaseHelper.getEverMadeSubscriptions())) { + AndroidUiHelper.updateVisibility(view, false); + return; + } else { + AndroidUiHelper.updateVisibility(view, true); + } + + updateSubscriptionsListCard(); + setupSupportRegion(); + + LinearLayout reportContainer = view.findViewById(R.id.report_container); + reportContainer.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(mapActivity, OsmLiveActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + mapActivity.startActivity(intent); + } + }); + + LinearLayout liveUpdatesContainer = view.findViewById(R.id.live_updates_container); + liveUpdatesContainer.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + LiveUpdatesFragment.showInstance(mapActivity.getSupportFragmentManager(), target); + } + }); + + final String subscriptionUrl = getSubscriptionUrl(); + LinearLayout manageSubscriptionContainer = view.findViewById(R.id.manage_subscription_container); + manageSubscriptionContainer.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(subscriptionUrl)); + if (AndroidUtils.isIntentSafe(mapActivity, intent)) { + target.startActivity(intent); + } + } + }); + } + + public void updateSubscriptionsListCard() { + if (subscriptionsListCard == null) { + ViewGroup subscriptionsListContainer = view.findViewById(R.id.subscriptions_list_container); + subscriptionsListContainer.removeAllViews(); + subscriptionsListCard = new SubscriptionsListCard(mapActivity, purchaseHelper); + subscriptionsListContainer.addView(subscriptionsListCard.build(mapActivity)); + } else { + subscriptionsListCard.update(); + } + } + + private void setupSupportRegion() { + String region = LiveUpdatesFragment.getSupportRegionName(app, purchaseHelper); + String header = LiveUpdatesFragment.getSupportRegionHeader(app, region); + TextView supportRegionHeader = view.findViewById(R.id.support_region_header); + TextView supportRegion = view.findViewById(R.id.support_region); + supportRegionHeader.setText(header); + supportRegion.setText(region); + + View supportRegionContainer = view.findViewById(R.id.support_region_container); + supportRegionContainer.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + CountrySelectionFragment countryCountrySelectionFragment = countrySelectionFragment; + countryCountrySelectionFragment.show(target.getChildFragmentManager(), CountrySelectionFragment.TAG); + } + }); + + countrySelectionFragment.initCountries(app); + } + + private String getSubscriptionUrl() { + InAppPurchaseHelper purchaseHelper = app.getInAppPurchaseHelper(); + if (purchaseHelper != null && purchaseHelper.getFullVersion() != null) { + String sku = purchaseHelper.getFullVersion().getSku(); + return String.format(PLAY_STORE_SUBSCRIPTION_DEEPLINK_URL, + sku, mapActivity.getPackageName()); + } else { + return PLAY_STORE_SUBSCRIPTION_URL; + } + } + + public void onSupportRegionSelected(CountrySelectionFragment.CountryItem selectedCountryItem) { + String countryName = selectedCountryItem != null ? selectedCountryItem.getLocalName() : ""; + String countryDownloadName = selectedCountryItem != null ? + selectedCountryItem.getDownloadName() : OsmandSettings.BILLING_USER_DONATION_WORLD_PARAMETER; + + TextView supportRegionHeader = view.findViewById(R.id.support_region_header); + TextView supportRegion = view.findViewById(R.id.support_region); + supportRegionHeader.setText(LiveUpdatesFragment.getSupportRegionHeader(app, countryName)); + supportRegion.setText(countryName); + app.getSettings().BILLING_USER_COUNTRY.set(countryName); + app.getSettings().BILLING_USER_COUNTRY_DOWNLOAD_NAME.set(countryDownloadName); + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/SubscriptionsListCard.java b/OsmAnd/src/net/osmand/plus/settings/fragments/SubscriptionsListCard.java new file mode 100644 index 0000000000..645a68b037 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/SubscriptionsListCard.java @@ -0,0 +1,124 @@ +package net.osmand.plus.settings.fragments; + +import android.os.Build; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import net.osmand.AndroidUtils; +import net.osmand.Period; +import net.osmand.plus.R; +import net.osmand.plus.UiUtilities; +import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.chooseplan.ChoosePlanDialogFragment; +import net.osmand.plus.helpers.AndroidUiHelper; +import net.osmand.plus.inapp.InAppPurchaseHelper; +import net.osmand.plus.inapp.InAppPurchases.InAppSubscription; +import net.osmand.plus.inapp.InAppPurchases.InAppSubscription.SubscriptionState; +import net.osmand.plus.routepreparationmenu.cards.BaseCard; +import net.osmand.util.Algorithms; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Locale; + +import androidx.annotation.NonNull; + +public class SubscriptionsListCard extends BaseCard { + + private final InAppPurchaseHelper purchaseHelper; + + private final SimpleDateFormat dateFormat; + + @Override + public int getCardLayoutId() { + return R.layout.subscriptions_list_card; + } + + public SubscriptionsListCard(@NonNull MapActivity mapActivity, @NonNull InAppPurchaseHelper purchaseHelper) { + super(mapActivity, false); + this.purchaseHelper = purchaseHelper; + this.dateFormat = new SimpleDateFormat("MMM d, yyyy", Locale.getDefault()); + } + + @Override + protected void updateContent() { + if (purchaseHelper == null || Algorithms.isEmpty(purchaseHelper.getEverMadeSubscriptions())) { + AndroidUiHelper.updateVisibility(view, false); + return; + } else { + AndroidUiHelper.updateVisibility(view, true); + } + + LayoutInflater inflater = UiUtilities.getInflater(mapActivity, nightMode); + ((ViewGroup) view).removeAllViews(); + + List subscriptions = purchaseHelper.getEverMadeSubscriptions(); + for (int i = 0; i < subscriptions.size(); i++) { + InAppSubscription subscription = subscriptions.get(i); + SubscriptionState state = subscription.getState(); + boolean autoRenewed = state == SubscriptionState.ACTIVE || state == SubscriptionState.IN_GRACE_PERIOD; + + View card = inflater.inflate(R.layout.subscription_layout, null, false); + ((ViewGroup) view).addView(card); + + TextView subscriptionPeriod = card.findViewById(R.id.subscription_type); + String period = app.getString(subscription.getPeriodTypeString()); + if (!Algorithms.isEmpty(period)) { + subscriptionPeriod.setText(period); + AndroidUiHelper.updateVisibility(subscriptionPeriod, true); + } + + if (autoRenewed) { + TextView nextBillingDate = card.findViewById(R.id.next_billing_date); + String date = getHumanDate(subscription.getPurchaseTime(), subscription.getSubscriptionPeriod()); + if (!Algorithms.isEmpty(date)) { + nextBillingDate.setText(app.getString(R.string.next_billing_date, date)); + AndroidUiHelper.updateVisibility(nextBillingDate, true); + } + } else { + View renewContainer = card.findViewById(R.id.renewContainer); + AndroidUiHelper.updateVisibility(renewContainer, true); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + AndroidUtils.setBackground(mapActivity, renewContainer, nightMode, R.drawable.ripple_light, R.drawable.ripple_dark); + } else { + AndroidUtils.setBackground(mapActivity, renewContainer, nightMode, R.drawable.btn_unstroked_light, R.drawable.btn_unstroked_dark); + } + final String sku = subscription.getSku(); + renewContainer.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + ChoosePlanDialogFragment.subscribe(app, mapActivity, purchaseHelper, sku); + } + }); + + View renew = card.findViewById(R.id.renew); + AndroidUtils.setBackground(mapActivity, renew, nightMode, + R.drawable.btn_solid_border_light, R.drawable.btn_solid_border_dark); + } + + TextView status = card.findViewById(R.id.status); + status.setText(app.getString(state.getStringRes())); + AndroidUtils.setBackground(status, app.getUIUtilities().getIcon(state.getBackgroundRes())); + + int dividerLayout = i + 1 == subscriptions.size() ? R.layout.simple_divider_item : R.layout.divider_half_item; + View divider = inflater.inflate(dividerLayout, (ViewGroup) view, false); + ((ViewGroup) view).addView(divider); + } + } + + private String getHumanDate(long time, Period period) { + if (period == null || period.getUnit() == null) { + return ""; + } + Date date = new Date(time); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.add(period.getUnit().getCalendarIdx(), period.getNumberOfUnits()); + date = calendar.getTime(); + return dateFormat.format(date); + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/TroubleshootingOrPurchasingCard.java b/OsmAnd/src/net/osmand/plus/settings/fragments/TroubleshootingOrPurchasingCard.java new file mode 100644 index 0000000000..44fb4a980b --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/TroubleshootingOrPurchasingCard.java @@ -0,0 +1,125 @@ +package net.osmand.plus.settings.fragments; + +import android.graphics.Typeface; +import android.net.Uri; +import android.os.Build; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.method.LinkMovementMethod; +import android.text.style.StyleSpan; +import android.text.style.URLSpan; +import android.view.View; +import android.widget.TextView; + +import net.osmand.AndroidUtils; +import net.osmand.plus.R; +import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.chooseplan.ChoosePlanDialogFragment; +import net.osmand.plus.inapp.InAppPurchaseHelper; +import net.osmand.plus.routepreparationmenu.cards.BaseCard; +import net.osmand.plus.wikipedia.WikipediaDialogFragment; + +import androidx.annotation.NonNull; +import androidx.cardview.widget.CardView; +import androidx.core.content.ContextCompat; + +public class TroubleshootingOrPurchasingCard extends BaseCard { + + private static final String OSMAND_NEW_DEVICE_URL = "https://docs.osmand.net/en/main@latest/osmand/purchases#new-device--new-account"; + private static final String OSMAND_EMAIL = "support@osmand.net"; + private static final String EMAIL_DEEPLINK_URI = "mailto:support@osmand.net"; + + protected InAppPurchaseHelper purchaseHelper; + + private final boolean isPaidVersion; + + @Override + public int getCardLayoutId() { + return isPaidVersion ? R.layout.troubleshooting_card : R.layout.no_purchases_card; + } + + public TroubleshootingOrPurchasingCard(@NonNull MapActivity mapActivity, @NonNull InAppPurchaseHelper purchaseHelper, boolean isPaidVersion) { + super(mapActivity, false); + this.purchaseHelper = purchaseHelper; + this.isPaidVersion = isPaidVersion; + } + + @Override + protected void updateContent() { + setupRestorePurchasesBtn(); + setupNewDeviceOrAccountBtn(); + setupSupportDescription(); + setupContactUsLink(); + + if (!isPaidVersion) { + TextView infoDescription = view.findViewById(R.id.info_description); + String restorePurchases = app.getString(R.string.restore_purchases); + String infoPurchases = String.format(app.getString(R.string.empty_purchases_description), restorePurchases); + infoDescription.setText(infoPurchases); + + View osmandLive = view.findViewById(R.id.osmand_live); + osmandLive.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + ChoosePlanDialogFragment.showDialogInstance(getMyApplication(), + mapActivity.getSupportFragmentManager(), + ChoosePlanDialogFragment.ChoosePlanDialogType.OSM_LIVE); + } + }); + + CardView getItButtonContainer = view.findViewById(R.id.card_view); + int colorRes = nightMode ? R.color.switch_button_active_dark : R.color.switch_button_active_light; + getItButtonContainer.setCardBackgroundColor(ContextCompat.getColor(mapActivity, colorRes)); + + View getItButton = view.findViewById(R.id.card_container); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + AndroidUtils.setBackground(mapActivity, getItButton, nightMode, R.drawable.ripple_light, R.drawable.ripple_dark); + } else { + AndroidUtils.setBackground(mapActivity, getItButton, nightMode, R.drawable.btn_unstroked_light, R.drawable.btn_unstroked_dark); + } + } + } + + protected void setupRestorePurchasesBtn() { + View purchasesRestore = view.findViewById(R.id.restore_purchases); + purchasesRestore.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (purchaseHelper != null) { + purchaseHelper.requestInventory(); + } + } + }); + } + + protected void setupNewDeviceOrAccountBtn() { + View newDeviceAccountContainer = view.findViewById(R.id.new_device_account_container); + newDeviceAccountContainer.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + WikipediaDialogFragment.showFullArticle(mapActivity, Uri.parse(OSMAND_NEW_DEVICE_URL), nightMode); + } + }); + } + + protected void setupSupportDescription() { + TextView supportDescription = view.findViewById(R.id.support_link_title); + String supportDescriptionString = app.getString(R.string.contact_support_description, OSMAND_EMAIL); + SpannableString spannableStringMail = new SpannableString(supportDescriptionString); + int startIndex = supportDescriptionString.indexOf(OSMAND_EMAIL); + int endIndex = startIndex + OSMAND_EMAIL.length(); + StyleSpan boldSpan = new StyleSpan(Typeface.BOLD); + spannableStringMail.setSpan(boldSpan, startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + supportDescription.setText(spannableStringMail); + } + + protected void setupContactUsLink() { + TextView contactSupportLink = view.findViewById(R.id.contact_support_title); + SpannableString spannableStringSupport = new SpannableString(app.getString(R.string.contact_support)); + spannableStringSupport.setSpan(new URLSpan(EMAIL_DEEPLINK_URI), 0, spannableStringSupport.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + contactSupportLink.setText(spannableStringSupport); + AndroidUtils.removeLinkUnderline(contactSupportLink); + contactSupportLink.setMovementMethod(LinkMovementMethod.getInstance()); + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/track/GpxBlockStatisticsBuilder.java b/OsmAnd/src/net/osmand/plus/track/GpxBlockStatisticsBuilder.java index 96478490bb..d4bef49ecf 100644 --- a/OsmAnd/src/net/osmand/plus/track/GpxBlockStatisticsBuilder.java +++ b/OsmAnd/src/net/osmand/plus/track/GpxBlockStatisticsBuilder.java @@ -30,6 +30,7 @@ import net.osmand.plus.R; import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.helpers.GpxUiHelper; import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetType; +import net.osmand.plus.myplaces.GPXTabItemType; import net.osmand.plus.myplaces.SegmentActionsListener; import net.osmand.plus.widgets.TextViewEx; import net.osmand.util.Algorithms; @@ -43,16 +44,13 @@ import java.util.Arrays; import java.util.Date; import java.util.List; -import static net.osmand.plus.liveupdates.LiveUpdatesFragmentNew.getDefaultIconColorId; +import static net.osmand.plus.liveupdates.LiveUpdatesFragment.getDefaultIconColorId; +import static net.osmand.plus.myplaces.GPXTabItemType.GPX_TAB_ITEM_SPEED; public class GpxBlockStatisticsBuilder { private static final Log LOG = PlatformUtil.getLog(GpxBlockStatisticsBuilder.class); private static final int BLOCKS_UPDATE_INTERVAL = 1000; - public static final String INIT_BLOCKS_BASE = "init_blocks_base"; - public static final String INIT_BLOCKS_GENERAL = "init_blocks_general"; - public static final String INIT_BLOCKS_ALTITUDE = "init_blocks_altitude"; - public static final String INIT_BLOCKS_SPEED = "init_blocks_speed"; private final OsmandApplication app; private final boolean nightMode; @@ -64,7 +62,7 @@ public class GpxBlockStatisticsBuilder { private BlockStatisticsAdapter adapter; private final List items = new ArrayList<>(); private boolean blocksClickable = true; - private String initBlocksKey = INIT_BLOCKS_BASE; + private GPXTabItemType tabItem = null; private final Handler handler = new Handler(); private Runnable updatingItems; @@ -88,8 +86,8 @@ public class GpxBlockStatisticsBuilder { this.blocksView = blocksView; } - public void setInitBlocksKey(String initBlocksKey) { - this.initBlocksKey = initBlocksKey; + public void setTabItem(GPXTabItemType tabItem) { + this.tabItem = tabItem; } @Nullable @@ -133,6 +131,11 @@ public class GpxBlockStatisticsBuilder { } } + public void restartUpdatingStatBlocks() { + stopUpdatingStatBlocks(); + runUpdatingStatBlocksIfNeeded(); + } + public void initItems() { GPXFile gpxFile = getGPXFile(); if (app == null || gpxFile == null) { @@ -154,55 +157,59 @@ public class GpxBlockStatisticsBuilder { } items.clear(); if (analysis != null) { - switch (initBlocksKey) { - case INIT_BLOCKS_GENERAL: { - float totalDistance = withoutGaps ? analysis.totalDistanceWithoutGaps : analysis.totalDistance; - float timeSpan = withoutGaps ? analysis.timeSpanWithoutGaps : analysis.timeSpan; - Date start = new Date(analysis.startTime); - Date end = new Date(analysis.endTime); - prepareDataDistance(totalDistance); - prepareDataTimeSpan(timeSpan); - prepareDataStartTime(start); - prepareDataEndTime(end); - break; - } - case INIT_BLOCKS_ALTITUDE: { - String min = OsmAndFormatter.getFormattedAlt(analysis.minElevation, app); - String max = OsmAndFormatter.getFormattedAlt(analysis.maxElevation, app); - String asc = OsmAndFormatter.getFormattedAlt(analysis.diffElevationUp, app); - String desc = OsmAndFormatter.getFormattedAlt(analysis.diffElevationDown, app); - prepareDataAverageAltitude(); - prepareDataAltitudeRange(min, max); - prepareDataAscent(asc); - prepareDataDescent(desc); - break; - } - case INIT_BLOCKS_SPEED: { - String avg = OsmAndFormatter.getFormattedSpeed(analysis.avgSpeed, app); - String max = OsmAndFormatter.getFormattedSpeed(analysis.maxSpeed, app); - long timeMoving = withoutGaps ? analysis.timeMovingWithoutGaps : analysis.timeMoving; - float totalDistanceMoving = withoutGaps ? analysis.totalDistanceMovingWithoutGaps : analysis.totalDistanceMoving; - prepareDataAverageSpeed(avg); - prepareDataMaximumSpeed(max); - prepareDataTimeMoving(timeMoving); - prepareDataDistanceCorrected(totalDistanceMoving); - break; - } - default: - case INIT_BLOCKS_BASE: { - float totalDistance = withoutGaps ? analysis.totalDistanceWithoutGaps : analysis.totalDistance; - String asc = OsmAndFormatter.getFormattedAlt(analysis.diffElevationUp, app); - String desc = OsmAndFormatter.getFormattedAlt(analysis.diffElevationDown, app); - String avg = OsmAndFormatter.getFormattedSpeed(analysis.avgSpeed, app); - String max = OsmAndFormatter.getFormattedSpeed(analysis.maxSpeed, app); - float timeSpan = withoutGaps ? analysis.timeSpanWithoutGaps : analysis.timeSpan; - prepareDataDistance(totalDistance); - prepareDataAscent(asc); - prepareDataDescent(desc); - prepareDataAverageSpeed(avg); - prepareDataMaximumSpeed(max); - prepareDataTimeSpan(timeSpan); - break; + if (tabItem == null) { + float totalDistance = withoutGaps ? analysis.totalDistanceWithoutGaps : analysis.totalDistance; + String asc = OsmAndFormatter.getFormattedAlt(analysis.diffElevationUp, app); + String desc = OsmAndFormatter.getFormattedAlt(analysis.diffElevationDown, app); + String minElevation = OsmAndFormatter.getFormattedAlt(analysis.minElevation, app); + String maxElevation = OsmAndFormatter.getFormattedAlt(analysis.maxElevation, app); + String avg = OsmAndFormatter.getFormattedSpeed(analysis.avgSpeed, app); + String maxSpeed = OsmAndFormatter.getFormattedSpeed(analysis.maxSpeed, app); + float timeSpan = withoutGaps ? analysis.timeSpanWithoutGaps : analysis.timeSpan; + long timeMoving = withoutGaps ? analysis.timeMovingWithoutGaps : analysis.timeMoving; + prepareDataDistance(totalDistance); + prepareDataAscent(asc); + prepareDataDescent(desc); + prepareDataAltitudeRange(minElevation, maxElevation); + prepareDataAverageSpeed(avg); + prepareDataMaximumSpeed(maxSpeed); + prepareDataTimeSpan(timeSpan); + prepareDataTimeMoving(timeMoving); + } else { + switch (tabItem) { + case GPX_TAB_ITEM_GENERAL: { + float totalDistance = withoutGaps ? analysis.totalDistanceWithoutGaps : analysis.totalDistance; + float timeSpan = withoutGaps ? analysis.timeSpanWithoutGaps : analysis.timeSpan; + Date start = new Date(analysis.startTime); + Date end = new Date(analysis.endTime); + prepareDataDistance(totalDistance); + prepareDataTimeSpan(timeSpan); + prepareDataStartTime(start); + prepareDataEndTime(end); + break; + } + case GPX_TAB_ITEM_ALTITUDE: { + String min = OsmAndFormatter.getFormattedAlt(analysis.minElevation, app); + String max = OsmAndFormatter.getFormattedAlt(analysis.maxElevation, app); + String asc = OsmAndFormatter.getFormattedAlt(analysis.diffElevationUp, app); + String desc = OsmAndFormatter.getFormattedAlt(analysis.diffElevationDown, app); + prepareDataAverageAltitude(); + prepareDataAltitudeRange(min, max); + prepareDataAscent(asc); + prepareDataDescent(desc); + break; + } + case GPX_TAB_ITEM_SPEED: { + String avg = OsmAndFormatter.getFormattedSpeed(analysis.avgSpeed, app); + String max = OsmAndFormatter.getFormattedSpeed(analysis.maxSpeed, app); + long timeMoving = withoutGaps ? analysis.timeMovingWithoutGaps : analysis.timeMoving; + float totalDistanceMoving = withoutGaps ? analysis.totalDistanceMovingWithoutGaps : analysis.totalDistanceMoving; + prepareDataAverageSpeed(avg); + prepareDataMaximumSpeed(max); + prepareDataTimeMoving(timeMoving); + prepareDataDistanceCorrected(totalDistanceMoving); + break; + } } } } @@ -219,8 +226,7 @@ public class GpxBlockStatisticsBuilder { } public void prepareDataAltitudeRange(String min, String max) { - String pattern = app.getString(R.string.ltr_or_rtl_combine_via_dash); - prepareData(app.getString(R.string.altitude_range), String.format(pattern, min, max), + prepareData(app.getString(R.string.altitude_range), min + " - " + max, R.drawable.ic_action_altitude_range_16, GPXDataSetType.ALTITUDE, null, ItemType.ITEM_ALTITUDE); } @@ -247,9 +253,8 @@ public class GpxBlockStatisticsBuilder { } public void prepareDataTimeMoving(long timeMoving) { - prepareData(app.getString(R.string.shared_string_time_moving), - Algorithms.formatDuration((int) (timeMoving / 1000), app.accessibilityEnabled()), - R.drawable.ic_action_time_span_16, GPXDataSetType.SPEED, null, ItemType.ITEM_TIME_MOVING); + prepareData(app.getString(R.string.moving_time), Algorithms.formatDuration((int) (timeMoving / 1000), app.accessibilityEnabled()), + R.drawable.ic_action_time_moving_16, GPXDataSetType.SPEED, null, ItemType.ITEM_TIME_MOVING); } public void prepareDataDistanceCorrected(float totalDistanceMoving) { diff --git a/OsmAnd/src/net/osmand/plus/track/GpxEditDescriptionDialogFragment.java b/OsmAnd/src/net/osmand/plus/track/GpxEditDescriptionDialogFragment.java index b8ef1c24d2..0d8e91e26c 100644 --- a/OsmAnd/src/net/osmand/plus/track/GpxEditDescriptionDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/track/GpxEditDescriptionDialogFragment.java @@ -110,9 +110,8 @@ public class GpxEditDescriptionDialogFragment extends BaseOsmAndDialogFragment { } private void setupSaveButton(View view) { - View btnSave = view.findViewById(R.id.btn_save); - - btnSave.setOnClickListener(new View.OnClickListener() { + View btnSaveContainer = view.findViewById(R.id.btn_save_container); + btnSaveContainer.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Editable editable = editableHtml.getText(); @@ -122,12 +121,16 @@ public class GpxEditDescriptionDialogFragment extends BaseOsmAndDialogFragment { } }); - Context ctx = btnSave.getContext(); + Context ctx = btnSaveContainer.getContext(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - AndroidUtils.setBackground(ctx, btnSave, isNightMode(true), R.drawable.ripple_light, R.drawable.ripple_dark); + AndroidUtils.setBackground(ctx, btnSaveContainer, isNightMode(true), R.drawable.ripple_light, R.drawable.ripple_dark); } else { - AndroidUtils.setBackground(ctx, btnSave, isNightMode(true), R.drawable.btn_unstroked_light, R.drawable.btn_unstroked_dark); + AndroidUtils.setBackground(ctx, btnSaveContainer, isNightMode(true), R.drawable.btn_unstroked_light, R.drawable.btn_unstroked_dark); } + + View btnSave = view.findViewById(R.id.btn_save); + int drawableRes = isNightMode(true) ? R.drawable.btn_solid_border_dark : R.drawable.btn_solid_border_light; + AndroidUtils.setBackground(btnSave, getMyApplication().getUIUtilities().getIcon(drawableRes)); } private void showDismissDialog() { diff --git a/OsmAnd/src/net/osmand/plus/track/TrackMenuFragment.java b/OsmAnd/src/net/osmand/plus/track/TrackMenuFragment.java index bb738a8e00..ef2bd196a7 100644 --- a/OsmAnd/src/net/osmand/plus/track/TrackMenuFragment.java +++ b/OsmAnd/src/net/osmand/plus/track/TrackMenuFragment.java @@ -82,8 +82,8 @@ import net.osmand.plus.myplaces.TrackActivityFragmentAdapter; import net.osmand.plus.osmedit.OsmEditingPlugin; import net.osmand.plus.routepreparationmenu.cards.BaseCard; import net.osmand.plus.routepreparationmenu.cards.BaseCard.CardListener; -import net.osmand.plus.search.QuickSearchDialogFragment; import net.osmand.plus.routing.GPXRouteParams.GPXRouteParamsBuilder; +import net.osmand.plus.search.QuickSearchDialogFragment; import net.osmand.plus.track.SaveGpxAsyncTask.SaveGpxListener; import net.osmand.plus.track.TrackSelectSegmentBottomSheet.OnSegmentSelectedListener; import net.osmand.plus.views.AddGpxPointBottomSheetHelper.NewGpxPoint; @@ -804,7 +804,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card if (gpxFile.getNonEmptySegmentsCount() > 1) { TrackSelectSegmentBottomSheet.showInstance(mapActivity.getSupportFragmentManager(), gpxFile, this); } else { - startNavigationForGPX(gpxFile, mapActions); + startNavigationForGPX(gpxFile, mapActions, mapActivity); dismiss(); } } @@ -827,7 +827,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card segment = segments.get(0); } } - GpxDisplayItemType[] filterTypes = new GpxDisplayItemType[] {GpxDisplayItemType.TRACK_SEGMENT}; + GpxDisplayItemType[] filterTypes = new GpxDisplayItemType[]{GpxDisplayItemType.TRACK_SEGMENT}; List items = TrackDisplayHelper.flatten(displayHelper.getOriginalGroups(filterTypes)); if (segment != null && !Algorithms.isEmpty(items)) { SplitSegmentDialogFragment.showInstance(fragmentManager, displayHelper, items.get(0), segment); @@ -866,7 +866,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card R.string.shared_string_ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - if (FileUtils.removeGpxFile(app, new File((gpxFile.path)))) { + if (FileUtils.removeGpxFile(app, new File(gpxFile.path))) { dismiss(); } } @@ -904,14 +904,15 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card } } - private void startNavigationForGPX(final GPXFile gpxFile, MapActivityActions mapActions) { - if (app.getRoutingHelper().isFollowingMode()) { + public static void startNavigationForGPX(final GPXFile gpxFile, MapActivityActions mapActions, final MapActivity mapActivity) { + if (mapActivity.getMyApplication().getRoutingHelper().isFollowingMode()) { + final WeakReference activityRef = new WeakReference<>(mapActivity); mapActions.stopNavigationActionConfirm(null, new Runnable() { @Override public void run() { - MapActivity mapActivity = getMapActivity(); - if (mapActivity != null) { - mapActivity.getMapActions().enterRoutePlanningModeGivenGpx(gpxFile, null, + MapActivity activity = activityRef.get(); + if (activity != null) { + activity.getMapActions().enterRoutePlanningModeGivenGpx(gpxFile, null, null, null, true, true, MenuState.HEADER_ONLY); } } @@ -1130,7 +1131,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card public boolean onMenuItemClick(MenuItem item) { int i = item.getItemId(); if (i == R.id.action_edit) { - editSegment(segment); + editSegment(); return true; } else if (i == R.id.action_delete) { FragmentActivity activity = getActivity(); @@ -1164,7 +1165,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card app.getSettings().GPX_ROUTE_SEGMENT.set(selectedSegment); MapActivity mapActivity = getMapActivity(); if (mapActivity != null) { - startNavigationForGPX(gpxFile, mapActivity.getMapActions()); + startNavigationForGPX(gpxFile, mapActivity.getMapActions(), mapActivity); GPXRouteParamsBuilder paramsBuilder = app.getRoutingHelper().getCurrentGPXRoute(); if (paramsBuilder != null) { paramsBuilder.setSelectedSegment(selectedSegment); @@ -1174,7 +1175,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card } } - private void editSegment(TrkSegment segment) { + private void editSegment() { GPXFile gpxFile = getGpx(); openPlanRoute(new GpxData(gpxFile)); hide(); @@ -1219,7 +1220,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card @Override public void gpxSavingFinished(Exception errorMessage) { if (selectedGpxFile != null) { - List groups = displayHelper.getDisplayGroups(new GpxDisplayItemType[] {GpxDisplayItemType.TRACK_SEGMENT}); + List groups = displayHelper.getDisplayGroups(new GpxDisplayItemType[]{GpxDisplayItemType.TRACK_SEGMENT}); selectedGpxFile.setDisplayGroups(groups, app); selectedGpxFile.processPoints(app); } @@ -1313,7 +1314,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card } public static void showInstance(@NonNull MapActivity mapActivity, - @Nullable String path, + @Nullable String path, boolean showCurrentTrack, @Nullable final LatLon latLon, @Nullable final String returnScreenName, @@ -1332,7 +1333,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card } public static boolean showInstance(@NonNull MapActivity mapActivity, - @NonNull SelectedGpxFile selectedGpxFile, + @NonNull SelectedGpxFile selectedGpxFile, @Nullable LatLon latLon, @Nullable String returnScreenName, @Nullable String callingFragmentTag) { diff --git a/OsmAnd/src/net/osmand/plus/views/Renderable.java b/OsmAnd/src/net/osmand/plus/views/Renderable.java index 5053f46a69..27d741d964 100644 --- a/OsmAnd/src/net/osmand/plus/views/Renderable.java +++ b/OsmAnd/src/net/osmand/plus/views/Renderable.java @@ -4,6 +4,7 @@ import android.graphics.Canvas; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.Path; +import android.graphics.PointF; import android.graphics.Shader; import net.osmand.GPXUtilities; @@ -67,6 +68,7 @@ public class Renderable { protected double zoom = -1; protected AsynchronousResampler culler = null; // The currently active resampler protected Paint paint = null; // MUST be set by 'updateLocalPaint' before use + protected Paint borderPaint; protected GradientScaleType scaleType = null; protected GpxGeometryWay geometryWay; @@ -88,6 +90,10 @@ public class Renderable { paint.setStrokeWidth(p.getStrokeWidth()); } + public void setBorderPaint(@NonNull Paint paint) { + borderPaint = paint; + } + public void setGradientScaleType(GradientScaleType type) { this.scaleType = type; } @@ -177,33 +183,74 @@ public class Renderable { protected void drawGradient(List pts, Paint p, Canvas canvas, RotatedTileBox tileBox) { QuadRect tileBounds = tileBox.getLatLonBounds(); - Path path = new Path(); + Path currentPath = new Path(); + Path nextPath = new Path(); Paint paint = new Paint(this.paint); - WptPt prevPt = pts.get(0); + + WptPt prevWpt = pts.get(0); + WptPt currWpt = pts.get(1); + + PointF prevXY = new PointF(); + PointF currXY = new PointF(); + PointF nextXY = new PointF(); + + boolean currLineVisible = arePointsInsideTile(prevWpt, currWpt, tileBounds); + boolean nextLineVisible; + + if (currLineVisible) { + pixXYFromWptPt(tileBox, prevXY, prevWpt); + pixXYFromWptPt(tileBox, currXY, currWpt); + canvas.drawPath(pathFromStartEnd(currentPath, prevXY, currXY), borderPaint); + } + for (int i = 1; i < pts.size(); i++) { - WptPt currentPt = pts.get(i); - if (arePointsInsideTile(currentPt, prevPt, tileBounds)) { - float startX = tileBox.getPixXFromLatLon(prevPt.lat, prevPt.lon); - float startY = tileBox.getPixYFromLatLon(prevPt.lat, prevPt.lon); - float endX = tileBox.getPixXFromLatLon(currentPt.lat, currentPt.lon); - float endY = tileBox.getPixYFromLatLon(currentPt.lat, currentPt.lon); - int prevColor = prevPt.getColor(scaleType.toColorizationType()); - int currentColor = currentPt.getColor(scaleType.toColorizationType()); - LinearGradient gradient = new LinearGradient(startX, startY, endX, endY, prevColor, currentColor, Shader.TileMode.CLAMP); - paint.setShader(gradient); - path.reset(); - path.moveTo(startX, startY); - path.lineTo(endX, endY); - canvas.drawPath(path, paint); + currWpt = pts.get(i); + WptPt nextWpt = i + 1 == pts.size() ? null : pts.get(i + 1); + + nextLineVisible = arePointsInsideTile(currWpt, nextWpt, tileBounds); + if (nextWpt != null && nextLineVisible) { + pixXYFromWptPt(tileBox, currXY, currWpt); + pixXYFromWptPt(tileBox, nextXY, nextWpt); + canvas.drawPath(pathFromStartEnd(nextPath, currXY, nextXY), borderPaint); } - prevPt = currentPt; + + if (currLineVisible) { + int prevColor = prevWpt.getColor(scaleType.toColorizationType()); + int currentColor = currWpt.getColor(scaleType.toColorizationType()); + LinearGradient gradient = new LinearGradient(prevXY.x, prevXY.y, currXY.x, currXY.y, + prevColor, currentColor, Shader.TileMode.CLAMP); + paint.setShader(gradient); + canvas.drawPath(currentPath, paint); + } + + prevWpt = currWpt; + currentPath.set(nextPath); + prevXY.set(currXY); + currXY.set(nextXY); + currLineVisible = nextLineVisible; } } protected boolean arePointsInsideTile(WptPt first, WptPt second, QuadRect tileBounds) { + if (first == null || second == null) { + return false; + } return Math.min(first.lon, second.lon) < tileBounds.right && Math.max(first.lon, second.lon) > tileBounds.left && Math.min(first.lat, second.lat) < tileBounds.top && Math.max(first.lat, second.lat) > tileBounds.bottom; } + + protected PointF pixXYFromWptPt(RotatedTileBox tileBox, PointF pointF, WptPt wptPt) { + pointF.x = tileBox.getPixXFromLatLon(wptPt.lat, wptPt.lon); + pointF.y = tileBox.getPixYFromLatLon(wptPt.lat, wptPt.lon); + return pointF; + } + + protected Path pathFromStartEnd(Path path, PointF start, PointF end) { + path.reset(); + path.moveTo(start.x, start.y); + path.lineTo(end.x, end.y); + return path; + } } public static class StandardTrack extends RenderableSegment { diff --git a/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java index c55fb675fd..c16d687614 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java @@ -95,6 +95,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM private OsmandMapTileView view; private Paint paint; + private Paint borderPaint; private Paint shadowPaint; private Paint paintIcon; @@ -190,6 +191,13 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM paint = new Paint(); paint.setStyle(Style.STROKE); paint.setAntiAlias(true); + + borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + borderPaint.setStyle(Style.STROKE); + borderPaint.setStrokeJoin(Paint.Join.ROUND); + borderPaint.setStrokeCap(Paint.Cap.ROUND); + borderPaint.setColor(0x80000000); + shadowPaint = new Paint(); shadowPaint.setStyle(Style.STROKE); shadowPaint.setAntiAlias(true); @@ -341,6 +349,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM } paint.setColor(color == 0 ? cachedColor : color); paint.setStrokeWidth(getTrackWidth(width, defaultTrackWidth)); + borderPaint.setStrokeWidth(paint.getStrokeWidth() + AndroidUtils.dpToPx(view.getContext(), 2)); } private void acquireTrackWidth(String widthKey, RenderingRulesStorage rrs, RenderingRuleSearchRequest req, RenderingContext rc) { @@ -700,6 +709,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM updatePaints(color, width, selectedGpxFile.isRoutePoints(), currentTrack, settings, tileBox); if (ts.renderer instanceof Renderable.RenderableSegment) { Renderable.RenderableSegment renderableSegment = (Renderable.RenderableSegment) ts.renderer; + renderableSegment.setBorderPaint(borderPaint); renderableSegment.setGradientScaleType(scaleType); renderableSegment.drawSegment(view.getZoom(), paint, canvas, tileBox); } diff --git a/OsmAnd/src/net/osmand/plus/views/layers/MapControlsLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/MapControlsLayer.java index 0bfde7edcf..22c077f02d 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/MapControlsLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/MapControlsLayer.java @@ -71,6 +71,7 @@ import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.views.OsmandMapLayer; import net.osmand.plus.views.OsmandMapTileView; import net.osmand.plus.views.corenative.NativeCoreContext; +import net.osmand.plus.views.mapwidgets.WidgetsVisibilityHelper; import java.util.ArrayList; import java.util.HashSet; @@ -851,47 +852,31 @@ public class MapControlsLayer extends OsmandMapLayer { // TODOnightMode // updatextColor(textColor, shadw, rulerControl, zoomControls, mapMenuControls); // default buttons - boolean routePlanningMode = false; + RoutingHelper rh = mapActivity.getRoutingHelper(); - if (rh.isRoutePlanningMode()) { - routePlanningMode = true; - } else if ((rh.isRouteCalculated() || rh.isRouteBeingCalculated()) && !rh.isFollowingMode()) { - routePlanningMode = true; - } - boolean routeFollowingMode = !routePlanningMode && rh.isFollowingMode(); - boolean trackDialogOpened = mapActivity.getTrackDetailsMenu().isVisible(); - boolean shouldHideTopControls = mapActivity.shouldHideTopControls(); - boolean showRouteCalculationControls = routePlanningMode || - ((app.accessibilityEnabled() || (System.currentTimeMillis() - touchEvent < TIMEOUT_TO_SHOW_BUTTONS)) && routeFollowingMode); - boolean routeDialogOpened = mapRouteInfoMenu.isVisible() || (showRouteCalculationControls && mapRouteInfoMenu.needShowMenu()); - updateMyLocationVisibility(backToLocationControl, rh, routeDialogOpened || shouldHideTopControls); - //routePlanningBtn.setIconResId(routeFollowingMode ? R.drawable.ic_action_info_dark : R.drawable.ic_action_gdirections_dark); + WidgetsVisibilityHelper vh = mapActivity.getWidgetsVisibilityHelper(); - updateRoutePlaningButton(rh, routePlanningMode); + boolean isRoutePlanningMode = isInRoutePlanningMode(); + boolean isRouteFollowingMode = !isRoutePlanningMode && rh.isFollowingMode(); + boolean isTimeToShowButtons = System.currentTimeMillis() - touchEvent < TIMEOUT_TO_SHOW_BUTTONS; + boolean shouldShowRouteCalculationControls = isRoutePlanningMode || ((app.accessibilityEnabled() || isTimeToShowButtons) && isRouteFollowingMode); + boolean isRouteDialogOpened = mapRouteInfoMenu.isVisible() || (shouldShowRouteCalculationControls && mapRouteInfoMenu.needShowMenu()); - boolean showBottomMenuButtons = (showRouteCalculationControls || !routeFollowingMode) - && !isInMovingMarkerMode() && !isInGpxDetailsMode() && !isInMeasurementToolMode() - && !isInPlanRouteMode() && !shouldHideTopControls && !isInChoosingRoutesMode() - && !isInWaypointsChoosingMode() && !isInFollowTrackMode() && !isInTrackAppearanceMode() - && !isInRouteLineAppearanceMode(); + boolean showBackToLocation = !isRouteDialogOpened && vh.shouldShowBackToLocationButton(); + backToLocationControl.updateVisibility(showBackToLocation); + + //routePlanningBtn.setIconResId(isRouteFollowingMode ? R.drawable.ic_action_info_dark : R.drawable.ic_action_gdirections_dark); + updateRoutePlaningButton(rh, isRoutePlanningMode); + + boolean showBottomMenuButtons = (shouldShowRouteCalculationControls || !isRouteFollowingMode) && vh.shouldShowBottomMenuButtons(); routePlanningBtn.updateVisibility(showBottomMenuButtons); menuControl.updateVisibility(showBottomMenuButtons); - boolean additionalDialogsHide = !isInGpxApproximationMode() - && !isInTrackAppearanceMode() - && !isInChoosingRoutesMode() - && !isInWaypointsChoosingMode() - && !isInRouteLineAppearanceMode(); - boolean showZoomButtons = !routeDialogOpened && !shouldHideTopControls - && !isInFollowTrackMode() - && (additionalDialogsHide || !portrait); + boolean showZoomButtons = !isRouteDialogOpened && vh.shouldShowZoomButtons(); mapZoomIn.updateVisibility(showZoomButtons); mapZoomOut.updateVisibility(showZoomButtons); - boolean forceHideCompass = routeDialogOpened || trackDialogOpened || isInMeasurementToolMode() - || isInPlanRouteMode() || shouldHideTopControls || isInChoosingRoutesMode() - || isInTrackAppearanceMode() || isInWaypointsChoosingMode() || isInFollowTrackMode() - || isInRouteLineAppearanceMode(); + boolean forceHideCompass = isRouteDialogOpened || vh.shouldHideCompass(); compassHud.forceHideCompass = forceHideCompass; compassHud.updateVisibility(!forceHideCompass && shouldShowCompass()); @@ -900,10 +885,7 @@ public class MapControlsLayer extends OsmandMapLayer { if (layersHud.setIconResId(appMode.getIconRes())) { layersHud.update(app, isNight); } - boolean showTopButtons = !routeDialogOpened && !trackDialogOpened && !shouldHideTopControls - && !isInMeasurementToolMode() && !isInPlanRouteMode() && !isInChoosingRoutesMode() - && !isInTrackAppearanceMode() && !isInWaypointsChoosingMode() && !isInFollowTrackMode() - && !isInRouteLineAppearanceMode(); + boolean showTopButtons = !isRouteDialogOpened && vh.shouldShowTopButtons(); layersHud.updateVisibility(showTopButtons); quickSearchHud.updateVisibility(showTopButtons); @@ -918,7 +900,7 @@ public class MapControlsLayer extends OsmandMapLayer { zoomText.setText(getZoomLevel(tileBox)); } - mapRouteInfoMenu.setVisible(showRouteCalculationControls); + mapRouteInfoMenu.setVisible(shouldShowRouteCalculationControls); if (!forceHideCompass) { updateCompass(isNight); } @@ -1026,19 +1008,6 @@ public class MapControlsLayer extends OsmandMapLayer { } } - public void updateMyLocationVisibility(MapHudButton backToLocationControl, RoutingHelper rh, boolean dialogOpened) { - boolean tracked = mapActivity.getMapViewTrackingUtilities().isMapLinkedToLocation(); - boolean visible = !(tracked && rh.isFollowingMode()); - boolean additionalDialogsHide = !isInTrackAppearanceMode() - && !isInGpxApproximationMode() - && !isInChoosingRoutesMode() - && !isInWaypointsChoosingMode() - && !isInFollowTrackMode() - && !isInRouteLineAppearanceMode(); - backToLocationControl.updateVisibility(visible && !dialogOpened && !isInPlanRouteMode() - && (additionalDialogsHide || !isPotrait())); - } - public boolean onSingleTap(PointF point, RotatedTileBox tileBox) { return mapRouteInfoMenu.onSingleTap(point, tileBox); } @@ -1365,49 +1334,11 @@ public class MapControlsLayer extends OsmandMapLayer { this.mapQuickActionLayer = mapQuickActionLayer; } - private boolean isInMovingMarkerMode() { - return mapQuickActionLayer == null ? contextMenuLayer.isInChangeMarkerPositionMode() || contextMenuLayer.isInAddGpxPointMode() : - mapQuickActionLayer.isInMovingMarkerMode() || contextMenuLayer.isInChangeMarkerPositionMode() || contextMenuLayer.isInAddGpxPointMode(); - } - - private boolean isInGpxDetailsMode() { - return contextMenuLayer.isInGpxDetailsMode(); - } - - private boolean isInMeasurementToolMode() { - return mapActivity.getMapLayers().getMeasurementToolLayer().isInMeasurementMode(); - } - - private boolean isInPlanRouteMode() { - return mapActivity.getMapLayers().getMapMarkersLayer().isInPlanRouteMode(); - } - - private boolean isInTrackAppearanceMode() { - return mapActivity.getMapLayers().getGpxLayer().isInTrackAppearanceMode(); - } - - private boolean isInGpxApproximationMode() { - return mapActivity.getMapLayers().getMeasurementToolLayer().isTapsDisabled(); - } - - public boolean isInTrackMenuMode() { - return mapActivity.getTrackMenuFragment() != null && mapActivity.getTrackMenuFragment().isVisible(); - } - - private boolean isInChoosingRoutesMode() { - return MapRouteInfoMenu.chooseRoutesVisible; - } - - private boolean isInWaypointsChoosingMode() { - return MapRouteInfoMenu.waypointsVisible; - } - - private boolean isInRouteLineAppearanceMode() { - return mapActivity.getMapLayers().getRouteLayer().isInRouteLineAppearanceMode(); - } - - private boolean isInFollowTrackMode() { - return MapRouteInfoMenu.followTrackVisible; + private boolean isInRoutePlanningMode() { + RoutingHelper routingHelper = mapActivity.getRoutingHelper(); + return routingHelper.isRoutePlanningMode() + || ((routingHelper.isRouteCalculated() || routingHelper.isRouteBeingCalculated()) + && !routingHelper.isFollowingMode()); } public static View.OnLongClickListener getOnClickMagnifierListener(final OsmandMapTileView view) { diff --git a/OsmAnd/src/net/osmand/plus/views/layers/MapQuickActionLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/MapQuickActionLayer.java index 3591a2eca9..87fa1f5cbf 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/MapQuickActionLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/MapQuickActionLayer.java @@ -19,7 +19,6 @@ import android.widget.ImageView; import androidx.annotation.DimenRes; import androidx.appcompat.content.res.AppCompatResources; import androidx.core.util.Pair; -import androidx.fragment.app.Fragment; import com.getkeepsafe.taptargetview.TapTarget; import com.getkeepsafe.taptargetview.TapTargetView; @@ -33,18 +32,13 @@ import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.mapcontextmenu.MapContextMenu; -import net.osmand.plus.mapcontextmenu.MapContextMenuFragment; -import net.osmand.plus.mapcontextmenu.other.MapMultiSelectionMenu; -import net.osmand.plus.measurementtool.MeasurementToolLayer; import net.osmand.plus.quickaction.QuickAction; import net.osmand.plus.quickaction.QuickActionRegistry; import net.osmand.plus.quickaction.QuickActionsWidget; -import net.osmand.plus.routepreparationmenu.MapRouteInfoMenu; import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.views.OsmandMapLayer; import net.osmand.plus.views.OsmandMapTileView; -import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; @@ -56,12 +50,6 @@ import static net.osmand.plus.views.layers.ContextMenuLayer.VIBRATE_SHORT; public class MapQuickActionLayer extends OsmandMapLayer implements QuickActionRegistry.QuickActionUpdatesListener, QuickAction.QuickActionSelectionListener { - private final ContextMenuLayer contextMenuLayer; - private final MeasurementToolLayer measurementToolLayer; - private final MapMarkersLayer mapMarkersLayer; - private final MapControlsLayer mapControlsLayer; - private final GPXLayer gpxLayer; - private final RouteLayer routeLayer; private ImageView contextMarker; private final MapActivity mapActivity; private final OsmandApplication app; @@ -81,17 +69,11 @@ public class MapQuickActionLayer extends OsmandMapLayer implements QuickActionRe private boolean nightMode; private Boolean currentWidgetState; - public MapQuickActionLayer(MapActivity activity, ContextMenuLayer contextMenuLayer) { + public MapQuickActionLayer(MapActivity activity) { this.mapActivity = activity; - this.contextMenuLayer = contextMenuLayer; app = activity.getMyApplication(); settings = activity.getMyApplication().getSettings(); quickActionRegistry = app.getQuickActionRegistry(); - measurementToolLayer = mapActivity.getMapLayers().getMeasurementToolLayer(); - mapMarkersLayer = mapActivity.getMapLayers().getMapMarkersLayer(); - gpxLayer = mapActivity.getMapLayers().getGpxLayer(); - mapControlsLayer = mapActivity.getMapLayers().getMapControlsLayer(); - routeLayer = mapActivity.getMapLayers().getRouteLayer(); } @Override @@ -415,29 +397,8 @@ public class MapQuickActionLayer extends OsmandMapLayer implements QuickActionRe } private void setupQuickActionBtnVisibility() { - MapContextMenu contextMenu = mapActivity.getContextMenu(); - MapRouteInfoMenu mapRouteInfoMenu = mapActivity.getMapRouteInfoMenu(); - MapMultiSelectionMenu multiSelectionMenu = contextMenu.getMultiSelectionMenu(); - WeakReference contextMenuMenuFragmentRef = contextMenu.findMenuFragment(); - MapContextMenuFragment contextMenuMenuFragment = contextMenuMenuFragmentRef != null ? contextMenuMenuFragmentRef.get() : null; - Fragment multiMenuFragment = multiSelectionMenu.getFragmentByTag(); - boolean hideQuickButton = !isLayerOn || - contextMenuLayer.isInChangeMarkerPositionMode() || - contextMenuLayer.isInGpxDetailsMode() || - measurementToolLayer.isInMeasurementMode() || - mapMarkersLayer.isInPlanRouteMode() || - gpxLayer.isInTrackAppearanceMode() || - mapControlsLayer.isInTrackMenuMode() || - routeLayer.isInRouteLineAppearanceMode() || - mapRouteInfoMenu.isVisible() || - MapRouteInfoMenu.chooseRoutesVisible || - MapRouteInfoMenu.waypointsVisible || - MapRouteInfoMenu.followTrackVisible || - contextMenu.isVisible() && contextMenuMenuFragment != null && !contextMenuMenuFragment.isRemoving() || - contextMenu.isVisible() && contextMenuMenuFragment != null && contextMenuMenuFragment.isAdded() || - multiSelectionMenu.isVisible() && multiMenuFragment != null && multiMenuFragment.isAdded() || - multiSelectionMenu.isVisible() && multiMenuFragment != null && !multiMenuFragment.isRemoving(); - quickActionButton.setVisibility(hideQuickButton ? View.GONE : View.VISIBLE); + boolean visible = mapActivity.getWidgetsVisibilityHelper().shouldShowQuickActionButton(); + quickActionButton.setVisibility(visible ? View.VISIBLE : View.GONE); } @Override diff --git a/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapInfoWidgetsFactory.java b/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapInfoWidgetsFactory.java index 993790bfea..601749d3dc 100644 --- a/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapInfoWidgetsFactory.java +++ b/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapInfoWidgetsFactory.java @@ -1204,13 +1204,7 @@ public class MapInfoWidgetsFactory { @SuppressLint("SetTextI18n") public boolean updateInfo() { - boolean visible = settings.SHOW_COORDINATES_WIDGET.get() && !map.shouldHideTopControls() - && map.getMapRouteInfoMenu().shouldShowTopControls() && !map.isTopToolbarActive() - && !map.getMapLayers().getGpxLayer().isInTrackAppearanceMode() - && !map.getMapLayers().getRouteLayer().isInRouteLineAppearanceMode() - && !MapRouteInfoMenu.chooseRoutesVisible && !MapRouteInfoMenu.waypointsVisible - && !MapRouteInfoMenu.followTrackVisible; - + boolean visible = map.getWidgetsVisibilityHelper().shouldShowTopCoordinatesWidget(); updateVisibility(visible); if (visible) { lastKnownLocation = locationProvider.getLastKnownLocation(); diff --git a/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapMarkersWidgetsFactory.java b/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapMarkersWidgetsFactory.java index 1490ab32b1..8794414553 100644 --- a/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapMarkersWidgetsFactory.java +++ b/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapMarkersWidgetsFactory.java @@ -36,7 +36,6 @@ public class MapMarkersWidgetsFactory { private boolean portraitMode; private View topBar; - private View addressTopBar; private View topBar2nd; private View rowView; private View rowView2nd; @@ -60,7 +59,6 @@ public class MapMarkersWidgetsFactory { helper = map.getMyApplication().getMapMarkersHelper(); portraitMode = AndroidUiHelper.isOrientationPortrait(map); - addressTopBar = map.findViewById(R.id.map_top_bar); topBar = map.findViewById(R.id.map_markers_top_bar); topBar2nd = map.findViewById(R.id.map_markers_top_bar_2nd); rowView = map.findViewById(R.id.map_marker_row); @@ -181,18 +179,8 @@ public class MapMarkersWidgetsFactory { } List markers = helper.getMapMarkers(); - if (zoom < 3 || markers.size() == 0 - || !map.getMyApplication().getSettings().MARKERS_DISTANCE_INDICATION_ENABLED.get() - || !map.getMyApplication().getSettings().MAP_MARKERS_MODE.get().isToolbar() - || map.getMyApplication().getRoutingHelper().isFollowingMode() - || map.getMyApplication().getRoutingHelper().isRoutePlanningMode() - || map.getMapRouteInfoMenu().isVisible() - || addressTopBar.getVisibility() == View.VISIBLE - || map.isTopToolbarActive() - || map.shouldHideTopControls() - || map.getMapLayers().getGpxLayer().isInTrackAppearanceMode() - || map.getMapLayers().getMapMarkersLayer().isInPlanRouteMode() - || map.getMapLayers().getRouteLayer().isInRouteLineAppearanceMode()) { + WidgetsVisibilityHelper vh = map.getWidgetsVisibilityHelper(); + if (zoom < 3 || markers.size() == 0 || vh.shouldHideMapMarkersWidget()) { updateVisibility(false); return; } diff --git a/OsmAnd/src/net/osmand/plus/views/mapwidgets/WidgetsVisibilityHelper.java b/OsmAnd/src/net/osmand/plus/views/mapwidgets/WidgetsVisibilityHelper.java new file mode 100644 index 0000000000..4c538ddba1 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/views/mapwidgets/WidgetsVisibilityHelper.java @@ -0,0 +1,235 @@ +package net.osmand.plus.views.mapwidgets; + +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; + +import net.osmand.plus.R; +import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.activities.MapActivityLayers; +import net.osmand.plus.helpers.AndroidUiHelper; +import net.osmand.plus.mapcontextmenu.MapContextMenu; +import net.osmand.plus.mapcontextmenu.MapContextMenuFragment; +import net.osmand.plus.mapcontextmenu.other.MapMultiSelectionMenu; +import net.osmand.plus.routepreparationmenu.MapRouteInfoMenu; +import net.osmand.plus.routing.RoutingHelper; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.views.layers.MapQuickActionLayer; + +import java.lang.ref.WeakReference; + +public class WidgetsVisibilityHelper { + + private MapActivity mapActivity; + private OsmandSettings settings; + private RoutingHelper routingHelper; + private MapActivityLayers mapLayers; + + public WidgetsVisibilityHelper(@NonNull MapActivity mapActivity) { + this.mapActivity = mapActivity; + this.settings = mapActivity.getMyApplication().getSettings(); + this.routingHelper = mapActivity.getRoutingHelper(); + this.mapLayers = mapActivity.getMapLayers(); + } + + public boolean shouldShowQuickActionButton() { + return isQuickActionLayerOn() + && !isInChangeMarkerPositionMode() + && !isInGpxDetailsMode() + && !isInMeasurementToolMode() + && !isInPlanRouteMode() + && !isInTrackAppearanceMode() + && !isInTrackMenuMode() + && !isInRouteLineAppearanceMode() + && !isMapRouteInfoMenuVisible() + && !isInChoosingRoutesMode() + && !isInWaypointsChoosingMode() + && !isInFollowTrackMode() + && !isContextMenuFragmentVisible() + && !isMultiSelectionMenuFragmentVisible(); + } + + public boolean shouldShowTopCoordinatesWidget() { + return settings.SHOW_COORDINATES_WIDGET.get() + && !mapActivity.shouldHideTopControls() + && mapActivity.getMapRouteInfoMenu().shouldShowTopControls() + && !mapActivity.isTopToolbarActive() + && !isInTrackAppearanceMode() + && !isInRouteLineAppearanceMode() + && !isInChoosingRoutesMode() + && !isInWaypointsChoosingMode() + && !isInFollowTrackMode(); + } + + public boolean shouldHideMapMarkersWidget() { + View addressTopBar = mapActivity.findViewById(R.id.map_top_bar); + return !settings.MARKERS_DISTANCE_INDICATION_ENABLED.get() + || !settings.MAP_MARKERS_MODE.get().isToolbar() + || addressTopBar != null && addressTopBar.getVisibility() == View.VISIBLE + || routingHelper.isFollowingMode() + || routingHelper.isRoutePlanningMode() + || isMapRouteInfoMenuVisible() + || mapActivity.isTopToolbarActive() + || mapActivity.shouldHideTopControls() + || isInTrackAppearanceMode() + || isInPlanRouteMode() + || isInRouteLineAppearanceMode(); + } + + public boolean shouldShowBottomMenuButtons() { + return !mapActivity.shouldHideTopControls() + && !isInMovingMarkerMode() + && !isInGpxDetailsMode() + && !isInMeasurementToolMode() + && !isInPlanRouteMode() + && !isInChoosingRoutesMode() + && !isInWaypointsChoosingMode() + && !isInFollowTrackMode() + && !isInTrackAppearanceMode() + && !isInRouteLineAppearanceMode(); + } + + public boolean shouldShowZoomButtons() { + boolean additionalDialogsHide = !isInGpxApproximationMode() + && !isInTrackAppearanceMode() + && !isInChoosingRoutesMode() + && !isInWaypointsChoosingMode() + && !isInRouteLineAppearanceMode(); + return !mapActivity.shouldHideTopControls() + && !isInFollowTrackMode() + && (additionalDialogsHide || !isPortrait()); + } + + public boolean shouldHideCompass() { + return mapActivity.shouldHideTopControls() + || isTrackDetailsMenuOpened() + || isInMeasurementToolMode() + || isInPlanRouteMode() + || isInChoosingRoutesMode() + || isInTrackAppearanceMode() + || isInWaypointsChoosingMode() + || isInFollowTrackMode() + || isInRouteLineAppearanceMode(); + } + + public boolean shouldShowTopButtons() { + return !mapActivity.shouldHideTopControls() + && !isTrackDetailsMenuOpened() + && !isInMeasurementToolMode() + && !isInPlanRouteMode() + && !isInChoosingRoutesMode() + && !isInTrackAppearanceMode() + && !isInWaypointsChoosingMode() + && !isInFollowTrackMode() + && !isInRouteLineAppearanceMode(); + } + + public boolean shouldShowBackToLocationButton() { + boolean additionalDialogsHide = !isInTrackAppearanceMode() + && !isInGpxApproximationMode() + && !isInChoosingRoutesMode() + && !isInWaypointsChoosingMode() + && !isInFollowTrackMode() + && !isInRouteLineAppearanceMode(); + return !mapActivity.shouldHideTopControls() + && !isInPlanRouteMode() + && !(isMapLinkedToLocation() && routingHelper.isFollowingMode()) + && (additionalDialogsHide || !isPortrait()); + } + + private boolean isQuickActionLayerOn() { + return mapLayers.getMapQuickActionLayer().isLayerOn(); + } + + private boolean isMapRouteInfoMenuVisible() { + return mapActivity.getMapRouteInfoMenu().isVisible(); + } + + private boolean isInMovingMarkerMode() { + MapQuickActionLayer quickActionLayer = mapLayers.getMapQuickActionLayer(); + boolean isInMovingMarkerMode = quickActionLayer != null && quickActionLayer.isInMovingMarkerMode(); + return isInMovingMarkerMode || isInChangeMarkerPositionMode() || isInAddGpxPointMode(); + } + + private boolean isInGpxDetailsMode() { + return mapLayers.getContextMenuLayer().isInGpxDetailsMode(); + } + + private boolean isInAddGpxPointMode() { + return mapLayers.getContextMenuLayer().isInAddGpxPointMode(); + } + + private boolean isInChangeMarkerPositionMode() { + return mapLayers.getContextMenuLayer().isInChangeMarkerPositionMode(); + } + + private boolean isInMeasurementToolMode() { + return mapLayers.getMeasurementToolLayer().isInMeasurementMode(); + } + + private boolean isInPlanRouteMode() { + return mapLayers.getMapMarkersLayer().isInPlanRouteMode(); + } + + private boolean isInTrackAppearanceMode() { + return mapLayers.getGpxLayer().isInTrackAppearanceMode(); + } + + private boolean isInGpxApproximationMode() { + return mapLayers.getMeasurementToolLayer().isTapsDisabled(); + } + + public boolean isInTrackMenuMode() { + return mapActivity.getTrackMenuFragment() != null && mapActivity.getTrackMenuFragment().isVisible(); + } + + private boolean isInChoosingRoutesMode() { + return MapRouteInfoMenu.chooseRoutesVisible; + } + + private boolean isInWaypointsChoosingMode() { + return MapRouteInfoMenu.waypointsVisible; + } + + private boolean isInRouteLineAppearanceMode() { + return mapLayers.getRouteLayer().isInRouteLineAppearanceMode(); + } + + private boolean isInFollowTrackMode() { + return MapRouteInfoMenu.followTrackVisible; + } + + private boolean isContextMenuFragmentVisible() { + MapContextMenu contextMenu = mapActivity.getContextMenu(); + WeakReference contextMenuMenuFragmentRef = contextMenu.findMenuFragment(); + MapContextMenuFragment contextMenuMenuFragment = contextMenuMenuFragmentRef != null ? contextMenuMenuFragmentRef.get() : null; + if (contextMenuMenuFragment != null && contextMenu.isVisible()) { + return !contextMenuMenuFragment.isRemoving() || contextMenuMenuFragment.isAdded(); + } + return false; + } + + private boolean isMultiSelectionMenuFragmentVisible() { + MapContextMenu contextMenu = mapActivity.getContextMenu(); + MapMultiSelectionMenu multiSelectionMenu = contextMenu.getMultiSelectionMenu(); + Fragment multiMenuFragment = multiSelectionMenu.getFragmentByTag(); + if (multiMenuFragment != null && multiSelectionMenu.isVisible()) { + return multiMenuFragment.isAdded() || !multiMenuFragment.isRemoving(); + } + return false; + } + + private boolean isMapLinkedToLocation() { + return mapActivity.getMapViewTrackingUtilities().isMapLinkedToLocation(); + } + + private boolean isTrackDetailsMenuOpened() { + return mapActivity.getTrackDetailsMenu().isVisible(); + } + + private boolean isPortrait() { + return AndroidUiHelper.isOrientationPortrait(mapActivity); + } + +}