diff --git a/VibroGuide/VibroGuide_User_Evaluation_Survey.pdf b/VibroGuide/VibroGuide_User_Evaluation_Survey.pdf new file mode 100644 index 000000000..0ea6d75dc Binary files /dev/null and b/VibroGuide/VibroGuide_User_Evaluation_Survey.pdf differ diff --git a/apps/ios/GuideDogs/Assets/Localization/en-US.lproj/Localizable.strings b/apps/ios/GuideDogs/Assets/Localization/en-US.lproj/Localizable.strings index 303d7a706..4a973efc4 100644 --- a/apps/ios/GuideDogs/Assets/Localization/en-US.lproj/Localizable.strings +++ b/apps/ios/GuideDogs/Assets/Localization/en-US.lproj/Localizable.strings @@ -1611,6 +1611,10 @@ // MARK: - Callouts //------------------------------------------------------------------------------ + + + + /* Title for the button panel at the bottom of the home view */ "callouts.panel.title" = "Hear My Surroundings"; @@ -1626,6 +1630,18 @@ /* Button, Allow Callouts */ "callouts.allow_callouts" = "Allow Callouts"; +/* Toggle label for disabling and enabling sound effects */ +"callouts.sound_effects" = "Sound Effects"; + +/* Toggle label for enabling/disabling callout sound effects */ +"callouts.sound_effects.info" = "Sounds that play before callouts"; + +/* Toggle label for enabling and disabling delays between callouts */ +"callouts.delays" = "Delays"; + +/* Toggle description for enabling/disabling pauses between automatic callouts */ +"callouts.delays.info" = "Short pauses between callouts"; + /* Callouts Title, Places and Landmarks */ "callouts.places_and_landmarks" = "Places and Landmarks"; diff --git a/apps/ios/GuideDogs/Code/App/Settings/AutoCalloutSettingsProvider.swift b/apps/ios/GuideDogs/Code/App/Settings/AutoCalloutSettingsProvider.swift index e331d4bdf..020dc4781 100644 --- a/apps/ios/GuideDogs/Code/App/Settings/AutoCalloutSettingsProvider.swift +++ b/apps/ios/GuideDogs/Code/App/Settings/AutoCalloutSettingsProvider.swift @@ -7,12 +7,35 @@ // protocol AutoCalloutSettingsProvider: AnyObject { + ///Whether or not callouts are enabled globally + ///false - no automatic callouts generated var automaticCalloutsEnabled: Bool { get set } + /// Whether callout sound effects (earcons) should play before spoken callouts. + /// When false, only the spoken text will be played with no preceding audio cue. + var calloutsEarconEnabled: Bool { get set } + + /// Whether delays should be inserted between automatic callouts. + /// When false, callouts will play back-to-back with no pause between them. + var calloutsDelayEnabled: Bool { get set } + + /// Whether place callouts are enabled var placeSenseEnabled: Bool { get set } + + /// Whether landmark callouts are enabled var landmarkSenseEnabled: Bool { get set } + + /// Whether mobility callouts are enabled var mobilitySenseEnabled: Bool { get set } + + /// Whether information callouts are enabled var informationSenseEnabled: Bool { get set } + + /// Whether safety callouts are enabled var safetySenseEnabled: Bool { get set } + + /// Whether intersection callouts are enabled var intersectionSenseEnabled: Bool { get set } + + /// Whether destination/beacon callouts are enabled var destinationSenseEnabled: Bool { get set } } diff --git a/apps/ios/GuideDogs/Code/App/Settings/SettingsContext.swift b/apps/ios/GuideDogs/Code/App/Settings/SettingsContext.swift index bca0eddd0..8c8cc1501 100644 --- a/apps/ios/GuideDogs/Code/App/Settings/SettingsContext.swift +++ b/apps/ios/GuideDogs/Code/App/Settings/SettingsContext.swift @@ -12,6 +12,8 @@ import CoreLocation extension Notification.Name { static let automaticCalloutsEnabledChanged = Notification.Name("GDAAutomaticCalloutsChanged") + static let calloutsEarconEnabled = Notification.Name("GDASettingsCalloutsEarconEnabled") + static let calloutsDelayEnabled = Notification.Name("GDASettingsCalloutsDelayEnabled") static let shakeCalloutsEnabledChanged = Notification.Name("GDAShakeCalloutsChanged") static let autoCalloutCategorySenseChanged = Notification.Name("GDAAutomaticCalloutSenseChanged") static let beaconVolumeChanged = Notification.Name("GDABeaconVolumeChanged") @@ -44,6 +46,8 @@ class SettingsContext { fileprivate static let useOldBeacon = "GDASettingsUseOldBeacon" fileprivate static let playBeaconStartEndMelody = "GDAPlayBeaconStartEndMelody" fileprivate static let automaticCalloutsEnabled = "GDASettingsAutomaticCalloutsEnabled" + fileprivate static let calloutsEarconEnabled = "GDASettingsCalloutsEarconEnabled" + fileprivate static let calloutsDelayEnabled = "GDASettingsCalloutsDelayEnabled" fileprivate static let shakeCalloutsEnabled = "GDASettingsShakeCalloutsEnabled" fileprivate static let sensePlace = "GDASettingsPlaceSenseEnabled" fileprivate static let senseLandmark = "GDASettingsLandmarkSenseEnabled" @@ -99,6 +103,8 @@ class SettingsContext { Keys.useOldBeacon: false, Keys.playBeaconStartEndMelody: false, Keys.automaticCalloutsEnabled: true, + Keys.calloutsEarconEnabled: true, + Keys.calloutsDelayEnabled: true, Keys.shakeCalloutsEnabled: false, Keys.sensePlace: true, Keys.senseLandmark: true, @@ -426,6 +432,26 @@ extension SettingsContext: AutoCalloutSettingsProvider { } } + var calloutsEarconEnabled: Bool { + get { + return userDefaults.bool(forKey: Keys.calloutsEarconEnabled) + } + set(newValue) { + userDefaults.set(newValue, forKey: Keys.calloutsEarconEnabled) + NotificationCenter.default.post(name: .calloutsEarconEnabled, object: self) + } + } + + var calloutsDelayEnabled: Bool { + get { + return userDefaults.bool(forKey: Keys.calloutsDelayEnabled) + } + set(newValue) { + userDefaults.set(newValue, forKey: Keys.calloutsDelayEnabled) + NotificationCenter.default.post(name: .calloutsDelayEnabled, object: self) + } + } + var shakeCalloutsEnabled: Bool { get { return userDefaults.bool(forKey: Keys.shakeCalloutsEnabled) diff --git a/apps/ios/GuideDogs/Code/Audio/AudioSessionManager.swift b/apps/ios/GuideDogs/Code/Audio/AudioSessionManager.swift index 86ef4a062..93e13c3c8 100644 --- a/apps/ios/GuideDogs/Code/Audio/AudioSessionManager.swift +++ b/apps/ios/GuideDogs/Code/Audio/AudioSessionManager.swift @@ -140,7 +140,7 @@ class AudioSessionManager { } catch let error as NSError { let avError = AVAudioSession.ErrorCode(rawValue: error.code) ?? .unspecified - GDLogAudioSessionWarn("An error occured setting the audio session category: \(error) \(avError.description)") + GDLogAudioSessionWarn("An error occurred setting the audio session category: \(error) \(avError.description)") var telemetryInfo = currentStateInfo telemetryInfo["error.code"] = String(error.code) @@ -180,7 +180,7 @@ class AudioSessionManager { } catch let error as NSError { let avError = AVAudioSession.ErrorCode(rawValue: error.code) ?? .unspecified - GDLogAudioSessionWarn("An error occured activating the audio session: \(error) \(avError.description)") + GDLogAudioSessionWarn("An error occurred activating the audio session: \(error) \(avError.description)") var telemetryInfo = currentStateInfo telemetryInfo["error.code"] = String(error.code) @@ -221,7 +221,7 @@ class AudioSessionManager { } catch let error as NSError { let avError = AVAudioSession.ErrorCode(rawValue: error.code) ?? .unspecified - GDLogAudioSessionWarn("An error occured deactivating the audio session: \(error) \(avError.description)") + GDLogAudioSessionWarn("An error occurred deactivating the audio session: \(error) \(avError.description)") var telemetryInfo = currentStateInfo telemetryInfo["error.code"] = String(error.code) diff --git a/apps/ios/GuideDogs/Code/Audio/DynamicAudioPlayer.swift b/apps/ios/GuideDogs/Code/Audio/DynamicAudioPlayer.swift index 69b5b9481..df9e0a111 100644 --- a/apps/ios/GuideDogs/Code/Audio/DynamicAudioPlayer.swift +++ b/apps/ios/GuideDogs/Code/Audio/DynamicAudioPlayer.swift @@ -434,3 +434,4 @@ fileprivate extension AVAudioPCMBuffer { return buffer } } + \ No newline at end of file diff --git a/apps/ios/GuideDogs/Code/Audio/FadeableAudioPlayer.swift b/apps/ios/GuideDogs/Code/Audio/FadeableAudioPlayer.swift index 299eac7cd..07fbdba73 100644 --- a/apps/ios/GuideDogs/Code/Audio/FadeableAudioPlayer.swift +++ b/apps/ios/GuideDogs/Code/Audio/FadeableAudioPlayer.swift @@ -5,7 +5,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. // - +// Allows audio to fade in whenver needed. import AVFoundation class FadeableAudioPlayer: AVAudioPlayer { diff --git a/apps/ios/GuideDogs/Code/Behaviors/Default/AutoCalloutGenerator.swift b/apps/ios/GuideDogs/Code/Behaviors/Default/AutoCalloutGenerator.swift index f007d716e..1df8d57bf 100644 --- a/apps/ios/GuideDogs/Code/Behaviors/Default/AutoCalloutGenerator.swift +++ b/apps/ios/GuideDogs/Code/Behaviors/Default/AutoCalloutGenerator.swift @@ -118,7 +118,9 @@ class AutoCalloutGenerator: AutomaticGenerator, ManualGenerator { // MARK: - Private Constants private let inVehicleBeaconUpdateDistance: CLLocationDistance = 1000.0 // meters - private let calloutDelay = 0.75 + private var calloutDelay: Double { + return SettingsContext.shared.calloutsDelayEnabled ? 0.75 : 0.0 + } // MARK: - Private Properties diff --git a/apps/ios/GuideDogs/Code/Behaviors/Default/BeaconCalloutGenerator.swift b/apps/ios/GuideDogs/Code/Behaviors/Default/BeaconCalloutGenerator.swift index f381ddd27..886d5b38f 100644 --- a/apps/ios/GuideDogs/Code/Behaviors/Default/BeaconCalloutGenerator.swift +++ b/apps/ios/GuideDogs/Code/Behaviors/Default/BeaconCalloutGenerator.swift @@ -62,7 +62,9 @@ class BeaconCalloutGenerator: AutomaticGenerator, ManualGenerator { // MARK: - Private Constants private let inVehicleBeaconUpdateDistance: CLLocationDistance = 1000.0 // meters - private let calloutDelay: TimeInterval = 0.75 + private var calloutDelay: Double { + return SettingsContext.shared.calloutsDelayEnabled ? 1.00 : 0.0 + } // MARK: - Private Properties diff --git a/apps/ios/GuideDogs/Code/Behaviors/Default/Callouts/DestinationCallout.swift b/apps/ios/GuideDogs/Code/Behaviors/Default/Callouts/DestinationCallout.swift index aaf398f2b..99cdec092 100644 --- a/apps/ios/GuideDogs/Code/Behaviors/Default/Callouts/DestinationCallout.swift +++ b/apps/ios/GuideDogs/Code/Behaviors/Default/Callouts/DestinationCallout.swift @@ -39,6 +39,9 @@ struct DestinationCallout: POICalloutProtocol { var key: String { return entityKey + + + } var marker: ReferenceEntity? { @@ -73,7 +76,7 @@ struct DestinationCallout: POICalloutProtocol { case .auto, .beaconChanged, .preview: var sounds = [Sound]() - if includePrefixSound { + if includePrefixSound && SettingsContext.shared.calloutsEarconEnabled{ let category = SuperCategory(rawValue: marker.getPOI().superCategory) ?? SuperCategory.undefined sounds.append(GlyphSound(category.glyph, at: markerLocation)) } @@ -90,19 +93,22 @@ struct DestinationCallout: POICalloutProtocol { // Inform the user why the audio beacon has stopped if causedAudioDisabled { - let earcon = GlyphSound(.beaconFound) var text = GDLocalizedString("beacon.beacon_location_within_audio_beacon_muted", formattedDistance) // Append suggestion to launch NaviLens if available at location if (poi != nil) && LocationDetail(entity: poi!).source.hasNaviLens { text += " " + GDLocalizedString("beacon.suggest_navilens") } let tts = TTSSound(text, at: markerLocation) - - guard let layered = LayeredSound(earcon, tts) else { - return Sounds([earcon, tts]) + + if SettingsContext.shared.calloutsEarconEnabled { + let earcon = GlyphSound(.beaconFound) + guard let layered = LayeredSound(earcon, tts) else { + return Sounds([earcon, tts]) + } + return Sounds(layered) } - - return Sounds(layered) + + return Sounds(tts) } return Sounds(TTSSound(GDLocalizedString("beacon.beacon_location_within", formattedDistance), at: markerLocation)) diff --git a/apps/ios/GuideDogs/Code/Behaviors/Default/Callouts/IntersectionCallout.swift b/apps/ios/GuideDogs/Code/Behaviors/Default/Callouts/IntersectionCallout.swift index c2827f361..533ea6dac 100644 --- a/apps/ios/GuideDogs/Code/Behaviors/Default/Callouts/IntersectionCallout.swift +++ b/apps/ios/GuideDogs/Code/Behaviors/Default/Callouts/IntersectionCallout.swift @@ -54,7 +54,7 @@ struct IntersectionCallout: CalloutProtocol { var sounds: [Sound] = [] // Add the sound effect - if includePrefixSound { + if includePrefixSound && SettingsContext.shared.calloutsEarconEnabled{ sounds.append(GlyphSound(.poiSense, direction: .ahead)) } diff --git a/apps/ios/GuideDogs/Code/Behaviors/Default/Callouts/POICallout.swift b/apps/ios/GuideDogs/Code/Behaviors/Default/Callouts/POICallout.swift index 4469c1f15..a4b131f1e 100644 --- a/apps/ios/GuideDogs/Code/Behaviors/Default/Callouts/POICallout.swift +++ b/apps/ios/GuideDogs/Code/Behaviors/Default/Callouts/POICallout.swift @@ -156,7 +156,7 @@ struct POICallout: POICalloutProtocol { var sounds = [Sound]() - if includePrefixSound { + if includePrefixSound && SettingsContext.shared.calloutsEarconEnabled{ sounds.append(GlyphSound(category.glyph, at: soundLocation)) } diff --git a/apps/ios/GuideDogs/Code/Behaviors/Default/ExplorationGenerator.swift b/apps/ios/GuideDogs/Code/Behaviors/Default/ExplorationGenerator.swift index 4b93b3f7d..695707358 100644 --- a/apps/ios/GuideDogs/Code/Behaviors/Default/ExplorationGenerator.swift +++ b/apps/ios/GuideDogs/Code/Behaviors/Default/ExplorationGenerator.swift @@ -124,7 +124,9 @@ class ExplorationGenerator: ManualGenerator, AutomaticGenerator { return nil } } - + /// Handles manual exploration events by the home screen buttons + /// Around Me, Ahead of Me, My Location, Nearby Markers + /// Generates callouts for POIs in the direction and returns them as a CalloutGroup func handle(event: UserInitiatedEvent, verbosity: Verbosity) -> HandledEventAction? { guard let event = event as? ExplorationModeToggled else { return nil @@ -197,8 +199,9 @@ class ExplorationGenerator: ManualGenerator, AutomaticGenerator { if callouts.isEmpty { callouts.append(RelativeStringCallout(event.mode.origin, event.mode.noCalloutsMessage, position: 0.0)) } - - let group = CalloutGroup(callouts, action: .interruptAndClear, playModeSounds: true, logContext: event.logContext) + /// This part is for the manual exploration buttons, (Around Me and Ahead of Me options) + /// calloutdelay will be set to 0 when the user disables the delay in settings, or there will be a 0.75 second delay + let group = CalloutGroup(callouts, action: .interruptAndClear, playModeSounds: true, calloutDelay: SettingsContext.shared.calloutsDelayEnabled ? 0.75 : 0.0, logContext: event.logContext) group.delegate = self group.onComplete = event.completionHandler diff --git a/apps/ios/GuideDogs/Code/Behaviors/Helpers/BeaconUpdateFilter.swift b/apps/ios/GuideDogs/Code/Behaviors/Helpers/BeaconUpdateFilter.swift index bcefb024d..7c402a69b 100644 --- a/apps/ios/GuideDogs/Code/Behaviors/Helpers/BeaconUpdateFilter.swift +++ b/apps/ios/GuideDogs/Code/Behaviors/Helpers/BeaconUpdateFilter.swift @@ -54,7 +54,7 @@ class BeaconUpdateFilter { // MARK: State Properties - /// State of the filter (location and time) when the most recent update occured + /// State of the filter (location and time) when the most recent update occurred private(set) var lastUpdate: FilterUpdateSnapshot? /// Indicates if the filter is in the reset state (`false`) or if `update(...)` has been called (`true`) diff --git a/apps/ios/GuideDogs/Code/Behaviors/Preview/PreviewGenerator.swift b/apps/ios/GuideDogs/Code/Behaviors/Preview/PreviewGenerator.swift index 63478ee62..aadfda110 100644 --- a/apps/ios/GuideDogs/Code/Behaviors/Preview/PreviewGenerator.swift +++ b/apps/ios/GuideDogs/Code/Behaviors/Preview/PreviewGenerator.swift @@ -106,27 +106,33 @@ struct PreviewGenerator: ManualGenerator { let formattedDistance = LanguageFormatter.formattedDistance(from: SettingsContext.shared.enterImmediateVicinityDistance) callouts.append(GenericCallout(.preview, description: "arrived at beacon (in preview)") { (_, _, _) -> [Sound] in - let earcon = GlyphSound(.beaconFound) let tts = TTSSound(GDLocalizedString("beacon.beacon_location_within_audio_beacon_muted", formattedDistance), at: event.location) - - guard let layered = LayeredSound(earcon, tts) else { - return [earcon, tts] + + if SettingsContext.shared.calloutsEarconEnabled { + let earcon = GlyphSound(.beaconFound) + guard let layered = LayeredSound(earcon, tts) else { + return [earcon, tts] + } + return [layered] } - - return [layered] + + return [tts] }) } else { let formattedDistance = LanguageFormatter.string(from: event.distance, accuracy: 0.0, name: GDLocalizedString("beacon.generic_name")) callouts.append(GenericCallout(.preview, description: "beacon update (in preview)") { (_, _, _) -> [Sound] in - let earcon = GlyphSound(SuperCategory.places.glyph, at: event.location) let tts = TTSSound(formattedDistance, at: event.location) - - guard let layered = LayeredSound(earcon, tts) else { - return [earcon, tts] + + if SettingsContext.shared.calloutsEarconEnabled { + let earcon = GlyphSound(SuperCategory.places.glyph, at: event.location) + guard let layered = LayeredSound(earcon, tts) else { + return [earcon, tts] + } + return [layered] } - - return [layered] + + return [tts] }) } @@ -146,14 +152,17 @@ struct PreviewGenerator: ManualGenerator { case let event as BehaviorDeactivatedEvent: let callouts = [GenericCallout(.preview, description: "preview ended") { (_, _, _) -> [Sound] in - let earcon = GlyphSound(.previewEnd) let tts = TTSSound(GDLocalizedString("preview.callout.end")) - - guard let layered = LayeredSound(earcon, tts) else { - return [earcon, tts] + + if SettingsContext.shared.calloutsEarconEnabled { + let earcon = GlyphSound(.previewEnd) + guard let layered = LayeredSound(earcon, tts) else { + return [earcon, tts] + } + return [layered] } - - return [layered] + + return [tts] }] // Play the sound that caps off the end of the preview mode diff --git a/apps/ios/GuideDogs/Code/Data/Preview/IntersectionDecisionPoint.swift b/apps/ios/GuideDogs/Code/Data/Preview/IntersectionDecisionPoint.swift index cb580e06c..d4298d166 100644 --- a/apps/ios/GuideDogs/Code/Data/Preview/IntersectionDecisionPoint.swift +++ b/apps/ios/GuideDogs/Code/Data/Preview/IntersectionDecisionPoint.swift @@ -99,9 +99,20 @@ struct IntersectionDecisionPoint: RootedPreviewGraph { let earcon = GlyphSound(.poiSense, compass: direction) let tts = TTSSound(approach, compass: direction) - guard let ttsSound = ConcatenatedSound(earcon, tts), let layered = LayeredSound(ttsSound, GlyphSound(.travelEnd, compass: direction)) else { - GDLogPreviewError("Unable to concatenate and layer sounds...") - return [] + let baseSound:Sound + if SettingsContext.shared.calloutsEarconEnabled { + guard let concatenated = ConcatenatedSound(earcon, tts) else { + GDLogPreviewError("Failed to concatenate sounds for intersection callout") + return [] + } + baseSound = concatenated + } else { + baseSound = tts + } + + guard let layered = LayeredSound(baseSound, GlyphSound(.travelEnd, compass: direction)) else { + GDLogPreviewError("Unable to concatenate and layer sounds ") + return [] } guard let roads = roads, !roads.isEmpty else { diff --git a/apps/ios/GuideDogs/Code/Generators/Callouts/Location Callouts/AlongRoadLocationCallout.swift b/apps/ios/GuideDogs/Code/Generators/Callouts/Location Callouts/AlongRoadLocationCallout.swift index 00979a408..cd32a268f 100644 --- a/apps/ios/GuideDogs/Code/Generators/Callouts/Location Callouts/AlongRoadLocationCallout.swift +++ b/apps/ios/GuideDogs/Code/Generators/Callouts/Location Callouts/AlongRoadLocationCallout.swift @@ -99,7 +99,7 @@ struct AlongRoadLocationCallout: LocationCalloutProtocol { } // If we aren't playing the mode enter/exit sounds, play the category sound instead - if includePrefixSound { + if includePrefixSound && SettingsContext.shared.calloutsEarconEnabled{ sounds.append(GlyphSound(.locationSense, direction: .ahead)) } diff --git a/apps/ios/GuideDogs/Code/Generators/Callouts/Location Callouts/InsideLocationCallout.swift b/apps/ios/GuideDogs/Code/Generators/Callouts/Location Callouts/InsideLocationCallout.swift index 76807fff6..d3f610a05 100644 --- a/apps/ios/GuideDogs/Code/Generators/Callouts/Location Callouts/InsideLocationCallout.swift +++ b/apps/ios/GuideDogs/Code/Generators/Callouts/Location Callouts/InsideLocationCallout.swift @@ -61,7 +61,7 @@ struct InsideLocationCallout: LocationCalloutProtocol { } // If we aren't playing the mode enter/exit sounds, play the category sound instead - if includePrefixSound { + if includePrefixSound && SettingsContext.shared.calloutsEarconEnabled{ sounds.append(GlyphSound(.locationSense, direction: .ahead)) } diff --git a/apps/ios/GuideDogs/Code/Generators/Callouts/Location Callouts/LocationCallout.swift b/apps/ios/GuideDogs/Code/Generators/Callouts/Location Callouts/LocationCallout.swift index d4e6c53c2..0b982e080 100644 --- a/apps/ios/GuideDogs/Code/Generators/Callouts/Location Callouts/LocationCallout.swift +++ b/apps/ios/GuideDogs/Code/Generators/Callouts/Location Callouts/LocationCallout.swift @@ -76,7 +76,7 @@ struct LocationCallout: LocationCalloutProtocol { var sounds: [Sound] = [] // If we aren't playing the mode enter/exit sounds, play the category sound instead - if includePrefixSound { + if includePrefixSound && SettingsContext.shared.calloutsEarconEnabled{ sounds.append(GlyphSound(.locationSense, direction: .ahead)) } diff --git a/apps/ios/GuideDogs/Code/Visual UI/Controls/Settings/CalloutSettingsCellView.swift b/apps/ios/GuideDogs/Code/Visual UI/Controls/Settings/CalloutSettingsCellView.swift index 8b6d09251..dc9c575a2 100644 --- a/apps/ios/GuideDogs/Code/Visual UI/Controls/Settings/CalloutSettingsCellView.swift +++ b/apps/ios/GuideDogs/Code/Visual UI/Controls/Settings/CalloutSettingsCellView.swift @@ -13,7 +13,7 @@ protocol CalloutSettingsCellViewDelegate: AnyObject { } internal enum CalloutSettingCellType { - case all, poi, mobility, beacon, shake + case all, poi, mobility, beacon, shake, soundEffects, delays } class CalloutSettingsCellView: UITableViewCell { @@ -44,6 +44,14 @@ class CalloutSettingsCellView: UITableViewCell { return case .shake: settingSwitch.isOn = SettingsContext.shared.shakeCalloutsEnabled + case .soundEffects: + textLabel?.text = GDLocalizedString("callouts.sound_effects") + detailTextLabel?.text = GDLocalizedString("callouts.sound_effects.info") + settingSwitch.isOn = SettingsContext.shared.calloutsEarconEnabled + case .delays: + textLabel?.text = GDLocalizedString("callouts.delays") + detailTextLabel?.text = GDLocalizedString("callouts.delays.info") + settingSwitch.isOn = SettingsContext.shared.calloutsDelayEnabled } } } @@ -97,6 +105,14 @@ class CalloutSettingsCellView: UITableViewCell { case .shake: SettingsContext.shared.shakeCalloutsEnabled = isOn GDATelemetry.track("settings.shake_callouts", value: isOn.description) + case .soundEffects: + // Enabling/Disabling sound effects for callouts + SettingsContext.shared.calloutsEarconEnabled = isOn + GDATelemetry.track("settings.callouts_earcon", value: isOn.description) + case .delays: + // Allowing delays between the automatic callouts + SettingsContext.shared.calloutsDelayEnabled = isOn + GDATelemetry.track("settings.callouts_delay", value: isOn.description) } } } diff --git a/apps/ios/GuideDogs/Code/Visual UI/View Controllers/Settings/SettingsViewController.swift b/apps/ios/GuideDogs/Code/Visual UI/View Controllers/Settings/SettingsViewController.swift index d5e5ef20c..fcbfc81b4 100644 --- a/apps/ios/GuideDogs/Code/Visual UI/View Controllers/Settings/SettingsViewController.swift +++ b/apps/ios/GuideDogs/Code/Visual UI/View Controllers/Settings/SettingsViewController.swift @@ -27,6 +27,8 @@ class SettingsViewController: BaseTableViewController { case mobility = 2 case beacon = 3 case shake = 4 + case soundEffects = 5 + case delays = 6 } private static let cellIdentifiers: [IndexPath: String] = [ @@ -36,6 +38,8 @@ class SettingsViewController: BaseTableViewController { IndexPath(row: 3, section: Section.general.rawValue): "volumeSettings", IndexPath(row: 4, section: Section.general.rawValue): "manageDevices", IndexPath(row: 5, section: Section.general.rawValue): "siriShortcuts", + IndexPath(row: CalloutsRow.soundEffects.rawValue, section: Section.callouts.rawValue): "shakeCallouts", + IndexPath(row: CalloutsRow.delays.rawValue, section: Section.callouts.rawValue): "shakeCallouts", IndexPath(row: 0, section: Section.audio.rawValue): "mixAudio", @@ -55,7 +59,10 @@ class SettingsViewController: BaseTableViewController { IndexPath(row: CalloutsRow.poi.rawValue, section: Section.callouts.rawValue), IndexPath(row: CalloutsRow.mobility.rawValue, section: Section.callouts.rawValue), IndexPath(row: CalloutsRow.beacon.rawValue, section: Section.callouts.rawValue), - IndexPath(row: CalloutsRow.shake.rawValue, section: Section.callouts.rawValue) + IndexPath(row: CalloutsRow.shake.rawValue, section: Section.callouts.rawValue), + IndexPath(row: CalloutsRow.soundEffects.rawValue, section: Section.callouts.rawValue), + IndexPath(row: CalloutsRow.delays.rawValue, section: Section.callouts.rawValue) + ] // MARK: Properties @@ -84,7 +91,7 @@ class SettingsViewController: BaseTableViewController { switch sectionType { case .general: return 6 case .audio: return 1 - case .callouts: return SettingsContext.shared.automaticCalloutsEnabled ? 5 : 1 + case .callouts: return SettingsContext.shared.automaticCalloutsEnabled ? 7 : 1 case .streetPreview: return 1 case .troubleshooting: return 1 case .about: return 1 @@ -111,6 +118,8 @@ class SettingsViewController: BaseTableViewController { case .mobility: cell.type = .mobility case .beacon: cell.type = .beacon case .shake: cell.type = .shake + case .soundEffects: cell.type = .soundEffects + case .delays: cell.type = .delays } } diff --git a/apps/ios/GuideDogs/Code/Visual UI/Views/Settings.storyboard b/apps/ios/GuideDogs/Code/Visual UI/Views/Settings.storyboard index ebc77e8fb..522d47ada 100644 --- a/apps/ios/GuideDogs/Code/Visual UI/Views/Settings.storyboard +++ b/apps/ios/GuideDogs/Code/Visual UI/Views/Settings.storyboard @@ -1,9 +1,9 @@ - + - + @@ -13,7 +13,7 @@ - + @@ -41,7 +41,7 @@ - + @@ -429,7 +429,7 @@ - + @@ -515,7 +515,7 @@ - + @@ -683,7 +683,7 @@ - + @@ -745,7 +745,7 @@