Merge pull request #11078 from osmandapp/osmand_live

Redesign OsmAnd Live
This commit is contained in:
Vitaliy 2021-03-15 18:38:41 +02:00 committed by GitHub
commit b107c430df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 2302 additions and 378 deletions

View file

@ -1064,7 +1064,7 @@
<activity android:name="net.osmand.plus.activities.PrintDialogActivity" />
<activity android:name=".liveupdates.OsmLiveActivity"
android:label="@string/osm_live"/>
android:label="@string/purchases"/>
<activity android:name=".wikivoyage.explore.WikivoyageExploreActivity">
<intent-filter>

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:osmand="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="@dimen/dialog_button_height"
android:layout_marginStart="@dimen/content_padding"
android:layout_marginLeft="@dimen/content_padding"
android:layout_marginEnd="@dimen/content_padding"
android:layout_marginRight="@dimen/content_padding"
android:orientation="vertical">
<LinearLayout
android:id="@+id/button_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:gravity="center"
android:orientation="horizontal"
tools:ignore="UselessParent">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/button_icon"
android:layout_width="@dimen/map_widget_icon"
android:layout_height="@dimen/map_widget_icon"
android:layout_marginEnd="@dimen/content_padding_small"
android:layout_marginRight="@dimen/content_padding_small"
android:tint="?attr/active_color_basic"
tools:srcCompat="@drawable/ic_action_appearance" />
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/button_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/context_menu_controller_height"
android:layout_marginRight="@dimen/context_menu_controller_height"
android:letterSpacing="@dimen/description_letter_spacing"
android:textColor="?attr/active_color_basic"
android:textSize="@dimen/default_desc_text_size"
osmand:typeface="@string/font_roboto_medium"
tools:text="Button" />
</LinearLayout>
</LinearLayout>

View file

@ -13,11 +13,11 @@
android:layout_marginEnd="@dimen/content_padding"
android:layout_marginRight="@dimen/content_padding"
android:minHeight="@dimen/bottom_sheet_list_item_height"
android:paddingStart="@dimen/content_padding"
android:paddingLeft="@dimen/content_padding"
android:paddingRight="@dimen/content_padding"
tools:background="@drawable/rectangle_rounded_right"
android:paddingEnd="@dimen/content_padding"
android:paddingStart="@dimen/content_padding">
android:paddingRight="@dimen/content_padding"
tools:background="@drawable/rectangle_rounded_right">
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/title"
@ -41,7 +41,8 @@
android:background="@null"
android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false" />
android:focusableInTouchMode="false"
android:saveEnabled="false" />
</LinearLayout>

View file

@ -1,63 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:gravity="center_vertical"
android:minHeight="@dimen/bottom_sheet_selected_item_title_height"
android:paddingLeft="@dimen/content_padding"
android:paddingRight="@dimen/content_padding"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:gravity="center_vertical"
android:minHeight="@dimen/bottom_sheet_selected_item_title_height"
android:paddingStart="@dimen/content_padding"
android:paddingEnd="@dimen/content_padding">
android:paddingLeft="@dimen/content_padding"
android:paddingEnd="@dimen/content_padding"
android:paddingRight="@dimen/content_padding">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/icon"
android:layout_width="@dimen/standard_icon_size"
android:layout_height="@dimen/standard_icon_size"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/bottom_sheet_icon_margin"
android:layout_marginRight="@dimen/bottom_sheet_icon_margin"
tools:src="@drawable/list_destination"/>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/icon"
android:layout_width="@dimen/standard_icon_size"
android:layout_height="@dimen/standard_icon_size"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/bottom_sheet_icon_margin"
android:layout_marginRight="@dimen/bottom_sheet_icon_margin"
tools:src="@drawable/list_destination" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:orientation="vertical">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.ListItemTitle"
tools:text="Some title"/>
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.ListItemTitle"
tools:text="Some title" />
<TextView
android:id="@+id/description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="2"
android:textAppearance="@style/TextAppearance.ContextMenuSubtitle"
android:textColor="@null"
tools:text="Some description"/>
</LinearLayout>
<TextView
android:id="@+id/description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="2"
android:textAppearance="@style/TextAppearance.ContextMenuSubtitle"
android:textColor="@null"
tools:text="Some description" />
</LinearLayout>
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/compound_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/bottom_sheet_content_margin"
android:layout_marginStart="@dimen/bottom_sheet_content_margin"
android:background="@null"
android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false"/>
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/compound_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="@dimen/bottom_sheet_content_margin"
android:layout_marginLeft="@dimen/bottom_sheet_content_margin"
android:background="@null"
android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false"
android:saveEnabled="false" />
</LinearLayout>

View file

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:minHeight="@dimen/bottom_sheet_selected_item_title_height"
android:paddingStart="@dimen/content_padding"
android:paddingLeft="@dimen/content_padding"
android:paddingEnd="@dimen/content_padding"
android:paddingRight="@dimen/content_padding">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/content_padding"
android:layout_marginRight="@dimen/content_padding"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/title"
style="@style/TextAppearance.ListItemTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
tools:text="Some title" />
<TextView
android:id="@+id/description"
style="@style/TextAppearance.ContextMenuSubtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:letterSpacing="@dimen/description_letter_spacing"
android:maxLines="1"
tools:text="Some description" />
</LinearLayout>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/icon"
android:layout_width="@dimen/standard_icon_size"
android:layout_height="@dimen/standard_icon_size"
android:layout_gravity="center_vertical"
tools:src="@drawable/list_destination" />
</LinearLayout>

View file

@ -1,8 +1,49 @@
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/activity_background_color"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="@dimen/dashboard_map_toolbar"
android:background="?attr/pstsTabBackground"
android:minHeight="@dimen/dashboard_map_toolbar"
android:theme="?attr/toolbar_theme"
app:contentInsetEnd="0dp"
app:contentInsetLeft="@dimen/settings_divider_margin_start"
app:contentInsetRight="0dp"
app:contentInsetStart="@dimen/settings_divider_margin_start">
<ImageButton
android:id="@+id/toolbar_action"
android:layout_width="@dimen/standard_icon_size"
android:layout_height="@dimen/standard_icon_size"
android:layout_gravity="end"
android:layout_marginTop="@dimen/content_padding"
android:layout_marginEnd="@dimen/content_padding"
android:layout_marginRight="@dimen/content_padding"
android:layout_marginBottom="@dimen/content_padding"
android:background="@null"
android:contentDescription="@string/shared_string_help"
tools:src="@drawable/ic_action_help" />
</androidx.appcompat.widget.Toolbar>
<include layout="@layout/preference_toolbar_switch" />
<include layout="@layout/list_item_import" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh"
@ -19,18 +60,8 @@
android:groupIndicator="@null"
android:headerDividersEnabled="false"
android:orientation="vertical"
tools:context=".liveupdates.LiveUpdatesFragment" />
tools:context=".liveupdates.LiveUpdatesFragmentNew" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<ProgressBar
android:id="@+id/progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="8dp"
android:layout_gravity="top"
android:indeterminate="true"
android:padding="0dp"
android:visibility="invisible"/>
</FrameLayout>
</LinearLayout>

View file

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="@dimen/context_menu_action_buttons_height"
android:clickable="false"
android:orientation="vertical">
<include layout="@layout/card_bottom_divider" />
<View
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<include layout="@layout/card_top_divider" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/setting_list_item_small_height"
android:layout_gravity="center_vertical"
android:background="?attr/bg_color"
android:gravity="center_vertical|start"
android:orientation="horizontal"
android:paddingStart="@dimen/content_padding"
android:paddingLeft="@dimen/content_padding"
android:paddingTop="@dimen/content_padding_small"
android:paddingEnd="@dimen/content_padding"
android:paddingRight="@dimen/content_padding"
android:paddingBottom="@dimen/content_padding_small_half">
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/title"
style="@style/TextAppearance.ListItemCategoryTitle"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
tools:text="@string/available_maps" />
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/description"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginStart="@dimen/content_padding"
android:layout_marginLeft="@dimen/content_padding"
android:paddingStart="@dimen/subHeaderMarginLeft"
android:paddingLeft="@dimen/subHeaderMarginLeft"
android:paddingEnd="0dp"
android:paddingRight="0dp"
android:textSize="@dimen/default_desc_text_size"
tools:text="2/6"
tools:textColor="@color/text_color_secondary_dark" />
</LinearLayout>
</LinearLayout>

View file

@ -13,6 +13,7 @@
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/item_import_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"

View file

@ -21,6 +21,7 @@
android:layout_marginStart="@dimen/content_padding">
<LinearLayout
android:id="@+id/text_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="@dimen/context_menu_action_buttons_height"

View file

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/bg_color"
android:gravity="center_vertical"
android:minHeight="@dimen/setting_list_item_large_height"
tools:layout_height="@dimen/list_item_triple_row_height">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/icon"
android:layout_width="@dimen/standard_icon_size"
android:layout_height="@dimen/standard_icon_size"
android:layout_gravity="center_vertical"
android:layout_marginStart="@dimen/content_padding"
android:layout_marginLeft="@dimen/content_padding"
android:layout_marginEnd="@dimen/content_padding"
android:layout_marginRight="@dimen/content_padding"
tools:src="@drawable/ic_action_info_dark"
tools:tint="@color/icon_color_osmand_light" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="@dimen/content_padding"
android:layout_marginLeft="@dimen/content_padding"
android:layout_weight="1"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:lines="1"
android:textAppearance="@style/TextAppearance.ListItemTitle"
tools:text="Title" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/sub_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:letterSpacing="@dimen/description_letter_spacing"
android:lines="1"
android:textColor="?attr/active_color_basic"
android:textSize="@dimen/default_desc_text_size"
tools:text="Sub" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:letterSpacing="@dimen/description_letter_spacing"
android:lines="1"
android:textColor="?android:textColorSecondary"
android:textSize="@dimen/default_desc_text_size"
tools:text="Description" />
</LinearLayout>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/secondary_icon"
android:layout_width="@dimen/standard_icon_size"
android:layout_height="@dimen/standard_icon_size"
android:layout_gravity="center_vertical"
android:layout_marginStart="@dimen/content_padding"
android:layout_marginLeft="@dimen/content_padding"
app:srcCompat="@drawable/ic_action_additional_option"
app:tint="?attr/default_icon_color" />
</LinearLayout>
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/compound_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/content_padding"
android:layout_marginLeft="@dimen/content_padding"
android:layout_marginEnd="@dimen/content_padding"
android:layout_marginRight="@dimen/content_padding"
android:focusable="false"
tools:checked="true" />
</LinearLayout>

