diff --git a/ios/Classes/WechatKitPlugin.m b/ios/Classes/WechatKitPlugin.m index ab75f9f..19d7da6 100644 --- a/ios/Classes/WechatKitPlugin.m +++ b/ios/Classes/WechatKitPlugin.m @@ -19,6 +19,10 @@ @implementation WechatKitPlugin { BOOL _isRunning; BOOL _handleInitialWXReqFlag; WechatKitWXReqRunnable _initialWXReqRunnable; + NSURL *_pendingOpenURL; + NSUserActivity *_pendingUserActivity; + NSString *_cachedLaunchReqType; + NSDictionary *_cachedLaunchReqData; } + (void)registerWithRegistrar:(NSObject *)registrar { @@ -34,7 +38,6 @@ + (void)registerWithRegistrar:(NSObject *)registrar { - (instancetype)init { self = [super init]; if (self) { - // _channel = channel; _qrauth = [[WechatAuthSDK alloc] init]; _qrauth.delegate = self; _isRunning = NO; @@ -54,7 +57,19 @@ - (void)handleMethodCall:(FlutterMethodCall *)call NSString *universalLink = call.arguments[@"universalLink"]; [WXApi registerApp:appId universalLink:universalLink]; _isRunning = YES; + [self handlePendingWXOpenIfNeeded]; result(nil); + } else if ([@"getLaunchReq" isEqualToString:call.method]) { + if (_cachedLaunchReqType != nil && _cachedLaunchReqData != nil) { + result(@{ + @"type": _cachedLaunchReqType, + @"data": _cachedLaunchReqData, + }); + _cachedLaunchReqType = nil; + _cachedLaunchReqData = nil; + } else { + result(nil); + } } else if ([@"handleInitialWXReq" isEqualToString:call.method]) { if (!_handleInitialWXReqFlag) { _handleInitialWXReqFlag = YES; @@ -114,15 +129,51 @@ - (void)handleMethodCall:(FlutterMethodCall *)call } } +- (void)handlePendingWXOpenIfNeeded { + if (_initialWXReqRunnable != nil) { + return; + } + if (_pendingOpenURL != nil) { + BOOL ok = [WXApi handleOpenURL:_pendingOpenURL delegate:self]; + if (!ok && _channel != nil) { + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + [dictionary setValue:_pendingOpenURL.absoluteString forKey:@"messageExt"]; + [self cacheLaunchReq:@"launch" data:dictionary]; + [_channel invokeMethod:@"onLaunchFromWXReq" arguments:dictionary]; + } + _pendingOpenURL = nil; + } + if (_pendingUserActivity != nil) { + BOOL ok = [WXApi handleOpenUniversalLink:_pendingUserActivity delegate:self]; + if (!ok && _channel != nil) { + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + if ([_pendingUserActivity.webpageURL absoluteString] != nil) { + [dictionary setValue:_pendingUserActivity.webpageURL.absoluteString forKey:@"messageExt"]; + } + [self cacheLaunchReq:@"launch" data:dictionary]; + [_channel invokeMethod:@"onLaunchFromWXReq" arguments:dictionary]; + } + _pendingUserActivity = nil; + } +} + +- (void)cacheLaunchReq:(NSString *)type data:(NSDictionary *)data { + if (type == nil || data == nil) { + return; + } + _cachedLaunchReqType = [type copy]; + _cachedLaunchReqData = [data copy]; +} + - (void)handleAuthCall:(FlutterMethodCall *)call result:(FlutterResult)result { SendAuthReq *req = [[SendAuthReq alloc] init]; req.scope = call.arguments[@"scope"]; req.state = call.arguments[@"state"]; NSNumber *type = call.arguments[@"type"]; + if ([type intValue] == 0) { [WXApi sendReq:req completion:^(BOOL success){ - // do nothing }]; } else if ([type intValue] == 1) { UIViewController *viewController = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; @@ -130,9 +181,9 @@ - (void)handleAuthCall:(FlutterMethodCall *)call result:(FlutterResult)result { viewController:viewController delegate:self completion:^(BOOL success){ - // do nothing }]; } + result(nil); } @@ -144,6 +195,7 @@ - (void)handleQRAuthCall:(FlutterMethodCall *)call NSString *noncestr = call.arguments[@"noncestr"]; NSString *timestamp = call.arguments[@"timestamp"]; NSString *signature = call.arguments[@"signature"]; + [_qrauth Auth:appId nonceStr:noncestr timeStamp:timestamp @@ -153,6 +205,7 @@ - (void)handleQRAuthCall:(FlutterMethodCall *)call } else if ([@"stopQrauth" isEqualToString:call.method]) { [_qrauth StopAuth]; } + result(nil); } @@ -160,9 +213,9 @@ - (void)handleOpenUrlCall:(FlutterMethodCall *)call result:(FlutterResult)result { OpenWebviewReq *req = [[OpenWebviewReq alloc] init]; req.url = call.arguments[@"url"]; + [WXApi sendReq:req completion:^(BOOL success){ - // do nothing }]; result(nil); } @@ -172,7 +225,6 @@ - (void)handleOpenRankListCall:(FlutterMethodCall *)call OpenRankListReq *req = [[OpenRankListReq alloc] init]; [WXApi sendReq:req completion:^(BOOL success){ - // do nothing }]; result(nil); } @@ -184,9 +236,9 @@ - (void)handleShareTextCall:(FlutterMethodCall *)call req.scene = [scene intValue]; req.bText = YES; req.text = call.arguments[@"text"]; + [WXApi sendReq:req completion:^(BOOL success){ - // do nothing }]; result(nil); } @@ -204,10 +256,10 @@ - (void)handleShareMediaCall:(FlutterMethodCall *)call if (thumbData != nil) { message.thumbData = thumbData.data; } + if ([@"shareImage" isEqualToString:call.method]) { WXImageObject *mediaObject = [WXImageObject object]; - FlutterStandardTypedData *imageData = - call.arguments[@"imageData"]; + FlutterStandardTypedData *imageData = call.arguments[@"imageData"]; if (imageData != nil) { mediaObject.imageData = imageData.data; } else { @@ -230,8 +282,7 @@ - (void)handleShareMediaCall:(FlutterMethodCall *)call message.mediaObject = mediaObject; } else if ([@"shareEmoji" isEqualToString:call.method]) { WXEmoticonObject *mediaObject = [WXEmoticonObject object]; - FlutterStandardTypedData *emojiData = - call.arguments[@"emojiData"]; + FlutterStandardTypedData *emojiData = call.arguments[@"emojiData"]; if (emojiData != nil) { mediaObject.emoticonData = emojiData.data; } else { @@ -245,8 +296,7 @@ - (void)handleShareMediaCall:(FlutterMethodCall *)call mediaObject.musicUrl = call.arguments[@"musicUrl"]; mediaObject.musicDataUrl = call.arguments[@"musicDataUrl"]; mediaObject.musicLowBandUrl = call.arguments[@"musicLowBandUrl"]; - mediaObject.musicLowBandDataUrl = - call.arguments[@"musicLowBandDataUrl"]; + mediaObject.musicLowBandDataUrl = call.arguments[@"musicLowBandDataUrl"]; message.mediaObject = mediaObject; } else if ([@"shareVideo" isEqualToString:call.method]) { WXVideoObject *mediaObject = [WXVideoObject object]; @@ -262,8 +312,7 @@ - (void)handleShareMediaCall:(FlutterMethodCall *)call mediaObject.webpageUrl = call.arguments[@"webpageUrl"]; mediaObject.userName = call.arguments[@"username"]; mediaObject.path = call.arguments[@"path"]; - FlutterStandardTypedData *hdImageData = - call.arguments[@"hdImageData"]; + FlutterStandardTypedData *hdImageData = call.arguments[@"hdImageData"]; if (hdImageData != nil) { mediaObject.hdImageData = hdImageData.data; } @@ -275,10 +324,10 @@ - (void)handleShareMediaCall:(FlutterMethodCall *)call mediaObject.disableForward = disableForward.boolValue; message.mediaObject = mediaObject; } + req.message = message; [WXApi sendReq:req completion:^(BOOL success){ - // do nothing }]; result(nil); } @@ -294,9 +343,9 @@ - (void)handleSubscribeMsgCall:(FlutterMethodCall *)call #endif req.templateId = call.arguments[@"templateId"]; req.reserved = call.arguments[@"reserved"]; + [WXApi sendReq:req completion:^(BOOL success){ - // do nothing }]; result(nil); } @@ -308,9 +357,9 @@ - (void)handleLaunchMiniProgramCall:(FlutterMethodCall *)call req.path = call.arguments[@"path"]; NSNumber *miniProgramType = call.arguments[@"type"]; req.miniProgramType = miniProgramType.unsignedIntegerValue; + [WXApi sendReq:req completion:^(BOOL success){ - // do nothing }]; result(nil); } @@ -320,9 +369,9 @@ - (void)handleOpenCustomerServiceChatCall:(FlutterMethodCall *)call WXOpenCustomerServiceReq *req = [[WXOpenCustomerServiceReq alloc] init]; req.corpid = call.arguments[@"corpId"]; req.url = call.arguments[@"url"]; + [WXApi sendReq:req completion:^(BOOL success){ - // do nothing }]; result(nil); } @@ -333,9 +382,9 @@ - (void)handleOpenBusinessViewCall:(FlutterMethodCall *)call req.businessType = call.arguments[@"businessType"]; req.query = call.arguments[@"query"]; req.extInfo = call.arguments[@"extInfo"]; + [WXApi sendReq:req completion:^(BOOL success){ - // do nothing }]; result(nil); } @@ -350,6 +399,7 @@ - (void)handleOpenBusinessWebviewCall:(FlutterMethodCall *)call req.businessType = [businessType unsignedLongValue]; #endif req.queryInfoDic = call.arguments[@"queryInfo"]; + result(nil); } @@ -363,9 +413,9 @@ - (void)handlePayCall:(FlutterMethodCall *)call result:(FlutterResult)result { req.timeStamp = [timeStamp intValue]; req.package = call.arguments[@"package"]; req.sign = call.arguments[@"sign"]; + [WXApi sendReq:req completion:^(BOOL success){ - // do nothing }]; result(nil); } @@ -374,27 +424,57 @@ - (void)handlePayCall:(FlutterMethodCall *)call result:(FlutterResult)result { #pragma mark - AppDelegate - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url { - return [WXApi handleOpenURL:url delegate:self]; + if (!_isRunning) { + _pendingOpenURL = url; + } + BOOL ret = [WXApi handleOpenURL:url delegate:self]; + return ret; } - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { - return [WXApi handleOpenURL:url delegate:self]; + if (!_isRunning) { + _pendingOpenURL = url; + } + BOOL ret = [WXApi handleOpenURL:url delegate:self]; + return ret; } - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options: (NSDictionary *)options { - return [WXApi handleOpenURL:url delegate:self]; + if (!_isRunning) { + _pendingOpenURL = url; + } + BOOL ret = [WXApi handleOpenURL:url delegate:self]; + if (!ret && _isRunning && _channel != nil) { + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + [dictionary setValue:url.absoluteString forKey:@"messageExt"]; + [self cacheLaunchReq:@"launch" data:dictionary]; + [_channel invokeMethod:@"onLaunchFromWXReq" arguments:dictionary]; + } + return ret; } - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *_Nonnull))restorationHandler { - return [WXApi handleOpenUniversalLink:userActivity delegate:self]; + if (!_isRunning) { + _pendingUserActivity = userActivity; + } + BOOL ret = [WXApi handleOpenUniversalLink:userActivity delegate:self]; + if (!ret && _isRunning && _channel != nil) { + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + if ([userActivity.webpageURL absoluteString] != nil) { + [dictionary setValue:userActivity.webpageURL.absoluteString forKey:@"messageExt"]; + } + [self cacheLaunchReq:@"launch" data:dictionary]; + [_channel invokeMethod:@"onLaunchFromWXReq" arguments:dictionary]; + } + return ret; } #pragma mark - WXApiDelegate @@ -407,6 +487,7 @@ - (void)onReq:(BaseReq *)req { [dictionary setValue:launchFromWXReq.message.messageExt forKey:@"messageExt"]; [dictionary setValue:launchFromWXReq.lang forKey:@"lang"]; [dictionary setValue:launchFromWXReq.country forKey:@"country"]; + [self cacheLaunchReq:@"launch" data:dictionary]; if (_isRunning) { [_channel invokeMethod:@"onLaunchFromWXReq" arguments:dictionary]; } else { @@ -422,6 +503,7 @@ - (void)onReq:(BaseReq *)req { [dictionary setValue:showMessageFromWXReq.message.messageExt forKey:@"messageExt"]; [dictionary setValue:showMessageFromWXReq.lang forKey:@"lang"]; [dictionary setValue:showMessageFromWXReq.country forKey:@"country"]; + [self cacheLaunchReq:@"showMessage" data:dictionary]; if (_isRunning) { [_channel invokeMethod:@"onShowMessageFromWXReq" arguments:dictionary]; } else { @@ -441,6 +523,7 @@ - (void)onResp:(BaseResp *)resp { if (resp.errStr != nil) { [dictionary setValue:resp.errStr forKey:@"errorMsg"]; } + if ([resp isKindOfClass:[SendAuthResp class]]) { // 授权 if (resp.errCode == WXSuccess) { @@ -519,6 +602,7 @@ - (void)onAuthGotQrcode:(UIImage *)image { if (imageData == nil) { imageData = UIImageJPEGRepresentation(image, 1); } + NSDictionary *dictionary = @{ @"imageData" : imageData, }; diff --git a/lib/src/wechat_kit_method_channel.dart b/lib/src/wechat_kit_method_channel.dart index c72aa05..25aaf5d 100644 --- a/lib/src/wechat_kit_method_channel.dart +++ b/lib/src/wechat_kit_method_channel.dart @@ -108,6 +108,24 @@ class MethodChannelWechatKit extends WechatKitPlatform { return methodChannel.invokeMethod('handleInitialWXReq'); } + @override + Future getLaunchReq() async { + final raw = await methodChannel.invokeMethod('getLaunchReq'); + if (raw == null) return null; + final type = raw['type']; + final data = raw['data']; + if (type is String && data is Map) { + final payload = data.cast(); + if (type == 'launch') { + return WechatLaunchFromWXReq.fromJson(payload); + } + if (type == 'showMessage') { + return WechatShowMessageFromWXReq.fromJson(payload); + } + } + return null; + } + @override Future isInstalled() async { return await methodChannel.invokeMethod('isInstalled') ?? false; diff --git a/lib/src/wechat_kit_platform_interface.dart b/lib/src/wechat_kit_platform_interface.dart index 815b44d..0ae1ca1 100644 --- a/lib/src/wechat_kit_platform_interface.dart +++ b/lib/src/wechat_kit_platform_interface.dart @@ -52,6 +52,10 @@ abstract class WechatKitPlatform extends PlatformInterface { throw UnimplementedError('qrauthRespStream() has not been implemented.'); } + Future getLaunchReq() { + throw UnimplementedError('getLaunchReq() has not been implemented.'); + } + /// 微信回调 - 冷启 Future handleInitialWXReq() { throw UnimplementedError('handleInitialWXReq() has not been implemented.');