Skip to content

Commit

Permalink
MAPSIOS-1593: Add opt out mechanism for geofencing in attributions (#…
Browse files Browse the repository at this point in the history
…2330)
  • Loading branch information
aleksproger authored Oct 14, 2024
1 parent ffb3105 commit c1c75f1
Show file tree
Hide file tree
Showing 3 changed files with 310 additions and 126 deletions.
301 changes: 223 additions & 78 deletions Sources/MapboxMaps/Style/AttributionDialogManager.swift
Original file line number Diff line number Diff line change
@@ -1,108 +1,109 @@
import UIKit
internal protocol AttributionDataSource: AnyObject {
import Foundation
@_implementationOnly import MapboxCommon_Private

protocol AttributionDataSource: AnyObject {
func loadAttributions(completion: @escaping ([Attribution]) -> Void)
}

internal protocol AttributionDialogManagerDelegate: AnyObject {
protocol AttributionDialogManagerDelegate: AnyObject {
func viewControllerForPresenting(_ attributionDialogManager: AttributionDialogManager) -> UIViewController?
func attributionDialogManager(_ attributionDialogManager: AttributionDialogManager, didTriggerActionFor attribution: Attribution)
}

internal class AttributionDialogManager {

final class AttributionDialogManager {
private weak var dataSource: AttributionDataSource?
private weak var delegate: AttributionDialogManagerDelegate?
private var inProcessOfParsingAttributions: Bool = false

internal init(dataSource: AttributionDataSource, delegate: AttributionDialogManagerDelegate?) {
private let isGeofenceActive: () -> Bool
private let setGeofenceConsent: (Bool) -> Void
private let getGeofenceConsent: () -> Bool

init(
dataSource: AttributionDataSource,
delegate: AttributionDialogManagerDelegate?,
isGeofenceActive: @escaping () -> Bool = { __GeofencingUtils.isActive() },
setGeofenceConsent: @escaping (Bool) -> Void = { isConsentGiven in
__GeofencingUtils.setUserConsent(isConsentGiven: isConsentGiven, callback: { expected in
if let error = expected.error { Log.error(forMessage: "Error: \(error) occurred while changing user consent for Geofencing.") }
})
},
getGeofenceConsent: @escaping () -> Bool = { __GeofencingUtils.getUserConsent() }
) {
self.dataSource = dataSource
self.delegate = delegate
self.isGeofenceActive = isGeofenceActive
self.setGeofenceConsent = setGeofenceConsent
self.getGeofenceConsent = getGeofenceConsent
}

internal var isMetricsEnabled: Bool {
get {
UserDefaults.standard.MGLMapboxMetricsEnabled
}
set {
UserDefaults.standard.MGLMapboxMetricsEnabled = newValue
}
var isMetricsEnabled: Bool {
get { UserDefaults.standard.MGLMapboxMetricsEnabled }
set { UserDefaults.standard.MGLMapboxMetricsEnabled = newValue }
}

//swiftlint:disable:next function_body_length
internal func showTelemetryAlertController(from viewController: UIViewController) {
let alert: UIAlertController
let bundle = Bundle.mapboxMaps
let telemetryTitle = NSLocalizedString("TELEMETRY_TITLE",
tableName: Ornaments.localizableTableName,
bundle: bundle,
value: "Make Mapbox Maps Better",
comment: "Telemetry prompt title")

let message: String
func showGeofencingAlertController(from viewController: UIViewController) {
let telemetryTitle = GeofencingStrings.geofencingTitle
let message = GeofencingStrings.geofencingMessage
let participateTitle: String
let declineTitle: String

if isMetricsEnabled {
message = NSLocalizedString("TELEMETRY_ENABLED_MSG",
tableName: Ornaments.localizableTableName,
bundle: bundle,
value: """
You are helping to make OpenStreetMap and
Mapbox maps better by contributing anonymous usage data.
""",
comment: "Telemetry prompt message")
participateTitle = NSLocalizedString("TELEMETRY_ENABLED_ON",
tableName: Ornaments.localizableTableName,
bundle: bundle,
value: "Keep Participating",
comment: "Telemetry prompt button")
declineTitle = NSLocalizedString("TELEMETRY_ENABLED_OFF",
tableName: Ornaments.localizableTableName,
bundle: bundle,
value: "Stop Participating",
comment: "Telemetry prompt button")
if getGeofenceConsent() {
participateTitle = GeofencingStrings.geofencingEnabledOnMessage
declineTitle = GeofencingStrings.geofencingEnabledOffMessage
} else {
message = NSLocalizedString("TELEMETRY_DISABLED_MSG",
tableName: Ornaments.localizableTableName,
bundle: bundle, value: """
You can help make OpenStreetMap and Mapbox maps better
by contributing anonymous usage data.
""",
comment: "Telemetry prompt message")
participateTitle = NSLocalizedString("TELEMETRY_DISABLED_ON",
tableName: Ornaments.localizableTableName,
bundle: bundle, value: "Participate",
comment: "Telemetry prompt button")
declineTitle = NSLocalizedString("TELEMETRY_DISABLED_OFF",
tableName: Ornaments.localizableTableName,
bundle: bundle, value: "Don’t Participate",
comment: "Telemetry prompt button")
participateTitle = GeofencingStrings.geofencingDisabledOnMessage
declineTitle = GeofencingStrings.geofencingDisabledOffMessage
}

if UIDevice.current.userInterfaceIdiom == .pad {
alert = UIAlertController(title: telemetryTitle, message: message, preferredStyle: .alert)
showAlertController(from: viewController, title: telemetryTitle, message: message, actions: [
UIAlertAction(title: declineTitle, style: .default, handler: { _ in self.setGeofenceConsent(false) }),
UIAlertAction(title: participateTitle, style: .cancel, handler: { _ in self.setGeofenceConsent(true) })
])
}

func showTelemetryAlertController(from viewController: UIViewController) {
let telemetryTitle = TelemetryStrings.telemetryTitle
let message: String
let participateTitle: String
let declineTitle: String

if isMetricsEnabled {
message = TelemetryStrings.telemetryEnabledMessage
participateTitle = TelemetryStrings.telemetryEnabledOnMessage
declineTitle = TelemetryStrings.telemetryEnabledOffMessage
} else {
alert = UIAlertController(title: telemetryTitle, message: message, preferredStyle: .actionSheet)
message = TelemetryStrings.telemetryDisabledMessage
participateTitle = TelemetryStrings.telemetryDisabledOnMessage
declineTitle = TelemetryStrings.telemetryDisabledOffMessage
}

let moreTitle = NSLocalizedString("TELEMETRY_MORE",
tableName: Ornaments.localizableTableName,
bundle: bundle, value: "Tell Me More",
comment: "Telemetry prompt button")
let moreAction = UIAlertAction(title: moreTitle, style: .default) { _ in
let openTelemetryURL: (UIAlertAction) -> Void = { _ in
guard let url = URL(string: Ornaments.telemetryURL) else { return }
self.delegate?.attributionDialogManager(self, didTriggerActionFor: Attribution(title: "", url: url))
}
alert.addAction(moreAction)

alert.addAction(UIAlertAction(title: declineTitle, style: .default) { _ in
self.isMetricsEnabled = false
})
showAlertController(from: viewController, title: telemetryTitle, message: message, actions: [
UIAlertAction(title: TelemetryStrings.telemetryMore, style: .default, handler: openTelemetryURL),
UIAlertAction(title: declineTitle, style: .default, handler: { _ in self.isMetricsEnabled = false }),
UIAlertAction(title: participateTitle, style: .cancel, handler: { _ in self.isMetricsEnabled = true })
])
}

alert.addAction(UIAlertAction(title: participateTitle, style: .cancel) { _ in
self.isMetricsEnabled = true
})
func showAlertController(
from viewController: UIViewController,
title: String,
message: String,
actions: [UIAlertAction]
) {
let alert = if UIDevice.current.userInterfaceIdiom == .pad {
UIAlertController(title: title, message: message, preferredStyle: .alert)
} else {
UIAlertController(title: title, message: message, preferredStyle: .actionSheet)
}

actions.forEach(alert.addAction)
viewController.present(alert, animated: true)
}
}
Expand Down Expand Up @@ -150,17 +151,19 @@ extension AttributionDialogManager: InfoButtonOrnamentDelegate {
}
}

let telemetryTitle = NSLocalizedString("TELEMETRY_NAME",
tableName: Ornaments.localizableTableName,
bundle: bundle,
value: "Mapbox Telemetry",
comment: "Action in attribution sheet")
let telemetryAction = UIAlertAction(title: telemetryTitle, style: .default) { _ in
let telemetryAction = UIAlertAction(title: TelemetryStrings.telemetryName, style: .default) { _ in
self.showTelemetryAlertController(from: viewController)
}

alert.addAction(telemetryAction)

if isGeofenceActive() || !getGeofenceConsent() {
let geofencingAction = UIAlertAction(title: GeofencingStrings.geofencingName, style: .default) { _ in
self.showGeofencingAlertController(from: viewController)
}
alert.addAction(geofencingAction)
}

let privacyPolicyAttribution = Attribution.makePrivacyPolicyAttribution()
let privacyPolicyAction = UIAlertAction(title: privacyPolicyAttribution.title, style: .default) { _ in
self.delegate?.attributionDialogManager(self, didTriggerActionFor: privacyPolicyAttribution)
Expand Down Expand Up @@ -191,3 +194,145 @@ private extension Attribution {
)
}
}

enum TelemetryStrings {
static let telemetryName = NSLocalizedString(
"TELEMETRY_NAME",
tableName: Ornaments.localizableTableName,
bundle: .mapboxMaps,
value: "Mapbox Telemetry",
comment: "Action in attribution sheet"
)

static let telemetryTitle = NSLocalizedString(
"TELEMETRY_TITLE",
tableName: Ornaments.localizableTableName,
bundle: .mapboxMaps,
value: "Make Mapbox Maps Better",
comment: "Telemetry prompt title"
)

static let telemetryEnabledMessage = NSLocalizedString(
"TELEMETRY_ENABLED_MSG",
tableName: Ornaments.localizableTableName,
bundle: .mapboxMaps,
value: """
You are helping to make OpenStreetMap and
Mapbox maps better by contributing anonymous usage data.
""",
comment: "Telemetry prompt message"
)

static let telemetryDisabledMessage = NSLocalizedString(
"TELEMETRY_DISABLED_MSG",
tableName: Ornaments.localizableTableName,
bundle: .mapboxMaps,
value: """
You can help make OpenStreetMap and Mapbox maps better
by contributing anonymous usage data.
""",
comment: "Telemetry prompt message"
)

static let telemetryEnabledOnMessage = NSLocalizedString(
"TELEMETRY_ENABLED_ON",
tableName: Ornaments.localizableTableName,
bundle: .mapboxMaps,
value: "Keep Participating",
comment: "Telemetry prompt button"
)

static let telemetryEnabledOffMessage = NSLocalizedString(
"TELEMETRY_ENABLED_OFF",
tableName: Ornaments.localizableTableName,
bundle: .mapboxMaps,
value: "Stop Participating",
comment: "Telemetry prompt button"
)

static let telemetryDisabledOnMessage = NSLocalizedString(
"TELEMETRY_DISABLED_ON",
tableName: Ornaments.localizableTableName,
bundle: .mapboxMaps,
value: "Participate",
comment: "Telemetry prompt button"
)

static let telemetryDisabledOffMessage = NSLocalizedString(
"TELEMETRY_DISABLED_OFF",
tableName: Ornaments.localizableTableName,
bundle: .mapboxMaps,
value: "Don’t Participate",
comment: "Telemetry prompt button"
)

static let telemetryMore = NSLocalizedString(
"TELEMETRY_MORE",
tableName: Ornaments.localizableTableName,
bundle: .mapboxMaps,
value: "Tell Me More",
comment: "Telemetry prompt button"
)
}

enum GeofencingStrings {
static let geofencingName = NSLocalizedString(
"GEOFENCING_NAME",
tableName: Ornaments.localizableTableName,
bundle: .mapboxMaps,
value: "Mapbox Geofencing",
comment: "Action in attribution sheet"
)

static let geofencingTitle = NSLocalizedString(
"GEOFENCING_TITLE",
tableName: Ornaments.localizableTableName,
bundle: .mapboxMaps,
value: "Allow This App to Use Geofencing",
comment: "Geofencing prompt title"
)

static let geofencingMessage = NSLocalizedString(
"GEOFENCING_MSG",
tableName: Ornaments.localizableTableName,
bundle: .mapboxMaps,
value: """
This app uses Mapbox Geofencing to detect your device’s presence in areas the app developer has defined.
Only the app developer can see where those areas are.
You have the option to disable Mapbox Geofencing, which may affect app functionality.
""",
comment: "Geofencing prompt message"
)

static let geofencingEnabledOnMessage = NSLocalizedString(
"GEOFENCING_ENABLED_ON",
tableName: Ornaments.localizableTableName,
bundle: .mapboxMaps,
value: "Keep Geofencing enabled",
comment: "Geofencing prompt button"
)

static let geofencingEnabledOffMessage = NSLocalizedString(
"GEOFENCING_ENABLED_OFF",
tableName: Ornaments.localizableTableName,
bundle: .mapboxMaps,
value: "Disable Geofencing",
comment: "Geofencing prompt button"
)

static let geofencingDisabledOnMessage = NSLocalizedString(
"GEOFENCING_DISABLED_ON",
tableName: Ornaments.localizableTableName,
bundle: .mapboxMaps,
value: "Enable Geofencing",
comment: "Geofencing prompt button"
)

static let geofencingDisabledOffMessage = NSLocalizedString(
"GEOFENCING_DISABLED_OFF",
tableName: Ornaments.localizableTableName,
bundle: .mapboxMaps,
value: "Keep Geofencing Disabled",
comment: "Geofencing prompt button"
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,19 @@ class InfoButtonOrnamentTests: XCTestCase {
var attributionDialogManager: AttributionDialogManager!
var tapCompletion: (() -> Void)?

private var isGeofenceActive: Bool = false
private var isGeofenceConsentGiven: Bool = true

override func setUp() {
super.setUp()
parentViewController = MockParentViewController()
attributionDialogManager = AttributionDialogManager(dataSource: self, delegate: self)
attributionDialogManager = AttributionDialogManager(
dataSource: self,
delegate: self,
isGeofenceActive: { self.isGeofenceActive },
setGeofenceConsent: { self.isGeofenceConsentGiven = $0 },
getGeofenceConsent: { self.isGeofenceConsentGiven }
)
}

func testInfoButtonTapped() throws {
Expand Down
Loading

0 comments on commit c1c75f1

Please sign in to comment.