From e7855de8cd8a7bfc64339bb8a88a025d9037b386 Mon Sep 17 00:00:00 2001 From: Dave Craig Date: Mon, 24 Aug 2026 10:18:22 +0100 Subject: [PATCH 1/3] Fix accessibility onboarding screen popping in from top-left Replace LazyColumn (unneeded for 2 items) with direct SwitchPreference/OnboardButton calls and center the toggle text, matching every other onboarding screen. --- .../accessibility/AccessibilityScreen.kt | 49 +++++++++---------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/shared/src/commonMain/kotlin/org/scottishtecharmy/soundscape/screens/onboarding/accessibility/AccessibilityScreen.kt b/shared/src/commonMain/kotlin/org/scottishtecharmy/soundscape/screens/onboarding/accessibility/AccessibilityScreen.kt index 67ef6e01f..00f99ca1e 100644 --- a/shared/src/commonMain/kotlin/org/scottishtecharmy/soundscape/screens/onboarding/accessibility/AccessibilityScreen.kt +++ b/shared/src/commonMain/kotlin/org/scottishtecharmy/soundscape/screens/onboarding/accessibility/AccessibilityScreen.kt @@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -24,7 +23,7 @@ import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.style.TextAlign import me.zhanghai.compose.preference.ProvidePreferenceLocals -import me.zhanghai.compose.preference.switchPreference +import me.zhanghai.compose.preference.SwitchPreference import org.jetbrains.compose.resources.stringResource import org.scottishtecharmy.soundscape.components.OnboardButton import org.scottishtecharmy.soundscape.preferences.PreferenceDefaults @@ -105,35 +104,31 @@ fun AccessibilityOnboardingScreen( Spacer(modifier = Modifier.height(spacing.large)) ProvidePreferenceLocals { - LazyColumn( - modifier = Modifier.fillMaxWidth(), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - switchPreference( - key = PreferenceKeys.SHOW_MAP, - defaultValue = PreferenceDefaults.SHOW_MAP, - rememberState = { showMap }, - modifier = Modifier.testTag("accessibilityOnboardingScreenShowMapToggle"), - title = { - Text( - text = stringResource(Res.string.settings_show_map), - color = MaterialTheme.colorScheme.onBackground, - modifier = Modifier.focusable() - ) - }, - ) - - item { - OnboardButton( - text = stringResource(Res.string.ui_continue), - onClick = { onNavigate() }, + SwitchPreference( + state = showMap, + title = { + Text( + text = stringResource(Res.string.settings_show_map), + color = MaterialTheme.colorScheme.onBackground, + textAlign = TextAlign.Center, modifier = Modifier .fillMaxWidth() .focusable() - .testTag("accessibilityOnboardingScreenContinueButton"), ) - } - } + }, + modifier = Modifier + .fillMaxWidth() + .testTag("accessibilityOnboardingScreenShowMapToggle"), + ) + + OnboardButton( + text = stringResource(Res.string.ui_continue), + onClick = { onNavigate() }, + modifier = Modifier + .fillMaxWidth() + .focusable() + .testTag("accessibilityOnboardingScreenContinueButton"), + ) } } } From 18ceb9fd54f240273fa27961f2ef86641a470d6a Mon Sep 17 00:00:00 2001 From: Dave Craig Date: Mon, 24 Aug 2026 10:45:30 +0100 Subject: [PATCH 2/3] Fix release notes dialog and splash sound repeating every iOS launch Both features gate on the shared "LastNewRelease" preference key, but computed the current minor version two different ways. Kotlin trims the custom AppVersionName Info.plist key (full x.y.z); Swift trimmed CFBundleShortVersionString, which CI already pre-trims to x.y for TestFlight grouping, so trimming it again yielded a different string. The mismatch meant each feature's "seen" write stomped the other's expected value, replaying both every launch on real builds. Also fix SharedNewReleaseDialog writing the "seen" flag from the dismiss button's composable body (fires on render) instead of its onClick handler. Co-Authored-By: Claude Sonnet 5 --- iosApp/iosApp/SplashView.swift | 10 +++++++++- .../screens/home/home/SharedNewReleaseDialog.kt | 12 +++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/iosApp/iosApp/SplashView.swift b/iosApp/iosApp/SplashView.swift index ce3439a81..b61cecf31 100644 --- a/iosApp/iosApp/SplashView.swift +++ b/iosApp/iosApp/SplashView.swift @@ -104,7 +104,15 @@ final class SplashCoordinator: ObservableObject { } private func currentMinorVersion() -> String { - let raw = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "0.0.0" + // Must mirror PlatformInfo.appVersionName()/appVersionMinorTrimmed() exactly: + // CI pre-trims MARKETING_VERSION (CFBundleShortVersionString) to major.minor for + // TestFlight grouping, while AppVersionName keeps the full major.minor.patch. Reading + // CFBundleShortVersionString here would trim an already-trimmed value on those builds, + // producing a different string than the Kotlin side writes to the same "LastNewRelease" + // key and causing this splash and the release-notes dialog to fight over it forever. + let raw = (Bundle.main.infoDictionary?["AppVersionName"] as? String) + ?? (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) + ?? "0.0.0" if let lastDot = raw.range(of: ".", options: .backwards) { return String(raw[.. Date: Mon, 24 Aug 2026 10:53:57 +0100 Subject: [PATCH 3/3] Update test instructions to cover iOS and offline maps The Android and iOS apps now share one codebase and UI, so the tester doc no longer treats Android as primary and iOS as an afterthought. Adds platform-specific requirements/install steps (iOS is TestFlight-invitation-only for now), a new offline maps test section, and gives ios-upgrade-from-legacy.md front matter so it renders as a linkable page. Co-Authored-By: Claude Sonnet 5 --- docs/ios-upgrade-from-legacy.md | 7 +++++ docs/testing/test-instructions.md | 46 +++++++++++++++++++++---------- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/docs/ios-upgrade-from-legacy.md b/docs/ios-upgrade-from-legacy.md index b84bc35e0..2cbcd6c5d 100644 --- a/docs/ios-upgrade-from-legacy.md +++ b/docs/ios-upgrade-from-legacy.md @@ -1,3 +1,10 @@ +--- +title: Upgrading from the legacy iOS app +layout: page +parent: "Using Soundscape" +has_toc: false +--- + # Upgrading from the legacy Soundscape iOS app This page is for users of the legacy Microsoft Soundscape iOS app who are diff --git a/docs/testing/test-instructions.md b/docs/testing/test-instructions.md index 4204dcf29..1046ba19f 100644 --- a/docs/testing/test-instructions.md +++ b/docs/testing/test-instructions.md @@ -6,18 +6,23 @@ has_toc: false --- # Testing the Soundscape app -The app is a port of the Soundscape iOS app and the UI is designed to be pretty much the same. -Whilst we're interested in improving the UI in the long term, matching the iOS behaviour is the -most important goal for this initial release. +The Android and iOS apps are now built from a single shared codebase, so the UI and behaviour +described in this document apply to both unless a section specifically calls out a difference +between the two platforms. Unless you are an STA member and can ping us on Slack, all feedback should go via the Help Desk by emailing . ## Requirements -The app currently requires Android 11 (API 30 - see ). We are hoping to drop this to Android 9 with some more work, but for now we only support Android 11 and later. -We don't know of any other requirements, but that's one of the thing this testing should help us understand. +* Android: Android 11 (API 30 - see ) or later. +* iOS: iOS 16 or later. + +We don't know of any other requirements, but that's one of the things this testing should help us understand. ## Installing the app -The app is freely available on the Play Store [here](https://play.google.com/store/apps/details?id=org.scottishtecharmy.soundscape). +* Android: the app is freely available on the Play Store [here](https://play.google.com/store/apps/details?id=org.scottishtecharmy.soundscape). +* iOS: the rewritten app is currently only available via TestFlight, by invitation. If you're an STA member, ask on Slack for an invite; otherwise request one via the Help Desk by emailing . Once you have an invitation, install TestFlight from the App Store first and then follow the invitation link to install the beta build. + +If you're updating from the older, Microsoft-authored iOS app rather than installing fresh, see [Upgrading from the legacy Soundscape iOS app]({{ "/ios-upgrade-from-legacy.html" | relative_url }}) for what's carried over automatically and what has changed. ## Running the app the first time The first time you run the Soundscape app you will see a series of onboarding screens which let @@ -39,7 +44,8 @@ the main screen: Screenshot of the Soundscape home screen Soundscape will now continue to run in the background. To exit it, click on the top right corner -to put the app to sleep, and then close the app (swipe up etc.). +to put the app to sleep, and then close the app (swipe up from the app switcher, or the +equivalent gesture on your device). Soundscape is designed to be used with headphones. @@ -52,7 +58,7 @@ Things that should happen on the Home screen and we're interested if they do not If you've got to this point and it all seems to be working, then you can move on to more detailed testing. ### Test 1 - Go for a walk -As you move around, Soundscape should periodically describe your location and call out any points of interest that you pass e.g. Shops, Bus Stops etc. We're interested if there's anything that doesn't sound right. The app will consume a little bit of data as it downloads maps as you move around, but in general those are fairly small (< 50kb for a 600m square bit of map). The map tiles are cached so they will generally only be downloaded once. +As you move around, Soundscape should periodically describe your location and call out any points of interest that you pass e.g. Shops, Bus Stops etc. We're interested if there's anything that doesn't sound right. The app will consume a little bit of data as it downloads maps as you move around, but in general those are fairly small (< 50kb for a 600m square bit of map). The map tiles are cached so they will generally only be downloaded once. If you'd rather not use any mobile data at all, or want to test the app with no network connection, see Test 3 below on offline maps. ### Test 2 - Create a route and play it back This uses a bit more of the UI, but once set up it should be fairly straightforward. @@ -77,20 +83,32 @@ There should now be a route listed. Click on that and you can check that it's wh #### Play the route Click _Start Route_ on the _Route Details_ screen to start an audio beacon playing at the first waypoint of the route. The audio beacon will sound from the direction of the waypoint from where you are. When you're using the Soundscape app and your phone is unlocked, the direction used is the direction that the phone is pointing in. You can lock your phone and put it in your bag and then it will start using the direction in which your walking. The sound of the beacon will be different if you are walking towards it or away from it. If you stop moving and your phone is locked then any beacon will go quieter to indicate that there's no available direction data. +### Test 3 - Offline maps +Soundscape can download map data for a region so that it keeps working - map, callouts and Places Nearby included - with no Internet connection at all. See [Offline map extracts]({{ "/users/help-offline-map-extracts.html" | relative_url }}) for the kinds of extracts available (country, region and city). + +1. Open the Menu hamburger in the top left and tap _Offline maps_. +1. You should see any extracts that cover your current location listed. Pick one and tap _Download offline map_. The first time anyone downloads a particular extract there can be a short delay while the server prepares it - after that it should start straight away. Larger extracts (e.g. a whole country) will take longer and use more storage than a city extract, so pick whichever is convenient for testing. +1. You can also find nearby extracts from any Location Details screen - look for a _Nearby offline maps_ button. +1. Once a download completes, turn on Airplane Mode (or otherwise disable WiFi and mobile data) and repeat Test 1 and Test 2 above. The map should still draw, and callouts and Places Nearby should still work using the downloaded data. +1. In Settings there's a _Search mode_ option (Auto/Online/Offline). With no network, Search should automatically fall back to offline data (Auto); you can also force this by setting it to _Offline_ so you can test that path even when you do have a network connection. +1. Downloaded maps can be removed again from the _Offline maps_ screen - tap on a downloaded map and choose _Delete offline map_ - which is worth checking too if you're short on storage. + +We're interested in anything that doesn't work the same offline as it does online, how long downloads take, and whether the list of available extracts makes sense for your area. + ## Providing debug location trace The app can store up to an hour buffer of the user location recorded whilst the app is running. This feature is disabled by default, and even when enabled the data stays on the phone unless the user chooses to share it via interaction with the app. To use the feature: 1. Tap on the Menu hamburger in the top left, and then tap on "Settings" scroll to the bottom and you'll see the "Enable recording of travel" option. Click to enable/disable. -2. With the setting enabled, a new option appears in the Menu drawer below "About Soundscape" which is "Share recording of travel". If you want to share a GPX track you can click on that and you can then choose whether to use email/slack etc. to send the file to us to debug. -The file contains the data from the Android location services for up to the last hour that the app has been running. Don't share it with us if you don't want us to know where you've been. There's no identifying data in it, though obviously we'll know who sent it. +2. With the setting enabled, a new option appears in the Menu drawer below "About Soundscape" which is "Share recording of travel". If you want to share a GPX track you can click on that and you can then choose to send the file to us via email etc. using the standard Android share menu or iOS share sheet. +The file contains the location data recorded by the device's location services for up to the last hour that the app has been running. Don't share it with us if you don't want us to know where you've been. There's no identifying data in it, though obviously we'll know who sent it. We can load the GPX file into our test code and it will generate the callouts that the user will have heard and we can see which road/path the app thought it was following, and figure out why callouts were generated incorrectly or not generated at all. Enabling the setting is absolutely optional, but it is useful to us for debugging. ## Final notes -There are other features in the app, but for this first phase of testing the focus is those -above. If there's anything unclear in these instructions let us know. Once we have some feedback, -there'll be some bugs to fix, and then we'll do incremental releases. If you are interested in helping out -further on the project, take a look at the STA volunteer app for some available roles. +There are other features in the app, but the focus for this testing is those above. If there's +anything unclear in these instructions let us know. Once we have some feedback, there'll be some +bugs to fix, and then we'll do incremental releases. If you are interested in helping out further +on the project, take a look at the STA volunteer app for some available roles. Thanks for reading! \ No newline at end of file