View file

@ -1,77 +1,97 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:osmand="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
xmlns:osmand="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants"
android:background="?attr/list_background_color"
android:minHeight="@dimen/list_item_height"
android:paddingTop="@dimen/content_padding_half"
android:paddingBottom="@dimen/content_padding_half"
android:orientation="vertical">
android:orientation="horizontal"
android:paddingStart="@dimen/list_content_padding"
android:paddingLeft="@dimen/list_content_padding"
android:paddingTop="@dimen/content_padding"
android:paddingEnd="@dimen/list_content_padding"
android:paddingRight="@dimen/list_content_padding"
android:paddingBottom="@dimen/content_padding">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="@dimen/list_item_height"
android:paddingLeft="@dimen/list_content_padding"
android:paddingRight="@dimen/list_content_padding"
android:paddingStart="@dimen/list_content_padding"
android:paddingEnd="@dimen/list_content_padding">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/favorites_icon_right_margin"
android:layout_marginRight="@dimen/favorites_icon_right_margin"
osmand:srcCompat="@drawable/ic_action_subscription_osmand_live" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="@dimen/favorites_icon_right_margin"
android:paddingTop="6dp"
android:tint="@color/map_widget_blue"
osmand:srcCompat="@drawable/ic_action_osm_live"
android:layout_marginEnd="@dimen/favorites_icon_right_margin" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:orientation="vertical">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/osm_live"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/default_list_text_size" />
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/default_list_text_size"
android:text="@string/osm_live"/>
<TextView
android:id="@+id/description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:letterSpacing="@dimen/description_letter_spacing"
android:maxLines="25"
android:text="@string/osm_live_banner_desc"
android:textColor="?android:textColorSecondary"
android:textSize="@dimen/default_desc_text_size" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/content_padding_small"
osmand:cardCornerRadius="3dp"
osmand:cardElevation="0dp"
tools:cardBackgroundColor="@color/active_buttons_and_links_trans_dark">
<TextView
android:id="@+id/description"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:maxLines="25"
android:textColor="?android:textColorSecondary"
android:textSize="@dimen/default_sub_text_size"
android:text="@string/osm_live_banner_desc"/>
<LinearLayout
android:id="@+id/card_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:gravity="center_vertical"
android:minHeight="@dimen/dialog_button_height"
android:orientation="horizontal"
android:paddingStart="@dimen/content_padding_small"
android:paddingLeft="@dimen/content_padding_small"
android:paddingEnd="@dimen/content_padding_half"
android:paddingRight="@dimen/content_padding_half">
</LinearLayout>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:letterSpacing="@dimen/description_letter_spacing"
android:text="@string/get_it"
android:textColor="?attr/colorPrimary"
android:textSize="@dimen/default_desc_text_size" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="6dp"
android:textColor="?attr/color_dialog_buttons"
android:textAllCaps="true"
android:textSize="@dimen/default_desc_text_size"
android:text="@string/get_it"/>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/additional_button_icon"
android:layout_width="@dimen/standard_icon_size"
android:layout_height="@dimen/standard_icon_size"
android:layout_marginStart="@dimen/content_padding_small"
android:layout_marginLeft="@dimen/content_padding_small"
android:tint="?attr/active_color_basic"
osmand:srcCompat="@drawable/ic_arrow_forward" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
</LinearLayout>

View file

@ -416,4 +416,6 @@
<dimen name="titleLineSpacingExtra">5sp</dimen>
<dimen name="descriptionLineSpacingExtra">3sp</dimen>
<dimen name="list_item_triple_row_height">80dp</dimen>
</resources>

View file

@ -20,6 +20,16 @@
<string name="copy_poi_name">Copy POI name</string>
<string name="track_recording_will_be_continued">The recording will be continued.</string>
<string name="select_category_descr">Select category or add new one</string>
<string name="purchases">Purchases</string>
<string name="live_update_delete_updates_msg">Are you sure you want to delete all %s live updates?</string>
<string name="delete_updates">Delete updates</string>
<string name="live_update_frequency_hour_variant">Map updates will be checked every hour. Next time%1$s in %2$s.</string>
<string name="live_update_frequency_day_variant">Map updates will be checked every day. Next time%1$s in %2$s.</string>
<string name="live_update_frequency_week_variant">Map updates will be checked every week. Next time%1$s in %2$s.</string>
<string name="update_frequency">Update frequency</string>
<string name="last_time_checked">Last time checked: %s</string>
<string name="updated">Updated: %s</string>
<string name="latest_openstreetmap_update">Latest OpenStreetMap update available:</string>
<string name="map_widget_distance_by_tap">Distance by tap</string>
<string name="quick_action_coordinates_widget_descr">A toggle to show or hide the Coordinates widget on the map.</string>
<string name="routing_attr_height_obstacles_description">Routing could avoid strong uphills.</string>
@ -2261,7 +2271,7 @@
<string name="shared_string_add">Add</string>
<string name="shared_string_add_to_favorites">Add to \'Favorites\'</string>
<string name="shared_string_my_location">My Position</string>
<string name="shared_string_my_places">My places</string>
<string name="shared_string_my_places">My Places</string>
<string name="shared_string_my_favorites">Favorites</string>
<string name="shared_string_tracks">Tracks</string>
<string name="shared_string_currently_recording_track">Currently recording track</string>

View file

