Skip to content

Commit

Permalink
Merge pull request #575 from ualch9/recent_stops
Browse files Browse the repository at this point in the history
Default to full sheet on voiceover
  • Loading branch information
aaronbrethorst authored Nov 30, 2021
2 parents 206d279 + d882ca4 commit 1d8cba1
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 11 deletions.
37 changes: 35 additions & 2 deletions OBAKit/Controls/FloatingPanel/OBAFloatingPanelController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,25 @@ import FloatingPanel

/// A subclass of `FloatingPanelController` with additional accessibility features.
class OBAFloatingPanelController: FloatingPanelController {
override init(delegate: FloatingPanelControllerDelegate?) {
static let UserHasSeenFullSheetVoiceoverChangeUserDefaultsKey = "OBAFloatingPanelController.userHasSeenFullSheetVoiceoverChange"
static let AlwaysShowFullSheetOnVoiceoverUserDefaultsKey = "OBAFloatingPanelController.alwaysShowFullSheetVoiceover"

let userDefaults: UserDefaults

/// Flag for displaying an alert informing the user that VoiceOver will automatically
/// display the full sheet and ignore map elements.
var userHasSeenFullSheetVoiceoverChange: Bool {
get { userDefaults.bool(forKey: OBAFloatingPanelController.UserHasSeenFullSheetVoiceoverChangeUserDefaultsKey) }
set { userDefaults.set(newValue, forKey: OBAFloatingPanelController.UserHasSeenFullSheetVoiceoverChangeUserDefaultsKey) }
}

var alwaysShowFullSheetOnVoiceover: Bool {
userDefaults.bool(forKey: OBAFloatingPanelController.AlwaysShowFullSheetOnVoiceoverUserDefaultsKey)
}

init(_ application: Application, delegate: FloatingPanelControllerDelegate?) {
userDefaults = application.userDefaults

super.init(delegate: delegate)

surfaceView.grabberHandle.accessibilityLabel = OBALoc("floating_panel.controller.accessibility_label", value: "Card controller", comment: "A voiceover title describing the 'grabber' for controlling the visibility of a card.")
Expand All @@ -22,10 +40,25 @@ class OBAFloatingPanelController: FloatingPanelController {
surfaceView.grabberHandle.accessibilityCustomActions = [expandAction, collapseAction]
surfaceView.grabberHandle.isAccessibilityElement = true
updateAccessibilityValue()

userDefaults.register(defaults: [
OBAFloatingPanelController.AlwaysShowFullSheetOnVoiceoverUserDefaultsKey: true,
OBAFloatingPanelController.UserHasSeenFullSheetVoiceoverChangeUserDefaultsKey: false
])
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
fatalError()
}

func fullSheetVoiceoverAlert() -> UIAlertController {
let title = OBALoc("floating_panel.controller.full_sheet_voiceover_change_alert.title", value: "Voiceover detected", comment: "")
let message = OBALoc("floating_panel.controller.full_sheet_voiceover_change_alert.message", value: "OneBusAway will automatically expand the sheet when VoiceOver is turned on. To disable this behavior, visit the Settings page.", comment: "")

let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(.dismissAction)

return alert
}

private func updateAccessibilityValue() {
Expand Down
31 changes: 25 additions & 6 deletions OBAKit/Mapping/MapRegionManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,24 @@ public class MapRegionManager: NSObject,
RegionsServiceDelegate,
StopAnnotationDelegate {

public static let DefaultLoadDataRegionFudgeFactor: Double = 1.1

/// The 'fudge factor' around the current size of the map's
/// visible region when loading map data. This will mean that we load some
/// stops that are just outside of the visible bounds of the screen, which mean
/// that stops should (fingers crossed) seem to load instantly.
///
/// The number of stops loaded is still limited by the server, see `RESTAPIService.getStops` for details.
/// Note, that this is a `preferred` value. `MapRegionManager` may or may not respect this value.
/// The default value may be accessed as a constant, `MapRegionManager.DefaultLoadDataRegionFudgeFactor`.
///
/// By default, this value is set to `1.1x`, but should be adjusted depending on user context, such as:
/// - If no stops were loaded within the given region, you could set this value to something higher and attempt to load data again.
/// - In low-density geographic regions, you may want to set this value higher in order to display a full list of stops.
/// - When VoiceOver is enabled, it can be reasonably assumed that the user won't be visually overloaded with
/// the map being full of annotations, therefore loading more stops is encouraged.
public var preferredLoadDataRegionFudgeFactor: Double = MapRegionManager.DefaultLoadDataRegionFudgeFactor

private let application: Application

private var regionChangeRequestTimer: Timer?
Expand All @@ -57,6 +75,11 @@ public class MapRegionManager: NSObject,
mapView.mapType = .mutedStandard
mapView.showsUserLocation = true
mapView.isRotateEnabled = false

// Disables voiceover interacting with map elements (such as streets and POIs).
// See #431.
mapView.accessibilityElementsHidden = true

return mapView
}()

Expand Down Expand Up @@ -214,13 +237,9 @@ public class MapRegionManager: NSObject,

notifyDelegatesDataLoadingStarted()

// Add a 'fudge factor' around the current size of the map's
// visible region. This will mean that we load some stops that
// are just outside of the visible bounds of the screen, which
// means that stops should (fingers crossed) seem to load instantly.
var mapRegion = mapView.region
mapRegion.span.latitudeDelta *= 1.1
mapRegion.span.longitudeDelta *= 1.1
mapRegion.span.latitudeDelta *= preferredLoadDataRegionFudgeFactor
mapRegion.span.longitudeDelta *= preferredLoadDataRegionFudgeFactor

let op = apiService.getStops(region: mapRegion)
op.complete { [weak self] result in
Expand Down
16 changes: 15 additions & 1 deletion OBAKit/Mapping/MapViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ public class MapViewController: UIViewController,
super.viewDidAppear(animated)

loadWeather()
updateVoiceover()
}

public override func viewWillDisappear(_ animated: Bool) {
Expand Down Expand Up @@ -379,7 +380,7 @@ public class MapViewController: UIViewController,

/// The floating panel controller, which displays a drawer at the bottom of the map.
private lazy var floatingPanel: OBAFloatingPanelController = {
let panel = OBAFloatingPanelController(delegate: self)
let panel = OBAFloatingPanelController(application, delegate: self)
panel.isRemovalInteractionEnabled = false
panel.surfaceView.cornerRadius = ThemeMetrics.cornerRadius
panel.surfaceView.backgroundColor = .clear
Expand Down Expand Up @@ -414,6 +415,19 @@ public class MapViewController: UIViewController,
}
}

func updateVoiceover() {
mapRegionManager.preferredLoadDataRegionFudgeFactor = UIAccessibility.isVoiceOverRunning ? 1.5 : MapRegionManager.DefaultLoadDataRegionFudgeFactor

if UIAccessibility.isVoiceOverRunning {
floatingPanel.move(to: .full, animated: true)

if !floatingPanel.userHasSeenFullSheetVoiceoverChange {
self.present(floatingPanel.fullSheetVoiceoverAlert(), animated: true)
floatingPanel.userHasSeenFullSheetVoiceoverChange = true
}
}
}

// MARK: - Modal Delegate

public func dismissModalController(_ controller: UIViewController) {
Expand Down
7 changes: 6 additions & 1 deletion OBAKit/Settings/SettingsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,18 @@ class SettingsViewController: FormViewController {
}()

private lazy var accessibilitySection: Section = {
let section = Section() /* Untitled section */
let section = Section(OBALoc("settings_controller.accessibility_section.title", value: "Accessibility", comment: "Settings > Accessibility section title"))

section <<< SwitchRow {
$0.tag = DataLoadFeedbackGenerator.EnabledUserDefaultsKey
$0.title = OBALoc("settings_controller.accessibility_section.enable_reload_haptic", value: "Haptic feedback on reload", comment: "Settings > Accessibility section > Haptic feedback on reload")
}

section <<< SwitchRow {
$0.tag = OBAFloatingPanelController.AlwaysShowFullSheetOnVoiceoverUserDefaultsKey
$0.title = OBALoc("settings_controller.accessibility_section.default_full_sheet_voiceover", value: "Always show full sheet on Voiceover", comment: "Settings > Accessibility section > Always show full sheet on Voiceover")
}

return section
}()

Expand Down
12 changes: 12 additions & 0 deletions OBAKit/Strings/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,12 @@
/* A voiceover title describing the action to expand the visibility of a card. */
"floating_panel.controller.expand_action_name" = "Expand";

/* No comment provided by engineer. */
"floating_panel.controller.full_sheet_voiceover_change_alert.message" = "OneBusAway will automatically expand the sheet when VoiceOver is turned on. To disable this behavior, visit the Settings page.";

/* No comment provided by engineer. */
"floating_panel.controller.full_sheet_voiceover_change_alert.title" = "Voiceover detected";

/* A voiceover title describing that the card's visibility is taking up the full screen. */
"floating_panel.controller.position.full" = "Full screen";

Expand Down Expand Up @@ -490,9 +496,15 @@
/* Directs the user to tap on the link that comes at the end of the string. Learn more: <HYPERLINK IS INSERTED HERE> */
"service_alert_controller.learn_more_fmt" = "Learn more: %@";

/* Settings > Accessibility section > Always show full sheet on Voiceover */
"settings_controller.accessibility_section.default_full_sheet_voiceover" = "Always show full sheet on Voiceover";

/* Settings > Accessibility section > Haptic feedback on reload */
"settings_controller.accessibility_section.enable_reload_haptic" = "Haptic feedback on reload";

/* Settings > Accessibility section title */
"settings_controller.accessibility_section.title" = "Accessibility";

/* Settings > Alerts section > Display test alerts */
"settings_controller.alerts_section.display_test_alerts" = "Display test alerts";

Expand Down
3 changes: 3 additions & 0 deletions OBAKit/Trip/TripStopListItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ final class TripStopCell: OBAListViewCell {
timeLabel.text = nil
tripSegmentView.image = nil
tripSegmentView.adjacentTripOrder = nil
accessibilityLabel = nil
accessibilityValue = nil
}

let titleLabel: UILabel = {
Expand Down Expand Up @@ -221,6 +223,7 @@ final class TripStopCell: OBAListViewCell {
}

titleLabel.text = String(format: titleFormat, config.routeHeadsign)
accessibilityLabel = titleLabel.text
tripSegmentView.adjacentTripOrder = config.order
}

Expand Down
14 changes: 13 additions & 1 deletion OBAKit/Trip/TripViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ class TripViewController: UIViewController,
}
}

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
updateVoiceover()
}

override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
enableIdleTimer()
Expand Down Expand Up @@ -199,7 +204,7 @@ class TripViewController: UIViewController,

/// The floating panel controller, which displays a drawer at the bottom of the map.
private lazy var floatingPanel: OBAFloatingPanelController = {
let panel = OBAFloatingPanelController(delegate: self)
let panel = OBAFloatingPanelController(application, delegate: self)
panel.isRemovalInteractionEnabled = false
panel.surfaceView.cornerRadius = ThemeMetrics.cornerRadius
panel.contentMode = .fitToBounds
Expand Down Expand Up @@ -256,6 +261,12 @@ class TripViewController: UIViewController,
}
}

func updateVoiceover() {
if UIAccessibility.isVoiceOverRunning {
self.floatingPanel.move(to: .full, animated: true)
}
}

// MARK: - Trip Details Data

private var tripDetailsOperation: DecodableOperation<RESTAPIResponse<TripDetails>>?
Expand Down Expand Up @@ -402,6 +413,7 @@ class TripViewController: UIViewController,
let map = TouchesMapView.autolayoutNew()
map.delegate = self
map.mapType = application.mapRegionManager.userSelectedMapType
map.accessibilityElementsHidden = true
return map
}()

Expand Down

0 comments on commit 1d8cba1

Please sign in to comment.