Merge pull request #10832 from osmandapp/master

update test branch
This commit is contained in:
Hardy 2021-02-10 22:41:28 +01:00 committed by GitHub
commit 76c65130fa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 990 additions and 947 deletions

View file

@ -4,7 +4,7 @@
<application
android:icon="@mipmap/icon_free"
android:label="@string/app_name_free"
android:label="@string/app_name"
tools:replace="android:icon, android:label">
<meta-data

View file

@ -24,6 +24,7 @@ android {
// Build that doesn't include 3D OpenGL
legacy {
dimension "coreversion"
resValue "string", "app_edition", ""
}
}
}

View file

@ -37,6 +37,7 @@
android:layout_marginRight="@dimen/dialog_content_margin"
android:layout_marginEnd="@dimen/dialog_content_margin"
android:clickable="false"
android:scrollbars="none"
android:focusableInTouchMode="false" />
<androidx.appcompat.widget.AppCompatImageView

View file

@ -1,198 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:osmand="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/text_margin_small"
android:layout_marginRight="@dimen/text_margin_small"
android:background="?attr/wikivoyage_travel_card_bg"
android:orientation="vertical"
android:layout_marginEnd="@dimen/text_margin_small"
android:layout_marginStart="@dimen/text_margin_small">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/content_padding"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/bottom_sheet_content_padding_small">
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/content_padding"
android:layout_marginRight="@dimen/content_padding"
android:layout_marginTop="4dp"
android:layout_weight="1"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?attr/wikivoyage_primary_text_color"
android:textSize="@dimen/travel_card_primary_text_size"
osmand:typeface="@string/font_roboto_medium"
tools:text="Download file"/>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:ignore="ContentDescription"
tools:src="@drawable/travel_card_download_icon"/>
</LinearLayout>
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lineSpacingMultiplier="@dimen/text_button_line_spacing_multiplier"
android:textColor="@color/wikivoyage_secondary_text"
android:textSize="@dimen/travel_card_primary_text_size"
osmand:typeface="@string/font_roboto_regular"
tools:text="Download this Wikivoyage travel guides file to view articles about places around the world without an internet connection."/>
</LinearLayout>
<LinearLayout
android:id="@+id/file_data_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/content_padding"
android:layout_marginLeft="@dimen/bottom_sheet_content_margin_small"
android:layout_marginRight="@dimen/bottom_sheet_content_margin_small"
android:background="?attr/wikivoyage_travel_card_stroke_bg"
android:minHeight="@dimen/bottom_sheet_selected_item_title_height"
android:paddingLeft="@dimen/content_padding"
android:paddingRight="@dimen/content_padding"
android:layout_marginStart="@dimen/bottom_sheet_content_margin_small"
android:paddingEnd="@dimen/content_padding"
android:paddingStart="@dimen/content_padding"
android:layout_marginEnd="@dimen/bottom_sheet_content_margin_small">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/file_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/content_padding"
android:layout_marginRight="@dimen/content_padding"
tools:ignore="ContentDescription"
tools:src="@drawable/ic_action_read_article"
tools:tint="?attr/wikivoyage_active_color"/>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:orientation="vertical">
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/file_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?attr/wikivoyage_primary_text_color"
android:textSize="@dimen/default_list_text_size"
osmand:typeface="@string/font_roboto_regular"
tools:text="Wikivoyage"/>
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/file_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/wikivoyage_secondary_text"
android:textSize="@dimen/default_sub_text_size"
osmand:typeface="@string/font_roboto_regular"
tools:text="255 Mb • Update 11 April"/>
<ProgressBar
android:id="@+id/progress_bar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:minHeight="0dp"
android:visibility="gone"
tools:visibility="visible"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/bottom_sheet_content_margin_small"
android:layout_marginLeft="@dimen/bottom_sheet_content_margin_small"
android:layout_marginRight="@dimen/bottom_sheet_content_margin_small"
android:layout_marginEnd="@dimen/bottom_sheet_content_margin_small"
android:layout_marginStart="@dimen/bottom_sheet_content_margin_small">
<FrameLayout
android:id="@+id/secondary_btn_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="?attr/wikivoyage_secondary_btn_bg">
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/secondary_button"
android:layout_width="match_parent"
android:layout_height="@dimen/wikivoyage_card_button_height"
android:layout_gravity="center"
android:background="?attr/selectableItemBackgroundBorderless"
android:ellipsize="end"
android:gravity="center"
android:letterSpacing="@dimen/text_button_letter_spacing"
android:maxLines="1"
android:textColor="?attr/wikivoyage_active_color"
android:textSize="@dimen/text_button_text_size"
osmand:typeface="@string/font_roboto_medium"
tools:ignore="UnusedAttribute"
tools:text="Later"/>
</FrameLayout>
<View
android:id="@+id/buttons_divider"
android:layout_width="@dimen/bottom_sheet_content_margin_small"
android:layout_height="match_parent"/>
<FrameLayout
android:id="@+id/primary_btn_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="?attr/wikivoyage_primary_btn_bg">
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/primary_button"
android:layout_width="match_parent"
android:layout_height="@dimen/wikivoyage_card_button_height"
android:layout_gravity="center"
android:background="?attr/selectableItemBackgroundBorderless"
android:ellipsize="end"
android:gravity="center"
android:letterSpacing="@dimen/text_button_letter_spacing"
android:maxLines="1"
android:textColor="?attr/wikivoyage_primary_btn_text_color"
android:textSize="@dimen/text_button_text_size"
osmand:typeface="@string/font_roboto_medium"
tools:ignore="UnusedAttribute"
tools:text="Update"/>
</FrameLayout>
</LinearLayout>
</LinearLayout>

View file

@ -18,16 +18,34 @@
android:layout_margin="@dimen/content_padding"
android:orientation="vertical">
<net.osmand.plus.widgets.TextViewEx
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/content_padding"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/maps_you_need"
android:textColor="?attr/wikivoyage_primary_text_color"
android:textSize="@dimen/travel_card_primary_text_size"
osmand:typeface="@string/font_roboto_medium"/>
android:layout_marginBottom="@dimen/bottom_sheet_content_padding_small">
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/content_padding"
android:layout_marginRight="@dimen/content_padding"
android:layout_marginTop="4dp"
android:layout_weight="1"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?attr/wikivoyage_primary_text_color"
android:textSize="@dimen/travel_card_primary_text_size"
osmand:typeface="@string/font_roboto_medium"
tools:text="Download file" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:ignore="ContentDescription"
tools:src="@drawable/travel_card_download_icon" />
</LinearLayout>
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/description"

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/content_padding"
android:background="?attr/wikivoyage_card_bg_color"
android:gravity="center_vertical">
<include
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="@dimen/dialog_button_height"
layout="@layout/bottom_sheet_dialog_button" />
</LinearLayout>
<include
android:id="@+id/shadow"
layout="@layout/card_bottom_divider"
android:visibility="visible" />
</LinearLayout>

View file

@ -103,6 +103,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="2"
android:scrollbars="none"
android:textColor="@color/wikivoyage_secondary_text"
android:textSize="@dimen/default_sub_text_size"
osmand:typeface="@string/font_roboto_regular"

File diff suppressed because it is too large Load diff

View file

@ -3881,4 +3881,14 @@
<string name="poi_wildlife_crossing_bat_bridge">Ponto por vespertoj</string>
<string name="poi_wildlife_crossing">Trairejo por sovaĝaj bestoj</string>
<string name="poi_swimming_area">Naĝejo natura</string>
<string name="poi_piste_status_closed">Stato de skivojo: fermita</string>
<string name="poi_piste_status_open">Stato de skivojo: malfermita</string>
<string name="poi_patrolled_no">Kontrolata: ne</string>
<string name="poi_patrolled_yes">Kontrolata: jes</string>
<string name="poi_gladed_yes">Senarbigita: jes</string>
<string name="poi_piste_name">Nomo de skivojo</string>
<string name="poi_piste_ski_jump">Skisaltejo</string>
<string name="poi_mobile_library">Movebla biblioteko (haltloko)</string>
<string name="poi_summit_register_no">Pinttaglibro: ne</string>
<string name="poi_summit_register_yes">Pinttaglibro: jes</string>
</resources>

View file

@ -4020,4 +4020,17 @@
<string name="open_place_reviews">OpenPlaceReviews</string>
<string name="opr_use_dev_url">Uzi test.openplacereviews.org</string>
<string name="login_open_place_reviews">Ensaluti al OpenPlaceReviews</string>
<string name="activity_type_water_name">Akvo</string>
<string name="activity_type_winter_name">Vintro</string>
<string name="activity_type_snowmobile_name">Motorsledo</string>
<string name="activity_type_riding_name">Rajdado</string>
<string name="activity_type_racing_name">Kurkonkurso</string>
<string name="activity_type_mountainbike_name">Montbiciklo</string>
<string name="activity_type_cycling_name">Biciklado</string>
<string name="activity_type_hiking_name">Marŝado</string>
<string name="activity_type_running_name">Kurado</string>
<string name="activity_type_walking_name">Piedirado</string>
<string name="activity_type_offroad_name">Ekstervoje</string>
<string name="activity_type_motorbike_name">Motorciklo</string>
<string name="activity_type_car_name">Aŭto</string>
</resources>

View file

@ -4005,4 +4005,13 @@
<string name="open_place_reviews">OpenPlaceReviews</string>
<string name="opr_use_dev_url">Utilisez test.openplacereviews.org</string>
<string name="login_open_place_reviews">Se connecter à OpenPlaceReviews</string>
<string name="activity_type_water_name">Eau</string>
<string name="activity_type_winter_name">Hiver</string>
<string name="activity_type_snowmobile_name">Motoneige</string>
<string name="activity_type_cycling_name">Vélo</string>
<string name="activity_type_mountainbike_name">VTT</string>
<string name="activity_type_hiking_name">Randonnée</string>
<string name="activity_type_running_name">Course à pied</string>
<string name="activity_type_walking_name">Marche</string>
<string name="activity_type_car_name">Voiture</string>
</resources>

View file

@ -54,7 +54,7 @@
<string name="poi_service_tyres">Autógumi-szerelő</string>
<string name="poi_car_wash">Autómosó</string>
<string name="poi_fuel">Benzinkút;Töltőállomás;Gázolajtöltő-állomás</string>
<string name="poi_electricity_combined_charging">Elektromos töltőállomás</string>
<string name="poi_electricity_combined_charging">Elektromos töltőállomás;Elektromos autó töltése;Villanyautó töltése;Elektromos töltőpont</string>
<string name="poi_compressed_air">Sűrített levegő</string>
<string name="poi_parking">Parkoló</string>
<string name="poi_motorcycle_parking">Motorkerékpár-parkoló</string>
@ -3879,4 +3879,14 @@
<string name="poi_wildlife_crossing_bat_tunnel">Denevéralagút</string>
<string name="poi_wildlife_crossing_bat_bridge">Denevérhíd</string>
<string name="poi_wildlife_crossing">Vadátjáró</string>
<string name="poi_summit_register_no">Csúcsnapló nincs</string>
<string name="poi_summit_register_yes">Csúcsnapló van</string>
<string name="poi_patrolled_no">Hegyi mentő nincs</string>
<string name="poi_patrolled_yes">Hegyi mentő van</string>
<string name="poi_gladed_yes">Erdei pálya</string>
<string name="poi_mobile_library">Mozgó könyvtár tartózkodási helye</string>
<string name="poi_piste_status_closed">Pálya állapota: zárva</string>
<string name="poi_piste_status_open">Pálya állapota: nyitva</string>
<string name="poi_piste_name">Pálya neve</string>
<string name="poi_piste_ski_jump">Síugrósánc</string>
</resources>

