A game about forced loneliness, made by TACStudios
1#if defined(__APPLE__)
2#include <TargetConditionals.h>
3#if TARGET_OS_IOS
4
5#include <CoreMotion/CoreMotion.h>
6
7#define ENABLE_STEP_COUNTER_LOGGING 1
8#if ENABLE_STEP_COUNTER_LOGGING
9#define STEP_COUNTER_LOG(...) NSLog(@"StepCounter - %@", [NSString stringWithFormat: __VA_ARGS__])
10#else
11#define STEP_COUNTER_LOG(...) {}
12#endif
13
14class iOSStepCounterWrapper
15{
16public:
17 typedef void (*OnDataReceived) (int deviceId, int numberOfSteps);
18
19 struct iOSStepCounterCallbacks
20 {
21 OnDataReceived dataReceived;
22 };
23
24
25 iOSStepCounterWrapper()
26 {
27 m_Pedometer = nullptr;
28 m_Device = -1;
29 }
30
31 void Enable(int deviceId, iOSStepCounterCallbacks* callbacks)
32 {
33 if (IsEnabled())
34 {
35 STEP_COUNTER_LOG(@"Was already enabled?");
36 if (m_Device != deviceId)
37 STEP_COUNTER_LOG(@"Enabling with different device id? Expected %d, was %d. Are you creating more than one iOSStepCounter", m_Device, deviceId);
38 }
39 m_Pedometer = [[CMPedometer alloc]init];
40 m_Callbacks = *callbacks;
41 m_Device = deviceId;
42 [m_Pedometer startPedometerUpdatesFromDate: [NSDate date] withHandler:^(CMPedometerData * _Nullable pedometerData, NSError * _Nullable error)
43 {
44 // Note: We need to call our callback on the same thread the Unity scripting is operating
45 dispatch_async(dispatch_get_main_queue(), ^{
46 if (error != nil)
47 {
48 STEP_COUNTER_LOG(@"startPedometerUpdatesFromDate threw an error '%@', was it authorized?", [error localizedDescription]);
49 if (m_Device != -1)
50 Disable(m_Device);
51 return;
52 }
53 // Guard against situation where device was disabled, any event which are received after that, should be ignored.
54 if (m_Device == -1)
55 return;
56 m_Callbacks.dataReceived(m_Device, [pedometerData.numberOfSteps intValue]);
57 });
58 }];
59 }
60
61 bool Disable(int deviceId)
62 {
63 if (m_Pedometer == nullptr)
64 return false;
65 if (m_Device != deviceId)
66 STEP_COUNTER_LOG(@"Disabling with wrong device id, expected %d, was %d", m_Device, deviceId);
67 [m_Pedometer stopPedometerUpdates];
68 m_Pedometer = nullptr;
69 m_Device = -1;
70 return true;
71 }
72
73 bool IsEnabled() const
74 {
75 return m_Pedometer != nullptr;
76 }
77
78private:
79 CMPedometer* m_Pedometer;
80 iOSStepCounterCallbacks m_Callbacks;
81 int m_Device;
82};
83
84static iOSStepCounterWrapper s_Wrapper;
85static const int kResultSuccess = 1;
86static const int kResultFailure = -1;
87
88extern "C" int _iOSStepCounterIsAvailable()
89{
90 return [CMPedometer isStepCountingAvailable] ? 1 : 0;
91}
92
93extern "C" int _iOSStepCounterGetAuthorizationStatus()
94{
95 if (@available(iOS 11.0, *))
96 {
97 return (int)[CMPedometer authorizationStatus];
98 }
99 return 0;
100}
101
102extern "C" int _iOSStepCounterEnable(int deviceId, iOSStepCounterWrapper::iOSStepCounterCallbacks* callbacks, int sizeOfCallbacks)
103{
104 if (sizeof(iOSStepCounterWrapper::iOSStepCounterCallbacks) != sizeOfCallbacks)
105 {
106 STEP_COUNTER_LOG(@"iOSStepCounterCallbacks size mismatch, expected %lu was %d", sizeof(iOSStepCounterWrapper::iOSStepCounterCallbacks), sizeOfCallbacks);
107 return kResultFailure;
108 }
109
110 if (_iOSStepCounterIsAvailable() == 0)
111 {
112 STEP_COUNTER_LOG(@"Step counting is not available");
113 return kResultFailure;
114 }
115
116 NSString* motionUsage = @"NSMotionUsageDescription";
117
118 if ([[NSBundle mainBundle] objectForInfoDictionaryKey: motionUsage] == nil)
119 {
120 STEP_COUNTER_LOG(@"%@ is missing in Info.plist, please enable Motion Usage in Input Settings", motionUsage);
121 return kResultFailure;
122 }
123
124 if (@available(iOS 11.0, *))
125 {
126 if ([CMPedometer authorizationStatus] == CMAuthorizationStatusRestricted)
127 {
128 STEP_COUNTER_LOG(@"Step Counter was restricted.");
129 return kResultFailure;
130 }
131
132 if ([CMPedometer authorizationStatus] == CMAuthorizationStatusDenied)
133 {
134 STEP_COUNTER_LOG(@"Step Counter was denied. Enable Motion & Fitness under app settings.");
135 return kResultFailure;
136 }
137 // Do nothing for Authorized and NotDetermined
138 }
139
140 // Note: After installation this function will prompt a dialog asking about Motion & Fitness authorization
141 // If user denies the prompt, there will be an error in startPedometerUpdatesFromDate callback
142 // The dialog only appears once, not sure how to trigger it again, besides reinstalling app
143 s_Wrapper.Enable(deviceId, callbacks);
144
145 return kResultSuccess;
146}
147
148extern "C" int _iOSStepCounterDisable(int deviceId)
149{
150 return s_Wrapper.Disable(deviceId) ? kResultSuccess : kResultFailure;
151}
152
153extern "C" int _iOSStepCounterIsEnabled(int deviceId)
154{
155 return s_Wrapper.IsEnabled() ? 1 : 0;
156}
157
158#endif
159#endif