diff --git a/.github/workflows/build-beta.yml b/.github/workflows/build-beta.yml index 05ae3b6..3939255 100644 --- a/.github/workflows/build-beta.yml +++ b/.github/workflows/build-beta.yml @@ -83,29 +83,9 @@ jobs: - name: Clone upstream run: task fetch - - name: Restore Boost from cache - id: boost - uses: actions/cache/restore@v6 - with: - path: | - .work/src/3party/boost - .work/src/.git/modules/3party/boost - .work/boost-stamp - key: boost-${{ hashFiles('upstream.tag') }} - - name: Check out upstream and apply patches run: task init SUBMODULE_JOBS=$(( $(nproc) * 20 )) - - name: Save Boost to cache - if: github.event_name != 'pull_request' && steps.boost.outputs.cache-hit != 'true' - uses: actions/cache/save@v6 - with: - path: | - .work/src/3party/boost - .work/src/.git/modules/3party/boost - .work/boost-stamp - key: boost-${{ hashFiles('upstream.tag') }} - - name: Restore signing keys env: RELEASE_KEYSTORE: ${{ secrets.RELEASE_KEYSTORE }} diff --git a/Taskfile.yml b/Taskfile.yml index 162ca9b..5806b19 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -92,10 +92,9 @@ tasks: # --depth 1 clone cannot answer it. blob:none keeps the whole commit graph and fetches file # contents on demand. # --branch lands HEAD on the pinned tag rather than the default branch. src:checkout would - # switch there anyway, but doing it here means a restored 3party/boost cache is already at the - # commit HEAD records - off the default branch it would read as a dirty submodule the moment - # upstream moves boost, and trip src:checkout's cleanliness precondition. All refs are still - # fetched, so `git log ..` keeps working. + # switch there anyway, but starting out on the commit it expects keeps the submodules reading + # clean, so its cleanliness precondition holds on a fresh clone. All refs are still fetched, + # so `git log ..` keeps working. - git clone --filter=blob:none --branch {{.UPSTREAM_TAG}} {{.UPSTREAM_URL}} {{.SRC_DIR}} src:checkout: @@ -115,23 +114,10 @@ tasks: - git reset --hard {{.UPSTREAM_TAG}} - git branch -D work || true - git switch -c work + # Since upstream swapped the boost submodule for the pre-generated boost_headers one + # (organicmaps#13316) this is the whole setup: there is no header tree left to bootstrap, and + # the configure.sh that used to do it is gone. - git submodule update {{.SUBMODULE_FLAGS}} - - task: src:bootstrap-boost - - src:bootstrap-boost: - desc: Regenerate the boost header tree. No-op unless the boost submodule moved. - internal: true - dir: '{{.SRC_DIR}}' - vars: - BOOST_SHA: - sh: git -C {{.SRC_DIR}} rev-parse HEAD:3party/boost 2>/dev/null || echo unknown - status: - - test -d 3party/boost/boost - - test "$(cat {{.WORK_DIR}}/boost-stamp 2>/dev/null)" = "{{.BOOST_SHA}}" - cmds: - - echo "Bootstrapping boost ({{.BOOST_SHA}})..." - - ./configure.sh - - echo "{{.BOOST_SHA}}" > {{.WORK_DIR}}/boost-stamp apply-patches: desc: Apply patches/*.patch onto the checked-out upstream tag. @@ -156,10 +142,9 @@ tasks: - git am --keep-non-patch --reject --whitespace=fix --interactive {{.PATCHES_DIR}}/*.patch init: - desc: Set up .work/src from scratch - clone, check out the pinned tag, apply patches, bootstrap boost. + desc: Set up .work/src from scratch - clone, check out the pinned tag, apply patches. deps: [apply-patches] cmds: - # boost is handled by src:bootstrap-boost inside src:checkout, so there is nothing left to do. - 'echo "Ready: {{.SRC_DIR}} is on branch work, patched to {{.UPSTREAM_TAG}}."' generate-patches: diff --git a/patches/0001-routing-Online-car-router-backed-by-Google.patch b/patches/0001-routing-Online-car-router-backed-by-Google.patch index 4dcc5e2..1bd1818 100644 --- a/patches/0001-routing-Online-car-router-backed-by-Google.patch +++ b/patches/0001-routing-Online-car-router-backed-by-Google.patch @@ -128,10 +128,10 @@ index 5824361dd7..1093d2fb55 100644 private void updateVoiceInstructionsPrefsSummary() diff --git a/android/app/src/main/res/layout/routing_plan_header.xml b/android/app/src/main/res/layout/routing_plan_header.xml -index dacf94b374..6c6dd65d0b 100644 +index 2bbf7b4e23..08cabc8914 100644 --- a/android/app/src/main/res/layout/routing_plan_header.xml +++ b/android/app/src/main/res/layout/routing_plan_header.xml -@@ -49,6 +49,11 @@ +@@ -48,6 +48,11 @@ android:id="@+id/ruler" style="@style/MwmWidget.RoutingToolbarCell" android:button="@drawable/ic_ruler_route" /> @@ -156,10 +156,10 @@ index 99fa8cc422..e70c11766c 100644 Information Transliteration diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml -index b0c9754ddf..544beb6524 100644 +index be4aab7ecf..25f16ccaab 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml -@@ -354,6 +354,10 @@ +@@ -350,6 +350,10 @@ Light from dawn till dusk Gift free maps with no ads to millions of users! @@ -273,10 +273,10 @@ index 0000000000..9df76770e8 + android:strokeColor="#000"/> + diff --git a/data/strings/strings.txt b/data/strings/strings.txt -index d8f9003616..e284e48076 100644 +index aae7e3bb69..5e9b088373 100644 --- a/data/strings/strings.txt +++ b/data/strings/strings.txt -@@ -11928,6 +11928,16 @@ vi = yer i̇mi +@@ -12050,6 +12050,16 @@ vi = yer i̇mi zh-Hans = 书签 zh-Hant = 書籤 @@ -2047,10 +2047,10 @@ index 0000000000..1f860a0584 +double MatchedLengthRatio(std::vector const & path, std::vector const & guide); +} // namespace routing diff --git a/libs/routing/route.hpp b/libs/routing/route.hpp -index 604a717c23..807d59f62a 100644 +index 8fa56fbe31..d5d8dcf836 100644 --- a/libs/routing/route.hpp +++ b/libs/routing/route.hpp -@@ -422,6 +422,12 @@ public: +@@ -425,6 +425,12 @@ public: explicit Route(RouteBase const & base) : RouteBase(base) { RebuildFollowedPolyline(); } Route(Route const & rhs) = default; diff --git a/patches/0002-search-Online-place-search-backed-by-Google.patch b/patches/0002-search-Online-place-search-backed-by-Google.patch index cf0ba76..0cdbfc4 100644 --- a/patches/0002-search-Online-place-search-backed-by-Google.patch +++ b/patches/0002-search-Online-place-search-backed-by-Google.patch @@ -22,7 +22,7 @@ nothing is requested. .../search/SearchPageViewModel.java | 18 + .../layout/toolbar_search_controls_sheet.xml | 9 + android/app/src/main/res/values/strings.xml | 4 + - .../organicmaps/sdk/search/SearchEngine.cpp | 45 ++ + .../organicmaps/sdk/search/SearchEngine.cpp | 46 ++ .../organicmaps/sdk/search/SearchEngine.java | 23 + data/strings/strings.txt | 10 + libs/map/CMakeLists.txt | 2 + @@ -33,13 +33,13 @@ nothing is requested. libs/map/map_tests/CMakeLists.txt | 1 + libs/map/map_tests/google_places_tests.cpp | 261 ++++++++ libs/map/place_page_info.hpp | 11 + - 15 files changed, 1209 insertions(+), 3 deletions(-) + 15 files changed, 1210 insertions(+), 3 deletions(-) create mode 100644 libs/map/google_places.cpp create mode 100644 libs/map/google_places.hpp create mode 100644 libs/map/map_tests/google_places_tests.cpp diff --git a/android/app/src/main/java/app/organicmaps/search/SearchFragment.java b/android/app/src/main/java/app/organicmaps/search/SearchFragment.java -index 7b6f7f0684..d3c726864f 100644 +index 30633d258b..9619d3c9f8 100644 --- a/android/app/src/main/java/app/organicmaps/search/SearchFragment.java +++ b/android/app/src/main/java/app/organicmaps/search/SearchFragment.java @@ -2,6 +2,7 @@ package app.organicmaps.search; @@ -116,7 +116,7 @@ index 7b6f7f0684..d3c726864f 100644 if (!started) { stopSearch(); -@@ -852,11 +866,46 @@ public class SearchFragment extends Fragment implements SearchListener, Categori +@@ -856,11 +870,46 @@ public class SearchFragment extends Fragment implements SearchListener, Categori private class ToolbarController extends SearchToolbarController { @@ -225,10 +225,10 @@ index 68aaf11cb5..8ce33ad6a9 100644 Google Maps API key Required to use the Google routing mode @@ -240,7 +240,7 @@ index 544beb6524..c77dc7bb07 100644 When following the route, please keep in mind: diff --git a/android/sdk/src/main/cpp/app/organicmaps/sdk/search/SearchEngine.cpp b/android/sdk/src/main/cpp/app/organicmaps/sdk/search/SearchEngine.cpp -index 66ce6cd036..a514fc5f37 100644 +index 02bb7f2784..ff3e5d0889 100644 --- a/android/sdk/src/main/cpp/app/organicmaps/sdk/search/SearchEngine.cpp +++ b/android/sdk/src/main/cpp/app/organicmaps/sdk/search/SearchEngine.cpp @@ -4,6 +4,7 @@ @@ -279,7 +279,7 @@ index 66ce6cd036..a514fc5f37 100644 } // namespace extern "C" -@@ -313,6 +328,29 @@ JNIEXPORT jboolean Java_app_organicmaps_sdk_search_SearchEngine_nativeRunInterac +@@ -314,6 +329,30 @@ JNIEXPORT jboolean Java_app_organicmaps_sdk_search_SearchEngine_nativeRunInterac return JNI_TRUE; } @@ -299,7 +299,8 @@ index 66ce6cd036..a514fc5f37 100644 + // isMapAndTable is false as well - there is no viewport search to poke in this mode. + auto const started = fr->GetGooglePlaces().Autocomplete( + jni::ToNativeString(env, bytes), jni::ToNativeString(env, lang), origin, -+ std::bind(&OnResults, _1, _2, timestamp, false /* isMapAndTable */, false /* hasPosition */, 0.0, 0.0)); ++ std::bind(&OnResults, std::placeholders::_1, std::placeholders::_2, timestamp, false /* isMapAndTable */, ++ false /* hasPosition */, 0.0, 0.0)); + + if (started) + g_queryTimestamp = timestamp; @@ -309,7 +310,7 @@ index 66ce6cd036..a514fc5f37 100644 JNIEXPORT void Java_app_organicmaps_sdk_search_SearchEngine_nativeRunSearchMaps(JNIEnv * env, jclass clazz, jbyteArray bytes, jstring lang, jlong timestamp) -@@ -341,6 +379,8 @@ JNIEXPORT void Java_app_organicmaps_sdk_search_SearchEngine_nativeShowResult(JNI +@@ -343,6 +382,8 @@ JNIEXPORT void Java_app_organicmaps_sdk_search_SearchEngine_nativeShowResult(JNI { if (index < 0 || index >= static_cast(g_results.GetCount())) return; @@ -318,7 +319,7 @@ index 66ce6cd036..a514fc5f37 100644 g_framework->NativeFramework()->ShowSearchResult(g_results[index]); } -@@ -352,6 +392,8 @@ JNIEXPORT void Java_app_organicmaps_sdk_search_SearchEngine_nativeSelectResult(J +@@ -354,6 +395,8 @@ JNIEXPORT void Java_app_organicmaps_sdk_search_SearchEngine_nativeSelectResult(J auto const mode = g_framework->GetMyPositionMode(); if (mode == location::Follow || mode == location::FollowAndRotate) g_framework->NativeFramework()->StopLocationFollow(); @@ -327,7 +328,7 @@ index 66ce6cd036..a514fc5f37 100644 g_framework->NativeFramework()->SelectSearchResult(g_results[index], true /* animation */); } -@@ -363,5 +405,8 @@ JNIEXPORT void Java_app_organicmaps_sdk_search_SearchEngine_nativeCancelInteract +@@ -371,5 +414,8 @@ JNIEXPORT void Java_app_organicmaps_sdk_search_SearchEngine_nativeCancelInteract JNIEXPORT void Java_app_organicmaps_sdk_search_SearchEngine_nativeCancelAllSearches(JNIEnv * env, jclass clazz) { g_framework->NativeFramework()->GetSearchAPI().CancelAllSearches(); @@ -337,7 +338,7 @@ index 66ce6cd036..a514fc5f37 100644 } } // extern "C" diff --git a/android/sdk/src/main/java/app/organicmaps/sdk/search/SearchEngine.java b/android/sdk/src/main/java/app/organicmaps/sdk/search/SearchEngine.java -index af9f9962a8..adad0436fd 100644 +index 332321ab5e..6b8eadcf2a 100644 --- a/android/sdk/src/main/java/app/organicmaps/sdk/search/SearchEngine.java +++ b/android/sdk/src/main/java/app/organicmaps/sdk/search/SearchEngine.java @@ -149,6 +149,23 @@ public enum SearchEngine implements SearchListener, MapSearchListener, @@ -364,7 +365,7 @@ index af9f9962a8..adad0436fd 100644 @MainThread public static void searchMaps(@NonNull Context context, @NonNull String query, long timestamp) { -@@ -249,6 +266,12 @@ public enum SearchEngine implements SearchListener, MapSearchListener, +@@ -258,6 +275,12 @@ public enum SearchEngine implements SearchListener, MapSearchListener, long timestamp, boolean isMapAndTable, boolean hasLocation, double lat, double lon); @@ -378,10 +379,10 @@ index af9f9962a8..adad0436fd 100644 * @param bytes utf-8 formatted query bytes */ diff --git a/data/strings/strings.txt b/data/strings/strings.txt -index e284e48076..8991dd4f1d 100644 +index 5e9b088373..e0b8f07c69 100644 --- a/data/strings/strings.txt +++ b/data/strings/strings.txt -@@ -11938,6 +11938,16 @@ comment = Settings: explanation under the Google Maps API key input field +@@ -12060,6 +12060,16 @@ comment = Settings: explanation under the Google Maps API key input field tags = android-app en = Required to use the Google routing mode @@ -399,7 +400,7 @@ index e284e48076..8991dd4f1d 100644 [dialog_routing_disclaimer_title] diff --git a/libs/map/CMakeLists.txt b/libs/map/CMakeLists.txt -index 4c656d1f73..c44e43df9e 100644 +index 32613a5b61..420ae38df5 100644 --- a/libs/map/CMakeLists.txt +++ b/libs/map/CMakeLists.txt @@ -25,6 +25,8 @@ set(SRC @@ -412,10 +413,10 @@ index 4c656d1f73..c44e43df9e 100644 gps_track_collection.hpp gps_track_filter.cpp diff --git a/libs/map/framework.cpp b/libs/map/framework.cpp -index dbe4f06a3a..14f37f4032 100644 +index ec1e788fa3..dd7dc4de13 100644 --- a/libs/map/framework.cpp +++ b/libs/map/framework.cpp -@@ -1420,6 +1420,27 @@ void Framework::SelectSearchResult(search::Result const & result, bool animation +@@ -1530,6 +1530,27 @@ void Framework::SelectSearchResult(search::Result const & result, bool animation ActivateMapSelection(); } @@ -444,7 +445,7 @@ index dbe4f06a3a..14f37f4032 100644 { GetSearchAPI().CancelAllSearches(); diff --git a/libs/map/framework.hpp b/libs/map/framework.hpp -index 8c494e0d57..106cb4b352 100644 +index c96f5642e0..eeaa9c5b38 100644 --- a/libs/map/framework.hpp +++ b/libs/map/framework.hpp @@ -4,6 +4,7 @@ @@ -455,7 +456,7 @@ index 8c494e0d57..106cb4b352 100644 #include "map/isolines_manager.hpp" #include "map/mwm_url.hpp" #include "map/place_page_info.hpp" -@@ -157,6 +158,9 @@ protected: +@@ -158,6 +159,9 @@ protected: std::unique_ptr m_searchAPI; @@ -465,7 +466,7 @@ index 8c494e0d57..106cb4b352 100644 ScreenBase m_currentModelView; m2::RectD m_visibleViewport; -@@ -267,6 +271,7 @@ public: +@@ -268,6 +272,7 @@ public: DataSource const & GetDataSource() const { return m_featuresFetcher.GetDataSource(); } SearchAPI & GetSearchAPI(); @@ -473,7 +474,7 @@ index 8c494e0d57..106cb4b352 100644 /// @name Bookmarks, Tracks and other UserMarks /// Scans and loads all kml files with bookmarks in WritableDir. -@@ -494,6 +499,10 @@ public: +@@ -503,6 +508,10 @@ public: // Moves viewport to the search result and taps on it. void SelectSearchResult(search::Result const & res, bool animation); @@ -1240,7 +1241,7 @@ index 0000000000..50bbfaaa09 +PlaceDetails ParsePlaceDetailsResponse(std::string const & json); +} // namespace search diff --git a/libs/map/map_tests/CMakeLists.txt b/libs/map/map_tests/CMakeLists.txt -index 0157d38cd3..6961e5a940 100644 +index 6abca4c1aa..1cbae92ef4 100644 --- a/libs/map/map_tests/CMakeLists.txt +++ b/libs/map/map_tests/CMakeLists.txt @@ -11,6 +11,7 @@ set(SRC @@ -1519,7 +1520,7 @@ index 0000000000..f3ac244ee9 +} +} // namespace google_places_tests diff --git a/libs/map/place_page_info.hpp b/libs/map/place_page_info.hpp -index 0a7d87b0dc..ea87a41839 100644 +index 431b46d1f9..eb989445ed 100644 --- a/libs/map/place_page_info.hpp +++ b/libs/map/place_page_info.hpp @@ -186,6 +186,17 @@ public: diff --git a/patches/0003-garmin-Turn-by-turn-navigation-on-watch.patch b/patches/0003-garmin-Turn-by-turn-navigation-on-watch.patch index 3ffff03..969e7b2 100644 --- a/patches/0003-garmin-Turn-by-turn-navigation-on-watch.patch +++ b/patches/0003-garmin-Turn-by-turn-navigation-on-watch.patch @@ -47,8 +47,7 @@ the watch app is not part of the Gradle build. garmin/om-nav/source/TurnAlerts.mc | 96 +++++ garmin/om-nav/source/TurnGlyphs.mc | 208 ++++++++++ garmin/tools/make_launcher_icon.py | 63 +++ - tools/hooks/format-config.bash | 2 + - 27 files changed, 2525 insertions(+) + 26 files changed, 2523 insertions(+) create mode 100644 android/libs/garmin/build.gradle create mode 100644 android/libs/garmin/src/main/java/app/organicmaps/garmin/GarminNavBridge.java create mode 100644 android/libs/garmin/src/main/java/app/organicmaps/garmin/GarminNavState.java @@ -71,23 +70,23 @@ the watch app is not part of the Gradle build. create mode 100644 garmin/tools/make_launcher_icon.py diff --git a/.gitignore b/.gitignore -index 9e482e3b23..6361aed978 100644 +index caee2c8c72..e341679747 100644 --- a/.gitignore +++ b/.gitignore -@@ -196,3 +196,8 @@ iphone/metadata/**/keywords.txt - - # clangd cache - .cache/clangd/ +@@ -105,3 +105,8 @@ CMakeUserPresets.json + /screenshots/ + /keywords/ + /iphone/metadata/**/keywords.txt + +# Connect IQ watch app build output and signing key -+garmin/om-nav/bin/ -+garmin/*.prg -+garmin/*developer_key* ++/garmin/om-nav/bin/ ++/garmin/*.prg ++/garmin/*developer_key* diff --git a/android/app/build.gradle b/android/app/build.gradle -index 00c4b3da5d..86b4887ad7 100644 +index ee2c54156b..df434fb910 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle -@@ -241,6 +241,7 @@ dependencies { +@@ -218,6 +218,7 @@ dependencies { implementation project(':libs:branding') implementation project(':libs:downloader') implementation project(':libs:car') @@ -96,7 +95,7 @@ index 00c4b3da5d..86b4887ad7 100644 implementation project(':libs:routing') implementation project(':libs:utils') diff --git a/android/app/src/main/java/app/organicmaps/MwmApplication.java b/android/app/src/main/java/app/organicmaps/MwmApplication.java -index 9b3b8d914e..029094a559 100644 +index c7a8606353..da218664a2 100644 --- a/android/app/src/main/java/app/organicmaps/MwmApplication.java +++ b/android/app/src/main/java/app/organicmaps/MwmApplication.java @@ -20,6 +20,7 @@ import androidx.lifecycle.ProcessLifecycleOwner; @@ -107,7 +106,7 @@ index 9b3b8d914e..029094a559 100644 import app.organicmaps.location.TrackRecordingService; import app.organicmaps.routing.NavigationService; import app.organicmaps.sdk.Map; -@@ -237,5 +238,9 @@ public class MwmApplication extends Application implements Application.ActivityL +@@ -243,5 +244,9 @@ public class MwmApplication extends Application implements Application.ActivityL final PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE); NavigationService.setOpenAppPendingIntent(pendingIntent); @@ -118,7 +117,7 @@ index 9b3b8d914e..029094a559 100644 } } diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml -index cbc4888dc8..4aa3b57903 100644 +index 7e06f8fc2d..1b6f557929 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -30,6 +30,9 @@ androidx-work = "2.10.5" @@ -131,7 +130,7 @@ index cbc4888dc8..4aa3b57903 100644 # firebase-crashlytics-ndk requires minSdkVersion 23 since 34.0.0 # See https://firebase.google.com/support/release-notes/android#crashlytics-ndk_v20-0-0 # Use 33.11.0 instead of 33.16.0 because of the https://github.com/firebase/firebase-android-sdk/issues/6993 -@@ -79,6 +82,8 @@ androidx-work = { module = "androidx.work:work-runtime", version.ref = "androidx +@@ -83,6 +86,8 @@ androidx-work = { module = "androidx.work:work-runtime", version.ref = "androidx detekt-cli = { module = "io.gitlab.arturbosch.detekt:detekt-cli", version.ref = "detekt" } devnullorthrow-mpandroidchart = { module = "com.github.devnullorthrow:MPAndroidChart", version.ref = "devnullorthrow-mpandroidchart" } @@ -1808,7 +1807,7 @@ index a43b9a1515..3a754dee17 100644 mPlayer.playback(R.raw.speed_cams_beep); diff --git a/android/settings.gradle b/android/settings.gradle -index d2b8fad3d2..d11422f308 100644 +index af8bd1cd38..cfe0ecab07 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -21,6 +21,7 @@ include ':libs:api' @@ -2831,23 +2830,3 @@ index 0000000000..b89bb14eda + +if __name__ == "__main__": + main() -diff --git a/tools/hooks/format-config.bash b/tools/hooks/format-config.bash -index 16c89c8c65..9b57dfd233 100644 ---- a/tools/hooks/format-config.bash -+++ b/tools/hooks/format-config.bash -@@ -12,6 +12,7 @@ CLANG_FORMAT_TARGETS=( - "android/libs/branding/src|*.java" - "android/libs/car/src|*.java" - "android/libs/downloader/src|*.java" -+ "android/libs/garmin/src|*.java" - "android/libs/googleassistant/src|*.java" - "android/libs/routing/src|*.java" - "android/libs/utils/src|*.java" -@@ -43,6 +44,7 @@ KTLINT_TARGETS=( - "android/libs/branding/src|*.kt" - "android/libs/car/src|*.kt" - "android/libs/downloader/src|*.kt" -+ "android/libs/garmin/src|*.kt" - "android/libs/googleassistant/src|*.kt" - "android/libs/routing/src|*.kt" - "android/libs/utils/src|*.kt" diff --git a/upstream.tag b/upstream.tag index 3c733a3..dc3073c 100644 --- a/upstream.tag +++ b/upstream.tag @@ -1,2 +1,2 @@ -2026.07.23-6-android -9d62622fb1701d0525dd66eaa8522ba9f9c33ada +2026.08.27-18-android +ebcfd753f32ef8bbc8d205f29040dcba8a1159ed