View file

@ -3881,4 +3881,14 @@
<string name="poi_wildlife_crossing_bat_bridge">Brú fyrir leðurblökur</string>
<string name="poi_wildlife_crossing">Þverun villtra dýra</string>
<string name="poi_swimming_area">Sundsvæði</string>
<string name="poi_mobile_library">Stæði fyrir bókabíl</string>
<string name="poi_summit_register_no">Skráning á tind: nei</string>
<string name="poi_summit_register_yes">Skráning á tind: já</string>
<string name="poi_piste_status_closed">Staða brautar: lokuð</string>
<string name="poi_piste_status_open">Staða brautar: opin</string>
<string name="poi_patrolled_no">Eftirlit: nei</string>
<string name="poi_patrolled_yes">Eftirlit: já</string>
<string name="poi_gladed_yes">Skafið: já</string>
<string name="poi_piste_name">Heiti brautar</string>
<string name="poi_piste_ski_jump">Skíðastökk</string>
</resources>

View file

@ -3972,7 +3972,7 @@
<string name="select_folder">Veldu möppu</string>
<string name="select_folder_descr">Veldu möppu eða bættu við nýrri</string>
<string name="shared_string_empty">Tómt</string>
<string name="analyze_by_intervals">Greina eftir millibilum (uppskipting millibila)</string>
<string name="analyze_by_intervals">Greina eftir uppskiptingu millibila</string>
<string name="upload_to_openstreetmap">Senda inn í OpenStreetMap</string>
<string name="edit_track">Breyta ferli</string>
<string name="rename_track">Endurnefna feril</string>
@ -4005,4 +4005,32 @@
<string name="routing_engine_vehicle_type_mtb">Fjallahjól</string>
<string name="message_server_error">Villa í netþjóni: %1$s</string>
<string name="message_name_is_already_exists">Nafnið er þegar til staðar</string>
<string name="delete_online_routing_engine">Eyða þessari leiðagerð á netinu\?</string>
<string name="context_menu_read_full">Lesa óstytt</string>
<string name="context_menu_edit_descr">Breyta lýsingu</string>
<string name="delete_waypoints">Eyða ferilpunktum</string>
<string name="copy_to_map_markers">Afrita í kortamerki</string>
<string name="copy_to_map_favorites">Afrita í eftirlæti</string>
<string name="upload_photo">Sendi inn</string>
<string name="upload_photo_completed">Innsendingu er lokið</string>
<string name="uploading_count">Sendi inn %1$d af %2$d</string>
<string name="uploaded_count">Sent inn %1$d af %2$d</string>
<string name="toast_select_edits_for_upload">Veldu breytingar til að senda inn</string>
<string name="hillshade_slope_contour_lines">Hæðaskygging / Halli / Hæðarlínur</string>
<string name="open_place_reviews">OpenPlaceReviews</string>
<string name="opr_use_dev_url">Nota test.openplacereviews.org</string>
<string name="login_open_place_reviews">Skrá inn á OpenPlaceReviews</string>
<string name="activity_type_water_name">Vatn</string>
<string name="activity_type_winter_name">Vetur</string>
<string name="activity_type_snowmobile_name">Vélsleðar</string>
<string name="activity_type_riding_name">Útreiðar</string>
<string name="activity_type_racing_name">Kappakstur</string>
<string name="activity_type_mountainbike_name">Fjallahjól</string>
<string name="activity_type_cycling_name">Hjólreiðar</string>
<string name="activity_type_hiking_name">Gönguferðir</string>
<string name="activity_type_running_name">Hlaup</string>
<string name="activity_type_walking_name">Gangandi</string>
<string name="activity_type_offroad_name">Utanvegaakstur</string>
<string name="activity_type_motorbike_name">Vélhjól</string>
<string name="activity_type_car_name">Akandi</string>
</resources>

View file

@ -3970,7 +3970,7 @@
<string name="select_folder">בחירת תקינה</string>
<string name="select_folder_descr">נא לבחור תיקייה או להוסיף אחת חדשה</string>
<string name="shared_string_empty">ריק</string>
<string name="analyze_by_intervals">ניתוח לפי מרווחים (פיצול מרווחים)</string>
<string name="analyze_by_intervals">ניתוח מרווחי פיצול</string>
<string name="upload_to_openstreetmap">העלאה ל־OpenStreetMap</string>
<string name="edit_track">עריכת מסלול</string>
<string name="rename_track">שינוי שם מסלול</string>
@ -4016,4 +4016,11 @@
<string name="open_place_reviews">OpenPlaceReviews</string>
<string name="opr_use_dev_url">להשתמש ב־test.openplacereviews.org</string>
<string name="login_open_place_reviews">כניסה ל־OpenPlaceReviews</string>
<string name="announcement_time_descr">זמן ההכרזה של ההנחיות הקוליות השונות תלוי בסוג ההכרזה, במהירות הניווט הנוכחית ובמהירות הניווט כבררת מחדל.</string>
<string name="hillshade_slope_contour_lines">הצללה / מדרון / קווי מתאר</string>
<string name="open_place_reviews_plugin_description">OpenPlaceReviews הוא מיזם בהובלת הקהילה בנוגע למקומות ציבוריים כגון מסעדות, מלונות, מוזיאונים ונקודות דרך. הוא אוסף מידע ציבורי עליהם כגון תמונות, סקירות, קישורים למערכות אחרות שמקושרות ל־OpenStreetMap, ויקיפדיה.
\n
\nכל הנתונים של OpenPlaceReview הם פתוחים וזמינים לציבור הרחב: http://openplacereviews.org/data.
\n
\nניתן לקרוא עוד באתר: http://openplacereviews.org</string>
</resources>

View file

@ -502,7 +502,7 @@
<string name="poi_climbing">Tırmanma</string>
<string name="poi_cricket">Kriket</string>
<string name="poi_croquet">Kroket</string>
<string name="poi_cycling">Bisiklete binme</string>
<string name="poi_cycling">Bisiklet sürme</string>
<string name="poi_diving">Dalış</string>
<string name="poi_scuba_diving">Tüplü dalış</string>
<string name="poi_dog_racing">Köpek yarışı</string>

View file

@ -2227,7 +2227,7 @@
<string name="marker_save_as_track_descr">İşaretleyicilerinizi şu GPX dosyasına aktarın:</string>
<string name="show_arrows_on_the_map">Haritada yön gösterici okları göster</string>
<string name="measurement_tool_snap_to_road_descr">OsmAnd seçilmiş profil için rotalı noktalara bağlanacak.</string>
<string name="none_point_error">Lütfen en az bir nokta ekleyiniz.</string>
<string name="none_point_error">Lütfen en az bir nokta ekleyin.</string>
<string name="measurement_tool_action_bar">Haritayı görüntüle ve nokta ekle</string>
<string name="store_tracks_in_monthly_directories">Kaydedilmiş izlenen yolları aylık klasörlerde depola</string>
<string name="mapillary_menu_filter_description">Resimleri göndericiye, tarihe ya da türüne göre filtreleyin. Sadece yakın mesafe yakınlaştırmasında etkin.</string>
@ -3738,7 +3738,7 @@
<string name="track_coloring_solid">Koyu</string>
<string name="plan_route_last_edited">Son düzenleme</string>
<string name="plan_route_import_track">Yolu içe aktar</string>
<string name="plan_route_open_existing_track">Mevcut yolu aç</string>
<string name="plan_route_open_existing_track">Var olan yolu aç</string>
<string name="plan_route_create_new_route">Yeni güzergah oluştur</string>
<string name="plan_route_select_track_file_for_open">Açmak için bir yol dosyası seçin.</string>
<string name="shared_string_done">Bitti</string>
@ -3968,7 +3968,7 @@
<string name="select_folder">Klasör seç</string>
<string name="select_folder_descr">Klasör seçin veya yeni bir tane ekleyin</string>
<string name="shared_string_empty">Boş</string>
<string name="analyze_by_intervals">Aralıklara göre analiz et (bölme aralığı)</string>
<string name="analyze_by_intervals">Bölünmüş aralıkları analiz et</string>
<string name="upload_to_openstreetmap">OpenStreetMap\'e yükle</string>
<string name="edit_track">Yolu düzenle</string>
<string name="rename_track">Yolu yeniden adlandır</string>
@ -4012,4 +4012,26 @@
<string name="uploading_count">%1$d / %2$d karşıya yükleniyor</string>
<string name="uploaded_count">%1$d / %2$d karşıya yüklendi</string>
<string name="toast_select_edits_for_upload">Karşıya yüklenecek düzenlemeleri seçin</string>
<string name="hillshade_slope_contour_lines">tepe gölgesi / yamaç / eş yükselti eğrileri</string>
<string name="open_place_reviews_plugin_description">OpenPlaceReviews, restoranlar, oteller, müzeler, ara noktalar gibi halka açık yerler hakkında topluluk tarafından yürütülen bir projedir. Fotoğraflar, yorumlar, OpenStreetMap, Wikipedia gibi diğer sistemlere bağlantılar gibi onlar hakkındaki tüm halka açık bilgileri toplar.
\n
\nTüm OpenPlaceReview verileri açık ve herkes tarafından kullanılabilir: http://openplacereviews.org/data
\n
\nDaha fazlasını şu adresten okuyabilirsiniz: http://openplacereviews.org</string>
<string name="open_place_reviews">OpenPlaceReviews</string>
<string name="opr_use_dev_url">test.openplacereviews.org adresini kullan</string>
<string name="login_open_place_reviews">OpenPlaceReviews\'te oturum aç</string>
<string name="activity_type_water_name">Su</string>
<string name="activity_type_winter_name">Kış</string>
<string name="activity_type_snowmobile_name">Kar arabası</string>
<string name="activity_type_riding_name">Binme</string>
<string name="activity_type_racing_name">Yarış</string>
<string name="activity_type_mountainbike_name">Dağ bisikleti</string>
<string name="activity_type_cycling_name">Bisiklet sürme</string>
<string name="activity_type_hiking_name">Yürüyüş</string>
<string name="activity_type_running_name">Koşma</string>
<string name="activity_type_walking_name">Yürüme</string>
<string name="activity_type_offroad_name">Arazi</string>
<string name="activity_type_motorbike_name">Motosiklet</string>
<string name="activity_type_car_name">Araba</string>
</resources>

View file

