forked from rubymotion-community/BubbleWrap
/
location.rb
152 lines (134 loc) · 4.82 KB
/
location.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# Provides a nice DSL for interacting with the standard
# CLLocationManager
#
module BubbleWrap
module CLLocationWrap
def latitude
self.coordinate.latitude
end
def longitude
self.coordinate.longitude
end
end
module Location
module Error
DISABLED=0
PERMISSION_DENIED=1
NETWORK_FAILURE=2
LOCATION_UNKNOWN=3
end
module_function
# Start getting locations
# @param [Hash] options = {
# significant: true/false; whether to listen for significant location changes or
# all location changes (see Apple docs for info); default == false
# distance_filter: minimum change in distance to be updated about, in meters;
# default == uses KCLDistanceFilterNone,
# desired_accuracy: minimum accuracy for updates to arrive;
# any of :best_for_navigation, :best, :nearest_ten_meters,
# :hundred_meters, :kilometer, or :three_kilometers; default == :best
# purpose: string to display when the system asks user for location,
# retries: if location cant be found. how many errors do we retry; default == 5
# }
# @block for callback. takes one argument, `result`.
# - On error or cancelled, is called with a hash {error: BW::Location::Error::<Type>}
# - On success, is called with a hash {to: #<CLLocation>, from: #<CLLocation>}
#
# Example
# BW::Location.get(distance_filter: 10, desired_accuracy: :nearest_ten_meters) do |result|
# result[:to].class == CLLocation
# result[:from].class == CLLocation
# p "Lat #{result[:to].latitude}, Long #{result[:to].longitude}"
# end
def get(options = {}, &block)
@callback = block
@options = options
@options[:significant] = false if @options[:significant].nil?
@options[:distance_filter] ||= KCLDistanceFilterNone
@options[:desired_accuracy] ||= KCLLocationAccuracyBest
@options[:retries] ||= 5
@retries = 0
if not enabled?
error(Error::DISABLED) and return
end
self.location_manager.distanceFilter = @options[:distance_filter]
self.location_manager.desiredAccuracy = const_int_get("KCLLocationAccuracy", @options[:desired_accuracy])
self.location_manager.purpose = @options[:purpose] if @options[:purpose]
if @options[:significant]
self.location_manager.startMonitoringSignificantLocationChanges
else
self.location_manager.startUpdatingLocation
end
end
def get_significant(options = {}, &block)
get(options.merge(significant: true), &block)
end
# Stop getting locations
def stop
if @options[:significant]
self.location_manager.stopMonitoringSignificantLocationChanges
else
self.location_manager.stopUpdatingLocation
end
end
def location_manager
@location_manager ||= CLLocationManager.alloc.init
@location_manager.delegate ||= self
@location_manager
end
# returns true/false whether services are enabled for the _device_
def enabled?
CLLocationManager.locationServicesEnabled
end
def error(type)
@callback && @callback.call({ error: type })
@callback = nil
self.location_manager.stopUpdatingLocation
end
##########
# CLLocationManagerDelegate Methods
def locationManager(manager, didUpdateToLocation:newLocation, fromLocation:oldLocation)
@callback.call({to: newLocation, from: oldLocation})
end
def locationManager(manager, didFailWithError:error)
if error.domain == KCLErrorDomain
case error.code
when KCLErrorDenied
error(Error::PERMISSION_DENIED)
when KCLErrorLocationUnknown
# Docs specify that this is a temporary error,
# so we stop/start updating to try again.
@retries += 1
if @retries > @options[:retries]
error(Error::LOCATION_UNKNOWN)
else
self.location_manager.stopUpdatingLocation
self.location_manager.startUpdatingLocation
end
when KCLErrorNetwork
error(Error::NETWORK_FAILURE)
end
end
end
def locationManager(manager, didChangeAuthorizationStatus:status)
case status
when KCLAuthorizationStatusRestricted
error(Error::PERMISSION_DENIED)
when KCLAuthorizationStatusDenied
error(Error::PERMISSION_DENIED)
end
end
def const_int_get(base, value)
return value if value.is_a? Numeric
value = value.to_s.camelize
Kernel.const_get("#{base}#{value}")
end
def load_constants_hack
[KCLLocationAccuracyBestForNavigation, KCLLocationAccuracyBest,
KCLLocationAccuracyNearestTenMeters, KCLLocationAccuracyHundredMeters,
KCLLocationAccuracyKilometer, KCLLocationAccuracyThreeKilometers,
]
end
end
end
::Location = BubbleWrap::Location