@rejourneyco/react-native 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/android/build.gradle.kts +135 -0
  2. package/android/consumer-rules.pro +10 -0
  3. package/android/proguard-rules.pro +1 -0
  4. package/android/src/main/AndroidManifest.xml +15 -0
  5. package/android/src/main/java/com/rejourney/RejourneyModuleImpl.kt +2981 -0
  6. package/android/src/main/java/com/rejourney/capture/ANRHandler.kt +206 -0
  7. package/android/src/main/java/com/rejourney/capture/ActivityTracker.kt +98 -0
  8. package/android/src/main/java/com/rejourney/capture/CaptureEngine.kt +1553 -0
  9. package/android/src/main/java/com/rejourney/capture/CaptureHeuristics.kt +375 -0
  10. package/android/src/main/java/com/rejourney/capture/CrashHandler.kt +153 -0
  11. package/android/src/main/java/com/rejourney/capture/MotionEvent.kt +215 -0
  12. package/android/src/main/java/com/rejourney/capture/SegmentUploader.kt +512 -0
  13. package/android/src/main/java/com/rejourney/capture/VideoEncoder.kt +773 -0
  14. package/android/src/main/java/com/rejourney/capture/ViewHierarchyScanner.kt +633 -0
  15. package/android/src/main/java/com/rejourney/capture/ViewSerializer.kt +286 -0
  16. package/android/src/main/java/com/rejourney/core/Constants.kt +117 -0
  17. package/android/src/main/java/com/rejourney/core/Logger.kt +93 -0
  18. package/android/src/main/java/com/rejourney/core/Types.kt +124 -0
  19. package/android/src/main/java/com/rejourney/lifecycle/SessionLifecycleService.kt +162 -0
  20. package/android/src/main/java/com/rejourney/network/DeviceAuthManager.kt +747 -0
  21. package/android/src/main/java/com/rejourney/network/HttpClientProvider.kt +16 -0
  22. package/android/src/main/java/com/rejourney/network/NetworkMonitor.kt +272 -0
  23. package/android/src/main/java/com/rejourney/network/UploadManager.kt +1363 -0
  24. package/android/src/main/java/com/rejourney/network/UploadWorker.kt +492 -0
  25. package/android/src/main/java/com/rejourney/privacy/PrivacyMask.kt +645 -0
  26. package/android/src/main/java/com/rejourney/touch/GestureClassifier.kt +233 -0
  27. package/android/src/main/java/com/rejourney/touch/KeyboardTracker.kt +158 -0
  28. package/android/src/main/java/com/rejourney/touch/TextInputTracker.kt +181 -0
  29. package/android/src/main/java/com/rejourney/touch/TouchInterceptor.kt +591 -0
  30. package/android/src/main/java/com/rejourney/utils/EventBuffer.kt +284 -0
  31. package/android/src/main/java/com/rejourney/utils/OEMDetector.kt +154 -0
  32. package/android/src/main/java/com/rejourney/utils/PerfTiming.kt +235 -0
  33. package/android/src/main/java/com/rejourney/utils/Telemetry.kt +297 -0
  34. package/android/src/main/java/com/rejourney/utils/WindowUtils.kt +84 -0
  35. package/android/src/newarch/java/com/rejourney/RejourneyModule.kt +187 -0
  36. package/android/src/newarch/java/com/rejourney/RejourneyPackage.kt +40 -0
  37. package/android/src/oldarch/java/com/rejourney/RejourneyModule.kt +218 -0
  38. package/android/src/oldarch/java/com/rejourney/RejourneyPackage.kt +23 -0
  39. package/ios/Capture/RJANRHandler.h +42 -0
  40. package/ios/Capture/RJANRHandler.m +328 -0
  41. package/ios/Capture/RJCaptureEngine.h +275 -0
  42. package/ios/Capture/RJCaptureEngine.m +2062 -0
  43. package/ios/Capture/RJCaptureHeuristics.h +80 -0
  44. package/ios/Capture/RJCaptureHeuristics.m +903 -0
  45. package/ios/Capture/RJCrashHandler.h +46 -0
  46. package/ios/Capture/RJCrashHandler.m +313 -0
  47. package/ios/Capture/RJMotionEvent.h +183 -0
  48. package/ios/Capture/RJMotionEvent.m +183 -0
  49. package/ios/Capture/RJPerformanceManager.h +100 -0
  50. package/ios/Capture/RJPerformanceManager.m +373 -0
  51. package/ios/Capture/RJPixelBufferDownscaler.h +42 -0
  52. package/ios/Capture/RJPixelBufferDownscaler.m +85 -0
  53. package/ios/Capture/RJSegmentUploader.h +146 -0
  54. package/ios/Capture/RJSegmentUploader.m +778 -0
  55. package/ios/Capture/RJVideoEncoder.h +247 -0
  56. package/ios/Capture/RJVideoEncoder.m +1036 -0
  57. package/ios/Capture/RJViewControllerTracker.h +73 -0
  58. package/ios/Capture/RJViewControllerTracker.m +508 -0
  59. package/ios/Capture/RJViewHierarchyScanner.h +215 -0
  60. package/ios/Capture/RJViewHierarchyScanner.m +1464 -0
  61. package/ios/Capture/RJViewSerializer.h +119 -0
  62. package/ios/Capture/RJViewSerializer.m +498 -0
  63. package/ios/Core/RJConstants.h +124 -0
  64. package/ios/Core/RJConstants.m +88 -0
  65. package/ios/Core/RJLifecycleManager.h +85 -0
  66. package/ios/Core/RJLifecycleManager.m +308 -0
  67. package/ios/Core/RJLogger.h +61 -0
  68. package/ios/Core/RJLogger.m +211 -0
  69. package/ios/Core/RJTypes.h +176 -0
  70. package/ios/Core/RJTypes.m +66 -0
  71. package/ios/Core/Rejourney.h +64 -0
  72. package/ios/Core/Rejourney.mm +2495 -0
  73. package/ios/Network/RJDeviceAuthManager.h +94 -0
  74. package/ios/Network/RJDeviceAuthManager.m +967 -0
  75. package/ios/Network/RJNetworkMonitor.h +68 -0
  76. package/ios/Network/RJNetworkMonitor.m +267 -0
  77. package/ios/Network/RJRetryManager.h +73 -0
  78. package/ios/Network/RJRetryManager.m +325 -0
  79. package/ios/Network/RJUploadManager.h +267 -0
  80. package/ios/Network/RJUploadManager.m +2296 -0
  81. package/ios/Privacy/RJPrivacyMask.h +163 -0
  82. package/ios/Privacy/RJPrivacyMask.m +922 -0
  83. package/ios/Rejourney.h +63 -0
  84. package/ios/Touch/RJGestureClassifier.h +130 -0
  85. package/ios/Touch/RJGestureClassifier.m +333 -0
  86. package/ios/Touch/RJTouchInterceptor.h +169 -0
  87. package/ios/Touch/RJTouchInterceptor.m +772 -0
  88. package/ios/Utils/RJEventBuffer.h +112 -0
  89. package/ios/Utils/RJEventBuffer.m +358 -0
  90. package/ios/Utils/RJGzipUtils.h +33 -0
  91. package/ios/Utils/RJGzipUtils.m +89 -0
  92. package/ios/Utils/RJKeychainManager.h +48 -0
  93. package/ios/Utils/RJKeychainManager.m +111 -0
  94. package/ios/Utils/RJPerfTiming.h +209 -0
  95. package/ios/Utils/RJPerfTiming.m +264 -0
  96. package/ios/Utils/RJTelemetry.h +92 -0
  97. package/ios/Utils/RJTelemetry.m +320 -0
  98. package/ios/Utils/RJWindowUtils.h +66 -0
  99. package/ios/Utils/RJWindowUtils.m +133 -0
  100. package/lib/commonjs/NativeRejourney.js +40 -0
  101. package/lib/commonjs/components/Mask.js +79 -0
  102. package/lib/commonjs/index.js +1381 -0
  103. package/lib/commonjs/sdk/autoTracking.js +1259 -0
  104. package/lib/commonjs/sdk/constants.js +151 -0
  105. package/lib/commonjs/sdk/errorTracking.js +199 -0
  106. package/lib/commonjs/sdk/index.js +50 -0
  107. package/lib/commonjs/sdk/metricsTracking.js +204 -0
  108. package/lib/commonjs/sdk/navigation.js +151 -0
  109. package/lib/commonjs/sdk/networkInterceptor.js +412 -0
  110. package/lib/commonjs/sdk/utils.js +363 -0
  111. package/lib/commonjs/types/expo-router.d.js +2 -0
  112. package/lib/commonjs/types/index.js +2 -0
  113. package/lib/module/NativeRejourney.js +38 -0
  114. package/lib/module/components/Mask.js +72 -0
  115. package/lib/module/index.js +1284 -0
  116. package/lib/module/sdk/autoTracking.js +1233 -0
  117. package/lib/module/sdk/constants.js +145 -0
  118. package/lib/module/sdk/errorTracking.js +189 -0
  119. package/lib/module/sdk/index.js +12 -0
  120. package/lib/module/sdk/metricsTracking.js +187 -0
  121. package/lib/module/sdk/navigation.js +143 -0
  122. package/lib/module/sdk/networkInterceptor.js +401 -0
  123. package/lib/module/sdk/utils.js +342 -0
  124. package/lib/module/types/expo-router.d.js +2 -0
  125. package/lib/module/types/index.js +2 -0
  126. package/lib/typescript/NativeRejourney.d.ts +147 -0
  127. package/lib/typescript/components/Mask.d.ts +39 -0
  128. package/lib/typescript/index.d.ts +117 -0
  129. package/lib/typescript/sdk/autoTracking.d.ts +204 -0
  130. package/lib/typescript/sdk/constants.d.ts +120 -0
  131. package/lib/typescript/sdk/errorTracking.d.ts +32 -0
  132. package/lib/typescript/sdk/index.d.ts +9 -0
  133. package/lib/typescript/sdk/metricsTracking.d.ts +58 -0
  134. package/lib/typescript/sdk/navigation.d.ts +33 -0
  135. package/lib/typescript/sdk/networkInterceptor.d.ts +47 -0
  136. package/lib/typescript/sdk/utils.d.ts +148 -0
  137. package/lib/typescript/types/index.d.ts +624 -0
  138. package/package.json +102 -0
  139. package/rejourney.podspec +21 -0
  140. package/src/NativeRejourney.ts +165 -0
  141. package/src/components/Mask.tsx +80 -0
  142. package/src/index.ts +1459 -0
  143. package/src/sdk/autoTracking.ts +1373 -0
  144. package/src/sdk/constants.ts +134 -0
  145. package/src/sdk/errorTracking.ts +231 -0
  146. package/src/sdk/index.ts +11 -0
  147. package/src/sdk/metricsTracking.ts +232 -0
  148. package/src/sdk/navigation.ts +157 -0
  149. package/src/sdk/networkInterceptor.ts +440 -0
  150. package/src/sdk/utils.ts +369 -0
  151. package/src/types/expo-router.d.ts +7 -0
  152. package/src/types/index.ts +739 -0