@ -2663,7 +2663,7 @@
<string name="shared_string_travel_book">旅游书</string>
<string name="download_images">下载图片</string>
<string name="rendering_value_black_name">黑色</string>
<string name="rendering_attr_surface_wood_name"></string>
<string name="rendering_attr_surface_wood_name"></string>
<string name="app_mode_subway">地铁</string>
<string name="app_mode_horse">骑马</string>
<string name="shared_string_color_magenta">品红色</string>
@ -2674,7 +2674,7 @@
<string name="points_of_interests">兴趣点POI</string>
<string name="shared_string_capacity">容量</string>
<string name="rendering_attr_surface_metal_name">金属</string>
<string name="rendering_attr_highway_class_path_name">路径</string>
<string name="rendering_attr_highway_class_path_name">走道</string>
<string name="app_mode_helicopter">直升机</string>
<string name="app_mode_skiing">滑雪</string>
<string name="base_profile_descr_ski">滑雪</string>
@ -2839,7 +2839,7 @@
<string name="show_closed_notes">显示关闭的注记</string>
<string name="osm_edit_closed_note">关闭的OSM注记</string>
<string name="add_online_source">添加在线资源</string>
<string name="rendering_attr_highway_class_bridleway_name"/>
<string name="rendering_attr_highway_class_bridleway_name">驮道</string>
<string name="rendering_attr_surface_gravel_name">砾石</string>
<string name="rendering_attr_surface_sett_name">比利时石砌路</string>
<string name="routing_attr_allow_expert_name">允许专家路线</string>
@ -3124,4 +3124,79 @@
<string name="context_menu_item_add_waypoint">添加轨迹航点</string>
<string name="save_track_to_gpx_globally">将轨迹记录到GPX文件</string>
<string name="disable_recording_once_app_killed_descrp">当应用程序被杀死时跟踪日志将被暂停通过最近的应用程序OsmAnd背景指示会从Android通知栏中消失</string>
<string name="add_online_routing_engine">添加在线路线引擎</string>
<string name="edit_online_routing_engine">编辑在线路线引擎</string>
<string name="shared_string_subtype">子类型</string>
<string name="shared_string_vehicle">车辆</string>
<string name="shared_string_api_key">API 密钥</string>
<string name="shared_string_server_url">服务器 URL</string>
<string name="shared_string_enter_param">输入参数</string>
<string name="keep_it_empty_if_not">否则请保持空白</string>
<string name="test_route_calculation">测试路线计算</string>
<string name="routing_engine_vehicle_type_driving">驾驶</string>
<string name="routing_engine_vehicle_type_car">汽车</string>
<string name="copy_address">复制地址</string>
<string name="online_routing_engine">在线路线引擎</string>
<string name="online_routing_engines">在线路线引擎</string>
<string name="shared_string_folders">文件夹</string>
<string name="select_folder">选择文件夹</string>
<string name="select_folder_descr">选择文件夹或添加新文件夹</string>
<string name="upload_to_openstreetmap">上传到 OpenStreetMap</string>
<string name="edit_track">编辑轨迹</string>
<string name="rename_track">重命名轨迹</string>
<string name="change_folder">更改文件夹</string>
<string name="shared_string_sec"></string>
<string name="announcement_time_passing">通过</string>
<string name="announcement_time_prepare_long">长时间准备</string>
<string name="announcement_time_prepare">准备</string>
<string name="announcement_time_off_route">偏离路线</string>
<string name="announcement_time_arrive">到达目的地</string>
<string name="shared_string_turn">转弯</string>
<string name="announcement_time_intervals">时间和距离间隔</string>
<string name="announcement_time_descr">不同语音提示的公告时间取决于提示类型、当前导航速度和默认导航速度。</string>
<string name="announcement_time_title">公告时间</string>
<string name="start_recording">开始录制</string>
<string name="show_track_on_map">在地图上显示轨迹</string>
<string name="routing_engine_vehicle_type_wheelchair">轮椅</string>
<string name="routing_engine_vehicle_type_hiking">徒步</string>
<string name="routing_engine_vehicle_type_walking">步行</string>
<string name="routing_engine_vehicle_type_cycling_electric">电动自行车</string>
<string name="routing_engine_vehicle_type_cycling_mountain">山地自行车</string>
<string name="routing_engine_vehicle_type_cycling_road">公路自行车</string>
<string name="routing_engine_vehicle_type_hgv">重型货车</string>
<string name="routing_engine_vehicle_type_small_truck">小型卡车</string>
<string name="routing_engine_vehicle_type_truck">卡车</string>
<string name="routing_engine_vehicle_type_scooter">踏板车</string>
<string name="routing_engine_vehicle_type_racingbike">竞速自行车</string>
<string name="routing_engine_vehicle_type_mtb">山地车</string>
<string name="message_server_error">服务器错误:%1$s</string>
<string name="delete_online_routing_engine">删除这个在线路线引擎?</string>
<string name="message_name_is_already_exists">名字已经存在</string>
<string name="context_menu_read_full">阅读全文</string>
<string name="context_menu_edit_descr">编辑描述</string>
<string name="delete_waypoints">删除航点</string>
<string name="copy_to_map_markers">复制到地图标记</string>
<string name="copy_to_map_favorites">复制到收藏</string>
<string name="upload_photo">上传中</string>
<string name="upload_photo_completed">上传完成</string>
<string name="uploading_count">正在上传 %1$d共 %2$d</string>
<string name="toast_select_edits_for_upload">选择要上传的编辑</string>
<string name="uploaded_count">已上传 %1$d共 %2$d</string>
<string name="hillshade_slope_contour_lines">山体阴影/斜坡/等高线</string>
<string name="open_place_reviews_plugin_description">OpenPlaceReviews 是一个社区驱动的项目,关于公共场所,如餐馆,酒店,博物馆,航点。它收集了所有关于它们的公共信息,如照片、评论、链接到其他系统的 OpenStreetMap、维基百科。
\n
\n所有 OpenPlaceReview 的数据都是开放的所有人都可以使用http://openplacereviews.org/data。
\n
\n你可以在以下网站阅读更多信息http://openplacereviews.org</string>
<string name="open_place_reviews">OpenPlaceReviews</string>
<string name="opr_use_dev_url">使用 test.openplacereviews.org</string>
<string name="login_open_place_reviews">登录 OpenPlaceReviews</string>
<string name="activity_type_winter_name">冬季</string>
<string name="activity_type_snowmobile_name">雪地摩托</string>
<string name="activity_type_riding_name">骑行</string>
<string name="activity_type_cycling_name">骑车</string>
<string name="activity_type_hiking_name">健行</string>
<string name="activity_type_running_name">跑步</string>
<string name="activity_type_walking_name">步行</string>
<string name="activity_type_motorbike_name">摩托车</string>
</resources>

View file

