@convep_mobilogy/react-native-qms-plugin 0.10.4 → 0.10.6
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.
- package/android/build.gradle +1 -1
- package/ios/QmsPlugin/QmsDashboardViewManager.mm +408 -2
- package/ios/QmsPlugin/QmsModule.mm +221 -0
- package/ios/QmsPlugin/UIViewController+QmsAnalytics.m +292 -19
- package/ios/QmsPluginFramework.xcframework/Info.plist +5 -5
- package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Assets.car +0 -0
- package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Info.plist +0 -0
- package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Montserrat-Bold.ttf +0 -0
- package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Montserrat-Medium.ttf +0 -0
- package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Montserrat-Regular.ttf +0 -0
- package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/QmsPluginFramework +0 -0
- package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/_CodeSignature/CodeResources +36 -3
- package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Assets.car +0 -0
- package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Info.plist +0 -0
- package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Montserrat-Bold.ttf +0 -0
- package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Montserrat-Medium.ttf +0 -0
- package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Montserrat-Regular.ttf +0 -0
- package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/QmsPluginFramework +0 -0
- package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/_CodeSignature/CodeResources +36 -3
- package/lib/module/QmsDashboardView.android.js +16 -16
- package/lib/module/QmsDashboardView.android.js.map +1 -1
- package/lib/module/QmsDashboardView.ios.js +38 -12
- package/lib/module/QmsDashboardView.ios.js.map +1 -1
- package/lib/typescript/src/QmsDashboardView.ios.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/QmsDashboardView.android.tsx +16 -16
- package/src/QmsDashboardView.ios.tsx +64 -25
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
#import <Foundation/Foundation.h>
|
|
2
|
+
#import <React/RCTBridgeModule.h>
|
|
3
|
+
#import <UIKit/UIKit.h>
|
|
4
|
+
#import <objc/runtime.h>
|
|
5
|
+
#import <QmsPluginFramework/QmsPlugin.h>
|
|
6
|
+
|
|
7
|
+
static NSString * const kQmsThemePrimaryColorKey = @"QmsThemePrimaryColor";
|
|
8
|
+
static NSString * const kQmsThemeHeaderTextColorKey = @"QmsThemeHeaderTextColor";
|
|
9
|
+
static NSString * const kQmsThemeHeaderIconColorKey = @"QmsThemeHeaderIconColor";
|
|
10
|
+
static NSString * const kQmsThemeAccentColorKey = @"QmsThemeAccentColor";
|
|
11
|
+
static NSString * const kQmsThemeFontFamilyKey = @"QmsThemeFontFamily";
|
|
12
|
+
static NSString * const kQmsRoutePayloadNotification = @"QmsRoutePayloadNotification";
|
|
13
|
+
|
|
14
|
+
@interface QmsModule : NSObject <RCTBridgeModule>
|
|
15
|
+
@property (nonatomic, strong) UIView *bannerView;
|
|
16
|
+
@end
|
|
17
|
+
|
|
18
|
+
@implementation QmsModule
|
|
19
|
+
|
|
20
|
+
RCT_EXPORT_MODULE(QmsModule)
|
|
21
|
+
|
|
22
|
+
- (dispatch_queue_t)methodQueue {
|
|
23
|
+
return dispatch_get_main_queue();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
- (UIViewController *)topViewController {
|
|
27
|
+
UIWindow *keyWindow = UIApplication.sharedApplication.keyWindow;
|
|
28
|
+
if (!keyWindow) {
|
|
29
|
+
for (UIScene *scene in UIApplication.sharedApplication.connectedScenes) {
|
|
30
|
+
if (![scene isKindOfClass:[UIWindowScene class]]) continue;
|
|
31
|
+
UIWindowScene *windowScene = (UIWindowScene *)scene;
|
|
32
|
+
if (windowScene.activationState != UISceneActivationStateForegroundActive) continue;
|
|
33
|
+
for (UIWindow *window in windowScene.windows) {
|
|
34
|
+
if (window.isKeyWindow) {
|
|
35
|
+
keyWindow = window;
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (keyWindow) break;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
UIViewController *vc = keyWindow.rootViewController;
|
|
43
|
+
while (vc.presentedViewController) vc = vc.presentedViewController;
|
|
44
|
+
return vc;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
- (void)applyDashboardPropsToViewController:(UIViewController *)vc
|
|
48
|
+
userToken:(NSString *)userToken
|
|
49
|
+
clientID:(NSString *)clientID
|
|
50
|
+
clientCode:(NSString *)clientCode
|
|
51
|
+
isOrigin:(BOOL)isOrigin {
|
|
52
|
+
if (!vc) return;
|
|
53
|
+
@try {
|
|
54
|
+
[vc setValue:(clientID ?: @"") forKey:@"ClientID"];
|
|
55
|
+
[vc setValue:(clientCode ?: @"") forKey:@"ClientCode"];
|
|
56
|
+
[vc setValue:(userToken ?: @"") forKey:@"user_token"];
|
|
57
|
+
[vc setValue:@(isOrigin) forKey:@"isOrigin"];
|
|
58
|
+
} @catch (__unused NSException *exception) {}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
- (void)routePayload:(NSDictionary *)payload {
|
|
62
|
+
if (![payload isKindOfClass:[NSDictionary class]] || payload.count == 0) return;
|
|
63
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:kQmsRoutePayloadNotification
|
|
64
|
+
object:nil
|
|
65
|
+
userInfo:payload];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
RCT_EXPORT_METHOD(showQms:(NSString *)userToken
|
|
69
|
+
ClientID:(NSString *)clientID
|
|
70
|
+
ClientCode:(NSString *)clientCode
|
|
71
|
+
isOrigin:(BOOL)isOrigin) {
|
|
72
|
+
UIViewController *presenter = [self topViewController];
|
|
73
|
+
if (!presenter) return;
|
|
74
|
+
|
|
75
|
+
UIViewController *vc = [QmsPluginUI makeViewController];
|
|
76
|
+
[self applyDashboardPropsToViewController:vc
|
|
77
|
+
userToken:userToken
|
|
78
|
+
clientID:clientID
|
|
79
|
+
clientCode:clientCode
|
|
80
|
+
isOrigin:isOrigin];
|
|
81
|
+
|
|
82
|
+
[presenter presentViewController:vc animated:YES completion:nil];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
RCT_EXPORT_METHOD(showQmsWithPayload:(NSString *)userToken
|
|
86
|
+
ClientID:(NSString *)clientID
|
|
87
|
+
ClientCode:(NSString *)clientCode
|
|
88
|
+
isOrigin:(BOOL)isOrigin
|
|
89
|
+
payload:(NSDictionary *)payload) {
|
|
90
|
+
UIViewController *presenter = [self topViewController];
|
|
91
|
+
if (!presenter) return;
|
|
92
|
+
|
|
93
|
+
UIViewController *vc = [QmsPluginUI makeViewController];
|
|
94
|
+
[self applyDashboardPropsToViewController:vc
|
|
95
|
+
userToken:userToken
|
|
96
|
+
clientID:clientID
|
|
97
|
+
clientCode:clientCode
|
|
98
|
+
isOrigin:isOrigin];
|
|
99
|
+
|
|
100
|
+
[presenter presentViewController:vc animated:YES completion:^{
|
|
101
|
+
[self routePayload:payload];
|
|
102
|
+
}];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
RCT_EXPORT_METHOD(setPrimaryColor:(NSString *)hexColor) {
|
|
106
|
+
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
107
|
+
if (hexColor.length > 0) [defaults setObject:hexColor forKey:kQmsThemePrimaryColorKey];
|
|
108
|
+
else [defaults removeObjectForKey:kQmsThemePrimaryColorKey];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
RCT_EXPORT_METHOD(setHeaderTextColor:(NSString *)hexColor) {
|
|
112
|
+
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
113
|
+
if (hexColor.length > 0) [defaults setObject:hexColor forKey:kQmsThemeHeaderTextColorKey];
|
|
114
|
+
else [defaults removeObjectForKey:kQmsThemeHeaderTextColorKey];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
RCT_EXPORT_METHOD(setHeaderIconColor:(NSString *)hexColor) {
|
|
118
|
+
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
119
|
+
if (hexColor.length > 0) [defaults setObject:hexColor forKey:kQmsThemeHeaderIconColorKey];
|
|
120
|
+
else [defaults removeObjectForKey:kQmsThemeHeaderIconColorKey];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
RCT_EXPORT_METHOD(setAccentColor:(NSString *)hexColor) {
|
|
124
|
+
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
125
|
+
if (hexColor.length > 0) [defaults setObject:hexColor forKey:kQmsThemeAccentColorKey];
|
|
126
|
+
else [defaults removeObjectForKey:kQmsThemeAccentColorKey];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
RCT_EXPORT_METHOD(setFontFamily:(NSString *)fontFamily) {
|
|
130
|
+
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
131
|
+
if (fontFamily.length > 0) [defaults setObject:fontFamily forKey:kQmsThemeFontFamilyKey];
|
|
132
|
+
else [defaults removeObjectForKey:kQmsThemeFontFamilyKey];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
RCT_EXPORT_METHOD(emitInAppNotification:(NSString *)title body:(NSString *)body) {
|
|
136
|
+
[self emitInAppNotificationWithPayload:title body:body data:nil];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
RCT_EXPORT_METHOD(emitInAppNotificationWithPayload:(NSString *)title
|
|
140
|
+
body:(NSString *)body
|
|
141
|
+
data:(NSDictionary *)data) {
|
|
142
|
+
UIViewController *vc = [self topViewController];
|
|
143
|
+
if (!vc || !vc.view) return;
|
|
144
|
+
|
|
145
|
+
if (self.bannerView) {
|
|
146
|
+
[self.bannerView removeFromSuperview];
|
|
147
|
+
self.bannerView = nil;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
UIView *banner = [[UIView alloc] init];
|
|
151
|
+
banner.translatesAutoresizingMaskIntoConstraints = NO;
|
|
152
|
+
banner.backgroundColor = [UIColor colorWithWhite:0.07 alpha:1.0];
|
|
153
|
+
banner.layer.cornerRadius = 12.0;
|
|
154
|
+
banner.clipsToBounds = YES;
|
|
155
|
+
|
|
156
|
+
UILabel *titleLabel = [[UILabel alloc] init];
|
|
157
|
+
titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
|
158
|
+
titleLabel.textColor = UIColor.whiteColor;
|
|
159
|
+
titleLabel.font = [UIFont boldSystemFontOfSize:16];
|
|
160
|
+
titleLabel.text = (title.length > 0 ? title : @"Notification");
|
|
161
|
+
|
|
162
|
+
UILabel *bodyLabel = [[UILabel alloc] init];
|
|
163
|
+
bodyLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
|
164
|
+
bodyLabel.textColor = [UIColor colorWithWhite:0.90 alpha:1.0];
|
|
165
|
+
bodyLabel.font = [UIFont systemFontOfSize:14];
|
|
166
|
+
bodyLabel.numberOfLines = 2;
|
|
167
|
+
bodyLabel.text = (body ?: @"");
|
|
168
|
+
|
|
169
|
+
[banner addSubview:titleLabel];
|
|
170
|
+
[banner addSubview:bodyLabel];
|
|
171
|
+
[vc.view addSubview:banner];
|
|
172
|
+
self.bannerView = banner;
|
|
173
|
+
|
|
174
|
+
[NSLayoutConstraint activateConstraints:@[
|
|
175
|
+
[banner.topAnchor constraintEqualToAnchor:vc.view.safeAreaLayoutGuide.topAnchor constant:8],
|
|
176
|
+
[banner.leadingAnchor constraintEqualToAnchor:vc.view.leadingAnchor constant:16],
|
|
177
|
+
[banner.trailingAnchor constraintEqualToAnchor:vc.view.trailingAnchor constant:-16],
|
|
178
|
+
[titleLabel.topAnchor constraintEqualToAnchor:banner.topAnchor constant:12],
|
|
179
|
+
[titleLabel.leadingAnchor constraintEqualToAnchor:banner.leadingAnchor constant:12],
|
|
180
|
+
[titleLabel.trailingAnchor constraintEqualToAnchor:banner.trailingAnchor constant:-12],
|
|
181
|
+
[bodyLabel.topAnchor constraintEqualToAnchor:titleLabel.bottomAnchor constant:6],
|
|
182
|
+
[bodyLabel.leadingAnchor constraintEqualToAnchor:banner.leadingAnchor constant:12],
|
|
183
|
+
[bodyLabel.trailingAnchor constraintEqualToAnchor:banner.trailingAnchor constant:-12],
|
|
184
|
+
[bodyLabel.bottomAnchor constraintEqualToAnchor:banner.bottomAnchor constant:-12],
|
|
185
|
+
]];
|
|
186
|
+
|
|
187
|
+
banner.alpha = 0.0;
|
|
188
|
+
banner.transform = CGAffineTransformMakeTranslation(0, -40);
|
|
189
|
+
[UIView animateWithDuration:0.22 animations:^{
|
|
190
|
+
banner.alpha = 1.0;
|
|
191
|
+
banner.transform = CGAffineTransformIdentity;
|
|
192
|
+
}];
|
|
193
|
+
|
|
194
|
+
if ([data isKindOfClass:[NSDictionary class]] && data.count > 0) {
|
|
195
|
+
banner.userInteractionEnabled = YES;
|
|
196
|
+
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleBannerTap:)];
|
|
197
|
+
objc_setAssociatedObject(tap, "qms_payload", data, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
|
198
|
+
[banner addGestureRecognizer:tap];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.5 * NSEC_PER_SEC)),
|
|
202
|
+
dispatch_get_main_queue(), ^{
|
|
203
|
+
if (self.bannerView != banner) return;
|
|
204
|
+
[UIView animateWithDuration:0.2 animations:^{
|
|
205
|
+
banner.alpha = 0.0;
|
|
206
|
+
banner.transform = CGAffineTransformMakeTranslation(0, -40);
|
|
207
|
+
} completion:^(__unused BOOL finished) {
|
|
208
|
+
[banner removeFromSuperview];
|
|
209
|
+
if (self.bannerView == banner) self.bannerView = nil;
|
|
210
|
+
}];
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
- (void)handleBannerTap:(UITapGestureRecognizer *)gesture {
|
|
215
|
+
NSDictionary *payload = objc_getAssociatedObject(gesture, "qms_payload");
|
|
216
|
+
[self routePayload:payload];
|
|
217
|
+
[gesture.view removeFromSuperview];
|
|
218
|
+
if (self.bannerView == gesture.view) self.bannerView = nil;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
@end
|
|
@@ -4,6 +4,28 @@
|
|
|
4
4
|
|
|
5
5
|
static NSString * const QmsAnalyticsScreenNotification = @"QmsAnalyticsScreenNotification";
|
|
6
6
|
static NSString * const QmsAnalyticsEventNotification = @"QmsAnalyticsEventNotification";
|
|
7
|
+
static NSString * const QmsThemePrimaryColorKey = @"QmsThemePrimaryColor";
|
|
8
|
+
static NSString * const QmsThemeHeaderTextColorKey = @"QmsThemeHeaderTextColor";
|
|
9
|
+
static NSString * const QmsThemeAccentColorKey = @"QmsThemeAccentColor";
|
|
10
|
+
static NSString * const QmsThemeFontFamilyKey = @"QmsThemeFontFamily";
|
|
11
|
+
static char QmsStatusBarFillViewKey;
|
|
12
|
+
|
|
13
|
+
static void qms_swizzleInstanceMethod(Class cls, SEL originalSelector, SEL swizzledSelector) {
|
|
14
|
+
Method originalMethod = class_getInstanceMethod(cls, originalSelector);
|
|
15
|
+
Method swizzledMethod = class_getInstanceMethod(cls, swizzledSelector);
|
|
16
|
+
BOOL didAddMethod = class_addMethod(cls,
|
|
17
|
+
originalSelector,
|
|
18
|
+
method_getImplementation(swizzledMethod),
|
|
19
|
+
method_getTypeEncoding(swizzledMethod));
|
|
20
|
+
if (didAddMethod) {
|
|
21
|
+
class_replaceMethod(cls,
|
|
22
|
+
swizzledSelector,
|
|
23
|
+
method_getImplementation(originalMethod),
|
|
24
|
+
method_getTypeEncoding(originalMethod));
|
|
25
|
+
} else {
|
|
26
|
+
method_exchangeImplementations(originalMethod, swizzledMethod);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
7
29
|
|
|
8
30
|
@interface UIViewController (QmsAnalytics)
|
|
9
31
|
@end
|
|
@@ -14,28 +36,19 @@ static NSString * const QmsAnalyticsEventNotification = @"QmsAnalyticsEventNotif
|
|
|
14
36
|
static dispatch_once_t onceToken;
|
|
15
37
|
dispatch_once(&onceToken, ^{
|
|
16
38
|
Class class = [self class];
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
Method originalMethod = class_getInstanceMethod(class, originalSelector);
|
|
21
|
-
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
|
|
22
|
-
|
|
23
|
-
BOOL didAddMethod =
|
|
24
|
-
class_addMethod(class,
|
|
25
|
-
originalSelector,
|
|
26
|
-
method_getImplementation(swizzledMethod),
|
|
27
|
-
method_getTypeEncoding(swizzledMethod));
|
|
28
|
-
if (didAddMethod) {
|
|
29
|
-
class_replaceMethod(class,
|
|
30
|
-
swizzledSelector,
|
|
31
|
-
method_getImplementation(originalMethod),
|
|
32
|
-
method_getTypeEncoding(originalMethod));
|
|
33
|
-
} else {
|
|
34
|
-
method_exchangeImplementations(originalMethod, swizzledMethod);
|
|
35
|
-
}
|
|
39
|
+
qms_swizzleInstanceMethod(class, @selector(viewWillAppear:), @selector(qms_viewWillAppear:));
|
|
40
|
+
qms_swizzleInstanceMethod(class, @selector(viewDidAppear:), @selector(qms_viewDidAppear:));
|
|
41
|
+
qms_swizzleInstanceMethod(class, @selector(viewDidLayoutSubviews), @selector(qms_viewDidLayoutSubviews));
|
|
36
42
|
});
|
|
37
43
|
}
|
|
38
44
|
|
|
45
|
+
- (void)qms_viewWillAppear:(BOOL)animated {
|
|
46
|
+
[self qms_viewWillAppear:animated];
|
|
47
|
+
if (![self qms_shouldTrackAnalytics]) return;
|
|
48
|
+
[self qms_applyThemeIfNeeded];
|
|
49
|
+
[self qms_scheduleThemeReapplyPasses];
|
|
50
|
+
}
|
|
51
|
+
|
|
39
52
|
- (void)qms_viewDidAppear:(BOOL)animated {
|
|
40
53
|
[self qms_viewDidAppear:animated];
|
|
41
54
|
|
|
@@ -43,6 +56,9 @@ static NSString * const QmsAnalyticsEventNotification = @"QmsAnalyticsEventNotif
|
|
|
43
56
|
return;
|
|
44
57
|
}
|
|
45
58
|
|
|
59
|
+
[self qms_applyThemeIfNeeded];
|
|
60
|
+
[self qms_scheduleThemeReapplyPasses];
|
|
61
|
+
|
|
46
62
|
NSString *screenName = NSStringFromClass([self class]);
|
|
47
63
|
NSString *screenClass = screenName;
|
|
48
64
|
|
|
@@ -67,6 +83,263 @@ static NSString * const QmsAnalyticsEventNotification = @"QmsAnalyticsEventNotif
|
|
|
67
83
|
userInfo:eventInfo];
|
|
68
84
|
}
|
|
69
85
|
|
|
86
|
+
- (void)qms_scheduleThemeReapplyPasses {
|
|
87
|
+
__weak typeof(self) weakSelf = self;
|
|
88
|
+
NSArray<NSNumber *> *delays = @[@0.02, @0.12, @0.30, @0.55];
|
|
89
|
+
for (NSNumber *delay in delays) {
|
|
90
|
+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay.doubleValue * NSEC_PER_SEC)),
|
|
91
|
+
dispatch_get_main_queue(), ^{
|
|
92
|
+
__strong typeof(weakSelf) strongSelf = weakSelf;
|
|
93
|
+
if (!strongSelf || !strongSelf.view.window) return;
|
|
94
|
+
if (![strongSelf qms_shouldTrackAnalytics]) return;
|
|
95
|
+
[strongSelf qms_applyThemeIfNeeded];
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
- (void)qms_viewDidLayoutSubviews {
|
|
101
|
+
[self qms_viewDidLayoutSubviews];
|
|
102
|
+
if (![self qms_shouldTrackAnalytics]) return;
|
|
103
|
+
[self qms_applyThemeIfNeeded];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
- (UIColor *)qms_colorFromHexString:(NSString *)hexString {
|
|
107
|
+
if (hexString.length == 0) return nil;
|
|
108
|
+
NSString *value = [[hexString stringByTrimmingCharactersInSet:
|
|
109
|
+
[NSCharacterSet whitespaceAndNewlineCharacterSet]]
|
|
110
|
+
uppercaseString];
|
|
111
|
+
if ([value hasPrefix:@"#"]) value = [value substringFromIndex:1];
|
|
112
|
+
if (value.length == 3) {
|
|
113
|
+
unichar c0 = [value characterAtIndex:0];
|
|
114
|
+
unichar c1 = [value characterAtIndex:1];
|
|
115
|
+
unichar c2 = [value characterAtIndex:2];
|
|
116
|
+
value = [NSString stringWithFormat:@"%C%C%C%C%C%C", c0, c0, c1, c1, c2, c2];
|
|
117
|
+
}
|
|
118
|
+
if (value.length != 6 && value.length != 8) return nil;
|
|
119
|
+
|
|
120
|
+
unsigned int hex = 0;
|
|
121
|
+
[[NSScanner scannerWithString:value] scanHexInt:&hex];
|
|
122
|
+
CGFloat a = 1.0, r = 0, g = 0, b = 0;
|
|
123
|
+
if (value.length == 8) {
|
|
124
|
+
a = ((hex >> 24) & 0xFF) / 255.0;
|
|
125
|
+
r = ((hex >> 16) & 0xFF) / 255.0;
|
|
126
|
+
g = ((hex >> 8) & 0xFF) / 255.0;
|
|
127
|
+
b = (hex & 0xFF) / 255.0;
|
|
128
|
+
} else {
|
|
129
|
+
r = ((hex >> 16) & 0xFF) / 255.0;
|
|
130
|
+
g = ((hex >> 8) & 0xFF) / 255.0;
|
|
131
|
+
b = (hex & 0xFF) / 255.0;
|
|
132
|
+
}
|
|
133
|
+
return [UIColor colorWithRed:r green:g blue:b alpha:a];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
- (BOOL)qms_isColor:(UIColor *)color near:(UIColor *)target tolerance:(CGFloat)tolerance {
|
|
137
|
+
if (!color || !target) return NO;
|
|
138
|
+
CGFloat r1 = 0, g1 = 0, b1 = 0, a1 = 0;
|
|
139
|
+
CGFloat r2 = 0, g2 = 0, b2 = 0, a2 = 0;
|
|
140
|
+
if (![color getRed:&r1 green:&g1 blue:&b1 alpha:&a1]) return NO;
|
|
141
|
+
if (![target getRed:&r2 green:&g2 blue:&b2 alpha:&a2]) return NO;
|
|
142
|
+
return (fabs(r1 - r2) <= tolerance &&
|
|
143
|
+
fabs(g1 - g2) <= tolerance &&
|
|
144
|
+
fabs(b1 - b2) <= tolerance &&
|
|
145
|
+
fabs(a1 - a2) <= tolerance);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
- (BOOL)qms_isDefaultPrimaryColor:(UIColor *)color {
|
|
149
|
+
UIColor *primaryA = [UIColor colorWithRed:0.0 green:0.50 blue:0.60 alpha:1.0];
|
|
150
|
+
UIColor *primaryB = [UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0];
|
|
151
|
+
return [self qms_isColor:color near:primaryA tolerance:0.18] ||
|
|
152
|
+
[self qms_isColor:color near:primaryB tolerance:0.18];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
- (void)qms_applyPrimaryRecursively:(UIView *)view themeColor:(UIColor *)themeColor {
|
|
156
|
+
if (!view || !themeColor) return;
|
|
157
|
+
if ([self qms_isDefaultPrimaryColor:view.backgroundColor]) {
|
|
158
|
+
view.backgroundColor = themeColor;
|
|
159
|
+
}
|
|
160
|
+
for (UIView *subview in view.subviews) {
|
|
161
|
+
[self qms_applyPrimaryRecursively:subview themeColor:themeColor];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
- (BOOL)qms_hasPrimaryAncestor:(UIView *)view primaryColor:(UIColor *)primaryColor {
|
|
166
|
+
UIView *current = view;
|
|
167
|
+
while (current) {
|
|
168
|
+
if ([self qms_isColor:current.backgroundColor near:primaryColor tolerance:0.08]) {
|
|
169
|
+
return YES;
|
|
170
|
+
}
|
|
171
|
+
current = current.superview;
|
|
172
|
+
}
|
|
173
|
+
return NO;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
- (void)qms_applyForegroundRecursively:(UIView *)view
|
|
177
|
+
primaryColor:(UIColor *)primaryColor
|
|
178
|
+
headerTextColor:(UIColor *)headerTextColor
|
|
179
|
+
accentColor:(UIColor *)accentColor {
|
|
180
|
+
if (!view) return;
|
|
181
|
+
|
|
182
|
+
BOOL onPrimary = [self qms_hasPrimaryAncestor:view primaryColor:primaryColor];
|
|
183
|
+
CGPoint origin = CGPointZero;
|
|
184
|
+
if (view.superview) {
|
|
185
|
+
origin = [view.superview convertPoint:view.frame.origin toView:self.view];
|
|
186
|
+
}
|
|
187
|
+
BOOL nearHeader = origin.y <= 170.0;
|
|
188
|
+
UIColor *resolved = nil;
|
|
189
|
+
if (onPrimary) {
|
|
190
|
+
if (nearHeader && headerTextColor) {
|
|
191
|
+
resolved = headerTextColor;
|
|
192
|
+
} else {
|
|
193
|
+
resolved = accentColor ?: headerTextColor;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (resolved) {
|
|
198
|
+
if ([view isKindOfClass:[UILabel class]]) {
|
|
199
|
+
((UILabel *)view).textColor = resolved;
|
|
200
|
+
} else if ([view isKindOfClass:[UIButton class]]) {
|
|
201
|
+
UIButton *button = (UIButton *)view;
|
|
202
|
+
button.tintColor = resolved;
|
|
203
|
+
[button setTitleColor:resolved forState:UIControlStateNormal];
|
|
204
|
+
} else if ([view isKindOfClass:[UIImageView class]]) {
|
|
205
|
+
((UIImageView *)view).tintColor = resolved;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
for (UIView *subview in view.subviews) {
|
|
210
|
+
[self qms_applyForegroundRecursively:subview
|
|
211
|
+
primaryColor:primaryColor
|
|
212
|
+
headerTextColor:headerTextColor
|
|
213
|
+
accentColor:accentColor];
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
- (void)qms_applyThemeIfNeeded {
|
|
218
|
+
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
219
|
+
UIColor *themeColor = [self qms_colorFromHexString:[defaults stringForKey:QmsThemePrimaryColorKey]];
|
|
220
|
+
UIColor *headerTextColor = [self qms_colorFromHexString:[defaults stringForKey:QmsThemeHeaderTextColorKey]];
|
|
221
|
+
UIColor *accentColor = [self qms_colorFromHexString:[defaults stringForKey:QmsThemeAccentColorKey]];
|
|
222
|
+
NSString *fontFamily = [defaults stringForKey:QmsThemeFontFamilyKey];
|
|
223
|
+
if (themeColor) {
|
|
224
|
+
[self qms_applyPrimaryRecursively:self.view themeColor:themeColor];
|
|
225
|
+
[self qms_applyForegroundRecursively:self.view
|
|
226
|
+
primaryColor:themeColor
|
|
227
|
+
headerTextColor:headerTextColor
|
|
228
|
+
accentColor:accentColor];
|
|
229
|
+
}
|
|
230
|
+
if (fontFamily.length > 0) {
|
|
231
|
+
[self qms_applyFontRecursively:self.view fontFamily:fontFamily];
|
|
232
|
+
} else {
|
|
233
|
+
[self qms_resetFontRecursively:self.view];
|
|
234
|
+
}
|
|
235
|
+
[self qms_applyStatusBarFillWithColor:themeColor];
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
- (void)qms_applyStatusBarFillWithColor:(UIColor *)themeColor {
|
|
239
|
+
UIColor *primary = themeColor ?: [UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0];
|
|
240
|
+
UIView *fillView = objc_getAssociatedObject(self, &QmsStatusBarFillViewKey);
|
|
241
|
+
if (!fillView) {
|
|
242
|
+
fillView = [[UIView alloc] init];
|
|
243
|
+
fillView.translatesAutoresizingMaskIntoConstraints = NO;
|
|
244
|
+
fillView.userInteractionEnabled = NO;
|
|
245
|
+
fillView.tag = 938477;
|
|
246
|
+
[self.view insertSubview:fillView atIndex:0];
|
|
247
|
+
[NSLayoutConstraint activateConstraints:@[
|
|
248
|
+
[fillView.topAnchor constraintEqualToAnchor:self.view.topAnchor],
|
|
249
|
+
[fillView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
|
|
250
|
+
[fillView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
|
|
251
|
+
[fillView.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor]
|
|
252
|
+
]];
|
|
253
|
+
objc_setAssociatedObject(self, &QmsStatusBarFillViewKey, fillView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
|
254
|
+
}
|
|
255
|
+
fillView.backgroundColor = primary;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
- (UIFont *)qms_fontByPreservingStyleFromFont:(UIFont *)sourceFont fontFamily:(NSString *)fontFamily {
|
|
259
|
+
if (!sourceFont || fontFamily.length == 0) return sourceFont;
|
|
260
|
+
UIFont *base = [UIFont fontWithName:fontFamily size:sourceFont.pointSize];
|
|
261
|
+
if (!base) return sourceFont;
|
|
262
|
+
|
|
263
|
+
UIFontDescriptorSymbolicTraits traits = sourceFont.fontDescriptor.symbolicTraits;
|
|
264
|
+
UIFontDescriptor *descriptor = [base.fontDescriptor fontDescriptorWithSymbolicTraits:traits];
|
|
265
|
+
if (descriptor) {
|
|
266
|
+
return [UIFont fontWithDescriptor:descriptor size:sourceFont.pointSize];
|
|
267
|
+
}
|
|
268
|
+
return base;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
- (UIFont *)qms_systemFontByPreservingStyleFromFont:(UIFont *)sourceFont {
|
|
272
|
+
if (!sourceFont) return sourceFont;
|
|
273
|
+
UIFontDescriptorSymbolicTraits traits = sourceFont.fontDescriptor.symbolicTraits;
|
|
274
|
+
BOOL isBold = (traits & UIFontDescriptorTraitBold) == UIFontDescriptorTraitBold;
|
|
275
|
+
BOOL isItalic = (traits & UIFontDescriptorTraitItalic) == UIFontDescriptorTraitItalic;
|
|
276
|
+
|
|
277
|
+
UIFont *result = nil;
|
|
278
|
+
if (isBold && isItalic) {
|
|
279
|
+
UIFontDescriptor *desc =
|
|
280
|
+
[[UIFont systemFontOfSize:sourceFont.pointSize].fontDescriptor
|
|
281
|
+
fontDescriptorWithSymbolicTraits:(UIFontDescriptorTraitBold | UIFontDescriptorTraitItalic)];
|
|
282
|
+
if (desc) {
|
|
283
|
+
result = [UIFont fontWithDescriptor:desc size:sourceFont.pointSize];
|
|
284
|
+
}
|
|
285
|
+
} else if (isBold) {
|
|
286
|
+
result = [UIFont boldSystemFontOfSize:sourceFont.pointSize];
|
|
287
|
+
} else if (isItalic) {
|
|
288
|
+
result = [UIFont italicSystemFontOfSize:sourceFont.pointSize];
|
|
289
|
+
} else {
|
|
290
|
+
result = [UIFont systemFontOfSize:sourceFont.pointSize];
|
|
291
|
+
}
|
|
292
|
+
return result ?: [UIFont systemFontOfSize:sourceFont.pointSize];
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
- (void)qms_applyFontRecursively:(UIView *)view fontFamily:(NSString *)fontFamily {
|
|
296
|
+
if (!view || fontFamily.length == 0) return;
|
|
297
|
+
|
|
298
|
+
if ([view isKindOfClass:[UILabel class]]) {
|
|
299
|
+
UILabel *label = (UILabel *)view;
|
|
300
|
+
label.font = [self qms_fontByPreservingStyleFromFont:label.font fontFamily:fontFamily];
|
|
301
|
+
} else if ([view isKindOfClass:[UIButton class]]) {
|
|
302
|
+
UIButton *button = (UIButton *)view;
|
|
303
|
+
if (button.titleLabel) {
|
|
304
|
+
button.titleLabel.font = [self qms_fontByPreservingStyleFromFont:button.titleLabel.font fontFamily:fontFamily];
|
|
305
|
+
}
|
|
306
|
+
} else if ([view isKindOfClass:[UITextField class]]) {
|
|
307
|
+
UITextField *textField = (UITextField *)view;
|
|
308
|
+
textField.font = [self qms_fontByPreservingStyleFromFont:textField.font fontFamily:fontFamily];
|
|
309
|
+
} else if ([view isKindOfClass:[UITextView class]]) {
|
|
310
|
+
UITextView *textView = (UITextView *)view;
|
|
311
|
+
textView.font = [self qms_fontByPreservingStyleFromFont:textView.font fontFamily:fontFamily];
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
for (UIView *subview in view.subviews) {
|
|
315
|
+
[self qms_applyFontRecursively:subview fontFamily:fontFamily];
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
- (void)qms_resetFontRecursively:(UIView *)view {
|
|
320
|
+
if (!view) return;
|
|
321
|
+
|
|
322
|
+
if ([view isKindOfClass:[UILabel class]]) {
|
|
323
|
+
UILabel *label = (UILabel *)view;
|
|
324
|
+
label.font = [self qms_systemFontByPreservingStyleFromFont:label.font];
|
|
325
|
+
} else if ([view isKindOfClass:[UIButton class]]) {
|
|
326
|
+
UIButton *button = (UIButton *)view;
|
|
327
|
+
if (button.titleLabel) {
|
|
328
|
+
button.titleLabel.font = [self qms_systemFontByPreservingStyleFromFont:button.titleLabel.font];
|
|
329
|
+
}
|
|
330
|
+
} else if ([view isKindOfClass:[UITextField class]]) {
|
|
331
|
+
UITextField *textField = (UITextField *)view;
|
|
332
|
+
textField.font = [self qms_systemFontByPreservingStyleFromFont:textField.font];
|
|
333
|
+
} else if ([view isKindOfClass:[UITextView class]]) {
|
|
334
|
+
UITextView *textView = (UITextView *)view;
|
|
335
|
+
textView.font = [self qms_systemFontByPreservingStyleFromFont:textView.font];
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
for (UIView *subview in view.subviews) {
|
|
339
|
+
[self qms_resetFontRecursively:subview];
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
70
343
|
- (BOOL)qms_shouldTrackAnalytics {
|
|
71
344
|
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
|
|
72
345
|
NSString *bundleId = bundle.bundleIdentifier ?: @"";
|
|
@@ -8,32 +8,32 @@
|
|
|
8
8
|
<key>BinaryPath</key>
|
|
9
9
|
<string>QmsPluginFramework.framework/QmsPluginFramework</string>
|
|
10
10
|
<key>LibraryIdentifier</key>
|
|
11
|
-
<string>ios-
|
|
11
|
+
<string>ios-arm64</string>
|
|
12
12
|
<key>LibraryPath</key>
|
|
13
13
|
<string>QmsPluginFramework.framework</string>
|
|
14
14
|
<key>SupportedArchitectures</key>
|
|
15
15
|
<array>
|
|
16
16
|
<string>arm64</string>
|
|
17
|
-
<string>x86_64</string>
|
|
18
17
|
</array>
|
|
19
18
|
<key>SupportedPlatform</key>
|
|
20
19
|
<string>ios</string>
|
|
21
|
-
<key>SupportedPlatformVariant</key>
|
|
22
|
-
<string>simulator</string>
|
|
23
20
|
</dict>
|
|
24
21
|
<dict>
|
|
25
22
|
<key>BinaryPath</key>
|
|
26
23
|
<string>QmsPluginFramework.framework/QmsPluginFramework</string>
|
|
27
24
|
<key>LibraryIdentifier</key>
|
|
28
|
-
<string>ios-
|
|
25
|
+
<string>ios-arm64_x86_64-simulator</string>
|
|
29
26
|
<key>LibraryPath</key>
|
|
30
27
|
<string>QmsPluginFramework.framework</string>
|
|
31
28
|
<key>SupportedArchitectures</key>
|
|
32
29
|
<array>
|
|
33
30
|
<string>arm64</string>
|
|
31
|
+
<string>x86_64</string>
|
|
34
32
|
</array>
|
|
35
33
|
<key>SupportedPlatform</key>
|
|
36
34
|
<string>ios</string>
|
|
35
|
+
<key>SupportedPlatformVariant</key>
|
|
36
|
+
<string>simulator</string>
|
|
37
37
|
</dict>
|
|
38
38
|
</array>
|
|
39
39
|
<key>CFBundlePackageType</key>
|
package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Assets.car
CHANGED
|
Binary file
|
package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Info.plist
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/QmsPluginFramework
CHANGED
|
Binary file
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<dict>
|
|
7
7
|
<key>Assets.car</key>
|
|
8
8
|
<data>
|
|
9
|
-
|
|
9
|
+
kEpAubglJYNFlRT4LSRI2MmXQlg=
|
|
10
10
|
</data>
|
|
11
11
|
<key>Headers/QmsPlugin.h</key>
|
|
12
12
|
<data>
|
|
@@ -18,12 +18,24 @@
|
|
|
18
18
|
</data>
|
|
19
19
|
<key>Info.plist</key>
|
|
20
20
|
<data>
|
|
21
|
-
|
|
21
|
+
sF1nzLQuh4BXmXMChhVDzjKB5C4=
|
|
22
22
|
</data>
|
|
23
23
|
<key>Modules/module.modulemap</key>
|
|
24
24
|
<data>
|
|
25
25
|
BD9YnWBkeFmDcuOl4cAjALd/7yw=
|
|
26
26
|
</data>
|
|
27
|
+
<key>Montserrat-Bold.ttf</key>
|
|
28
|
+
<data>
|
|
29
|
+
vnwUOAcKtjfhFxaRXk5QkDcm3Mg=
|
|
30
|
+
</data>
|
|
31
|
+
<key>Montserrat-Medium.ttf</key>
|
|
32
|
+
<data>
|
|
33
|
+
d/NVNBXYWMZTuupFqsqyAZZ3YzQ=
|
|
34
|
+
</data>
|
|
35
|
+
<key>Montserrat-Regular.ttf</key>
|
|
36
|
+
<data>
|
|
37
|
+
7BPvHyyZ+HgNby6z+fJ++J8wFhs=
|
|
38
|
+
</data>
|
|
27
39
|
</dict>
|
|
28
40
|
<key>files2</key>
|
|
29
41
|
<dict>
|
|
@@ -31,7 +43,7 @@
|
|
|
31
43
|
<dict>
|
|
32
44
|
<key>hash2</key>
|
|
33
45
|
<data>
|
|
34
|
-
|
|
46
|
+
0sUdnIkwFavEcnM4vnqYtEL76ynKxgMhnZSeTzEv99c=
|
|
35
47
|
</data>
|
|
36
48
|
</dict>
|
|
37
49
|
<key>Headers/QmsPlugin.h</key>
|
|
@@ -55,6 +67,27 @@
|
|
|
55
67
|
e+P6RtiuicqWfZ3H2B43r960DPkFwUWqXKWU+w7OVD0=
|
|
56
68
|
</data>
|
|
57
69
|
</dict>
|
|
70
|
+
<key>Montserrat-Bold.ttf</key>
|
|
71
|
+
<dict>
|
|
72
|
+
<key>hash2</key>
|
|
73
|
+
<data>
|
|
74
|
+
f6k9fapcL4gaZycinCEXgt5rdgCPvWNGs9dmTo/IP0M=
|
|
75
|
+
</data>
|
|
76
|
+
</dict>
|
|
77
|
+
<key>Montserrat-Medium.ttf</key>
|
|
78
|
+
<dict>
|
|
79
|
+
<key>hash2</key>
|
|
80
|
+
<data>
|
|
81
|
+
zRYcujo83PaK1yGSD80WTIfnt8yea61RAnjD+jeNkdI=
|
|
82
|
+
</data>
|
|
83
|
+
</dict>
|
|
84
|
+
<key>Montserrat-Regular.ttf</key>
|
|
85
|
+
<dict>
|
|
86
|
+
<key>hash2</key>
|
|
87
|
+
<data>
|
|
88
|
+
75z5nwF1vvUwuIk0vZBPz1b3c87G/U37jMr3ziu9OV4=
|
|
89
|
+
</data>
|
|
90
|
+
</dict>
|
|
58
91
|
</dict>
|
|
59
92
|
<key>rules</key>
|
|
60
93
|
<dict>
|