@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,124 @@
1
+ //
2
+ // RJConstants.h
3
+ // Rejourney
4
+ //
5
+ // SDK-wide constants and configuration defaults.
6
+ // This file contains all magic numbers and configuration values
7
+ // used throughout the Rejourney SDK.
8
+ //
9
+ // Video Capture Model:
10
+ // - H.264 video segments at 1 FPS
11
+ // - JSON view hierarchy snapshots
12
+ // - Motion events for gesture reconstruction
13
+ //
14
+ // Licensed under the Apache License, Version 2.0 (the "License");
15
+ // you may not use this file except in compliance with the License.
16
+ // You may obtain a copy of the License at
17
+ //
18
+ // http://www.apache.org/licenses/LICENSE-2.0
19
+ //
20
+ // Unless required by applicable law or agreed to in writing, software
21
+ // distributed under the License is distributed on an "AS IS" BASIS,
22
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23
+ // See the License for the specific language governing permissions and
24
+ // limitations under the License.
25
+ //
26
+ // Copyright (c) 2026 Rejourney
27
+ //
28
+
29
+ #import <Foundation/Foundation.h>
30
+
31
+ NS_ASSUME_NONNULL_BEGIN
32
+
33
+ #pragma mark - SDK Version
34
+
35
+ /// Current SDK version string
36
+ FOUNDATION_EXTERN NSString *const RJSDKVersion;
37
+
38
+ #pragma mark - Capture Configuration (Video Model)
39
+
40
+ /// Default capture scale factor (0.35 = 35% of original size for video)
41
+ FOUNDATION_EXTERN const CGFloat RJDefaultCaptureScale;
42
+
43
+ #pragma mark - Motion Event Configuration
44
+
45
+ /// Minimum interval between motion events (0.016 = 60 FPS motion capture)
46
+ FOUNDATION_EXTERN const NSTimeInterval RJDefaultMotionEventInterval;
47
+
48
+ /// Minimum velocity to record scroll motion (points/second)
49
+ FOUNDATION_EXTERN const CGFloat RJMotionVelocityThreshold;
50
+
51
+ /// Scroll distance threshold for motion events (points)
52
+ FOUNDATION_EXTERN const CGFloat RJDefaultScrollThreshold;
53
+
54
+ /// Time after last scroll event to consider scroll ended (seconds)
55
+ FOUNDATION_EXTERN const NSTimeInterval RJScrollEndDelay;
56
+
57
+ #pragma mark - Memory Thresholds
58
+
59
+ /// Memory warning threshold in bytes (100MB)
60
+ FOUNDATION_EXTERN const NSUInteger RJMemoryWarningThresholdBytes;
61
+
62
+ /// Maximum frame data bytes to keep in memory (2MB)
63
+ FOUNDATION_EXTERN const NSUInteger RJMaxFrameBytesInMemory;
64
+
65
+ /// Default maximum frames to keep in memory
66
+ FOUNDATION_EXTERN const NSInteger RJDefaultMaxFramesInMemory;
67
+
68
+ #pragma mark - Performance Thresholds
69
+
70
+ /// Battery level threshold for low-power mode (15%)
71
+ FOUNDATION_EXTERN const float RJLowBatteryThreshold;
72
+
73
+ /// Maximum consecutive captures before cooldown
74
+ FOUNDATION_EXTERN const NSInteger RJMaxConsecutiveCaptures;
75
+
76
+ /// Cooldown duration after max consecutive captures (seconds)
77
+ FOUNDATION_EXTERN const NSTimeInterval RJCaptureCooldownSeconds;
78
+
79
+ #pragma mark - Upload Configuration
80
+
81
+ /// Batch upload interval in seconds
82
+ FOUNDATION_EXTERN const NSTimeInterval RJBatchUploadInterval;
83
+
84
+ /// Initial upload delay for short sessions (seconds)
85
+ FOUNDATION_EXTERN const NSTimeInterval RJInitialUploadDelay;
86
+
87
+ /// Network request timeout (seconds)
88
+ FOUNDATION_EXTERN const NSTimeInterval RJNetworkRequestTimeout;
89
+
90
+ /// Network resource timeout (seconds)
91
+ FOUNDATION_EXTERN const NSTimeInterval RJNetworkResourceTimeout;
92
+
93
+ #pragma mark - Session Configuration
94
+
95
+ /// Background duration threshold for new session (seconds)
96
+ FOUNDATION_EXTERN const NSTimeInterval RJBackgroundSessionTimeout;
97
+
98
+ #pragma mark - Gesture Detection
99
+
100
+ /// Maximum time between taps for double-tap detection (milliseconds)
101
+ FOUNDATION_EXTERN const NSTimeInterval RJDoubleTapMaxInterval;
102
+
103
+ /// Maximum distance between taps for double-tap detection (points)
104
+ FOUNDATION_EXTERN const CGFloat RJDoubleTapMaxDistance;
105
+
106
+ /// Minimum duration for long press detection (milliseconds)
107
+ FOUNDATION_EXTERN const NSTimeInterval RJLongPressMinDuration;
108
+
109
+ /// Force touch threshold (normalized force value)
110
+ FOUNDATION_EXTERN const CGFloat RJForceTouchThreshold;
111
+
112
+ /// Minimum distance for swipe gesture detection (points)
113
+ FOUNDATION_EXTERN const CGFloat RJSwipeMinDistance;
114
+
115
+ /// Minimum distance for pinch gesture detection (points)
116
+ FOUNDATION_EXTERN const CGFloat RJPinchMinDistance;
117
+
118
+ /// Minimum distance change percentage for pinch detection
119
+ FOUNDATION_EXTERN const CGFloat RJPinchMinChangePercent;
120
+
121
+ /// Minimum rotation angle for rotation gesture detection (degrees)
122
+ FOUNDATION_EXTERN const CGFloat RJRotationMinAngle;
123
+
124
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,88 @@
1
+ //
2
+ // RJConstants.m
3
+ // Rejourney
4
+ //
5
+ // SDK-wide constants 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 "RJConstants.h"
23
+
24
+ #pragma mark - SDK Version
25
+
26
+ NSString *const RJSDKVersion = @"1.0.0";
27
+
28
+ #pragma mark - Capture Configuration Defaults (Video Model)
29
+
30
+ const CGFloat RJDefaultCaptureScale = 0.35;
31
+
32
+ #pragma mark - Motion Event Configuration
33
+
34
+ const NSTimeInterval RJDefaultMotionEventInterval = 0.016;
35
+
36
+ const CGFloat RJMotionVelocityThreshold = 10.0;
37
+
38
+ const CGFloat RJDefaultScrollThreshold = 5.0;
39
+
40
+ const NSTimeInterval RJScrollEndDelay = 0.15;
41
+
42
+ #pragma mark - Memory Thresholds
43
+
44
+ const NSUInteger RJMemoryWarningThresholdBytes = 100 * 1024 * 1024;
45
+
46
+ const NSUInteger RJMaxFrameBytesInMemory = 4 * 1024 * 1024;
47
+
48
+ const NSInteger RJDefaultMaxFramesInMemory = 20;
49
+
50
+ #pragma mark - Performance Thresholds
51
+
52
+ const float RJLowBatteryThreshold = 0.15f;
53
+
54
+ const NSInteger RJMaxConsecutiveCaptures = 5;
55
+
56
+ const NSTimeInterval RJCaptureCooldownSeconds = 1.0;
57
+
58
+ #pragma mark - Upload Configuration
59
+
60
+ const NSTimeInterval RJBatchUploadInterval = 5.0;
61
+
62
+ const NSTimeInterval RJInitialUploadDelay = 1.0;
63
+
64
+ const NSTimeInterval RJNetworkRequestTimeout = 60.0;
65
+
66
+ const NSTimeInterval RJNetworkResourceTimeout = 120.0;
67
+
68
+ #pragma mark - Session Configuration
69
+
70
+ const NSTimeInterval RJBackgroundSessionTimeout = 60.0;
71
+
72
+ #pragma mark - Gesture Detection
73
+
74
+ const NSTimeInterval RJDoubleTapMaxInterval = 300.0;
75
+
76
+ const CGFloat RJDoubleTapMaxDistance = 50.0;
77
+
78
+ const NSTimeInterval RJLongPressMinDuration = 500.0;
79
+
80
+ const CGFloat RJForceTouchThreshold = 2.0;
81
+
82
+ const CGFloat RJSwipeMinDistance = 50.0;
83
+
84
+ const CGFloat RJPinchMinDistance = 30.0;
85
+
86
+ const CGFloat RJPinchMinChangePercent = 0.20;
87
+
88
+ const CGFloat RJRotationMinAngle = 15.0;
@@ -0,0 +1,85 @@
1
+ //
2
+ // RJLifecycleManager.h
3
+ // Rejourney
4
+ //
5
+ // App lifecycle and keyboard event handling.
6
+ //
7
+ // Copyright (c) 2026 Rejourney
8
+ //
9
+
10
+ #import <Foundation/Foundation.h>
11
+ #import <UIKit/UIKit.h>
12
+
13
+ NS_ASSUME_NONNULL_BEGIN
14
+
15
+ /// Delegate for lifecycle events
16
+ @protocol RJLifecycleManagerDelegate <NSObject>
17
+ @optional
18
+ - (void)lifecycleManagerDidEnterBackground;
19
+ - (void)lifecycleManagerWillTerminate;
20
+ - (void)lifecycleManagerDidBecomeActive;
21
+ - (void)lifecycleManagerDidResignActive;
22
+ - (void)lifecycleManagerKeyboardDidShow:(CGRect)keyboardFrame;
23
+ - (void)lifecycleManagerKeyboardWillHide:(NSInteger)keyPressCount;
24
+ - (void)lifecycleManagerTextDidChange;
25
+ - (void)lifecycleManagerSessionDidTimeout:(NSTimeInterval)backgroundDuration;
26
+ @end
27
+
28
+ /**
29
+ * Manages app lifecycle events and keyboard notifications.
30
+ */
31
+ @interface RJLifecycleManager : NSObject
32
+
33
+ /// Delegate for lifecycle events
34
+ @property(nonatomic, weak, nullable) id<RJLifecycleManagerDelegate> delegate;
35
+
36
+ /// Whether the keyboard is currently visible
37
+ @property(nonatomic, readonly) BOOL isKeyboardVisible;
38
+
39
+ /// Current keyboard frame
40
+ @property(nonatomic, readonly) CGRect keyboardFrame;
41
+
42
+ /// Whether recording is currently active (set by owner)
43
+ @property(nonatomic, assign) BOOL isRecording;
44
+
45
+ /// Whether the app is currently in background
46
+ @property(nonatomic, readonly) BOOL isInBackground;
47
+
48
+ /// Time when app entered background (epoch seconds), 0 if not in background
49
+ @property(nonatomic, readonly) NSTimeInterval backgroundEntryTime;
50
+
51
+ /// Total background time in milliseconds for the current session
52
+ @property(nonatomic, readonly) NSTimeInterval totalBackgroundTimeMs;
53
+
54
+ /// Background session timeout threshold in seconds
55
+ @property(nonatomic, assign) NSTimeInterval backgroundTimeoutThreshold;
56
+
57
+ /**
58
+ * Start observing lifecycle and keyboard notifications.
59
+ */
60
+ - (void)startObserving;
61
+
62
+ /**
63
+ * Stop observing notifications.
64
+ */
65
+ - (void)stopObserving;
66
+
67
+ /**
68
+ * Reset background time tracking for a new session.
69
+ */
70
+ - (void)resetBackgroundTime;
71
+
72
+ /**
73
+ * Mark that an external URL was opened.
74
+ */
75
+ - (void)markExternalURLOpened:(NSString *)urlScheme;
76
+
77
+ /**
78
+ * Check and clear external URL opened flag.
79
+ */
80
+ - (BOOL)consumeExternalURLOpenedWithScheme:
81
+ (NSString *_Nullable *_Nullable)scheme;
82
+
83
+ @end
84
+
85
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,308 @@
1
+ //
2
+ // RJLifecycleManager.m
3
+ // Rejourney
4
+ //
5
+ // App lifecycle and keyboard event handling implementation.
6
+ //
7
+ // BACKGROUND TIMEOUT LOGIC:
8
+ // -------------------------
9
+ // When app returns from background:
10
+ // 1. Calculate how long we were in background
11
+ // 2. If < 60s: Add to accumulated background time, resume session normally
12
+ // 3. If >= 60s: Signal timeout to delegate with the background duration
13
+ // The delegate (Rejourney.mm) will:
14
+ // a) End the old session with total background time (including this period)
15
+ // b) Start a new session with fresh state
16
+ //
17
+ // Copyright (c) 2026 Rejourney
18
+ //
19
+
20
+ #import "RJLifecycleManager.h"
21
+ #import "RJConstants.h"
22
+ #import "RJLogger.h"
23
+
24
+ #pragma mark - Private Interface
25
+
26
+ @interface RJLifecycleManager ()
27
+
28
+ @property(nonatomic, assign) BOOL keyboardVisible;
29
+ @property(nonatomic, assign) CGRect currentKeyboardFrame;
30
+ @property(nonatomic, assign) NSInteger keyPressCount;
31
+
32
+ @property(nonatomic, assign) BOOL inBackground;
33
+ @property(nonatomic, assign) NSTimeInterval backgroundEntryTime;
34
+ @property(nonatomic, assign) NSTimeInterval accumulatedBackgroundTimeMs;
35
+
36
+ @property(nonatomic, assign) BOOL didOpenExternalURL;
37
+ @property(nonatomic, copy, nullable) NSString *lastOpenedURLScheme;
38
+
39
+ @end
40
+
41
+ #pragma mark - Implementation
42
+
43
+ @implementation RJLifecycleManager
44
+
45
+ #pragma mark - Initialization
46
+
47
+ - (instancetype)init {
48
+ self = [super init];
49
+ if (self) {
50
+ _keyboardVisible = NO;
51
+ _currentKeyboardFrame = CGRectZero;
52
+ _keyPressCount = 0;
53
+ _inBackground = NO;
54
+ _backgroundEntryTime = 0;
55
+ _accumulatedBackgroundTimeMs = 0;
56
+ _backgroundTimeoutThreshold = RJBackgroundSessionTimeout;
57
+ _isRecording = NO;
58
+ _didOpenExternalURL = NO;
59
+ }
60
+ return self;
61
+ }
62
+
63
+ - (void)dealloc {
64
+ [self stopObserving];
65
+ }
66
+
67
+ #pragma mark - Public Properties
68
+
69
+ - (BOOL)isKeyboardVisible {
70
+ return _keyboardVisible;
71
+ }
72
+
73
+ - (CGRect)keyboardFrame {
74
+ return _currentKeyboardFrame;
75
+ }
76
+
77
+ - (BOOL)isInBackground {
78
+ return _inBackground;
79
+ }
80
+
81
+ - (NSTimeInterval)backgroundEntryTime {
82
+ return _backgroundEntryTime;
83
+ }
84
+
85
+ - (NSTimeInterval)totalBackgroundTimeMs {
86
+ return _accumulatedBackgroundTimeMs;
87
+ }
88
+
89
+ #pragma mark - Observation Lifecycle
90
+
91
+ - (void)startObserving {
92
+ RJLogInfo(@"[RJ-LIFECYCLE] startObserving called (isRecording=%@)",
93
+ self.isRecording ? @"YES" : @"NO");
94
+ NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
95
+
96
+ // Keyboard notifications
97
+ [center addObserver:self
98
+ selector:@selector(keyboardWillShow:)
99
+ name:UIKeyboardWillShowNotification
100
+ object:nil];
101
+ [center addObserver:self
102
+ selector:@selector(keyboardWillHide:)
103
+ name:UIKeyboardWillHideNotification
104
+ object:nil];
105
+
106
+ // App lifecycle notifications
107
+ [center addObserver:self
108
+ selector:@selector(appDidEnterBackground:)
109
+ name:UIApplicationDidEnterBackgroundNotification
110
+ object:nil];
111
+ [center addObserver:self
112
+ selector:@selector(appWillTerminate:)
113
+ name:UIApplicationWillTerminateNotification
114
+ object:nil];
115
+ [center addObserver:self
116
+ selector:@selector(appWillResignActive:)
117
+ name:UIApplicationWillResignActiveNotification
118
+ object:nil];
119
+ [center addObserver:self
120
+ selector:@selector(appDidBecomeActive:)
121
+ name:UIApplicationDidBecomeActiveNotification
122
+ object:nil];
123
+
124
+ // Text change notifications
125
+ [center addObserver:self
126
+ selector:@selector(textDidChange:)
127
+ name:UITextFieldTextDidChangeNotification
128
+ object:nil];
129
+ [center addObserver:self
130
+ selector:@selector(textDidChange:)
131
+ name:UITextViewTextDidChangeNotification
132
+ object:nil];
133
+
134
+ RJLogInfo(@"[RJ-LIFECYCLE] Lifecycle manager started observing all notifications");
135
+ }
136
+
137
+ - (void)stopObserving {
138
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
139
+ RJLogDebug(@"Lifecycle manager stopped observing");
140
+ }
141
+
142
+ #pragma mark - State Management
143
+
144
+ - (void)resetBackgroundTime {
145
+ RJLogInfo(@"[RJ-LIFECYCLE] resetBackgroundTime called (was %.0fms)", self.accumulatedBackgroundTimeMs);
146
+ self.accumulatedBackgroundTimeMs = 0;
147
+ self.inBackground = NO;
148
+ self.backgroundEntryTime = 0;
149
+ }
150
+
151
+ - (void)markExternalURLOpened:(NSString *)urlScheme {
152
+ self.didOpenExternalURL = YES;
153
+ self.lastOpenedURLScheme = urlScheme;
154
+ }
155
+
156
+ - (BOOL)consumeExternalURLOpenedWithScheme:(NSString *_Nullable *_Nullable)scheme {
157
+ if (!self.didOpenExternalURL) {
158
+ return NO;
159
+ }
160
+
161
+ if (scheme) {
162
+ *scheme = self.lastOpenedURLScheme;
163
+ }
164
+
165
+ self.didOpenExternalURL = NO;
166
+ self.lastOpenedURLScheme = nil;
167
+ return YES;
168
+ }
169
+
170
+ #pragma mark - Keyboard Handling
171
+
172
+ - (void)keyboardWillShow:(NSNotification *)notification {
173
+ self.keyboardVisible = YES;
174
+ self.currentKeyboardFrame =
175
+ [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
176
+
177
+ if (self.isRecording) {
178
+ if ([self.delegate
179
+ respondsToSelector:@selector(lifecycleManagerKeyboardDidShow:)]) {
180
+ [self.delegate lifecycleManagerKeyboardDidShow:self.currentKeyboardFrame];
181
+ }
182
+ }
183
+ }
184
+
185
+ - (void)keyboardWillHide:(NSNotification *)notification {
186
+ if (self.isRecording && self.keyPressCount > 0) {
187
+ if ([self.delegate
188
+ respondsToSelector:@selector(lifecycleManagerKeyboardWillHide:)]) {
189
+ [self.delegate lifecycleManagerKeyboardWillHide:self.keyPressCount];
190
+ }
191
+ self.keyPressCount = 0;
192
+ }
193
+
194
+ self.keyboardVisible = NO;
195
+ self.currentKeyboardFrame = CGRectZero;
196
+ }
197
+
198
+ - (void)textDidChange:(NSNotification *)notification {
199
+ if (self.isRecording) {
200
+ self.keyPressCount++;
201
+ if ([self.delegate
202
+ respondsToSelector:@selector(lifecycleManagerTextDidChange)]) {
203
+ [self.delegate lifecycleManagerTextDidChange];
204
+ }
205
+ }
206
+ }
207
+
208
+ #pragma mark - App Lifecycle
209
+
210
+ - (void)appWillResignActive:(NSNotification *)notification {
211
+ if (self.isRecording) {
212
+ if ([self.delegate
213
+ respondsToSelector:@selector(lifecycleManagerDidResignActive)]) {
214
+ [self.delegate lifecycleManagerDidResignActive];
215
+ }
216
+ }
217
+ }
218
+
219
+ - (void)appDidEnterBackground:(NSNotification *)notification {
220
+ RJLogInfo(@"[RJ-LIFECYCLE] appDidEnterBackground (isRecording=%@)",
221
+ self.isRecording ? @"YES" : @"NO");
222
+
223
+ // Always track background entry time, even if not recording
224
+ // This allows us to detect timeout when session was ended while in background
225
+ self.inBackground = YES;
226
+ self.backgroundEntryTime = [[NSDate date] timeIntervalSince1970];
227
+
228
+ if (!self.isRecording) {
229
+ RJLogInfo(@"[RJ-LIFECYCLE] Not recording - just tracking background time");
230
+ return;
231
+ }
232
+
233
+ RJLogInfo(@"[RJ-LIFECYCLE] Calling lifecycleManagerDidEnterBackground delegate");
234
+ if ([self.delegate
235
+ respondsToSelector:@selector(lifecycleManagerDidEnterBackground)]) {
236
+ [self.delegate lifecycleManagerDidEnterBackground];
237
+ }
238
+ }
239
+
240
+ - (void)appWillTerminate:(NSNotification *)notification {
241
+ if ([self.delegate
242
+ respondsToSelector:@selector(lifecycleManagerWillTerminate)]) {
243
+ [self.delegate lifecycleManagerWillTerminate];
244
+ }
245
+ }
246
+
247
+ - (void)appDidBecomeActive:(NSNotification *)notification {
248
+ NSTimeInterval currentTime = [[NSDate date] timeIntervalSince1970];
249
+ BOOL wasInBackground = self.inBackground && self.backgroundEntryTime > 0;
250
+ NSTimeInterval backgroundDurationSec = 0;
251
+
252
+ if (wasInBackground) {
253
+ backgroundDurationSec = currentTime - self.backgroundEntryTime;
254
+ RJLogInfo(@"[RJ-LIFECYCLE] Returned from background after %.1fs (isRecording=%@)",
255
+ backgroundDurationSec, self.isRecording ? @"YES" : @"NO");
256
+ } else {
257
+ RJLogInfo(@"[RJ-LIFECYCLE] appDidBecomeActive - was NOT in background");
258
+ }
259
+
260
+ // Reset background tracking state
261
+ self.inBackground = NO;
262
+ self.backgroundEntryTime = 0;
263
+
264
+ // Handle the case where we weren't recording (session already ended)
265
+ if (!self.isRecording) {
266
+ // If we were in background long enough, signal that a new session should start
267
+ if (wasInBackground && backgroundDurationSec >= self.backgroundTimeoutThreshold) {
268
+ RJLogInfo(@"[RJ-LIFECYCLE] Was not recording, background >= %.0fs - signaling for new session start",
269
+ self.backgroundTimeoutThreshold);
270
+ if ([self.delegate respondsToSelector:@selector(lifecycleManagerSessionDidTimeout:)]) {
271
+ [self.delegate lifecycleManagerSessionDidTimeout:backgroundDurationSec];
272
+ }
273
+ }
274
+ return;
275
+ }
276
+
277
+ // We ARE recording - handle background return
278
+ if (wasInBackground) {
279
+ NSTimeInterval bgDurationMs = backgroundDurationSec * 1000;
280
+
281
+ if (backgroundDurationSec >= self.backgroundTimeoutThreshold) {
282
+ // TIMEOUT CASE: End old session, start new one
283
+ // Add this background duration to accumulated time BEFORE signaling timeout
284
+ // so the old session gets the correct total background time
285
+ self.accumulatedBackgroundTimeMs += bgDurationMs;
286
+ RJLogInfo(@"[RJ-LIFECYCLE] TIMEOUT: Added %.0fms, total background=%.0fms - signaling session restart",
287
+ bgDurationMs, self.accumulatedBackgroundTimeMs);
288
+
289
+ if ([self.delegate respondsToSelector:@selector(lifecycleManagerSessionDidTimeout:)]) {
290
+ [self.delegate lifecycleManagerSessionDidTimeout:backgroundDurationSec];
291
+ }
292
+ // Note: The delegate's handleSessionTimeout will read totalBackgroundTimeMs
293
+ // and then call resetBackgroundTime for the new session
294
+ } else {
295
+ // SHORT BACKGROUND: Just accumulate and resume
296
+ self.accumulatedBackgroundTimeMs += bgDurationMs;
297
+ RJLogInfo(@"[RJ-LIFECYCLE] Short background: Added %.0fms, total=%.0fms - resuming session",
298
+ bgDurationMs, self.accumulatedBackgroundTimeMs);
299
+ }
300
+ }
301
+
302
+ // Call didBecomeActive for normal resume handling (video capture, etc.)
303
+ if ([self.delegate respondsToSelector:@selector(lifecycleManagerDidBecomeActive)]) {
304
+ [self.delegate lifecycleManagerDidBecomeActive];
305
+ }
306
+ }
307
+
308
+ @end
@@ -0,0 +1,61 @@
1
+ //
2
+ // RJLogger.h
3
+ // Rejourney
4
+ //
5
+ // Centralized logging utility for the Rejourney SDK.
6
+ // Provides consistent log formatting and level-based filtering.
7
+ //
8
+ // Licensed under the Apache License, Version 2.0 (the "License");
9
+ // you may not use this file except in compliance with the License.
10
+ // You may obtain a copy of the License at
11
+ //
12
+ // http://www.apache.org/licenses/LICENSE-2.0
13
+ //
14
+ // Unless required by applicable law or agreed to in writing, software
15
+ // distributed under the License is distributed on an "AS IS" BASIS,
16
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ // See the License for the specific language governing permissions and
18
+ // limitations under the License.
19
+ //
20
+ // Copyright (c) 2026 Rejourney
21
+ //
22
+
23
+ #import <Foundation/Foundation.h>
24
+
25
+ NS_ASSUME_NONNULL_BEGIN
26
+
27
+ typedef NS_ENUM(NSInteger, RJLogLevel) {
28
+ RJLogLevelDebug = 0,
29
+ RJLogLevelInfo = 1,
30
+ RJLogLevelWarning = 2,
31
+ RJLogLevelError = 3,
32
+ RJLogLevelSilent = 4
33
+ };
34
+
35
+ @interface RJLogger : NSObject
36
+
37
+ @property(class, nonatomic, assign) RJLogLevel minimumLogLevel;
38
+ @property(class, nonatomic, assign) BOOL includeTimestamps;
39
+ @property(class, nonatomic, assign) BOOL debugMode;
40
+
41
+ + (void)debug:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2);
42
+ + (void)info:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2);
43
+ + (void)warning:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2);
44
+ + (void)error:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2);
45
+ + (void)logWithLevel:(RJLogLevel)level
46
+ format:(NSString *)format, ... NS_FORMAT_FUNCTION(2, 3);
47
+ + (void)logInitSuccess:(NSString *)version;
48
+ + (void)logInitFailure:(NSString *)reason;
49
+ + (void)logSessionStart:(NSString *)sessionId;
50
+ + (void)logSessionEnd:(NSString *)sessionId;
51
+ + (void)setDebugMode:(BOOL)enabled;
52
+
53
+ @end
54
+
55
+ #define RJLogDebug(fmt, ...) [RJLogger debug:fmt, ##__VA_ARGS__]
56
+ #define RJLogInfo(fmt, ...) [RJLogger info:fmt, ##__VA_ARGS__]
57
+ #define RJLogWarning(fmt, ...) [RJLogger warning:fmt, ##__VA_ARGS__]
58
+ #define RJLogError(fmt, ...) [RJLogger error:fmt, ##__VA_ARGS__]
59
+ #define RJLogPerf(fmt, ...) [RJLogger info:fmt, ##__VA_ARGS__]
60
+
61
+ NS_ASSUME_NONNULL_END