@ -36,6 +36,7 @@ import net.osmand.data.LatLon;
import net.osmand.data.QuadRect;
import net.osmand.data.RotatedTileBox;
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem;
import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.UiUtilities;
@ -66,6 +67,8 @@ public class TrackDetailsMenu {
@Nullable
private GpxDisplayItem gpxItem;
@Nullable
private SelectedGpxFile selectedGpxFile;
@Nullable
private TrackDetailsBarController toolbarController;
@Nullable
private TrkSegment segment;
@ -101,6 +104,15 @@ public class TrackDetailsMenu {
this.gpxItem = gpxItem;
}
@Nullable
public SelectedGpxFile getSelectedGpxFile() {
return selectedGpxFile;
}
public void setSelectedGpxFile(@NonNull SelectedGpxFile selectedGpxFile) {
this.selectedGpxFile = selectedGpxFile;
}
public boolean isVisible() {
return visible;
}
@ -539,7 +551,7 @@ public class TrackDetailsMenu {
}
}
public boolean shouldShowXAxisPoints () {
public boolean shouldShowXAxisPoints() {
return true;
}
@ -707,18 +719,19 @@ public class TrackDetailsMenu {
if (gpxItem.chartTypes != null && gpxItem.chartTypes.length > 0) {
for (int i = 0; i < gpxItem.chartTypes.length; i++) {
OrderedLineDataSet dataSet = null;
boolean withoutGaps = selectedGpxFile != null && (!selectedGpxFile.isJoinSegments() && gpxItem.isGeneralTrack());
switch (gpxItem.chartTypes[i]) {
case ALTITUDE:
dataSet = GpxUiHelper.createGPXElevationDataSet(app, chart, analysis,
gpxItem.chartAxisType, false, true, false);
gpxItem.chartAxisType, false, true, withoutGaps);
break;
case SPEED:
dataSet = GpxUiHelper.createGPXSpeedDataSet(app, chart, analysis,
gpxItem.chartAxisType, gpxItem.chartTypes.length > 1, true, false);
gpxItem.chartAxisType, gpxItem.chartTypes.length > 1, true, withoutGaps);
break;
case SLOPE:
dataSet = GpxUiHelper.createGPXSlopeDataSet(app, chart, analysis,
gpxItem.chartAxisType, null, gpxItem.chartTypes.length > 1, true, false);
gpxItem.chartAxisType, null, gpxItem.chartTypes.length > 1, true, withoutGaps);
break;
}
if (dataSet != null) {

View file

@ -1,10 +1,10 @@
package net.osmand.plus.track;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.ColorInt;
@ -12,21 +12,19 @@ import androidx.annotation.ColorRes;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.AppCompatImageView;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import net.osmand.AndroidUtils;
import net.osmand.GPXUtilities.GPXFile;
import net.osmand.GPXUtilities.GPXTrackAnalysis;
import net.osmand.plus.GpxSelectionHelper.GpxDisplayGroup;
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem;
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItemType;
import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
import net.osmand.plus.OsmAndFormatter;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.UiUtilities;
import net.osmand.plus.helpers.AndroidUiHelper;
import net.osmand.plus.helpers.GpxUiHelper;
import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetType;
import net.osmand.plus.myplaces.SegmentActionsListener;
import net.osmand.plus.widgets.TextViewEx;
@ -40,101 +38,134 @@ public class GpxBlockStatisticsBuilder {
private final OsmandApplication app;
private RecyclerView blocksView;
private final SelectedGpxFile selectedGpxFile;
private final TrackDisplayHelper displayHelper;
private final GpxDisplayItemType[] filterTypes = {GpxDisplayItemType.TRACK_SEGMENT};
public GpxBlockStatisticsBuilder(OsmandApplication app, SelectedGpxFile selectedGpxFile, TrackDisplayHelper displayHelper) {
private BlockStatisticsAdapter adapter;
private final List<StatBlock> items = new ArrayList<>();
private final Handler handler = new Handler();
private Runnable updatingItems;
private boolean updateRunning = false;
public GpxBlockStatisticsBuilder(OsmandApplication app, SelectedGpxFile selectedGpxFile) {
this.app = app;
this.selectedGpxFile = selectedGpxFile;
this.displayHelper = displayHelper;
}
public void setBlocksView(RecyclerView blocksView) {
this.blocksView = blocksView;
}
private GPXTrackAnalysis getAnalysis() {
return selectedGpxFile.getTrackAnalysis(app);
private GpxDisplayItem getDisplayItem(GPXFile gpxFile) {
return GpxUiHelper.makeGpxDisplayItem(app, gpxFile);
}
private GPXFile getGPXFile() {
return selectedGpxFile.getGpxFile();
}
public void initStatBlocks(SegmentActionsListener actionsListener, @ColorInt int activeColor, boolean nightMode) {
GPXTrackAnalysis analysis = getAnalysis();
float totalDistance = analysis.totalDistance;
float timeSpan = analysis.timeSpan;
String asc = OsmAndFormatter.getFormattedAlt(analysis.diffElevationUp, app);
String desc = OsmAndFormatter.getFormattedAlt(analysis.diffElevationDown, app);
String avg = OsmAndFormatter.getFormattedSpeed(analysis.avgSpeed, app);
String max = OsmAndFormatter.getFormattedSpeed(analysis.maxSpeed, app);
List<StatBlock> items = new ArrayList<>();
prepareData(analysis, items, app.getString(R.string.distance), OsmAndFormatter.getFormattedDistance(totalDistance, app),
R.drawable.ic_action_track_16, R.color.icon_color_default_light, GPXDataSetType.ALTITUDE, GPXDataSetType.SPEED, ItemType.ITEM_DISTANCE);
prepareData(analysis, items, app.getString(R.string.altitude_ascent), asc,
R.drawable.ic_action_arrow_up_16, R.color.gpx_chart_red, GPXDataSetType.SLOPE, null, ItemType.ITEM_ALTITUDE);
prepareData(analysis, items, app.getString(R.string.altitude_descent), desc,
R.drawable.ic_action_arrow_down_16, R.color.gpx_pale_green, GPXDataSetType.ALTITUDE, GPXDataSetType.SLOPE, ItemType.ITEM_ALTITUDE);
prepareData(analysis, items, app.getString(R.string.average_speed), avg,
R.drawable.ic_action_speed_16, R.color.icon_color_default_light, GPXDataSetType.SPEED, null, ItemType.ITEM_SPEED);
prepareData(analysis, items, app.getString(R.string.max_speed), max,
R.drawable.ic_action_max_speed_16, R.color.icon_color_default_light, GPXDataSetType.SPEED, null, ItemType.ITEM_SPEED);
prepareData(analysis, items, app.getString(R.string.shared_string_time_span),
Algorithms.formatDuration((int) (timeSpan / 1000), app.accessibilityEnabled()),
R.drawable.ic_action_time_span_16, R.color.icon_color_default_light, GPXDataSetType.SPEED, null, ItemType.ITEM_TIME);
if (Algorithms.isEmpty(items)) {
AndroidUiHelper.updateVisibility(blocksView, false);
} else {
final BlockStatisticsAdapter sbAdapter = new BlockStatisticsAdapter(items, actionsListener, activeColor, nightMode);
initItems();
boolean isNotEmpty = !Algorithms.isEmpty(items);
AndroidUiHelper.updateVisibility(blocksView, isNotEmpty);
if (isNotEmpty) {
adapter = new BlockStatisticsAdapter(getDisplayItem(getGPXFile()), actionsListener, activeColor, nightMode);
adapter.setItems(items);
blocksView.setLayoutManager(new LinearLayoutManager(app, LinearLayoutManager.HORIZONTAL, false));
blocksView.setAdapter(sbAdapter);
blocksView.setAdapter(adapter);
}
}
public void prepareData(GPXTrackAnalysis analysis, List<StatBlock> listItems, String title,
String value, @DrawableRes int imageResId, @ColorRes int imageColorId,
public void stopUpdatingStatBlocks() {
handler.removeCallbacks(updatingItems);
updateRunning = false;
}
public void runUpdatingStatBlocks() {
updatingItems = new Runnable() {
@Override
public void run() {
if (adapter != null) {
initItems();
adapter.setItems(items);
AndroidUiHelper.updateVisibility(blocksView, !Algorithms.isEmpty(items));
}
int interval = app.getSettings().SAVE_GLOBAL_TRACK_INTERVAL.get();
handler.postDelayed(this, Math.max(1000, interval));
}
};
updateRunning = handler.post(updatingItems);
}
public void initItems() {
GPXFile gpxFile = getGPXFile();
GpxDisplayItem gpxDisplayItem = null;
GPXTrackAnalysis analysis = null;
boolean withoutGaps = true;
if (gpxFile.tracks.size() > 0) {
gpxDisplayItem = getDisplayItem(gpxFile);
}
if (gpxDisplayItem != null) {
analysis = gpxDisplayItem.analysis;
withoutGaps = !selectedGpxFile.isJoinSegments() && gpxDisplayItem.isGeneralTrack();
}
if (analysis != null) {
float totalDistance = withoutGaps ? analysis.totalDistanceWithoutGaps : analysis.totalDistance;
float timeSpan = withoutGaps ? analysis.timeSpanWithoutGaps : analysis.timeSpan;
String asc = OsmAndFormatter.getFormattedAlt(analysis.diffElevationUp, app);
String desc = OsmAndFormatter.getFormattedAlt(analysis.diffElevationDown, app);
String avg = OsmAndFormatter.getFormattedSpeed(analysis.avgSpeed, app);
String max = OsmAndFormatter.getFormattedSpeed(analysis.maxSpeed, app);
items.clear();
prepareData(analysis, app.getString(R.string.distance), OsmAndFormatter.getFormattedDistance(totalDistance, app),
R.drawable.ic_action_track_16, R.color.icon_color_default_light, GPXDataSetType.ALTITUDE, GPXDataSetType.SPEED, ItemType.ITEM_DISTANCE);
prepareData(analysis, app.getString(R.string.altitude_ascent), asc,
R.drawable.ic_action_arrow_up_16, R.color.gpx_chart_red, GPXDataSetType.SLOPE, null, ItemType.ITEM_ALTITUDE);
prepareData(analysis, app.getString(R.string.altitude_descent), desc,
R.drawable.ic_action_arrow_down_16, R.color.gpx_pale_green, GPXDataSetType.ALTITUDE, GPXDataSetType.SLOPE, ItemType.ITEM_ALTITUDE);
prepareData(analysis, app.getString(R.string.average_speed), avg,
R.drawable.ic_action_speed_16, R.color.icon_color_default_light, GPXDataSetType.SPEED, null, ItemType.ITEM_SPEED);
prepareData(analysis, app.getString(R.string.max_speed), max,
R.drawable.ic_action_max_speed_16, R.color.icon_color_default_light, GPXDataSetType.SPEED, null, ItemType.ITEM_SPEED);
prepareData(analysis, app.getString(R.string.shared_string_time_span),
Algorithms.formatDuration((int) (timeSpan / 1000), app.accessibilityEnabled()),
R.drawable.ic_action_time_span_16, R.color.icon_color_default_light, GPXDataSetType.SPEED, null, ItemType.ITEM_TIME);
}
}
public void prepareData(GPXTrackAnalysis analysis, String title, String value,
@DrawableRes int imageResId, @ColorRes int imageColorId,
GPXDataSetType firstType, GPXDataSetType secondType, ItemType itemType) {
StatBlock statBlock = new StatBlock(title, value, imageResId, imageColorId, firstType, secondType, itemType);
switch (statBlock.itemType) {
case ITEM_DISTANCE: {
if (analysis.totalDistance != 0f) {
listItems.add(statBlock);
items.add(statBlock);
}
break;
}
case ITEM_ALTITUDE: {
if (analysis.hasElevationData) {
listItems.add(statBlock);
items.add(statBlock);
}
break;
}
case ITEM_SPEED: {
if (analysis.isSpeedSpecified()) {
listItems.add(statBlock);
items.add(statBlock);
}
break;
}
case ITEM_TIME: {
if (analysis.hasSpeedData) {
listItems.add(statBlock);
items.add(statBlock);
}
break;
}
}
}
private void setImageDrawable(ImageView iv, @DrawableRes Integer resId, @ColorRes int color) {
Drawable icon = resId != null ? app.getUIUtilities().getIcon(resId, color)
: UiUtilities.tintDrawable(iv.getDrawable(), getResolvedColor(color));
iv.setImageDrawable(icon);
}
@ColorInt
protected int getResolvedColor(@ColorRes int colorId) {
return ContextCompat.getColor(app, colorId);
}
public class StatBlock {
private final String title;
private final String value;
private final int imageResId;
@ -164,14 +195,16 @@ public class GpxBlockStatisticsBuilder {
private class BlockStatisticsAdapter extends RecyclerView.Adapter<BlockStatisticsViewHolder> {
private final List<StatBlock> items = new ArrayList<>();
private final GpxDisplayItem displayItem;
private final SegmentActionsListener actionsListener;
@ColorInt
private final int activeColor;
private final List<StatBlock> statBlocks;
private final boolean nightMode;
private final SegmentActionsListener actionsListener;
public BlockStatisticsAdapter(List<StatBlock> statBlocks, SegmentActionsListener actionsListener, @ColorInt int activeColor, boolean nightMode) {
this.statBlocks = statBlocks;
public BlockStatisticsAdapter(GpxDisplayItem displayItem, SegmentActionsListener actionsListener,
@ColorInt int activeColor, boolean nightMode) {
this.displayItem = displayItem;
this.actionsListener = actionsListener;
this.activeColor = activeColor;
this.nightMode = nightMode;
@ -179,7 +212,7 @@ public class GpxBlockStatisticsBuilder {
@Override
public int getItemCount() {
return statBlocks.size();
return items.size();
}
@NonNull
@ -192,46 +225,44 @@ public class GpxBlockStatisticsBuilder {
@Override
public void onBindViewHolder(BlockStatisticsViewHolder holder, int position) {
final StatBlock item = statBlocks.get(position);
final StatBlock item = items.get(position);
holder.valueText.setText(item.value);
holder.titleText.setText(item.title);
if (updateRunning) {
holder.titleText.setWidth(app.getResources().getDimensionPixelSize(R.dimen.map_route_buttons_width));
}
holder.valueText.setTextColor(activeColor);
holder.titleText.setTextColor(app.getResources().getColor(R.color.text_color_secondary_light));
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
List<GpxDisplayGroup> groups = displayHelper.getDisplayGroups(filterTypes);
GpxDisplayGroup group = null;
for (GpxDisplayGroup g : groups) {
if (g.isGeneralTrack()) {
group = g;
}
}
if (group == null && !groups.isEmpty()) {
group = groups.get(0);
}
if (group != null) {
GpxDisplayItem displayItem = group.getModifiableList().get(0);
if (displayItem != null && displayItem.analysis != null) {
ArrayList<GPXDataSetType> list = new ArrayList<>();
if (displayItem.analysis.hasElevationData || displayItem.analysis.isSpeedSpecified() || displayItem.analysis.hasSpeedData) {
if (item.firstType != null) {
list.add(item.firstType);
}
if (item.secondType != null) {
list.add(item.secondType);
}
GPXTrackAnalysis analysis = displayItem != null ? displayItem.analysis : null;
if (analysis != null) {
ArrayList<GPXDataSetType> list = new ArrayList<>();
if (analysis.hasElevationData || analysis.isSpeedSpecified() || analysis.hasSpeedData) {
if (item.firstType != null) {
list.add(item.firstType);
}
if (item.secondType != null) {
list.add(item.secondType);
}
displayItem.chartTypes = list.size() > 0 ? list.toArray(new GPXDataSetType[0]) : null;
displayItem.locationOnMap = displayItem.locationStart;
actionsListener.openAnalyzeOnMap(displayItem);
}
displayItem.chartTypes = list.size() > 0 ? list.toArray(new GPXDataSetType[0]) : null;
displayItem.locationOnMap = displayItem.locationStart;
actionsListener.openAnalyzeOnMap(displayItem);
}
}
});
setImageDrawable(holder.imageView, item.imageResId, item.imageColorId);
Drawable icon = app.getUIUtilities().getIcon(item.imageResId, item.imageColorId);
holder.imageView.setImageDrawable(icon);
AndroidUtils.setBackgroundColor(app, holder.divider, nightMode, R.color.divider_color_light, R.color.divider_color_dark);
AndroidUiHelper.updateVisibility(holder.divider, position != statBlocks.size() - 1);
AndroidUiHelper.updateVisibility(holder.divider, position != items.size() - 1);
}
public void setItems(List<StatBlock> items) {
this.items.clear();
this.items.addAll(items);
notifyDataSetChanged();
}
}

View file

@ -43,12 +43,11 @@ public class OverviewCard extends BaseCard {
private final SelectedGpxFile selectedGpxFile;
private final GpxBlockStatisticsBuilder blockStatisticsBuilder;
public OverviewCard(@NonNull MapActivity mapActivity, @NonNull TrackDisplayHelper displayHelper,
@NonNull SegmentActionsListener actionsListener, SelectedGpxFile selectedGpxFile) {
public OverviewCard(@NonNull MapActivity mapActivity, @NonNull SegmentActionsListener actionsListener, SelectedGpxFile selectedGpxFile) {
super(mapActivity);
this.actionsListener = actionsListener;
this.selectedGpxFile = selectedGpxFile;
blockStatisticsBuilder = new GpxBlockStatisticsBuilder(app, selectedGpxFile, displayHelper);
blockStatisticsBuilder = new GpxBlockStatisticsBuilder(app, selectedGpxFile);
}
@Override

View file

@ -326,7 +326,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
}
headerContainer.addView(overviewCard.getView());
} else {
overviewCard = new OverviewCard(getMapActivity(), displayHelper, this, selectedGpxFile);
overviewCard = new OverviewCard(getMapActivity(), this, selectedGpxFile);
overviewCard.setListener(this);
headerContainer.addView(overviewCard.build(getMapActivity()));
}
@ -760,7 +760,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
segment = segments.get(0);
}
}
GpxDisplayItemType[] filterTypes = new GpxDisplayItemType[] {GpxDisplayItemType.TRACK_SEGMENT};
GpxDisplayItemType[] filterTypes = new GpxDisplayItemType[]{GpxDisplayItemType.TRACK_SEGMENT};
List<GpxDisplayItem> items = TrackDisplayHelper.flatten(displayHelper.getOriginalGroups(filterTypes));
if (segment != null && !Algorithms.isEmpty(items)) {
SplitSegmentDialogFragment.showInstance(fragmentManager, displayHelper, items.get(0), segment);
@ -1013,6 +1013,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
public void openAnalyzeOnMap(GpxDisplayItem gpxItem) {
TrackDetailsMenu trackDetailsMenu = getMapActivity().getTrackDetailsMenu();
trackDetailsMenu.setGpxItem(gpxItem);
trackDetailsMenu.setSelectedGpxFile(selectedGpxFile);
trackDetailsMenu.show();
hide();
}
@ -1110,7 +1111,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
@Override
public void gpxSavingFinished(Exception errorMessage) {
if (selectedGpxFile != null) {
List<GpxDisplayGroup> groups = displayHelper.getDisplayGroups(new GpxDisplayItemType[] {GpxDisplayItemType.TRACK_SEGMENT});
List<GpxDisplayGroup> groups = displayHelper.getDisplayGroups(new GpxDisplayItemType[]{GpxDisplayItemType.TRACK_SEGMENT});
selectedGpxFile.setDisplayGroups(groups, app);
selectedGpxFile.processPoints(app);
}

View file

@ -169,6 +169,26 @@ public class TravelArticle {
return new String[]{md5.substring(0, 1), md5.substring(0, 2)};
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TravelArticle that = (TravelArticle) o;
return TravelArticleIdentifier.areLatLonEqual(that.lat, that.lon, lat, lon) &&
Algorithms.objectEquals(file, that.file) &&
Algorithms.stringsEqual(routeId, that.routeId) &&
Algorithms.stringsEqual(routeSource, that.routeSource);
}
@Override
public int hashCode() {
return Algorithms.hash(file, lat, lon, routeId, routeSource);
}
public static class TravelArticleIdentifier implements Parcelable {
@Nullable File file;
double lat;
@ -249,7 +269,7 @@ public class TravelArticle {
return Algorithms.hash(file, lat, lon, routeId, routeSource);
}
private static boolean areLatLonEqual(double lat1, double lon1, double lat2, double lon2) {
public static boolean areLatLonEqual(double lat1, double lon1, double lat2, double lon2) {
boolean latEqual = (Double.isNaN(lat1) && Double.isNaN(lat2)) || Math.abs(lat1 - lat2) < 0.00001;
boolean lonEqual = (Double.isNaN(lon1) && Double.isNaN(lon2)) || Math.abs(lon1 - lon2) < 0.00001;
return latEqual && lonEqual;

View file

@ -174,7 +174,7 @@ public class TravelDbHelper implements TravelHelper {
}
@Override
public void initializeDataToDisplay() {
public void initializeDataToDisplay(boolean resetData) {
localDataHelper.refreshCachedData();
loadPopularArticles();
}
@ -292,7 +292,7 @@ public class TravelDbHelper implements TravelHelper {
}
String LANG_WHERE = " WHERE " + ARTICLES_COL_LANG + " = '" + language + "'";
SQLiteCursor cursor = conn.rawQuery(POP_ARTICLES_TABLE_SELECT + LANG_WHERE, null);
if(cursor == null) {
if (cursor == null) {
return popularArticles;
}
// read popular articles
@ -549,13 +549,13 @@ public class TravelDbHelper implements TravelHelper {
@Override
@Nullable
public TravelArticle getArticleById(@NonNull TravelArticleIdentifier articleId, @NonNull String lang, boolean readGpx, @Nullable GpxReadCallback callback) {
public TravelArticle getArticleById(@NonNull TravelArticleIdentifier articleId, @Nullable String lang, boolean readGpx, @Nullable GpxReadCallback callback) {
TravelArticle res = null;
SQLiteConnection conn = openConnection();
String routeId = articleId.routeId;
if (conn != null && !Algorithms.isEmpty(routeId)) {
if (conn != null && !Algorithms.isEmpty(routeId) && lang != null) {
SQLiteCursor cursor = conn.rawQuery(ARTICLES_TABLE_SELECT + " WHERE " + ARTICLES_COL_TRIP_ID + " = ? AND "
+ ARTICLES_COL_LANG + " = ?", new String[] { routeId, lang });
+ ARTICLES_COL_LANG + " = ?", new String[]{routeId, lang});
if (cursor != null) {
if (cursor.moveToFirst()) {
res = readArticle(cursor);

View file

@ -3,7 +3,6 @@ package net.osmand.plus.wikivoyage.data;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import net.osmand.GPXUtilities;
import net.osmand.GPXUtilities.GPXFile;
import net.osmand.data.LatLon;
import net.osmand.data.QuadRect;
@ -18,6 +17,7 @@ public interface TravelHelper {
interface GpxReadCallback {
void onGpxFileReading();
void onGpxFileRead(@Nullable GPXFile gpxFile);
}
@ -25,7 +25,7 @@ public interface TravelHelper {
void initializeDataOnAppStartup();
void initializeDataToDisplay();
void initializeDataToDisplay(boolean resetData);
boolean isAnyTravelBookPresent();
@ -39,7 +39,7 @@ public interface TravelHelper {
Map<WikivoyageSearchResult, List<WikivoyageSearchResult>> getNavigationMap(@NonNull TravelArticle article);
@Nullable
TravelArticle getArticleById(@NonNull TravelArticleIdentifier articleId, @NonNull String lang, boolean readGpx, @Nullable GpxReadCallback callback);
TravelArticle getArticleById(@NonNull TravelArticleIdentifier articleId, @Nullable String lang, boolean readGpx, @Nullable GpxReadCallback callback);
@Nullable
TravelArticle getArticleByTitle(@NonNull String title, @NonNull String lang, boolean readGpx, @Nullable GpxReadCallback callback);

View file

@ -69,9 +69,7 @@ public class TravelObfHelper implements TravelHelper {
public static final String ROUTE_ARTICLE = "route_article";
public static final String ROUTE_ARTICLE_POINT = "route_article_point";
public static final String ROUTE_TRACK = "route_track";
public static final int POPULAR_ARTICLES_SEARCH_RADIUS = 100000;
public static final int ARTICLE_SEARCH_RADIUS = 50000;
public static final int GPX_TRACKS_SEARCH_RADIUS = 10000;
public static final int MAX_POPULAR_ARTICLES_COUNT = 30;
public static final String REF_TAG = "ref";
public static final String NAME_TAG = "name";
@ -82,6 +80,9 @@ public class TravelObfHelper implements TravelHelper {
private List<TravelArticle> popularArticles = new ArrayList<>();
private final Map<TravelArticleIdentifier, Map<String, TravelArticle>> cachedArticles = new ConcurrentHashMap<>();
private final TravelLocalDataHelper localDataHelper;
private int searchRadius = ARTICLE_SEARCH_RADIUS;
private int foundAmenitiesIndex = 0;
private final List<Pair<File, Amenity>> foundAmenities = new ArrayList<>();
public TravelObfHelper(OsmandApplication app) {
this.app = app;
@ -99,7 +100,13 @@ public class TravelObfHelper implements TravelHelper {
}
@Override
public void initializeDataToDisplay() {
public void initializeDataToDisplay(boolean resetData) {
if (resetData) {
foundAmenities.clear();
foundAmenitiesIndex = 0;
popularArticles.clear();
searchRadius = ARTICLE_SEARCH_RADIUS;
}
localDataHelper.refreshCachedData();
loadPopularArticles();
}
@ -107,38 +114,47 @@ public class TravelObfHelper implements TravelHelper {
@NonNull
public synchronized List<TravelArticle> loadPopularArticles() {
String lang = app.getLanguage();
List<TravelArticle> popularArticles = new ArrayList<>();
final List<Pair<File, Amenity>> amenities = new ArrayList<>();
final LatLon location = app.getMapViewTrackingUtilities().getMapLocation();
for (final BinaryMapIndexReader reader : getReaders()) {
try {
searchAmenity(amenities, location, reader, POPULAR_ARTICLES_SEARCH_RADIUS, -1, ROUTE_ARTICLE);
searchAmenity(amenities, location, reader, GPX_TRACKS_SEARCH_RADIUS, 15, ROUTE_TRACK);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
List<TravelArticle> popularArticles = new ArrayList<>(this.popularArticles);
if (foundAmenities.size() - foundAmenitiesIndex < MAX_POPULAR_ARTICLES_COUNT) {
final LatLon location = app.getMapViewTrackingUtilities().getMapLocation();
for (final BinaryMapIndexReader reader : getReaders()) {
try {
searchAmenity(foundAmenities, location, reader, searchRadius, -1, ROUTE_ARTICLE);
searchAmenity(foundAmenities, location, reader, searchRadius / 5, 15, ROUTE_TRACK);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
if (foundAmenities.size() > 0) {
Collections.sort(foundAmenities, new Comparator<Pair<File, Amenity>>() {
@Override
public int compare(Pair article1, Pair article2) {
Amenity amenity1 = (Amenity) article1.second;
double d1 = MapUtils.getDistance(amenity1.getLocation(), location)
/ (ROUTE_ARTICLE.equals(amenity1.getSubType()) ? 5 : 1);
Amenity amenity2 = (Amenity) article2.second;
double d2 = MapUtils.getDistance(amenity2.getLocation(), location)
/ (ROUTE_ARTICLE.equals(amenity2.getSubType()) ? 5 : 1);
return Double.compare(d1, d2);
}
});
}
searchRadius *= 2;
}
if (amenities.size() > 0) {
Collections.sort(amenities, new Comparator<Pair<File, Amenity>>() {
@Override
public int compare(Pair article1, Pair article2) {
int d1 = (int) (MapUtils.getDistance(((Amenity) article1.second).getLocation(), location));
int d2 = (int) (MapUtils.getDistance(((Amenity) article2.second).getLocation(), location));
return d1 < d2 ? -1 : (d1 == d2 ? 0 : 1);
}
});
for (Pair<File, Amenity> amenity : amenities) {
if (!Algorithms.isEmpty(amenity.second.getName(lang))) {
TravelArticle article = cacheTravelArticles(amenity.first, amenity.second, lang, false, null);
if (article != null) {
popularArticles.add(article);
if (popularArticles.size() >= MAX_POPULAR_ARTICLES_COUNT) {
break;
}
int pagesCount = popularArticles.size() / MAX_POPULAR_ARTICLES_COUNT;
while (foundAmenitiesIndex < foundAmenities.size() - 1) {
Pair<File, Amenity> amenity = foundAmenities.get(foundAmenitiesIndex);
if (!Algorithms.isEmpty(amenity.second.getName(lang))) {
TravelArticle article = cacheTravelArticles(amenity.first, amenity.second, lang, false, null);
if (article != null && !popularArticles.contains(article)) {
popularArticles.add(article);
if (popularArticles.size() >= (pagesCount + 1) * MAX_POPULAR_ARTICLES_COUNT) {
break;
}
}
}
foundAmenitiesIndex++;
}
this.popularArticles = popularArticles;
return popularArticles;
@ -616,8 +632,8 @@ public class TravelObfHelper implements TravelHelper {
}
@Override
public TravelArticle getArticleById(@NonNull TravelArticleIdentifier articleId, @NonNull String lang,
boolean readGpx, @Nullable GpxReadCallback callback) {
public TravelArticle getArticleById(@NonNull TravelArticleIdentifier articleId, @Nullable String lang,
boolean readGpx, @Nullable GpxReadCallback callback) {
TravelArticle article = getCachedArticle(articleId, lang, readGpx, callback);
if (article == null) {
article = localDataHelper.getSavedArticle(articleId.file, articleId.routeId, lang);
@ -629,8 +645,8 @@ public class TravelObfHelper implements TravelHelper {
}
@Nullable
private TravelArticle getCachedArticle(@NonNull TravelArticleIdentifier articleId, @NonNull String lang,
boolean readGpx, @Nullable GpxReadCallback callback) {
private TravelArticle getCachedArticle(@NonNull TravelArticleIdentifier articleId, @Nullable String lang,
boolean readGpx, @Nullable GpxReadCallback callback) {
TravelArticle article = null;
Map<String, TravelArticle> articles = cachedArticles.get(articleId);
if (articles != null) {

View file

@ -18,8 +18,9 @@ import net.osmand.plus.wikivoyage.explore.travelcards.OpenBetaTravelCard;
import net.osmand.plus.wikivoyage.explore.travelcards.OpenBetaTravelCard.OpenBetaTravelVH;
import net.osmand.plus.wikivoyage.explore.travelcards.StartEditingTravelCard;
import net.osmand.plus.wikivoyage.explore.travelcards.StartEditingTravelCard.StartEditingTravelVH;
import net.osmand.plus.wikivoyage.explore.travelcards.TravelButtonCard;
import net.osmand.plus.wikivoyage.explore.travelcards.TravelButtonCard.TravelButtonVH;
import net.osmand.plus.wikivoyage.explore.travelcards.TravelDownloadUpdateCard;
import net.osmand.plus.wikivoyage.explore.travelcards.TravelDownloadUpdateCard.DownloadUpdateVH;
import net.osmand.plus.wikivoyage.explore.travelcards.TravelGpxCard;
import net.osmand.plus.wikivoyage.explore.travelcards.TravelGpxCard.TravelGpxVH;
import net.osmand.plus.wikivoyage.explore.travelcards.TravelNeededMapsCard;
@ -53,12 +54,13 @@ public class ExploreRvAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
case TravelGpxCard.TYPE:
return new TravelGpxVH(inflate(parent, R.layout.wikivoyage_travel_gpx_card));
case TravelDownloadUpdateCard.TYPE:
return new DownloadUpdateVH(inflate(parent, R.layout.travel_download_update_card));
case HeaderTravelCard.TYPE:
return new HeaderTravelVH(inflate(parent, R.layout.wikivoyage_list_header));
case TravelButtonCard.TYPE:
return new TravelButtonVH(inflate(parent, R.layout.wikivoyage_button_card));
case TravelDownloadUpdateCard.TYPE:
case TravelNeededMapsCard.TYPE:
return new NeededMapsVH(inflate(parent, R.layout.travel_needed_maps_card));
@ -115,6 +117,9 @@ public class ExploreRvAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
private int getLastArticleItemIndex() {
for (int i = items.size() - 1; i > 0; i--) {
BaseTravelCard o = items.get(i);
if (o instanceof TravelButtonCard) {
return 0;
}
if (o instanceof ArticleTravelCard || o instanceof TravelGpxCard) {
return i;
}
@ -205,7 +210,7 @@ public class ExploreRvAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
if(onlyProgress) {
TravelDownloadUpdateCard dc = this.downloadCard;
if(dc != null) {
dc.updateProgresBar();
dc.updateView();
}
return;
}

View file

@ -1,7 +1,6 @@
package net.osmand.plus.wikivoyage.explore;
import android.app.Activity;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Pair;
@ -37,6 +36,7 @@ import net.osmand.plus.wikivoyage.explore.travelcards.BaseTravelCard;
import net.osmand.plus.wikivoyage.explore.travelcards.HeaderTravelCard;
import net.osmand.plus.wikivoyage.explore.travelcards.OpenBetaTravelCard;
import net.osmand.plus.wikivoyage.explore.travelcards.StartEditingTravelCard;
import net.osmand.plus.wikivoyage.explore.travelcards.TravelButtonCard;
import net.osmand.plus.wikivoyage.explore.travelcards.TravelDownloadUpdateCard;
import net.osmand.plus.wikivoyage.explore.travelcards.TravelGpxCard;
import net.osmand.plus.wikivoyage.explore.travelcards.TravelNeededMapsCard;
@ -46,6 +46,8 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import static net.osmand.plus.wikivoyage.explore.WikivoyageExploreActivity.*;
public class ExploreTabFragment extends BaseOsmAndFragment implements DownloadEvents, TravelLocalDataHelper.Listener {
private static boolean SHOW_TRAVEL_UPDATE_CARD = true;
@ -63,9 +65,7 @@ public class ExploreTabFragment extends BaseOsmAndFragment implements DownloadEv
private DownloadValidationManager downloadManager;
@Nullable
private IndexItem currentDownloadingIndexItem;
@Nullable
private IndexItem mainIndexItem;
private final List<IndexItem> mainIndexItems = new ArrayList<>();
private final List<IndexItem> neededIndexItems = new ArrayList<>();
private boolean waitForIndexes;
@ -175,17 +175,39 @@ public class ExploreTabFragment extends BaseOsmAndFragment implements DownloadEv
if (!Version.isPaidVersion(app) && !OpenBetaTravelCard.isClosed()) {
items.add(new OpenBetaTravelCard(activity, nightMode));
}
List<TravelArticle> popularArticles = app.getTravelHelper().getPopularArticles();
final List<TravelArticle> popularArticles = app.getTravelHelper().getPopularArticles();
if (!popularArticles.isEmpty()) {
items.add(new HeaderTravelCard(app, nightMode, getString(R.string.popular_destinations)));
for (TravelArticle article : popularArticles) {
if (article instanceof TravelGpx) {
items.add(new TravelGpxCard(app, nightMode, (TravelGpx) article, getActivity()));
items.add(new TravelGpxCard(app, nightMode, (TravelGpx) article, activity));
} else {
items.add(new ArticleTravelCard(app, nightMode, article, activity.getSupportFragmentManager()));
}
}
}
TravelButtonCard travelButtonCard = new TravelButtonCard(app, nightMode);
travelButtonCard.setListener(new TravelNeededMapsCard.CardListener() {
@Override
public void onPrimaryButtonClick() {
if (activity instanceof WikivoyageExploreActivity) {
new LoadWikivoyageData((WikivoyageExploreActivity) activity,false).execute();
}
}
@Override
public void onSecondaryButtonClick() {
}
@Override
public void onIndexItemClick(IndexItem item) {
}
});
items.add(travelButtonCard);
items.add(new StartEditingTravelCard(activity, nightMode));
adapter.setItems(items);
final DownloadIndexesThread downloadThread = app.getDownloadThread();
@ -199,17 +221,24 @@ public class ExploreTabFragment extends BaseOsmAndFragment implements DownloadEv
}
private void removeRedundantCards() {
if (mainIndexItem != null && mainIndexItem.isDownloaded() && !mainIndexItem.isOutdated()) {
removeDownloadUpdateCard();
}
boolean allMapsDownloaded = true;
for (IndexItem item : neededIndexItems) {
boolean allTravelGuideDownloaded = true;
for (IndexItem item : mainIndexItems) {
if (!item.isDownloaded()) {
allMapsDownloaded = false;
allTravelGuideDownloaded = false;
break;
}
}
if (allMapsDownloaded) {
if (allTravelGuideDownloaded) {
removeDownloadUpdateCard();
}
boolean neededMapsDownloaded = true;
for (IndexItem item : neededIndexItems) {
if (!item.isDownloaded()) {
neededMapsDownloaded = false;
break;
}
}
if (neededMapsDownloaded) {
removeNeededMapsCard();
}
}
@ -218,8 +247,9 @@ public class ExploreTabFragment extends BaseOsmAndFragment implements DownloadEv
new ProcessIndexItemsTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private void addIndexItemCards(IndexItem mainIndexItem, List<IndexItem> neededIndexItems) {
this.mainIndexItem = mainIndexItem;
private void addIndexItemCards(List<IndexItem> mainIndexItem, List<IndexItem> neededIndexItems) {
this.mainIndexItems.clear();
this.mainIndexItems.addAll(mainIndexItem);
this.neededIndexItems.clear();
this.neededIndexItems.addAll(neededIndexItems);
addDownloadUpdateCard();
@ -228,53 +258,58 @@ public class ExploreTabFragment extends BaseOsmAndFragment implements DownloadEv
private void addDownloadUpdateCard() {
final OsmandApplication app = getMyApplication();
if (app != null) {
final DownloadIndexesThread downloadThread = app.getDownloadThread();
boolean outdated = mainIndexItem != null && mainIndexItem.isOutdated();
boolean needsDownloading = mainIndexItem != null && !mainIndexItem.isDownloaded();
if (!app.getTravelHelper().isAnyTravelBookPresent() || needsDownloading || (outdated && SHOW_TRAVEL_UPDATE_CARD)) {
boolean showOtherMaps = false;
if (needsDownloading) {
List<IndexItem> items = downloadThread.getIndexes().getWikivoyageItems();
showOtherMaps = items != null && items.size() > 1;
if (app != null && !mainIndexItems.isEmpty() && SHOW_TRAVEL_UPDATE_CARD) {
boolean outdated = isMapsOutdated();
downloadUpdateCard = new TravelDownloadUpdateCard(app, nightMode, mainIndexItems, !outdated);
downloadUpdateCard.setListener(new TravelDownloadUpdateCard.CardListener() {
@Override
public void onPrimaryButtonClick() {
if (downloadManager != null) {
downloadManager.startDownload(getMyActivity(), getAllItemsForDownload(mainIndexItems));
adapter.updateDownloadUpdateCard(false);
}
}
downloadUpdateCard = new TravelDownloadUpdateCard(app, nightMode, !outdated);
downloadUpdateCard.setShowOtherMapsBtn(showOtherMaps);
downloadUpdateCard.setListener(new TravelDownloadUpdateCard.ClickListener() {
@Override
public void onPrimaryButtonClick() {
if (mainIndexItem != null && downloadManager != null) {
downloadManager.startDownload(getMyActivity(), mainIndexItem);
adapter.updateDownloadUpdateCard(false);
}
@Override
public void onSecondaryButtonClick() {
if (downloadUpdateCard.isDownloading()) {
app.getDownloadThread().cancelDownload(mainIndexItems);
adapter.updateDownloadUpdateCard(false);
} else {
SHOW_TRAVEL_UPDATE_CARD = false;
removeDownloadUpdateCard();
}
}
@Override
public void onSecondaryButtonClick() {
if (downloadUpdateCard.isLoading()) {
downloadThread.cancelDownload(mainIndexItem);
adapter.updateDownloadUpdateCard(false);
} else if (!downloadUpdateCard.isDownload()) {
SHOW_TRAVEL_UPDATE_CARD = false;
removeDownloadUpdateCard();
} else if (downloadUpdateCard.isShowOtherMapsBtn()) {
Activity activity = getActivity();
if (activity != null) {
Intent newIntent = new Intent(activity,
((OsmandApplication) activity.getApplication()).getAppCustomization().getDownloadActivity());
newIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
activity.startActivity(newIntent);
}
@Override
public void onIndexItemClick(IndexItem item) {
if (item.getType() == DownloadActivityType.WIKIPEDIA_FILE && !Version.isPaidVersion(app)) {
FragmentManager fm = getFragmentManager();
if (fm != null) {
ChoosePlanDialogFragment.showWikipediaInstance(fm);
}
} else {
DownloadIndexesThread downloadThread = app.getDownloadThread();
if (downloadThread.isDownloading(item)) {
downloadThread.cancelDownload(item);
} else if (!item.isDownloaded() && downloadManager != null) {
downloadManager.startDownload(getMyActivity(), item);
}
adapter.updateDownloadUpdateCard(false);
}
});
downloadUpdateCard.setIndexItem(mainIndexItem);
adapter.addDownloadUpdateCard(downloadUpdateCard);
}
});
adapter.addDownloadUpdateCard(downloadUpdateCard);
}
}
private boolean isMapsOutdated() {
for (IndexItem indexItem : mainIndexItems) {
if (indexItem.isOutdated()) {
return true;
}
}
return false;
}
private void addNeededMapsCard() {
@ -285,7 +320,7 @@ public class ExploreTabFragment extends BaseOsmAndFragment implements DownloadEv
@Override
public void onPrimaryButtonClick() {
if (downloadManager != null) {
downloadManager.startDownload(getMyActivity(), getAllItemsForDownload());
downloadManager.startDownload(getMyActivity(), getAllItemsForDownload(neededIndexItems));
adapter.updateNeededMapsCard(false);
}
}
@ -323,10 +358,10 @@ public class ExploreTabFragment extends BaseOsmAndFragment implements DownloadEv
}
}
private IndexItem[] getAllItemsForDownload() {
private IndexItem[] getAllItemsForDownload(List<IndexItem> indexItems) {
boolean paidVersion = Version.isPaidVersion(getMyApplication());
ArrayList<IndexItem> res = new ArrayList<>();
for (IndexItem item : neededIndexItems) {
for (IndexItem item : indexItems) {
if (!item.isDownloaded() && (paidVersion || item.getType() != DownloadActivityType.WIKIPEDIA_FILE)) {
res.add(item);
}
@ -344,7 +379,7 @@ public class ExploreTabFragment extends BaseOsmAndFragment implements DownloadEv
neededMapsCard = null;
}
private static class ProcessIndexItemsTask extends AsyncTask<Void, Void, Pair<IndexItem, List<IndexItem>>> {
private static class ProcessIndexItemsTask extends AsyncTask<Void, Void, Pair<List<IndexItem>, List<IndexItem>>> {
private static final DownloadActivityType[] types = new DownloadActivityType[]{
DownloadActivityType.NORMAL_FILE,
@ -354,40 +389,41 @@ public class ExploreTabFragment extends BaseOsmAndFragment implements DownloadEv
private final OsmandApplication app;
private final WeakReference<ExploreTabFragment> weakFragment;
private final String fileName;
ProcessIndexItemsTask(ExploreTabFragment fragment) {
app = fragment.getMyApplication();
weakFragment = new WeakReference<>(fragment);
fileName = app != null ? app.getTravelHelper().getWikivoyageFileName() : null;
}
@Override
protected Pair<IndexItem, List<IndexItem>> doInBackground(Void... voids) {
if (fileName != null) {
IndexItem mainItem = app.getDownloadThread().getIndexes().getWikivoyageItem(fileName);
List<IndexItem> neededItems = new ArrayList<>();
for (TravelArticle article : app.getTravelHelper().getBookmarksHelper().getSavedArticles()) {
LatLon latLon = new LatLon(article.getLat(), article.getLon());
try {
for (DownloadActivityType type : types) {
IndexItem item = DownloadResources.findSmallestIndexItemAt(app, latLon, type);
if (item != null && !item.isDownloaded() && !neededItems.contains(item)) {
neededItems.add(item);
}
}
} catch (IOException e) {
// ignore
protected Pair<List<IndexItem>, List<IndexItem>> doInBackground(Void... voids) {
List<IndexItem> mainItems = new ArrayList<>();
List<IndexItem> allWikivoyageItems = app.getDownloadThread().getIndexes().getWikivoyageItems();
if (allWikivoyageItems != null) {
for (IndexItem item : allWikivoyageItems) {
if (!item.isDownloaded() && !mainItems.contains(item)) {
mainItems.add(item);
}
}
return new Pair<>(mainItem, neededItems);
}
return null;
List<IndexItem> neededItems = new ArrayList<>();
for (TravelArticle article : app.getTravelHelper().getBookmarksHelper().getSavedArticles()) {
LatLon latLon = new LatLon(article.getLat(), article.getLon());
try {
for (DownloadActivityType type : types) {
IndexItem item = DownloadResources.findSmallestIndexItemAt(app, latLon, type);
if (item != null && !item.isDownloaded() && !neededItems.contains(item)) {
neededItems.add(item);
}
}
} catch (IOException e) {
// ignore
}
}
return new Pair<>(mainItems, neededItems);
}
@Override
protected void onPostExecute(Pair<IndexItem, List<IndexItem>> res) {
protected void onPostExecute(Pair<List<IndexItem>, List<IndexItem>> res) {
ExploreTabFragment fragment = weakFragment.get();
if (res != null && fragment != null && fragment.isResumed()) {
fragment.addIndexItemCards(res.first, res.second);

View file

@ -326,12 +326,12 @@ public class WikivoyageExploreActivity extends TabActivity implements DownloadEv
public void onFinish(AppInitializer init) {
WikivoyageExploreActivity activity = activityRef.get();
if (AndroidUtils.isActivityNotDestroyed(activity)) {
new LoadWikivoyageData(activity).execute();
new LoadWikivoyageData(activity,true).execute();
}
}
});
} else {
new LoadWikivoyageData(this).execute();
new LoadWikivoyageData(this,true).execute();
}
}
@ -380,19 +380,21 @@ public class WikivoyageExploreActivity extends TabActivity implements DownloadEv
updateFragments();
}
private static class LoadWikivoyageData extends AsyncTask<Void, Void, Void> {
public static class LoadWikivoyageData extends AsyncTask<Void, Void, Void> {
private final WeakReference<WikivoyageExploreActivity> activityRef;
private final TravelHelper travelHelper;
private final boolean resetData;
LoadWikivoyageData(WikivoyageExploreActivity activity) {
LoadWikivoyageData(WikivoyageExploreActivity activity, boolean resetData) {
travelHelper = activity.getMyApplication().getTravelHelper();
activityRef = new WeakReference<>(activity);
this.resetData = resetData;
}
@Override
protected Void doInBackground(Void... params) {
travelHelper.initializeDataToDisplay();
travelHelper.initializeDataToDisplay(resetData);
return null;
}

View file

@ -0,0 +1,56 @@
package net.osmand.plus.wikivoyage.explore.travelcards;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.UiUtilities;
import net.osmand.plus.wikivoyage.explore.travelcards.TravelNeededMapsCard.CardListener;
public class TravelButtonCard extends BaseTravelCard {
public static final int TYPE = 5;
private CardListener listener;
public TravelButtonCard(OsmandApplication app, boolean nightMode) {
super(app, nightMode);
}
public void setListener(CardListener listener) {
this.listener = listener;
}
@Override
public void bindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder) {
if (viewHolder instanceof TravelButtonVH) {
final TravelButtonVH holder = (TravelButtonVH) viewHolder;
UiUtilities.setupDialogButton(nightMode, holder.button, UiUtilities.DialogButtonType.SECONDARY, R.string.show_more);
holder.button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (listener != null) {
listener.onPrimaryButtonClick();
}
}
});
}
}
public static class TravelButtonVH extends RecyclerView.ViewHolder {
final View button;
public TravelButtonVH(View itemView) {
super(itemView);
button = itemView.findViewById(R.id.button);
}
}
@Override
public int getCardType() {
return TYPE;
}
}

View file

@ -1,257 +1,47 @@
package net.osmand.plus.wikivoyage.explore.travelcards;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.recyclerview.widget.RecyclerView;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.download.IndexItem;
import java.lang.ref.WeakReference;
import java.text.DateFormat;
import java.util.List;
public class TravelDownloadUpdateCard extends BaseTravelCard {
public class TravelDownloadUpdateCard extends TravelNeededMapsCard {
public static final int TYPE = 50;
private boolean download;
private boolean showOtherMapsBtn;
private WeakReference<DownloadUpdateVH> ref;
private final boolean download;
private ClickListener listener;
@Nullable
private IndexItem indexItem;
private DateFormat dateFormat;
public boolean isDownload() {
return download;
}
public boolean isShowOtherMapsBtn() {
return showOtherMapsBtn;
}
public void setShowOtherMapsBtn(boolean showOtherMapsBtn) {
this.showOtherMapsBtn = showOtherMapsBtn;
}
public void setListener(ClickListener listener) {
this.listener = listener;
}
public void setIndexItem(@Nullable IndexItem indexItem) {
this.indexItem = indexItem;
}
public TravelDownloadUpdateCard(OsmandApplication app, boolean nightMode, boolean download) {
super(app, nightMode);
public TravelDownloadUpdateCard(@NonNull OsmandApplication app, boolean nightMode, @NonNull List<IndexItem> items,
boolean download) {
super(app, nightMode, items);
this.download = download;
dateFormat = android.text.format.DateFormat.getMediumDateFormat(app);
}
public int getTitle() {
if (isDownloading()) {
return R.string.shared_string_downloading;
}
return download ? R.string.download_file : R.string.update_is_available;
}
@Override
public void bindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder) {
if (viewHolder instanceof DownloadUpdateVH) {
boolean loading = isLoading();
DownloadUpdateVH holder = (DownloadUpdateVH) viewHolder;
this.ref = new WeakReference<TravelDownloadUpdateCard.DownloadUpdateVH>(holder);
holder.title.setText(getTitle(loading));
holder.icon.setImageResource(getIconRes());
holder.description.setText(getDescription());
if (indexItem == null) {
holder.fileDataContainer.setVisibility(View.GONE);
} else {
holder.fileDataContainer.setVisibility(View.VISIBLE);
holder.fileIcon.setImageDrawable(getFileIcon());
holder.fileTitle.setText(getFileTitle());
holder.fileDescription.setText(getFileDescription());
holder.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
updateProgressBar(holder);
}
boolean primaryBtnVisible = updatePrimaryButton(holder, loading);
boolean secondaryBtnVisible = updateSecondaryButton(holder, loading);
holder.buttonsDivider.setVisibility(primaryBtnVisible && secondaryBtnVisible ? View.VISIBLE : View.GONE);
}
}
public void updateProgresBar() {
if(ref != null) {
DownloadUpdateVH holder = ref.get();
if (holder != null && holder.itemView.isShown()) {
updateProgressBar(holder);
}
public int getDescription() {
if (!isInternetAvailable()) {
return R.string.no_index_file_to_download;
}
return download ? R.string.travel_card_download_descr : R.string.travel_card_update_descr;
}
private void updateProgressBar(DownloadUpdateVH holder) {
if (isLoadingInProgress()) {
int progress = app.getDownloadThread().getCurrentDownloadingItemProgress();
holder.progressBar.setProgress(progress < 0 ? 0 : progress);
} else {
holder.progressBar.setProgress(0);
}
@Override
public int getIconRes() {
return download ? R.drawable.travel_card_download_icon : R.drawable.travel_card_update_icon;
}
@Override
public int getCardType() {
return TYPE;
}
@NonNull
private String getTitle(boolean loading) {
if (loading) {
return app.getString(R.string.shared_string_downloading);
}
return app.getString(download ? R.string.download_file : R.string.update_is_available);
}
private int getIconRes() {
return download ? R.drawable.travel_card_download_icon : R.drawable.travel_card_update_icon;
}
@NonNull
private String getDescription() {
if (!isInternetAvailable()) {
return app.getString(R.string.no_index_file_to_download);
}
return app.getString(download ? R.string.travel_card_download_descr : R.string.travel_card_update_descr);
}
@NonNull
private String getFileTitle() {
return indexItem == null ? "" : indexItem.getVisibleName(app, app.getRegions(), false);
}
@NonNull
private String getFileDescription() {
StringBuilder sb = new StringBuilder();
if (indexItem != null) {
sb.append(app.getString(R.string.file_size_in_mb, indexItem.getArchiveSizeMB()));
sb.append("");
sb.append(indexItem.getRemoteDate(dateFormat));
}
return sb.toString();
}
private Drawable getFileIcon() {
return getActiveIcon(R.drawable.ic_action_read_article);
}
/**
* @return true if button is visible, false otherwise.
*/
private boolean updateSecondaryButton(DownloadUpdateVH vh, boolean loading) {
if (loading || !download || showOtherMapsBtn) {
vh.secondaryBtnContainer.setVisibility(View.VISIBLE);
vh.secondaryBtn.setText(getSecondaryBtnTextId(loading));
vh.secondaryBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) {
listener.onSecondaryButtonClick();
}
}
});
return true;
}
vh.secondaryBtnContainer.setVisibility(View.GONE);
return false;
}
@StringRes
private int getSecondaryBtnTextId(boolean loading) {
if (loading) {
return R.string.shared_string_cancel;
}
if (!download) {
return R.string.later;
}
return R.string.download_select_map_types;
}
/**
* @return true if button is visible, false otherwise.
*/
private boolean updatePrimaryButton(DownloadUpdateVH vh, boolean loading) {
if (!loading) {
boolean enabled = isInternetAvailable();
vh.primaryBtnContainer.setVisibility(View.VISIBLE);
vh.primaryBtnContainer.setBackgroundResource(getPrimaryBtnBgRes(enabled));
vh.primaryButton.setTextColor(getResolvedColor(getPrimaryBtnTextColorRes(enabled)));
vh.primaryButton.setEnabled(enabled);
vh.primaryButton.setText(download ? R.string.shared_string_download : R.string.shared_string_update);
vh.primaryButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) {
listener.onPrimaryButtonClick();
}
}
});
return true;
}
vh.primaryBtnContainer.setVisibility(View.GONE);
return false;
}
public boolean isLoading() {
return indexItem != null && app.getDownloadThread().isDownloading(indexItem);
}
private boolean isLoadingInProgress() {
IndexItem current = app.getDownloadThread().getCurrentDownloadingItem();
return indexItem != null && current != null && indexItem == current;
}
public interface ClickListener {
void onPrimaryButtonClick();
void onSecondaryButtonClick();
}
public static class DownloadUpdateVH extends RecyclerView.ViewHolder {
final TextView title;
final ImageView icon;
final TextView description;
final View fileDataContainer;
final ImageView fileIcon;
final TextView fileTitle;
final TextView fileDescription;
final ProgressBar progressBar;
final View secondaryBtnContainer;
final TextView secondaryBtn;
final View buttonsDivider;
final View primaryBtnContainer;
final TextView primaryButton;
@SuppressWarnings("RedundantCast")
public DownloadUpdateVH(View itemView) {
super(itemView);
title = (TextView) itemView.findViewById(R.id.title);
icon = (ImageView) itemView.findViewById(R.id.icon);
description = (TextView) itemView.findViewById(R.id.description);
fileDataContainer = itemView.findViewById(R.id.file_data_container);
fileIcon = (ImageView) itemView.findViewById(R.id.file_icon);
fileTitle = (TextView) itemView.findViewById(R.id.file_title);
fileDescription = (TextView) itemView.findViewById(R.id.file_description);
progressBar = (ProgressBar) itemView.findViewById(R.id.progress_bar);
secondaryBtnContainer = itemView.findViewById(R.id.secondary_btn_container);
secondaryBtn = (TextView) itemView.findViewById(R.id.secondary_button);
buttonsDivider = itemView.findViewById(R.id.buttons_divider);
primaryBtnContainer = itemView.findViewById(R.id.primary_btn_container);
primaryButton = (TextView) itemView.findViewById(R.id.primary_button);
}
}
}

View file

@ -9,7 +9,9 @@ import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.recyclerview.widget.RecyclerView;
import net.osmand.plus.OsmandApplication;
@ -60,8 +62,12 @@ public class TravelNeededMapsCard extends BaseTravelCard {
if (viewHolder instanceof NeededMapsVH) {
NeededMapsVH holder = (NeededMapsVH) viewHolder;
ref = new WeakReference<NeededMapsVH>(holder);
holder.description.setText(isInternetAvailable()
? R.string.maps_you_need_descr : R.string.no_index_file_to_download);
holder.title.setText(getTitle());
holder.description.setText(getDescription());
int iconRes = getIconRes();
if (iconRes > 0) {
holder.icon.setImageResource(iconRes);
}
adjustChildCount(holder.itemsContainer);
updateView(holder);
@ -71,7 +77,23 @@ public class TravelNeededMapsCard extends BaseTravelCard {
holder.buttonsDivider.setVisibility(primaryBtnVisible && secondaryBtnVisible ? View.VISIBLE : View.GONE);
}
}
@StringRes
public int getTitle() {
return R.string.maps_you_need;
}
@StringRes
public int getDescription() {
return isInternetAvailable()
? R.string.maps_you_need_descr : R.string.no_index_file_to_download;
}
@DrawableRes
public int getIconRes() {
return 0;
}
public void updateView() {
if (ref != null) {
NeededMapsVH holder = ref.get();
@ -227,7 +249,9 @@ public class TravelNeededMapsCard extends BaseTravelCard {
public static class NeededMapsVH extends RecyclerView.ViewHolder {
final TextView title;
final TextView description;
final ImageView icon;
final LinearLayout itemsContainer;
final View secondaryBtnContainer;
final TextView secondaryBtn;
@ -238,7 +262,9 @@ public class TravelNeededMapsCard extends BaseTravelCard {
@SuppressWarnings("RedundantCast")
public NeededMapsVH(View itemView) {
super(itemView);
title = (TextView) itemView.findViewById(R.id.title);
description = (TextView) itemView.findViewById(R.id.description);
icon = (ImageView) itemView.findViewById(R.id.icon);
itemsContainer = (LinearLayout) itemView.findViewById(R.id.items_container);
secondaryBtnContainer = itemView.findViewById(R.id.secondary_btn_container);
secondaryBtn = (TextView) itemView.findViewById(R.id.secondary_button);