@ -92,12 +92,12 @@ public class AndroidUtils {
public static final MessageFormat formatKb = new MessageFormat("{0, number,##.#}", Locale.US);
public static final MessageFormat formatGb = new MessageFormat("{0, number,#.##}", Locale.US);
public static final MessageFormat formatMb = new MessageFormat("{0, number,##.#}", Locale.US);
/**
* @param context
* @return true if Hardware keyboard is available
*/
public static boolean isHardwareKeyboardAvailable(Context context) {
return context.getResources().getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS;
}
@ -480,6 +480,16 @@ public class AndroidUtils {
: ctx.getResources().getColor(R.color.text_color_secondary_light));
}
@ColorRes
public static int getPrimaryTextColorId(boolean nightMode) {
return nightMode ? R.color.text_color_primary_dark : R.color.text_color_primary_light;
}
@ColorRes
public static int getSecondaryTextColorId(boolean nightMode) {
return nightMode ? R.color.text_color_secondary_dark : R.color.text_color_secondary_light;
}
public static int getTextMaxWidth(float textSize, List<String> titles) {
int width = 0;
for (String title : titles) {
@ -761,7 +771,7 @@ public class AndroidUtils {
tv.setTextDirection(textDirection);
}
}
public static int getLayoutDirection(@NonNull Context ctx) {
Locale currentLocale = ctx.getResources().getConfiguration().locale;
return TextUtilsCompat.getLayoutDirectionFromLocale(currentLocale);
@ -909,7 +919,7 @@ public class AndroidUtils {
public static boolean isRTL() {
return TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == ViewCompat.LAYOUT_DIRECTION_RTL;
}
public static String createNewFileName(String oldName) {
int firstDotIndex = oldName.indexOf('.');
String nameWithoutExt = oldName.substring(0, firstDotIndex);
@ -931,7 +941,7 @@ public class AndroidUtils {
i--;
} while (i >= 0);
int newNumberValue = Integer.parseInt(hasNameNumberSection ? numberSection.toString() : "0") + 1;
String newName;
if (newNumberValue == 1) {
newName = nameWithoutExt + " " + newNumberValue + ext;

View file

@ -94,8 +94,8 @@ import btools.routingapp.IBRouterService;
import static net.osmand.plus.AppVersionUpgradeOnInit.LAST_APP_VERSION;
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.preferenceTimeOfDayToUpdate;
import static net.osmand.plus.liveupdates.LiveUpdatesHelper.preferenceUpdateFrequency;
import static net.osmand.plus.liveupdates.LiveUpdatesHelper.runLiveUpdate;
@ -716,7 +716,7 @@ public class AppInitializer implements IProgress {
AlarmManager alarmMgr = (AlarmManager) app.getSystemService(Context.ALARM_SERVICE);
for (LocalIndexInfo fm : fullMaps) {
String fileName = fm.getFileName();
if (!preferenceLiveUpdatesOn(fileName, settings).get()) {
if (!preferenceForLocalIndex(fileName, settings).get()) {
continue;
}
int updateFrequencyOrd = preferenceUpdateFrequency(fileName, settings).get();
@ -725,7 +725,7 @@ public class AppInitializer implements IProgress {
long lastCheck = preferenceLastCheck(fileName, settings).get();
if (System.currentTimeMillis() - lastCheck > updateFrequency.getTime() * 2) {
runLiveUpdate(app, fileName, false);
runLiveUpdate(app, fileName, false, null);
PendingIntent alarmIntent = getPendingIntent(app, fileName);
int timeOfDayOrd = preferenceTimeOfDayToUpdate(fileName, settings).get();
LiveUpdatesHelper.TimeOfDay timeOfDayToUpdate =

View file

@ -8,7 +8,7 @@ import net.osmand.plus.activities.LocalIndexHelper.LocalIndexType;
import java.io.File;
public class LocalIndexInfo {
public class LocalIndexInfo implements Comparable<LocalIndexInfo> {
private LocalIndexType type;
private String description = "";
@ -46,11 +46,11 @@ public class LocalIndexInfo {
public void setAttachedObject(Object attachedObject) {
this.attachedObject = attachedObject;
}
public Object getAttachedObject() {
return attachedObject;
}
private String formatName(String name) {
int ext = name.indexOf('.');
if (ext != -1) {
@ -172,8 +172,8 @@ public class LocalIndexInfo {
return type.getBasename(this);
}
@Override
public int compareTo(LocalIndexInfo o) {
return getFileName().compareTo(o.getFileName());
}
}

View file

@ -927,22 +927,6 @@ public class MapActivityActions implements DialogProvider {
}
}).createItem());
if (Version.isGooglePlayEnabled() || Version.isHuawei() || Version.isDeveloperVersion(app)) {
optionsMenuHelper.addItem(new ItemBuilder().setTitleId(R.string.osm_live, mapActivity)
.setId(DRAWER_OSMAND_LIVE_ID)
.setIcon(R.drawable.ic_action_osm_live)
.setListener(new ItemClickListener() {
@Override
public boolean onContextMenuClick(ArrayAdapter<ContextMenuItem> 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().setTitle(getString(R.string.shared_string_travel_guides) + " (Beta)")
.setId(DRAWER_TRAVEL_GUIDES_ID)
.setIcon(R.drawable.ic_action_travel)
@ -1046,6 +1030,22 @@ 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<ContextMenuItem> 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)

View file

@ -3,8 +3,12 @@ package net.osmand.plus.download.ui;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@ -13,17 +17,31 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import androidx.annotation.ColorRes;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.cardview.widget.CardView;
import androidx.core.content.ContextCompat;
import androidx.core.view.MenuItemCompat;
import net.osmand.AndroidUtils;
import net.osmand.Collator;
import net.osmand.OsmAndCollator;
import net.osmand.map.OsmandRegions;
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.LoadLiveMapsTask;
import net.osmand.plus.liveupdates.LoadLiveMapsTask.LocalIndexInfoAdapter;
import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.R;
import net.osmand.plus.base.OsmAndListFragment;
@ -35,20 +53,27 @@ import net.osmand.plus.download.IndexItem;
import net.osmand.plus.inapp.InAppPurchaseHelper;
import net.osmand.util.Algorithms;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class UpdatesIndexFragment extends OsmAndListFragment implements DownloadEvents {
import static net.osmand.plus.liveupdates.LiveUpdatesFragmentNew.showUpdateDialog;
import static net.osmand.plus.liveupdates.LiveUpdatesFragmentNew.updateCountEnabled;
public class UpdatesIndexFragment extends OsmAndListFragment implements DownloadEvents, RefreshLiveUpdates {
private static final int RELOAD_ID = 5;
private UpdateIndexAdapter listAdapter;
private String errorMessage;
private OsmandSettings settings;
private LoadLiveMapsTask loadLiveMapsTask;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
settings = getMyApplication().getSettings();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@ -62,29 +87,31 @@ public class UpdatesIndexFragment extends OsmAndListFragment implements Download
super.onActivityCreated(savedInstanceState);
updateErrorMessage();
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
invalidateListView(activity);
startLoadLiveMapsAsyncTask(getMyApplication());
}
@Override
public ArrayAdapter<?> getAdapter() {
return listAdapter;
}
@Override
public void downloadHasFinished() {
invalidateListView(getMyActivity());
updateUpdateAllButton();
startLoadLiveMapsAsyncTask(getMyApplication());
}
@Override
public void downloadInProgress() {
listAdapter.notifyDataSetChanged();
}
@Override
public void newDownloadIndexes() {
invalidateListView(getMyActivity());
@ -92,25 +119,24 @@ public class UpdatesIndexFragment extends OsmAndListFragment implements Download
}
public void invalidateListView(Activity a) {
DownloadResources indexes = getMyApplication().getDownloadThread().getIndexes();
final OsmandApplication app = getMyApplication();
OsmandSettings settings = app.getSettings();
DownloadResources indexes = app.getDownloadThread().getIndexes();
List<IndexItem> indexItems = indexes.getItemsToUpdate();
final OsmandRegions osmandRegions =
getMyApplication().getResourceManager().getOsmandRegions();
OsmandSettings settings = getMyApplication().getSettings();
final OsmandRegions osmandRegions = app.getResourceManager().getOsmandRegions();
listAdapter = new UpdateIndexAdapter(a, R.layout.download_index_list_item, indexItems,
!InAppPurchaseHelper.isSubscribedToLiveUpdates(getMyApplication()) || settings.SHOULD_SHOW_FREE_VERSION_BANNER.get());
!InAppPurchaseHelper.isSubscribedToLiveUpdates(app) || settings.SHOULD_SHOW_FREE_VERSION_BANNER.get());
final Collator collator = OsmAndCollator.primaryCollator();
listAdapter.sort(new Comparator<IndexItem>() {
@Override
public int compare(IndexItem indexItem, IndexItem indexItem2) {
return collator.compare(indexItem.getVisibleName(getMyApplication(), osmandRegions),
indexItem2.getVisibleName(getMyApplication(), osmandRegions));
return collator.compare(indexItem.getVisibleName(app, osmandRegions),
indexItem2.getVisibleName(app, osmandRegions));
}
});
setListAdapter(listAdapter);
updateErrorMessage();
}
private void updateErrorMessage() {
@ -130,7 +156,7 @@ public class UpdatesIndexFragment extends OsmAndListFragment implements Download
}
private void updateUpdateAllButton() {
View view = getView();
if (view == null) {
return;
@ -153,26 +179,27 @@ public class UpdatesIndexFragment extends OsmAndListFragment implements Download
for (IndexItem indexItem : indexItems) {
downloadsSize += indexItem.getSize();
}
String updateAllText = getActivity().getString(
String updateAllText = getString(
R.string.update_all, String.valueOf(downloadsSize >> 20));
updateAllButton.setText(updateAllText);
updateAllButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final DownloadActivity activity = getMyActivity();
if (indexItems.size() > 3) {
AlertDialog.Builder dialog = new AlertDialog.Builder(getMyActivity());
AlertDialog.Builder dialog = new AlertDialog.Builder(activity);
dialog.setTitle(R.string.update_all_maps);
dialog.setMessage(getString(R.string.update_all_maps_q, indexItems.size()));
dialog.setNegativeButton(R.string.shared_string_cancel, null);
dialog.setPositiveButton(R.string.shared_string_update, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
getMyActivity().startDownload(indexItems.toArray(new IndexItem[0]));
activity.startDownload(indexItems.toArray(new IndexItem[0]));
}
});
dialog.create().show();
} else {
getMyActivity().startDownload(indexItems.toArray(new IndexItem[0]));
activity.startDownload(indexItems.toArray(new IndexItem[0]));
}
}
});
@ -185,12 +212,31 @@ public class UpdatesIndexFragment extends OsmAndListFragment implements Download
updateUpdateAllButton();
}
@Override
public void onPause() {
super.onPause();
stopLoadLiveMapsAsyncTask();
}
private void startLoadLiveMapsAsyncTask(OsmandApplication app) {
loadLiveMapsTask = new LoadLiveMapsTask(listAdapter, app);
loadLiveMapsTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private void stopLoadLiveMapsAsyncTask() {
if (loadLiveMapsTask != null && loadLiveMapsTask.getStatus() == AsyncTask.Status.RUNNING) {
loadLiveMapsTask.cancel(false);
}
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
if (listAdapter.isShowOsmLiveBanner() && position == 0) {
if (position == 0) {
DownloadActivity activity = getMyActivity();
if (activity != null) {
ChoosePlanDialogFragment.showOsmLiveInstance(activity.getSupportFragmentManager());
if (!listAdapter.isShowOsmLivePurchaseBanner()) {
LiveUpdatesFragmentNew.showInstance(activity.getSupportFragmentManager(), this);
}
}
} else {
final IndexItem e = (IndexItem) getListAdapter().getItem(position);
@ -220,10 +266,6 @@ public class UpdatesIndexFragment extends OsmAndListFragment implements Download
}
}
public OsmandApplication getMyApplication() {
return getMyActivity().getMyApplication();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == RELOAD_ID) {
@ -234,75 +276,147 @@ public class UpdatesIndexFragment extends OsmAndListFragment implements Download
return super.onOptionsItemSelected(item);
}
private class UpdateIndexAdapter extends ArrayAdapter<IndexItem> {
@Override
public void onUpdateStates(Context context) {
if (context instanceof OsmandApplication) {
startLoadLiveMapsAsyncTask((OsmandApplication) context);
}
}
private class UpdateIndexAdapter extends ArrayAdapter<IndexItem> implements LocalIndexInfoAdapter {
static final int INDEX_ITEM = 0;
static final int OSM_LIVE_BANNER = 1;
List<IndexItem> items;
boolean showOsmLiveBanner;
private final ArrayList<LocalIndexInfo> mapsList = new ArrayList<>();
private final boolean showOsmLivePurchaseBanner;
private TextView countView;
private int countEnabled = 0;
public UpdateIndexAdapter(Context context, int resource, List<IndexItem> items, boolean showOsmLiveBanner) {
super(context, resource, items);
this.items = items;
this.showOsmLiveBanner = showOsmLiveBanner;
@Override
public void addData(LocalIndexInfo info) {
mapsList.add(info);
}
public boolean isShowOsmLiveBanner() {
return showOsmLiveBanner;
@Override
public void clearData() {
mapsList.clear();
}
@Override
public void onDataUpdated() {
countEnabled = updateCountEnabled(countView, mapsList, settings);
}
public UpdateIndexAdapter(Context context, int resource, List<IndexItem> items, boolean showOsmLivePurchaseBanner) {
super(context, resource, items);
this.items = items;
this.showOsmLivePurchaseBanner = showOsmLivePurchaseBanner;
}
public boolean isShowOsmLivePurchaseBanner() {
return showOsmLivePurchaseBanner;
}
@Override
public int getCount() {
return super.getCount() + (showOsmLiveBanner ? 1 : 0);
return super.getCount() + 1;
}
@Override
public IndexItem getItem(int position) {
if (showOsmLiveBanner && position == 0) {
if (position == 0) {
return null;
} else {
return super.getItem(position - (showOsmLiveBanner ? 1 : 0));
return super.getItem(position - 1);
}
}
@Override
public int getPosition(IndexItem item) {
return super.getPosition(item) + (showOsmLiveBanner ? 1 : 0);
return super.getPosition(item) + 1;
}
@Override
public int getViewTypeCount() {
return showOsmLiveBanner ? 2 : 1;
return 2;
}
@Override
public int getItemViewType(int position) {
return showOsmLiveBanner && position == 0 ? OSM_LIVE_BANNER : INDEX_ITEM;
return position == 0 ? OSM_LIVE_BANNER : INDEX_ITEM;
}
@Override
public View getView(final int position, final View convertView, final ViewGroup parent) {
View v = convertView;
View view = convertView;
int viewType = getItemViewType(position);
if (v == null) {
if (view == null) {
LayoutInflater inflater = LayoutInflater.from(getMyActivity());
if (viewType == INDEX_ITEM) {
LayoutInflater inflater = LayoutInflater.from(getMyActivity());
v = inflater.inflate(R.layout.two_line_with_images_list_item, parent, false);
v.setTag(new ItemViewHolder(v, getMyActivity()));
view = inflater.inflate(R.layout.two_line_with_images_list_item, parent, false);
view.setTag(new ItemViewHolder(view, getMyActivity()));
} else if (viewType == OSM_LIVE_BANNER) {
LayoutInflater inflater = LayoutInflater.from(getMyActivity());
v = inflater.inflate(R.layout.osm_live_banner_list_item, parent, false);
OsmandApplication app = getMyApplication();
boolean nightMode = !app.getSettings().isLightContent();
if (showOsmLivePurchaseBanner) {
view = inflater.inflate(R.layout.osm_live_banner_list_item, parent, false);
ColorStateList stateList = AndroidUtils.createPressedColorStateList(app, nightMode,
R.color.switch_button_active_light, R.color.switch_button_active_stroke_light,
R.color.switch_button_active_dark, R.color.switch_button_active_stroke_dark);
CardView cardView = ((CardView) view.findViewById(R.id.card_view));
cardView.setCardBackgroundColor(stateList);
cardView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
ChoosePlanDialogFragment.showOsmLiveInstance(getMyActivity().getSupportFragmentManager());
}
});
} else {
view = inflater.inflate(R.layout.bottom_sheet_item_with_descr_switch_and_additional_button_56dp, parent, false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
view.setBackground(null);
}
AndroidUiHelper.setVisibility(View.GONE, view.findViewById(R.id.compound_button));
((ImageView) view.findViewById(R.id.icon)).setImageResource(R.drawable.ic_action_subscription_osmand_live);
TextView tvTitle = view.findViewById(R.id.title);
tvTitle.setText(R.string.download_live_updates);
AndroidUtils.setTextPrimaryColor(app, tvTitle, nightMode);
countView = view.findViewById(R.id.description);
AndroidUtils.setTextSecondaryColor(app, countView, nightMode);
Drawable additionalIconDrawable = AppCompatResources.getDrawable(app, R.drawable.ic_action_update);
UiUtilities.tintDrawable(additionalIconDrawable, ContextCompat.getColor(app, getDefaultIconColorId(nightMode)));
((ImageView) view.findViewById(R.id.additional_button_icon)).setImageDrawable(additionalIconDrawable);
LinearLayout additionalButton = view.findViewById(R.id.additional_button);
TypedValue typedValue = new TypedValue();
app.getTheme().resolveAttribute(android.R.attr.selectableItemBackground, typedValue, true);
additionalButton.setBackgroundResource(typedValue.resourceId);
additionalButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (!listAdapter.isShowOsmLivePurchaseBanner()) {
showUpdateDialog(getActivity(), getMyApplication().getSettings(),
listAdapter.mapsList, listAdapter.countEnabled, null);
}
}
});
}
}
}
if (viewType == INDEX_ITEM) {
ItemViewHolder holder = (ItemViewHolder) v.getTag();
ItemViewHolder holder = (ItemViewHolder) view.getTag();
holder.setShowRemoteDate(true);
holder.setShowTypeInDesc(true);
holder.setShowParentRegionName(true);
holder.bindIndexItem(getItem(position));
}
return v;
return view;
}
}
@ColorRes
public static int getDefaultIconColorId(boolean nightMode) {
return nightMode ? R.color.icon_color_default_dark : R.color.icon_color_default_light;
}
}

View file

@ -32,7 +32,7 @@ public class LiveUpdatesAlarmReceiver extends BroadcastReceiver {
final OsmandSettings settings = application.getSettings();
if (!preferenceDownloadViaWiFi(localIndexInfoFile, settings).get() || wifi.isWifiEnabled()) {
new PerformLiveUpdateAsyncTask(context, localIndexInfoFile, false).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, fileName);
new PerformLiveUpdateAsyncTask(context, localIndexInfoFile, false, null).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, fileName);
} else {
PerformLiveUpdateAsyncTask.tryRescheduleDownload(context, settings, localIndexInfoFile);
}

View file

@ -0,0 +1,128 @@
package net.osmand.plus.liveupdates;
import android.content.Context;
import android.graphics.Typeface;
import android.os.Bundle;
import android.text.SpannableString;
import android.text.style.StyleSpan;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import net.osmand.PlatformUtil;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.UiUtilities;
import net.osmand.plus.UiUtilities.DialogButtonType;
import net.osmand.plus.base.MenuBottomSheetDialogFragment;
import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem;
import net.osmand.plus.base.bottomsheetmenu.simpleitems.DividerSpaceItem;
import net.osmand.plus.base.bottomsheetmenu.simpleitems.LongDescriptionItem;
import net.osmand.plus.resources.IncrementalChangesManager;
import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.util.Algorithms;
import org.apache.commons.logging.Log;
import static net.osmand.AndroidUtils.getPrimaryTextColorId;
import static net.osmand.AndroidUtils.getSecondaryTextColorId;
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 static final String TAG = LiveUpdatesClearDialogFragment.class.getSimpleName();
private static final Log LOG = PlatformUtil.getLog(LiveUpdatesClearDialogFragment.class);
private static final String LOCAL_INDEX_FILE_NAME = "local_index_file_name";
private OsmandApplication app;
private OsmandSettings settings;
private String fileName;
public static void showInstance(@NonNull FragmentManager fragmentManager, Fragment target, String fileName) {
if (!fragmentManager.isStateSaved()) {
LiveUpdatesClearDialogFragment fragment = new LiveUpdatesClearDialogFragment();
fragment.setTargetFragment(target, 0);
fragment.fileName = fileName;
fragment.show(fragmentManager, TAG);
}
}
@Override
public void createMenuItems(Bundle savedInstanceState) {
app = getMyApplication();
settings = app.getSettings();
if (savedInstanceState != null && savedInstanceState.containsKey(LOCAL_INDEX_FILE_NAME)) {
fileName = savedInstanceState.getString(LOCAL_INDEX_FILE_NAME);
}
items.add(new SimpleBottomSheetItem.Builder()
.setTitle(getString(R.string.delete_updates))
.setTitleColorId(getPrimaryTextColorId(nightMode))
.setLayoutId(R.layout.bottom_sheet_item_title)
.create());
String nameToDisplay = getNameToDisplay(fileName, app);
String text = getString(R.string.live_update_delete_updates_msg, nameToDisplay);
SpannableString message = UiUtilities.createSpannableString(text, new StyleSpan(Typeface.BOLD), nameToDisplay);
items.add(new LongDescriptionItem.Builder()
.setDescription(message)
.setDescriptionColorId(getSecondaryTextColorId(nightMode))
.setDescriptionMaxLines(5)
.setLayoutId(R.layout.bottom_sheet_item_description_long)
.create());
items.add(new DividerSpaceItem(app, getResources().getDimensionPixelSize(R.dimen.content_padding_small)));
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(LOCAL_INDEX_FILE_NAME, fileName);
}
private void deleteUpdates() {
IncrementalChangesManager changesManager = app.getResourceManager().getChangesManager();
String fileNameWithoutExt = Algorithms.getFileNameWithoutExtension(fileName);
changesManager.deleteUpdates(fileNameWithoutExt);
preferenceLastCheck(fileName, settings).resetToDefault();
preferenceLatestUpdateAvailable(fileName, settings).resetToDefault();
}
@Override
protected void onRightBottomButtonClick() {
deleteUpdates();
Fragment fragment = getTargetFragment();
if (fragment instanceof RefreshLiveUpdates) {
((RefreshLiveUpdates) fragment).onUpdateStates(app);
}
dismiss();
}
public interface RefreshLiveUpdates {
void onUpdateStates(Context context);
}
@Override
protected int getDismissButtonTextId() {
return R.string.shared_string_cancel;
}
@Override
protected int getRightBottomButtonTextId() {
return R.string.shared_string_delete;
}
@Override
protected DialogButtonType getRightBottomButtonType() {
return DialogButtonType.SECONDARY_HARMFUL;
}
}

View file

@ -4,7 +4,6 @@ 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.content.res.Resources;
import android.graphics.drawable.Drawable;
@ -26,8 +25,6 @@ import android.widget.TextView;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AlertDialog.Builder;
import androidx.appcompat.widget.SwitchCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentActivity;
@ -61,6 +58,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
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;
@ -71,7 +69,6 @@ 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 LiveUpdatesFragment extends BaseOsmAndFragment implements InAppPurchaseListener {
@ -131,11 +128,11 @@ public class LiveUpdatesFragment extends BaseOsmAndFragment implements InAppPurc
swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
adapter.showUpdateDialog();
showUpdateDialog(getActivity(), getSettings(), adapter.dataShouldUpdate, adapter.dataShouldUpdate.size(), null);
swipeRefresh.setRefreshing(false);
}
});
View bottomShadowView = inflater.inflate(R.layout.card_bottom_divider, listView, false);
if (!showSettingsOnly) {
listView.addFooterView(bottomShadowView);
@ -423,7 +420,7 @@ public class LiveUpdatesFragment extends BaseOsmAndFragment implements InAppPurc
}
}
});
} else {
topShadowView.setVisibility(View.VISIBLE);
@ -442,32 +439,9 @@ public class LiveUpdatesFragment extends BaseOsmAndFragment implements InAppPurc
private void switchOnLiveUpdates(final OsmandSettings settings) {
settings.IS_LIVE_UPDATES_ON.set(true);
enableLiveUpdates(true);
showUpdateDialog();
showUpdateDialog(getActivity(), getSettings(), adapter.dataShouldUpdate, adapter.dataShouldUpdate.size(), null);
}
private void showUpdateDialog() {
if(dataShouldUpdate.size() > 0) {
if (dataShouldUpdate.size() == 1) {
runLiveUpdate(app, dataShouldUpdate.get(0).getFileName(), false);
} else {
Builder bld = new AlertDialog.Builder(ctx);
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 : dataShouldUpdate) {
runLiveUpdate(app, li.getFileName(), false);
}
notifyDataSetChanged();
}
});
bld.setNegativeButton(R.string.shared_string_no, null);
bld.show();
}
}
}
private void enableLiveUpdates(boolean enable) {
AlarmManager alarmMgr = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
for (LocalIndexInfo li : dataShouldUpdate) {
@ -491,7 +465,7 @@ public class LiveUpdatesFragment extends BaseOsmAndFragment implements InAppPurc
public int getChildrenCount(int groupPosition) {
if (showSettingsOnly) {
return 0;
}else if (groupPosition == SHOULD_UPDATE_GROUP_POSITION) {
} else if (groupPosition == SHOULD_UPDATE_GROUP_POSITION) {
return dataShouldUpdate.size();
} else if (groupPosition == SHOULD_NOT_UPDATE_GROUP_POSITION) {
return dataShouldNotUpdate.size();
@ -575,7 +549,7 @@ public class LiveUpdatesFragment extends BaseOsmAndFragment implements InAppPurc
preferenceLiveUpdatesOn(item, fragment.getSettings());
IncrementalChangesManager changesManager = context.getResourceManager().getChangesManager();
nameTextView.setText(getNameToDisplay(item, fragment.getMyActivity()));
nameTextView.setText(getNameToDisplay(item, context));
if (shouldUpdatePreference.get()) {
final Integer frequencyId = preferenceUpdateFrequency(item, fragment.getSettings()).get();
final Integer timeOfDateToUpdateId = preferenceTimeOfDayToUpdate(item, fragment.getSettings()).get();
@ -602,11 +576,11 @@ public class LiveUpdatesFragment extends BaseOsmAndFragment implements InAppPurc
final long timestamp = changesManager.getTimestamp(fileNameWithoutExtension);
final long lastCheck = preferenceLastCheck(item, fragment.getSettings()).get();
CommonPreference<Boolean> liveUpdateOn = preferenceLiveUpdatesOn(item, fragment.getSettings());
if(liveUpdateOn.get() && lastCheck != DEFAULT_LAST_CHECK) {
String lastCheckString = formatDateTime(fragment.getActivity(), lastCheck );
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 );
String lastCheckString = formatDateTime(fragment.getActivity(), timestamp);
descriptionTextView.setText(context.getString(R.string.last_map_change, lastCheckString));
}

View file

@ -0,0 +1,710 @@
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.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.TripRecordingActiveBottomSheet.getActiveTextColorId;
import static net.osmand.plus.monitoring.TripRecordingActiveBottomSheet.getOsmandIconColorId;
import static net.osmand.plus.monitoring.TripRecordingActiveBottomSheet.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<LocalIndexInfo> 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<Boolean> 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<Boolean> localUpdateOn = preferenceForLocalIndex(li.getFileName(), settings);
if (localUpdateOn.get()) {
String fileName = li.getFileName();
PendingIntent alarmIntent = getPendingIntent(app, fileName);
if (enable) {
final CommonPreference<Integer> updateFrequencyPreference =
preferenceUpdateFrequency(fileName, settings);
final CommonPreference<Integer> 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 void notifyLiveUpdatesChanged() {
if (getActivity() != null) {
runSort();
}
}
public static int updateCountEnabled(TextView countView, ArrayList<LocalIndexInfo> mapsList, OsmandSettings settings) {
int countEnabled = 0;
if (countView != null) {
for (LocalIndexInfo map : mapsList) {
CommonPreference<Boolean> 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<LocalIndexInfo> 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<LocalIndexInfo>() {
@Override
public int compare(LocalIndexInfo o1, LocalIndexInfo o2) {
CommonPreference<Boolean> preference1 = preferenceForLocalIndex(o1.getFileName(), getSettings());
CommonPreference<Boolean> 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<Boolean> 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 = ContextCompat.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<Void, Void, String> {
private final OsmandApplication app;
private final WeakReference<LiveUpdatesFragmentNew> 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<Boolean> 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;
}
}

View file

@ -5,18 +5,22 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.text.format.DateUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import net.osmand.plus.OsmandApplication;
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.R;
import net.osmand.plus.activities.OsmandActionBarActivity;
import net.osmand.plus.helpers.FileNameTranslationHelper;
import net.osmand.util.Algorithms;
import java.io.File;
import java.util.Calendar;
import java.util.concurrent.TimeUnit;
public class LiveUpdatesHelper {
private static final String UPDATE_TIMES_POSTFIX = "_update_times";
@ -25,6 +29,7 @@ public class LiveUpdatesHelper {
private static final String LIVE_UPDATES_ON_POSTFIX = "_live_updates_on";
private static final String LAST_UPDATE_ATTEMPT_ON_POSTFIX = "_last_update_attempt";
public static final String LOCAL_INDEX_INFO = "local_index_info";
public static final String LIVE_UPDATES_LAST_AVAILABLE = "live_updates_last_available";
private static final int MORNING_UPDATE_TIME = 8;
@ -45,6 +50,7 @@ public class LiveUpdatesHelper {
}
return p;
}
public static CommonPreference<Boolean> preferenceForLocalIndex(
String fileName, OsmandSettings settings) {
final String settingId = fileName + LIVE_UPDATES_ON_POSTFIX;
@ -81,9 +87,19 @@ public class LiveUpdatesHelper {
return checkPref(settings.registerLongPreference(settingId, DEFAULT_LAST_CHECK));
}
public static String getNameToDisplay(String fileName, OsmandActionBarActivity activity) {
return FileNameTranslationHelper.getFileName(activity,
activity.getMyApplication().getResourceManager().getOsmandRegions(),
public static CommonPreference<Long> preferenceLatestUpdateAvailable(
String fileName, OsmandSettings settings) {
final String settingId = fileName + LIVE_UPDATES_LAST_AVAILABLE;
return checkPref(settings.registerLongPreference(settingId, DEFAULT_LAST_CHECK));
}
public static CommonPreference<Long> preferenceLatestUpdateAvailable(OsmandSettings settings) {
return checkPref(settings.registerLongPreference(LIVE_UPDATES_LAST_AVAILABLE, DEFAULT_LAST_CHECK));
}
public static String getNameToDisplay(String fileName, OsmandApplication context) {
return FileNameTranslationHelper.getFileName(context,
context.getResourceManager().getOsmandRegions(),
fileName);
}
@ -93,6 +109,70 @@ public class LiveUpdatesHelper {
return dateFormat.format(dateTime) + " " + timeFormat.format(dateTime);
}
public static boolean isCurrentYear(long dateTime) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(dateTime);
return calendar.get(Calendar.YEAR) == Calendar.getInstance().get(Calendar.YEAR);
}
public static String formatShortDateTime(Context ctx, long dateTime) {
if (dateTime == DEFAULT_LAST_CHECK) {
return ctx.getResources().getString(R.string.shared_string_never);
} else {
String date, times;
if (DateUtils.isToday(dateTime)) {
date = ctx.getResources().getString(R.string.today);
} else {
int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH;
if (isCurrentYear(dateTime)) {
flags = flags | DateUtils.FORMAT_NO_YEAR;
}
date = DateUtils.formatDateTime(ctx, dateTime, flags);
}
times = DateUtils.formatDateTime(ctx, dateTime, DateUtils.FORMAT_SHOW_TIME);
String pattern = ctx.getString(R.string.ltr_or_rtl_combine_via_dash);
return String.format(pattern, date, times);
}
}
public static String formatHelpDateTime(Context ctx, UpdateFrequency updateFrequency, TimeOfDay timeOfDay, long lastDateTime) {
if (lastDateTime == DEFAULT_LAST_CHECK) {
lastDateTime = System.currentTimeMillis();
}
switch (updateFrequency) {
case DAILY: {
return helpDateTimeBuilder(ctx, R.string.live_update_frequency_day_variant, lastDateTime, 1, TimeUnit.DAYS, timeOfDay);
}
case WEEKLY: {
return helpDateTimeBuilder(ctx, R.string.live_update_frequency_week_variant, lastDateTime, 7, TimeUnit.DAYS, timeOfDay);
}
default:
case HOURLY: {
return helpDateTimeBuilder(ctx, R.string.live_update_frequency_hour_variant, lastDateTime, 1, TimeUnit.HOURS, timeOfDay);
}
}
}
private static String helpDateTimeBuilder(Context ctx, int stringResId, long lastDateTime, long sourceDuration, TimeUnit sourceUnit, TimeOfDay timeOfDay) {
long nextDateTime = lastDateTime + TimeUnit.MILLISECONDS.convert(sourceDuration, sourceUnit);
if (sourceUnit != TimeUnit.HOURS) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(nextDateTime);
calendar.set(Calendar.HOUR_OF_DAY, timeOfDay == TimeOfDay.MORNING ? MORNING_UPDATE_TIME : NIGHT_UPDATE_TIME);
nextDateTime = calendar.getTimeInMillis();
}
int flagsBase = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH;
int flagsBaseNoYear = flagsBase | DateUtils.FORMAT_NO_YEAR;
int flagsTime = DateUtils.FORMAT_SHOW_TIME;
String date = DateUtils.formatDateTime(ctx, nextDateTime, isCurrentYear(nextDateTime) ? flagsBaseNoYear : flagsBase);
String time = DateUtils.formatDateTime(ctx, nextDateTime, flagsTime);
return ctx.getResources().getString(stringResId, DateUtils.isToday(nextDateTime) ? "" : " " + date, time);
}
public static PendingIntent getPendingIntent(@NonNull Context context,
@NonNull String fileName) {
Intent intent = new Intent(context, LiveUpdatesAlarmReceiver.class);
@ -168,13 +248,14 @@ public class LiveUpdatesHelper {
public int getLocalizedId() {
return localizedId;
}
public long getTime() {
return time;
}
}
public static void runLiveUpdate(Context context, final String fileName, boolean userRequested) {
public static void runLiveUpdate(Context context, final String fileName, boolean userRequested, @Nullable final LiveUpdateListener listener) {
final String fnExt = Algorithms.getFileNameWithoutExtension(new File(fileName));
new PerformLiveUpdateAsyncTask(context, fileName, userRequested).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, fnExt);
new PerformLiveUpdateAsyncTask(context, fileName, userRequested, listener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, fnExt);
}
}

View file

@ -54,10 +54,10 @@ public class LiveUpdatesSettingsDialogFragment extends DialogFragment {
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) {
@ -77,7 +77,7 @@ public class LiveUpdatesSettingsDialogFragment extends DialogFragment {
final View updateTimesOfDayLayout = view.findViewById(R.id.updateTimesOfDayLayout);
sizeTextView = (TextView) view.findViewById(R.id.sizeTextView);
regionNameTextView.setText(getNameToDisplay(fileName, getMyActivity()));
regionNameTextView.setText(getNameToDisplay(fileName, getMyActivity().getMyApplication()));
fileNameWithoutExtension = Algorithms.getFileNameWithoutExtension(new File(fileName));
final IncrementalChangesManager changesManager = getMyApplication().getResourceManager().getChangesManager();
final long timestamp = changesManager.getTimestamp(fileNameWithoutExtension);
@ -167,7 +167,7 @@ public class LiveUpdatesSettingsDialogFragment extends DialogFragment {
timeOfDayPreference.set(timeOfDayInt);
if (liveUpdatesSwitch.isChecked() && getSettings().IS_LIVE_UPDATES_ON.get()) {
runLiveUpdate(getActivity(), fileName, false);
runLiveUpdate(getActivity(), fileName, false, null);
UpdateFrequency updateFrequency = UpdateFrequency.values()[updateFrequencyInt];
TimeOfDay timeOfDayToUpdate = TimeOfDay.values()[timeOfDayInt];
setAlarmForPendingIntent(alarmIntent, alarmMgr, updateFrequency, timeOfDayToUpdate);
@ -194,7 +194,7 @@ public class LiveUpdatesSettingsDialogFragment extends DialogFragment {
if (!getSettings().isInternetConnectionAvailable()) {
getMyApplication().showShortToastMessage(R.string.no_internet_connection);
} else {
runLiveUpdate(getActivity(), fileName, true);
runLiveUpdate(getActivity(), fileName, true, null);
final IncrementalChangesManager changesManager = getMyApplication().getResourceManager().getChangesManager();
sizeTextView.setText(getUpdatesSize(getMyActivity(), fileNameWithoutExtension, changesManager));
dialog.dismiss();

View file

@ -0,0 +1,548 @@
package net.osmand.plus.liveupdates;
import android.content.Context;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.SpannableString;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.ColorRes;
import androidx.annotation.DimenRes;
import androidx.annotation.NonNull;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.appcompat.widget.AppCompatImageView;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
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.base.MenuBottomSheetDialogFragment;
import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem;
import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithCompoundButton;
import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithDescription;
import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem;
import net.osmand.plus.base.bottomsheetmenu.simpleitems.DividerItem;
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.LiveUpdatesHelper.TimeOfDay;
import net.osmand.plus.liveupdates.LiveUpdatesHelper.UpdateFrequency;
import net.osmand.plus.resources.IncrementalChangesManager;
import net.osmand.plus.settings.backend.CommonPreference;
import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.widgets.MultiStateToggleButton;
import net.osmand.plus.widgets.MultiStateToggleButton.OnRadioItemClickListener;
import net.osmand.plus.widgets.MultiStateToggleButton.RadioItem;
import net.osmand.plus.widgets.TextViewEx;
import net.osmand.plus.widgets.style.CustomTypefaceSpan;
import net.osmand.util.Algorithms;
import org.apache.commons.logging.Log;
import java.util.Arrays;
import static net.osmand.AndroidUtils.getPrimaryTextColorId;
import static net.osmand.AndroidUtils.getSecondaryTextColorId;
import static net.osmand.plus.UiUtilities.CompoundButtonType.TOOLBAR;
import static net.osmand.plus.liveupdates.LiveUpdatesHelper.formatHelpDateTime;
import static net.osmand.plus.liveupdates.LiveUpdatesHelper.formatShortDateTime;
import static net.osmand.plus.liveupdates.LiveUpdatesHelper.getNameToDisplay;
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.preferenceLatestUpdateAvailable;
import static net.osmand.plus.liveupdates.LiveUpdatesHelper.preferenceTimeOfDayToUpdate;
import static net.osmand.plus.liveupdates.LiveUpdatesHelper.preferenceUpdateFrequency;
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 static final String TAG = LiveUpdatesSettingsDialogFragmentNew.class.getSimpleName();
private static final Log LOG = PlatformUtil.getLog(LiveUpdatesSettingsDialogFragmentNew.class);
private static final String LOCAL_INDEX_FILE_NAME = "local_index_file_name";
private OsmandApplication app;
private OsmandSettings settings;
private OnLiveUpdatesForLocalChange onLiveUpdatesForLocalChange;
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;
public static void showInstance(@NonNull FragmentManager fragmentManager, Fragment target, String fileName) {
if (!fragmentManager.isStateSaved()) {
LiveUpdatesSettingsDialogFragmentNew fragment = new LiveUpdatesSettingsDialogFragmentNew();
fragment.setTargetFragment(target, 0);
fragment.fileName = fileName;
fragment.show(fragmentManager, TAG);
}
}
@Override
public void createMenuItems(Bundle savedInstanceState) {
app = getMyApplication();
settings = app.getSettings();
LayoutInflater inflater = UiUtilities.getInflater(app, nightMode);
if (getTargetFragment() instanceof OnLiveUpdatesForLocalChange) {
onLiveUpdatesForLocalChange = (OnLiveUpdatesForLocalChange) getTargetFragment();
}
if (savedInstanceState != null && savedInstanceState.containsKey(LOCAL_INDEX_FILE_NAME)) {
fileName = savedInstanceState.getString(LOCAL_INDEX_FILE_NAME);
}
CommonPreference<Boolean> localUpdatePreference = preferenceForLocalIndex(fileName, settings);
CommonPreference<Boolean> downloadViaWiFiPreference = preferenceDownloadViaWiFi(fileName, settings);
CommonPreference<Integer> frequencyPreference = preferenceUpdateFrequency(fileName, settings);
CommonPreference<Integer> timeOfDayPreference = preferenceTimeOfDayToUpdate(fileName, settings);
int dp12 = getDimen(R.dimen.content_padding_small);
int dp16 = getDimen(R.dimen.content_padding);
int dp40 = getDimen(R.dimen.list_header_height);
int dp48 = getDimen(R.dimen.context_menu_buttons_bottom_height);
items.add(new SimpleBottomSheetItem.Builder()
.setTitle(getNameToDisplay(fileName, app))
.setTitleColorId(getPrimaryTextColorId(nightMode))
.setLayoutId(R.layout.bottom_sheet_item_title_big)
.create());
items.add(new ShortDescriptionItem.Builder()
.setDescription(getLastCheckString())
.setDescriptionColorId(getSecondaryTextColorId(nightMode))
.setDescriptionMaxLines(2)
.setLayoutId(R.layout.bottom_sheet_item_description)
.create());
indexLastCheckItem = items.size() - 1;
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()
.setChecked(localUpdatePreference.get())
.setTitle(getStateText(localUpdatePreference.get()))
.setTitleColorId(getActiveTabTextColorId(nightMode))
.setCustomView(itemLiveUpdate)
.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();
}
})) {
if (indexSwitchLiveUpdateItem != -1 && items.size() > 0) {
button.setTitle(getStateText(button.isChecked()));
updateCustomButtonView(app, null, button.getView(), button.isChecked(), nightMode);
}
CommonPreference<Boolean> localUpdatePreference = preferenceForLocalIndex(fileName, settings);
frequencyToggleButton.updateView(localUpdatePreference.get());
timeOfDayToggleButton.updateView(localUpdatePreference.get());
setStateViaWiFiButton(localUpdatePreference);
}
}
}
})
.create());
indexSwitchLiveUpdateItem = items.size() - 1;
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);
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);
AndroidUtils.setMargins(itemFrequencyParams, dp16, 0, dp16, dp12);
itemFrequencyButtons.setLayoutParams(itemFrequencyParams);
String hourly = getString(R.string.hourly);
String daily = getString(R.string.daily);
String weekly = getString(R.string.weekly);
RadioItem hourlyButton = new RadioItem(hourly);
RadioItem dailyButton = new RadioItem(daily);
RadioItem weeklyButton = new RadioItem(weekly);
frequencyToggleButton = new MultiStateToggleButton(app, itemFrequencyButtons, nightMode);
frequencyToggleButton.setItems(hourlyButton, dailyButton, weeklyButton);
setSelectedRadioItem(frequencyToggleButton, frequencyPreference.get(), hourlyButton, dailyButton, weeklyButton);
frequencyToggleButton.updateView(localUpdatePreference.get());
items.add(new BaseBottomSheetItem.Builder()
.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()
.setDescription(getFrequencyHelpMessage())
.setDescriptionColorId(getSecondaryTextColorId(nightMode))
.setLayoutId(R.layout.bottom_sheet_item_description)
.create());
indexFrequencyHelpMessageItem = items.size() - 1;
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);
itemUpdateNowButton.setLayoutParams(itemUpdateNowParams);
((AppCompatImageView) itemUpdateNowButton.findViewById(R.id.button_icon)).setImageDrawable(
ContextCompat.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)));
items.add(new BaseBottomSheetItem.Builder()
.setCustomView(itemUpdateNowButton)
.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!settings.isInternetConnectionAvailable()) {
app.showShortToastMessage(R.string.no_internet_connection);
} else {
if (onLiveUpdatesForLocalChange != null) {
onLiveUpdatesForLocalChange.forceUpdateLocal(fileName, true, new Runnable() {
@Override
public void run() {
updateLastCheck();
updateFrequencyHelpMessage();
updateFileSize();
}
});
}
}
}
})
.create());
items.add(createDividerItem());
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);
}
}
}
})
.create()
);
indexClearItem = items.size() - 1;
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;
items.add(new DividerSpaceItem(app, getDimen(R.dimen.context_menu_padding_margin_large)));
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, parent, savedInstanceState);
CommonPreference<Boolean> localUpdatePreference = preferenceForLocalIndex(fileName, settings);
setStateViaWiFiButton(localUpdatePreference);
return view;
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(LOCAL_INDEX_FILE_NAME, fileName);
}
private void setStateViaWiFiButton(CommonPreference<Boolean> 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 (localUpdatePreference.get()) {
AndroidUtils.setTextPrimaryColor(app, title, nightMode);
AndroidUtils.setTextSecondaryColor(app, description, nightMode);
compoundButton.setEnabled(true);
} else {
AndroidUtils.setTextSecondaryColor(app, title, nightMode);
description.setTextColor(ContextCompat.getColor(app, getTertiaryTextColorId(nightMode)));
compoundButton.setEnabled(false);
}
}
}
}
private void updateLastCheck() {
if (indexLastCheckItem != -1 && items.size() > 0) {
((BottomSheetItemWithDescription) items.get(indexLastCheckItem))
.setDescription(getLastCheckString());
}
}
private void updateFrequencyHelpMessage() {
if (indexFrequencyHelpMessageItem != -1 && items.size() > 0) {
((BottomSheetItemWithDescription) items.get(indexFrequencyHelpMessageItem))
.setDescription(getFrequencyHelpMessage());
}
}
private void updateFileSize() {
if (indexClearItem != -1 && items.size() > 0) {
((BottomSheetItemWithDescription) items.get(indexClearItem))
.setDescription(getUpdatesSizeStr());
}
}
protected SpannableString getLastCheckString() {
final long lastUpdate = preferenceLatestUpdateAvailable(fileName, settings).get();
String updatedTimeStr = getString(R.string.updated, formatShortDateTime(app, lastUpdate));
if (!updatedTimeStr.contains(getString(R.string.shared_string_never))) {
final long lastCheck = preferenceLastCheck(fileName, settings).get();
String lastCheckStr = getString(R.string.last_time_checked, formatShortDateTime(app, lastCheck));
updatedTimeStr = updatedTimeStr.concat("\n").concat(lastCheckStr);
SpannableString spanStr = new SpannableString(updatedTimeStr);
Typeface typeface = FontCache.getRobotoMedium(getContext());
int start = updatedTimeStr.indexOf("");
if (start != -1) {
int end = updatedTimeStr.indexOf(lastCheckStr) - 1;
spanStr.setSpan(new CustomTypefaceSpan(typeface), start, end, 0);
start = updatedTimeStr.lastIndexOf("");
if (start != -1) {
end = updatedTimeStr.length();
spanStr.setSpan(new CustomTypefaceSpan(typeface), start, end, 0);
}
}
return spanStr;
}
return new SpannableString(updatedTimeStr);
}
protected String getFrequencyHelpMessage() {
CommonPreference<Integer> updateFrequency = preferenceUpdateFrequency(fileName, settings);
CommonPreference<Integer> timeOfDayToUpdate = preferenceTimeOfDayToUpdate(fileName, settings);
final long lastUpdate = preferenceLatestUpdateAvailable(fileName, settings).get();
return formatHelpDateTime(app, UpdateFrequency.values()[updateFrequency.get()], TimeOfDay.values()[timeOfDayToUpdate.get()], lastUpdate);
}
private long getUpdatesSize() {
IncrementalChangesManager changesManager = app.getResourceManager().getChangesManager();
String fileNameWithoutExt = Algorithms.getFileNameWithoutExtension(fileName);
return changesManager.getUpdatesSize(fileNameWithoutExt);
}
private String getUpdatesSizeStr() {
long updatesSize = getUpdatesSize();
return updatesSize > 0
? AndroidUtils.formatSize(app, updatesSize)
: getString(R.string.ltr_or_rtl_combine_via_space, "0.0", "kB");
}
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);
dividerItem.setMargins(start, vertical, 0, vertical);
return dividerItem;
}
private void setSelectedRadioItem(MultiStateToggleButton toggleButton, int position, RadioItem... buttons) {
toggleButton.setSelectedItem(buttons[position]);
}
private void refreshTimeOfDayLayout(int position, View... timeOfDayLayouts) {
switch (UpdateFrequency.values()[position]) {
case HOURLY:
for (View timeOfDayLayout : timeOfDayLayouts) {
AndroidUiHelper.updateVisibility(timeOfDayLayout, false);
}
break;
case DAILY:
case WEEKLY:
for (View timeOfDayLayout : timeOfDayLayouts) {
AndroidUiHelper.updateVisibility(timeOfDayLayout, true);
}
break;
}
}
private OnRadioItemClickListener getFrequencyButtonListener(@NonNull final UpdateFrequency type, final View... timeOfDayLayouts) {
return new OnRadioItemClickListener() {
@Override
public boolean onRadioItemClick(RadioItem radioItem, View view) {
final CommonPreference<Integer> frequencyPreference = preferenceUpdateFrequency(fileName, settings);
setOnRadioItemClick(frequencyPreference, type.ordinal(), timeOfDayLayouts);
return true;
}
};
}
private OnRadioItemClickListener getTimeOfDayButtonListener(@NonNull final TimeOfDay type) {
return new OnRadioItemClickListener() {
@Override
public boolean onRadioItemClick(RadioItem radioItem, View view) {
final CommonPreference<Integer> timeOfDayPreference = preferenceTimeOfDayToUpdate(fileName, settings);
setOnRadioItemClick(timeOfDayPreference, type.ordinal());
return true;
}
};
}
private void setOnRadioItemClick(CommonPreference<Integer> preference, int newValue, View... timeOfDayLayouts) {
CommonPreference<Boolean> liveUpdatePreference = preferenceForLocalIndex(fileName, settings);
if (liveUpdatePreference.get()) {
preference.set(newValue);
if (!Algorithms.isEmpty(Arrays.asList(timeOfDayLayouts))) {
refreshTimeOfDayLayout(newValue, timeOfDayLayouts);
}
updateFrequencyHelpMessage();
OnLiveUpdatesForLocalChange confirmationInterface = (OnLiveUpdatesForLocalChange) getTargetFragment();
if (confirmationInterface != null) {
confirmationInterface.updateList();
}
}
}
@Override
public void onUpdateStates(Context context) {
final OnLiveUpdatesForLocalChange confirmationInterface = (OnLiveUpdatesForLocalChange) getTargetFragment();
if (confirmationInterface != null) {
confirmationInterface.updateList();
}
updateLastCheck();
updateFileSize();
}
public interface OnLiveUpdatesForLocalChange {
boolean onUpdateLocalIndex(String fileName, boolean newValue, Runnable callback);
void forceUpdateLocal(String fileName, boolean userRequested, Runnable callback);
void runSort();
void updateList();
}
public int getDimen(@DimenRes int id) {
return getResources().getDimensionPixelSize(id);
}
public String getStateText(boolean isEnabled) {
return getString(isEnabled ? R.string.shared_string_enabled : R.string.shared_string_disabled);
}
@ColorRes
public static int getActiveTabTextColorId(boolean nightMode) {
return nightMode ? R.color.text_color_tab_active_dark : R.color.text_color_tab_active_light;
}
@ColorRes
public static int getActivePrimaryColorId(boolean nightMode) {
return nightMode ? R.color.active_color_primary_dark : R.color.active_color_primary_light;
}
@ColorRes
public static int getTertiaryTextColorId(boolean nightMode) {
return nightMode ? R.color.text_color_tertiary_dark : R.color.text_color_tertiary_light;
}
@Override
protected int getDismissButtonTextId() {
return R.string.shared_string_close;
}
}