@@ -0,0 +1,183 @@
1
+ //
2
+ // RJMotionEvent.m
3
+ // Rejourney
4
+ //
5
+ // Motion event implementation.
6
+ //
7
+ // Licensed under the Apache License, Version 2.0 (the "License");
8
+ // you may not use this file except in compliance with the License.
9
+ // You may obtain a copy of the License at
10
+ //
11
+ // http://www.apache.org/licenses/LICENSE-2.0
12
+ //
13
+ // Unless required by applicable law or agreed to in writing, software
14
+ // distributed under the License is distributed on an "AS IS" BASIS,
15
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ // See the License for the specific language governing permissions and
17
+ // limitations under the License.
18
+ //
19
+ // Copyright (c) 2026 Rejourney
20
+ //
21
+
22
+ #import "RJMotionEvent.h"
23
+ #import <math.h>
24
+
25
+ @implementation RJMotionEvent
26
+
27
+ #pragma mark - Computed Properties
28
+
29
+ - (NSTimeInterval)duration {
30
+ return self.t1 - self.t0;
31
+ }
32
+
33
+ - (CGFloat)distance {
34
+ return sqrt(self.dx * self.dx + self.dy * self.dy);
35
+ }
36
+
37
+ - (CGFloat)averageVelocity {
38
+ NSTimeInterval dur = self.duration;
39
+ if (dur <= 0)
40
+ return 0;
41
+ return self.distance / (dur / 1000.0);
42
+ }
43
+
44
+ - (CGFloat)direction {
45
+ return atan2(self.dy, self.dx);
46
+ }
47
+
48
+ #pragma mark - Serialization
49
+
50
+ - (NSDictionary *)toDictionary {
51
+ NSMutableDictionary *dict = [NSMutableDictionary dictionary];
52
+
53
+ dict[@"type"] = [RJMotionEvent motionTypeName:self.type];
54
+ dict[@"t0"] = @(self.t0);
55
+ dict[@"t1"] = @(self.t1);
56
+ dict[@"dx"] = @(self.dx);
57
+ dict[@"dy"] = @(self.dy);
58
+ dict[@"v0"] = @(self.v0);
59
+ dict[@"v1"] = @(self.v1);
60
+ dict[@"curve"] = [RJMotionEvent curveNameForType:self.curve];
61
+ dict[@"duration"] = @(self.duration);
62
+ dict[@"distance"] = @(self.distance);
63
+
64
+ if (self.targetId) {
65
+ dict[@"targetId"] = self.targetId;
66
+ }
67
+
68
+ return [dict copy];
69
+ }
70
+
71
+ + (instancetype)eventFromDictionary:(NSDictionary *)dict {
72
+ if (!dict || ![dict isKindOfClass:[NSDictionary class]]) {
73
+ return nil;
74
+ }
75
+
76
+ RJMotionEvent *event = [[RJMotionEvent alloc] init];
77
+
78
+ NSString *typeName = dict[@"type"];
79
+ if (typeName) {
80
+ event.type = [RJMotionEvent motionTypeFromName:typeName];
81
+ }
82
+
83
+ NSNumber *t0 = dict[@"t0"];
84
+ if (t0)
85
+ event.t0 = t0.doubleValue;
86
+
87
+ NSNumber *t1 = dict[@"t1"];
88
+ if (t1)
89
+ event.t1 = t1.doubleValue;
90
+
91
+ NSNumber *dx = dict[@"dx"];
92
+ if (dx)
93
+ event.dx = dx.doubleValue;
94
+
95
+ NSNumber *dy = dict[@"dy"];
96
+ if (dy)
97
+ event.dy = dy.doubleValue;
98
+
99
+ NSNumber *v0 = dict[@"v0"];
100
+ if (v0)
101
+ event.v0 = v0.doubleValue;
102
+
103
+ NSNumber *v1 = dict[@"v1"];
104
+ if (v1)
105
+ event.v1 = v1.doubleValue;
106
+
107
+ NSString *curveName = dict[@"curve"];
108
+ if (curveName) {
109
+ event.curve = [RJMotionEvent curveTypeFromName:curveName];
110
+ }
111
+
112
+ event.targetId = dict[@"targetId"];
113
+
114
+ return event;
115
+ }
116
+
117
+ #pragma mark - Curve Helpers
118
+
119
+ + (NSString *)curveNameForType:(RJMotionCurve)curve {
120
+ switch (curve) {
121
+ case RJMotionCurveLinear:
122
+ return @"linear";
123
+ case RJMotionCurveExponentialDecay:
124
+ return @"exponential_decay";
125
+ case RJMotionCurveEaseOut:
126
+ return @"ease_out";
127
+ case RJMotionCurveBounce:
128
+ return @"bounce";
129
+ case RJMotionCurveSpring:
130
+ return @"spring";
131
+ }
132
+ return @"linear";
133
+ }
134
+
135
+ + (RJMotionCurve)curveTypeFromName:(NSString *)name {
136
+ if ([name isEqualToString:@"exponential_decay"]) {
137
+ return RJMotionCurveExponentialDecay;
138
+ } else if ([name isEqualToString:@"ease_out"]) {
139
+ return RJMotionCurveEaseOut;
140
+ } else if ([name isEqualToString:@"bounce"]) {
141
+ return RJMotionCurveBounce;
142
+ } else if ([name isEqualToString:@"spring"]) {
143
+ return RJMotionCurveSpring;
144
+ }
145
+ return RJMotionCurveLinear;
146
+ }
147
+
148
+ + (NSString *)motionTypeName:(RJMotionType)type {
149
+ switch (type) {
150
+ case RJMotionTypeScroll:
151
+ return @"scroll";
152
+ case RJMotionTypePan:
153
+ return @"pan";
154
+ case RJMotionTypeSwipe:
155
+ return @"swipe";
156
+ case RJMotionTypeFling:
157
+ return @"fling";
158
+ }
159
+ return @"scroll";
160
+ }
161
+
162
+ + (RJMotionType)motionTypeFromName:(NSString *)name {
163
+ if ([name isEqualToString:@"pan"]) {
164
+ return RJMotionTypePan;
165
+ } else if ([name isEqualToString:@"swipe"]) {
166
+ return RJMotionTypeSwipe;
167
+ } else if ([name isEqualToString:@"fling"]) {
168
+ return RJMotionTypeFling;
169
+ }
170
+ return RJMotionTypeScroll;
171
+ }
172
+
173
+ #pragma mark - Instance Convenience Methods
174
+
175
+ - (NSString *)typeName {
176
+ return [RJMotionEvent motionTypeName:self.type];
177
+ }
178
+
179
+ - (NSString *)curveName {
180
+ return [RJMotionEvent curveNameForType:self.curve];
181
+ }
182
+
183
+ @end
@@ -0,0 +1,100 @@
1
+ //
2
+ // RJPerformanceManager.h
3
+ // Rejourney
4
+ //
5
+ // System performance monitoring and adaptive throttling.
6
+ //
7
+ // Licensed under the Apache License, Version 2.0 (the "License");
8
+ // you may not use this file except in compliance with the License.
9
+ // You may obtain a copy of the License at
10
+ //
11
+ // http://www.apache.org/licenses/LICENSE-2.0
12
+ //
13
+ // Unless required by applicable law or agreed to in writing, software
14
+ // distributed under the License is distributed on an "AS IS" BASIS,
15
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ // See the License for the specific language governing permissions and
17
+ // limitations under the License.
18
+ //
19
+ // Copyright (c) 2026 Rejourney
20
+ //
21
+
22
+ #import "../Core/RJTypes.h"
23
+ #import <Foundation/Foundation.h>
24
+
25
+ NS_ASSUME_NONNULL_BEGIN
26
+
27
+ // RJPerformanceLevel is defined in RJTypes.h
28
+
29
+ /// Delegate for performance level changes
30
+ @protocol RJPerformanceManagerDelegate <NSObject>
31
+ @optional
32
+ - (void)performanceManagerDidChangeLevel:(RJPerformanceLevel)level;
33
+ - (void)performanceManagerDidReceiveMemoryWarning;
34
+ @end
35
+
36
+ /**
37
+ * Manages system performance monitoring including thermal state,
38
+ * battery level, and memory pressure.
39
+ */
40
+ @interface RJPerformanceManager : NSObject
41
+
42
+ /// Current performance level based on system conditions
43
+ @property(nonatomic, readonly) RJPerformanceLevel currentLevel;
44
+
45
+ /// Delegate for performance events
46
+ @property(nonatomic, weak, nullable) id<RJPerformanceManagerDelegate> delegate;
47
+
48
+ /// Whether thermal throttling is enabled
49
+ @property(nonatomic, assign) BOOL thermalThrottleEnabled;
50
+
51
+ /// Whether battery-aware throttling is enabled
52
+ @property(nonatomic, assign) BOOL batteryAwareEnabled;
53
+
54
+ /// Shared instance
55
+ + (instancetype)sharedManager;
56
+
57
+ /**
58
+ * Start monitoring system performance.
59
+ */
60
+ - (void)startMonitoring;
61
+
62
+ /**
63
+ * Stop monitoring system performance.
64
+ */
65
+ - (void)stopMonitoring;
66
+
67
+ /**
68
+ * Force a performance level update check.
69
+ */
70
+ - (void)updatePerformanceLevel;
71
+
72
+ /**
73
+ * Get current memory usage in bytes.
74
+ */
75
+ - (NSUInteger)currentMemoryUsage;
76
+
77
+ /**
78
+ * Handle memory warning - clears caches and notifies delegate.
79
+ */
80
+ - (void)handleMemoryWarning;
81
+
82
+ /**
83
+ * Get current CPU usage as percentage (0.0-100.0).
84
+ * Returns -1 if unavailable.
85
+ */
86
+ /**
87
+ * Get current CPU usage as percentage (0.0-100.0).
88
+ * Returns -1 if unavailable.
89
+ */
90
+ - (float)currentCPUUsage;
91
+
92
+ /**
93
+ * Returns YES if CPU usage is considered high enough to skip expensive
94
+ * operations.
95
+ */
96
+ @property(nonatomic, readonly) BOOL isCPUHigh;
97
+
98
+ @end
99
+
100
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,373 @@
1
+ //
2
+ // RJPerformanceManager.m
3
+ // Rejourney
4
+ //
5
+ // System performance monitoring and adaptive throttling implementation.
6
+ //
7
+ // Copyright (c) 2026 Rejourney
8
+ //
9
+
10
+ #import "RJPerformanceManager.h"
11
+ #import "../Core/RJConstants.h"
12
+ #import "../Core/RJLogger.h"
13
+ #import <UIKit/UIKit.h>
14
+ #import <mach/mach.h>
15
+
16
+ #pragma mark - Private Interface
17
+
18
+ @interface RJPerformanceManager ()
19
+
20
+ @property(nonatomic, assign) RJPerformanceLevel currentLevel;
21
+
22
+ @property(nonatomic, strong, nullable) dispatch_source_t memoryPressureSource;
23
+
24
+ @property(nonatomic, strong, nullable) NSTimer *cpuMonitorTimer;
25
+
26
+ @property(nonatomic, assign) float rollingCPUAverage;
27
+
28
+ @property(nonatomic, assign) NSInteger highCPUSampleCount;
29
+
30
+ @property(nonatomic, assign) BOOL isMonitoring;
31
+
32
+ @end
33
+
34
+ #pragma mark - Implementation
35
+
36
+ @implementation RJPerformanceManager
37
+
38
+ #pragma mark - Singleton
39
+
40
+ + (instancetype)sharedManager {
41
+ static RJPerformanceManager *sharedInstance = nil;
42
+ static dispatch_once_t onceToken;
43
+ dispatch_once(&onceToken, ^{
44
+ sharedInstance = [[self alloc] init];
45
+ });
46
+ return sharedInstance;
47
+ }
48
+
49
+ #pragma mark - Initialization
50
+
51
+ - (instancetype)init {
52
+ self = [super init];
53
+ if (self) {
54
+ _currentLevel = RJPerformanceLevelNormal;
55
+ _thermalThrottleEnabled = YES;
56
+ _batteryAwareEnabled = YES;
57
+ _isMonitoring = NO;
58
+ _rollingCPUAverage = 0.0;
59
+ _highCPUSampleCount = 0;
60
+ }
61
+ return self;
62
+ }
63
+
64
+ - (void)dealloc {
65
+ [self stopMonitoring];
66
+ }
67
+
68
+ #pragma mark - Monitoring Lifecycle
69
+
70
+ - (void)startMonitoring {
71
+ if (self.isMonitoring)
72
+ return;
73
+
74
+ self.isMonitoring = YES;
75
+
76
+ [self setupMemoryPressureMonitoring];
77
+
78
+ [[NSNotificationCenter defaultCenter]
79
+ addObserver:self
80
+ selector:@selector(handleMemoryWarningNotification:)
81
+ name:UIApplicationDidReceiveMemoryWarningNotification
82
+ object:nil];
83
+
84
+ if (@available(iOS 11.0, *)) {
85
+ [[NSNotificationCenter defaultCenter]
86
+ addObserver:self
87
+ selector:@selector(handleThermalStateChange:)
88
+ name:NSProcessInfoThermalStateDidChangeNotification
89
+ object:nil];
90
+ }
91
+
92
+ [[UIDevice currentDevice] setBatteryMonitoringEnabled:YES];
93
+ [[NSNotificationCenter defaultCenter]
94
+ addObserver:self
95
+ selector:@selector(handleBatteryStateChange:)
96
+ name:UIDeviceBatteryLevelDidChangeNotification
97
+ object:nil];
98
+
99
+ __weak typeof(self) weakSelf = self;
100
+ self.cpuMonitorTimer =
101
+ [NSTimer scheduledTimerWithTimeInterval:2.0
102
+ repeats:YES
103
+ block:^(NSTimer *timer) {
104
+ [weakSelf checkCPUUsage];
105
+ }];
106
+ [[NSRunLoop currentRunLoop] addTimer:self.cpuMonitorTimer
107
+ forMode:NSRunLoopCommonModes];
108
+
109
+ RJLogDebug(@"Performance monitoring started");
110
+ }
111
+
112
+ - (void)stopMonitoring {
113
+ if (!self.isMonitoring)
114
+ return;
115
+
116
+ self.isMonitoring = NO;
117
+
118
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
119
+
120
+ if (self.cpuMonitorTimer) {
121
+ [self.cpuMonitorTimer invalidate];
122
+ self.cpuMonitorTimer = nil;
123
+ }
124
+
125
+ if (self.memoryPressureSource) {
126
+ @try {
127
+ dispatch_source_cancel(self.memoryPressureSource);
128
+ } @catch (NSException *e) {
129
+ }
130
+ self.memoryPressureSource = nil;
131
+ }
132
+
133
+ RJLogDebug(@"Performance monitoring stopped");
134
+ }
135
+
136
+ #pragma mark - Memory Pressure Monitoring
137
+
138
+ - (void)setupMemoryPressureMonitoring {
139
+ dispatch_source_t source = dispatch_source_create(
140
+ DISPATCH_SOURCE_TYPE_MEMORYPRESSURE, 0,
141
+ DISPATCH_MEMORYPRESSURE_WARN | DISPATCH_MEMORYPRESSURE_CRITICAL,
142
+ dispatch_get_main_queue());
143
+
144
+ if (!source) {
145
+ RJLogWarning(@"Failed to create memory pressure source");
146
+ return;
147
+ }
148
+
149
+ __weak typeof(self) weakSelf = self;
150
+ dispatch_source_set_event_handler(source, ^{
151
+ __strong typeof(weakSelf) strongSelf = weakSelf;
152
+ if (!strongSelf)
153
+ return;
154
+
155
+ dispatch_source_memorypressure_flags_t flags =
156
+ dispatch_source_get_data(source);
157
+
158
+ if (flags & DISPATCH_MEMORYPRESSURE_CRITICAL) {
159
+ RJLogWarning(@"CRITICAL memory pressure - pausing captures");
160
+ [strongSelf setLevel:RJPerformanceLevelPaused];
161
+ [strongSelf handleMemoryWarning];
162
+ } else if (flags & DISPATCH_MEMORYPRESSURE_WARN) {
163
+ RJLogWarning(@"Memory pressure warning - reducing captures");
164
+ [strongSelf setLevel:RJPerformanceLevelMinimal];
165
+ }
166
+ });
167
+
168
+ dispatch_resume(source);
169
+ self.memoryPressureSource = source;
170
+ }
171
+
172
+ #pragma mark - Level Management
173
+
174
+ - (void)setLevel:(RJPerformanceLevel)level {
175
+ if (self.currentLevel != level) {
176
+ self.currentLevel = level;
177
+ if ([self.delegate
178
+ respondsToSelector:@selector(performanceManagerDidChangeLevel:)]) {
179
+ [self.delegate performanceManagerDidChangeLevel:level];
180
+ }
181
+ }
182
+ }
183
+
184
+ - (void)updatePerformanceLevel {
185
+ if (!self.thermalThrottleEnabled && !self.batteryAwareEnabled) {
186
+ [self setLevel:RJPerformanceLevelNormal];
187
+ return;
188
+ }
189
+
190
+ RJPerformanceLevel newLevel = RJPerformanceLevelNormal;
191
+
192
+ if (@available(iOS 11.0, *)) {
193
+ if (self.thermalThrottleEnabled) {
194
+ NSProcessInfoThermalState thermalState =
195
+ [[NSProcessInfo processInfo] thermalState];
196
+
197
+ switch (thermalState) {
198
+ case NSProcessInfoThermalStateCritical:
199
+ [self setLevel:RJPerformanceLevelPaused];
200
+ return;
201
+ case NSProcessInfoThermalStateSerious:
202
+ [self setLevel:RJPerformanceLevelMinimal];
203
+ return;
204
+ case NSProcessInfoThermalStateFair:
205
+ newLevel = RJPerformanceLevelReduced;
206
+ break;
207
+ case NSProcessInfoThermalStateNominal:
208
+ break;
209
+ }
210
+ }
211
+ }
212
+
213
+ if (self.batteryAwareEnabled) {
214
+ float batteryLevel = [[UIDevice currentDevice] batteryLevel];
215
+ UIDeviceBatteryState batteryState = [[UIDevice currentDevice] batteryState];
216
+
217
+ BOOL isCharging = (batteryState == UIDeviceBatteryStateCharging ||
218
+ batteryState == UIDeviceBatteryStateFull);
219
+
220
+ if (!isCharging && batteryLevel >= 0 &&
221
+ batteryLevel < RJLowBatteryThreshold) {
222
+ newLevel = MAX(newLevel, RJPerformanceLevelReduced);
223
+ }
224
+ }
225
+
226
+ NSUInteger usedMemory = [self currentMemoryUsage];
227
+ if (usedMemory > RJMemoryWarningThresholdBytes) {
228
+ newLevel = MAX(newLevel, RJPerformanceLevelReduced);
229
+ }
230
+
231
+ [self setLevel:newLevel];
232
+ }
233
+
234
+ #pragma mark - Notification Handlers
235
+
236
+ - (void)handleThermalStateChange:(NSNotification *)notification {
237
+ [self updatePerformanceLevel];
238
+ }
239
+
240
+ - (void)handleBatteryStateChange:(NSNotification *)notification {
241
+ [self updatePerformanceLevel];
242
+ }
243
+
244
+ - (void)handleMemoryWarningNotification:(NSNotification *)notification {
245
+ [self handleMemoryWarning];
246
+ }
247
+
248
+ #pragma mark - Memory Management
249
+
250
+ - (void)handleMemoryWarning {
251
+ RJLogWarning(@"Memory warning received");
252
+
253
+ [self setLevel:RJPerformanceLevelMinimal];
254
+
255
+ if ([self.delegate respondsToSelector:@selector
256
+ (performanceManagerDidReceiveMemoryWarning)]) {
257
+ [self.delegate performanceManagerDidReceiveMemoryWarning];
258
+ }
259
+ }
260
+
261
+ - (NSUInteger)currentMemoryUsage {
262
+ struct task_basic_info info;
263
+ mach_msg_type_number_t size = TASK_BASIC_INFO_COUNT;
264
+
265
+ kern_return_t result =
266
+ task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size);
267
+
268
+ return (result == KERN_SUCCESS) ? info.resident_size : 0;
269
+ }
270
+
271
+ #pragma mark - CPU Monitoring
272
+
273
+ - (float)currentCPUUsage {
274
+ thread_array_t threadList;
275
+ mach_msg_type_number_t threadCount;
276
+
277
+ kern_return_t kr = task_threads(mach_task_self(), &threadList, &threadCount);
278
+ if (kr != KERN_SUCCESS) {
279
+ return -1.0;
280
+ }
281
+
282
+ float totalUsage = 0.0;
283
+
284
+ for (mach_msg_type_number_t i = 0; i < threadCount; i++) {
285
+ thread_info_data_t threadInfo;
286
+ mach_msg_type_number_t threadInfoCount = THREAD_INFO_MAX;
287
+
288
+ kr = thread_info(threadList[i], THREAD_BASIC_INFO,
289
+ (thread_info_t)threadInfo, &threadInfoCount);
290
+
291
+ if (kr == KERN_SUCCESS) {
292
+ thread_basic_info_t basicInfo = (thread_basic_info_t)threadInfo;
293
+
294
+ if (!(basicInfo->flags & TH_FLAGS_IDLE)) {
295
+ totalUsage += basicInfo->cpu_usage / (float)TH_USAGE_SCALE * 100.0;
296
+ }
297
+ }
298
+
299
+ mach_port_deallocate(mach_task_self(), threadList[i]);
300
+ }
301
+
302
+ vm_deallocate(mach_task_self(), (vm_address_t)threadList,
303
+ threadCount * sizeof(thread_t));
304
+
305
+ return totalUsage;
306
+ }
307
+
308
+ - (BOOL)isCPUHigh {
309
+ return self.rollingCPUAverage >= 60.0;
310
+ }
311
+
312
+ - (void)checkCPUUsage {
313
+ float currentCPU = [self currentCPUUsage];
314
+ if (currentCPU < 0)
315
+ return;
316
+
317
+ if (self.rollingCPUAverage == 0) {
318
+ self.rollingCPUAverage = currentCPU;
319
+ } else {
320
+ self.rollingCPUAverage = 0.3 * currentCPU + 0.7 * self.rollingCPUAverage;
321
+ }
322
+
323
+ /*
324
+ * Updated CPU Thresholds:
325
+ * CRITICAL: > 90%
326
+ * HIGH: > 60%
327
+ * NORMAL: < 40%
328
+ */
329
+ const float CPU_CRITICAL_THRESHOLD = 90.0; // was 80
330
+ const float CPU_HIGH_THRESHOLD = 60.0; // was 60
331
+ const float CPU_NORMAL_THRESHOLD = 40.0;
332
+ const NSInteger HYSTERESIS_SAMPLES = 3;
333
+
334
+ RJPerformanceLevel suggestedLevel = RJPerformanceLevelNormal;
335
+
336
+ if (self.rollingCPUAverage >= CPU_CRITICAL_THRESHOLD) {
337
+ suggestedLevel = RJPerformanceLevelMinimal;
338
+ self.highCPUSampleCount++;
339
+ } else if (self.rollingCPUAverage >= CPU_HIGH_THRESHOLD) {
340
+ // Force Reduced level (scales down to 0.4) when CPU is high
341
+ suggestedLevel = RJPerformanceLevelReduced;
342
+ self.highCPUSampleCount++;
343
+ } else if (self.rollingCPUAverage < CPU_NORMAL_THRESHOLD) {
344
+ self.highCPUSampleCount = 0;
345
+ suggestedLevel = RJPerformanceLevelNormal;
346
+ } else {
347
+ // In hysteresis zone, maintain current level if it's already
348
+ // reduced/minimal
349
+ if (self.currentLevel >= RJPerformanceLevelReduced) {
350
+ return;
351
+ }
352
+ // Otherwise keep monitoring
353
+ return;
354
+ }
355
+
356
+ // Apply with hysteresis
357
+ if (suggestedLevel > self.currentLevel) {
358
+ if (self.highCPUSampleCount >= HYSTERESIS_SAMPLES) {
359
+ RJLogInfo(@"CPU usage high (%.1f%%), throttling to level %ld",
360
+ self.rollingCPUAverage, (long)suggestedLevel);
361
+ [self setLevel:suggestedLevel];
362
+ }
363
+ } else if (suggestedLevel < self.currentLevel) {
364
+ // Immediate recovery if CPU drops comfortably
365
+ if (self.highCPUSampleCount == 0) {
366
+ RJLogInfo(@"CPU usage normalized (%.1f%%), restoring level %ld",
367
+ self.rollingCPUAverage, (long)suggestedLevel);
368
+ [self setLevel:suggestedLevel];
369
+ }
370
+ }
371
+ }
372
+
373
+ @end
@@ -0,0 +1,42 @@
1
+
2
+ #import <CoreVideo/CoreVideo.h>
3
+ #import <Foundation/Foundation.h>
4
+
5
+ NS_ASSUME_NONNULL_BEGIN
6
+
7
+ /**
8
+ * Utility for high-performance pixel buffer downscaling using vImage
9
+ * (Accelerate). Moves scaling work off the main thread to avoid blocking UI.
10
+ */
11
+ typedef NS_ENUM(NSInteger, RJDownscaleQuality) {
12
+ RJDownscaleQualityBalanced = 0,
13
+ RJDownscaleQualityHigh = 1,
14
+ };
15
+
16
+ @interface RJPixelBufferDownscaler : NSObject
17
+
18
+ /**
19
+ * Downscales a source pixel buffer to target dimensions using vImage.
20
+ *
21
+ * @param src The source CVPixelBuffer (typically at native screen scale).
22
+ * @param dstW The target width.
23
+ * @param dstH The target height.
24
+ * @param pool A pixel buffer pool configured for the target dimensions
25
+ * (optional but recommended for perf).
26
+ * @return A new CVPixelBuffer containing the scaled image, or NULL if failed.
27
+ * Caller must release.
28
+ */
29
+ + (CVPixelBufferRef _Nullable)downscale:(CVPixelBufferRef)src
30
+ toW:(size_t)dstW
31
+ toH:(size_t)dstH
32
+ usingPool:(CVPixelBufferPoolRef _Nullable)pool;
33
+
34
+ + (CVPixelBufferRef _Nullable)downscale:(CVPixelBufferRef)src
35
+ toW:(size_t)dstW
36
+ toH:(size_t)dstH
37
+ usingPool:(CVPixelBufferPoolRef _Nullable)pool
38
+ quality:(RJDownscaleQuality)quality;
39
+
40
+ @end
41
+
42
+ NS_ASSUME_NONNULL_END