1
This commit is contained in:
29
Pods/LookinServer/Src/Main/Server/Connection/LKS_ConnectionManager.h
generated
Normal file
29
Pods/LookinServer/Src/Main/Server/Connection/LKS_ConnectionManager.h
generated
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// Lookin.h
|
||||
// Lookin
|
||||
//
|
||||
// Created by Li Kai on 2018/8/5.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
extern NSString *const LKS_ConnectionDidEndNotificationName;
|
||||
|
||||
@class LookinConnectionResponseAttachment;
|
||||
|
||||
@interface LKS_ConnectionManager : NSObject
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
@property(nonatomic, assign) BOOL applicationIsActive;
|
||||
|
||||
- (void)respond:(LookinConnectionResponseAttachment *)data requestType:(uint32_t)requestType tag:(uint32_t)tag;
|
||||
|
||||
- (void)pushData:(NSObject *)data type:(uint32_t)type;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
268
Pods/LookinServer/Src/Main/Server/Connection/LKS_ConnectionManager.m
generated
Normal file
268
Pods/LookinServer/Src/Main/Server/Connection/LKS_ConnectionManager.m
generated
Normal file
@@ -0,0 +1,268 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LookinServer.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2018/8/5.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LKS_ConnectionManager.h"
|
||||
#import "Lookin_PTChannel.h"
|
||||
#import "LKS_RequestHandler.h"
|
||||
#import "LookinConnectionResponseAttachment.h"
|
||||
#import "LKS_ExportManager.h"
|
||||
#import "LookinServerDefines.h"
|
||||
#import "LKS_TraceManager.h"
|
||||
#import "LKS_MultiplatformAdapter.h"
|
||||
|
||||
NSString *const LKS_ConnectionDidEndNotificationName = @"LKS_ConnectionDidEndNotificationName";
|
||||
|
||||
@interface LKS_ConnectionManager () <Lookin_PTChannelDelegate>
|
||||
|
||||
@property(nonatomic, weak) Lookin_PTChannel *peerChannel_;
|
||||
|
||||
@property(nonatomic, strong) LKS_RequestHandler *requestHandler;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LKS_ConnectionManager
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
static LKS_ConnectionManager *sharedInstance;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedInstance = [[LKS_ConnectionManager alloc] init];
|
||||
});
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
+ (void)load {
|
||||
// 触发 init 方法
|
||||
[LKS_ConnectionManager sharedInstance];
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
NSLog(@"LookinServer - Will launch. Framework version: %@", LOOKIN_SERVER_READABLE_VERSION);
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleApplicationDidBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleWillResignActiveNotification) name:UIApplicationWillResignActiveNotification object:nil];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleLocalInspect:) name:@"Lookin_2D" object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleLocalInspect:) name:@"Lookin_3D" object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:@"Lookin_Export" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
|
||||
[[LKS_ExportManager sharedInstance] exportAndShare];
|
||||
}];
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:@"Lookin_RelationSearch" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
|
||||
[[LKS_TraceManager sharedInstance] addSearchTarger:note.object];
|
||||
}];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleGetLookinInfo:) name:@"GetLookinInfo" object:nil];
|
||||
|
||||
self.requestHandler = [LKS_RequestHandler new];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_handleWillResignActiveNotification {
|
||||
self.applicationIsActive = NO;
|
||||
|
||||
if (self.peerChannel_ && ![self.peerChannel_ isConnected]) {
|
||||
[self.peerChannel_ close];
|
||||
self.peerChannel_ = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_handleApplicationDidBecomeActive {
|
||||
self.applicationIsActive = YES;
|
||||
[self searchPortToListenIfNoConnection];
|
||||
}
|
||||
|
||||
- (void)searchPortToListenIfNoConnection {
|
||||
if ([self.peerChannel_ isConnected]) {
|
||||
NSLog(@"LookinServer - Abort to search ports. Already has connected channel.");
|
||||
return;
|
||||
}
|
||||
NSLog(@"LookinServer - Searching port to listen...");
|
||||
[self.peerChannel_ close];
|
||||
self.peerChannel_ = nil;
|
||||
|
||||
if ([self isiOSAppOnMac]) {
|
||||
[self _tryToListenOnPortFrom:LookinSimulatorIPv4PortNumberStart to:LookinSimulatorIPv4PortNumberEnd current:LookinSimulatorIPv4PortNumberStart];
|
||||
} else {
|
||||
[self _tryToListenOnPortFrom:LookinUSBDeviceIPv4PortNumberStart to:LookinUSBDeviceIPv4PortNumberEnd current:LookinUSBDeviceIPv4PortNumberStart];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isiOSAppOnMac {
|
||||
#if TARGET_OS_SIMULATOR
|
||||
return YES;
|
||||
#else
|
||||
if (@available(iOS 14.0, *)) {
|
||||
// isiOSAppOnMac 这个 API 看似在 iOS 14.0 上可用,但其实在 iOS 14 beta 上是不存在的、有 unrecognized selector 问题,因此这里要用 respondsToSelector 做一下保护
|
||||
NSProcessInfo *info = [NSProcessInfo processInfo];
|
||||
if ([info respondsToSelector:@selector(isiOSAppOnMac)]) {
|
||||
return [info isiOSAppOnMac];
|
||||
} else if ([info respondsToSelector:@selector(isMacCatalystApp)]) {
|
||||
return [info isMacCatalystApp];
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
if (@available(iOS 13.0, tvOS 13.0, *)) {
|
||||
return [NSProcessInfo processInfo].isMacCatalystApp;
|
||||
}
|
||||
return NO;
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)_tryToListenOnPortFrom:(int)fromPort to:(int)toPort current:(int)currentPort {
|
||||
Lookin_PTChannel *channel = [Lookin_PTChannel channelWithDelegate:self];
|
||||
channel.targetPort = currentPort;
|
||||
[channel listenOnPort:currentPort IPv4Address:INADDR_LOOPBACK callback:^(NSError *error) {
|
||||
if (error) {
|
||||
if (error.code == 48) {
|
||||
// 该地址已被占用
|
||||
} else {
|
||||
// 未知失败
|
||||
}
|
||||
|
||||
if (currentPort < toPort) {
|
||||
// 尝试下一个端口
|
||||
NSLog(@"LookinServer - 127.0.0.1:%d is unavailable(%@). Will try anothor address ...", currentPort, error);
|
||||
[self _tryToListenOnPortFrom:fromPort to:toPort current:(currentPort + 1)];
|
||||
} else {
|
||||
// 所有端口都尝试完毕,全部失败
|
||||
NSLog(@"LookinServer - 127.0.0.1:%d is unavailable(%@).", currentPort, error);
|
||||
NSLog(@"LookinServer - Connect failed in the end.");
|
||||
}
|
||||
|
||||
} else {
|
||||
// 成功
|
||||
NSLog(@"LookinServer - Connected successfully on 127.0.0.1:%d", currentPort);
|
||||
// 此时 peerChannel_ 状态为 listening
|
||||
self.peerChannel_ = channel;
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (self.peerChannel_) {
|
||||
[self.peerChannel_ close];
|
||||
}
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)respond:(LookinConnectionResponseAttachment *)data requestType:(uint32_t)requestType tag:(uint32_t)tag {
|
||||
[self _sendData:data frameOfType:requestType tag:tag];
|
||||
}
|
||||
|
||||
- (void)pushData:(NSObject *)data type:(uint32_t)type {
|
||||
[self _sendData:data frameOfType:type tag:0];
|
||||
}
|
||||
|
||||
- (void)_sendData:(NSObject *)data frameOfType:(uint32_t)frameOfType tag:(uint32_t)tag {
|
||||
if (self.peerChannel_) {
|
||||
NSData *archivedData = [NSKeyedArchiver archivedDataWithRootObject:data];
|
||||
dispatch_data_t payload = [archivedData createReferencingDispatchData];
|
||||
|
||||
[self.peerChannel_ sendFrameOfType:frameOfType tag:tag withPayload:payload callback:^(NSError *error) {
|
||||
if (error) {
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Lookin_PTChannelDelegate
|
||||
|
||||
- (BOOL)ioFrameChannel:(Lookin_PTChannel*)channel shouldAcceptFrameOfType:(uint32_t)type tag:(uint32_t)tag payloadSize:(uint32_t)payloadSize {
|
||||
if (channel != self.peerChannel_) {
|
||||
return NO;
|
||||
} else if ([self.requestHandler canHandleRequestType:type]) {
|
||||
return YES;
|
||||
} else {
|
||||
[channel close];
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)ioFrameChannel:(Lookin_PTChannel*)channel didReceiveFrameOfType:(uint32_t)type tag:(uint32_t)tag payload:(Lookin_PTData*)payload {
|
||||
id object = nil;
|
||||
if (payload) {
|
||||
id unarchivedObject = [NSKeyedUnarchiver unarchiveObjectWithData:[NSData dataWithContentsOfDispatchData:payload.dispatchData]];
|
||||
if ([unarchivedObject isKindOfClass:[LookinConnectionAttachment class]]) {
|
||||
LookinConnectionAttachment *attachment = (LookinConnectionAttachment *)unarchivedObject;
|
||||
object = attachment.data;
|
||||
} else {
|
||||
object = unarchivedObject;
|
||||
}
|
||||
}
|
||||
[self.requestHandler handleRequestType:type tag:tag object:object];
|
||||
}
|
||||
|
||||
/// 当 Client 端链接成功时,该方法会被调用,然后 channel 的状态会变成 connected
|
||||
- (void)ioFrameChannel:(Lookin_PTChannel*)channel didAcceptConnection:(Lookin_PTChannel*)otherChannel fromAddress:(Lookin_PTAddress*)address {
|
||||
NSLog(@"LookinServer - channel:%@, acceptConnection:%@", channel.debugTag, otherChannel.debugTag);
|
||||
|
||||
Lookin_PTChannel *previousChannel = self.peerChannel_;
|
||||
|
||||
otherChannel.targetPort = address.port;
|
||||
self.peerChannel_ = otherChannel;
|
||||
|
||||
[previousChannel cancel];
|
||||
}
|
||||
|
||||
/// 当连接过 Lookin 客户端,然后 Lookin 客户端又被关闭时,会走到这里
|
||||
- (void)ioFrameChannel:(Lookin_PTChannel*)channel didEndWithError:(NSError*)error {
|
||||
if (self.peerChannel_ != channel) {
|
||||
// Client 端第一次连接上时,之前 listen 的 port 会被 Peertalk 内部 cancel(并在 didAcceptConnection 方法里给业务抛一个新建的 connected 状态的 channel),那个被 cancel 的 channel 会走到这里
|
||||
NSLog(@"LookinServer - Ignore channel%@ end.", channel.debugTag);
|
||||
return;
|
||||
}
|
||||
// Client 端关闭时,会走到这里
|
||||
NSLog(@"LookinServer - channel%@ DidEndWithError:%@", channel.debugTag, error);
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:LKS_ConnectionDidEndNotificationName object:self];
|
||||
[self searchPortToListenIfNoConnection];
|
||||
}
|
||||
|
||||
#pragma mark - Handler
|
||||
|
||||
- (void)_handleLocalInspect:(NSNotification *)note {
|
||||
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Lookin" message:@"Failed to run local inspection. The feature has been removed. Please use the computer version of Lookin or consider SDKs like FLEX for similar functionality." preferredStyle:UIAlertControllerStyleAlert];
|
||||
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];
|
||||
[alertController addAction:okAction];
|
||||
UIWindow *keyWindow = [LKS_MultiplatformAdapter keyWindow];
|
||||
UIViewController *rootViewController = [keyWindow rootViewController];
|
||||
[rootViewController presentViewController:alertController animated:YES completion:nil];
|
||||
|
||||
NSLog(@"LookinServer - Failed to run local inspection. The feature has been removed. Please use the computer version of Lookin or consider SDKs like FLEX for similar functionality.");
|
||||
}
|
||||
|
||||
- (void)handleGetLookinInfo:(NSNotification *)note {
|
||||
NSDictionary* userInfo = note.userInfo;
|
||||
if (!userInfo) {
|
||||
return;
|
||||
}
|
||||
NSMutableDictionary* infoWrapper = userInfo[@"infos"];
|
||||
if (![infoWrapper isKindOfClass:[NSMutableDictionary class]]) {
|
||||
NSLog(@"LookinServer - GetLookinInfo failed. Params invalid.");
|
||||
return;
|
||||
}
|
||||
infoWrapper[@"lookinServerVersion"] = LOOKIN_SERVER_READABLE_VERSION;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/// 这个类使得用户可以通过 NSClassFromString(@"Lookin") 来判断 LookinServer 是否被编译进了项目里
|
||||
|
||||
@interface Lookin : NSObject
|
||||
|
||||
@end
|
||||
|
||||
@implementation Lookin
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
21
Pods/LookinServer/Src/Main/Server/Connection/LKS_RequestHandler.h
generated
Normal file
21
Pods/LookinServer/Src/Main/Server/Connection/LKS_RequestHandler.h
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_RequestHandler.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/1/15.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface LKS_RequestHandler : NSObject
|
||||
|
||||
- (BOOL)canHandleRequestType:(uint32_t)requestType;
|
||||
|
||||
- (void)handleRequestType:(uint32_t)requestType tag:(uint32_t)tag object:(id)object;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
558
Pods/LookinServer/Src/Main/Server/Connection/LKS_RequestHandler.m
generated
Normal file
558
Pods/LookinServer/Src/Main/Server/Connection/LKS_RequestHandler.m
generated
Normal file
@@ -0,0 +1,558 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_RequestHandler.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/1/15.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LKS_RequestHandler.h"
|
||||
#import "NSObject+LookinServer.h"
|
||||
#import "UIImage+LookinServer.h"
|
||||
#import "LKS_ConnectionManager.h"
|
||||
#import "LookinConnectionResponseAttachment.h"
|
||||
#import "LookinAttributeModification.h"
|
||||
#import "LookinDisplayItemDetail.h"
|
||||
#import "LookinHierarchyInfo.h"
|
||||
#import "LookinServerDefines.h"
|
||||
#import <objc/runtime.h>
|
||||
#import "LookinObject.h"
|
||||
#import "LookinAppInfo.h"
|
||||
#import "LKS_AttrGroupsMaker.h"
|
||||
#import "LKS_InbuiltAttrModificationHandler.h"
|
||||
#import "LKS_CustomAttrModificationHandler.h"
|
||||
#import "LKS_AttrModificationPatchHandler.h"
|
||||
#import "LKS_HierarchyDetailsHandler.h"
|
||||
#import "LookinStaticAsyncUpdateTask.h"
|
||||
|
||||
@interface LKS_RequestHandler ()
|
||||
|
||||
@property(nonatomic, strong) NSMutableSet<LKS_HierarchyDetailsHandler *> *activeDetailHandlers;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LKS_RequestHandler {
|
||||
NSSet *_validRequestTypes;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
_validRequestTypes = [NSSet setWithObjects:@(LookinRequestTypePing),
|
||||
@(LookinRequestTypeApp),
|
||||
@(LookinRequestTypeHierarchy),
|
||||
@(LookinRequestTypeInbuiltAttrModification),
|
||||
@(LookinRequestTypeCustomAttrModification),
|
||||
@(LookinRequestTypeAttrModificationPatch),
|
||||
@(LookinRequestTypeHierarchyDetails),
|
||||
@(LookinRequestTypeFetchObject),
|
||||
@(LookinRequestTypeAllAttrGroups),
|
||||
@(LookinRequestTypeAllSelectorNames),
|
||||
@(LookinRequestTypeInvokeMethod),
|
||||
@(LookinRequestTypeFetchImageViewImage),
|
||||
@(LookinRequestTypeModifyRecognizerEnable),
|
||||
@(LookinPush_CanceHierarchyDetails),
|
||||
nil];
|
||||
|
||||
self.activeDetailHandlers = [NSMutableSet set];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)canHandleRequestType:(uint32_t)requestType {
|
||||
if ([_validRequestTypes containsObject:@(requestType)]) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)handleRequestType:(uint32_t)requestType tag:(uint32_t)tag object:(id)object {
|
||||
if (requestType == LookinRequestTypePing) {
|
||||
LookinConnectionResponseAttachment *responseAttachment = [LookinConnectionResponseAttachment new];
|
||||
// 当 app 处于后台时,可能可以执行代码也可能不能执行代码,如果运气好了可以执行代码,则这里直接主动使用 appIsInBackground 标识 app 处于后台,不要让 Lookin 客户端傻傻地等待超时了
|
||||
if (![LKS_ConnectionManager sharedInstance].applicationIsActive) {
|
||||
responseAttachment.appIsInBackground = YES;
|
||||
}
|
||||
[[LKS_ConnectionManager sharedInstance] respond:responseAttachment requestType:requestType tag:tag];
|
||||
|
||||
} else if (requestType == LookinRequestTypeApp) {
|
||||
// 请求可用设备信息
|
||||
if (![object isKindOfClass:[NSDictionary class]]) {
|
||||
[self _submitResponseWithError:LookinErr_Inner requestType:requestType tag:tag];
|
||||
return;
|
||||
}
|
||||
NSDictionary<NSString *, id> *params = object;
|
||||
BOOL needImages = ((NSNumber *)params[@"needImages"]).boolValue;
|
||||
NSArray<NSNumber *> *localIdentifiers = params[@"local"];
|
||||
|
||||
LookinAppInfo *appInfo = [LookinAppInfo currentInfoWithScreenshot:needImages icon:needImages localIdentifiers:localIdentifiers];
|
||||
|
||||
LookinConnectionResponseAttachment *responseAttachment = [LookinConnectionResponseAttachment new];
|
||||
responseAttachment.data = appInfo;
|
||||
[[LKS_ConnectionManager sharedInstance] respond:responseAttachment requestType:requestType tag:tag];
|
||||
|
||||
} else if (requestType == LookinRequestTypeHierarchy) {
|
||||
// 从 LookinClient 1.0.4 开始有这个参数,之前是 nil
|
||||
NSString *clientVersion = nil;
|
||||
if ([object isKindOfClass:[NSDictionary class]]) {
|
||||
NSDictionary<NSString *, id> *params = object;
|
||||
NSString *version = params[@"clientVersion"];
|
||||
if ([version isKindOfClass:[NSString class]]) {
|
||||
clientVersion = version;
|
||||
}
|
||||
}
|
||||
|
||||
LookinConnectionResponseAttachment *responseAttachment = [LookinConnectionResponseAttachment new];
|
||||
responseAttachment.data = [LookinHierarchyInfo staticInfoWithLookinVersion:clientVersion];
|
||||
[[LKS_ConnectionManager sharedInstance] respond:responseAttachment requestType:requestType tag:tag];
|
||||
|
||||
} else if (requestType == LookinRequestTypeInbuiltAttrModification) {
|
||||
// 请求修改某个属性
|
||||
[LKS_InbuiltAttrModificationHandler handleModification:object completion:^(LookinDisplayItemDetail *data, NSError *error) {
|
||||
LookinConnectionResponseAttachment *attachment = [LookinConnectionResponseAttachment new];
|
||||
if (error) {
|
||||
attachment.error = error;
|
||||
} else {
|
||||
attachment.data = data;
|
||||
}
|
||||
[[LKS_ConnectionManager sharedInstance] respond:attachment requestType:requestType tag:tag];
|
||||
}];
|
||||
|
||||
} else if (requestType == LookinRequestTypeCustomAttrModification) {
|
||||
BOOL succ = [LKS_CustomAttrModificationHandler handleModification:object];
|
||||
if (succ) {
|
||||
[self _submitResponseWithData:nil requestType:requestType tag:tag];
|
||||
} else {
|
||||
[self _submitResponseWithError:LookinErr_Inner requestType:requestType tag:tag];
|
||||
}
|
||||
|
||||
} else if (requestType == LookinRequestTypeAttrModificationPatch) {
|
||||
NSArray<LookinStaticAsyncUpdateTask *> *tasks = object;
|
||||
NSUInteger dataTotalCount = tasks.count;
|
||||
[LKS_InbuiltAttrModificationHandler handlePatchWithTasks:tasks block:^(LookinDisplayItemDetail *data) {
|
||||
LookinConnectionResponseAttachment *attrAttachment = [LookinConnectionResponseAttachment new];
|
||||
attrAttachment.data = data;
|
||||
attrAttachment.dataTotalCount = dataTotalCount;
|
||||
attrAttachment.currentDataCount = 1;
|
||||
[[LKS_ConnectionManager sharedInstance] respond:attrAttachment requestType:LookinRequestTypeAttrModificationPatch tag:tag];
|
||||
}];
|
||||
|
||||
} else if (requestType == LookinRequestTypeHierarchyDetails) {
|
||||
NSArray<LookinStaticAsyncUpdateTasksPackage *> *packages = object;
|
||||
NSUInteger responsesDataTotalCount = [packages lookin_reduceInteger:^NSInteger(NSInteger accumulator, NSUInteger idx, LookinStaticAsyncUpdateTasksPackage *package) {
|
||||
accumulator += package.tasks.count;
|
||||
return accumulator;
|
||||
} initialAccumlator:0];
|
||||
|
||||
LKS_HierarchyDetailsHandler *handler = [LKS_HierarchyDetailsHandler new];
|
||||
[self.activeDetailHandlers addObject:handler];
|
||||
|
||||
[handler startWithPackages:packages block:^(NSArray<LookinDisplayItemDetail *> *details) {
|
||||
LookinConnectionResponseAttachment *attachment = [LookinConnectionResponseAttachment new];
|
||||
attachment.data = details;
|
||||
attachment.dataTotalCount = responsesDataTotalCount;
|
||||
attachment.currentDataCount = details.count;
|
||||
[[LKS_ConnectionManager sharedInstance] respond:attachment requestType:LookinRequestTypeHierarchyDetails tag:tag];
|
||||
|
||||
} finishedBlock:^{
|
||||
[self.activeDetailHandlers removeObject:handler];
|
||||
}];
|
||||
|
||||
} else if (requestType == LookinRequestTypeFetchObject) {
|
||||
unsigned long oid = ((NSNumber *)object).unsignedLongValue;
|
||||
NSObject *object = [NSObject lks_objectWithOid:oid];
|
||||
LookinObject *lookinObj = [LookinObject instanceWithObject:object];
|
||||
|
||||
LookinConnectionResponseAttachment *attach = [LookinConnectionResponseAttachment new];
|
||||
attach.data = lookinObj;
|
||||
[[LKS_ConnectionManager sharedInstance] respond:attach requestType:requestType tag:tag];
|
||||
|
||||
} else if (requestType == LookinRequestTypeAllAttrGroups) {
|
||||
unsigned long oid = ((NSNumber *)object).unsignedLongValue;
|
||||
CALayer *layer = (CALayer *)[NSObject lks_objectWithOid:oid];
|
||||
if (![layer isKindOfClass:[CALayer class]]) {
|
||||
[self _submitResponseWithError:LookinErr_ObjNotFound requestType:LookinRequestTypeAllAttrGroups tag:tag];
|
||||
return;
|
||||
}
|
||||
|
||||
NSArray<LookinAttributesGroup *> *list = [LKS_AttrGroupsMaker attrGroupsForLayer:layer];
|
||||
[self _submitResponseWithData:list requestType:LookinRequestTypeAllAttrGroups tag:tag];
|
||||
|
||||
} else if (requestType == LookinRequestTypeAllSelectorNames) {
|
||||
if (![object isKindOfClass:[NSDictionary class]]) {
|
||||
[self _submitResponseWithError:LookinErr_Inner requestType:requestType tag:tag];
|
||||
return;
|
||||
}
|
||||
NSDictionary *params = object;
|
||||
Class targetClass = NSClassFromString(params[@"className"]);
|
||||
BOOL hasArg = [(NSNumber *)params[@"hasArg"] boolValue];
|
||||
if (!targetClass) {
|
||||
NSString *errorMsg = [NSString stringWithFormat:LKS_Localized(@"Didn't find the class named \"%@\". Please input another class and try again."), object];
|
||||
[self _submitResponseWithError:LookinErrorMake(errorMsg, @"") requestType:requestType tag:tag];
|
||||
return;
|
||||
}
|
||||
|
||||
NSArray<NSString *> *selNames = [self _methodNameListForClass:targetClass hasArg:hasArg];
|
||||
[self _submitResponseWithData:selNames requestType:requestType tag:tag];
|
||||
|
||||
} else if (requestType == LookinRequestTypeInvokeMethod) {
|
||||
if (![object isKindOfClass:[NSDictionary class]]) {
|
||||
[self _submitResponseWithError:LookinErr_Inner requestType:requestType tag:tag];
|
||||
return;
|
||||
}
|
||||
NSDictionary *param = object;
|
||||
unsigned long oid = [param[@"oid"] unsignedLongValue];
|
||||
NSString *text = param[@"text"];
|
||||
if (!text.length) {
|
||||
[self _submitResponseWithError:LookinErr_Inner requestType:requestType tag:tag];
|
||||
return;
|
||||
}
|
||||
NSObject *targerObj = [NSObject lks_objectWithOid:oid];
|
||||
if (!targerObj) {
|
||||
[self _submitResponseWithError:LookinErr_ObjNotFound requestType:requestType tag:tag];
|
||||
return;
|
||||
}
|
||||
|
||||
SEL targetSelector = NSSelectorFromString(text);
|
||||
if (targetSelector && [targerObj respondsToSelector:targetSelector]) {
|
||||
NSString *resultDescription;
|
||||
NSObject *resultObject;
|
||||
NSError *error;
|
||||
[self _handleInvokeWithObject:targerObj selector:targetSelector resultDescription:&resultDescription resultObject:&resultObject error:&error];
|
||||
if (error) {
|
||||
[self _submitResponseWithError:error requestType:requestType tag:tag];
|
||||
return;
|
||||
}
|
||||
NSMutableDictionary *responseData = [NSMutableDictionary dictionaryWithCapacity:2];
|
||||
if (resultDescription) {
|
||||
responseData[@"description"] = resultDescription;
|
||||
}
|
||||
if (resultObject) {
|
||||
responseData[@"object"] = resultObject;
|
||||
}
|
||||
[self _submitResponseWithData:responseData requestType:requestType tag:tag];
|
||||
} else {
|
||||
NSString *errMsg = [NSString stringWithFormat:LKS_Localized(@"%@ doesn't have an instance method called \"%@\"."), NSStringFromClass(targerObj.class), text];
|
||||
[self _submitResponseWithError:LookinErrorMake(errMsg, @"") requestType:requestType tag:tag];
|
||||
}
|
||||
|
||||
} else if (requestType == LookinPush_CanceHierarchyDetails) {
|
||||
[self.activeDetailHandlers enumerateObjectsUsingBlock:^(LKS_HierarchyDetailsHandler * _Nonnull handler, BOOL * _Nonnull stop) {
|
||||
[handler cancel];
|
||||
}];
|
||||
[self.activeDetailHandlers removeAllObjects];
|
||||
|
||||
} else if (requestType == LookinRequestTypeFetchImageViewImage) {
|
||||
if (![object isKindOfClass:[NSNumber class]]) {
|
||||
[self _submitResponseWithError:LookinErr_Inner requestType:requestType tag:tag];
|
||||
return;
|
||||
}
|
||||
unsigned long imageViewOid = [(NSNumber *)object unsignedLongValue];
|
||||
UIImageView *imageView = (UIImageView *)[NSObject lks_objectWithOid:imageViewOid];
|
||||
if (!imageView) {
|
||||
[self _submitResponseWithError:LookinErr_ObjNotFound requestType:requestType tag:tag];
|
||||
return;
|
||||
}
|
||||
if (![imageView isKindOfClass:[UIImageView class]]) {
|
||||
[self _submitResponseWithError:LookinErr_Inner requestType:requestType tag:tag];
|
||||
return;
|
||||
}
|
||||
UIImage *image = imageView.image;
|
||||
NSData *imageData = [image lookin_data];
|
||||
[self _submitResponseWithData:imageData requestType:requestType tag:tag];
|
||||
|
||||
} else if (requestType == LookinRequestTypeModifyRecognizerEnable) {
|
||||
if (![object isKindOfClass:[NSDictionary class]]) {
|
||||
[self _submitResponseWithError:LookinErr_Inner requestType:requestType tag:tag];
|
||||
return;
|
||||
}
|
||||
NSDictionary<NSString *, NSNumber *> *params = object;
|
||||
unsigned long recognizerOid = ((NSNumber *)params[@"oid"]).unsignedLongValue;
|
||||
BOOL shouldBeEnabled = ((NSNumber *)params[@"enable"]).boolValue;
|
||||
|
||||
UIGestureRecognizer *recognizer = (UIGestureRecognizer *)[NSObject lks_objectWithOid:recognizerOid];
|
||||
if (!recognizer) {
|
||||
[self _submitResponseWithError:LookinErr_ObjNotFound requestType:requestType tag:tag];
|
||||
return;
|
||||
}
|
||||
if (![recognizer isKindOfClass:[UIGestureRecognizer class]]) {
|
||||
[self _submitResponseWithError:LookinErr_Inner requestType:requestType tag:tag];
|
||||
return;
|
||||
}
|
||||
recognizer.enabled = shouldBeEnabled;
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
// dispatch 以确保拿到的 enabled 是比较新的
|
||||
[self _submitResponseWithData:@(recognizer.enabled) requestType:requestType tag:tag];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)_methodNameListForClass:(Class)aClass hasArg:(BOOL)hasArg {
|
||||
NSSet<NSString *> *prefixesToVoid = [NSSet setWithObjects:@"_", @"CA_", @"cpl", @"mf_", @"vs_", @"pep_", @"isNS", @"avkit_", @"PG_", @"px_", @"pl_", @"nsli_", @"pu_", @"pxg_", nil];
|
||||
NSMutableArray<NSString *> *array = [NSMutableArray array];
|
||||
|
||||
Class currentClass = aClass;
|
||||
while (currentClass) {
|
||||
NSString *className = NSStringFromClass(currentClass);
|
||||
BOOL isSystemClass = ([className hasPrefix:@"UI"] || [className hasPrefix:@"CA"] || [className hasPrefix:@"NS"]);
|
||||
|
||||
unsigned int methodCount = 0;
|
||||
Method *methods = class_copyMethodList(currentClass, &methodCount);
|
||||
for (unsigned int i = 0; i < methodCount; i++) {
|
||||
NSString *selName = NSStringFromSelector(method_getName(methods[i]));
|
||||
|
||||
if (!hasArg && [selName containsString:@":"]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isSystemClass) {
|
||||
BOOL invalid = [prefixesToVoid lookin_any:^BOOL(NSString *prefix) {
|
||||
return [selName hasPrefix:prefix];
|
||||
}];
|
||||
if (invalid) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (selName.length && ![array containsObject:selName]) {
|
||||
[array addObject:selName];
|
||||
}
|
||||
}
|
||||
if (methods) free(methods);
|
||||
currentClass = [currentClass superclass];
|
||||
}
|
||||
|
||||
return [array lookin_sortedArrayByStringLength];
|
||||
}
|
||||
|
||||
- (void)_handleInvokeWithObject:(NSObject *)obj selector:(SEL)selector resultDescription:(NSString **)description resultObject:(LookinObject **)resultObject error:(NSError **)error {
|
||||
NSMethodSignature *signature = [obj methodSignatureForSelector:selector];
|
||||
if (signature.numberOfArguments > 2) {
|
||||
*error = LookinErrorMake(LKS_Localized(@"Lookin doesn't support invoking methods with arguments yet."), @"");
|
||||
return;
|
||||
}
|
||||
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||
[invocation setTarget:obj];
|
||||
[invocation setSelector:selector];
|
||||
[invocation invoke];
|
||||
|
||||
const char *returnType = [signature methodReturnType];
|
||||
|
||||
|
||||
if (strcmp(returnType, @encode(void)) == 0) {
|
||||
//void, do nothing
|
||||
*description = LookinStringFlag_VoidReturn;
|
||||
|
||||
} else if (strcmp(returnType, @encode(char)) == 0) {
|
||||
char charValue;
|
||||
[invocation getReturnValue:&charValue];
|
||||
*description = [NSString stringWithFormat:@"%@", @(charValue)];
|
||||
|
||||
} else if (strcmp(returnType, @encode(int)) == 0) {
|
||||
int intValue;
|
||||
[invocation getReturnValue:&intValue];
|
||||
if (intValue == INT_MAX) {
|
||||
*description = @"INT_MAX";
|
||||
} else if (intValue == INT_MIN) {
|
||||
*description = @"INT_MIN";
|
||||
} else {
|
||||
*description = [NSString stringWithFormat:@"%@", @(intValue)];
|
||||
}
|
||||
|
||||
} else if (strcmp(returnType, @encode(short)) == 0) {
|
||||
short shortValue;
|
||||
[invocation getReturnValue:&shortValue];
|
||||
if (shortValue == SHRT_MAX) {
|
||||
*description = @"SHRT_MAX";
|
||||
} else if (shortValue == SHRT_MIN) {
|
||||
*description = @"SHRT_MIN";
|
||||
} else {
|
||||
*description = [NSString stringWithFormat:@"%@", @(shortValue)];
|
||||
}
|
||||
|
||||
} else if (strcmp(returnType, @encode(long)) == 0) {
|
||||
long longValue;
|
||||
[invocation getReturnValue:&longValue];
|
||||
if (longValue == NSNotFound) {
|
||||
*description = @"NSNotFound";
|
||||
} else if (longValue == LONG_MAX) {
|
||||
*description = @"LONG_MAX";
|
||||
} else if (longValue == LONG_MIN) {
|
||||
*description = @"LONG_MAX";
|
||||
} else {
|
||||
*description = [NSString stringWithFormat:@"%@", @(longValue)];
|
||||
}
|
||||
|
||||
} else if (strcmp(returnType, @encode(long long)) == 0) {
|
||||
long long longLongValue;
|
||||
[invocation getReturnValue:&longLongValue];
|
||||
if (longLongValue == LLONG_MAX) {
|
||||
*description = @"LLONG_MAX";
|
||||
} else if (longLongValue == LLONG_MIN) {
|
||||
*description = @"LLONG_MIN";
|
||||
} else {
|
||||
*description = [NSString stringWithFormat:@"%@", @(longLongValue)];
|
||||
}
|
||||
|
||||
} else if (strcmp(returnType, @encode(unsigned char)) == 0) {
|
||||
unsigned char ucharValue;
|
||||
[invocation getReturnValue:&ucharValue];
|
||||
if (ucharValue == UCHAR_MAX) {
|
||||
*description = @"UCHAR_MAX";
|
||||
} else {
|
||||
*description = [NSString stringWithFormat:@"%@", @(ucharValue)];
|
||||
}
|
||||
|
||||
} else if (strcmp(returnType, @encode(unsigned int)) == 0) {
|
||||
unsigned int uintValue;
|
||||
[invocation getReturnValue:&uintValue];
|
||||
if (uintValue == UINT_MAX) {
|
||||
*description = @"UINT_MAX";
|
||||
} else {
|
||||
*description = [NSString stringWithFormat:@"%@", @(uintValue)];
|
||||
}
|
||||
|
||||
} else if (strcmp(returnType, @encode(unsigned short)) == 0) {
|
||||
unsigned short ushortValue;
|
||||
[invocation getReturnValue:&ushortValue];
|
||||
if (ushortValue == USHRT_MAX) {
|
||||
*description = @"USHRT_MAX";
|
||||
} else {
|
||||
*description = [NSString stringWithFormat:@"%@", @(ushortValue)];
|
||||
}
|
||||
|
||||
} else if (strcmp(returnType, @encode(unsigned long)) == 0) {
|
||||
unsigned long ulongValue;
|
||||
[invocation getReturnValue:&ulongValue];
|
||||
if (ulongValue == ULONG_MAX) {
|
||||
*description = @"ULONG_MAX";
|
||||
} else {
|
||||
*description = [NSString stringWithFormat:@"%@", @(ulongValue)];
|
||||
}
|
||||
|
||||
} else if (strcmp(returnType, @encode(unsigned long long)) == 0) {
|
||||
unsigned long long ulongLongValue;
|
||||
[invocation getReturnValue:&ulongLongValue];
|
||||
if (ulongLongValue == ULONG_LONG_MAX) {
|
||||
*description = @"ULONG_LONG_MAX";
|
||||
} else {
|
||||
*description = [NSString stringWithFormat:@"%@", @(ulongLongValue)];
|
||||
}
|
||||
|
||||
} else if (strcmp(returnType, @encode(float)) == 0) {
|
||||
float floatValue;
|
||||
[invocation getReturnValue:&floatValue];
|
||||
if (floatValue == FLT_MAX) {
|
||||
*description = @"FLT_MAX";
|
||||
} else if (floatValue == FLT_MIN) {
|
||||
*description = @"FLT_MIN";
|
||||
} else {
|
||||
*description = [NSString stringWithFormat:@"%@", @(floatValue)];
|
||||
}
|
||||
|
||||
} else if (strcmp(returnType, @encode(double)) == 0) {
|
||||
double doubleValue;
|
||||
[invocation getReturnValue:&doubleValue];
|
||||
if (doubleValue == DBL_MAX) {
|
||||
*description = @"DBL_MAX";
|
||||
} else if (doubleValue == DBL_MIN) {
|
||||
*description = @"DBL_MIN";
|
||||
} else {
|
||||
*description = [NSString stringWithFormat:@"%@", @(doubleValue)];
|
||||
}
|
||||
|
||||
} else if (strcmp(returnType, @encode(BOOL)) == 0) {
|
||||
BOOL boolValue;
|
||||
[invocation getReturnValue:&boolValue];
|
||||
*description = boolValue ? @"YES" : @"NO";
|
||||
|
||||
} else if (strcmp(returnType, @encode(SEL)) == 0) {
|
||||
SEL selValue;
|
||||
[invocation getReturnValue:&selValue];
|
||||
*description = [NSString stringWithFormat:@"SEL(%@)", NSStringFromSelector(selValue)];
|
||||
|
||||
} else if (strcmp(returnType, @encode(Class)) == 0) {
|
||||
Class classValue;
|
||||
[invocation getReturnValue:&classValue];
|
||||
*description = [NSString stringWithFormat:@"<%@>", NSStringFromClass(classValue)];
|
||||
|
||||
} else if (strcmp(returnType, @encode(CGPoint)) == 0) {
|
||||
CGPoint targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
*description = NSStringFromCGPoint(targetValue);
|
||||
|
||||
} else if (strcmp(returnType, @encode(CGVector)) == 0) {
|
||||
CGVector targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
*description = NSStringFromCGVector(targetValue);
|
||||
|
||||
} else if (strcmp(returnType, @encode(CGSize)) == 0) {
|
||||
CGSize targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
*description = NSStringFromCGSize(targetValue);
|
||||
|
||||
} else if (strcmp(returnType, @encode(CGRect)) == 0) {
|
||||
CGRect rectValue;
|
||||
[invocation getReturnValue:&rectValue];
|
||||
*description = NSStringFromCGRect(rectValue);
|
||||
|
||||
} else if (strcmp(returnType, @encode(CGAffineTransform)) == 0) {
|
||||
CGAffineTransform rectValue;
|
||||
[invocation getReturnValue:&rectValue];
|
||||
*description = NSStringFromCGAffineTransform(rectValue);
|
||||
|
||||
} else if (strcmp(returnType, @encode(UIEdgeInsets)) == 0) {
|
||||
UIEdgeInsets targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
*description = NSStringFromUIEdgeInsets(targetValue);
|
||||
|
||||
} else if (strcmp(returnType, @encode(UIOffset)) == 0) {
|
||||
UIOffset targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
*description = NSStringFromUIOffset(targetValue);
|
||||
|
||||
} else {
|
||||
if (@available(iOS 11.0, tvOS 11.0, *)) {
|
||||
if (strcmp(returnType, @encode(NSDirectionalEdgeInsets)) == 0) {
|
||||
NSDirectionalEdgeInsets targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
*description = NSStringFromDirectionalEdgeInsets(targetValue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NSString *argType_string = [[NSString alloc] lookin_safeInitWithUTF8String:returnType];
|
||||
if ([argType_string hasPrefix:@"@"] || [argType_string hasPrefix:@"^{"]) {
|
||||
__unsafe_unretained id returnObjValue;
|
||||
[invocation getReturnValue:&returnObjValue];
|
||||
|
||||
if (returnObjValue) {
|
||||
*description = [NSString stringWithFormat:@"%@", returnObjValue];
|
||||
|
||||
LookinObject *parsedLookinObj = [LookinObject instanceWithObject:returnObjValue];
|
||||
*resultObject = parsedLookinObj;
|
||||
} else {
|
||||
*description = @"nil";
|
||||
}
|
||||
} else {
|
||||
*description = [NSString stringWithFormat:LKS_Localized(@"%@ was invoked successfully, but Lookin can't parse the return value:%@"), NSStringFromSelector(selector), argType_string];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_submitResponseWithError:(NSError *)error requestType:(uint32_t)requestType tag:(uint32_t)tag {
|
||||
LookinConnectionResponseAttachment *attachment = [LookinConnectionResponseAttachment new];
|
||||
attachment.error = error;
|
||||
[[LKS_ConnectionManager sharedInstance] respond:attachment requestType:requestType tag:tag];
|
||||
}
|
||||
|
||||
- (void)_submitResponseWithData:(NSObject *)data requestType:(uint32_t)requestType tag:(uint32_t)tag {
|
||||
LookinConnectionResponseAttachment *attachment = [LookinConnectionResponseAttachment new];
|
||||
attachment.data = data;
|
||||
[[LKS_ConnectionManager sharedInstance] respond:attachment requestType:requestType tag:tag];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
26
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_AttrModificationPatchHandler.h
generated
Normal file
26
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_AttrModificationPatchHandler.h
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_AttrModificationPatchHandler.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/6/12.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class LookinDisplayItemDetail;
|
||||
|
||||
@interface LKS_AttrModificationPatchHandler : NSObject
|
||||
|
||||
/**
|
||||
@param oids 数组内 idx 较小的应该为 displayItems 里的 subItem,idx 较大的应该为 superItem
|
||||
@param lowImageQuality 是否采用低图像质量
|
||||
@param block 该 block 会被多次调用,其中 tasksTotalCount 是总的调用次数(即可被用来作为 TotalResponseCount)
|
||||
*/
|
||||
+ (void)handleLayerOids:(NSArray<NSNumber *> *)oids lowImageQuality:(BOOL)lowImageQuality block:(void (^)(LookinDisplayItemDetail *detail, NSUInteger tasksTotalCount, NSError *error))block;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
51
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_AttrModificationPatchHandler.m
generated
Normal file
51
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_AttrModificationPatchHandler.m
generated
Normal file
@@ -0,0 +1,51 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_AttrModificationPatchHandler.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/6/12.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LKS_AttrModificationPatchHandler.h"
|
||||
#import "LookinDisplayItemDetail.h"
|
||||
#import "LookinServerDefines.h"
|
||||
|
||||
@implementation LKS_AttrModificationPatchHandler
|
||||
|
||||
+ (void)handleLayerOids:(NSArray<NSNumber *> *)oids lowImageQuality:(BOOL)lowImageQuality block:(void (^)(LookinDisplayItemDetail *detail, NSUInteger tasksTotalCount, NSError *error))block {
|
||||
if (!block) {
|
||||
NSAssert(NO, @"");
|
||||
return;
|
||||
}
|
||||
if (![oids isKindOfClass:[NSArray class]]) {
|
||||
block(nil, 1, LookinErr_Inner);
|
||||
return;
|
||||
}
|
||||
|
||||
[oids enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
unsigned long oid = [obj unsignedLongValue];
|
||||
LookinDisplayItemDetail *detail = [LookinDisplayItemDetail new];
|
||||
detail.displayItemOid = oid;
|
||||
|
||||
CALayer *layer = (CALayer *)[NSObject lks_objectWithOid:oid];
|
||||
if (![layer isKindOfClass:[CALayer class]]) {
|
||||
block(nil, idx + 1, LookinErr_ObjNotFound);
|
||||
*stop = YES;
|
||||
return;
|
||||
}
|
||||
|
||||
if (idx == 0) {
|
||||
detail.soloScreenshot = [layer lks_soloScreenshotWithLowQuality:lowImageQuality];
|
||||
detail.groupScreenshot = [layer lks_groupScreenshotWithLowQuality:lowImageQuality];
|
||||
} else {
|
||||
detail.groupScreenshot = [layer lks_groupScreenshotWithLowQuality:lowImageQuality];
|
||||
}
|
||||
block(detail, oids.count, nil);
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
19
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_CustomAttrModificationHandler.h
generated
Normal file
19
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_CustomAttrModificationHandler.h
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
//
|
||||
// LKS_CustomAttrModificationHandler.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by likaimacbookhome on 2023/11/4.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "LookinCustomAttrModification.h"
|
||||
|
||||
@interface LKS_CustomAttrModificationHandler : NSObject
|
||||
|
||||
/// 返回值表示是否修改成功(有成功调用 setter block 就算成功)
|
||||
+ (BOOL)handleModification:(LookinCustomAttrModification *)modification;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
155
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_CustomAttrModificationHandler.m
generated
Normal file
155
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_CustomAttrModificationHandler.m
generated
Normal file
@@ -0,0 +1,155 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
//
|
||||
// LKS_CustomAttrModificationHandler.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by likaimacbookhome on 2023/11/4.
|
||||
//
|
||||
|
||||
#import "LKS_CustomAttrModificationHandler.h"
|
||||
#import "LKS_CustomAttrSetterManager.h"
|
||||
#import "UIColor+LookinServer.h"
|
||||
|
||||
@implementation LKS_CustomAttrModificationHandler
|
||||
|
||||
+ (BOOL)handleModification:(LookinCustomAttrModification *)modification {
|
||||
if (!modification || modification.customSetterID.length == 0) {
|
||||
return NO;
|
||||
}
|
||||
switch (modification.attrType) {
|
||||
case LookinAttrTypeNSString: {
|
||||
NSString *newValue = modification.value;
|
||||
if (newValue != nil && ![newValue isKindOfClass:[NSString class]]) {
|
||||
// nil 是合法的
|
||||
return NO;
|
||||
}
|
||||
LKS_StringSetter setter = [[LKS_CustomAttrSetterManager sharedInstance] getStringSetterWithID:modification.customSetterID];
|
||||
if (!setter) {
|
||||
return NO;
|
||||
}
|
||||
setter(newValue);
|
||||
return YES;
|
||||
}
|
||||
|
||||
case LookinAttrTypeDouble: {
|
||||
NSNumber *newValue = modification.value;
|
||||
if (![newValue isKindOfClass:[NSNumber class]]) {
|
||||
return NO;
|
||||
}
|
||||
LKS_NumberSetter setter = [[LKS_CustomAttrSetterManager sharedInstance] getNumberSetterWithID:modification.customSetterID];
|
||||
if (!setter) {
|
||||
return NO;
|
||||
}
|
||||
setter(newValue);
|
||||
return YES;
|
||||
}
|
||||
|
||||
case LookinAttrTypeBOOL: {
|
||||
NSNumber *newValue = modification.value;
|
||||
if (![newValue isKindOfClass:[NSNumber class]]) {
|
||||
return NO;
|
||||
}
|
||||
LKS_BoolSetter setter = [[LKS_CustomAttrSetterManager sharedInstance] getBoolSetterWithID:modification.customSetterID];
|
||||
if (!setter) {
|
||||
return NO;
|
||||
}
|
||||
setter(newValue.boolValue);
|
||||
return YES;
|
||||
}
|
||||
|
||||
case LookinAttrTypeUIColor: {
|
||||
LKS_ColorSetter setter = [[LKS_CustomAttrSetterManager sharedInstance] getColorSetterWithID:modification.customSetterID];
|
||||
if (!setter) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSArray<NSNumber *> *newValue = modification.value;
|
||||
if (newValue == nil) {
|
||||
// nil 是合法的
|
||||
setter(nil);
|
||||
return YES;
|
||||
}
|
||||
if (![newValue isKindOfClass:[NSArray class]]) {
|
||||
return NO;
|
||||
}
|
||||
UIColor *color = [UIColor lks_colorFromRGBAComponents:newValue];
|
||||
if (!color) {
|
||||
return NO;
|
||||
}
|
||||
setter(color);
|
||||
return YES;
|
||||
}
|
||||
|
||||
case LookinAttrTypeEnumString: {
|
||||
NSString *newValue = modification.value;
|
||||
if (![newValue isKindOfClass:[NSString class]]) {
|
||||
return NO;
|
||||
}
|
||||
LKS_EnumSetter setter = [[LKS_CustomAttrSetterManager sharedInstance] getEnumSetterWithID:modification.customSetterID];
|
||||
if (!setter) {
|
||||
return NO;
|
||||
}
|
||||
setter(newValue);
|
||||
return YES;
|
||||
}
|
||||
|
||||
case LookinAttrTypeCGRect: {
|
||||
NSValue *newValue = modification.value;
|
||||
if (![newValue isKindOfClass:[NSValue class]]) {
|
||||
return NO;
|
||||
}
|
||||
LKS_RectSetter setter = [[LKS_CustomAttrSetterManager sharedInstance] getRectSetterWithID:modification.customSetterID];
|
||||
if (!setter) {
|
||||
return NO;
|
||||
}
|
||||
setter(newValue.CGRectValue);
|
||||
return YES;
|
||||
}
|
||||
|
||||
case LookinAttrTypeCGSize: {
|
||||
NSValue *newValue = modification.value;
|
||||
if (![newValue isKindOfClass:[NSValue class]]) {
|
||||
return NO;
|
||||
}
|
||||
LKS_SizeSetter setter = [[LKS_CustomAttrSetterManager sharedInstance] getSizeSetterWithID:modification.customSetterID];
|
||||
if (!setter) {
|
||||
return NO;
|
||||
}
|
||||
setter(newValue.CGSizeValue);
|
||||
return YES;
|
||||
}
|
||||
|
||||
case LookinAttrTypeCGPoint: {
|
||||
NSValue *newValue = modification.value;
|
||||
if (![newValue isKindOfClass:[NSValue class]]) {
|
||||
return NO;
|
||||
}
|
||||
LKS_PointSetter setter = [[LKS_CustomAttrSetterManager sharedInstance] getPointSetterWithID:modification.customSetterID];
|
||||
if (!setter) {
|
||||
return NO;
|
||||
}
|
||||
setter(newValue.CGPointValue);
|
||||
return YES;
|
||||
}
|
||||
|
||||
case LookinAttrTypeUIEdgeInsets: {
|
||||
NSValue *newValue = modification.value;
|
||||
if (![newValue isKindOfClass:[NSValue class]]) {
|
||||
return NO;
|
||||
}
|
||||
LKS_InsetsSetter setter = [[LKS_CustomAttrSetterManager sharedInstance] getInsetsSetterWithID:modification.customSetterID];
|
||||
if (!setter) {
|
||||
return NO;
|
||||
}
|
||||
setter(newValue.UIEdgeInsetsValue);
|
||||
return YES;
|
||||
}
|
||||
|
||||
default:
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
30
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_HierarchyDetailsHandler.h
generated
Normal file
30
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_HierarchyDetailsHandler.h
generated
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_HierarchyDetailsHandler.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/6/20.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class LookinDisplayItemDetail, LookinStaticAsyncUpdateTasksPackage;
|
||||
|
||||
typedef void (^LKS_HierarchyDetailsHandler_ProgressBlock)(NSArray<LookinDisplayItemDetail *> *details);
|
||||
typedef void (^LKS_HierarchyDetailsHandler_FinishBlock)(void);
|
||||
|
||||
@interface LKS_HierarchyDetailsHandler : NSObject
|
||||
|
||||
/// packages 会按照 idx 从小到大的顺序被执行
|
||||
/// 全部任务完成时,finishBlock 会被调用
|
||||
/// 如果调用了 cancel,则 finishBlock 不会被执行
|
||||
- (void)startWithPackages:(NSArray<LookinStaticAsyncUpdateTasksPackage *> *)packages block:(LKS_HierarchyDetailsHandler_ProgressBlock)progressBlock finishedBlock:(LKS_HierarchyDetailsHandler_FinishBlock)finishBlock;
|
||||
|
||||
/// 取消所有任务
|
||||
- (void)cancel;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
148
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_HierarchyDetailsHandler.m
generated
Normal file
148
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_HierarchyDetailsHandler.m
generated
Normal file
@@ -0,0 +1,148 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_HierarchyDetailsHandler.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/6/20.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LKS_HierarchyDetailsHandler.h"
|
||||
#import "LookinDisplayItemDetail.h"
|
||||
#import "LKS_AttrGroupsMaker.h"
|
||||
#import "LookinStaticAsyncUpdateTask.h"
|
||||
#import "LKS_ConnectionManager.h"
|
||||
#import "LookinServerDefines.h"
|
||||
#import "LKS_CustomAttrGroupsMaker.h"
|
||||
#import "LKS_HierarchyDisplayItemsMaker.h"
|
||||
|
||||
@interface LKS_HierarchyDetailsHandler ()
|
||||
|
||||
@property(nonatomic, strong) NSMutableArray<LookinStaticAsyncUpdateTasksPackage *> *taskPackages;
|
||||
/// 标识哪些 oid 已经拉取过 attrGroups 了
|
||||
@property(nonatomic, strong) NSMutableSet<NSNumber *> *attrGroupsSyncedOids;
|
||||
|
||||
@property(nonatomic, copy) LKS_HierarchyDetailsHandler_ProgressBlock progressBlock;
|
||||
@property(nonatomic, copy) LKS_HierarchyDetailsHandler_FinishBlock finishBlock;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LKS_HierarchyDetailsHandler
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleConnectionDidEnd:) name:LKS_ConnectionDidEndNotificationName object:nil];
|
||||
|
||||
self.attrGroupsSyncedOids = [NSMutableSet set];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)startWithPackages:(NSArray<LookinStaticAsyncUpdateTasksPackage *> *)packages block:(LKS_HierarchyDetailsHandler_ProgressBlock)progressBlock finishedBlock:(LKS_HierarchyDetailsHandler_FinishBlock)finishBlock {
|
||||
if (!progressBlock || !finishBlock) {
|
||||
NSAssert(NO, @"");
|
||||
return;
|
||||
}
|
||||
if (!packages.count) {
|
||||
finishBlock();
|
||||
return;
|
||||
}
|
||||
self.taskPackages = [packages mutableCopy];
|
||||
self.progressBlock = progressBlock;
|
||||
self.finishBlock = finishBlock;
|
||||
|
||||
[UIView lks_rebuildGlobalInvolvedRawConstraints];
|
||||
|
||||
[self _dequeueAndHandlePackage];
|
||||
}
|
||||
|
||||
- (void)cancel {
|
||||
[self.taskPackages removeAllObjects];
|
||||
}
|
||||
|
||||
- (void)_dequeueAndHandlePackage {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
LookinStaticAsyncUpdateTasksPackage *package = self.taskPackages.firstObject;
|
||||
if (!package) {
|
||||
self.finishBlock();
|
||||
return;
|
||||
}
|
||||
// NSLog(@"LookinServer - will handle tasks, count: %@", @(tasks.count));
|
||||
NSArray<LookinDisplayItemDetail *> *details = [package.tasks lookin_map:^id(NSUInteger idx, LookinStaticAsyncUpdateTask *task) {
|
||||
LookinDisplayItemDetail *itemDetail = [LookinDisplayItemDetail new];
|
||||
itemDetail.displayItemOid = task.oid;
|
||||
|
||||
id object = [NSObject lks_objectWithOid:task.oid];
|
||||
if (!object || ![object isKindOfClass:[CALayer class]]) {
|
||||
itemDetail.failureCode = -1;
|
||||
return itemDetail;
|
||||
}
|
||||
|
||||
CALayer *layer = object;
|
||||
|
||||
if (task.taskType == LookinStaticAsyncUpdateTaskTypeSoloScreenshot) {
|
||||
UIImage *image = [layer lks_soloScreenshotWithLowQuality:NO];
|
||||
itemDetail.soloScreenshot = image;
|
||||
} else if (task.taskType == LookinStaticAsyncUpdateTaskTypeGroupScreenshot) {
|
||||
UIImage *image = [layer lks_groupScreenshotWithLowQuality:NO];
|
||||
itemDetail.groupScreenshot = image;
|
||||
}
|
||||
|
||||
BOOL shouldMakeAttr = [self queryIfShouldMakeAttrsFromTask:task];
|
||||
if (shouldMakeAttr) {
|
||||
itemDetail.attributesGroupList = [LKS_AttrGroupsMaker attrGroupsForLayer:layer];
|
||||
|
||||
NSString *version = task.clientReadableVersion;
|
||||
if (version.length > 0 && [version lookin_numbericOSVersion] >= 10004) {
|
||||
LKS_CustomAttrGroupsMaker *maker = [[LKS_CustomAttrGroupsMaker alloc] initWithLayer:layer];
|
||||
[maker execute];
|
||||
itemDetail.customAttrGroupList = [maker getGroups];
|
||||
itemDetail.customDisplayTitle = [maker getCustomDisplayTitle];
|
||||
itemDetail.danceUISource = [maker getDanceUISource];
|
||||
}
|
||||
[self.attrGroupsSyncedOids addObject:@(task.oid)];
|
||||
}
|
||||
|
||||
if (task.needBasisVisualInfo) {
|
||||
itemDetail.frameValue = [NSValue valueWithCGRect:layer.frame];
|
||||
itemDetail.boundsValue = [NSValue valueWithCGRect:layer.bounds];
|
||||
itemDetail.hiddenValue = [NSNumber numberWithBool:layer.isHidden];
|
||||
itemDetail.alphaValue = @(layer.opacity);
|
||||
}
|
||||
|
||||
if (task.needSubitems) {
|
||||
itemDetail.subitems = [LKS_HierarchyDisplayItemsMaker subitemsOfLayer:layer];
|
||||
}
|
||||
|
||||
return itemDetail;
|
||||
}];
|
||||
self.progressBlock(details);
|
||||
|
||||
[self.taskPackages removeObjectAtIndex:0];
|
||||
[self _dequeueAndHandlePackage];
|
||||
});
|
||||
}
|
||||
|
||||
- (BOOL)queryIfShouldMakeAttrsFromTask:(LookinStaticAsyncUpdateTask *)task {
|
||||
switch (task.attrRequest) {
|
||||
case LookinDetailUpdateTaskAttrRequest_Automatic: {
|
||||
BOOL alreadyMadeBefore = [self.attrGroupsSyncedOids containsObject:@(task.oid)];
|
||||
return !alreadyMadeBefore;
|
||||
}
|
||||
case LookinDetailUpdateTaskAttrRequest_Need:
|
||||
return YES;
|
||||
case LookinDetailUpdateTaskAttrRequest_NotNeed:
|
||||
return NO;
|
||||
}
|
||||
NSAssert(NO, @"");
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)_handleConnectionDidEnd:(id)obj {
|
||||
[self cancel];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
23
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_InbuiltAttrModificationHandler.h
generated
Normal file
23
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_InbuiltAttrModificationHandler.h
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_InbuiltAttrModificationHandler.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/6/12.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class LookinAttributeModification, LookinDisplayItemDetail, LookinStaticAsyncUpdateTask;
|
||||
|
||||
@interface LKS_InbuiltAttrModificationHandler : NSObject
|
||||
|
||||
+ (void)handleModification:(LookinAttributeModification *)modification completion:(void (^)(LookinDisplayItemDetail *data, NSError *error))completion;
|
||||
|
||||
+ (void)handlePatchWithTasks:(NSArray<LookinStaticAsyncUpdateTask *> *)tasks block:(void (^)(LookinDisplayItemDetail *data))block;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
255
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_InbuiltAttrModificationHandler.m
generated
Normal file
255
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_InbuiltAttrModificationHandler.m
generated
Normal file
@@ -0,0 +1,255 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_InbuiltAttrModificationHandler.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/6/12.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LKS_InbuiltAttrModificationHandler.h"
|
||||
#import "UIColor+LookinServer.h"
|
||||
#import "LookinAttributeModification.h"
|
||||
#import "LKS_AttrGroupsMaker.h"
|
||||
#import "LookinDisplayItemDetail.h"
|
||||
#import "LookinStaticAsyncUpdateTask.h"
|
||||
#import "LookinServerDefines.h"
|
||||
#import "LKS_CustomAttrGroupsMaker.h"
|
||||
|
||||
@implementation LKS_InbuiltAttrModificationHandler
|
||||
|
||||
+ (void)handleModification:(LookinAttributeModification *)modification completion:(void (^)(LookinDisplayItemDetail *data, NSError *error))completion {
|
||||
if (!completion) {
|
||||
NSAssert(NO, @"");
|
||||
return;
|
||||
}
|
||||
if (!modification || ![modification isKindOfClass:[LookinAttributeModification class]]) {
|
||||
completion(nil, LookinErr_Inner);
|
||||
return;
|
||||
}
|
||||
|
||||
NSObject *receiver = [NSObject lks_objectWithOid:modification.targetOid];
|
||||
if (!receiver) {
|
||||
completion(nil, LookinErr_ObjNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
NSMethodSignature *setterSignature = [receiver methodSignatureForSelector:modification.setterSelector];
|
||||
NSInvocation *setterInvocation = [NSInvocation invocationWithMethodSignature:setterSignature];
|
||||
setterInvocation.target = receiver;
|
||||
setterInvocation.selector = modification.setterSelector;
|
||||
|
||||
if (setterSignature.numberOfArguments != 3 || ![receiver respondsToSelector:modification.setterSelector]) {
|
||||
completion(nil, LookinErr_Inner);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (modification.attrType) {
|
||||
case LookinAttrTypeNone:
|
||||
case LookinAttrTypeVoid: {
|
||||
completion(nil, LookinErr_Inner);
|
||||
return;
|
||||
}
|
||||
case LookinAttrTypeChar: {
|
||||
char expectedValue = [(NSNumber *)modification.value charValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeInt:
|
||||
case LookinAttrTypeEnumInt: {
|
||||
int expectedValue = [(NSNumber *)modification.value intValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeShort: {
|
||||
short expectedValue = [(NSNumber *)modification.value shortValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeLong:
|
||||
case LookinAttrTypeEnumLong: {
|
||||
long expectedValue = [(NSNumber *)modification.value longValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeLongLong: {
|
||||
long long expectedValue = [(NSNumber *)modification.value longLongValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeUnsignedChar: {
|
||||
unsigned char expectedValue = [(NSNumber *)modification.value unsignedCharValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeUnsignedInt: {
|
||||
unsigned int expectedValue = [(NSNumber *)modification.value unsignedIntValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeUnsignedShort: {
|
||||
unsigned short expectedValue = [(NSNumber *)modification.value unsignedShortValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeUnsignedLong: {
|
||||
unsigned long expectedValue = [(NSNumber *)modification.value unsignedLongValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeUnsignedLongLong: {
|
||||
unsigned long long expectedValue = [(NSNumber *)modification.value unsignedLongLongValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeFloat: {
|
||||
float expectedValue = [(NSNumber *)modification.value floatValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeDouble: {
|
||||
double expectedValue = [(NSNumber *)modification.value doubleValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeBOOL: {
|
||||
BOOL expectedValue = [(NSNumber *)modification.value boolValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeSel: {
|
||||
SEL expectedValue = NSSelectorFromString(modification.value);
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeClass: {
|
||||
Class expectedValue = NSClassFromString(modification.value);
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeCGPoint: {
|
||||
CGPoint expectedValue = [(NSValue *)modification.value CGPointValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeCGVector: {
|
||||
CGVector expectedValue = [(NSValue *)modification.value CGVectorValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeCGSize: {
|
||||
CGSize expectedValue = [(NSValue *)modification.value CGSizeValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeCGRect: {
|
||||
CGRect expectedValue = [(NSValue *)modification.value CGRectValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeCGAffineTransform: {
|
||||
CGAffineTransform expectedValue = [(NSValue *)modification.value CGAffineTransformValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeUIEdgeInsets: {
|
||||
UIEdgeInsets expectedValue = [(NSValue *)modification.value UIEdgeInsetsValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeUIOffset: {
|
||||
UIOffset expectedValue = [(NSValue *)modification.value UIOffsetValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeCustomObj:
|
||||
case LookinAttrTypeNSString: {
|
||||
NSObject *expectedValue = modification.value;
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
[setterInvocation retainArguments];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeUIColor: {
|
||||
NSArray<NSNumber *> *rgba = modification.value;
|
||||
UIColor *expectedValue = [UIColor lks_colorFromRGBAComponents:rgba];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
[setterInvocation retainArguments];
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
completion(nil, LookinErr_Inner);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NSError *error = nil;
|
||||
@try {
|
||||
[setterInvocation invoke];
|
||||
} @catch (NSException *exception) {
|
||||
NSString *errorMsg = [NSString stringWithFormat:LKS_Localized(@"<%@: %p>: an exception was raised when invoking %@. (%@)"), NSStringFromClass(receiver.class), receiver, NSStringFromSelector(modification.setterSelector), exception.reason];
|
||||
error = [NSError errorWithDomain:LookinErrorDomain code:LookinErrCode_Exception userInfo:@{NSLocalizedDescriptionKey:LKS_Localized(@"The modification may failed."), NSLocalizedRecoverySuggestionErrorKey:errorMsg}];
|
||||
} @finally {
|
||||
|
||||
}
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
CALayer *layer = nil;
|
||||
if ([receiver isKindOfClass:[CALayer class]]) {
|
||||
layer = (CALayer *)receiver;
|
||||
} else if ([receiver isKindOfClass:[UIView class]]) {
|
||||
layer = ((UIView *)receiver).layer;
|
||||
} else {
|
||||
completion(nil, LookinErr_ObjNotFound);
|
||||
return;
|
||||
}
|
||||
// 比如试图更改 frame 时,这个改动很有可能触发用户业务的 relayout,因此这时 dispatch 一下以确保拿到的 attrGroups 数据是最新的
|
||||
LookinDisplayItemDetail *detail = [LookinDisplayItemDetail new];
|
||||
detail.displayItemOid = modification.targetOid;
|
||||
detail.attributesGroupList = [LKS_AttrGroupsMaker attrGroupsForLayer:layer];
|
||||
|
||||
NSString *version = modification.clientReadableVersion;
|
||||
if (version.length > 0 && [version lookin_numbericOSVersion] >= 10004) {
|
||||
LKS_CustomAttrGroupsMaker *maker = [[LKS_CustomAttrGroupsMaker alloc] initWithLayer:layer];
|
||||
[maker execute];
|
||||
detail.customAttrGroupList = [maker getGroups];
|
||||
}
|
||||
|
||||
detail.frameValue = [NSValue valueWithCGRect:layer.frame];
|
||||
detail.boundsValue = [NSValue valueWithCGRect:layer.bounds];
|
||||
detail.hiddenValue = [NSNumber numberWithBool:layer.isHidden];
|
||||
detail.alphaValue = @(layer.opacity);
|
||||
completion(detail, error);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+ (void)handlePatchWithTasks:(NSArray<LookinStaticAsyncUpdateTask *> *)tasks block:(void (^)(LookinDisplayItemDetail *data))block {
|
||||
if (!block) {
|
||||
NSAssert(NO, @"");
|
||||
return;
|
||||
}
|
||||
[tasks enumerateObjectsUsingBlock:^(LookinStaticAsyncUpdateTask * _Nonnull task, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
LookinDisplayItemDetail *itemDetail = [LookinDisplayItemDetail new];
|
||||
itemDetail.displayItemOid = task.oid;
|
||||
id object = [NSObject lks_objectWithOid:task.oid];
|
||||
if (!object || ![object isKindOfClass:[CALayer class]]) {
|
||||
block(itemDetail);
|
||||
return;
|
||||
}
|
||||
|
||||
CALayer *layer = object;
|
||||
if (task.taskType == LookinStaticAsyncUpdateTaskTypeSoloScreenshot) {
|
||||
UIImage *image = [layer lks_soloScreenshotWithLowQuality:NO];
|
||||
itemDetail.soloScreenshot = image;
|
||||
} else if (task.taskType == LookinStaticAsyncUpdateTaskTypeGroupScreenshot) {
|
||||
UIImage *image = [layer lks_groupScreenshotWithLowQuality:NO];
|
||||
itemDetail.groupScreenshot = image;
|
||||
}
|
||||
block(itemDetail);
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
Reference in New Issue
Block a user