View file

@ -0,0 +1,62 @@
package net.osmand.plus.liveupdates;
import android.os.AsyncTask;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.activities.LocalIndexHelper;
import net.osmand.plus.activities.LocalIndexInfo;
import net.osmand.plus.download.ui.AbstractLoadLocalIndexTask;
public class LoadLiveMapsTask
extends AsyncTask<Void, LocalIndexInfo, Void>
implements AbstractLoadLocalIndexTask {
public interface LocalIndexInfoAdapter {
void addData(LocalIndexInfo localIndexInfo);
void clearData();
void onDataUpdated();
}
private final LocalIndexInfoAdapter adapter;
private final LocalIndexHelper helper;
public LoadLiveMapsTask(LocalIndexInfoAdapter adapter, OsmandApplication app) {
this.adapter = adapter;
helper = new LocalIndexHelper(app);
}
@Override
protected void onPreExecute() {
adapter.clearData();
}
@Override
protected Void doInBackground(Void... params) {
helper.getLocalFullMaps(this);
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.addData(localIndexInfo);
}
}
}
@Override
protected void onPostExecute(Void result) {
adapter.onDataUpdated();
}
}

View file

@ -2,12 +2,10 @@ package net.osmand.plus.liveupdates;
import android.content.Intent;
import android.content.res.Resources;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import androidx.appcompat.app.ActionBar;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
@ -16,33 +14,22 @@ import androidx.viewpager.widget.ViewPager;
import com.google.android.material.tabs.TabLayout;
import net.osmand.AndroidNetworkUtils;
import net.osmand.PlatformUtil;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.chooseplan.ChoosePlanDialogFragment;
import net.osmand.plus.chooseplan.ChoosePlanDialogFragment.ChoosePlanDialogListener;
import net.osmand.plus.download.AbstractDownloadActivity;
import net.osmand.plus.download.DownloadIndexesThread.DownloadEvents;
import net.osmand.plus.inapp.InAppPurchaseHelper;
import org.apache.commons.logging.Log;
import java.lang.ref.WeakReference;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
public class OsmLiveActivity extends AbstractDownloadActivity implements DownloadEvents, ChoosePlanDialogListener {
public class OsmLiveActivity extends AbstractDownloadActivity implements ChoosePlanDialogListener {
private final static Log LOG = PlatformUtil.getLog(OsmLiveActivity.class);
public final static String SHOW_SETTINGS_ONLY_INTENT_PARAM = "show_settings_only_intent_param";
private LiveUpdatesFragmentPagerAdapter pagerAdapter;
private boolean showSettingOnly;
private GetLastUpdateDateTask getLastUpdateDateTask;
private static final String URL = "https://osmand.net/api/osmlive_status";
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -67,9 +54,6 @@ public class OsmLiveActivity extends AbstractDownloadActivity implements Downloa
tabLayout.setupWithViewPager(viewPager);
if (showSettingOnly) {
tabLayout.setVisibility(View.GONE);
} else {
getLastUpdateDateTask = new GetLastUpdateDateTask(this);
getLastUpdateDateTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
boolean nightMode = !app.getSettings().isLightContent();
@ -99,20 +83,6 @@ public class OsmLiveActivity extends AbstractDownloadActivity implements Downloa
}
}
@Override
protected void onPause() {
super.onPause();
getMyApplication().getDownloadThread().resetUiActivity(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (getLastUpdateDateTask != null) {
getLastUpdateDateTask.cancel(true);
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
@ -132,64 +102,9 @@ public class OsmLiveActivity extends AbstractDownloadActivity implements Downloa
return true;
}
@Override
public void newDownloadIndexes() {
}
@Override
public void downloadInProgress() {
}
@Override
public void downloadHasFinished() {
((LiveUpdatesFragment) pagerAdapter.fragments[0]).notifyLiveUpdatesChanged();
}
private static class GetLastUpdateDateTask extends AsyncTask<Void, Void, String> {
private OsmandApplication app;
private WeakReference<OsmLiveActivity> activity;
GetLastUpdateDateTask(OsmLiveActivity activity) {
this.activity = new WeakReference<>(activity);
app = activity.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) {
OsmLiveActivity a = activity.get();
if (response != null && a != null) {
ActionBar actionBar = a.getSupportActionBar();
if (actionBar != 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 {
Date parsed = source.parse(response);
actionBar.setSubtitle(dest.format(parsed));
} catch (ParseException e) {
e.printStackTrace();
}
}
}
}
}
public static class LiveUpdatesFragmentPagerAdapter extends FragmentPagerAdapter {
private final Fragment[] fragments = new Fragment[] { new LiveUpdatesFragment(), new ReportsFragment() };
private static final int[] titleIds = new int[] { LiveUpdatesFragment.TITLE, ReportsFragment.TITLE };
private final Fragment[] fragments = new Fragment[] { new ReportsFragment() };
private static final int[] titleIds = new int[] { ReportsFragment.TITLE };
private final String[] titles;
private final boolean showSettingsOnly;

View file

@ -6,9 +6,13 @@ 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;
import net.osmand.PlatformUtil;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.download.DownloadActivity;
import net.osmand.plus.settings.backend.CommonPreference;
import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.R;
@ -17,12 +21,20 @@ import net.osmand.plus.download.DownloadActivityType;
import net.osmand.plus.download.DownloadIndexesThread;
import net.osmand.plus.download.IndexItem;
import net.osmand.plus.resources.IncrementalChangesManager;
import net.osmand.util.Algorithms;
import org.apache.commons.logging.Log;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import static net.osmand.plus.liveupdates.LiveUpdatesHelper.preferenceLastCheck;
import static net.osmand.plus.liveupdates.LiveUpdatesHelper.preferenceLatestUpdateAvailable;
import static net.osmand.plus.liveupdates.LiveUpdatesHelper.preferenceUpdateFrequency;
public class PerformLiveUpdateAsyncTask
@ -34,13 +46,20 @@ public class PerformLiveUpdateAsyncTask
@NonNull
private final String localIndexFileName;
private final boolean userRequested;
private final LiveUpdateListener listener;
public interface LiveUpdateListener {
void processFinish();
}
public PerformLiveUpdateAsyncTask(@NonNull Context context,
@NonNull String localIndexFileName,
boolean userRequested) {
boolean userRequested,
@Nullable LiveUpdateListener listener) {
this.context = context;
this.localIndexFileName = localIndexFileName;
this.userRequested = userRequested;
this.listener = listener;
}
@Override
@ -52,7 +71,7 @@ public class PerformLiveUpdateAsyncTask
}
final OsmandApplication myApplication = getMyApplication();
CommonPreference<Long> lastCheckPreference =
LiveUpdatesHelper.preferenceLastCheck(localIndexFileName, myApplication.getSettings());
preferenceLastCheck(localIndexFileName, myApplication.getSettings());
lastCheckPreference.set(System.currentTimeMillis());
}
@ -129,16 +148,24 @@ public class PerformLiveUpdateAsyncTask
if (context instanceof DownloadIndexesThread.DownloadEvents) {
((DownloadIndexesThread.DownloadEvents) context).downloadInProgress();
}
updateLatestAvailability(application, localIndexFileName);
if (listener != null) {
listener.processFinish();
}
} else {
LOG.debug("onPostExecute: Not enough space for updates");
}
}
}
LOG.debug("onPostExecute: No internet connection");
}
} else {
if (context instanceof DownloadIndexesThread.DownloadEvents) {
((DownloadIndexesThread.DownloadEvents) context).downloadInProgress();
if (userRequested && context instanceof OsmLiveActivity) {
if (userRequested && context instanceof DownloadActivity) {
updateLatestAvailability(application, localIndexFileName);
if (listener != null) {
listener.processFinish();
}
application.showShortToastMessage(R.string.no_updates_available);
}
}
@ -170,4 +197,30 @@ public class PerformLiveUpdateAsyncTask
settings.LIVE_UPDATES_RETRIES.resetToDefault();
}
}
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() {
@Override
public void onResult(String result) {
if (!Algorithms.isEmpty(result)) {
SimpleDateFormat source = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US);
source.setTimeZone(TimeZone.getTimeZone("UTC"));
try {
Date parsed = source.parse(result);
if (parsed != null) {
long dateTime = parsed.getTime();
preferenceLatestUpdateAvailable(settings).set(dateTime);
preferenceLatestUpdateAvailable(localIndexFileName, settings).set(dateTime);
}
} catch (ParseException e) {
long dateTime = preferenceLatestUpdateAvailable(settings).get();
preferenceLatestUpdateAvailable(localIndexFileName, settings).set(dateTime);
LOG.error(e.getMessage(), e);
}
}
}
});
}
}

