File size: 5,050 Bytes
18a519f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
153
154
155
156
157
158
159
160
#if defined(__APPLE__)
#include <TargetConditionals.h>
#if TARGET_OS_IOS

#include <CoreMotion/CoreMotion.h>

#define ENABLE_STEP_COUNTER_LOGGING 1
#if ENABLE_STEP_COUNTER_LOGGING
#define STEP_COUNTER_LOG(...) NSLog(@"StepCounter - %@", [NSString stringWithFormat: __VA_ARGS__])
#else
#define STEP_COUNTER_LOG(...) {}
#endif

class iOSStepCounterWrapper
{
public:
    typedef void (*OnDataReceived) (int deviceId, int numberOfSteps);

    struct iOSStepCounterCallbacks
    {
        OnDataReceived dataReceived;
    };


    iOSStepCounterWrapper()
    {
        m_Pedometer = nullptr;
        m_Device = -1;
    }

    void Enable(int deviceId, iOSStepCounterCallbacks* callbacks)
    {
        if (IsEnabled())
        {
            STEP_COUNTER_LOG(@"Was already enabled?");
            if (m_Device != deviceId)
                STEP_COUNTER_LOG(@"Enabling with different device id? Expected %d, was %d. Are you creating more than one iOSStepCounter", m_Device, deviceId);
        }
        m_Pedometer = [[CMPedometer alloc]init];
        m_Callbacks = *callbacks;
        m_Device = deviceId;
        [m_Pedometer startPedometerUpdatesFromDate: [NSDate date] withHandler:^(CMPedometerData * _Nullable pedometerData, NSError * _Nullable error)
        {
            // Note: We need to call our callback on the same thread the Unity scripting is operating
            dispatch_async(dispatch_get_main_queue(), ^{
                if (error != nil)
                {
                    STEP_COUNTER_LOG(@"startPedometerUpdatesFromDate threw an error '%@', was it authorized?", [error localizedDescription]);
                    if (m_Device != -1)
                        Disable(m_Device);
                    return;
                }
                // Guard against situation where device was disabled, any event which are received after that, should be ignored.
                if (m_Device == -1)
                    return;
                m_Callbacks.dataReceived(m_Device, [pedometerData.numberOfSteps intValue]);
            });
        }];
    }

    bool Disable(int deviceId)
    {
        if (m_Pedometer == nullptr)
            return false;
        if (m_Device != deviceId)
            STEP_COUNTER_LOG(@"Disabling with wrong device id, expected %d, was %d", m_Device, deviceId);
        [m_Pedometer stopPedometerUpdates];
        m_Pedometer = nullptr;
        m_Device = -1;
        return true;
    }

    bool IsEnabled() const
    {
        return m_Pedometer != nullptr;
    }

private:
    CMPedometer* m_Pedometer;
    iOSStepCounterCallbacks m_Callbacks;
    int m_Device;
};

static iOSStepCounterWrapper s_Wrapper;
static const int kResultSuccess = 1;
static const int kResultFailure = -1;

extern "C" int _iOSStepCounterIsAvailable()
{
    return [CMPedometer isStepCountingAvailable] ? 1 : 0;
}

extern "C" int _iOSStepCounterGetAuthorizationStatus()
{
    if (@available(iOS 11.0, *))
    {
        return (int)[CMPedometer authorizationStatus];
    }
    return 0;
}

extern "C" int _iOSStepCounterEnable(int deviceId, iOSStepCounterWrapper::iOSStepCounterCallbacks* callbacks, int sizeOfCallbacks)
{
    if (sizeof(iOSStepCounterWrapper::iOSStepCounterCallbacks) != sizeOfCallbacks)
    {
        STEP_COUNTER_LOG(@"iOSStepCounterCallbacks size mismatch, expected %lu was %d", sizeof(iOSStepCounterWrapper::iOSStepCounterCallbacks), sizeOfCallbacks);
        return kResultFailure;
    }

    if (_iOSStepCounterIsAvailable() == 0)
    {
        STEP_COUNTER_LOG(@"Step counting is not available");
        return kResultFailure;
    }

    NSString* motionUsage = @"NSMotionUsageDescription";

    if ([[NSBundle mainBundle] objectForInfoDictionaryKey: motionUsage] == nil)
    {
        STEP_COUNTER_LOG(@"%@ is missing in Info.plist, please enable Motion Usage in Input Settings", motionUsage);
        return kResultFailure;
    }

    if (@available(iOS 11.0, *))
    {
        if ([CMPedometer authorizationStatus] == CMAuthorizationStatusRestricted)
        {
            STEP_COUNTER_LOG(@"Step Counter was restricted.");
            return kResultFailure;
        }

        if ([CMPedometer authorizationStatus] == CMAuthorizationStatusDenied)
        {
            STEP_COUNTER_LOG(@"Step Counter was denied. Enable Motion & Fitness under app settings.");
            return kResultFailure;
        }
        // Do nothing for Authorized and NotDetermined
    }

    // Note: After installation this function will prompt a dialog asking about Motion & Fitness authorization
    //       If user denies the prompt, there will be an error in startPedometerUpdatesFromDate callback
    //       The dialog only appears once, not sure how to trigger it again, besides reinstalling app
    s_Wrapper.Enable(deviceId, callbacks);

    return kResultSuccess;
}

extern "C" int _iOSStepCounterDisable(int deviceId)
{
    return s_Wrapper.Disable(deviceId) ? kResultSuccess : kResultFailure;
}

extern "C" int _iOSStepCounterIsEnabled(int deviceId)
{
    return s_Wrapper.IsEnabled() ? 1 : 0;
}

#endif
#endif