diff --git a/DiDiPrism.podspec b/DiDiPrism.podspec index abcd45e..1623402 100644 --- a/DiDiPrism.podspec +++ b/DiDiPrism.podspec @@ -136,6 +136,7 @@ Pod::Spec.new do |spec| spec.subspec 'Core' do |ss| ss.source_files = ['iOS/DiDiPrism/Src/Core/**/*{.h,.m}', 'iOS/DiDiPrism/Src/Category/**/*{.h,.m}', 'iOS/DiDiPrism/Src/Util/**/*{.h,.m}', 'iOS/DiDiPrism/Src/Protocol/**/*{.h,.m}'] + ss.dependency 'RSSwizzle' end spec.subspec 'WithBehaviorRecord' do |ss| diff --git a/iOS/DiDiPrism/Src/Ability/BehaviorDetect/Manager/PrismBehaviorDetectManager.m b/iOS/DiDiPrism/Src/Ability/BehaviorDetect/Manager/PrismBehaviorDetectManager.m index 82a87a4..d4fc0ba 100644 --- a/iOS/DiDiPrism/Src/Ability/BehaviorDetect/Manager/PrismBehaviorDetectManager.m +++ b/iOS/DiDiPrism/Src/Ability/BehaviorDetect/Manager/PrismBehaviorDetectManager.m @@ -33,15 +33,8 @@ - (instancetype)init { return self; } -- (BOOL)canDetect { - return YES; -} - #pragma mark - public method - (void)setupWithConfigModel:(PrismBehaviorDetectRuleConfigModel*)configModel { - if ([self canDetect] == NO) { - return; - } if (!configModel) { return; } @@ -57,7 +50,7 @@ - (void)addInstruction:(NSString*)instruction withParams:(NSDictionary*)params { if (!instruction.length) { return; } - if (!self.ruleConfigModel || [self canDetect] == NO) { + if (!self.ruleConfigModel) { return; } NSMutableDictionary *newParams = [NSMutableDictionary dictionaryWithDictionary:params]; diff --git a/iOS/DiDiPrism/Src/Ability/BehaviorRecord/Intercept/NSURLSessionConfiguration+PrismRecord.m b/iOS/DiDiPrism/Src/Ability/BehaviorRecord/Intercept/NSURLSessionConfiguration+PrismRecord.m deleted file mode 100644 index 323d675..0000000 --- a/iOS/DiDiPrism/Src/Ability/BehaviorRecord/Intercept/NSURLSessionConfiguration+PrismRecord.m +++ /dev/null @@ -1,37 +0,0 @@ -// -// NSURLSessionConfiguration+PrismRecord.m -// DiDiPrism -// -// Created by hulk on 2020/4/1. -// - -#import "NSURLSessionConfiguration+PrismRecord.h" -#import "PrismRecordNSURLProtocol.h" -// Util -#import "PrismRuntimeUtil.h" - -@implementation NSURLSessionConfiguration (PrismRecord) -+ (void)load { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - [PrismRuntimeUtil hookClass:object_getClass(self) originalSelector:@selector(defaultSessionConfiguration) swizzledSelector:@selector(autoDot_defaultSessionConfiguration) isClassMethod:YES]; - [PrismRuntimeUtil hookClass:object_getClass(self) originalSelector:@selector(ephemeralSessionConfiguration) swizzledSelector:@selector(autoDot_ephemeralSessionConfiguration) isClassMethod:YES]; - }); -} - -+ (NSURLSessionConfiguration *)autoDot_defaultSessionConfiguration{ - NSURLSessionConfiguration *configuration = [self autoDot_defaultSessionConfiguration]; - NSMutableArray * protocolClasses = [NSMutableArray arrayWithArray:configuration.protocolClasses]; - [protocolClasses insertObject:[PrismRecordNSURLProtocol class] atIndex:0]; - configuration.protocolClasses = [protocolClasses copy]; - return configuration; -} - -+ (NSURLSessionConfiguration *)autoDot_ephemeralSessionConfiguration{ - NSURLSessionConfiguration *configuration = [self autoDot_ephemeralSessionConfiguration]; - NSMutableArray * protocolClasses = [NSMutableArray arrayWithArray:configuration.protocolClasses]; - [protocolClasses insertObject:[PrismRecordNSURLProtocol class] atIndex:0]; - configuration.protocolClasses = [protocolClasses copy]; - return configuration; -} -@end diff --git a/iOS/DiDiPrism/Src/Ability/BehaviorRecord/Intercept/PrismRecordNSURLProtocol.h b/iOS/DiDiPrism/Src/Ability/BehaviorRecord/Intercept/PrismRecordNSURLProtocol.h index 5612ab3..4a772f8 100644 --- a/iOS/DiDiPrism/Src/Ability/BehaviorRecord/Intercept/PrismRecordNSURLProtocol.h +++ b/iOS/DiDiPrism/Src/Ability/BehaviorRecord/Intercept/PrismRecordNSURLProtocol.h @@ -10,7 +10,11 @@ NS_ASSUME_NONNULL_BEGIN @interface PrismRecordNSURLProtocol : NSURLProtocol - +/* + 定制用于提取网络请求信息的逻辑。 + */ +@property (nonatomic, strong, class) NSString*(^urlFlagPickBlock)(NSURLRequest*); +@property (nonatomic, strong, class) NSString*(^traceIdPickBlock)(NSURLRequest*); @end NS_ASSUME_NONNULL_END diff --git a/iOS/DiDiPrism/Src/Ability/BehaviorRecord/Intercept/PrismRecordNSURLProtocol.m b/iOS/DiDiPrism/Src/Ability/BehaviorRecord/Intercept/PrismRecordNSURLProtocol.m index 4937f67..4523477 100644 --- a/iOS/DiDiPrism/Src/Ability/BehaviorRecord/Intercept/PrismRecordNSURLProtocol.m +++ b/iOS/DiDiPrism/Src/Ability/BehaviorRecord/Intercept/PrismRecordNSURLProtocol.m @@ -6,7 +6,10 @@ // #import "PrismRecordNSURLProtocol.h" +#import #import "PrismBehaviorRecordManager.h" +// Category +#import "NSDictionary+PrismExtends.h" @interface PrismRecordNSURLProtocol() @property (nonatomic, strong) NSURLSession *session; @@ -22,15 +25,12 @@ + (BOOL)canInitWithTask:(NSURLSessionTask *)task { } + (BOOL)canInitWithRequest:(NSURLRequest *)request { - if ([[PrismBehaviorRecordManager sharedManager] canUpload] == NO) { - return NO; - } if (![request.URL.scheme isEqualToString:@"http"] && ![request.URL.scheme isEqualToString:@"https"]) { return NO; } - NSString *urlFlag = [PrismBehaviorRecordManager sharedManager].urlFlagPickBlock ? [PrismBehaviorRecordManager sharedManager].urlFlagPickBlock(request) : nil; - NSString *traceId = [PrismBehaviorRecordManager sharedManager].traceIdPickBlock ? [PrismBehaviorRecordManager sharedManager].traceIdPickBlock(request) : nil; + NSString *urlFlag = self.urlFlagPickBlock ? self.urlFlagPickBlock(request) : nil; + NSString *traceId = self.traceIdPickBlock ? self.traceIdPickBlock(request) : nil; [[PrismBehaviorRecordManager sharedManager] addRequestInfoWithUrl:urlFlag traceId:traceId]; return NO; @@ -39,7 +39,37 @@ + (BOOL)canInitWithRequest:(NSURLRequest *)request { #pragma mark - delegate #pragma mark - setters ++ (void)setUrlFlagPickBlock:(NSString * _Nonnull (^)(NSURLRequest * _Nonnull))urlFlagPickBlock { + objc_setAssociatedObject(self, @selector(urlFlagPickBlock), urlFlagPickBlock, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + ++ (void)setTraceIdPickBlock:(NSString * _Nonnull (^)(NSURLRequest * _Nonnull))traceIdPickBlock { + objc_setAssociatedObject(self, @selector(traceIdPickBlock), traceIdPickBlock, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} #pragma mark - getters ++ (NSString * _Nonnull (^)(NSURLRequest * _Nonnull))urlFlagPickBlock { + id result = objc_getAssociatedObject(self, _cmd); + if (!result) { + result = ^(NSURLRequest* request) { + return [NSString stringWithFormat:@"%@%@", request.URL.host, request.URL.path]; + }; + objc_setAssociatedObject(self, @selector(urlFlagPickBlock), result, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + return result; +} + ++ (NSString * _Nonnull (^)(NSURLRequest * _Nonnull))traceIdPickBlock { + id result = objc_getAssociatedObject(self, _cmd); + if (!result) { + result = ^(NSURLRequest* request) { + NSDictionary *header = request.allHTTPHeaderFields; + NSString *traceIdKey = @"traceid"; + return [header prism_stringForKey:traceIdKey]; + }; + objc_setAssociatedObject(self, @selector(traceIdPickBlock), result, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + return result; +} @end diff --git a/iOS/DiDiPrism/Src/Ability/BehaviorRecord/Manager/PrismBehaviorRecordManager+PrismDispatchListenerProtocol.m b/iOS/DiDiPrism/Src/Ability/BehaviorRecord/Manager/PrismBehaviorRecordManager+PrismDispatchListenerProtocol.m index 9a9524c..fb978db 100644 --- a/iOS/DiDiPrism/Src/Ability/BehaviorRecord/Manager/PrismBehaviorRecordManager+PrismDispatchListenerProtocol.m +++ b/iOS/DiDiPrism/Src/Ability/BehaviorRecord/Manager/PrismBehaviorRecordManager+PrismDispatchListenerProtocol.m @@ -7,6 +7,7 @@ #import "PrismBehaviorRecordManager+PrismDispatchListenerProtocol.h" #import "PrismInstructionParamUtil.h" +#import "PrismInstructionDefines.h" // Category #import "NSDictionary+PrismExtends.h" #import "UIView+PrismExtends.h" @@ -17,6 +18,7 @@ #import "PrismControlInstructionGenerator.h" #import "PrismEdgePanInstructionGenerator.h" #import "PrismTapGestureInstructionGenerator.h" +#import "PrismLongPressGestureInstructionGenerator.h" #import "PrismCellInstructionGenerator.h" #import "PrismViewControllerInstructionGenerator.h" @@ -24,16 +26,23 @@ @implementation PrismBehaviorRecordManager (PrismDispatchListenerProtocol) #pragma mark -delegate #pragma mark PrismDispatchListenerProtocol - (void)dispatchEvent:(PrismDispatchEvent)event withSender:(NSObject *)sender params:(NSDictionary *)params { - if (![self canUpload]) { - return; - } if (event == PrismDispatchEventUIControlSendAction_Start) { UIControl *control = (UIControl*)sender; NSObject *target = [params objectForKey:@"target"]; NSString *action = [params objectForKey:@"action"]; NSString *targetAndSelector = [NSString stringWithFormat:@"%@_&_%@", NSStringFromClass([target class]), action]; - if ([targetAndSelector isEqualToString:control.autoDotTargetAndSelector]) { - NSString *instruction = [PrismControlInstructionGenerator getInstructionOfControl:control]; + NSDictionary *prismAutoDotTargetAndSelector = [control.prismAutoDotTargetAndSelector copy]; + if ([[prismAutoDotTargetAndSelector allValues] containsObject:targetAndSelector]) { + NSMutableString *controlEvents = [NSMutableString string]; + for (NSString *key in [prismAutoDotTargetAndSelector allKeys]) { + if ([prismAutoDotTargetAndSelector[key] isEqualToString:targetAndSelector]) { + if (controlEvents.length) { + [controlEvents appendString:@"_&_"]; + } + [controlEvents appendString:key]; + } + } + NSString *instruction = [PrismControlInstructionGenerator getInstructionOfControl:control withTargetAndSelector:targetAndSelector withControlEvents:[controlEvents copy]]; if (instruction.length) { NSDictionary *eventParams = [PrismInstructionParamUtil getEventParamsWithElement:control]; [self addInstruction:instruction withEventParams:eventParams]; @@ -48,9 +57,9 @@ - (void)dispatchEvent:(PrismDispatchEvent)event withSender:(NSObject *)sender pa if (edgePanGestureRecognizer.state == UIGestureRecognizerStateBegan) { UIViewController *viewController = [edgePanGestureRecognizer.view prism_viewController]; UINavigationController *navigationController = [viewController isKindOfClass:[UINavigationController class]] ? (UINavigationController*)viewController : viewController.navigationController; - [edgePanGestureRecognizer setAutoDotNavigationController:navigationController]; + [edgePanGestureRecognizer setPrismAutoDotNavigationController:navigationController]; NSInteger viewControllerCount = navigationController.viewControllers.count; - [edgePanGestureRecognizer setAutoDotViewControllerCount:[NSNumber numberWithInteger:viewControllerCount]]; + [edgePanGestureRecognizer setPrismAutoDotViewControllerCount:[NSNumber numberWithInteger:viewControllerCount]]; } // 输入后退手势时,如果手指始终未离开屏幕,state会变为UIGestureRecognizerStateCancelled if (edgePanGestureRecognizer.state != UIGestureRecognizerStateEnded && @@ -63,9 +72,9 @@ - (void)dispatchEvent:(PrismDispatchEvent)event withSender:(NSObject *)sender pa return; } dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - UINavigationController *navigationController = [edgePanGestureRecognizer autoDotNavigationController]; + UINavigationController *navigationController = [edgePanGestureRecognizer prismAutoDotNavigationController]; NSInteger viewControllerCount = navigationController.viewControllers.count; - if (navigationController && (viewControllerCount <= [edgePanGestureRecognizer autoDotViewControllerCount].integerValue)) { + if (navigationController && (viewControllerCount <= [edgePanGestureRecognizer prismAutoDotViewControllerCount].integerValue)) { [self addInstruction:instruction]; } }); @@ -78,6 +87,14 @@ - (void)dispatchEvent:(PrismDispatchEvent)event withSender:(NSObject *)sender pa [self addInstruction:instruction withEventParams:eventParams]; } } + else if (event == PrismDispatchEventUILongPressGestureRecognizerAction) { + UILongPressGestureRecognizer *longPressGesture = (UILongPressGestureRecognizer*)sender; + NSString *instruction = [PrismLongPressGestureInstructionGenerator getInstructionOfLongPressGesture:longPressGesture]; + if (instruction.length) { + NSDictionary *eventParams = [PrismInstructionParamUtil getEventParamsWithElement:longPressGesture.view]; + [self addInstruction:instruction withEventParams:eventParams]; + } + } else if (event == PrismDispatchEventUIViewTouchesEnded_End) { UIView *view = (UIView*)sender; if ([view isKindOfClass:[UITableViewCell class]] || [view isKindOfClass:[UICollectionViewCell class]]) { @@ -94,17 +111,25 @@ - (void)dispatchEvent:(PrismDispatchEvent)event withSender:(NSObject *)sender pa [self addInstruction:instruction]; } else if (event == PrismDispatchEventWKWebViewInitWithFrame) { - if (![self canH5Upload]) { - return; - } WKWebView *webView = (WKWebView*)sender; WKWebViewConfiguration *configuration = [params objectForKey:@"configuration"]; NSString *recordScript = @"!function(){\"use strict\";var e=new(function(){function e(){}return e.prototype.record=function(e){for(var t=this.getContent(e),r=[];e&&\"body\"!==e.nodeName.toLowerCase();){var n=e.nodeName.toLowerCase();if(e.id)n+=\"#\"+e.id;else{for(var i=e,o=1;i.previousElementSibling;)i=i.previousElementSibling,o+=1;o>1&&(n+=\":nth-child(\"+o+\")\")}r.unshift(n),e=e.parentElement}return r.unshift(\"body\"),{instruct:r.join(\">\"),content:t}},e.prototype.getContent=function(e){return e.innerText?this.getText(e):e.getAttribute(\"src\")?e.getAttribute(\"src\"):e.querySelectorAll(\"img\")&&e.querySelectorAll(\"img\").length>0?this.getImgSrc(e):\"\"},e.prototype.getText=function(e){if(!(e.childNodes&&e.childNodes.length>0))return e.innerText||e.nodeValue;for(var t=0;t + (instancetype)sharedManager; -/* - 定制用于提取网络请求信息的逻辑。 - */ -@property (nonatomic, strong) NSString*(^urlFlagPickBlock)(NSURLRequest*); -@property (nonatomic, strong) NSString*(^traceIdPickBlock)(NSURLRequest*); - /* 初始化 */ - (void)install; - (void)uninstall; -/* - 是否可以Hook。 - */ -- (BOOL)canHook; -/* - 是否可以上报指令。 - */ -- (BOOL)canUpload; -/* - 是否可以上报H5页面指令。 -*/ -- (BOOL)canH5Upload; - - (void)addInstruction:(NSString*)instruction; - (void)addInstruction:(NSString*)instruction withEventParams:(NSDictionary*)eventParams; - (void)addRequestInfoWithUrl:(NSString*)url traceId:(NSString*)traceId; diff --git a/iOS/DiDiPrism/Src/Ability/BehaviorRecord/Manager/PrismBehaviorRecordManager.m b/iOS/DiDiPrism/Src/Ability/BehaviorRecord/Manager/PrismBehaviorRecordManager.m index defe0ae..916d35b 100644 --- a/iOS/DiDiPrism/Src/Ability/BehaviorRecord/Manager/PrismBehaviorRecordManager.m +++ b/iOS/DiDiPrism/Src/Ability/BehaviorRecord/Manager/PrismBehaviorRecordManager.m @@ -30,9 +30,7 @@ + (instancetype)sharedManager { - (instancetype)init { self = [super init]; if (self) { - if ([self canHook]) { - [self addNotifications]; - } + } return self; } @@ -47,18 +45,6 @@ - (void)uninstall { } -- (BOOL)canHook { - return YES; -} - -- (BOOL)canUpload { - return YES; -} - -- (BOOL)canH5Upload { - return YES; -} - - (void)addInstruction:(NSString *)instruction { [self addInstruction:instruction withEventParams:nil]; } @@ -82,9 +68,6 @@ - (void)addRequestInfoWithUrl:(NSString *)url traceId:(NSString *)traceId { #pragma mark - delegate #pragma mark WKScriptMessageHandler - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { - if ([self canH5Upload] == NO) { - return; - } if ([message.name isEqualToString:@"prism_record_instruct"]) { NSString *instruct = [NSString stringWithFormat:@"%@%@%@%@", kBeginOfH5Flag, [message.body prism_stringForKey:@"instruct"], kBeginOfViewRepresentativeContentFlag, [message.body prism_stringForKey:@"content"]]; [self addInstruction:instruct]; @@ -92,65 +75,11 @@ - (void)userContentController:(WKUserContentController *)userContentController d } #pragma mark - action -- (void)didFinishLaunching:(NSNotification*)notification { - if ([self canHook] == NO) { - return; - } - // 支持端外打开逻辑 - if ([notification.userInfo.allKeys containsObject:UIApplicationLaunchOptionsURLKey]) { - NSURL *openUrl = notification.userInfo[UIApplicationLaunchOptionsURLKey]; - if (openUrl.absoluteString.length) { - NSString *instruction = [NSString stringWithFormat:@"%@%@%@", kUIApplicationOpenURL, kBeginOfViewRepresentativeContentFlag, openUrl.absoluteString]; - [self addInstruction:instruction]; - } - } -} - -- (void)didBecomeActive:(NSNotification*)notification { - if ([self canUpload]) { - [self addInstruction:kUIApplicationBecomeActive]; - } -} - -- (void)willResignActive:(NSNotification*)notification { - if ([self canUpload]) { - [self addInstruction:kUIApplicationResignActive]; - } -} #pragma mark - private method -- (void)addNotifications { - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didFinishLaunching:) name:UIApplicationDidFinishLaunchingNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willResignActive:) name:UIApplicationWillResignActiveNotification object:nil]; -} #pragma mark - setters #pragma mark - getters -- (NSString * _Nonnull (^)(NSURLRequest * _Nonnull))urlFlagPickBlock { - if (_urlFlagPickBlock) { - return _urlFlagPickBlock; - } - else { - NSString*(^defaultPickBlock)(NSURLRequest*) = ^(NSURLRequest* request) { - return [NSString stringWithFormat:@"%@%@", request.URL.host, request.URL.path]; - }; - return defaultPickBlock; - } -} -- (NSString * _Nonnull (^)(NSURLRequest * _Nonnull))traceIdPickBlock { - if (_traceIdPickBlock) { - return _traceIdPickBlock; - } - else { - NSString*(^defaultPickBlock)(NSURLRequest*) = ^(NSURLRequest* request) { - NSDictionary *header = request.allHTTPHeaderFields; - NSString *traceIdKey = @"traceid"; - return [header prism_stringForKey:traceIdKey]; - }; - return defaultPickBlock; - } -} @end diff --git a/iOS/DiDiPrism/Src/Ability/BehaviorReplay/Manager/PrismBehaviorReplayManager+PrismDispatchListenerProtocol.m b/iOS/DiDiPrism/Src/Ability/BehaviorReplay/Manager/PrismBehaviorReplayManager+PrismDispatchListenerProtocol.m index faa2656..725634b 100644 --- a/iOS/DiDiPrism/Src/Ability/BehaviorReplay/Manager/PrismBehaviorReplayManager+PrismDispatchListenerProtocol.m +++ b/iOS/DiDiPrism/Src/Ability/BehaviorReplay/Manager/PrismBehaviorReplayManager+PrismDispatchListenerProtocol.m @@ -18,7 +18,7 @@ - (void)dispatchEvent:(PrismDispatchEvent)event withSender:(NSObject *)sender pa WKWebViewConfiguration *configuration = [params objectForKey:@"configuration"]; NSString *replayScript = @"!function(){\"use strict\";var t=new(function(){function t(){}return t.prototype.play=function(t){var e=this,n=document.querySelector(t);n&&(n.scrollIntoView({behavior:\"smooth\"}),this.change(n),setTimeout((function(){e.fireClick(n)}),3e3))},t.prototype.fireClick=function(t){if(document.createEvent){var e=document.createEvent(\"MouseEvents\");e.initEvent(\"click\",!0,!1),t.dispatchEvent(e)}},t.prototype.change=function(t){var e=t.getAttribute(\"style\"),n=\"background\",o=0,i=setTimeout((function r(){if(o)e?t.setAttribute(\"style\",e):t.setAttribute(\"style\",""),clearTimeout(i);else{var c=\"background-color: rgba(255, 0, 0,0.3)\";if(e&&(e.indexOf(n)>=0||e.indexOf(\"background-color\")>=0)){var u=e.split(\";\"),a=u.findIndex((function(t){return-1!==t.indexOf(n)||-1!==t.indexOf(\"background-color\")}));-1!==a&&u.splice(a,1,\"background-color: rgba(255, 0, 0,0.3)\"),c=u.join(\";\")}t.setAttribute(\"style\",c),o=1,i=setTimeout(r,1e3)}}),1e3)},t}());\"object\"==typeof window&&(window.PRISM_PLAYBACK_PLAY=t.play.bind(t))}();"; - [webView autoDot_addCustomScript:replayScript withConfiguration:configuration]; + [webView prism_autoDot_addCustomScript:replayScript withConfiguration:configuration]; } } @end diff --git a/iOS/DiDiPrism/Src/Ability/BehaviorReplay/Model/PrismBehaviorTranslater.m b/iOS/DiDiPrism/Src/Ability/BehaviorReplay/Model/PrismBehaviorTranslater.m index 0216d86..41e45cf 100644 --- a/iOS/DiDiPrism/Src/Ability/BehaviorReplay/Model/PrismBehaviorTranslater.m +++ b/iOS/DiDiPrism/Src/Ability/BehaviorReplay/Model/PrismBehaviorTranslater.m @@ -26,7 +26,6 @@ + (PrismBehaviorTextModel *)translateWithModel:(PrismBehaviorVideoModel *)model NSArray *eventArray = [model.instructionFormatter instructionFragmentWithType:PrismInstructionFragmentTypeEvent]; NSArray *h5ViewArray = [model.instructionFormatter instructionFragmentWithType:PrismInstructionFragmentTypeH5View]; NSArray *viewMotionArray = [model.instructionFormatter instructionFragmentWithType:PrismInstructionFragmentTypeViewMotion]; - NSArray *viewFunctionArray = [model.instructionFormatter instructionFragmentWithType:PrismInstructionFragmentTypeViewFunction]; NSArray *viewRepresentativeContentArray = [model.instructionFormatter instructionFragmentWithType:PrismInstructionFragmentTypeViewRepresentativeContent]; NSArray *viewPathArray = [model.instructionFormatter instructionFragmentWithType:PrismInstructionFragmentTypeViewPath]; @@ -71,33 +70,25 @@ + (PrismBehaviorTextModel *)translateWithModel:(PrismBehaviorVideoModel *)model textModel.descType = PrismBehaviorDescTypeText; textModel.descContent = @"列表"; } - // 翻译参考信息和功能信息 - NSArray *contentArray = [[[NSArray array] arrayByAddingObjectsFromArray:viewFunctionArray] arrayByAddingObjectsFromArray:viewRepresentativeContentArray]; + else if ([viewMotionType isEqualToString:kViewMotionLongPressGestureFlag]) { + textModel.operationName = @"长按"; + } + // 翻译参考信息 + NSArray *contentArray = [[NSArray array] arrayByAddingObjectsFromArray:viewRepresentativeContentArray]; [contentArray enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { - if ([self hasChinese:obj]) { + if ([obj containsString:kViewRepresentativeContentTypeText]) { textModel.descType = PrismBehaviorDescTypeText; - textModel.descContent = obj; + textModel.descContent = [obj stringByReplacingOccurrencesOfString:kViewRepresentativeContentTypeText withString:@""]; *stop = YES; } - else if ([self isOnlyNumber:obj]) { - textModel.descType = PrismBehaviorDescTypeText; - textModel.descContent = obj; + else if ([obj containsString:kViewRepresentativeContentTypeLocalImage]) { + textModel.descType = PrismBehaviorDescTypeLocalImage; + textModel.descContent = [obj stringByReplacingOccurrencesOfString:kViewRepresentativeContentTypeLocalImage withString:@""]; *stop = YES; } - else if ([self hasNetworkImage:obj]) { + else if ([obj containsString:kViewRepresentativeContentTypeNetworkImage]) { textModel.descType = PrismBehaviorDescTypeNetworkImage; - textModel.descContent = obj; - *stop = YES; - } - }]; - if (textModel.descType != PrismBehaviorDescTypeNone) { - return textModel; - } - - [viewFunctionArray enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { - if ([self hasLocalImage:obj]) { - textModel.descType = PrismBehaviorDescTypeLocalImage; - textModel.descContent = obj; + textModel.descContent = [obj stringByReplacingOccurrencesOfString:kViewRepresentativeContentTypeNetworkImage withString:@""]; *stop = YES; } }]; @@ -118,24 +109,6 @@ + (PrismBehaviorTextModel *)translateWithModel:(PrismBehaviorVideoModel *)model } #pragma mark - private method -+ (BOOL)hasChinese:(NSString *)str { - for(int i=0; i< [str length];i++){ - int a = [str characterAtIndex:i]; - if( a > 0x4e00 && a < 0x9fff) - { - return YES; - } - } - return NO; -} - -+ (BOOL)isOnlyNumber:(NSString *)str { - if ([[str prism_trimmingWhitespace] isEqualToString:@"0"] || str.integerValue) { - return YES; - } - return NO; -} - + (BOOL)hasNetworkImage:(NSString *)str { if (([str containsString:@"http://"] || [str containsString:@"https://"]) && ([str containsString:@".png"] || [str containsString:@".jpg"] || [str containsString:@".jpeg"])) { @@ -144,27 +117,6 @@ + (BOOL)hasNetworkImage:(NSString *)str { return NO; } -+ (BOOL)hasLocalImage:(NSString *)str { - NSString *lastStr = nil; - NSArray *result = [str componentsSeparatedByString:@"/"]; - if (result.lastObject) { - lastStr = result.lastObject; - } - - if ([UIImage imageNamed:str] || [UIImage imageNamed:lastStr]) { - return YES; - } - - for (NSBundle *bundle in [NSBundle allBundles]) { - UIImage *image = [UIImage imageNamed:lastStr inBundle:bundle compatibleWithTraitCollection:nil]; - if (image) { - return YES; - } - } - - return NO; -} - + (BOOL)isWindowWith:(NSString*)str { if ([NSClassFromString(str) isSubclassOfClass:[UIWindow class]]) { return YES; diff --git a/iOS/DiDiPrism/Src/Ability/DataVisualization/Manager/PrismDataVisualizationManager.m b/iOS/DiDiPrism/Src/Ability/DataVisualization/Manager/PrismDataVisualizationManager.m index 0d421de..d096c73 100644 --- a/iOS/DiDiPrism/Src/Ability/DataVisualization/Manager/PrismDataVisualizationManager.m +++ b/iOS/DiDiPrism/Src/Ability/DataVisualization/Manager/PrismDataVisualizationManager.m @@ -31,7 +31,6 @@ - (void)install { - (void)uninstall { [[PrismEventDispatcher sharedInstance] unregisterListener:(id)self]; [self setEnable:NO]; - [self.allComponents removeAllObjects]; } - (void)registerComponent:(PrismDataBaseComponent*)component { @@ -59,6 +58,9 @@ - (void)setEnable:(BOOL)enable { for (PrismDataBaseComponent *component in self.allComponents) { component.enable = _enable; } + if (!_enable) { + [self.allComponents removeAllObjects]; + } } #pragma mark - getters diff --git a/iOS/DiDiPrism/Src/Category/UIColor+PrismExtends.m b/iOS/DiDiPrism/Src/Category/UIColor+PrismExtends.m index 443553f..0d6a038 100644 --- a/iOS/DiDiPrism/Src/Category/UIColor+PrismExtends.m +++ b/iOS/DiDiPrism/Src/Category/UIColor+PrismExtends.m @@ -8,7 +8,7 @@ #import "UIColor+PrismExtends.h" @implementation UIColor (PrismExtends) -CGFloat colorComponentFrom(NSString *string, NSUInteger start, NSUInteger length) { +CGFloat prism_colorComponentFrom(NSString *string, NSUInteger start, NSUInteger length) { NSString *substring = [string substringWithRange:NSMakeRange(start, length)]; NSString *fullHex = length == 2 ? substring : [NSString stringWithFormat:@"%@%@", substring, substring]; @@ -25,30 +25,30 @@ + (UIColor *)prism_colorWithHexString:(NSString *)hexString { switch ([colorString length]) { case 3: // #RGB alpha = 1.0f; - red = colorComponentFrom(colorString, 0, 1); - green = colorComponentFrom(colorString, 1, 1); - blue = colorComponentFrom(colorString, 2, 1); + red = prism_colorComponentFrom(colorString, 0, 1); + green = prism_colorComponentFrom(colorString, 1, 1); + blue = prism_colorComponentFrom(colorString, 2, 1); break; case 4: // #ARGB - alpha = colorComponentFrom(colorString, 0, 1); - red = colorComponentFrom(colorString, 1, 1); - green = colorComponentFrom(colorString, 2, 1); - blue = colorComponentFrom(colorString, 3, 1); + alpha = prism_colorComponentFrom(colorString, 0, 1); + red = prism_colorComponentFrom(colorString, 1, 1); + green = prism_colorComponentFrom(colorString, 2, 1); + blue = prism_colorComponentFrom(colorString, 3, 1); break; case 6: // #RRGGBB alpha = 1.0f; - red = colorComponentFrom(colorString, 0, 2); - green = colorComponentFrom(colorString, 2, 2); - blue = colorComponentFrom(colorString, 4, 2); + red = prism_colorComponentFrom(colorString, 0, 2); + green = prism_colorComponentFrom(colorString, 2, 2); + blue = prism_colorComponentFrom(colorString, 4, 2); break; case 8: // #AARRGGBB - alpha = colorComponentFrom(colorString, 0, 2); - red = colorComponentFrom(colorString, 2, 2); - green = colorComponentFrom(colorString, 4, 2); - blue = colorComponentFrom(colorString, 6, 2); + alpha = prism_colorComponentFrom(colorString, 0, 2); + red = prism_colorComponentFrom(colorString, 2, 2); + green = prism_colorComponentFrom(colorString, 4, 2); + blue = prism_colorComponentFrom(colorString, 6, 2); break; default: diff --git a/iOS/DiDiPrism/Src/Category/UIGestureRecognizer+PrismExtends.h b/iOS/DiDiPrism/Src/Category/UIGestureRecognizer+PrismExtends.h new file mode 100644 index 0000000..839a6a8 --- /dev/null +++ b/iOS/DiDiPrism/Src/Category/UIGestureRecognizer+PrismExtends.h @@ -0,0 +1,19 @@ +// +// UIGestureRecognizer+PrismExtends.h +// DiDiPrism +// +// Created by hulk on 2021/6/23. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface UIGestureRecognizer (PrismExtends) + +@property (nonatomic, copy) NSString *prismAutoDotTargetAndSelector; +@property (nonatomic, copy) NSString *prismAutoDotResponseChainInfo; +@property (nonatomic, copy) NSArray *prismAutoDotAreaInfo; +@end + +NS_ASSUME_NONNULL_END diff --git a/iOS/DiDiPrism/Src/Category/UIGestureRecognizer+PrismExtends.m b/iOS/DiDiPrism/Src/Category/UIGestureRecognizer+PrismExtends.m new file mode 100644 index 0000000..7c776cd --- /dev/null +++ b/iOS/DiDiPrism/Src/Category/UIGestureRecognizer+PrismExtends.m @@ -0,0 +1,34 @@ +// +// UIGestureRecognizer+PrismExtends.m +// DiDiPrism +// +// Created by hulk on 2021/6/23. +// + +#import "UIGestureRecognizer+PrismExtends.h" +// Util +#import "PrismRuntimeUtil.h" + +@implementation UIGestureRecognizer (PrismExtends) +#pragma mark - property +- (NSString *)prismAutoDotTargetAndSelector { + return objc_getAssociatedObject(self, _cmd); +} +- (void)setPrismAutoDotTargetAndSelector:(NSString *)prismAutoDotTargetAndSelector { + objc_setAssociatedObject(self, @selector(prismAutoDotTargetAndSelector), prismAutoDotTargetAndSelector, OBJC_ASSOCIATION_COPY_NONATOMIC); +} + +- (NSString *)prismAutoDotResponseChainInfo { + return objc_getAssociatedObject(self, _cmd); +} +- (void)setPrismAutoDotResponseChainInfo:(NSString *)prismAutoDotResponseChainInfo { + objc_setAssociatedObject(self, @selector(prismAutoDotResponseChainInfo), prismAutoDotResponseChainInfo, OBJC_ASSOCIATION_COPY_NONATOMIC); +} + +- (NSArray *)prismAutoDotAreaInfo { + return objc_getAssociatedObject(self, _cmd); +} +- (void)setPrismAutoDotAreaInfo:(NSArray *)prismAutoDotAreaInfo { + objc_setAssociatedObject(self, @selector(prismAutoDotAreaInfo), prismAutoDotAreaInfo, OBJC_ASSOCIATION_COPY_NONATOMIC); +} +@end diff --git a/iOS/DiDiPrism/Src/Core/EventDispatcher/EventDefine/PrismDispatchEventDefine.h b/iOS/DiDiPrism/Src/Core/EventDispatcher/EventDefine/PrismDispatchEventDefine.h index a4ad6ca..560be56 100644 --- a/iOS/DiDiPrism/Src/Core/EventDispatcher/EventDefine/PrismDispatchEventDefine.h +++ b/iOS/DiDiPrism/Src/Core/EventDispatcher/EventDefine/PrismDispatchEventDefine.h @@ -8,6 +8,17 @@ #ifndef PrismDispatchEventDefine_h #define PrismDispatchEventDefine_h +typedef NS_ENUM(NSUInteger, PrismEventCategory) { + PrismEventCategoryTouch = 1 << 0, //点击事件 + PrismEventCategorySlip = 1 << 1, //滑动事件 + PrismEventCategoryPageSwitch = 1 << 2, //页面切换事件 + PrismEventCategoryNetwork = 1 << 3, //网络事件 + PrismEventCategorySystem = 1 << 4, //系统事件 + + PrismEventCategoryAll = 0xFFFFFFFF +}; + + typedef NS_ENUM(NSUInteger, PrismDispatchEvent) { /* UIView @@ -28,6 +39,10 @@ typedef NS_ENUM(NSUInteger, PrismDispatchEvent) { UITapGestureRecognizer */ PrismDispatchEventUITapGestureRecognizerAction, + /* + UILongPressGestureRecognizer + */ + PrismDispatchEventUILongPressGestureRecognizerAction, /* UIViewController */ @@ -47,6 +62,12 @@ typedef NS_ENUM(NSUInteger, PrismDispatchEvent) { WKWebView */ PrismDispatchEventWKWebViewInitWithFrame, + /* + UIApplication + */ + PrismDispatchEventUIApplicationDidBecomeActive, + PrismDispatchEventUIApplicationWillResignActive, + PrismDispatchEventUIApplicationLaunchByURL, }; #endif /* PrismDispatchEventDefine_h */ diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Define/PrismInstructionDefines.h b/iOS/DiDiPrism/Src/Core/Instruction/Define/PrismInstructionDefines.h index 53ff0d6..aab8b4c 100644 --- a/iOS/DiDiPrism/Src/Core/Instruction/Define/PrismInstructionDefines.h +++ b/iOS/DiDiPrism/Src/Core/Instruction/Define/PrismInstructionDefines.h @@ -11,6 +11,7 @@ // 基础标识 #define kFragmentFlag @"_^_" #define kConnectorFlag @"_&_" +#define kDictionaryKeyPrefix @"prism-" #define kViewMotionFlag @"vm" #define kViewPathFlag @"vp" #define kViewListFlag @"vl" @@ -40,7 +41,13 @@ // View Motion具体标识 #define kViewMotionControlFlag @"control" #define kViewMotionEdgePanGestureFlag @"edgePanGesture" -#define kViewMotionGestureFlag @"gesture" +#define kViewMotionTapGestureFlag @"tapGesture" +#define kViewMotionLongPressGestureFlag @"longPressGesture" #define kViewMotionCellFlag @"cell" + +// View RepresentativeContent +#define kViewRepresentativeContentTypeText @"[_text]" +#define kViewRepresentativeContentTypeLocalImage @"[_l_image]" +#define kViewRepresentativeContentTypeNetworkImage @"[_n_image]" #endif /* PrismInstructionDefines_h */ diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Define/PrismInstructionModel.h b/iOS/DiDiPrism/Src/Core/Instruction/Define/PrismInstructionModel.h new file mode 100644 index 0000000..f2a46a2 --- /dev/null +++ b/iOS/DiDiPrism/Src/Core/Instruction/Define/PrismInstructionModel.h @@ -0,0 +1,24 @@ +// +// PrismInstructionModel.h +// DiDiPrism +// +// Created by hulk on 2021/6/16. +// + +#import +NS_ASSUME_NONNULL_BEGIN + +@interface PrismInstructionModel : NSObject +@property (nonatomic, copy) NSString *vm; +@property (nonatomic, copy) NSString *vp; +@property (nonatomic, copy) NSString *vl; +@property (nonatomic, copy) NSString *vq; +@property (nonatomic, copy) NSString *vr; +@property (nonatomic, copy) NSString *e; +@property (nonatomic, copy) NSString *vf; +@property (nonatomic, copy) NSString *h5; + +- (NSDictionary*)toDictionary; +@end + +NS_ASSUME_NONNULL_END diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Define/PrismInstructionModel.m b/iOS/DiDiPrism/Src/Core/Instruction/Define/PrismInstructionModel.m new file mode 100644 index 0000000..35caebe --- /dev/null +++ b/iOS/DiDiPrism/Src/Core/Instruction/Define/PrismInstructionModel.m @@ -0,0 +1,28 @@ +// +// PrismInstructionModel.m +// DiDiPrism +// +// Created by hulk on 2021/6/16. +// + +#import "PrismInstructionModel.h" +#import "PrismInstructionDefines.h" + +@implementation PrismInstructionModel +- (NSDictionary *)toDictionary { + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + self.vm.length ? [dictionary setObject:self.vm forKey:[NSString stringWithFormat:@"%@%@", kDictionaryKeyPrefix, @"vm"]] : NO; + self.vp.length ? [dictionary setObject:self.vp forKey:[NSString stringWithFormat:@"%@%@", kDictionaryKeyPrefix, @"vp"]] : NO; + self.vl.length ? [dictionary setObject:self.vl forKey:[NSString stringWithFormat:@"%@%@", kDictionaryKeyPrefix, @"vl"]] : NO; + self.vq.length ? [dictionary setObject:self.vq forKey:[NSString stringWithFormat:@"%@%@", kDictionaryKeyPrefix, @"vq"]] : NO; + self.vr.length ? [dictionary setObject:self.vr forKey:[NSString stringWithFormat:@"%@%@", kDictionaryKeyPrefix, @"vr"]] : NO; + self.e.length ? [dictionary setObject:self.e forKey:[NSString stringWithFormat:@"%@%@", kDictionaryKeyPrefix, @"e"]] : NO; + self.vf.length ? [dictionary setObject:self.vf forKey:[NSString stringWithFormat:@"%@%@", kDictionaryKeyPrefix, @"vf"]] : NO; + self.h5.length ? [dictionary setObject:self.h5 forKey:[NSString stringWithFormat:@"%@%@", kDictionaryKeyPrefix, @"h5"]] : NO; + return [dictionary copy]; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"Prism: vm:%@ | vp:%@ | vl:%@ | vq:%@ | vr:%@ | vf:%@ | h5:%@", self.vm ?: @"", self.vp ?: @"", self.vl ?: @"", self.vq ?: @"", self.vr ?: @"", self.vf ?: @"", self.h5 ?: @""]; +} +@end diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismCellInstructionGenerator.h b/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismCellInstructionGenerator.h index 7b41249..e3b33d4 100644 --- a/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismCellInstructionGenerator.h +++ b/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismCellInstructionGenerator.h @@ -6,12 +6,14 @@ // #import +#import "PrismInstructionModel.h" NS_ASSUME_NONNULL_BEGIN @interface PrismCellInstructionGenerator : NSObject + (NSString*)getInstructionOfCell:(UIView*)cell; ++ (PrismInstructionModel*)getInstructionModelOfCell:(UIView*)cell; @end NS_ASSUME_NONNULL_END diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismCellInstructionGenerator.m b/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismCellInstructionGenerator.m index 5e1e0d3..e64eb1e 100644 --- a/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismCellInstructionGenerator.m +++ b/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismCellInstructionGenerator.m @@ -8,11 +8,12 @@ #import "PrismCellInstructionGenerator.h" #import "PrismInstructionDefines.h" // Util -#import "PrismInstructionResponseChainUtil.h" -#import "PrismInstructionAreaUtil.h" +#import "PrismInstructionResponseChainInfoUtil.h" +#import "PrismInstructionAreaInfoUtil.h" #import "PrismInstructionContentUtil.h" // Category #import "UIResponder+PrismIntercept.h" +#import "NSArray+PrismExtends.h" @interface PrismCellInstructionGenerator() @@ -23,11 +24,24 @@ @implementation PrismCellInstructionGenerator #pragma mark - public method + (NSString*)getInstructionOfCell:(UIView*)cell { - NSString *responseChainInfo = [PrismInstructionResponseChainUtil getResponseChainInfoWithElement:cell]; - NSString *areaInfo = [PrismInstructionAreaUtil getAreaInfoWithElement:cell]; + NSString *responseChainInfo = [PrismInstructionResponseChainInfoUtil getResponseChainInfoWithElement:cell]; + NSArray *areaInfo = [PrismInstructionAreaInfoUtil getAreaInfoWithElement:cell]; + NSString *listInfo = [areaInfo prism_stringWithIndex:0]; + NSString *quadrantInfo = [areaInfo prism_stringWithIndex:1]; NSString *viewContent = [PrismInstructionContentUtil getRepresentativeContentOfView:cell needRecursive:YES]; - cell.autoDotFinalMark = [NSString stringWithFormat:@"%@%@%@%@%@%@", kBeginOfViewMotionFlag, kViewMotionCellFlag, responseChainInfo, areaInfo, kBeginOfViewRepresentativeContentFlag, viewContent.length ? viewContent : @""]; - return cell.autoDotFinalMark; + cell.prismAutoDotFinalMark = [NSString stringWithFormat:@"%@%@%@%@%@%@%@%@%@%@", kBeginOfViewMotionFlag, kViewMotionCellFlag, kBeginOfViewPathFlag, responseChainInfo ?: @"", kBeginOfViewListFlag, listInfo ?: @"", kBeginOfViewQuadrantFlag, quadrantInfo ?: @"", kBeginOfViewRepresentativeContentFlag, viewContent ?: @""]; + return cell.prismAutoDotFinalMark; +} + ++ (PrismInstructionModel *)getInstructionModelOfCell:(UIView *)cell { + PrismInstructionModel *model = [[PrismInstructionModel alloc] init]; + model.vm = kViewMotionCellFlag; + model.vp = [PrismInstructionResponseChainInfoUtil getResponseChainInfoWithElement:cell]; + NSArray *areaInfo = [PrismInstructionAreaInfoUtil getAreaInfoWithElement:cell]; + model.vl = [areaInfo prism_stringWithIndex:0]; + model.vq = [areaInfo prism_stringWithIndex:1]; + model.vr = [PrismInstructionContentUtil getRepresentativeContentOfView:cell needRecursive:YES]; + return model; } #pragma mark - private method diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismControlInstructionGenerator.h b/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismControlInstructionGenerator.h index d8c3907..9234a68 100644 --- a/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismControlInstructionGenerator.h +++ b/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismControlInstructionGenerator.h @@ -6,13 +6,19 @@ // #import +#import "PrismInstructionModel.h" NS_ASSUME_NONNULL_BEGIN @interface PrismControlInstructionGenerator : NSObject -+ (NSString*)getInstructionOfControl:(UIControl*)control; -+ (NSString*)getFunctionNameOfControl:(UIControl*)control; ++ (NSString *)getInstructionOfControl:(UIControl *)control + withTargetAndSelector:(NSString *)targetAndSelector + withControlEvents:(NSString*)controlEvents; ++ (PrismInstructionModel *)getInstructionModelOfControl:(UIControl *)control + withTargetAndSelector:(NSString *)targetAndSelector + withControlEvents:(NSString*)controlEvents; ++ (NSString*)getViewContentOfControl:(UIControl*)control; @end diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismControlInstructionGenerator.m b/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismControlInstructionGenerator.m index 33d1d8c..986158b 100644 --- a/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismControlInstructionGenerator.m +++ b/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismControlInstructionGenerator.m @@ -11,9 +11,10 @@ #import "UIControl+PrismIntercept.h" #import "UIImage+PrismIntercept.h" #import "UIResponder+PrismIntercept.h" +#import "NSArray+PrismExtends.h" // Util -#import "PrismInstructionResponseChainUtil.h" -#import "PrismInstructionAreaUtil.h" +#import "PrismInstructionResponseChainInfoUtil.h" +#import "PrismInstructionAreaInfoUtil.h" #import "PrismInstructionContentUtil.h" @interface PrismControlInstructionGenerator() @@ -24,77 +25,78 @@ @implementation PrismControlInstructionGenerator #pragma mark - life cycle #pragma mark - public method -+ (NSString*)getInstructionOfControl:(UIControl*)control { - // 避免重复生成,但会出现被复用控件标识不变的问题。 - if (control.autoDotFinalMark.length) { - return control.autoDotFinalMark; - } - NSString *responseChainInfo = [PrismInstructionResponseChainUtil getResponseChainInfoWithElement:control]; - NSString *areaInfo = [PrismInstructionAreaUtil getAreaInfoWithElement:control]; - NSString *functionName = [self getFunctionNameOfControl:control]; - NSString *instruction = [NSString stringWithFormat:@"%@%@%@%@%@", kBeginOfViewMotionFlag, kViewMotionControlFlag, responseChainInfo, areaInfo, functionName]; - // 注:列表中的cell存在复用机制,cell复用时指令不可复用。 - if ([areaInfo containsString:kBeginOfViewListFlag]) { - return instruction; - } - else { - control.autoDotFinalMark = instruction; - return control.autoDotFinalMark; - } ++ (NSString *)getInstructionOfControl:(UIControl *)control + withTargetAndSelector:(NSString *)targetAndSelector + withControlEvents:(NSString*)controlEvents { + NSString *responseChainInfo = [PrismInstructionResponseChainInfoUtil getResponseChainInfoWithElement:control]; + NSArray *areaInfo = [PrismInstructionAreaInfoUtil getAreaInfoWithElement:control]; + NSString *listInfo = [areaInfo prism_stringWithIndex:0]; + NSString *quadrantInfo = [areaInfo prism_stringWithIndex:1]; + NSString *viewContent = [self getViewContentOfControl:control]; + NSString *functionName = [NSString stringWithFormat:@"%@%@%@", targetAndSelector ?: @"", kConnectorFlag, controlEvents ?: @""]; + NSString *instruction = [NSString stringWithFormat:@"%@%@%@%@%@%@%@%@%@%@%@%@", kBeginOfViewMotionFlag, kViewMotionControlFlag, kBeginOfViewPathFlag , responseChainInfo ?: @"", kBeginOfViewListFlag, listInfo ?: @"", kBeginOfViewQuadrantFlag, quadrantInfo ?: @"", kBeginOfViewRepresentativeContentFlag, viewContent ?: @"", kBeginOfViewFunctionFlag, functionName ?: @""]; + return instruction; +} + ++ (PrismInstructionModel *)getInstructionModelOfControl:(UIControl *)control + withTargetAndSelector:(NSString *)targetAndSelector + withControlEvents:(NSString*)controlEvents { + PrismInstructionModel *model = [[PrismInstructionModel alloc] init]; + model.vm = kViewMotionControlFlag; + model.vp = [PrismInstructionResponseChainInfoUtil getResponseChainInfoWithElement:control]; + NSArray *areaInfo = [PrismInstructionAreaInfoUtil getAreaInfoWithElement:control]; + model.vl = [areaInfo prism_stringWithIndex:0]; + model.vq = [areaInfo prism_stringWithIndex:1]; + model.vr = [self getViewContentOfControl:control]; + model.vf = [NSString stringWithFormat:@"%@%@%@", targetAndSelector ?: @"", kConnectorFlag, controlEvents ?: @""]; + return model; } -+ (NSString*)getFunctionNameOfControl:(UIControl*)control { - NSString *functionName = nil; ++ (NSString*)getViewContentOfControl:(UIControl*)control { + NSString *viewContent = @""; if ([control isKindOfClass:[UIButton class]]) { - functionName = [self getFunctionNameOfButton:(UIButton *)control]; + viewContent = [self getViewContentOfButton:(UIButton *)control]; } else if ([control isKindOfClass:[UISwitch class]]) { - functionName = [self getFunctionNameOfSwitch:(UISwitch *)control]; + viewContent = [self getViewContentOfSwitch:(UISwitch *)control]; } else if ([control isKindOfClass:[UITextField class]]) { - functionName = [self getFunctionNameOfTextField:(UITextField *)control]; + viewContent = [self getViewContentOfTextField:(UITextField *)control]; } - - if (!functionName.length) { + if (!viewContent.length) { // 获取有代表性的内容便于更好的定位view - NSString *subviewContent = [PrismInstructionContentUtil getRepresentativeContentOfView:control needRecursive:YES]; - if (subviewContent.length) { - functionName = [NSString stringWithFormat:@"%@_&_%@", subviewContent, control.autoDotTargetAndSelector]; - } - else { - functionName = control.autoDotTargetAndSelector; - } + viewContent = [PrismInstructionContentUtil getRepresentativeContentOfView:control needRecursive:YES]; } - return [NSString stringWithFormat:@"%@%@", kBeginOfViewFunctionFlag, functionName]; + return viewContent; } #pragma mark - private method -+ (NSString*)getFunctionNameOfButton:(UIButton*)button { ++ (NSString*)getViewContentOfButton:(UIButton*)button { if (button.titleLabel.text.length) { - return button.titleLabel.text; + return [NSString stringWithFormat:@"%@%@", kViewRepresentativeContentTypeText, button.titleLabel.text]; } else if (button.titleLabel.attributedText.length) { - return button.titleLabel.attributedText.string; + return [NSString stringWithFormat:@"%@%@", kViewRepresentativeContentTypeText, button.titleLabel.attributedText.string]; } else if (button.imageView.image) { - return button.imageView.image.autoDotImageName; + return [NSString stringWithFormat:@"%@%@", kViewRepresentativeContentTypeLocalImage, button.imageView.image.prismAutoDotImageName]; } return nil; } -+ (NSString*)getFunctionNameOfSwitch:(UISwitch*)switchControl { - if (switchControl.onImage.autoDotImageName.length) { - return switchControl.onImage.autoDotImageName; ++ (NSString*)getViewContentOfSwitch:(UISwitch*)switchControl { + if (switchControl.onImage.prismAutoDotImageName.length) { + return [NSString stringWithFormat:@"%@%@", kViewRepresentativeContentTypeLocalImage, switchControl.onImage.prismAutoDotImageName]; } - else if (switchControl.offImage.autoDotImageName.length) { - return switchControl.offImage.autoDotImageName; + else if (switchControl.offImage.prismAutoDotImageName.length) { + return [NSString stringWithFormat:@"%@%@", kViewRepresentativeContentTypeLocalImage, switchControl.offImage.prismAutoDotImageName]; } return nil; } -+ (NSString*)getFunctionNameOfTextField:(UITextField*)textField { ++ (NSString*)getViewContentOfTextField:(UITextField*)textField { if (textField.placeholder.length) { - return textField.placeholder; + return [NSString stringWithFormat:@"%@%@", kViewRepresentativeContentTypeText, textField.placeholder]; } return nil; } diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismEdgePanInstructionGenerator.h b/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismEdgePanInstructionGenerator.h index 351ce5f..0d342e4 100644 --- a/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismEdgePanInstructionGenerator.h +++ b/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismEdgePanInstructionGenerator.h @@ -6,12 +6,13 @@ // #import +#import "PrismInstructionModel.h" NS_ASSUME_NONNULL_BEGIN @interface PrismEdgePanInstructionGenerator : NSObject - + (NSString*)getInstructionOfEdgePanGesture:(UIScreenEdgePanGestureRecognizer*)edgePanGesture; ++ (PrismInstructionModel*)getInstructionModelOfEdgePanGesture:(UIScreenEdgePanGestureRecognizer*)edgePanGesture; @end NS_ASSUME_NONNULL_END diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismEdgePanInstructionGenerator.m b/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismEdgePanInstructionGenerator.m index 14b8abf..5691519 100644 --- a/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismEdgePanInstructionGenerator.m +++ b/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismEdgePanInstructionGenerator.m @@ -11,7 +11,7 @@ #import "UIResponder+PrismIntercept.h" #import "UIView+PrismExtends.h" // Util -#import "PrismInstructionResponseChainUtil.h" +#import "PrismInstructionResponseChainInfoUtil.h" @interface PrismEdgePanInstructionGenerator() @@ -26,13 +26,24 @@ + (NSString*)getInstructionOfEdgePanGesture:(UIScreenEdgePanGestureRecognizer*)e if (!view) { return nil; } - if (view.autoDotFinalMark.length) { - return view.autoDotFinalMark; + if (view.prismAutoDotFinalMark.length) { + return view.prismAutoDotFinalMark; } - NSString *responseChainInfo = [PrismInstructionResponseChainUtil getResponseChainInfoWithElement:view]; - view.autoDotFinalMark = [NSString stringWithFormat:@"%@%@%@", kBeginOfViewMotionFlag, kViewMotionEdgePanGestureFlag, responseChainInfo]; - return view.autoDotFinalMark; + NSString *responseChainInfo = [PrismInstructionResponseChainInfoUtil getResponseChainInfoWithElement:view]; + view.prismAutoDotFinalMark = [NSString stringWithFormat:@"%@%@%@%@", kBeginOfViewMotionFlag, kViewMotionEdgePanGestureFlag, kBeginOfViewPathFlag, responseChainInfo]; + return view.prismAutoDotFinalMark; +} + ++ (PrismInstructionModel *)getInstructionModelOfEdgePanGesture:(UIScreenEdgePanGestureRecognizer *)edgePanGesture { + UIView *view = edgePanGesture.view; + if (!view) { + return nil; + } + PrismInstructionModel *model = [[PrismInstructionModel alloc] init]; + model.vm = kViewMotionEdgePanGestureFlag; + model.vp = [PrismInstructionResponseChainInfoUtil getResponseChainInfoWithElement:view]; + return model; } #pragma mark - private method diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismLongPressGestureInstructionGenerator.h b/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismLongPressGestureInstructionGenerator.h new file mode 100644 index 0000000..fa9e551 --- /dev/null +++ b/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismLongPressGestureInstructionGenerator.h @@ -0,0 +1,19 @@ +// +// PrismLongPressGestureInstructionGenerator.h +// DiDiPrism +// +// Created by hulk on 2021/6/25. +// + +#import +#import "PrismInstructionModel.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PrismLongPressGestureInstructionGenerator : NSObject ++ (NSString*)getInstructionOfLongPressGesture:(UILongPressGestureRecognizer*)longPressGesture; ++ (PrismInstructionModel *)getInstructionModelOfLongPressGesture:(UILongPressGestureRecognizer *)longPressGesture; ++ (NSString*)getFunctionNameOfLongPressGesture:(UILongPressGestureRecognizer*)longPressGesture; +@end + +NS_ASSUME_NONNULL_END diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismLongPressGestureInstructionGenerator.m b/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismLongPressGestureInstructionGenerator.m new file mode 100644 index 0000000..112828a --- /dev/null +++ b/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismLongPressGestureInstructionGenerator.m @@ -0,0 +1,102 @@ +// +// PrismLongPressGestureInstructionGenerator.m +// DiDiPrism +// +// Created by hulk on 2021/6/25. +// + +#import "PrismLongPressGestureInstructionGenerator.h" +#import "PrismInstructionDefines.h" +// Util +#import "PrismInstructionResponseChainInfoUtil.h" +#import "PrismInstructionContentUtil.h" +// Category +#import "UIResponder+PrismIntercept.h" +#import "UIImage+PrismIntercept.h" +#import "UIView+PrismExtends.h" +#import "NSArray+PrismExtends.h" +#import "UIGestureRecognizer+PrismExtends.h" + +@implementation PrismLongPressGestureInstructionGenerator +#pragma mark - life cycle + +#pragma mark - public method ++ (NSString*)getInstructionOfLongPressGesture:(UILongPressGestureRecognizer*)longPressGesture { + UIView *view = longPressGesture.view; + if (!view) { + return nil; + } + if (view.prismAutoDotFinalMark.length) { + return view.prismAutoDotFinalMark; + } + + NSString *responseChainInfo = [longPressGesture prismAutoDotResponseChainInfo]; + NSArray *areaInfo = [longPressGesture prismAutoDotAreaInfo]; + NSString *listInfo = [areaInfo prism_stringWithIndex:0]; + NSString *quadrantInfo = [areaInfo prism_stringWithIndex:1]; + NSString *viewContent = [PrismInstructionContentUtil getRepresentativeContentOfView:view needRecursive:YES]; + NSString *functionName = [self getFunctionNameOfLongPressGesture:longPressGesture]; + NSString *instruction = [NSString stringWithFormat:@"%@%@%@%@%@%@%@%@%@%@%@%@", kBeginOfViewMotionFlag, kViewMotionLongPressGestureFlag, kBeginOfViewPathFlag , responseChainInfo ?: @"", kBeginOfViewListFlag, listInfo ?: @"", kBeginOfViewQuadrantFlag, quadrantInfo ?: @"", kBeginOfViewRepresentativeContentFlag, viewContent ?: @"", kBeginOfViewFunctionFlag, functionName ?: @""]; + // 注:列表中的cell存在复用机制,cell复用时指令不可复用。 + if (listInfo.length) { + return instruction; + } + else { + view.prismAutoDotFinalMark = instruction; + return view.prismAutoDotFinalMark; + } +} + ++ (PrismInstructionModel *)getInstructionModelOfLongPressGesture:(UILongPressGestureRecognizer *)longPressGesture { + UIView *view = longPressGesture.view; + if (!view) { + return nil; + } + PrismInstructionModel *model = [[PrismInstructionModel alloc] init]; + model.vm = kViewMotionLongPressGestureFlag; + model.vp = [longPressGesture prismAutoDotResponseChainInfo]; + NSArray *areaInfo = [longPressGesture prismAutoDotAreaInfo]; + model.vl = [areaInfo prism_stringWithIndex:0]; + model.vq = [areaInfo prism_stringWithIndex:1]; + model.vr = [PrismInstructionContentUtil getRepresentativeContentOfView:view needRecursive:YES]; + model.vf = [self getFunctionNameOfLongPressGesture:longPressGesture]; + return model; +} + ++ (NSString*)getFunctionNameOfLongPressGesture:(UILongPressGestureRecognizer*)longPressGesture { + UIView *view = longPressGesture.view; + + // WEEX + id wxComponent = [view prism_wxComponent]; + if (wxComponent && [wxComponent isKindOfClass:NSClassFromString(@"WXComponent")]) { + NSMutableString *functionName = [NSMutableString string]; + NSDictionary *wxAttributes = [wxComponent valueForKey:@"attributes"]; + NSString *wxFunctionName = [wxAttributes valueForKey:@"prismFunctionName"]; + NSString *wxClassName = [wxAttributes valueForKey:@"prismClassName"]; + if (([wxFunctionName isKindOfClass:[NSString class]] && wxFunctionName.length) + || ([wxClassName isKindOfClass:[NSString class]] && wxClassName.length)) { + if ([wxFunctionName isKindOfClass:[NSString class]] && wxFunctionName.length) { + [functionName appendString:wxFunctionName]; + } + if ([wxClassName isKindOfClass:[NSString class]] && wxClassName.length) { + if (functionName.length) { + [functionName appendString:@"-"]; + } + [functionName appendString:wxClassName]; + } + } + if (functionName.length) { + return [NSString stringWithFormat:@"%@_&_%@", functionName, longPressGesture.prismAutoDotTargetAndSelector]; + } + } + + return longPressGesture.prismAutoDotTargetAndSelector; +} + +#pragma mark - private method + +#pragma mark - setters + +#pragma mark - getters + +@end diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismTapGestureInstructionGenerator.h b/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismTapGestureInstructionGenerator.h index 4a9aa9a..3d8a008 100644 --- a/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismTapGestureInstructionGenerator.h +++ b/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismTapGestureInstructionGenerator.h @@ -6,12 +6,14 @@ // #import +#import "PrismInstructionModel.h" NS_ASSUME_NONNULL_BEGIN @interface PrismTapGestureInstructionGenerator : NSObject + (NSString*)getInstructionOfTapGesture:(UITapGestureRecognizer*)tapGesture; ++ (PrismInstructionModel*)getInstructionModelOfTapGesture:(UITapGestureRecognizer*)tapGesture; + (NSString*)getFunctionNameOfTapGesture:(UITapGestureRecognizer*)tapGesture; @end diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismTapGestureInstructionGenerator.m b/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismTapGestureInstructionGenerator.m index a089703..bf17c5c 100644 --- a/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismTapGestureInstructionGenerator.m +++ b/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismTapGestureInstructionGenerator.m @@ -8,13 +8,15 @@ #import "PrismTapGestureInstructionGenerator.h" #import "PrismInstructionDefines.h" // Util -#import "PrismInstructionResponseChainUtil.h" +#import "PrismInstructionResponseChainInfoUtil.h" #import "PrismInstructionContentUtil.h" // Category #import "UITapGestureRecognizer+PrismIntercept.h" #import "UIResponder+PrismIntercept.h" #import "UIImage+PrismIntercept.h" #import "UIView+PrismExtends.h" +#import "NSArray+PrismExtends.h" +#import "UIGestureRecognizer+PrismExtends.h" @interface PrismTapGestureInstructionGenerator() @@ -29,30 +31,54 @@ + (NSString*)getInstructionOfTapGesture:(UITapGestureRecognizer*)tapGesture { if (!view) { return nil; } - if (view.autoDotFinalMark.length) { - return view.autoDotFinalMark; + if (view.prismAutoDotFinalMark.length) { + return view.prismAutoDotFinalMark; } - NSString *responseChainInfo = [tapGesture autoDotResponseChainInfo]; - NSString *areaInfo = [tapGesture autoDotAreaInfo]; + NSString *responseChainInfo = [tapGesture prismAutoDotResponseChainInfo]; + NSArray *areaInfo = [tapGesture prismAutoDotAreaInfo]; + NSString *listInfo = [areaInfo prism_stringWithIndex:0]; + NSString *quadrantInfo = [areaInfo prism_stringWithIndex:1]; // 屏蔽Native侧的H5页面点击指令 - if (([areaInfo containsString:@"WKScrollView"] || [areaInfo containsString:@"WKContentView"])) { + if (([listInfo containsString:@"WKScrollView"] || [listInfo containsString:@"WKContentView"])) { return nil; } + NSString *viewContent = [PrismInstructionContentUtil getRepresentativeContentOfView:view needRecursive:YES]; NSString *functionName = [self getFunctionNameOfTapGesture:tapGesture]; - NSString *instruction = [NSString stringWithFormat:@"%@%@%@%@%@", kBeginOfViewMotionFlag, kViewMotionGestureFlag, responseChainInfo, areaInfo, functionName]; + NSString *instruction = [NSString stringWithFormat:@"%@%@%@%@%@%@%@%@%@%@%@%@", kBeginOfViewMotionFlag, kViewMotionTapGestureFlag, kBeginOfViewPathFlag , responseChainInfo ?: @"", kBeginOfViewListFlag, listInfo ?: @"", kBeginOfViewQuadrantFlag, quadrantInfo ?: @"", kBeginOfViewRepresentativeContentFlag, viewContent ?: @"", kBeginOfViewFunctionFlag, functionName ?: @""]; // 注:列表中的cell存在复用机制,cell复用时指令不可复用。 - if ([areaInfo containsString:kBeginOfViewListFlag]) { + if (listInfo.length) { return instruction; } else { - view.autoDotFinalMark = instruction; - return view.autoDotFinalMark; + view.prismAutoDotFinalMark = instruction; + return view.prismAutoDotFinalMark; + } +} + ++ (PrismInstructionModel *)getInstructionModelOfTapGesture:(UITapGestureRecognizer *)tapGesture { + UIView *view = tapGesture.view; + if (!view) { + return nil; + } + PrismInstructionModel *model = [[PrismInstructionModel alloc] init]; + model.vm = kViewMotionTapGestureFlag; + model.vp = [tapGesture prismAutoDotResponseChainInfo]; + NSArray *areaInfo = [tapGesture prismAutoDotAreaInfo]; + model.vl = [areaInfo prism_stringWithIndex:0]; + model.vq = [areaInfo prism_stringWithIndex:1]; + // 屏蔽Native侧的H5页面点击指令 + if (([model.vl containsString:@"WKScrollView"] || [model.vl containsString:@"WKContentView"])) { + return nil; } + model.vr = [PrismInstructionContentUtil getRepresentativeContentOfView:view needRecursive:YES]; + model.vf = [self getFunctionNameOfTapGesture:tapGesture]; + return model; } + (NSString*)getFunctionNameOfTapGesture:(UITapGestureRecognizer*)tapGesture { UIView *view = tapGesture.view; + // WEEX id wxComponent = [view prism_wxComponent]; if (wxComponent && [wxComponent isKindOfClass:NSClassFromString(@"WXComponent")]) { @@ -60,46 +86,24 @@ + (NSString*)getFunctionNameOfTapGesture:(UITapGestureRecognizer*)tapGesture { NSDictionary *wxAttributes = [wxComponent valueForKey:@"attributes"]; NSString *wxFunctionName = [wxAttributes valueForKey:@"prismFunctionName"]; NSString *wxClassName = [wxAttributes valueForKey:@"prismClassName"]; - NSString *representativeContent = [PrismInstructionContentUtil getRepresentativeContentOfView:view needRecursive:YES]; - if (representativeContent.length) { - [functionName appendFormat:@"%@", representativeContent]; - } if (([wxFunctionName isKindOfClass:[NSString class]] && wxFunctionName.length) || ([wxClassName isKindOfClass:[NSString class]] && wxClassName.length)) { - - if (functionName.length) { - [functionName insertString:kBeginOfViewRepresentativeContentFlag atIndex:0]; - } - NSMutableString *mutableFunctionName = [NSMutableString stringWithString:kBeginOfViewFunctionFlag]; if ([wxFunctionName isKindOfClass:[NSString class]] && wxFunctionName.length) { - [mutableFunctionName appendString:wxFunctionName]; + [functionName appendString:wxFunctionName]; } if ([wxClassName isKindOfClass:[NSString class]] && wxClassName.length) { - if (mutableFunctionName.length) { - [mutableFunctionName appendString:@"-"]; + if (functionName.length) { + [functionName appendString:@"-"]; } - [mutableFunctionName appendString:wxClassName]; - } - if (![mutableFunctionName isEqualToString:kBeginOfViewFunctionFlag]) { - [functionName appendString:[mutableFunctionName copy]]; + [functionName appendString:wxClassName]; } } - else { - if (functionName.length) { - [functionName insertString:kBeginOfViewFunctionFlag atIndex:0]; - } - } - if (functionName.length) { - return [NSString stringWithFormat:@"%@_&_%@", functionName, tapGesture.autoDotTargetAndSelector]; + return [NSString stringWithFormat:@"%@_&_%@", functionName, tapGesture.prismAutoDotTargetAndSelector]; } } - // 获取有代表性的内容便于更好的定位view - NSString *viewContent = [PrismInstructionContentUtil getRepresentativeContentOfView:view needRecursive:YES]; - if (viewContent.length) { - return [NSString stringWithFormat:@"%@%@%@%@", kBeginOfViewRepresentativeContentFlag, viewContent, kBeginOfViewFunctionFlag, tapGesture.autoDotTargetAndSelector]; - } - return [NSString stringWithFormat:@"%@%@", kBeginOfViewFunctionFlag, tapGesture.autoDotTargetAndSelector]; + + return tapGesture.prismAutoDotTargetAndSelector; } #pragma mark - private method diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismViewControllerInstructionGenerator.h b/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismViewControllerInstructionGenerator.h index ffadfb2..2ee16fb 100644 --- a/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismViewControllerInstructionGenerator.h +++ b/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismViewControllerInstructionGenerator.h @@ -6,12 +6,14 @@ // #import +#import "PrismInstructionModel.h" NS_ASSUME_NONNULL_BEGIN @interface PrismViewControllerInstructionGenerator : NSObject + (NSString*)getInstructionOfViewController:(UIViewController*)viewController; ++ (PrismInstructionModel*)getInstructionModelOfViewController:(UIViewController*)viewController; @end NS_ASSUME_NONNULL_END diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismViewControllerInstructionGenerator.m b/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismViewControllerInstructionGenerator.m index a61cb14..3dc5edf 100644 --- a/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismViewControllerInstructionGenerator.m +++ b/iOS/DiDiPrism/Src/Core/Instruction/Generator/PrismViewControllerInstructionGenerator.m @@ -20,23 +20,18 @@ + (NSString*)getInstructionOfViewController:(UIViewController*)viewController { if ([self isSystemKeyboardOfViewController:viewController]) { return nil; } - - NSMutableString *vrContent = [NSMutableString stringWithString:NSStringFromClass([viewController class])]; - NSString *url = nil; - SEL urlSelector = @selector(getUrl); - if ([viewController respondsToSelector:urlSelector]) { - url = [viewController performSelector:urlSelector];; - if ([url isKindOfClass:[NSString class]] && url.length) { - [vrContent appendFormat:@"%@%@", kConnectorFlag, url]; - } - } - if (!url && [viewController title].length) { - [vrContent appendFormat:@"%@%@", kConnectorFlag, [viewController title]]; - } - else { - [vrContent appendFormat:@"%@%@", kConnectorFlag, NSStringFromClass([viewController class])]; + NSString *viewContent = [self getViewContentOfViewController:viewController]; + return [NSString stringWithFormat:@"%@%@%@", kUIViewControllerDidAppear, kBeginOfViewRepresentativeContentFlag, viewContent ?: @""]; +} + ++ (PrismInstructionModel *)getInstructionModelOfViewController:(UIViewController *)viewController { + if ([self isSystemKeyboardOfViewController:viewController]) { + return nil; } - return [NSString stringWithFormat:@"%@%@%@", kUIViewControllerDidAppear, kBeginOfViewRepresentativeContentFlag, [vrContent copy]]; + PrismInstructionModel *model = [[PrismInstructionModel alloc] init]; + model.e = kUIViewControllerDidAppear; + model.vr = [self getViewContentOfViewController:viewController]; + return model; } #pragma mark - private method @@ -48,6 +43,32 @@ + (BOOL)isSystemKeyboardOfViewController:(UIViewController*)viewController { [viewController isKindOfClass:NSClassFromString(@"UISystemKeyboardDockController")]; } ++ (NSString*)getViewContentOfViewController:(UIViewController*)viewController { + NSMutableString *vrContent = [NSMutableString stringWithString:NSStringFromClass([viewController class])]; + NSString *url = nil; + if ([viewController respondsToSelector:@selector(getUrl)]) { + url = [viewController performSelector:@selector(getUrl)];; + if ([url isKindOfClass:[NSString class]] && url.length) { + [vrContent appendFormat:@"%@%@", kConnectorFlag, url]; + } + } + else if ([viewController respondsToSelector:@selector(url)]) { + url = [viewController performSelector:@selector(url)];; + if ([url isKindOfClass:[NSString class]] && url.length) { + [vrContent appendFormat:@"%@%@", kConnectorFlag, url]; + } + } + + if (!url.length && [viewController title].length) { + [vrContent appendFormat:@"%@%@", kConnectorFlag, [viewController title]]; + } + else if (!url.length && [[[viewController navigationController] navigationItem] title].length) { + [vrContent appendFormat:@"%@%@", kConnectorFlag, [[[viewController navigationController] navigationItem] title]]; + } + + return [vrContent copy]; +} + #pragma mark - setters #pragma mark - getters diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismBaseInstructionParser+Protected.m b/iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismBaseInstructionParser+Protected.m index 305853e..5331ed5 100644 --- a/iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismBaseInstructionParser+Protected.m +++ b/iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismBaseInstructionParser+Protected.m @@ -9,7 +9,7 @@ // Category #import "UIView+PrismExtends.h" // Util -#import "PrismInstructionAreaUtil.h" +#import "PrismInstructionAreaInfoUtil.h" @implementation PrismBaseInstructionParser (Protected) - (UIResponder*)searchRootResponderWithClassName:(NSString*)className { @@ -166,7 +166,7 @@ - (UIView*)searchScrollViewCellWithScrollViewClassName:(NSString*)scrollViewClas for (UIView *subview in scrollView.subviews) { if ([subview isKindOfClass:NSClassFromString(cellClassName)]) { - NSInteger subviewIndex = [PrismInstructionAreaUtil getIndexOf:subview fromScrollView:scrollView]; + NSInteger subviewIndex = [PrismInstructionAreaInfoUtil getIndexOf:subview fromScrollView:scrollView]; if (subviewIndex == cellSectionOrOriginX) { CGPoint offset = CGPointMake(subview.frame.origin.x > 50 ? subview.frame.origin.x - 50 : subview.frame.origin.x, subview.frame.origin.y > 100 ? subview.frame.origin.y - 100 : subview.frame.origin.y); [scrollView setContentOffset:offset animated:NO]; diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismBaseInstructionParser.m b/iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismBaseInstructionParser.m index a193e1b..b2c404f 100644 --- a/iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismBaseInstructionParser.m +++ b/iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismBaseInstructionParser.m @@ -11,11 +11,12 @@ #import "NSArray+PrismExtends.h" // Parser #import "PrismControlInstructionParser.h" -#import "PrismGestureInstructionParser.h" +#import "PrismTapGestureInstructionParser.h" #import "PrismCellInstructionParser.h" #import "PrismTagInstructionParser.h" #import "PrismEdgePanGestureInstructionParser.h" #import "PrismH5InstructionParser.h" +#import "PrismLongPressGestureInstructionParser.h" @interface PrismBaseInstructionParser() @@ -36,8 +37,8 @@ + (instancetype)instructionParserWithFormatter:(PrismInstructionFormatter*)forma if ([viewMotion isEqualToString:kViewMotionControlFlag]) { return [[PrismControlInstructionParser alloc] init]; } - else if ([viewMotion isEqualToString:kViewMotionGestureFlag]) { - return [[PrismGestureInstructionParser alloc] init]; + else if ([viewMotion isEqualToString:kViewMotionTapGestureFlag]) { + return [[PrismTapGestureInstructionParser alloc] init]; } else if ([viewMotion isEqualToString:kViewMotionCellFlag]) { return [[PrismCellInstructionParser alloc] init]; @@ -45,6 +46,9 @@ + (instancetype)instructionParserWithFormatter:(PrismInstructionFormatter*)forma else if ([viewMotion isEqualToString:kViewMotionEdgePanGestureFlag]) { return [[PrismEdgePanGestureInstructionParser alloc] init]; } + else if ([viewMotion isEqualToString:kViewMotionLongPressGestureFlag]) { + return [[PrismLongPressGestureInstructionParser alloc] init]; + } return [[PrismBaseInstructionParser alloc] init]; } diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismControlInstructionParser.m b/iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismControlInstructionParser.m index 3745421..fb5dc35 100644 --- a/iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismControlInstructionParser.m +++ b/iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismControlInstructionParser.m @@ -13,8 +13,7 @@ #import "UIImage+PrismIntercept.h" #import "NSArray+PrismExtends.h" // Util -#import "PrismInstructionAreaUtil.h" -#import "PrismInstructionContentUtil.h" +#import "PrismInstructionAreaInfoUtil.h" @interface PrismControlInstructionParser() @@ -79,14 +78,20 @@ - (PrismInstructionParseResult)parseWithFormatter:(PrismInstructionFormatter *)f NSString *areaInfo = [viewQuadrantArray prism_stringWithIndex:1]; if (viewRepresentativeContentArray.count && viewFunctionArray.count) { - NSString *representativeContent = [viewRepresentativeContentArray prism_stringWithIndex:1]; + NSMutableString *representativeContent = [NSMutableString string]; + for (NSInteger index = 1; index < viewRepresentativeContentArray.count; index++) { + if (index > 1) { + [representativeContent appendString:kConnectorFlag]; + } + [representativeContent appendString:[viewRepresentativeContentArray prism_stringWithIndex:index]]; + } Class targetClass = NSClassFromString([viewFunctionArray prism_objectAtIndex:1]); NSString *targetAction = [viewFunctionArray prism_stringWithIndex:2]; - targetControl = [self searchControlWithArea:areaInfo withRepresentativeContent:representativeContent withFunctionName:nil withTargetClass:targetClass withAction:targetAction fromSuperView:targetView]; + targetControl = [self searchControlWithArea:areaInfo withRepresentativeContent:[representativeContent copy] withTargetClass:targetClass withAction:targetAction fromSuperView:targetView]; // 有列表的场景,考虑到列表中的元素index可能会变化,可以做一定的兼容。 if (!targetControl && lastScrollView) { for (UIView *subview in lastScrollView.subviews) { - UIControl *resultControl = [self searchControlWithArea:areaInfo withRepresentativeContent:representativeContent withFunctionName:nil withTargetClass:targetClass withAction:targetAction fromSuperView:subview]; + UIControl *resultControl = [self searchControlWithArea:areaInfo withRepresentativeContent:[representativeContent copy] withTargetClass:targetClass withAction:targetAction fromSuperView:subview]; if (resultControl) { targetControl = resultControl; break; @@ -97,27 +102,8 @@ - (PrismInstructionParseResult)parseWithFormatter:(PrismInstructionFormatter *)f targetControl = [self searchControlWithArea:areaInfo withTargetClass:targetClass withAction:targetAction fromSuperView:targetView]; } } - // title or image + target + selector - else if (viewFunctionArray.count == 4) { - NSString *functionName = [viewFunctionArray prism_objectAtIndex:1]; - Class targetClass = NSClassFromString([viewFunctionArray prism_objectAtIndex:2]); - NSString *targetAction = [viewFunctionArray prism_stringWithIndex:3]; - targetControl = [self searchControlWithArea:areaInfo withRepresentativeContent:nil withFunctionName:functionName withTargetClass:targetClass withAction:targetAction fromSuperView:targetView]; - // 有列表的场景,考虑到列表中的元素index可能会变化,可以做一定的兼容。 - if (!targetControl && lastScrollView) { - for (UIView *subview in lastScrollView.subviews) { - targetControl = [self searchControlWithArea:areaInfo withRepresentativeContent:nil withFunctionName:functionName withTargetClass:targetClass withAction:targetAction fromSuperView:subview]; - if (targetControl) { - break; - } - } - } - if (!targetControl) { - targetControl = [self searchControlWithArea:areaInfo withTargetClass:targetClass withAction:targetAction fromSuperView:targetView]; - } - } // target + selector - else if (viewFunctionArray.count == 3) { + else if (viewFunctionArray.count) { Class targetClass = NSClassFromString([viewFunctionArray prism_objectAtIndex:1]); NSString *targetAction = [viewFunctionArray prism_stringWithIndex:2]; targetControl = [self searchControlWithArea:areaInfo withTargetClass:targetClass withAction:targetAction fromSuperView:targetView]; @@ -132,13 +118,19 @@ - (PrismInstructionParseResult)parseWithFormatter:(PrismInstructionFormatter *)f } } // title or image - else if (viewFunctionArray.count == 2) { - NSString *functionName = [viewFunctionArray prism_objectAtIndex:1]; - targetControl = [self searchControlWithArea:areaInfo withFunctionName:functionName fromSuperView:targetView]; + else if (viewRepresentativeContentArray.count) { + NSMutableString *viewContent = [NSMutableString string]; + for (NSInteger index = 1; index < viewRepresentativeContentArray.count; index++) { + if (index > 1) { + [viewContent appendString:kConnectorFlag]; + } + [viewContent appendString:[viewRepresentativeContentArray prism_stringWithIndex:index]]; + } + targetControl = [self searchControlWithArea:areaInfo withRepresentativeContent:[viewContent copy] fromSuperView:targetView]; // 有列表的场景,考虑到列表中的元素index可能会变化,可以做一定的兼容。 if (!targetControl && lastScrollView) { for (UIView *subview in lastScrollView.subviews) { - targetControl = [self searchControlWithArea:areaInfo withFunctionName:functionName fromSuperView:subview]; + targetControl = [self searchControlWithArea:areaInfo withRepresentativeContent:[viewContent copy] fromSuperView:subview]; if (targetControl) { break; } @@ -158,32 +150,27 @@ - (PrismInstructionParseResult)parseWithFormatter:(PrismInstructionFormatter *)f } #pragma mark - private method -- (UIControl*)searchControlWithArea:(NSString*)areaInfo withFunctionName:(NSString*)functionName fromSuperView:(UIView*)superView { - return [self searchControlWithArea:areaInfo withRepresentativeContent:nil withFunctionName:functionName withTargetClass:nil withAction:nil fromSuperView:superView]; +- (UIControl*)searchControlWithArea:(NSString*)areaInfo withRepresentativeContent:(NSString*)representativeContent fromSuperView:(UIView*)superView { + return [self searchControlWithArea:areaInfo withRepresentativeContent:representativeContent withTargetClass:nil withAction:nil fromSuperView:superView]; } - (UIControl*)searchControlWithArea:(NSString*)areaInfo withTargetClass:(Class)targetClass withAction:(NSString*)action fromSuperView:(UIView*)superView { - return [self searchControlWithArea:areaInfo withRepresentativeContent:nil withFunctionName:nil withTargetClass:targetClass withAction:action fromSuperView:superView]; + return [self searchControlWithArea:areaInfo withRepresentativeContent:nil withTargetClass:targetClass withAction:action fromSuperView:superView]; } - (UIControl*)searchControlWithArea:(NSString*)areaInfo withRepresentativeContent:(NSString*)representativeContent - withFunctionName:(NSString*)functionName withTargetClass:(Class)targetClass withAction:(NSString*)action fromSuperView:(UIView*)superView { if ([superView isKindOfClass:[UIControl class]]) { UIControl *control = (UIControl*)superView; - NSString *areaAndListInfo = [PrismInstructionAreaUtil getAreaInfoWithElement:control]; - NSString *functionInfo = [PrismControlInstructionGenerator getFunctionNameOfControl:control]; - PrismInstructionFormatter *controlFormatter = [[PrismInstructionFormatter alloc] initWithInstruction:[NSString stringWithFormat:@"%@%@", areaAndListInfo, functionInfo]]; - NSString *controlAreaInfo = [[controlFormatter instructionFragmentWithType:PrismInstructionFragmentTypeViewQuadrant] prism_stringWithIndex:1]; - NSString *viewContent = [PrismInstructionContentUtil getRepresentativeContentOfView:control needRecursive:YES]; - NSString *controlFunctionName = [[controlFormatter instructionFragmentWithType:PrismInstructionFragmentTypeViewFunction] prism_stringWithIndex:1]; + NSString *controlAreaInfo = [[PrismInstructionAreaInfoUtil getAreaInfoWithElement:control] prism_stringWithIndex:1]; + NSString *controlViewContent = [PrismControlInstructionGenerator getViewContentOfControl:control]; control.highlighted = YES; NSString *highlightedImageName = nil; if ([control isKindOfClass:[UIButton class]]) { - highlightedImageName = ((UIButton*)control).imageView.image.autoDotImageName; + highlightedImageName = ((UIButton*)control).imageView.image.prismAutoDotImageName; } control.highlighted = NO; for (NSObject *target in control.allTargets) { @@ -197,8 +184,7 @@ - (UIControl*)searchControlWithArea:(NSString*)areaInfo } BOOL isAreaInfoEqual = [self isAreaInfoEqualBetween:controlAreaInfo withAnother:areaInfo allowCompatibleMode:NO]; if (isAreaInfoEqual - && (!representativeContent.length || [representativeContent isEqualToString:viewContent]) - && (!functionName.length || ([controlFunctionName isEqualToString:functionName] || [highlightedImageName isEqualToString:functionName])) + && (!representativeContent.length || ([representativeContent isEqualToString:controlViewContent] || [representativeContent isEqualToString:highlightedImageName])) && (!targetClass || [target isKindOfClass:targetClass]) && (!action.length || [controlActions containsObject:action])) { return control; @@ -210,7 +196,7 @@ - (UIControl*)searchControlWithArea:(NSString*)areaInfo return nil; } for (UIView *view in superView.subviews) { - UIControl *control = [self searchControlWithArea:areaInfo withRepresentativeContent:representativeContent withFunctionName:functionName withTargetClass:targetClass withAction:action fromSuperView:view]; + UIControl *control = [self searchControlWithArea:areaInfo withRepresentativeContent:representativeContent withTargetClass:targetClass withAction:action fromSuperView:view]; if (control) { return control; } diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismLongPressGestureInstructionParser.h b/iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismLongPressGestureInstructionParser.h new file mode 100644 index 0000000..6351562 --- /dev/null +++ b/iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismLongPressGestureInstructionParser.h @@ -0,0 +1,16 @@ +// +// PrismLongPressGestureInstructionParser.h +// DiDiPrism +// +// Created by hulk on 2021/6/25. +// + +#import "PrismBaseInstructionParser.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PrismLongPressGestureInstructionParser : PrismBaseInstructionParser + +@end + +NS_ASSUME_NONNULL_END diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismLongPressGestureInstructionParser.m b/iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismLongPressGestureInstructionParser.m new file mode 100644 index 0000000..eb89b01 --- /dev/null +++ b/iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismLongPressGestureInstructionParser.m @@ -0,0 +1,158 @@ +// +// PrismLongPressGestureInstructionParser.m +// DiDiPrism +// +// Created by hulk on 2021/6/25. +// + +#import "PrismLongPressGestureInstructionParser.h" +#import "PrismBaseInstructionParser+Protected.h" +#import "PrismLongPressGestureInstructionGenerator.h" +// Category +#import "NSArray+PrismExtends.h" +// Util +#import "PrismInstructionAreaInfoUtil.h" +#import "PrismInstructionContentUtil.h" + +@implementation PrismLongPressGestureInstructionParser +#pragma mark - life cycle + +#pragma mark - public method +- (PrismInstructionParseResult)parseWithFormatter:(PrismInstructionFormatter *)formatter { + // 解析响应链信息 + NSArray *viewPathArray = [formatter instructionFragmentWithType:PrismInstructionFragmentTypeViewPath]; + UIResponder *responder = [self searchRootResponderWithClassName:[viewPathArray prism_stringWithIndex:1]]; + if (!responder) { + return PrismInstructionParseResultFail; + } + + for (NSInteger index = 2; index < viewPathArray.count; index++) { + Class class = NSClassFromString(viewPathArray[index]); + if (!class) { + break; + } + UIResponder *result = [self searchResponderWithClassName:viewPathArray[index] superResponder:responder]; + if (!result) { + break; + } + responder = result; + } + + // 解析列表信息 + NSArray *viewListArray = [formatter instructionFragmentWithType:PrismInstructionFragmentTypeViewList]; + UIView *lastScrollView = nil; + UIView *targetView = [responder isKindOfClass:[UIViewController class]] ? [(UIViewController*)responder view] : (UIView*)responder; + for (NSInteger index = 1; index < viewListArray.count; index = index + 4) { + if ([NSClassFromString(viewListArray[index]) isSubclassOfClass:[UIScrollView class]]) { + // 兼容模式直接省略对列表信息的解析。 + if (!self.isCompatibleMode) { + NSString *scrollViewClassName = viewListArray[index]; + NSString *cellClassName = viewListArray[index + 1]; + CGFloat cellSectionOrOriginX = viewListArray[index + 2].floatValue; + CGFloat cellRowOrOriginY = viewListArray[index + 3].floatValue; + UIView *scrollViewCell = [self searchScrollViewCellWithScrollViewClassName:scrollViewClassName + cellClassName:cellClassName + cellSectionOrOriginX:cellSectionOrOriginX + cellRowOrOriginY:cellRowOrOriginY + fromSuperView:targetView]; + if (!scrollViewCell) { + return PrismInstructionParseResultFail; + } + targetView = scrollViewCell; + lastScrollView = scrollViewCell.superview; + } + } + } + + // 解析区位信息+功能信息 + NSArray *viewQuadrantArray = [formatter instructionFragmentWithType:PrismInstructionFragmentTypeViewQuadrant]; + NSArray *viewRepresentativeContentArray = [formatter instructionFragmentWithType:PrismInstructionFragmentTypeViewRepresentativeContent]; + NSArray *viewFunctionArray = [formatter instructionFragmentWithType:PrismInstructionFragmentTypeViewFunction]; + + NSString *areaInfo = [viewQuadrantArray prism_stringWithIndex:1]; + UILongPressGestureRecognizer *longPressGesture = nil; + // vr = 参考信息 + // 且 + // ( + // vf = WEEX标签信息 + selector + action + // 或 + // vf = selector + action + // ) + NSMutableString *representativeContent = [NSMutableString string]; + for (NSInteger index = 1; index < viewRepresentativeContentArray.count; index++) { + if (index > 1) { + [representativeContent appendString:kConnectorFlag]; + } + [representativeContent appendString:[viewRepresentativeContentArray prism_stringWithIndex:index]]; + } + + NSMutableString *functionName = [NSMutableString string]; + for (NSInteger index = 1; index < viewFunctionArray.count; index++) { + if (index > 1) { + [functionName appendString:kConnectorFlag]; + } + [functionName appendString:[viewFunctionArray prism_stringWithIndex:index]]; + } + + longPressGesture = [self searchLongPressGestureFromSuperView:targetView withAreaInfo:areaInfo withRepresentativeContent:[representativeContent copy] withFunctionName:functionName]; + if (!longPressGesture) { + // 有列表的场景,考虑到列表中的元素index可能会变化,可以做一定的兼容。 + if (representativeContent.length && lastScrollView) { + for (UIView *subview in lastScrollView.subviews) { + UITapGestureRecognizer *resultGesture = [self searchLongPressGestureFromSuperView:subview withAreaInfo:areaInfo withRepresentativeContent:[representativeContent copy] withFunctionName:functionName]; + if (resultGesture) { + longPressGesture = resultGesture; + break; + } + } + } + } + if (!longPressGesture) { + // 兜底处理 + longPressGesture = [self searchLongPressGestureFromSuperView:targetView withAreaInfo:areaInfo withRepresentativeContent:nil withFunctionName:functionName]; + } + + if (longPressGesture) { + [self scrollToIdealOffsetWithScrollView:(UIScrollView*)lastScrollView targetElement:longPressGesture.view]; + [self highlightTheElement:longPressGesture.view withCompletion:^{ + [longPressGesture setState:UIGestureRecognizerStateRecognized]; + }]; + return PrismInstructionParseResultSuccess; + } + return PrismInstructionParseResultFail; +} + +#pragma mark - private method +- (UILongPressGestureRecognizer*)searchLongPressGestureFromSuperView:(UIView*)superView + withAreaInfo:(NSString*)areaInfo + withRepresentativeContent:(NSString*)representativeContent + withFunctionName:(NSString*)functionName { + for (UIGestureRecognizer *gesture in superView.gestureRecognizers) { + if ([gesture isKindOfClass:[UILongPressGestureRecognizer class]]) { + UILongPressGestureRecognizer *longPressGesture = (UILongPressGestureRecognizer*)gesture; + NSString *gestureViewAreaInfo = [[PrismInstructionAreaInfoUtil getAreaInfoWithElement:longPressGesture.view] prism_stringWithIndex:1]; + NSString *gestureFunctionName = [PrismLongPressGestureInstructionGenerator getFunctionNameOfLongPressGesture:longPressGesture]; + NSString *gestureRepresentativeContent = [PrismInstructionContentUtil getRepresentativeContentOfView:superView needRecursive:YES]; + + BOOL isAreaInfoEqual = [self isAreaInfoEqualBetween:gestureViewAreaInfo withAnother:areaInfo allowCompatibleMode:self.isCompatibleMode]; + if (isAreaInfoEqual + && (!functionName.length || [functionName isEqualToString:gestureFunctionName]) + && (!representativeContent.length || [representativeContent isEqualToString:gestureRepresentativeContent])) { + return (UILongPressGestureRecognizer*)gesture; + } + } + } + for (UIView *subview in superView.subviews) { + UILongPressGestureRecognizer *longPressGesture = [self searchLongPressGestureFromSuperView:subview withAreaInfo:areaInfo withRepresentativeContent:representativeContent withFunctionName:functionName]; + if (longPressGesture) { + return longPressGesture; + } + } + return nil; +} + +#pragma mark - setters + +#pragma mark - getters + +@end diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismGestureInstructionParser.h b/iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismTapGestureInstructionParser.h similarity index 72% rename from iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismGestureInstructionParser.h rename to iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismTapGestureInstructionParser.h index 10d703f..d13f4f2 100644 --- a/iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismGestureInstructionParser.h +++ b/iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismTapGestureInstructionParser.h @@ -9,7 +9,7 @@ NS_ASSUME_NONNULL_BEGIN -@interface PrismGestureInstructionParser : PrismBaseInstructionParser +@interface PrismTapGestureInstructionParser : PrismBaseInstructionParser @end diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismGestureInstructionParser.m b/iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismTapGestureInstructionParser.m similarity index 73% rename from iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismGestureInstructionParser.m rename to iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismTapGestureInstructionParser.m index ef992c0..7a7e567 100644 --- a/iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismGestureInstructionParser.m +++ b/iOS/DiDiPrism/Src/Core/Instruction/Parser/PrismTapGestureInstructionParser.m @@ -5,20 +5,21 @@ // Created by hulk on 2019/7/25. // -#import "PrismGestureInstructionParser.h" +#import "PrismTapGestureInstructionParser.h" #import "PrismBaseInstructionParser+Protected.h" #import "PrismTapGestureInstructionGenerator.h" // Category #import "UITapGestureRecognizer+PrismIntercept.h" #import "NSArray+PrismExtends.h" // Util -#import "PrismInstructionAreaUtil.h" +#import "PrismInstructionAreaInfoUtil.h" +#import "PrismInstructionContentUtil.h" -@interface PrismGestureInstructionParser() +@interface PrismTapGestureInstructionParser() @end -@implementation PrismGestureInstructionParser +@implementation PrismTapGestureInstructionParser #pragma mark - life cycle #pragma mark - public method @@ -75,24 +76,35 @@ - (PrismInstructionParseResult)parseWithFormatter:(PrismInstructionFormatter *)f NSString *areaInfo = [viewQuadrantArray prism_stringWithIndex:1]; UITapGestureRecognizer *tapGesture = nil; - // vr = WEEX参考信息 + // vr = 参考信息 // 且 // ( // vf = WEEX标签信息 + selector + action // 或 - // vf = title or image + selector + action - // 或 - // vf = title or image + 空字符串 + selector + action (在WEEX的@click和class未被正则匹配到时,即functionName和className不存在时,会出现) - // 或 // vf = selector + action // ) - NSString *representativeContent = [viewRepresentativeContentArray prism_stringWithIndex:1]; - tapGesture = [self searchTapGestureFromSuperView:targetView withAreaInfo:areaInfo withRepresentativeContent:representativeContent withFunctionArray:viewFunctionArray]; + NSMutableString *representativeContent = [NSMutableString string]; + for (NSInteger index = 1; index < viewRepresentativeContentArray.count; index++) { + if (index > 1) { + [representativeContent appendString:kConnectorFlag]; + } + [representativeContent appendString:[viewRepresentativeContentArray prism_stringWithIndex:index]]; + } + + NSMutableString *functionName = [NSMutableString string]; + for (NSInteger index = 1; index < viewFunctionArray.count; index++) { + if (index > 1) { + [functionName appendString:kConnectorFlag]; + } + [functionName appendString:[viewFunctionArray prism_stringWithIndex:index]]; + } + + tapGesture = [self searchTapGestureFromSuperView:targetView withAreaInfo:areaInfo withRepresentativeContent:[representativeContent copy] withFunctionName:functionName]; if (!tapGesture) { // 有列表的场景,考虑到列表中的元素index可能会变化,可以做一定的兼容。 if (representativeContent.length && lastScrollView) { for (UIView *subview in lastScrollView.subviews) { - UITapGestureRecognizer *resultGesture = [self searchTapGestureFromSuperView:subview withAreaInfo:areaInfo withRepresentativeContent:representativeContent withFunctionArray:viewFunctionArray]; + UITapGestureRecognizer *resultGesture = [self searchTapGestureFromSuperView:subview withAreaInfo:areaInfo withRepresentativeContent:[representativeContent copy] withFunctionName:functionName]; if (resultGesture) { tapGesture = resultGesture; break; @@ -102,7 +114,7 @@ - (PrismInstructionParseResult)parseWithFormatter:(PrismInstructionFormatter *)f } if (!tapGesture) { // 兜底处理 - tapGesture = [self searchTapGestureFromSuperView:targetView withAreaInfo:areaInfo withRepresentativeContent:nil withFunctionArray:viewFunctionArray]; + tapGesture = [self searchTapGestureFromSuperView:targetView withAreaInfo:areaInfo withRepresentativeContent:nil withFunctionName:functionName]; } if (tapGesture) { @@ -119,28 +131,24 @@ - (PrismInstructionParseResult)parseWithFormatter:(PrismInstructionFormatter *)f - (UITapGestureRecognizer*)searchTapGestureFromSuperView:(UIView*)superView withAreaInfo:(NSString*)areaInfo withRepresentativeContent:(NSString*)representativeContent - withFunctionArray:(NSArray*)functionArray { + withFunctionName:(NSString*)functionName { for (UIGestureRecognizer *gesture in superView.gestureRecognizers) { if ([gesture isKindOfClass:[UITapGestureRecognizer class]]) { UITapGestureRecognizer *tapGesture = (UITapGestureRecognizer*)gesture; - NSString *areaAndListInfo = [PrismInstructionAreaUtil getAreaInfoWithElement:tapGesture.view]; - NSString *functionInfo = [PrismTapGestureInstructionGenerator getFunctionNameOfTapGesture:tapGesture]; - PrismInstructionFormatter *gestureFormatter = [[PrismInstructionFormatter alloc] initWithInstruction:[NSString stringWithFormat:@"%@%@", areaAndListInfo, functionInfo]]; - NSString *gestureViewAreaInfo = [[gestureFormatter instructionFragmentWithType:PrismInstructionFragmentTypeViewQuadrant] prism_stringWithIndex:1]; - NSArray *representativeContentArray = [gestureFormatter instructionFragmentWithType:PrismInstructionFragmentTypeViewRepresentativeContent]; - NSArray *functionNameArray = [gestureFormatter instructionFragmentWithType:PrismInstructionFragmentTypeViewFunction]; - NSString *gestureRepresentativeContent = representativeContentArray.count ? [representativeContentArray prism_stringWithIndex:1] : [functionNameArray prism_stringWithIndex:1]; + NSString *gestureViewAreaInfo = [[PrismInstructionAreaInfoUtil getAreaInfoWithElement:tapGesture.view] prism_stringWithIndex:1]; + NSString *gestureFunctionName = [PrismTapGestureInstructionGenerator getFunctionNameOfTapGesture:tapGesture]; + NSString *gestureRepresentativeContent = [PrismInstructionContentUtil getRepresentativeContentOfView:superView needRecursive:YES]; - BOOL isAreaInfoEqual = [self isAreaInfoEqualBetween:gestureViewAreaInfo withAnother:areaInfo allowCompatibleMode:functionArray.count > 3]; + BOOL isAreaInfoEqual = [self isAreaInfoEqualBetween:gestureViewAreaInfo withAnother:areaInfo allowCompatibleMode:self.isCompatibleMode]; if (isAreaInfoEqual - && (!functionArray.count || [functionArray isEqual:functionNameArray]) + && (!functionName.length || [functionName isEqualToString:gestureFunctionName]) && (!representativeContent.length || [representativeContent isEqualToString:gestureRepresentativeContent])) { return (UITapGestureRecognizer*)gesture; } } } for (UIView *subview in superView.subviews) { - UITapGestureRecognizer *tapGesture = [self searchTapGestureFromSuperView:subview withAreaInfo:areaInfo withRepresentativeContent:representativeContent withFunctionArray:functionArray]; + UITapGestureRecognizer *tapGesture = [self searchTapGestureFromSuperView:subview withAreaInfo:areaInfo withRepresentativeContent:representativeContent withFunctionName:functionName]; if (tapGesture) { return tapGesture; } diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionAreaUtil.h b/iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionAreaInfoUtil.h similarity index 78% rename from iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionAreaUtil.h rename to iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionAreaInfoUtil.h index effb931..96012d4 100644 --- a/iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionAreaUtil.h +++ b/iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionAreaInfoUtil.h @@ -1,5 +1,5 @@ // -// PrismInstructionAreaUtil.h +// PrismInstructionAreaInfoUtil.h // DiDiPrism // // Created by hulk on 2019/7/1. @@ -23,9 +23,11 @@ typedef NS_ENUM(NSUInteger, PrismInstructionArea) { PrismInstructionAreaCanScroll = 100, }; -@interface PrismInstructionAreaUtil : NSObject - -+ (NSString*)getAreaInfoWithElement:(UIView *)element; +@interface PrismInstructionAreaInfoUtil : NSObject +/* + @return 数组,[0]为vl,[1]为vq + */ ++ (NSArray*)getAreaInfoWithElement:(UIView *)element; + (NSInteger)getIndexOf:(UIView*)element fromScrollView:(UIScrollView*)scrollView; @end diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionAreaUtil.m b/iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionAreaInfoUtil.m similarity index 85% rename from iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionAreaUtil.m rename to iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionAreaInfoUtil.m index bdd4dbb..ca9875c 100644 --- a/iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionAreaUtil.m +++ b/iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionAreaInfoUtil.m @@ -1,24 +1,26 @@ // -// AutoDotAreaUtil.m +// PrismInstructionAreaInfoUtil.m // DiDiPrism // // Created by hulk on 2019/7/1. // -#import "PrismInstructionAreaUtil.h" -#import "PrismInstructionDefines.h" +#import "PrismInstructionAreaInfoUtil.h" // Category #import "UIView+PrismExtends.h" -@implementation PrismInstructionAreaUtil +@implementation PrismInstructionAreaInfoUtil #pragma mark - public method -+ (NSString*)getAreaInfoWithElement:(UIView *)element { +/* + @return 数组,[0]为vl,[1]为vq + */ ++ (NSArray*)getAreaInfoWithElement:(UIView *)element { if (!element || ![element superview]) { - return [NSString stringWithFormat:@"%@%ld", kBeginOfViewQuadrantFlag, PrismInstructionAreaUnknown]; + return @[@"", [NSString stringWithFormat:@"%ld", PrismInstructionAreaUnknown]]; } BOOL isAllPagingEnabled = YES; // 列表信息 - NSMutableString *scrollInfo = [NSMutableString string]; + NSMutableString *listInfo = [NSMutableString string]; UIResponder *temporaryView = element; UIResponder *temporarySuperview = temporaryView.nextResponder; while (temporaryView && ![temporaryView isKindOfClass:[UIViewController class]]) { @@ -31,7 +33,7 @@ + (NSString*)getAreaInfoWithElement:(UIView *)element { indexPath = [tableView indexPathForRowAtPoint:tableViewCell.center]; } NSString *thisScrollInfo = [NSString stringWithFormat:@"%@_&_%@_&_%ld_&_%ld_&_", NSStringFromClass([tableView class]), NSStringFromClass([tableViewCell class]), (long)indexPath.section, (long)indexPath.row]; - [scrollInfo insertString:thisScrollInfo atIndex:0]; + [listInfo insertString:thisScrollInfo atIndex:0]; } else if ([temporaryView isKindOfClass:[UICollectionViewCell class]]) { UICollectionViewCell *collectionViewCell = (UICollectionViewCell*)temporaryView; @@ -42,23 +44,19 @@ + (NSString*)getAreaInfoWithElement:(UIView *)element { indexPath = [collectionView indexPathForItemAtPoint:collectionViewCell.center]; } NSString *thisScrollInfo = [NSString stringWithFormat:@"%@_&_%@_&_%ld_&_%ld_&_", NSStringFromClass([collectionView class]), NSStringFromClass([collectionViewCell class]), (long)indexPath.section, (long)indexPath.row]; - [scrollInfo insertString:thisScrollInfo atIndex:0]; + [listInfo insertString:thisScrollInfo atIndex:0]; } else if ([temporarySuperview isKindOfClass:[UIScrollView class]]) { UIScrollView *scrollView = (UIScrollView*)temporarySuperview; isAllPagingEnabled = !scrollView.isPagingEnabled ? NO : isAllPagingEnabled; NSInteger index = [self getIndexOf:(UIView*)temporaryView fromScrollView:scrollView]; NSString *thisScrollInfo = [NSString stringWithFormat:@"%@_&_%@_&_%ld_&_%ld_&_", NSStringFromClass([scrollView class]), NSStringFromClass([temporaryView class]), index, index]; - [scrollInfo insertString:thisScrollInfo atIndex:0]; + [listInfo insertString:thisScrollInfo atIndex:0]; } temporaryView = temporaryView.nextResponder; temporarySuperview = temporaryView.nextResponder; } - if (scrollInfo.length) { - [scrollInfo insertString:kBeginOfViewListFlag atIndex:0]; - } - NSString *areaInfo = [scrollInfo copy]; // 区位信息 UIWindow *mainWindow = [UIApplication sharedApplication].delegate.window; @@ -85,11 +83,9 @@ + (NSString*)getAreaInfoWithElement:(UIView *)element { else { verticalValue = PrismInstructionAreaBottom; } - NSInteger coordAtScreen = isAllPagingEnabled == NO ? PrismInstructionAreaCanScroll : horizontalValue * verticalValue; - areaInfo = [NSString stringWithFormat:@"%@%@%ld", areaInfo, kBeginOfViewQuadrantFlag, coordAtScreen]; - return areaInfo; + return @[listInfo.length ? [listInfo copy] : @"", [NSString stringWithFormat:@"%ld", coordAtScreen]]; } + (NSInteger)getIndexOf:(UIView*)element fromScrollView:(UIScrollView*)scrollView { diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionContentUtil.m b/iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionContentUtil.m index 522cbe5..a4c286b 100644 --- a/iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionContentUtil.m +++ b/iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionContentUtil.m @@ -6,6 +6,7 @@ // #import "PrismInstructionContentUtil.h" +#import "PrismInstructionDefines.h" // Category #import "UIImage+PrismIntercept.h" #import "NSArray+PrismExtends.h" @@ -27,26 +28,26 @@ + (NSString*)getRepresentativeContentOfView:(UIView*)view needRecursive:(BOOL)ne if ([view isKindOfClass:[UILabel class]]) { UILabel *label = (UILabel*)view; if (label.text.length) { - return label.text; + return [NSString stringWithFormat:@"%@%@", kViewRepresentativeContentTypeText, label.text]; } } id wxComponent = [view prism_wxComponent]; if ([wxComponent isKindOfClass:NSClassFromString(@"WXTextComponent")]) { NSString *text = [wxComponent valueForKey:@"text"]; if ([text isKindOfClass:[NSString class]] && text.length) { - return text; + return [NSString stringWithFormat:@"%@%@", kViewRepresentativeContentTypeText, text]; } } if ([view isKindOfClass:[UIImageView class]]) { UIImageView *imageView = (UIImageView*)view; - if (imageView.image.autoDotImageName.length) { - return imageView.image.autoDotImageName; + if (imageView.image.prismAutoDotImageName.length) { + return [NSString stringWithFormat:@"%@%@", kViewRepresentativeContentTypeLocalImage, imageView.image.prismAutoDotImageName]; } } if ([wxComponent isKindOfClass:NSClassFromString(@"WXImageComponent")]) { NSString *src = [[wxComponent valueForKey:@"attributes"] valueForKey:@"src"]; if ([src isKindOfClass:[NSString class]] && src.length) { - return src; + return [NSString stringWithFormat:@"%@%@", kViewRepresentativeContentTypeNetworkImage, src]; } } if (!needRecursive) { @@ -65,12 +66,15 @@ + (NSString*)getRepresentativeContentOfView:(UIView*)view needRecursive:(BOOL)ne if (firstTextView) { NSString *firstText = [self getRepresentativeContentOfView:firstTextView needRecursive:NO]; if (firstText.length) { - [functionName appendFormat:@"%@_&_", firstText]; + [functionName appendString:firstText]; } } if (bigTextView && bigTextView != firstTextView) { NSString *bigText = [self getRepresentativeContentOfView:bigTextView needRecursive:NO]; if (bigText.length) { + if (functionName.length) { + [functionName appendString:@"_&_"]; + } [functionName appendString:bigText]; } } diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionParamUtil.m b/iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionParamUtil.m index 9f17d00..0d4e65a 100644 --- a/iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionParamUtil.m +++ b/iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionParamUtil.m @@ -38,7 +38,7 @@ + (NSDictionary*)getEventParamsWithElement:(UIView*)element { } // Native else { - itemName = view.autoDotItemName; + itemName = view.prismAutoDotItemName; } if (itemName.length) { diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionResponseChainUtil.h b/iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionResponseChainInfoUtil.h similarity index 80% rename from iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionResponseChainUtil.h rename to iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionResponseChainInfoUtil.h index 5e03325..5c30d18 100644 --- a/iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionResponseChainUtil.h +++ b/iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionResponseChainInfoUtil.h @@ -9,7 +9,7 @@ NS_ASSUME_NONNULL_BEGIN -@interface PrismInstructionResponseChainUtil : NSObject +@interface PrismInstructionResponseChainInfoUtil : NSObject + (NSString*)getResponseChainInfoWithElement:(UIView*)element; @end diff --git a/iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionResponseChainUtil.m b/iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionResponseChainInfoUtil.m similarity index 75% rename from iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionResponseChainUtil.m rename to iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionResponseChainInfoUtil.m index 9cec259..bd11f29 100644 --- a/iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionResponseChainUtil.m +++ b/iOS/DiDiPrism/Src/Core/Instruction/Util/PrismInstructionResponseChainInfoUtil.m @@ -5,39 +5,40 @@ // Created by hulk on 2019/6/27. // -#import "PrismInstructionResponseChainUtil.h" -#import "PrismInstructionDefines.h" +#import "PrismInstructionResponseChainInfoUtil.h" // Category #import "UIResponder+PrismIntercept.h" #import "UIView+PrismExtends.h" -@implementation PrismInstructionResponseChainUtil +@implementation PrismInstructionResponseChainInfoUtil #pragma mark - public method + (NSString*)getResponseChainInfoWithElement:(UIView*)element { if (!element || ![element superview]) { return nil; } - NSMutableString *description = [NSMutableString stringWithString:kBeginOfViewPathFlag]; + NSMutableString *description = [NSMutableString string]; NSArray *viewControllers = [self getParentViewControllersOfView:element]; if (viewControllers.count) { [viewControllers enumerateObjectsUsingBlock:^(UIViewController * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { - NSString *mark = obj.autoDotSpecialMark.length ? obj.autoDotSpecialMark : NSStringFromClass([obj class]); - [description appendFormat:@"%@_&_", mark]; + NSString *mark = obj.prismAutoDotSpecialMark.length ? obj.prismAutoDotSpecialMark : NSStringFromClass([obj class]); + if (description.length) { + [description appendString:@"_&_"]; + } + [description appendString:mark]; }]; } else { NSArray *views = [self getParentViewOfView:element]; if (views.count) { [views enumerateObjectsUsingBlock:^(UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { - NSString *mark = obj.autoDotSpecialMark.length ? obj.autoDotSpecialMark : NSStringFromClass([obj class]); - [description appendFormat:@"%@_&_", mark]; + NSString *mark = obj.prismAutoDotSpecialMark.length ? obj.prismAutoDotSpecialMark : NSStringFromClass([obj class]); + if (description.length) { + [description appendString:@"_&_"]; + } + [description appendString:mark]; }]; } } - - if ([description isEqualToString:kBeginOfViewPathFlag]) { - return nil; - } return [description copy]; } diff --git a/iOS/DiDiPrism/Src/Ability/BehaviorRecord/Intercept/NSURLSessionConfiguration+PrismRecord.h b/iOS/DiDiPrism/Src/Core/Intercept/NSURLSessionConfiguration+PrismIntercept.h similarity index 52% rename from iOS/DiDiPrism/Src/Ability/BehaviorRecord/Intercept/NSURLSessionConfiguration+PrismRecord.h rename to iOS/DiDiPrism/Src/Core/Intercept/NSURLSessionConfiguration+PrismIntercept.h index a1d4390..8d48833 100644 --- a/iOS/DiDiPrism/Src/Ability/BehaviorRecord/Intercept/NSURLSessionConfiguration+PrismRecord.h +++ b/iOS/DiDiPrism/Src/Core/Intercept/NSURLSessionConfiguration+PrismIntercept.h @@ -1,5 +1,5 @@ // -// NSURLSessionConfiguration+PrismRecord.h +// NSURLSessionConfiguration+PrismIntercept.h // DiDiPrism // // Created by hulk on 2020/4/1. @@ -9,8 +9,9 @@ NS_ASSUME_NONNULL_BEGIN -@interface NSURLSessionConfiguration (PrismRecord) +@interface NSURLSessionConfiguration (PrismIntercept) ++ (void)prism_swizzleMethodIMP; @end NS_ASSUME_NONNULL_END diff --git a/iOS/DiDiPrism/Src/Core/Intercept/NSURLSessionConfiguration+PrismIntercept.m b/iOS/DiDiPrism/Src/Core/Intercept/NSURLSessionConfiguration+PrismIntercept.m new file mode 100644 index 0000000..60af34e --- /dev/null +++ b/iOS/DiDiPrism/Src/Core/Intercept/NSURLSessionConfiguration+PrismIntercept.m @@ -0,0 +1,42 @@ +// +// NSURLSessionConfiguration+PrismIntercept.m +// DiDiPrism +// +// Created by hulk on 2020/4/1. +// + +#import "NSURLSessionConfiguration+PrismIntercept.h" +// Util +#import "PrismRuntimeUtil.h" + +@implementation NSURLSessionConfiguration (PrismIntercept) ++ (void)prism_swizzleMethodIMP { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + [PrismRuntimeUtil hookClass:object_getClass(self) originalSelector:@selector(defaultSessionConfiguration) swizzledSelector:@selector(prism_autoDot_defaultSessionConfiguration) isClassMethod:YES]; + [PrismRuntimeUtil hookClass:object_getClass(self) originalSelector:@selector(ephemeralSessionConfiguration) swizzledSelector:@selector(prism_autoDot_ephemeralSessionConfiguration) isClassMethod:YES]; + }); +} + ++ (NSURLSessionConfiguration *)prism_autoDot_defaultSessionConfiguration { + NSURLSessionConfiguration *configuration = [self prism_autoDot_defaultSessionConfiguration]; + NSMutableArray * protocolClasses = [NSMutableArray arrayWithArray:configuration.protocolClasses]; + Class protocolClass = NSClassFromString(@"PrismRecordNSURLProtocol"); + if (protocolClass) { + [protocolClasses insertObject:protocolClass atIndex:0]; + } + configuration.protocolClasses = [protocolClasses copy]; + return configuration; +} + ++ (NSURLSessionConfiguration *)prism_autoDot_ephemeralSessionConfiguration { + NSURLSessionConfiguration *configuration = [self prism_autoDot_ephemeralSessionConfiguration]; + NSMutableArray * protocolClasses = [NSMutableArray arrayWithArray:configuration.protocolClasses]; + Class protocolClass = NSClassFromString(@"PrismRecordNSURLProtocol"); + if (protocolClass) { + [protocolClasses insertObject:protocolClass atIndex:0]; + } + configuration.protocolClasses = [protocolClasses copy]; + return configuration; +} +@end diff --git a/iOS/DiDiPrism/Src/Core/Intercept/PrismInterceptSystemEventAssist.h b/iOS/DiDiPrism/Src/Core/Intercept/PrismInterceptSystemEventAssist.h new file mode 100644 index 0000000..b326280 --- /dev/null +++ b/iOS/DiDiPrism/Src/Core/Intercept/PrismInterceptSystemEventAssist.h @@ -0,0 +1,17 @@ +// +// PrismInterceptSystemEventAssist.h +// DiDiPrism +// +// Created by hulk on 2021/7/1. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface PrismInterceptSystemEventAssist : NSObject + ++ (void)prism_addObserver; +@end + +NS_ASSUME_NONNULL_END diff --git a/iOS/DiDiPrism/Src/Core/Intercept/PrismInterceptSystemEventAssist.m b/iOS/DiDiPrism/Src/Core/Intercept/PrismInterceptSystemEventAssist.m new file mode 100644 index 0000000..dee4a72 --- /dev/null +++ b/iOS/DiDiPrism/Src/Core/Intercept/PrismInterceptSystemEventAssist.m @@ -0,0 +1,40 @@ +// +// PrismInterceptSystemEventAssist.m +// DiDiPrism +// +// Created by hulk on 2021/7/1. +// + +#import "PrismInterceptSystemEventAssist.h" +// Dispatcher +#import "PrismEventDispatcher.h" + +@implementation PrismInterceptSystemEventAssist +#pragma mark - public method ++ (void)prism_addObserver { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didFinishLaunching:) name:UIApplicationDidFinishLaunchingNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willResignActive:) name:UIApplicationWillResignActiveNotification object:nil]; +} + +#pragma mark - action ++ (void)didFinishLaunching:(NSNotification*)notification { + // 支持端外打开逻辑 + if ([notification.userInfo.allKeys containsObject:UIApplicationLaunchOptionsURLKey]) { + NSURL *openUrl = notification.userInfo[UIApplicationLaunchOptionsURLKey]; + if (openUrl.absoluteString.length) { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + params[@"openUrl"] = openUrl.absoluteString; + [[PrismEventDispatcher sharedInstance] dispatchEvent:PrismDispatchEventUIApplicationLaunchByURL withSender:nil params:[params copy]]; + } + } +} + ++ (void)didBecomeActive:(NSNotification*)notification { + [[PrismEventDispatcher sharedInstance] dispatchEvent:PrismDispatchEventUIApplicationDidBecomeActive withSender:nil params:nil]; +} + ++ (void)willResignActive:(NSNotification*)notification { + [[PrismEventDispatcher sharedInstance] dispatchEvent:PrismDispatchEventUIApplicationWillResignActive withSender:nil params:nil]; +} +@end diff --git a/iOS/DiDiPrism/Src/Core/Intercept/UIControl+PrismIntercept.h b/iOS/DiDiPrism/Src/Core/Intercept/UIControl+PrismIntercept.h index 78dce58..3b523a3 100644 --- a/iOS/DiDiPrism/Src/Core/Intercept/UIControl+PrismIntercept.h +++ b/iOS/DiDiPrism/Src/Core/Intercept/UIControl+PrismIntercept.h @@ -11,8 +11,9 @@ NS_ASSUME_NONNULL_BEGIN @interface UIControl (PrismIntercept) -@property (nonatomic, copy) NSString *autoDotTargetAndSelector; +@property (nonatomic, strong) NSMutableDictionary *prismAutoDotTargetAndSelector; ++ (void)prism_swizzleMethodIMP; @end NS_ASSUME_NONNULL_END diff --git a/iOS/DiDiPrism/Src/Core/Intercept/UIControl+PrismIntercept.m b/iOS/DiDiPrism/Src/Core/Intercept/UIControl+PrismIntercept.m index 79bfaa5..b682646 100644 --- a/iOS/DiDiPrism/Src/Core/Intercept/UIControl+PrismIntercept.m +++ b/iOS/DiDiPrism/Src/Core/Intercept/UIControl+PrismIntercept.m @@ -10,61 +10,69 @@ #import "PrismEventDispatcher.h" // Util #import "PrismRuntimeUtil.h" +// Category +#import "NSDictionary+PrismExtends.h" @implementation UIControl (PrismIntercept) -+ (void)load { + +#pragma mark - public method ++ (void)prism_swizzleMethodIMP { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(sendAction:to:forEvent:) swizzledSelector:@selector(autoDot_sendAction:to:forEvent:)]; - [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(addTarget:action:forControlEvents:) swizzledSelector:@selector(autoDot_addTarget:action:forControlEvents:)]; - [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(removeTarget:action:forControlEvents:) swizzledSelector:@selector(autoDot_removeTarget:action:forControlEvents:)]; + [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(sendAction:to:forEvent:) swizzledSelector:@selector(prism_autoDot_sendAction:to:forEvent:)]; + [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(addTarget:action:forControlEvents:) swizzledSelector:@selector(prism_autoDot_addTarget:action:forControlEvents:)]; + [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(removeTarget:action:forControlEvents:) swizzledSelector:@selector(prism_autoDot_removeTarget:action:forControlEvents:)]; }); } -- (void)autoDot_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event { +#pragma mark - private method +- (void)prism_autoDot_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event { NSMutableDictionary *params = [NSMutableDictionary dictionary]; params[@"target"] = target; params[@"action"] = NSStringFromSelector(action); [[PrismEventDispatcher sharedInstance] dispatchEvent:PrismDispatchEventUIControlSendAction_Start withSender:self params:[params copy]]; //原始逻辑 - [self autoDot_sendAction:action to:target forEvent:event]; + [self prism_autoDot_sendAction:action to:target forEvent:event]; } -- (void)autoDot_addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents { +- (void)prism_autoDot_addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents { //原始逻辑 - [self autoDot_addTarget:target action:action forControlEvents:controlEvents]; + [self prism_autoDot_addTarget:target action:action forControlEvents:controlEvents]; + NSString *controlEventsStr = [NSString stringWithFormat:@"%ld", controlEvents]; + BOOL isTouchEvent = (controlEvents & UIControlEventAllTouchEvents) || (controlEvents & UIControlEventPrimaryActionTriggered); // 忽略用户输入过程 - BOOL isEditingChangedEvent = controlEvents == UIControlEventEditingChanged; - if (!isEditingChangedEvent && !self.autoDotTargetAndSelector.length) { + BOOL isEditingEvent = (controlEvents & UIControlEventAllEditingEvents) && controlEvents != UIControlEventEditingChanged; + BOOL isValueChangedEvent = controlEvents & UIControlEventValueChanged; + BOOL isAllowedEvents = isTouchEvent || isEditingEvent || isValueChangedEvent; + if (isAllowedEvents && ![[self.prismAutoDotTargetAndSelector prism_stringForKey:controlEventsStr] length]) { NSString *classString = NSStringFromClass([target class]); NSString *actionString = NSStringFromSelector(action); if (classString.length && actionString.length) { - self.autoDotTargetAndSelector = [NSString stringWithFormat:@"%@_&_%@", classString, actionString]; - + self.prismAutoDotTargetAndSelector[controlEventsStr] = [NSString stringWithFormat:@"%@_&_%@", classString, actionString]; } } } -- (void)autoDot_removeTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents { +- (void)prism_autoDot_removeTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents { //原始逻辑 - [self autoDot_removeTarget:target action:action forControlEvents:controlEvents]; + [self prism_autoDot_removeTarget:target action:action forControlEvents:controlEvents]; - self.autoDotTargetAndSelector = @""; + NSString *controlEventsStr = [NSString stringWithFormat:@"%ld", controlEvents]; + self.prismAutoDotTargetAndSelector[controlEventsStr] = @""; } -#pragma mark - actions - -#pragma mark - public method - -#pragma mark - private method - #pragma mark - property -- (NSString *)autoDotTargetAndSelector { - return objc_getAssociatedObject(self, _cmd); +- (NSMutableDictionary *)prismAutoDotTargetAndSelector { + NSMutableDictionary *result = objc_getAssociatedObject(self, _cmd); + if (!result) { + result = [NSMutableDictionary dictionary]; + objc_setAssociatedObject(self, @selector(prismAutoDotTargetAndSelector), result, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + return result; } -- (void)setAutoDotTargetAndSelector:(NSString *)autoDotTargetAndSelector { - objc_setAssociatedObject(self, @selector(autoDotTargetAndSelector), autoDotTargetAndSelector, OBJC_ASSOCIATION_COPY_NONATOMIC); +- (void)setPrismAutoDotTargetAndSelector:(NSMutableDictionary *)prismAutoDotTargetAndSelector { + objc_setAssociatedObject(self, @selector(prismAutoDotTargetAndSelector), prismAutoDotTargetAndSelector, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } @end diff --git a/iOS/DiDiPrism/Src/Core/Intercept/UIImage+PrismIntercept.h b/iOS/DiDiPrism/Src/Core/Intercept/UIImage+PrismIntercept.h index 4deb1de..d52d934 100644 --- a/iOS/DiDiPrism/Src/Core/Intercept/UIImage+PrismIntercept.h +++ b/iOS/DiDiPrism/Src/Core/Intercept/UIImage+PrismIntercept.h @@ -11,8 +11,9 @@ NS_ASSUME_NONNULL_BEGIN @interface UIImage (PrismIntercept) -@property (nonatomic, copy) NSString *autoDotImageName; +@property (nonatomic, copy) NSString *prismAutoDotImageName; ++ (void)prism_swizzleMethodIMP; @end NS_ASSUME_NONNULL_END diff --git a/iOS/DiDiPrism/Src/Core/Intercept/UIImage+PrismIntercept.m b/iOS/DiDiPrism/Src/Core/Intercept/UIImage+PrismIntercept.m index 6794c49..2f46d74 100644 --- a/iOS/DiDiPrism/Src/Core/Intercept/UIImage+PrismIntercept.m +++ b/iOS/DiDiPrism/Src/Core/Intercept/UIImage+PrismIntercept.m @@ -10,60 +10,52 @@ #import "PrismRuntimeUtil.h" @implementation UIImage (PrismIntercept) -+ (void)load { +#pragma mark - public method ++ (void)prism_swizzleMethodIMP { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - [PrismRuntimeUtil hookClass:object_getClass(self) originalSelector:@selector(imageNamed:) swizzledSelector:@selector(autoDot_imageNamed:) isClassMethod:YES]; + [PrismRuntimeUtil hookClass:object_getClass(self) originalSelector:@selector(imageNamed:) swizzledSelector:@selector(prism_autoDot_imageNamed:) isClassMethod:YES]; #if __has_include() - [PrismRuntimeUtil hookClass:object_getClass(self) originalSelector:@selector(imageNamed:inBundle:compatibleWithTraitCollection:) swizzledSelector:@selector(autoDot_imageNamed:inBundle:compatibleWithTraitCollection:) isClassMethod:YES]; + [PrismRuntimeUtil hookClass:object_getClass(self) originalSelector:@selector(imageNamed:inBundle:compatibleWithTraitCollection:) swizzledSelector:@selector(prism_autoDot_imageNamed:inBundle:compatibleWithTraitCollection:) isClassMethod:YES]; #endif - [PrismRuntimeUtil hookClass:object_getClass(self) originalSelector:@selector(imageWithContentsOfFile:) swizzledSelector:@selector(autoDot_imageWithContentsOfFile:) isClassMethod:YES]; - [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(initWithContentsOfFile:) swizzledSelector:@selector(autoDot_initWithContentsOfFile:)]; + [PrismRuntimeUtil hookClass:object_getClass(self) originalSelector:@selector(imageWithContentsOfFile:) swizzledSelector:@selector(prism_autoDot_imageWithContentsOfFile:) isClassMethod:YES]; + [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(initWithContentsOfFile:) swizzledSelector:@selector(prism_autoDot_initWithContentsOfFile:)]; }); } -+ (UIImage *)autoDot_imageNamed:(NSString *)name { - UIImage *image = [self autoDot_imageNamed:name]; +#pragma mark - private method ++ (UIImage *)prism_autoDot_imageNamed:(NSString *)name { + UIImage *image = [self prism_autoDot_imageNamed:name]; - image.autoDotImageName = [self getImageNameFromPath:name]; + image.prismAutoDotImageName = [self getImageNameFromPath:name]; return image; } -+ (UIImage *)autoDot_imageNamed:(NSString *)name inBundle:(NSBundle *)bundle compatibleWithTraitCollection:(UITraitCollection *)traitCollection { - UIImage *image = [self autoDot_imageNamed:name inBundle:bundle compatibleWithTraitCollection:traitCollection]; ++ (UIImage *)prism_autoDot_imageNamed:(NSString *)name inBundle:(NSBundle *)bundle compatibleWithTraitCollection:(UITraitCollection *)traitCollection { + UIImage *image = [self prism_autoDot_imageNamed:name inBundle:bundle compatibleWithTraitCollection:traitCollection]; - image.autoDotImageName = [self getImageNameFromPath:name]; + image.prismAutoDotImageName = [self getImageNameFromPath:name]; return image; } -+ (UIImage *)autoDot_imageWithContentsOfFile:(NSString *)path { - UIImage *image = [self autoDot_imageWithContentsOfFile:path]; ++ (UIImage *)prism_autoDot_imageWithContentsOfFile:(NSString *)path { + UIImage *image = [self prism_autoDot_imageWithContentsOfFile:path]; - image.autoDotImageName = [self getImageNameFromPath:path]; + image.prismAutoDotImageName = [self getImageNameFromPath:path]; return image; } -- (instancetype)autoDot_initWithContentsOfFile:(NSString *)path { - UIImage *image = [self autoDot_initWithContentsOfFile:path]; +- (instancetype)prism_autoDot_initWithContentsOfFile:(NSString *)path { + UIImage *image = [self prism_autoDot_initWithContentsOfFile:path]; - image.autoDotImageName = [UIImage getImageNameFromPath:path]; + image.prismAutoDotImageName = [UIImage getImageNameFromPath:path]; return image; } -#pragma mark - property -- (NSString *)autoDotImageName { - NSString *name = objc_getAssociatedObject(self, _cmd); - return name; -} -- (void)setAutoDotImageName:(NSString *)autoDotImageName { - objc_setAssociatedObject(self, @selector(autoDotImageName), autoDotImageName, OBJC_ASSOCIATION_COPY_NONATOMIC); -} - -#pragma mark - private method + (NSString*)getImageNameFromPath:(NSString*)path { static NSString *separator = @"/"; if ([path containsString:separator]) { @@ -75,4 +67,13 @@ + (NSString*)getImageNameFromPath:(NSString*)path { return path; } +#pragma mark - property +- (NSString *)prismAutoDotImageName { + NSString *name = objc_getAssociatedObject(self, _cmd); + return name; +} +- (void)setPrismAutoDotImageName:(NSString *)prismAutoDotImageName { + objc_setAssociatedObject(self, @selector(prismAutoDotImageName), prismAutoDotImageName, OBJC_ASSOCIATION_COPY_NONATOMIC); +} + @end diff --git a/iOS/DiDiPrism/Src/Core/Intercept/UILongPressGestureRecognizer+PrismIntercept.h b/iOS/DiDiPrism/Src/Core/Intercept/UILongPressGestureRecognizer+PrismIntercept.h new file mode 100644 index 0000000..e001066 --- /dev/null +++ b/iOS/DiDiPrism/Src/Core/Intercept/UILongPressGestureRecognizer+PrismIntercept.h @@ -0,0 +1,17 @@ +// +// UILongPressGestureRecognizer+PrismIntercept.h +// DiDiPrism +// +// Created by hulk on 2021/6/23. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface UILongPressGestureRecognizer (PrismIntercept) + ++ (void)prism_swizzleMethodIMP; +@end + +NS_ASSUME_NONNULL_END diff --git a/iOS/DiDiPrism/Src/Core/Intercept/UILongPressGestureRecognizer+PrismIntercept.m b/iOS/DiDiPrism/Src/Core/Intercept/UILongPressGestureRecognizer+PrismIntercept.m new file mode 100644 index 0000000..1eec976 --- /dev/null +++ b/iOS/DiDiPrism/Src/Core/Intercept/UILongPressGestureRecognizer+PrismIntercept.m @@ -0,0 +1,103 @@ +// +// UILongPressGestureRecognizer+PrismIntercept.m +// DiDiPrism +// +// Created by hulk on 2021/6/23. +// + +#import "UILongPressGestureRecognizer+PrismIntercept.h" +#import +// Dispatcher +#import "PrismEventDispatcher.h" +// Util +#import "PrismRuntimeUtil.h" +#import "PrismInstructionAreaInfoUtil.h" +#import "PrismInstructionResponseChainInfoUtil.h" +// Category +#import "UIResponder+PrismIntercept.h" +#import "UIGestureRecognizer+PrismExtends.h" + +@implementation UILongPressGestureRecognizer (PrismIntercept) +#pragma mark - public method ++ (void)prism_swizzleMethodIMP { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + RSSwizzleInstanceMethod(UILongPressGestureRecognizer, @selector(setState:), + RSSWReturnType(void), + RSSWArguments(UIGestureRecognizerState state), + RSSWReplacement({ + RSSWCallOriginal(state); + SEL swizzleSelector = NSSelectorFromString(@"prism_autoDot_setState:"); + Method swizzleMethod = class_getInstanceMethod([UILongPressGestureRecognizer class], swizzleSelector); + IMP swizzleMethodImp = method_getImplementation(swizzleMethod); + void (*functionPointer)(id, SEL, UIGestureRecognizerState) = (void (*)(id, SEL, UIGestureRecognizerState))swizzleMethodImp; + functionPointer(self, _cmd, state); + }), + RSSwizzleModeAlways, + NULL); + + + [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(initWithTarget:action:) swizzledSelector:@selector(prism_autoDot_initWithTarget:action:)]; + [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(addTarget:action:) swizzledSelector:@selector(prism_autoDot_addTarget:action:)]; + [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(removeTarget:action:) swizzledSelector:@selector(prism_autoDot_removeTarget:action:)]; + }); +} + +#pragma mark - private method +- (void)prism_autoDot_setState:(UIGestureRecognizerState)state { + // set逻辑后 state 和 self.state 应一致,某些场景下self.state依然为UIGestureRecognizerStateFailed不符合预期。 + if (state == UIGestureRecognizerStateRecognized && self.state == UIGestureRecognizerStateRecognized) { + //注1:没有选择在setState阶段直接进行event id的收集,是因为类似于WEEX场景中一次操作可以识别到多个手势(区别于实际起作用的手势)。 + //注2:选择在setState阶段先收集响应链信息和区位信息,是因为有些场景下点击事件触发后view.superview可能为nil,需提前捕捉。 + if ([self.view superview]) { + NSString *responseChainInfo = [PrismInstructionResponseChainInfoUtil getResponseChainInfoWithElement:self.view]; + if (responseChainInfo.length) { + [self setPrismAutoDotResponseChainInfo:responseChainInfo]; + } + NSArray *areaInfo = [PrismInstructionAreaInfoUtil getAreaInfoWithElement:self.view]; + if (areaInfo.count) { + [self setPrismAutoDotAreaInfo:areaInfo]; + } + } + } +} + + +- (instancetype)prism_autoDot_initWithTarget:(id)target action:(SEL)action { + //原始逻辑 + UILongPressGestureRecognizer *gesture = [self prism_autoDot_initWithTarget:target action:action]; + + [gesture addTarget:self action:@selector(prism_autoDot_longPressAction:)]; + gesture.prismAutoDotTargetAndSelector = [NSString stringWithFormat:@"%@_&_%@", NSStringFromClass([target class]), NSStringFromSelector(action)]; + + return gesture; +} + +- (void)prism_autoDot_addTarget:(id)target action:(SEL)action { + //原始逻辑 + [self prism_autoDot_addTarget:target action:action]; + + [self prism_autoDot_addTarget:self action:@selector(prism_autoDot_longPressAction:)]; + if (!self.prismAutoDotTargetAndSelector.length) { + self.prismAutoDotTargetAndSelector = [NSString stringWithFormat:@"%@_&_%@", NSStringFromClass([target class]), NSStringFromSelector(action)]; + } +} + +- (void)prism_autoDot_removeTarget:(id)target action:(SEL)action { + //原始逻辑 + [self prism_autoDot_removeTarget:target action:action]; + + [self prism_autoDot_removeTarget:self action:@selector(prism_autoDot_longPressAction:)]; + self.prismAutoDotTargetAndSelector = @""; +} + +#pragma mark - actions +- (void)prism_autoDot_longPressAction:(UILongPressGestureRecognizer*)longPressGestureRecognizer { + if (longPressGestureRecognizer.state == UIGestureRecognizerStateEnded) { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + params[@"target"] = self; + params[@"action"] = NSStringFromSelector(@selector(prism_autoDot_longPressAction:)); + [[PrismEventDispatcher sharedInstance] dispatchEvent:PrismDispatchEventUILongPressGestureRecognizerAction withSender:self params:[params copy]]; + } +} +@end diff --git a/iOS/DiDiPrism/Src/Core/Intercept/UIResponder+PrismIntercept.h b/iOS/DiDiPrism/Src/Core/Intercept/UIResponder+PrismIntercept.h index 7873c9a..fb2f6c9 100644 --- a/iOS/DiDiPrism/Src/Core/Intercept/UIResponder+PrismIntercept.h +++ b/iOS/DiDiPrism/Src/Core/Intercept/UIResponder+PrismIntercept.h @@ -10,9 +10,9 @@ NS_ASSUME_NONNULL_BEGIN @interface UIResponder (PrismIntercept) -@property (nonatomic, copy) NSString *autoDotItemName; //辅助参数,用来存储通用ID -@property (nonatomic, copy) NSString *autoDotSpecialMark; //特殊节点标识 -@property (nonatomic, copy) NSString *autoDotFinalMark; //支持自定义节点标识 +@property (nonatomic, copy) NSString *prismAutoDotItemName; //辅助参数,用来存储通用ID +@property (nonatomic, copy) NSString *prismAutoDotSpecialMark; //特殊节点标识 +@property (nonatomic, copy) NSString *prismAutoDotFinalMark; //支持自定义节点标识 @end NS_ASSUME_NONNULL_END diff --git a/iOS/DiDiPrism/Src/Core/Intercept/UIResponder+PrismIntercept.m b/iOS/DiDiPrism/Src/Core/Intercept/UIResponder+PrismIntercept.m index 4fdd7e4..f7d34c0 100644 --- a/iOS/DiDiPrism/Src/Core/Intercept/UIResponder+PrismIntercept.m +++ b/iOS/DiDiPrism/Src/Core/Intercept/UIResponder+PrismIntercept.m @@ -12,24 +12,24 @@ @implementation UIResponder (PrismIntercept) #pragma mark - property -- (NSString *)autoDotItemName { +- (NSString *)prismAutoDotItemName { return objc_getAssociatedObject(self, _cmd); } -- (void)setAutoDotItemName:(NSString *)autoDotItemName { - objc_setAssociatedObject(self, @selector(autoDotItemName), autoDotItemName, OBJC_ASSOCIATION_COPY_NONATOMIC); +- (void)setPrismAutoDotItemName:(NSString *)prismAutoDotItemName { + objc_setAssociatedObject(self, @selector(prismAutoDotItemName), prismAutoDotItemName, OBJC_ASSOCIATION_COPY_NONATOMIC); } -- (NSString *)autoDotSpecialMark { +- (NSString *)prismAutoDotSpecialMark { return objc_getAssociatedObject(self, _cmd); } -- (void)setAutoDotSpecialMark:(NSString *)autoDotSpecialMark { - objc_setAssociatedObject(self, @selector(autoDotSpecialMark), autoDotSpecialMark, OBJC_ASSOCIATION_COPY_NONATOMIC); +- (void)setPrismAutoDotSpecialMark:(NSString *)prismAutoDotSpecialMark { + objc_setAssociatedObject(self, @selector(prismAutoDotSpecialMark), prismAutoDotSpecialMark, OBJC_ASSOCIATION_COPY_NONATOMIC); } -- (NSString *)autoDotFinalMark { +- (NSString *)prismAutoDotFinalMark { return objc_getAssociatedObject(self, _cmd); } -- (void)setAutoDotFinalMark:(NSString *)autoDotFinalMark { - objc_setAssociatedObject(self, @selector(autoDotFinalMark), autoDotFinalMark, OBJC_ASSOCIATION_COPY_NONATOMIC); +- (void)setPrismAutoDotFinalMark:(NSString *)prismAutoDotFinalMark { + objc_setAssociatedObject(self, @selector(prismAutoDotFinalMark), prismAutoDotFinalMark, OBJC_ASSOCIATION_COPY_NONATOMIC); } @end diff --git a/iOS/DiDiPrism/Src/Core/Intercept/UIScreenEdgePanGestureRecognizer+PrismIntercept.h b/iOS/DiDiPrism/Src/Core/Intercept/UIScreenEdgePanGestureRecognizer+PrismIntercept.h index bab6334..22da912 100644 --- a/iOS/DiDiPrism/Src/Core/Intercept/UIScreenEdgePanGestureRecognizer+PrismIntercept.h +++ b/iOS/DiDiPrism/Src/Core/Intercept/UIScreenEdgePanGestureRecognizer+PrismIntercept.h @@ -11,8 +11,10 @@ NS_ASSUME_NONNULL_BEGIN @interface UIScreenEdgePanGestureRecognizer (PrismIntercept) -@property (nonatomic, strong) NSNumber *autoDotViewControllerCount; -@property (nonatomic, weak) UINavigationController *autoDotNavigationController; +@property (nonatomic, strong) NSNumber *prismAutoDotViewControllerCount; +@property (nonatomic, weak) UINavigationController *prismAutoDotNavigationController; + ++ (void)setPrismHookEnable:(BOOL)hookEnable; @end NS_ASSUME_NONNULL_END diff --git a/iOS/DiDiPrism/Src/Core/Intercept/UIScreenEdgePanGestureRecognizer+PrismIntercept.m b/iOS/DiDiPrism/Src/Core/Intercept/UIScreenEdgePanGestureRecognizer+PrismIntercept.m index 97154a2..dfac99b 100644 --- a/iOS/DiDiPrism/Src/Core/Intercept/UIScreenEdgePanGestureRecognizer+PrismIntercept.m +++ b/iOS/DiDiPrism/Src/Core/Intercept/UIScreenEdgePanGestureRecognizer+PrismIntercept.m @@ -14,64 +14,79 @@ // 响应链信息 + gesture.edges = UIRectEdgeLeft; @implementation UIScreenEdgePanGestureRecognizer (PrismIntercept) +#pragma mark - public method +/* + 不使用被动触发而是在load中进行,是因为 + 必须及时swizzle,不然赶不上后退手势初始化。 + */ + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(initWithTarget:action:) swizzledSelector:@selector(autoDot_initWithTarget:action:)]; - [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(addTarget:action:) swizzledSelector:@selector(autoDot_addTarget:action:)]; - [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(removeTarget:action:) swizzledSelector:@selector(autoDot_removeTarget:action:)]; + [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(initWithTarget:action:) swizzledSelector:@selector(prism_autoDot_initWithTarget:action:)]; + [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(addTarget:action:) swizzledSelector:@selector(prism_autoDot_addTarget:action:)]; + [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(removeTarget:action:) swizzledSelector:@selector(prism_autoDot_removeTarget:action:)]; }); } -- (instancetype)autoDot_initWithTarget:(id)target action:(SEL)action { +#pragma mark - private method +- (instancetype)prism_autoDot_initWithTarget:(id)target action:(SEL)action { //原始逻辑 - UIScreenEdgePanGestureRecognizer *gesture = [self autoDot_initWithTarget:target action:action]; + UIScreenEdgePanGestureRecognizer *gesture = [self prism_autoDot_initWithTarget:target action:action]; - [gesture addTarget:self action:@selector(autoDot_edgePanAction:)]; + [gesture addTarget:self action:@selector(prism_autoDot_edgePanAction:)]; return gesture; } -- (void)autoDot_addTarget:(id)target action:(SEL)action { +- (void)prism_autoDot_addTarget:(id)target action:(SEL)action { //原始逻辑 - [self autoDot_addTarget:target action:action]; + [self prism_autoDot_addTarget:target action:action]; - [self autoDot_addTarget:self action:@selector(autoDot_edgePanAction:)]; + [self prism_autoDot_addTarget:self action:@selector(prism_autoDot_edgePanAction:)]; } -- (void)autoDot_removeTarget:(id)target action:(SEL)action { +- (void)prism_autoDot_removeTarget:(id)target action:(SEL)action { //原始逻辑 - [self autoDot_removeTarget:target action:action]; + [self prism_autoDot_removeTarget:target action:action]; - [self autoDot_removeTarget:self action:@selector(autoDot_edgePanAction:)]; + [self prism_autoDot_removeTarget:self action:@selector(prism_autoDot_edgePanAction:)]; } #pragma mark - actions -- (void)autoDot_edgePanAction:(UIScreenEdgePanGestureRecognizer*)edgePanGestureRecognizer { +- (void)prism_autoDot_edgePanAction:(UIScreenEdgePanGestureRecognizer*)edgePanGestureRecognizer { + if ([UIScreenEdgePanGestureRecognizer prismHookEnable] == NO) { + return; + } NSMutableDictionary *params = [NSMutableDictionary dictionary]; params[@"target"] = self; - params[@"action"] = NSStringFromSelector(@selector(autoDot_edgePanAction:)); + params[@"action"] = NSStringFromSelector(@selector(prism_autoDot_edgePanAction:)); [[PrismEventDispatcher sharedInstance] dispatchEvent:PrismDispatchEventUIScreenEdgePanGestureRecognizerAction withSender:self params:[params copy]]; } -#pragma mark - private method - #pragma mark - property -- (NSNumber*)autoDotViewControllerCount { +- (NSNumber*)prismAutoDotViewControllerCount { return objc_getAssociatedObject(self, _cmd); } -- (void)setAutoDotViewControllerCount:(NSNumber*)autoDotViewControllerCount { - objc_setAssociatedObject(self, @selector(autoDotViewControllerCount), autoDotViewControllerCount, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +- (void)setPrismAutoDotViewControllerCount:(NSNumber*)prismAutoDotViewControllerCount { + objc_setAssociatedObject(self, @selector(prismAutoDotViewControllerCount), prismAutoDotViewControllerCount, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } -- (UINavigationController*)autoDotNavigationController { - UINavigationController* (^block)(void) = objc_getAssociatedObject(self, @selector(autoDotNavigationController)); +- (UINavigationController*)prismAutoDotNavigationController { + UINavigationController* (^block)(void) = objc_getAssociatedObject(self, @selector(prismAutoDotNavigationController)); return (block ? block() : nil); } -- (void)setAutoDotNavigationController:(UINavigationController*)autoDotNavigationController { - __weak UINavigationController *weakController = autoDotNavigationController; +- (void)setPrismAutoDotNavigationController:(UINavigationController*)prismAutoDotNavigationController { + __weak UINavigationController *weakController = prismAutoDotNavigationController; UINavigationController* (^block)(void) = ^{ return weakController; }; - objc_setAssociatedObject(self, @selector(autoDotNavigationController), block, OBJC_ASSOCIATION_COPY); + objc_setAssociatedObject(self, @selector(prismAutoDotNavigationController), block, OBJC_ASSOCIATION_COPY_NONATOMIC); +} + ++ (BOOL)prismHookEnable { + NSNumber *hookEnable = objc_getAssociatedObject(self, _cmd); + return [hookEnable boolValue]; +} ++ (void)setPrismHookEnable:(BOOL)hookEnable { + objc_setAssociatedObject(self, @selector(prismHookEnable), [NSNumber numberWithBool:hookEnable], OBJC_ASSOCIATION_RETAIN_NONATOMIC); } @end diff --git a/iOS/DiDiPrism/Src/Core/Intercept/UITapGestureRecognizer+PrismIntercept.h b/iOS/DiDiPrism/Src/Core/Intercept/UITapGestureRecognizer+PrismIntercept.h index 06cc682..89f441b 100644 --- a/iOS/DiDiPrism/Src/Core/Intercept/UITapGestureRecognizer+PrismIntercept.h +++ b/iOS/DiDiPrism/Src/Core/Intercept/UITapGestureRecognizer+PrismIntercept.h @@ -11,9 +11,7 @@ NS_ASSUME_NONNULL_BEGIN @interface UITapGestureRecognizer (PrismIntercept) -@property (nonatomic, copy) NSString *autoDotTargetAndSelector; -@property (nonatomic, copy) NSString *autoDotResponseChainInfo; -@property (nonatomic, copy) NSString *autoDotAreaInfo; ++ (void)prism_swizzleMethodIMP; @end NS_ASSUME_NONNULL_END diff --git a/iOS/DiDiPrism/Src/Core/Intercept/UITapGestureRecognizer+PrismIntercept.m b/iOS/DiDiPrism/Src/Core/Intercept/UITapGestureRecognizer+PrismIntercept.m index 284df50..f75dac5 100644 --- a/iOS/DiDiPrism/Src/Core/Intercept/UITapGestureRecognizer+PrismIntercept.m +++ b/iOS/DiDiPrism/Src/Core/Intercept/UITapGestureRecognizer+PrismIntercept.m @@ -6,106 +6,100 @@ // #import "UITapGestureRecognizer+PrismIntercept.h" +#import // Dispatcher #import "PrismEventDispatcher.h" // Util #import "PrismRuntimeUtil.h" -#import "PrismInstructionAreaUtil.h" -#import "PrismInstructionResponseChainUtil.h" +#import "PrismInstructionAreaInfoUtil.h" +#import "PrismInstructionResponseChainInfoUtil.h" // Category #import "UIResponder+PrismIntercept.h" +#import "UIGestureRecognizer+PrismExtends.h" @implementation UITapGestureRecognizer (PrismIntercept) -+ (void)load { + +#pragma mark - public method ++ (void)prism_swizzleMethodIMP { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(setState:) swizzledSelector:@selector(autoDot_setState:)]; - [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(initWithTarget:action:) swizzledSelector:@selector(autoDot_initWithTarget:action:)]; - [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(addTarget:action:) swizzledSelector:@selector(autoDot_addTarget:action:)]; - [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(removeTarget:action:) swizzledSelector:@selector(autoDot_removeTarget:action:)]; + RSSwizzleInstanceMethod(UITapGestureRecognizer, @selector(setState:), + RSSWReturnType(void), + RSSWArguments(UIGestureRecognizerState state), + RSSWReplacement({ + RSSWCallOriginal(state); + SEL swizzleSelector = NSSelectorFromString(@"prism_autoDot_setState:"); + Method swizzleMethod = class_getInstanceMethod([UITapGestureRecognizer class], swizzleSelector); + IMP swizzleMethodImp = method_getImplementation(swizzleMethod); + void (*functionPointer)(id, SEL, UIGestureRecognizerState) = (void (*)(id, SEL, UIGestureRecognizerState))swizzleMethodImp; + functionPointer(self, _cmd, state); + }), + RSSwizzleModeAlways, + NULL); + + + [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(initWithTarget:action:) swizzledSelector:@selector(prism_autoDot_initWithTarget:action:)]; + [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(addTarget:action:) swizzledSelector:@selector(prism_autoDot_addTarget:action:)]; + [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(removeTarget:action:) swizzledSelector:@selector(prism_autoDot_removeTarget:action:)]; }); } -- (void)autoDot_setState:(UIGestureRecognizerState)state { - [self autoDot_setState:state]; - - // 某些场景下 state 和 self.state 不一致,self.state为UIGestureRecognizerStateFailed +#pragma mark - private method +- (void)prism_autoDot_setState:(UIGestureRecognizerState)state { + // set逻辑后 state 和 self.state 应一致,某些场景下self.state依然为UIGestureRecognizerStateFailed不符合预期。 if (state == UIGestureRecognizerStateRecognized && self.state == UIGestureRecognizerStateRecognized) { //注1:没有选择在setState阶段直接进行event id的收集,是因为类似于WEEX场景中一次操作可以识别到多个手势(区别于实际起作用的手势)。 //注2:选择在setState阶段先收集响应链信息和区位信息,是因为有些场景下点击事件触发后view.superview可能为nil,需提前捕捉。 if ([self.view superview]) { - NSString *responseChainInfo = [PrismInstructionResponseChainUtil getResponseChainInfoWithElement:self.view]; + NSString *responseChainInfo = [PrismInstructionResponseChainInfoUtil getResponseChainInfoWithElement:self.view]; if (responseChainInfo.length) { - [self setAutoDotResponseChainInfo:responseChainInfo]; + [self setPrismAutoDotResponseChainInfo:responseChainInfo]; } - NSString *areaInfo = [PrismInstructionAreaUtil getAreaInfoWithElement:self.view]; - if (areaInfo.length) { - [self setAutoDotAreaInfo:areaInfo]; + NSArray *areaInfo = [PrismInstructionAreaInfoUtil getAreaInfoWithElement:self.view]; + if (areaInfo.count) { + [self setPrismAutoDotAreaInfo:areaInfo]; } } } } -- (instancetype)autoDot_initWithTarget:(id)target action:(SEL)action { +- (instancetype)prism_autoDot_initWithTarget:(id)target action:(SEL)action { //原始逻辑 - UITapGestureRecognizer *gesture = [self autoDot_initWithTarget:target action:action]; + UITapGestureRecognizer *gesture = [self prism_autoDot_initWithTarget:target action:action]; - [gesture addTarget:self action:@selector(autoDot_tapAction:)]; - gesture.autoDotTargetAndSelector = [NSString stringWithFormat:@"%@_&_%@", NSStringFromClass([target class]), NSStringFromSelector(action)]; + [gesture addTarget:self action:@selector(prism_autoDot_tapAction:)]; + gesture.prismAutoDotTargetAndSelector = [NSString stringWithFormat:@"%@_&_%@", NSStringFromClass([target class]), NSStringFromSelector(action)]; return gesture; } -- (void)autoDot_addTarget:(id)target action:(SEL)action { +- (void)prism_autoDot_addTarget:(id)target action:(SEL)action { //原始逻辑 - [self autoDot_addTarget:target action:action]; + [self prism_autoDot_addTarget:target action:action]; - [self autoDot_addTarget:self action:@selector(autoDot_tapAction:)]; - if (!self.autoDotTargetAndSelector.length) { - self.autoDotTargetAndSelector = [NSString stringWithFormat:@"%@_&_%@", NSStringFromClass([target class]), NSStringFromSelector(action)]; + [self prism_autoDot_addTarget:self action:@selector(prism_autoDot_tapAction:)]; + if (!self.prismAutoDotTargetAndSelector.length) { + self.prismAutoDotTargetAndSelector = [NSString stringWithFormat:@"%@_&_%@", NSStringFromClass([target class]), NSStringFromSelector(action)]; } } -- (void)autoDot_removeTarget:(id)target action:(SEL)action { +- (void)prism_autoDot_removeTarget:(id)target action:(SEL)action { //原始逻辑 - [self autoDot_removeTarget:target action:action]; + [self prism_autoDot_removeTarget:target action:action]; - [self autoDot_removeTarget:self action:@selector(autoDot_tapAction:)]; - self.autoDotTargetAndSelector = @""; + [self prism_autoDot_removeTarget:self action:@selector(prism_autoDot_tapAction:)]; + self.prismAutoDotTargetAndSelector = @""; } #pragma mark - actions -- (void)autoDot_tapAction:(UITapGestureRecognizer*)tapGestureRecognizer { +- (void)prism_autoDot_tapAction:(UITapGestureRecognizer*)tapGestureRecognizer { NSMutableDictionary *params = [NSMutableDictionary dictionary]; params[@"target"] = self; - params[@"action"] = NSStringFromSelector(@selector(autoDot_tapAction:)); + params[@"action"] = NSStringFromSelector(@selector(prism_autoDot_tapAction:)); [[PrismEventDispatcher sharedInstance] dispatchEvent:PrismDispatchEventUITapGestureRecognizerAction withSender:self params:[params copy]]; } -#pragma mark - public method - -#pragma mark - private method - #pragma mark - property -- (NSString *)autoDotTargetAndSelector { - return objc_getAssociatedObject(self, _cmd); -} -- (void)setAutoDotTargetAndSelector:(NSString *)autoDotTargetAndSelector { - objc_setAssociatedObject(self, @selector(autoDotTargetAndSelector), autoDotTargetAndSelector, OBJC_ASSOCIATION_COPY_NONATOMIC); -} - -- (NSString *)autoDotResponseChainInfo { - return objc_getAssociatedObject(self, _cmd); -} -- (void)setAutoDotResponseChainInfo:(NSString *)autoDotResponseChainInfo { - objc_setAssociatedObject(self, @selector(autoDotResponseChainInfo), autoDotResponseChainInfo, OBJC_ASSOCIATION_COPY_NONATOMIC); -} -- (NSString *)autoDotAreaInfo { - return objc_getAssociatedObject(self, _cmd); -} -- (void)setAutoDotAreaInfo:(NSString *)autoDotAreaInfo { - objc_setAssociatedObject(self, @selector(autoDotAreaInfo), autoDotAreaInfo, OBJC_ASSOCIATION_COPY_NONATOMIC); -} @end diff --git a/iOS/DiDiPrism/Src/Core/Intercept/UIView+PrismIntercept.h b/iOS/DiDiPrism/Src/Core/Intercept/UIView+PrismIntercept.h index 472ab65..3b9acaa 100644 --- a/iOS/DiDiPrism/Src/Core/Intercept/UIView+PrismIntercept.h +++ b/iOS/DiDiPrism/Src/Core/Intercept/UIView+PrismIntercept.h @@ -11,6 +11,7 @@ NS_ASSUME_NONNULL_BEGIN @interface UIView (PrismIntercept) ++ (void)prism_swizzleMethodIMP; @end NS_ASSUME_NONNULL_END diff --git a/iOS/DiDiPrism/Src/Core/Intercept/UIView+PrismIntercept.m b/iOS/DiDiPrism/Src/Core/Intercept/UIView+PrismIntercept.m index eb371fa..324c3ce 100644 --- a/iOS/DiDiPrism/Src/Core/Intercept/UIView+PrismIntercept.m +++ b/iOS/DiDiPrism/Src/Core/Intercept/UIView+PrismIntercept.m @@ -12,19 +12,21 @@ #import "PrismRuntimeUtil.h" @implementation UIView (PrismIntercept) -+ (void)load { +#pragma mark - public method ++ (void)prism_swizzleMethodIMP { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(touchesEnded:withEvent:) swizzledSelector:@selector(autoDot_touchesEnded:withEvent:)]; - [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(didMoveToSuperview) swizzledSelector:@selector(autoDot_didMoveToSuperview)]; - [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(didMoveToWindow) swizzledSelector:@selector(autoDot_didMoveToWindow)]; + [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(touchesEnded:withEvent:) swizzledSelector:@selector(prism_autoDot_touchesEnded:withEvent:)]; + [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(didMoveToSuperview) swizzledSelector:@selector(prism_autoDot_didMoveToSuperview)]; + [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(didMoveToWindow) swizzledSelector:@selector(prism_autoDot_didMoveToWindow)]; }); } +#pragma mark - private method // 考虑到可能的手势影响,选择hook touchesEnded:withEvent:更合理。 -- (void)autoDot_touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { +- (void)prism_autoDot_touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { //原始逻辑 - Method original_TouchesEnded = class_getInstanceMethod([UIView class], @selector(autoDot_touchesEnded:withEvent:)); + Method original_TouchesEnded = class_getInstanceMethod([UIView class], @selector(prism_autoDot_touchesEnded:withEvent:)); IMP original_TouchesEnded_Method_Imp = method_getImplementation(original_TouchesEnded); void (*functionPointer)(id, SEL, NSSet *, UIEvent *) = (void (*)(id, SEL, NSSet *, UIEvent *))original_TouchesEnded_Method_Imp; functionPointer(self, _cmd, touches, event); @@ -32,16 +34,16 @@ - (void)autoDot_touchesEnded:(NSSet *)touches withEvent:(UIEvent *)ev [[PrismEventDispatcher sharedInstance] dispatchEvent:PrismDispatchEventUIViewTouchesEnded_End withSender:self params:nil]; } -- (void)autoDot_didMoveToSuperview { - [self autoDot_didMoveToSuperview]; +- (void)prism_autoDot_didMoveToSuperview { + [self prism_autoDot_didMoveToSuperview]; if (!self.superview) { return; } [[PrismEventDispatcher sharedInstance] dispatchEvent:PrismDispatchEventUIViewDidMoveToSuperview withSender:self params:nil]; } -- (void)autoDot_didMoveToWindow { - [self autoDot_didMoveToWindow]; +- (void)prism_autoDot_didMoveToWindow { + [self prism_autoDot_didMoveToWindow]; if (!self.superview) { return; } diff --git a/iOS/DiDiPrism/Src/Core/Intercept/UIViewController+PrismIntercept.h b/iOS/DiDiPrism/Src/Core/Intercept/UIViewController+PrismIntercept.h index e248069..2e2b495 100644 --- a/iOS/DiDiPrism/Src/Core/Intercept/UIViewController+PrismIntercept.h +++ b/iOS/DiDiPrism/Src/Core/Intercept/UIViewController+PrismIntercept.h @@ -11,6 +11,7 @@ NS_ASSUME_NONNULL_BEGIN @interface UIViewController (PrismIntercept) ++ (void)prism_swizzleMethodIMP; @end NS_ASSUME_NONNULL_END diff --git a/iOS/DiDiPrism/Src/Core/Intercept/UIViewController+PrismIntercept.m b/iOS/DiDiPrism/Src/Core/Intercept/UIViewController+PrismIntercept.m index 12c24a2..c56e065 100644 --- a/iOS/DiDiPrism/Src/Core/Intercept/UIViewController+PrismIntercept.m +++ b/iOS/DiDiPrism/Src/Core/Intercept/UIViewController+PrismIntercept.m @@ -12,16 +12,18 @@ #import "PrismRuntimeUtil.h" @implementation UIViewController (PrismIntercept) -+ (void)load { +#pragma mark - public method ++ (void)prism_swizzleMethodIMP { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(viewDidAppear:) swizzledSelector:@selector(autoDot_viewDidAppear:)]; + [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(viewDidAppear:) swizzledSelector:@selector(prism_autoDot_viewDidAppear:)]; }); } -- (void)autoDot_viewDidAppear:(BOOL)animated { +#pragma mark - private method +- (void)prism_autoDot_viewDidAppear:(BOOL)animated { //原始逻辑 - [self autoDot_viewDidAppear:animated]; + [self prism_autoDot_viewDidAppear:animated]; [[PrismEventDispatcher sharedInstance] dispatchEvent:PrismDispatchEventUIViewControllerViewDidAppear withSender:self params:nil]; } diff --git a/iOS/DiDiPrism/Src/Core/Intercept/WKWebView+PrismIntercept.h b/iOS/DiDiPrism/Src/Core/Intercept/WKWebView+PrismIntercept.h index b7748ca..00bf257 100644 --- a/iOS/DiDiPrism/Src/Core/Intercept/WKWebView+PrismIntercept.h +++ b/iOS/DiDiPrism/Src/Core/Intercept/WKWebView+PrismIntercept.h @@ -11,7 +11,8 @@ NS_ASSUME_NONNULL_BEGIN @interface WKWebView (PrismIntercept) -- (void)autoDot_addCustomScript:(NSString *)customScript withConfiguration:(WKWebViewConfiguration *)configuration; ++ (void)prism_swizzleMethodIMP; +- (void)prism_autoDot_addCustomScript:(NSString *)customScript withConfiguration:(WKWebViewConfiguration *)configuration; @end diff --git a/iOS/DiDiPrism/Src/Core/Intercept/WKWebView+PrismIntercept.m b/iOS/DiDiPrism/Src/Core/Intercept/WKWebView+PrismIntercept.m index 7bc6bca..73c391f 100644 --- a/iOS/DiDiPrism/Src/Core/Intercept/WKWebView+PrismIntercept.m +++ b/iOS/DiDiPrism/Src/Core/Intercept/WKWebView+PrismIntercept.m @@ -12,26 +12,15 @@ #import "PrismRuntimeUtil.h" @implementation WKWebView (PrismIntercept) -+ (void)load { +#pragma mark - public method ++ (void)prism_swizzleMethodIMP { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(initWithFrame:configuration:) swizzledSelector:@selector(autoDot_initWithFrame:configuration:)]; + [PrismRuntimeUtil hookClass:[self class] originalSelector:@selector(initWithFrame:configuration:) swizzledSelector:@selector(prism_autoDot_initWithFrame:configuration:)]; }); } -- (instancetype)autoDot_initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration { - WKWebView *webView = [self autoDot_initWithFrame:frame configuration:configuration]; - - NSMutableDictionary *params = [NSMutableDictionary dictionary]; - if (configuration) { - params[@"configuration"] = configuration; - } - [[PrismEventDispatcher sharedInstance] dispatchEvent:PrismDispatchEventWKWebViewInitWithFrame withSender:self params:[params copy]]; - - return webView; -} - -- (void)autoDot_addCustomScript:(NSString *)customScript withConfiguration:(WKWebViewConfiguration *)configuration { +- (void)prism_autoDot_addCustomScript:(NSString *)customScript withConfiguration:(WKWebViewConfiguration *)configuration { if(!customScript.length) return; NSArray *scripts = [configuration.userContentController userScripts]; @@ -43,4 +32,17 @@ - (void)autoDot_addCustomScript:(NSString *)customScript withConfiguration:(WKWe WKUserScript *script = [[WKUserScript alloc] initWithSource:customScript injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES]; [configuration.userContentController addUserScript:script]; } + +#pragma mark - private method +- (instancetype)prism_autoDot_initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration { + WKWebView *webView = [self prism_autoDot_initWithFrame:frame configuration:configuration]; + + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + if (configuration) { + params[@"configuration"] = configuration; + } + [[PrismEventDispatcher sharedInstance] dispatchEvent:PrismDispatchEventWKWebViewInitWithFrame withSender:self params:[params copy]]; + + return webView; +} @end diff --git a/iOS/DiDiPrism/Src/Core/PrismEngine.h b/iOS/DiDiPrism/Src/Core/PrismEngine.h new file mode 100644 index 0000000..ac1b1cd --- /dev/null +++ b/iOS/DiDiPrism/Src/Core/PrismEngine.h @@ -0,0 +1,17 @@ +// +// PrismEngine.h +// DiDiPrism +// +// Created by hulk on 2021/6/30. +// + +#import +#import "PrismDispatchEventDefine.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PrismEngine : NSObject ++ (void)startEngineWithEventCategorys:(PrismEventCategory)eventCategorys; +@end + +NS_ASSUME_NONNULL_END diff --git a/iOS/DiDiPrism/Src/Core/PrismEngine.m b/iOS/DiDiPrism/Src/Core/PrismEngine.m new file mode 100644 index 0000000..d3ba454 --- /dev/null +++ b/iOS/DiDiPrism/Src/Core/PrismEngine.m @@ -0,0 +1,50 @@ +// +// PrismEngine.m +// DiDiPrism +// +// Created by hulk on 2021/6/30. +// + +#import "PrismEngine.h" +#import "PrismInterceptSystemEventAssist.h" +// Category +#import "UIControl+PrismIntercept.h" +#import "UIImage+PrismIntercept.h" +#import "UILongPressGestureRecognizer+PrismIntercept.h" +#import "UITapGestureRecognizer+PrismIntercept.h" +#import "UIView+PrismIntercept.h" +#import "UIViewController+PrismIntercept.h" +#import "WKWebView+PrismIntercept.h" +#import "NSURLSessionConfiguration+PrismIntercept.h" +#import "UIScreenEdgePanGestureRecognizer+PrismIntercept.h" + +@implementation PrismEngine +#pragma mark public method ++ (void)startEngineWithEventCategorys:(PrismEventCategory)eventCategorys { + [UIImage prism_swizzleMethodIMP]; + + if (eventCategorys & PrismEventCategoryTouch) { + [UIControl prism_swizzleMethodIMP]; + [UITapGestureRecognizer prism_swizzleMethodIMP]; + [UILongPressGestureRecognizer prism_swizzleMethodIMP]; + [UIView prism_swizzleMethodIMP]; + [WKWebView prism_swizzleMethodIMP]; + } + + if (eventCategorys & PrismEventCategorySlip) { + [UIScreenEdgePanGestureRecognizer setPrismHookEnable:YES]; + } + + if (eventCategorys & PrismEventCategoryPageSwitch) { + [UIViewController prism_swizzleMethodIMP]; + } + + if (eventCategorys & PrismEventCategoryNetwork) { + [NSURLSessionConfiguration prism_swizzleMethodIMP]; + } + + if (eventCategorys & PrismEventCategorySystem) { + [PrismInterceptSystemEventAssist prism_addObserver]; + } +} +@end diff --git a/iOS/DiDiPrismDemo/DiDiPrismDemo/View/DetailTableViewCell.m b/iOS/DiDiPrismDemo/DiDiPrismDemo/View/DetailTableViewCell.m index 87e5180..8e6c093 100644 --- a/iOS/DiDiPrismDemo/DiDiPrismDemo/View/DetailTableViewCell.m +++ b/iOS/DiDiPrismDemo/DiDiPrismDemo/View/DetailTableViewCell.m @@ -60,7 +60,7 @@ - (void)initView { #pragma mark - setters - (void)setIndex:(NSInteger)index { _index = index; - self.autoDotItemName = [NSString stringWithFormat:@"%ld", _index]; + self.prismAutoDotItemName = [NSString stringWithFormat:@"%ld", _index]; self.titleLabel.text = [NSString stringWithFormat:@"Cell %ld\n点击前往详情页-A", _index]; [self.goButton setTitle:[self buttonTitleOfIndex:_index % 4] forState:UIControlStateNormal]; } diff --git a/iOS/DiDiPrismDemo/DiDiPrismDemo/ViewController/DetailViewController/DetailBViewController.m b/iOS/DiDiPrismDemo/DiDiPrismDemo/ViewController/DetailViewController/DetailBViewController.m index 47d5bc4..65f262b 100644 --- a/iOS/DiDiPrismDemo/DiDiPrismDemo/ViewController/DetailViewController/DetailBViewController.m +++ b/iOS/DiDiPrismDemo/DiDiPrismDemo/ViewController/DetailViewController/DetailBViewController.m @@ -14,8 +14,10 @@ @interface DetailBViewController () @property (nonatomic, strong) UILabel *buttonLabel; @property (nonatomic, strong) UILabel *textfieldLabel; @property (nonatomic, strong) UILabel *switchLabel; -@property (nonatomic, strong) UILabel *gestureLabel; -@property (nonatomic, strong) UIView *testView; +@property (nonatomic, strong) UILabel *testTapLabel; +@property (nonatomic, strong) UIView *testTapView; +@property (nonatomic, strong) UILabel *testLongPressLabel; +@property (nonatomic, strong) UIView *testLongPressView; @property (nonatomic, strong) UIButton *testButton; @property (nonatomic, strong) UITextField *testTextField; @property (nonatomic, strong) UISwitch *testSwitch; @@ -34,6 +36,16 @@ - (void)tapAction:(UITapGestureRecognizer*)gesture { [self.testTextField resignFirstResponder]; } +- (void)testDownAction:(UIButton*)sender { + MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES]; + hud.mode = MBProgressHUDModeText; + hud.label.text = sender.titleLabel.text; + dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC); + dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ + [MBProgressHUD hideHUDForView:self.view animated:YES]; + }); +} + - (void)testAction:(UIButton*)sender { MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES]; hud.mode = MBProgressHUDModeText; @@ -47,7 +59,17 @@ - (void)testAction:(UIButton*)sender { - (void)testTapAction:(UITapGestureRecognizer*)gesture { MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES]; hud.mode = MBProgressHUDModeText; - hud.label.text = @"手势触发"; + hud.label.text = @"点击手势触发"; + dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC); + dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ + [MBProgressHUD hideHUDForView:self.view animated:YES]; + }); +} + +- (void)testLongPressAction:(UILongPressGestureRecognizer*)gesture { + MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES]; + hud.mode = MBProgressHUDModeText; + hud.label.text = @"长按手势触发"; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ [MBProgressHUD hideHUDForView:self.view animated:YES]; @@ -78,8 +100,10 @@ - (void)_initView { [self.view addSubview:self.testTextField]; [self.view addSubview:self.switchLabel]; [self.view addSubview:self.testSwitch]; - [self.view addSubview:self.gestureLabel]; - [self.view addSubview:self.testView]; + [self.view addSubview:self.testTapLabel]; + [self.view addSubview:self.testTapView]; + [self.view addSubview:self.testLongPressLabel]; + [self.view addSubview:self.testLongPressView]; [self.buttonLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.left.equalTo(self.view).offset(15); @@ -109,13 +133,23 @@ - (void)_initView { make.left.equalTo(self.switchLabel.mas_right).offset(30); make.width.mas_equalTo(100); }]; - [self.gestureLabel mas_makeConstraints:^(MASConstraintMaker *make) { + [self.testTapLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.left.equalTo(self.view).offset(15); make.top.equalTo(self.switchLabel.mas_bottom).offset(50); }]; - [self.testView mas_makeConstraints:^(MASConstraintMaker *make) { - make.centerY.equalTo(self.gestureLabel); - make.left.equalTo(self.gestureLabel.mas_right).offset(30); + [self.testTapView mas_makeConstraints:^(MASConstraintMaker *make) { + make.centerY.equalTo(self.testTapLabel); + make.left.equalTo(self.testTapLabel.mas_right).offset(30); + make.width.mas_equalTo(100); + make.height.mas_equalTo(50); + }]; + [self.testLongPressLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.equalTo(self.view).offset(15); + make.top.equalTo(self.testTapLabel.mas_bottom).offset(50); + }]; + [self.testLongPressView mas_makeConstraints:^(MASConstraintMaker *make) { + make.centerY.equalTo(self.testLongPressLabel); + make.left.equalTo(self.testLongPressLabel.mas_right).offset(30); make.width.mas_equalTo(100); make.height.mas_equalTo(50); }]; @@ -154,32 +188,60 @@ - (UILabel *)switchLabel { return _switchLabel; } -- (UILabel *)gestureLabel { - if (!_gestureLabel) { - _gestureLabel = [[UILabel alloc] init]; - _gestureLabel.font = [UIFont systemFontOfSize:15]; - _gestureLabel.textColor = [UIColor blackColor]; - _gestureLabel.text = @"手势示例:"; +- (UILabel *)testTapLabel { + if (!_testTapLabel) { + _testTapLabel = [[UILabel alloc] init]; + _testTapLabel.font = [UIFont systemFontOfSize:15]; + _testTapLabel.textColor = [UIColor blackColor]; + _testTapLabel.text = @"点击手势示例:"; } - return _gestureLabel; + return _testTapLabel; } -- (UIView *)testView { - if (!_testView) { - _testView = [[UIView alloc] init]; - _testView.backgroundColor = [UIColor blueColor]; +- (UIView *)testTapView { + if (!_testTapView) { + _testTapView = [[UIView alloc] init]; + _testTapView.backgroundColor = [UIColor blueColor]; UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(testTapAction:)]; - [_testView addGestureRecognizer:tapGesture]; + [_testTapView addGestureRecognizer:tapGesture]; + UILabel *label = [[UILabel alloc] init]; + label.font = [UIFont systemFontOfSize:15]; + label.textColor = [UIColor whiteColor]; + label.text = @"测试点击手势"; + [_testTapView addSubview:label]; + [label mas_makeConstraints:^(MASConstraintMaker *make) { + make.center.equalTo(_testTapView); + }]; + } + return _testTapView; +} + +- (UILabel *)testLongPressLabel { + if (!_testLongPressLabel) { + _testLongPressLabel = [[UILabel alloc] init]; + _testLongPressLabel.font = [UIFont systemFontOfSize:15]; + _testLongPressLabel.textColor = [UIColor blackColor]; + _testLongPressLabel.text = @"长按手势示例:"; + } + return _testLongPressLabel; +} + +- (UIView *)testLongPressView { + if (!_testLongPressView) { + _testLongPressView = [[UIView alloc] init]; + _testLongPressView.backgroundColor = [UIColor blueColor]; + UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(testLongPressAction:)]; + [_testLongPressView addGestureRecognizer:longPressGesture]; UILabel *label = [[UILabel alloc] init]; label.font = [UIFont systemFontOfSize:15]; label.textColor = [UIColor whiteColor]; - label.text = @"测试手势"; - [_testView addSubview:label]; + label.text = @"测试长按手势"; + [_testLongPressView addSubview:label]; [label mas_makeConstraints:^(MASConstraintMaker *make) { - make.center.equalTo(_testView); + make.center.equalTo(_testLongPressView); }]; } - return _testView; + return _testLongPressView; } - (UIButton *)testButton { @@ -190,6 +252,7 @@ - (UIButton *)testButton { [_testButton setTitle:@"测试按钮" forState:UIControlStateNormal]; _testButton.layer.borderColor = [UIColor grayColor].CGColor; _testButton.layer.borderWidth = 1.0; + [_testButton addTarget:self action:@selector(testDownAction:) forControlEvents:UIControlEventTouchDown]; [_testButton addTarget:self action:@selector(testAction:) forControlEvents:UIControlEventTouchUpInside]; } return _testButton; diff --git a/iOS/DiDiPrismDemo/DiDiPrismDemo/ViewController/EntranceViewController.m b/iOS/DiDiPrismDemo/DiDiPrismDemo/ViewController/EntranceViewController.m index e34e2de..cd4262a 100644 --- a/iOS/DiDiPrismDemo/DiDiPrismDemo/ViewController/EntranceViewController.m +++ b/iOS/DiDiPrismDemo/DiDiPrismDemo/ViewController/EntranceViewController.m @@ -7,6 +7,7 @@ // #import "EntranceViewController.h" +#import "PrismEngine.h" #import "BehaviorReplayViewController.h" #import "BehaviorDetectViewController.h" #import "DataVisualizationViewController.h" @@ -19,6 +20,7 @@ @implementation EntranceViewController #pragma mark - life cycle - (void)viewDidLoad { [super viewDidLoad]; + [PrismEngine startEngineWithEventCategorys:PrismEventCategoryAll]; [self _initView]; } diff --git a/iOS/DiDiPrismDemo/Podfile b/iOS/DiDiPrismDemo/Podfile index a0c8f80..daf1387 100644 --- a/iOS/DiDiPrismDemo/Podfile +++ b/iOS/DiDiPrismDemo/Podfile @@ -10,6 +10,7 @@ target 'DiDiPrismDemo' do pod 'Masonry' pod 'SDWebImage', '~> 5.0' pod 'MBProgressHUD', '~> 1.2.0' + pod 'RSSwizzle' end post_install do |installer_representation|