View file

@ -118,7 +118,7 @@ public class MapDataMenuController extends MenuController {
} else if (!downloaded || indexItem.isOutdated()) {
new DownloadValidationManager(app).startDownload(activity, indexItem);
} else if (isLiveUpdatesOn()) {
LiveUpdatesHelper.runLiveUpdate(activity, indexItem.getTargetFileName(), true);
LiveUpdatesHelper.runLiveUpdate(activity, indexItem.getTargetFileName(), true, null);
}
}
}

View file

@ -11,9 +11,9 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
@ -688,7 +688,7 @@ public class PlanRouteFragment extends BaseOsmAndFragment implements OsmAndLocat
}
});
if (!portrait) {
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) appModesBtn.getLayoutParams();
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) appModesBtn.getLayoutParams();
params.leftMargin = mapActivity.getResources().getDimensionPixelSize(R.dimen.dashboard_land_width);
appModesBtn.setLayoutParams(params);
}

View file

@ -65,6 +65,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static net.osmand.AndroidUtils.getSecondaryTextColorId;
import static net.osmand.plus.UiUtilities.CompoundButtonType.PROFILE_DEPENDENT;
public class TripRecordingActiveBottomSheet extends MenuBottomSheetDialogFragment {
@ -612,11 +613,6 @@ public class TripRecordingActiveBottomSheet extends MenuBottomSheetDialogFragmen
return nightMode ? R.color.active_color_primary_dark : R.color.active_color_primary_light;
}
@ColorRes
public static int getSecondaryTextColorId(boolean nightMode) {
return nightMode ? R.color.text_color_secondary_dark : R.color.text_color_secondary_light;
}
@ColorRes
public static int getActiveIconColorId(boolean nightMode) {
return nightMode ? R.color.icon_color_active_dark : R.color.icon_color_active_light;

View file

@ -6,6 +6,8 @@ import android.graphics.drawable.LayerDrawable;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -32,6 +34,10 @@ import net.osmand.plus.settings.preferences.SwitchPreferenceEx;
import org.apache.commons.logging.Log;
import static net.osmand.plus.UiUtilities.CompoundButtonType.TOOLBAR;
import static net.osmand.plus.liveupdates.LiveUpdatesSettingsDialogFragmentNew.getActivePrimaryColorId;
import static net.osmand.plus.monitoring.TripRecordingActiveBottomSheet.getSecondaryIconColorId;
public class BooleanPreferenceBottomSheet extends BasePreferenceBottomSheet {
public static final String TAG = BooleanPreferenceBottomSheet.class.getSimpleName();
@ -125,17 +131,25 @@ public class BooleanPreferenceBottomSheet extends BasePreferenceBottomSheet {
public static View getCustomButtonView(OsmandApplication app, ApplicationMode mode, boolean checked, boolean nightMode) {
View customView = UiUtilities.getInflater(app, nightMode).inflate(R.layout.bottom_sheet_item_preference_switch, null);
updateCustomButtonView(app, mode, customView, checked, nightMode);
return customView;
}
public static void updateCustomButtonView(OsmandApplication app, ApplicationMode mode, View customView, boolean checked, boolean nightMode) {
Context themedCtx = UiUtilities.getThemedContext(app, nightMode);
View buttonView = customView.findViewById(R.id.button_container);
LinearLayout buttonView = customView.findViewById(R.id.button_container);
int color = checked ? mode.getProfileColor(nightMode) : AndroidUtils.getColorFromAttr(themedCtx, R.attr.divider_color_basic);
int bgColor = UiUtilities.getColorWithAlpha(color, checked ? 0.1f : 0.5f);
int selectedColor = UiUtilities.getColorWithAlpha(color, checked ? 0.3f : 0.5f);
int bgColor;
int selectedColor;
if (mode != null) {
int color = checked ? mode.getProfileColor(nightMode) : AndroidUtils.getColorFromAttr(themedCtx, R.attr.divider_color_basic);
bgColor = UiUtilities.getColorWithAlpha(color, checked ? 0.1f : 0.5f);
selectedColor = UiUtilities.getColorWithAlpha(color, checked ? 0.3f : 0.5f);
} else {
bgColor = ContextCompat.getColor(app, checked
? getActivePrimaryColorId(nightMode) : getSecondaryIconColorId(nightMode));
selectedColor = UiUtilities.getColorWithAlpha(
ContextCompat.getColor(app, getActivePrimaryColorId(nightMode)), checked ? 0.3f : 0.5f);
}
int bgResId = R.drawable.rectangle_rounded_right;
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {

View file

@ -37,7 +37,7 @@ public class MultiStateToggleButton {
this.nightMode = nightMode;
}
public void setItems(RadioItem firstBtn, RadioItem secondBtn, RadioItem ... other) {
public void setItems(RadioItem firstBtn, RadioItem secondBtn, RadioItem... other) {
items.clear();
items.add(firstBtn);
items.add(secondBtn);
@ -101,12 +101,16 @@ public class MultiStateToggleButton {
}
private void updateView() {
updateView(true);
}
public void updateView(boolean isEnabled) {
int activeColor = ContextCompat.getColor(app, nightMode
? R.color.active_color_primary_dark
: R.color.active_color_primary_light);
? isEnabled ? R.color.active_color_primary_dark : R.color.icon_color_default_dark
: isEnabled ? R.color.active_color_primary_light : R.color.icon_color_default_light);
int textColor = ContextCompat.getColor(app, nightMode
? R.color.text_color_primary_dark
: R.color.text_color_primary_light);
? isEnabled ? R.color.text_color_primary_dark : R.color.text_color_secondary_dark
: isEnabled ? R.color.text_color_primary_light : R.color.text_color_secondary_light);
int radius = AndroidUtils.dpToPx(app, 4);
float[] leftBtnRadii = new float[]{radius, radius, 0, 0, 0, 0, radius, radius};
float[] rightBtnRadii = new float[]{0, 0, radius, radius, radius, radius, 0, 0};
@ -121,6 +125,7 @@ public class MultiStateToggleButton {
for (int i = 0; i < items.size(); i++) {
RadioItem item = items.get(i);
ViewGroup container = buttons.get(i);
container.setEnabled(isEnabled);
TextView tvTitle = (TextView) container.findViewById(R.id.title);
if (selectedItem == item) {
if (i == 0) {
@ -148,7 +153,7 @@ public class MultiStateToggleButton {
}
}
private void hideDividers(int ... dividerIndexes) {
private void hideDividers(int... dividerIndexes) {
for (int dividerIndex : dividerIndexes) {
if (dividerIndex >= 0 && dividerIndex < dividers.size()) {
dividers.get(dividerIndex).setVisibility(View.GONE);