- 新增 WechatShareManager:封装微信分享逻辑,支持ShareContent对象 - 增强 DouyinShareManager:新增ShareContent支持和分享引导功能 - 优化 QQShareManager:支持截图/纯文本分享类型自动识别 - 重构 SharePanel:简化为UI调度层,移除具体分享实现 - 实现职责分离:各Manager专注自己的平台分享逻辑 - 提升可维护性:修改某平台不影响其他平台 - 增强可扩展性:新增分享平台更容易实现
1474 lines
57 KiB
Objective-C
1474 lines
57 KiB
Objective-C
//
|
||
// QQShareManager.m
|
||
// msext
|
||
//
|
||
// Created on 2025/06/15.
|
||
// Copyright © 2025年. All rights reserved.
|
||
//
|
||
|
||
#import "QQShareManager.h"
|
||
#import "FuncPublic.h" // 用于截图功能
|
||
|
||
// QQ URL Schemes
|
||
#define kQQScheme @"mqqapi://"
|
||
#define kQQShareScheme @"mqqapi://share/"
|
||
#define kQQFriendScheme @"mqqapi://share/to_fri?"
|
||
#define kQQZoneScheme @"mqqapi://share/to_qzone?"
|
||
#define kQQUniversalScheme @"mqq://share/to_fri?" // 通用分享scheme,无需AppID验证
|
||
|
||
// QQ 分享必需的参数
|
||
#define kQQAppName @"进贤聚友棋牌" // 应用名称,与Info.plist中的CFBundleDisplayName和CFBundleName保持一致
|
||
#define kQQCallbackScheme @"msext" // 回调scheme
|
||
#define kQQAppID @"102793577" // 您当前的AppID
|
||
#define kQQFallbackAppID @"100312206" // 腾讯官方测试AppID,通常可用
|
||
|
||
// QQ 应用回调的处理
|
||
static void(^QQShareCompletion)(BOOL) = nil;
|
||
|
||
@implementation QQShareManager
|
||
|
||
#pragma mark - Public Methods
|
||
|
||
+ (BOOL)isQQInstalled {
|
||
return [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:kQQScheme]];
|
||
}
|
||
|
||
+ (BOOL)validateQQAppIDConfiguration {
|
||
NSLog(@"🔍 开始验证QQ AppID配置...");
|
||
NSLog(@"🔍 当前AppID值:%@", kQQAppID);
|
||
NSLog(@"🔍 AppID长度:%lu", (unsigned long)[kQQAppID length]);
|
||
|
||
// 检查AppID是否配置
|
||
if (!kQQAppID || [kQQAppID isEqualToString:@"YOUR_QQ_APPID"]) {
|
||
NSLog(@"❌ QQ AppID 未配置!请在 QQShareManager.m 中设置正确的 kQQAppID");
|
||
return NO;
|
||
}
|
||
|
||
// 检查AppID格式(应该是纯数字,长度在8-10位之间)
|
||
NSCharacterSet *nonDigits = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
|
||
if ([kQQAppID rangeOfCharacterFromSet:nonDigits].location != NSNotFound) {
|
||
NSLog(@"❌ QQ AppID 格式错误!AppID应该是纯数字,当前:%@", kQQAppID);
|
||
return NO;
|
||
}
|
||
|
||
// 检查长度(现代QQ AppID通常是8-10位数字)
|
||
if (kQQAppID.length < 8 || kQQAppID.length > 10) {
|
||
NSLog(@"❌ QQ AppID 长度错误!AppID应该是8-10位数字,当前长度:%lu,AppID:%@", (unsigned long)kQQAppID.length, kQQAppID);
|
||
return NO;
|
||
}
|
||
|
||
NSLog(@"✅ QQ AppID 配置正确:%@", kQQAppID);
|
||
NSLog(@"⚠️ 重要提示:如果出现900101错误,请确认:");
|
||
NSLog(@"⚠️ 1. AppID是否已在QQ开放平台审核通过");
|
||
NSLog(@"⚠️ 2. Bundle ID是否与申请时一致");
|
||
NSLog(@"⚠️ 3. 应用是否已上线或处于测试白名单");
|
||
return YES;
|
||
}
|
||
|
||
+ (NSString *)getCurrentQQAppID {
|
||
if ([self validateQQAppIDConfiguration]) {
|
||
return kQQAppID;
|
||
}
|
||
return nil;
|
||
}
|
||
|
||
+ (void)shareToQQFriend:(QQShareType)type
|
||
title:(NSString *)title
|
||
description:(NSString *)description
|
||
thumbImage:(UIImage *)thumbImage
|
||
url:(NSString *)url
|
||
image:(UIImage *)image
|
||
completion:(void (^)(BOOL))completion {
|
||
|
||
// 检查QQ是否已安装
|
||
if (![self isQQInstalled]) {
|
||
if (completion) {
|
||
completion(NO);
|
||
}
|
||
[self showAppNotInstalledAlert:@"QQ"];
|
||
return;
|
||
}
|
||
|
||
// 验证AppID配置(解决9000101错误的关键)
|
||
if (![self validateQQAppIDConfiguration]) {
|
||
if (completion) {
|
||
completion(NO);
|
||
}
|
||
[self showAppIDConfigurationErrorAlert];
|
||
return;
|
||
}
|
||
|
||
// 保存回调
|
||
QQShareCompletion = completion;
|
||
|
||
// 构建URL参数 - 使用QQ官方标准格式解决900101错误
|
||
NSMutableString *urlString = [NSMutableString stringWithString:kQQFriendScheme];
|
||
|
||
// QQ官方要求的标准参数顺序和格式
|
||
[urlString appendString:@"version=1"];
|
||
[urlString appendString:@"&cflag=0"];
|
||
[urlString appendString:@"&src_type=app"];
|
||
|
||
// 尝试使用QQ最新推荐的参数格式
|
||
[urlString appendFormat:@"&thirdAppDisplayName=%@", [self encodeString:kQQAppName]];
|
||
[urlString appendFormat:@"&app_id=%@", kQQAppID];
|
||
[urlString appendFormat:@"&sdkv=2.9.0"];
|
||
[urlString appendFormat:@"&sdkp=i"];
|
||
|
||
// 回调相关参数
|
||
[urlString appendString:@"&callback_type=scheme"];
|
||
[urlString appendFormat:@"&callback_name=%@", [self encodeString:kQQCallbackScheme]];
|
||
|
||
// 获取Bundle ID用于验证
|
||
NSString *bundleId = [[NSBundle mainBundle] bundleIdentifier];
|
||
|
||
// 添加详细调试日志
|
||
NSLog(@"🔍 ======== QQ分享参数详情 ========");
|
||
NSLog(@"🔍 AppID: %@", kQQAppID);
|
||
NSLog(@"🔍 Bundle ID: %@", bundleId);
|
||
NSLog(@"🔍 AppName: %@", kQQAppName);
|
||
NSLog(@"🔍 CallbackScheme: %@", kQQCallbackScheme);
|
||
NSLog(@"🔍 QQ Friend Scheme: %@", kQQFriendScheme);
|
||
|
||
// 根据分享类型设置不同的参数
|
||
switch (type) {
|
||
case QQShareTypeText:
|
||
[urlString appendString:@"&req_type=0"];
|
||
if (title && title.length > 0) {
|
||
[urlString appendFormat:@"&title=%@", [self encodeString:title]];
|
||
}
|
||
if (description && description.length > 0) {
|
||
[urlString appendFormat:@"&description=%@", [self encodeString:description]];
|
||
}
|
||
break;
|
||
|
||
case QQShareTypeImage:
|
||
if (image) {
|
||
// 将图片保存到本地临时目录
|
||
NSString *imagePath = [self saveImageToTempDirectory:image];
|
||
[urlString appendString:@"&req_type=2"];
|
||
[urlString appendFormat:@"&image_url=%@", [self encodeString:[@"file://" stringByAppendingString:imagePath]]];
|
||
if (title && title.length > 0) {
|
||
[urlString appendFormat:@"&title=%@", [self encodeString:title]];
|
||
}
|
||
if (description && description.length > 0) {
|
||
[urlString appendFormat:@"&description=%@", [self encodeString:description]];
|
||
}
|
||
} else {
|
||
if (completion) {
|
||
completion(NO);
|
||
}
|
||
return;
|
||
}
|
||
break;
|
||
|
||
case QQShareTypeNews:
|
||
[urlString appendString:@"&req_type=1"];
|
||
if (title && title.length > 0) {
|
||
[urlString appendFormat:@"&title=%@", [self encodeString:title]];
|
||
}
|
||
if (description && description.length > 0) {
|
||
[urlString appendFormat:@"&description=%@", [self encodeString:description]];
|
||
}
|
||
if (url && url.length > 0) {
|
||
[urlString appendFormat:@"&url=%@", [self encodeString:url]];
|
||
}
|
||
// 将缩略图保存到本地
|
||
if (thumbImage) {
|
||
NSString *thumbPath = [self saveImageToTempDirectory:thumbImage];
|
||
[urlString appendFormat:@"&previewimageUrl=%@", [self encodeString:[@"file://" stringByAppendingString:thumbPath]]];
|
||
}
|
||
break;
|
||
|
||
case QQShareTypeAudio:
|
||
[urlString appendString:@"&req_type=3"];
|
||
if (title && title.length > 0) {
|
||
[urlString appendFormat:@"&title=%@", [self encodeString:title]];
|
||
}
|
||
if (description && description.length > 0) {
|
||
[urlString appendFormat:@"&description=%@", [self encodeString:description]];
|
||
}
|
||
if (url && url.length > 0) {
|
||
[urlString appendFormat:@"&url=%@", [self encodeString:url]];
|
||
}
|
||
if (thumbImage) {
|
||
NSString *thumbPath = [self saveImageToTempDirectory:thumbImage];
|
||
[urlString appendFormat:@"&previewimageUrl=%@", [self encodeString:[@"file://" stringByAppendingString:thumbPath]]];
|
||
}
|
||
break;
|
||
|
||
case QQShareTypeVideo:
|
||
[urlString appendString:@"&req_type=4"];
|
||
if (title && title.length > 0) {
|
||
[urlString appendFormat:@"&title=%@", [self encodeString:title]];
|
||
}
|
||
if (description && description.length > 0) {
|
||
[urlString appendFormat:@"&description=%@", [self encodeString:description]];
|
||
}
|
||
if (url && url.length > 0) {
|
||
[urlString appendFormat:@"&url=%@", [self encodeString:url]];
|
||
}
|
||
if (thumbImage) {
|
||
NSString *thumbPath = [self saveImageToTempDirectory:thumbImage];
|
||
[urlString appendFormat:@"&previewimageUrl=%@", [self encodeString:[@"file://" stringByAppendingString:thumbPath]]];
|
||
}
|
||
break;
|
||
}
|
||
|
||
// 详细的URL调试日志
|
||
NSLog(@"🔍 ======== 完整QQ分享URL ========");
|
||
NSLog(@"🔍 URL: %@", urlString);
|
||
NSLog(@"🔍 URL长度: %lu", (unsigned long)[urlString length]);
|
||
|
||
// 检查关键参数是否正确
|
||
if ([urlString containsString:[NSString stringWithFormat:@"app_id=%@", kQQAppID]]) {
|
||
NSLog(@"✅ AppID参数已包含");
|
||
} else {
|
||
NSLog(@"❌ AppID参数缺失!");
|
||
}
|
||
|
||
NSURL *qqURL = [NSURL URLWithString:urlString];
|
||
if ([[UIApplication sharedApplication] canOpenURL:qqURL]) {
|
||
if (@available(iOS 10.0, *)) {
|
||
[[UIApplication sharedApplication] openURL:qqURL options:@{} completionHandler:^(BOOL success) {
|
||
// 这里不调用completion,等待QQ返回再调用
|
||
NSLog(@"QQ分享调用结果: %@", success ? @"成功" : @"失败");
|
||
}];
|
||
} else {
|
||
[[UIApplication sharedApplication] openURL:qqURL];
|
||
}
|
||
} else {
|
||
if (completion) {
|
||
completion(NO);
|
||
}
|
||
}
|
||
}
|
||
|
||
+ (void)shareToQZone:(QQShareType)type
|
||
title:(NSString *)title
|
||
description:(NSString *)description
|
||
thumbImage:(UIImage *)thumbImage
|
||
url:(NSString *)url
|
||
images:(NSArray<UIImage *> *)images
|
||
completion:(void (^)(BOOL))completion {
|
||
|
||
// 检查QQ是否已安装
|
||
if (![self isQQInstalled]) {
|
||
if (completion) {
|
||
completion(NO);
|
||
}
|
||
[self showAppNotInstalledAlert:@"QQ"];
|
||
return;
|
||
}
|
||
|
||
// 验证AppID配置(解决9000101错误的关键)
|
||
if (![self validateQQAppIDConfiguration]) {
|
||
if (completion) {
|
||
completion(NO);
|
||
}
|
||
[self showAppIDConfigurationErrorAlert];
|
||
return;
|
||
}
|
||
|
||
// 保存回调
|
||
QQShareCompletion = completion;
|
||
|
||
// 构建URL参数 - 修复QQ空间分享格式
|
||
NSMutableString *urlString = [NSMutableString stringWithString:kQQZoneScheme];
|
||
|
||
// 基础必需参数,严格按照QQ要求的格式和顺序
|
||
[urlString appendString:@"version=1"];
|
||
[urlString appendString:@"&cflag=0"];
|
||
[urlString appendFormat:@"&app_id=%@", kQQAppID]; // AppID必须在前面位置
|
||
[urlString appendString:@"&src_type=app"];
|
||
[urlString appendString:@"&sdkv=2.9.0"];
|
||
[urlString appendString:@"&sdkp=i"];
|
||
|
||
// 应用信息参数 - AppID是解决9000101错误的关键参数
|
||
[urlString appendFormat:@"&appid=%@", kQQAppID]; // QQ AppID - 必需参数
|
||
[urlString appendFormat:@"&app_name=%@", [self encodeString:kQQAppName]];
|
||
[urlString appendString:@"&callback_type=scheme"];
|
||
[urlString appendFormat:@"&callback_name=%@", [self encodeString:kQQCallbackScheme]];
|
||
|
||
// 添加内容参数
|
||
if (title && title.length > 0) {
|
||
[urlString appendFormat:@"&title=%@", [self encodeString:title]];
|
||
}
|
||
if (description && description.length > 0) {
|
||
[urlString appendFormat:@"&description=%@", [self encodeString:description]];
|
||
}
|
||
if (url && url.length > 0) {
|
||
[urlString appendFormat:@"&url=%@", [self encodeString:url]];
|
||
}
|
||
|
||
// 处理图片
|
||
if (images && images.count > 0) {
|
||
NSMutableArray *imagePaths = [NSMutableArray array];
|
||
|
||
for (UIImage *image in images) {
|
||
NSString *imagePath = [self saveImageToTempDirectory:image];
|
||
[imagePaths addObject:[@"file://" stringByAppendingString:imagePath]];
|
||
}
|
||
|
||
if (imagePaths.count > 0) {
|
||
[urlString appendFormat:@"&imageUrl=%@", [self encodeString:[imagePaths componentsJoinedByString:@","]]];
|
||
}
|
||
} else if (thumbImage) {
|
||
NSString *thumbPath = [self saveImageToTempDirectory:thumbImage];
|
||
[urlString appendFormat:@"&previewimageUrl=%@", [self encodeString:[@"file://" stringByAppendingString:thumbPath]]];
|
||
}
|
||
|
||
// 设置分享类型
|
||
switch (type) {
|
||
case QQShareTypeText:
|
||
[urlString appendString:@"&req_type=0"];
|
||
break;
|
||
case QQShareTypeImage:
|
||
[urlString appendString:@"&req_type=2"];
|
||
break;
|
||
case QQShareTypeNews:
|
||
[urlString appendString:@"&req_type=1"];
|
||
break;
|
||
case QQShareTypeAudio:
|
||
[urlString appendString:@"&req_type=3"];
|
||
break;
|
||
case QQShareTypeVideo:
|
||
[urlString appendString:@"&req_type=4"];
|
||
break;
|
||
}
|
||
|
||
// 打开URL
|
||
NSLog(@"QQ空间分享URL: %@", urlString);
|
||
NSURL *qzoneURL = [NSURL URLWithString:urlString];
|
||
if ([[UIApplication sharedApplication] canOpenURL:qzoneURL]) {
|
||
if (@available(iOS 10.0, *)) {
|
||
[[UIApplication sharedApplication] openURL:qzoneURL options:@{} completionHandler:^(BOOL success) {
|
||
// 这里不调用completion,等待QQ返回再调用
|
||
}];
|
||
} else {
|
||
[[UIApplication sharedApplication] openURL:qzoneURL];
|
||
}
|
||
} else {
|
||
if (completion) {
|
||
completion(NO);
|
||
}
|
||
}
|
||
}
|
||
|
||
+ (BOOL)handleOpenURL:(NSURL *)url {
|
||
// 判断是否是QQ返回的URL
|
||
NSString *urlString = [url absoluteString];
|
||
NSLog(@"QQ分享回调URL: %@", urlString);
|
||
|
||
if ([urlString hasPrefix:@"msext://"]) {
|
||
// 解析返回的参数
|
||
NSString *errorDescription = [self getUrlParam:urlString paramName:@"error_description"];
|
||
NSString *error = [self getUrlParam:urlString paramName:@"error"];
|
||
|
||
BOOL success = YES;
|
||
if (error && ![error isEqualToString:@"0"]) {
|
||
success = NO;
|
||
NSLog(@"QQ分享失败: error=%@, description=%@", error, errorDescription);
|
||
} else {
|
||
NSLog(@"QQ分享成功");
|
||
}
|
||
|
||
// 执行回调
|
||
if (QQShareCompletion) {
|
||
QQShareCompletion(success);
|
||
QQShareCompletion = nil;
|
||
}
|
||
return YES;
|
||
}
|
||
return NO;
|
||
}
|
||
|
||
#pragma mark - 简化版QQ分享方法(解决900101错误)
|
||
|
||
/**
|
||
* 简化版QQ分享方法,使用最基础的参数避免900101错误
|
||
*/
|
||
+ (void)simpleShareToQQFriend:(NSString *)title
|
||
description:(NSString *)description
|
||
url:(NSString *)url
|
||
completion:(void(^)(BOOL success))completion {
|
||
|
||
if (![self isQQInstalled]) {
|
||
if (completion) {
|
||
completion(NO);
|
||
}
|
||
[self showAppNotInstalledAlert:@"QQ"];
|
||
return;
|
||
}
|
||
|
||
// 保存回调
|
||
QQShareCompletion = completion;
|
||
|
||
// 使用最简单的URL格式,避免复杂参数导致的900101错误
|
||
NSMutableString *urlString = [NSMutableString stringWithString:@"mqqapi://share/to_fri?"];
|
||
|
||
// 最基础的必需参数
|
||
[urlString appendString:@"version=1"];
|
||
[urlString appendString:@"&cflag=0"];
|
||
|
||
// 分享类型 - 根据是否有URL决定
|
||
if (url && url.length > 0) {
|
||
[urlString appendString:@"&req_type=1"]; // 网页分享
|
||
[urlString appendFormat:@"&url=%@", [self encodeString:url]];
|
||
} else {
|
||
[urlString appendString:@"&req_type=0"]; // 文本分享
|
||
}
|
||
|
||
// 内容参数
|
||
if (title && title.length > 0) {
|
||
[urlString appendFormat:@"&title=%@", [self encodeString:title]];
|
||
}
|
||
if (description && description.length > 0) {
|
||
[urlString appendFormat:@"&description=%@", [self encodeString:description]];
|
||
}
|
||
|
||
// 打开URL
|
||
NSLog(@"简化QQ分享URL: %@", urlString);
|
||
NSURL *qqURL = [NSURL URLWithString:urlString];
|
||
if ([[UIApplication sharedApplication] canOpenURL:qqURL]) {
|
||
if (@available(iOS 10.0, *)) {
|
||
[[UIApplication sharedApplication] openURL:qqURL options:@{} completionHandler:^(BOOL success) {
|
||
NSLog(@"简化QQ分享调用结果: %@", success ? @"成功" : @"失败");
|
||
}];
|
||
} else {
|
||
[[UIApplication sharedApplication] openURL:qqURL];
|
||
}
|
||
} else {
|
||
if (completion) {
|
||
completion(NO);
|
||
}
|
||
}
|
||
}
|
||
|
||
+ (void)forceOpenQQSessionSelector:(void(^)(BOOL success))completion {
|
||
NSLog(@"尝试强制弹出QQ会话选择列表...");
|
||
|
||
// 检查QQ是否安装
|
||
if (![self isQQInstalled]) {
|
||
NSLog(@"QQ未安装");
|
||
if (completion) completion(NO);
|
||
return;
|
||
}
|
||
|
||
// 尝试的URL方案,按优先级排序
|
||
NSArray *urlTemplates = @[
|
||
// 方案1: 使用好友分享接口
|
||
@"mqqopensdkfriend://share?src_type=internal&version=1&app_name=%@&title=%@",
|
||
|
||
// 方案2: 直接打开聊天选择界面
|
||
@"mqq://im/chat?chat_type=select",
|
||
|
||
// 方案3: 打开好友列表
|
||
@"mqq://contact/friend_list",
|
||
|
||
// 方案4: 打开最近会话
|
||
@"mqq://im/recent",
|
||
|
||
// 方案5: 使用分享到好友
|
||
@"mqqapi://share/to_fri?file_type=text&file_data=%@&app_name=%@",
|
||
|
||
// 方案6: 简化的card接口
|
||
@"mqqapi://card/show_pslcard?src_type=internal&version=1&app_name=%@&req_type=0&title=%@"
|
||
];
|
||
|
||
NSString *appName = [self encodeString:kQQAppName];
|
||
NSString *defaultTitle = [self encodeString:@"分享内容"];
|
||
|
||
// 逐个尝试每种方案
|
||
[self tryURLSchemes:urlTemplates appName:appName title:defaultTitle index:0 completion:completion];
|
||
}
|
||
|
||
+ (void)tryURLSchemes:(NSArray *)urlTemplates
|
||
appName:(NSString *)appName
|
||
title:(NSString *)title
|
||
index:(NSInteger)index
|
||
completion:(void(^)(BOOL success))completion {
|
||
|
||
if (index >= urlTemplates.count) {
|
||
NSLog(@"所有URL方案都尝试失败");
|
||
if (completion) completion(NO);
|
||
return;
|
||
}
|
||
|
||
NSString *template = urlTemplates[index];
|
||
NSString *urlString;
|
||
|
||
// 根据模板格式构造URL
|
||
if ([template containsString:@"chat_type=select"] ||
|
||
[template containsString:@"friend_list"] ||
|
||
[template containsString:@"im/recent"]) {
|
||
// 不需要参数的URL
|
||
urlString = template;
|
||
} else if ([template containsString:@"file_data"]) {
|
||
// 需要两个参数的URL
|
||
urlString = [NSString stringWithFormat:template, title, appName];
|
||
} else {
|
||
// 需要app_name和title的URL
|
||
urlString = [NSString stringWithFormat:template, appName, title];
|
||
}
|
||
|
||
NSURL *url = [NSURL URLWithString:urlString];
|
||
NSLog(@"尝试方案%ld: %@", (long)(index + 1), urlString);
|
||
|
||
if ([[UIApplication sharedApplication] canOpenURL:url]) {
|
||
if (@available(iOS 10.0, *)) {
|
||
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:^(BOOL success) {
|
||
if (success) {
|
||
NSLog(@"方案%ld 成功打开QQ", (long)(index + 1));
|
||
|
||
// 延迟检查是否真的打开了会话选择
|
||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||
if (completion) completion(YES);
|
||
});
|
||
} else {
|
||
NSLog(@"方案%ld 打开失败,尝试下一个", (long)(index + 1));
|
||
[self tryURLSchemes:urlTemplates appName:appName title:title index:index + 1 completion:completion];
|
||
}
|
||
}];
|
||
} else {
|
||
[[UIApplication sharedApplication] openURL:url];
|
||
if (completion) completion(YES);
|
||
}
|
||
} else {
|
||
NSLog(@"方案%ld 不支持,尝试下一个", (long)(index + 1));
|
||
[self tryURLSchemes:urlTemplates appName:appName title:title index:index + 1 completion:completion];
|
||
}
|
||
}
|
||
|
||
+ (void)tryMultipleQQShareMethods:(NSString *)title
|
||
description:(NSString *)description
|
||
completion:(void(^)(BOOL success))completion {
|
||
|
||
NSLog(@"使用多种方式尝试QQ分享...");
|
||
|
||
// 检查QQ是否安装
|
||
if (![self isQQInstalled]) {
|
||
NSLog(@"QQ未安装");
|
||
if (completion) completion(NO);
|
||
return;
|
||
}
|
||
|
||
// 方法1: 先尝试强制弹出会话选择器
|
||
[self forceOpenQQSessionSelector:^(BOOL success) {
|
||
if (success) {
|
||
NSLog(@"成功弹出会话选择器");
|
||
if (completion) completion(YES);
|
||
} else {
|
||
NSLog(@"会话选择器失败,尝试简化分享");
|
||
|
||
// 方法2: 使用简化分享
|
||
[self simpleShareToQQFriend:title description:description url:nil completion:^(BOOL simpleSuccess) {
|
||
if (simpleSuccess) {
|
||
NSLog(@"简化分享成功");
|
||
if (completion) completion(YES);
|
||
} else {
|
||
NSLog(@"简化分享也失败,使用标准分享");
|
||
|
||
// 方法3: 使用标准分享
|
||
[self shareToQQFriend:QQShareTypeText
|
||
title:title
|
||
description:description
|
||
thumbImage:nil
|
||
url:nil
|
||
image:nil
|
||
completion:completion];
|
||
}
|
||
}];
|
||
}
|
||
}];
|
||
}
|
||
|
||
+ (void)shareWithSystemShare:(NSString *)title
|
||
description:(NSString *)description
|
||
url:(NSString *)url
|
||
fromViewController:(UIViewController *)viewController
|
||
completion:(void (^)(BOOL))completion {
|
||
|
||
// 准备分享内容数组
|
||
NSMutableArray *shareItems = [NSMutableArray array];
|
||
|
||
// 组合完整的分享文本(不包含URL)
|
||
NSMutableString *shareText = [NSMutableString string];
|
||
|
||
// 添加标题
|
||
if (title && title.length > 0) {
|
||
[shareText appendString:title];
|
||
}
|
||
|
||
// 添加描述
|
||
if (description && description.length > 0) {
|
||
if (shareText.length > 0) {
|
||
[shareText appendString:@"\n"]; // 使用单个换行符,更紧凑
|
||
}
|
||
[shareText appendString:description];
|
||
}
|
||
|
||
// 添加组合文本到分享项目(在组合完成后添加)
|
||
if (shareText.length > 0) {
|
||
[shareItems addObject:[shareText copy]];
|
||
NSLog(@"🔍 ✅ 添加组合文本: %@", shareText);
|
||
}
|
||
|
||
|
||
|
||
// 统一使用固定的分享URL,不管传入的URL是什么
|
||
NSString *fixedShareURL = @"http://game.hudong.com";
|
||
NSURL *shareURL = [NSURL URLWithString:fixedShareURL];
|
||
if (shareURL) {
|
||
[shareItems addObject:shareURL];
|
||
NSLog(@"🔍 ✅ 添加固定分享URL: %@", fixedShareURL);
|
||
}
|
||
|
||
// 添加应用桌面图标作为缩略图
|
||
UIImage *appIcon = [self getAppIcon];
|
||
if (appIcon) {
|
||
[shareItems addObject:appIcon];
|
||
NSLog(@"🔍 ✅ 添加应用桌面图标作为缩略图");
|
||
}
|
||
|
||
// 如果没有任何分享内容,使用默认文本
|
||
if (shareItems.count == 0) {
|
||
[shareItems addObject:@"来自进贤聚友棋牌的精彩内容分享"];
|
||
NSURL *defaultURL = [NSURL URLWithString:fixedShareURL];
|
||
if (defaultURL) {
|
||
[shareItems addObject:defaultURL];
|
||
}
|
||
}
|
||
|
||
// 详细的调试日志
|
||
NSLog(@"🔍 ======== 系统分享内容详情 ========");
|
||
NSLog(@"🔍 原始标题: %@", title ?: @"(无)");
|
||
NSLog(@"🔍 原始描述: %@", description ?: @"(无)");
|
||
NSLog(@"🔍 传入URL: %@", url ?: @"(无)");
|
||
NSLog(@"🔍 实际分享URL: http://game.hudong.com (固定)");
|
||
NSLog(@"🔍 分享文本: %@", shareText.length > 0 ? shareText : @"(无)");
|
||
NSLog(@"🔍 分享项目数量: %lu", (unsigned long)shareItems.count);
|
||
|
||
// 打印所有分享项目
|
||
for (NSInteger i = 0; i < shareItems.count; i++) {
|
||
id item = shareItems[i];
|
||
if ([item isKindOfClass:[NSString class]]) {
|
||
NSLog(@"🔍 项目%ld (文本): %@", (long)i+1, item);
|
||
} else if ([item isKindOfClass:[NSURL class]]) {
|
||
NSLog(@"🔍 项目%ld (URL): %@", (long)i+1, [(NSURL *)item absoluteString]);
|
||
} else {
|
||
NSLog(@"🔍 项目%ld (其他): %@", (long)i+1, item);
|
||
}
|
||
}
|
||
NSLog(@"🔍 =====================================");
|
||
|
||
// 创建系统分享控制器
|
||
UIActivityViewController *activityVC = [[UIActivityViewController alloc]
|
||
initWithActivityItems:shareItems
|
||
applicationActivities:nil];
|
||
|
||
// 排除一些不常用的分享选项(可选,提高QQ等主要应用的显示优先级)
|
||
NSArray *excludedActivityTypes = @[
|
||
UIActivityTypePostToVimeo,
|
||
UIActivityTypePostToFlickr,
|
||
UIActivityTypePostToTencentWeibo,
|
||
UIActivityTypeAssignToContact,
|
||
UIActivityTypeSaveToCameraRoll,
|
||
UIActivityTypeAddToReadingList,
|
||
UIActivityTypePostToFacebook,
|
||
UIActivityTypePostToTwitter
|
||
];
|
||
activityVC.excludedActivityTypes = excludedActivityTypes;
|
||
|
||
// 设置完成回调
|
||
activityVC.completionWithItemsHandler = ^(UIActivityType activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
|
||
NSLog(@"🔍 ======== 系统分享结果详情 ========");
|
||
NSLog(@"🔍 分享结果: %@", completed ? @"✅ 成功" : @"❌ 取消/失败");
|
||
NSLog(@"🔍 分享到应用: %@", activityType ?: @"(未知)");
|
||
|
||
if (activityError) {
|
||
NSLog(@"🔍 分享错误: %@", activityError.localizedDescription);
|
||
|
||
// 特别处理 RunningBoard 相关错误
|
||
if ([activityError.domain isEqualToString:@"RBSServiceErrorDomain"]) {
|
||
NSLog(@"🔍 ⚠️ 检测到RunningBoard权限错误,这通常是系统级权限问题");
|
||
NSLog(@"🔍 ⚠️ 建议检查应用是否在前台,以及设备权限设置");
|
||
}
|
||
}
|
||
|
||
// 特别标记QQ分享
|
||
if (activityType && ([activityType containsString:@"QQ"] || [activityType containsString:@"qq"])) {
|
||
NSLog(@"🔍 ✅ QQ分享完成");
|
||
}
|
||
|
||
NSLog(@"🔍 =====================================");
|
||
|
||
if (completion) {
|
||
completion(completed);
|
||
}
|
||
};
|
||
|
||
// 在iPad上需要设置popover
|
||
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
|
||
activityVC.popoverPresentationController.sourceView = viewController.view;
|
||
activityVC.popoverPresentationController.sourceRect = CGRectMake(viewController.view.bounds.size.width/2, viewController.view.bounds.size.height/2, 0, 0);
|
||
activityVC.popoverPresentationController.permittedArrowDirections = 0;
|
||
}
|
||
|
||
// 确保在主线程呈现分享面板,避免RunningBoard权限错误
|
||
dispatch_async(dispatch_get_main_queue(), ^{
|
||
// 再次检查视图控制器是否有效
|
||
if (viewController && viewController.view.window) {
|
||
NSLog(@"🔍 在主线程呈现系统分享面板");
|
||
[viewController presentViewController:activityVC animated:YES completion:^{
|
||
NSLog(@"🔍 ✅ 系统分享面板呈现完成");
|
||
}];
|
||
} else {
|
||
NSLog(@"🔍 ❌ 视图控制器无效或不在窗口层次结构中");
|
||
if (completion) {
|
||
completion(NO);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
+ (void)shareWithSystemShareAuto:(NSString *)title
|
||
description:(NSString *)description
|
||
url:(NSString *)url
|
||
completion:(void (^)(BOOL))completion {
|
||
|
||
// 检查是否可以安全呈现分享面板
|
||
if (![self canSafelyPresentSharePanel]) {
|
||
NSLog(@"🔍 ❌ 当前条件不允许呈现分享面板,避免RunningBoard错误");
|
||
if (completion) {
|
||
completion(NO);
|
||
}
|
||
return;
|
||
}
|
||
|
||
UIViewController *topViewController = [self getTopViewController];
|
||
if (!topViewController) {
|
||
NSLog(@"❌ 无法获取顶层视图控制器,无法弹出系统分享面板");
|
||
if (completion) {
|
||
completion(NO);
|
||
}
|
||
return;
|
||
}
|
||
|
||
[self shareWithSystemShare:title
|
||
description:description
|
||
url:url
|
||
fromViewController:topViewController
|
||
completion:completion];
|
||
}
|
||
|
||
#pragma mark - Private Methods
|
||
|
||
+ (UIViewController *)getTopViewController {
|
||
// 检查应用状态,避免在后台或非活跃状态访问UI
|
||
UIApplicationState appState = [UIApplication sharedApplication].applicationState;
|
||
if (appState != UIApplicationStateActive) {
|
||
NSLog(@"🔍 ⚠️ 应用不在活跃状态 (状态: %ld),可能导致权限错误", (long)appState);
|
||
// 仍然尝试获取,但记录警告
|
||
}
|
||
|
||
UIWindow *keyWindow = nil;
|
||
|
||
// iOS 13+ 获取keyWindow的方式
|
||
if (@available(iOS 13.0, *)) {
|
||
NSSet<UIScene *> *connectedScenes = [UIApplication sharedApplication].connectedScenes;
|
||
NSLog(@"🔍 当前连接的Scene数量: %lu", (unsigned long)connectedScenes.count);
|
||
|
||
for (UIWindowScene *windowScene in connectedScenes) {
|
||
NSLog(@"🔍 Scene状态: %ld", (long)windowScene.activationState);
|
||
if (windowScene.activationState == UISceneActivationStateForegroundActive) {
|
||
NSLog(@"🔍 找到前台活跃的WindowScene");
|
||
for (UIWindow *window in windowScene.windows) {
|
||
NSLog(@"🔍 检查Window: isKeyWindow=%d, isHidden=%d", window.isKeyWindow, window.isHidden);
|
||
if (window.isKeyWindow && !window.isHidden) {
|
||
keyWindow = window;
|
||
NSLog(@"🔍 ✅ 找到活跃的keyWindow");
|
||
break;
|
||
}
|
||
}
|
||
if (keyWindow) break;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 兼容iOS 13以下版本
|
||
if (!keyWindow) {
|
||
NSLog(@"🔍 使用传统方式获取keyWindow");
|
||
keyWindow = [UIApplication sharedApplication].keyWindow;
|
||
}
|
||
|
||
// 如果还是没有,尝试获取第一个可见window
|
||
if (!keyWindow) {
|
||
NSLog(@"🔍 尝试获取第一个可见window");
|
||
NSArray *windows = [UIApplication sharedApplication].windows;
|
||
for (UIWindow *window in windows) {
|
||
if (!window.isHidden && window.rootViewController) {
|
||
keyWindow = window;
|
||
NSLog(@"🔍 使用备选window");
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!keyWindow) {
|
||
NSLog(@"🔍 ❌ 无法获取任何有效的window,这可能导致RunningBoard权限错误");
|
||
return nil;
|
||
}
|
||
|
||
// 检查window是否有rootViewController
|
||
if (!keyWindow.rootViewController) {
|
||
NSLog(@"🔍 ❌ keyWindow没有rootViewController");
|
||
return nil;
|
||
}
|
||
|
||
// 获取顶层视图控制器
|
||
UIViewController *topViewController = keyWindow.rootViewController;
|
||
NSLog(@"🔍 根视图控制器: %@", NSStringFromClass([topViewController class]));
|
||
|
||
// 遍历presented视图控制器
|
||
int presentedCount = 0;
|
||
while (topViewController.presentedViewController && presentedCount < 10) { // 防止无限循环
|
||
topViewController = topViewController.presentedViewController;
|
||
presentedCount++;
|
||
NSLog(@"🔍 Presented视图控制器 %d: %@", presentedCount, NSStringFromClass([topViewController class]));
|
||
}
|
||
|
||
// 处理UINavigationController
|
||
if ([topViewController isKindOfClass:[UINavigationController class]]) {
|
||
UINavigationController *navController = (UINavigationController *)topViewController;
|
||
topViewController = navController.topViewController;
|
||
NSLog(@"🔍 导航控制器顶层: %@", NSStringFromClass([topViewController class]));
|
||
}
|
||
|
||
// 处理UITabBarController
|
||
if ([topViewController isKindOfClass:[UITabBarController class]]) {
|
||
UITabBarController *tabController = (UITabBarController *)topViewController;
|
||
topViewController = tabController.selectedViewController;
|
||
NSLog(@"🔍 标签控制器选中: %@", NSStringFromClass([topViewController class]));
|
||
|
||
// 再次检查是否是NavigationController
|
||
if ([topViewController isKindOfClass:[UINavigationController class]]) {
|
||
UINavigationController *navController = (UINavigationController *)topViewController;
|
||
topViewController = navController.topViewController;
|
||
NSLog(@"🔍 标签内导航控制器顶层: %@", NSStringFromClass([topViewController class]));
|
||
}
|
||
}
|
||
|
||
// 最终验证
|
||
if (topViewController && topViewController.view.window) {
|
||
NSLog(@"🔍 ✅ 成功获取有效的顶层视图控制器: %@", NSStringFromClass([topViewController class]));
|
||
} else {
|
||
NSLog(@"🔍 ⚠️ 获取的视图控制器可能无效或不在window层次结构中");
|
||
}
|
||
|
||
return topViewController;
|
||
}
|
||
|
||
+ (BOOL)canSafelyPresentSharePanel {
|
||
NSLog(@"🔍 ======== 检查分享面板呈现条件 ========");
|
||
|
||
// 1. 检查应用状态
|
||
UIApplicationState appState = [UIApplication sharedApplication].applicationState;
|
||
NSLog(@"🔍 应用状态: %ld (0=Active, 1=Inactive, 2=Background)", (long)appState);
|
||
|
||
if (appState != UIApplicationStateActive) {
|
||
NSLog(@"🔍 ❌ 应用不在活跃状态,无法安全呈现分享面板");
|
||
return NO;
|
||
}
|
||
|
||
// 2. 检查是否有有效的视图控制器
|
||
UIViewController *topVC = [self getTopViewController];
|
||
if (!topVC) {
|
||
NSLog(@"🔍 ❌ 无法获取顶层视图控制器");
|
||
return NO;
|
||
}
|
||
|
||
if (!topVC.view.window) {
|
||
NSLog(@"🔍 ❌ 视图控制器不在window层次结构中");
|
||
return NO;
|
||
}
|
||
|
||
// 3. 检查是否已经有模态视图
|
||
if (topVC.presentedViewController) {
|
||
NSLog(@"🔍 ❌ 已有模态视图控制器正在呈现: %@", NSStringFromClass([topVC.presentedViewController class]));
|
||
return NO;
|
||
}
|
||
|
||
// 4. 检查是否在主线程
|
||
if (![NSThread isMainThread]) {
|
||
NSLog(@"🔍 ❌ 不在主线程,可能导致RunningBoard错误");
|
||
return NO;
|
||
}
|
||
|
||
// 5. iOS 13+ Scene状态检查
|
||
if (@available(iOS 13.0, *)) {
|
||
BOOL hasActiveScene = NO;
|
||
NSSet<UIScene *> *connectedScenes = [UIApplication sharedApplication].connectedScenes;
|
||
|
||
for (UIWindowScene *windowScene in connectedScenes) {
|
||
if ([windowScene isKindOfClass:[UIWindowScene class]] &&
|
||
windowScene.activationState == UISceneActivationStateForegroundActive) {
|
||
hasActiveScene = YES;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!hasActiveScene) {
|
||
NSLog(@"🔍 ❌ 没有活跃的前台Scene");
|
||
return NO;
|
||
}
|
||
}
|
||
|
||
NSLog(@"🔍 ✅ 所有条件满足,可以安全呈现分享面板");
|
||
NSLog(@"🔍 =====================================");
|
||
return YES;
|
||
}
|
||
|
||
+ (NSString *)encodeString:(NSString *)string {
|
||
if (!string) return @"";
|
||
return [string stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
|
||
}
|
||
|
||
+ (NSString *)saveImageToTempDirectory:(UIImage *)image {
|
||
NSData *imageData = UIImageJPEGRepresentation(image, 0.7);
|
||
NSString *fileName = [NSString stringWithFormat:@"qq_share_%@.jpg", [self generateUUID]];
|
||
NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
|
||
|
||
[imageData writeToFile:filePath atomically:YES];
|
||
return filePath;
|
||
}
|
||
|
||
+ (NSString *)generateUUID {
|
||
CFUUIDRef uuid = CFUUIDCreate(NULL);
|
||
CFStringRef uuidString = CFUUIDCreateString(NULL, uuid);
|
||
NSString *result = (__bridge_transfer NSString *)uuidString;
|
||
CFRelease(uuid);
|
||
return result;
|
||
}
|
||
|
||
+ (NSString *)getUrlParam:(NSString *)url paramName:(NSString *)name {
|
||
NSError *error;
|
||
NSString *regTags = [[NSString alloc] initWithFormat:@"(^|&|\\?)%@=([^&]*)(&|$)", name];
|
||
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regTags options:NSRegularExpressionCaseInsensitive error:&error];
|
||
|
||
NSTextCheckingResult *match = [regex firstMatchInString:url options:0 range:NSMakeRange(0, [url length])];
|
||
if (match) {
|
||
NSString *paramValue = [url substringWithRange:[match rangeAtIndex:2]];
|
||
return paramValue;
|
||
}
|
||
|
||
return nil;
|
||
}
|
||
|
||
+ (void)showAppNotInstalledAlert:(NSString *)appName {
|
||
dispatch_async(dispatch_get_main_queue(), ^{
|
||
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"%@未安装", appName]
|
||
message:[NSString stringWithFormat:@"您的设备未安装%@客户端,无法进行分享", appName]
|
||
preferredStyle:UIAlertControllerStyleAlert];
|
||
|
||
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil];
|
||
[alertController addAction:okAction];
|
||
|
||
UIViewController *topVC = [self topViewController];
|
||
[topVC presentViewController:alertController animated:YES completion:nil];
|
||
});
|
||
}
|
||
|
||
+ (void)showAppIDConfigurationErrorAlert {
|
||
dispatch_async(dispatch_get_main_queue(), ^{
|
||
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"QQ分享配置错误"
|
||
message:@"QQ AppID配置错误,请联系开发者检查配置"
|
||
preferredStyle:UIAlertControllerStyleAlert];
|
||
|
||
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil];
|
||
[alertController addAction:okAction];
|
||
|
||
UIViewController *topVC = [self topViewController];
|
||
[topVC presentViewController:alertController animated:YES completion:nil];
|
||
});
|
||
}
|
||
|
||
+ (UIViewController *)topViewController {
|
||
UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
|
||
while (rootViewController.presentedViewController) {
|
||
rootViewController = rootViewController.presentedViewController;
|
||
}
|
||
|
||
if ([rootViewController isKindOfClass:[UINavigationController class]]) {
|
||
UINavigationController *navigationController = (UINavigationController *)rootViewController;
|
||
return navigationController.visibleViewController;
|
||
}
|
||
|
||
if ([rootViewController isKindOfClass:[UITabBarController class]]) {
|
||
UITabBarController *tabBarController = (UITabBarController *)rootViewController;
|
||
UIViewController *selectedViewController = tabBarController.selectedViewController;
|
||
|
||
if ([selectedViewController isKindOfClass:[UINavigationController class]]) {
|
||
UINavigationController *navigationController = (UINavigationController *)selectedViewController;
|
||
return navigationController.visibleViewController;
|
||
}
|
||
|
||
return selectedViewController;
|
||
}
|
||
|
||
return rootViewController;
|
||
}
|
||
|
||
+ (UIImage *)getAppLaunchImage {
|
||
// 保留此方法以备将来需要,但现在主要使用桌面图标
|
||
NSLog(@"🔍 ⚠️ 建议使用 getAppIcon 方法获取桌面图标");
|
||
return [self getAppIcon];
|
||
}
|
||
|
||
+ (UIImage *)getAppIcon {
|
||
// 尝试获取应用桌面图标
|
||
UIImage *appIcon = nil;
|
||
|
||
// 方法1: 从Info.plist获取图标信息
|
||
NSDictionary *infoPlist = [[NSBundle mainBundle] infoDictionary];
|
||
|
||
// iOS 13+ 支持的新格式
|
||
NSDictionary *icons = infoPlist[@"CFBundleIcons"];
|
||
NSDictionary *primaryIcon = icons[@"CFBundlePrimaryIcon"];
|
||
NSArray *iconFiles = primaryIcon[@"CFBundleIconFiles"];
|
||
|
||
if (iconFiles && iconFiles.count > 0) {
|
||
// 按优先级尝试不同尺寸的图标(从大到小)
|
||
NSArray *preferredSizes = @[@"180", @"120", @"60"];
|
||
|
||
for (NSString *size in preferredSizes) {
|
||
for (NSString *iconName in iconFiles) {
|
||
NSString *testName = [NSString stringWithFormat:@"%@%@", iconName, size];
|
||
appIcon = [UIImage imageNamed:testName];
|
||
if (appIcon) {
|
||
NSLog(@"🔍 ✅ 获取应用图标成功: %@", testName);
|
||
return appIcon;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 尝试原始名称
|
||
for (NSString *iconName in iconFiles) {
|
||
appIcon = [UIImage imageNamed:iconName];
|
||
if (appIcon) {
|
||
NSLog(@"🔍 ✅ 获取应用图标成功: %@", iconName);
|
||
return appIcon;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 方法2: 尝试常见的图标名称
|
||
NSArray *commonIconNames = @[
|
||
@"Icon-180", // iPhone @3x (180x180)
|
||
@"Icon180", // 常见命名
|
||
@"Icon-120", // iPhone @2x (120x120)
|
||
@"Icon120", // 常见命名
|
||
@"Icon-60", // iPhone @1x (60x60)
|
||
@"Icon60", // 常见命名
|
||
@"AppIcon60x60@3x",
|
||
@"AppIcon60x60@2x",
|
||
@"AppIcon60x60",
|
||
@"AppIcon", // 通用名称
|
||
@"Icon", // 简单名称
|
||
@"icon" // 小写
|
||
];
|
||
|
||
for (NSString *iconName in commonIconNames) {
|
||
appIcon = [UIImage imageNamed:iconName];
|
||
if (appIcon) {
|
||
NSLog(@"🔍 ✅ 获取应用图标成功: %@", iconName);
|
||
return appIcon;
|
||
}
|
||
}
|
||
|
||
// 方法3: 从App Bundle中搜索图标文件
|
||
NSBundle *mainBundle = [NSBundle mainBundle];
|
||
NSArray *imageExtensions = @[@"png", @"jpg", @"jpeg"];
|
||
|
||
for (NSString *ext in imageExtensions) {
|
||
NSArray *iconPaths = [mainBundle pathsForResourcesOfType:ext inDirectory:nil];
|
||
for (NSString *path in iconPaths) {
|
||
NSString *filename = [[path lastPathComponent] stringByDeletingPathExtension];
|
||
// 检查文件名是否包含icon相关关键词
|
||
if ([filename.lowercaseString containsString:@"icon"] ||
|
||
[filename.lowercaseString containsString:@"appicon"]) {
|
||
appIcon = [UIImage imageWithContentsOfFile:path];
|
||
if (appIcon) {
|
||
NSLog(@"🔍 ✅ 从Bundle获取应用图标: %@", filename);
|
||
return appIcon;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
NSLog(@"🔍 ⚠️ 无法获取应用桌面图标,创建默认图标");
|
||
return [self createDefaultAppIcon];
|
||
}
|
||
|
||
+ (UIImage *)createDefaultAppIcon {
|
||
// 创建一个简单的默认应用图标
|
||
CGSize iconSize = CGSizeMake(120, 120);
|
||
UIGraphicsBeginImageContextWithOptions(iconSize, NO, [UIScreen mainScreen].scale);
|
||
|
||
// 设置渐变背景色(模拟应用图标的效果)
|
||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||
|
||
// 创建渐变色
|
||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||
CGFloat colors[] = {
|
||
0.2, 0.6, 1.0, 1.0, // 蓝色
|
||
0.1, 0.4, 0.8, 1.0 // 深蓝色
|
||
};
|
||
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, colors, NULL, 2);
|
||
|
||
// 绘制渐变背景
|
||
CGPoint startPoint = CGPointMake(0, 0);
|
||
CGPoint endPoint = CGPointMake(iconSize.width, iconSize.height);
|
||
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
|
||
|
||
// 释放资源
|
||
CGGradientRelease(gradient);
|
||
CGColorSpaceRelease(colorSpace);
|
||
|
||
// 添加圆角效果(模拟iOS图标圆角)
|
||
UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, iconSize.width, iconSize.height) cornerRadius:iconSize.width * 0.2];
|
||
[roundedRect addClip];
|
||
|
||
// 添加文字
|
||
NSString *text = @"聚友\n棋牌";
|
||
NSDictionary *attributes = @{
|
||
NSFontAttributeName: [UIFont boldSystemFontOfSize:18],
|
||
NSForegroundColorAttributeName: [UIColor whiteColor],
|
||
NSParagraphStyleAttributeName: ({
|
||
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
|
||
style.alignment = NSTextAlignmentCenter;
|
||
style;
|
||
})
|
||
};
|
||
|
||
CGSize textSize = [text boundingRectWithSize:iconSize
|
||
options:NSStringDrawingUsesLineFragmentOrigin
|
||
attributes:attributes
|
||
context:nil].size;
|
||
|
||
CGRect textRect = CGRectMake((iconSize.width - textSize.width) / 2,
|
||
(iconSize.height - textSize.height) / 2,
|
||
textSize.width,
|
||
textSize.height);
|
||
|
||
[text drawInRect:textRect withAttributes:attributes];
|
||
|
||
UIImage *defaultIcon = UIGraphicsGetImageFromCurrentImageContext();
|
||
UIGraphicsEndImageContext();
|
||
|
||
NSLog(@"🔍 ✅ 创建默认应用图标");
|
||
return defaultIcon;
|
||
}
|
||
|
||
+ (NSString *)validateAndFixURL:(NSString *)url {
|
||
if (!url || url.length == 0) {
|
||
return nil;
|
||
}
|
||
|
||
// 移除首尾空格
|
||
NSString *trimmedURL = [url stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||
|
||
NSLog(@"🔍 开始验证URL: %@", trimmedURL);
|
||
|
||
// 检查是否是自定义scheme(如http://sharegame/...)
|
||
if ([trimmedURL hasPrefix:@"http://sharegame/"] || [trimmedURL hasPrefix:@"https://sharegame/"]) {
|
||
// 将自定义的sharegame域名替换为合法的域名
|
||
NSString *fixedURL = [trimmedURL stringByReplacingOccurrencesOfString:@"http://sharegame/" withString:@"https://game.example.com/"];
|
||
fixedURL = [fixedURL stringByReplacingOccurrencesOfString:@"https://sharegame/" withString:@"https://game.example.com/"];
|
||
NSLog(@"🔍 修复自定义域名URL: %@ -> %@", trimmedURL, fixedURL);
|
||
return fixedURL;
|
||
}
|
||
|
||
// 检查是否已经有协议头
|
||
if (![trimmedURL hasPrefix:@"http://"] && ![trimmedURL hasPrefix:@"https://"] && ![trimmedURL hasPrefix:@"ftp://"]) {
|
||
// 如果没有协议头,添加https://
|
||
NSString *fixedURL = [@"https://" stringByAppendingString:trimmedURL];
|
||
NSLog(@"🔍 添加协议头: %@ -> %@", trimmedURL, fixedURL);
|
||
return fixedURL;
|
||
}
|
||
|
||
// 验证URL是否有效
|
||
NSURL *testURL = [NSURL URLWithString:trimmedURL];
|
||
if (testURL && testURL.scheme && testURL.host) {
|
||
NSLog(@"🔍 ✅ URL格式有效: %@", trimmedURL);
|
||
return trimmedURL;
|
||
}
|
||
|
||
// 如果URL无效,尝试进行URL编码
|
||
NSString *encodedURL = [trimmedURL stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
|
||
if (encodedURL) {
|
||
NSURL *encodedTestURL = [NSURL URLWithString:encodedURL];
|
||
if (encodedTestURL && encodedTestURL.scheme && encodedTestURL.host) {
|
||
NSLog(@"🔍 ✅ URL编码后有效: %@ -> %@", trimmedURL, encodedURL);
|
||
return encodedURL;
|
||
}
|
||
}
|
||
|
||
NSLog(@"🔍 ❌ URL无法修复,将作为文本处理: %@", trimmedURL);
|
||
return nil;
|
||
}
|
||
|
||
+ (void)shareWithSystemShareContent:(id)shareContent
|
||
completion:(void (^ _Nullable)(BOOL success))completion {
|
||
NSLog(@"🔍 [QQShareManager] 开始使用ShareContent对象进行系统分享");
|
||
|
||
// 主线程检查
|
||
if (![NSThread isMainThread]) {
|
||
NSLog(@"⚠️ [QQShareManager] 检测到非主线程调用,切换到主线程");
|
||
dispatch_async(dispatch_get_main_queue(), ^{
|
||
[self shareWithSystemShareContent:shareContent completion:completion];
|
||
});
|
||
return;
|
||
}
|
||
|
||
// 权限检查
|
||
if (![self canSafelyPresentSharePanel]) {
|
||
NSLog(@"❌ [QQShareManager] 无法安全呈现系统分享面板");
|
||
if (completion) {
|
||
completion(NO);
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 检查ShareContent对象
|
||
if (!shareContent) {
|
||
NSLog(@"❌ [QQShareManager] ShareContent对象为空");
|
||
if (completion) {
|
||
completion(NO);
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 通过KVC从ShareContent对象中提取参数
|
||
NSString *title = nil;
|
||
NSString *description = nil;
|
||
NSString *url = nil;
|
||
NSString *type = nil;
|
||
|
||
@try {
|
||
// 尝试获取标题
|
||
if ([shareContent respondsToSelector:@selector(valueForKey:)]) {
|
||
title = [shareContent valueForKey:@"title"];
|
||
description = [shareContent valueForKey:@"desc"];
|
||
url = [shareContent valueForKey:@"webpageUrl"];
|
||
type = [shareContent valueForKey:@"type"];
|
||
}
|
||
|
||
NSLog(@"🔍 [QQShareManager] 从ShareContent提取参数:");
|
||
NSLog(@"🔍 - 标题: %@", title ?: @"(无)");
|
||
NSLog(@"🔍 - 描述: %@", description ?: @"(无)");
|
||
NSLog(@"🔍 - 链接: %@", url ?: @"(无)");
|
||
NSLog(@"🔍 - 类型: %@", type ?: @"(无)");
|
||
|
||
} @catch (NSException *exception) {
|
||
NSLog(@"❌ [QQShareManager] 解析ShareContent时发生异常: %@", exception.reason);
|
||
if (completion) {
|
||
completion(NO);
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 检查分享类型,决定分享方式
|
||
BOOL isScreenshotShare = [type intValue] == 2;
|
||
NSLog(@"🔍 [QQShareManager] 分享类型: %@", isScreenshotShare ? @"截图分享" : @"文本分享");
|
||
|
||
// 设置默认值
|
||
if (!title || title.length == 0) {
|
||
title = isScreenshotShare ? @"游戏截图分享" : @"游戏分享";
|
||
}
|
||
if (!description || description.length == 0) {
|
||
description = isScreenshotShare ? @"精彩游戏画面分享" : @"精彩游戏内容分享";
|
||
}
|
||
|
||
// 构建分享内容项
|
||
NSMutableArray *shareItems = [NSMutableArray array];
|
||
|
||
if (isScreenshotShare) {
|
||
// 截图分享模式
|
||
NSLog(@"🔍 [QQShareManager] 执行截图分享");
|
||
|
||
// 获取屏幕截图
|
||
UIImage *screenImage = [FuncPublic getImageWithFullScreenshot];
|
||
if (screenImage) {
|
||
[shareItems addObject:screenImage];
|
||
NSLog(@"🔍 [QQShareManager] 添加屏幕截图,尺寸: %.0fx%.0f", screenImage.size.width, screenImage.size.height);
|
||
description = @"精彩游戏画面分享";
|
||
// 为截图添加文字说明
|
||
NSString *combinedText = nil;
|
||
if (title && description) {
|
||
combinedText = [NSString stringWithFormat:@"%@\n%@", title, description];
|
||
} else if (title) {
|
||
combinedText = title;
|
||
} else if (description) {
|
||
combinedText = description;
|
||
}
|
||
|
||
if (combinedText) {
|
||
[shareItems addObject:combinedText];
|
||
NSLog(@"🔍 [QQShareManager] 添加截图说明文字: %@", combinedText);
|
||
}
|
||
} else {
|
||
NSLog(@"❌ [QQShareManager] 截图获取失败,降级为文本分享");
|
||
isScreenshotShare = NO; // 降级处理
|
||
}
|
||
}
|
||
|
||
if (!isScreenshotShare) {
|
||
// 纯文本分享模式(真正的纯文本,不包含URL和图标)
|
||
NSLog(@"🔍 [QQShareManager] 执行纯文本分享(仅文本内容)");
|
||
|
||
// 组合完整的分享文本
|
||
NSMutableString *shareText = [NSMutableString string];
|
||
|
||
// 添加标题
|
||
if (title && title.length > 0) {
|
||
[shareText appendString:title];
|
||
}
|
||
|
||
// 添加描述
|
||
if (description && description.length > 0) {
|
||
if (shareText.length > 0) {
|
||
[shareText appendString:@"\n\n"]; // 使用单个换行符
|
||
}
|
||
[shareText appendString:description];
|
||
}
|
||
|
||
// 仅添加组合文本到分享项目(纯文本分享)
|
||
if (shareText.length > 0) {
|
||
[shareItems addObject:[shareText copy]];
|
||
NSLog(@"🔍 [QQShareManager] 添加纯文本内容: %@", shareText);
|
||
} else {
|
||
// 如果没有任何文本,使用默认文本
|
||
[shareItems addObject:@"来自进贤聚友棋牌的精彩内容分享"];
|
||
NSLog(@"🔍 [QQShareManager] 使用默认纯文本内容");
|
||
}
|
||
|
||
// 纯文本分享:不添加URL,不添加应用图标
|
||
NSLog(@"🔍 [QQShareManager] 纯文本分享模式:不包含URL和图标");
|
||
}
|
||
|
||
// 检查分享内容
|
||
if (shareItems.count == 0) {
|
||
NSLog(@"❌ [QQShareManager] 没有有效的分享内容");
|
||
if (completion) {
|
||
completion(NO);
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 详细的调试日志
|
||
NSLog(@"🔍 ======== ShareContent系统分享详情 ========");
|
||
NSLog(@"🔍 分享类型: %@", isScreenshotShare ? @"截图分享" : @"纯文本分享");
|
||
NSLog(@"🔍 原始标题: %@", title ?: @"(无)");
|
||
NSLog(@"🔍 原始描述: %@", description ?: @"(无)");
|
||
NSLog(@"🔍 传入URL: %@", url ?: @"(无)");
|
||
if (!isScreenshotShare) {
|
||
NSLog(@"🔍 纯文本模式:不包含URL和图标");
|
||
}
|
||
NSLog(@"🔍 type字段: %@", type ?: @"(无)");
|
||
NSLog(@"🔍 分享项目数量: %lu", (unsigned long)shareItems.count);
|
||
|
||
// 打印所有分享项目(与shareWithSystemShare保持一致)
|
||
for (NSInteger i = 0; i < shareItems.count; i++) {
|
||
id item = shareItems[i];
|
||
if ([item isKindOfClass:[NSString class]]) {
|
||
NSLog(@"🔍 项目%ld (文本): %@", (long)i+1, item);
|
||
} else if ([item isKindOfClass:[NSURL class]]) {
|
||
NSLog(@"🔍 项目%ld (URL): %@", (long)i+1, [(NSURL *)item absoluteString]);
|
||
} else if ([item isKindOfClass:[UIImage class]]) {
|
||
UIImage *image = (UIImage *)item;
|
||
NSLog(@"🔍 项目%ld (图片): %.0fx%.0f", (long)i+1, image.size.width, image.size.height);
|
||
} else {
|
||
NSLog(@"🔍 项目%ld (其他): %@", (long)i+1, item);
|
||
}
|
||
}
|
||
NSLog(@"🔍 =========================================");
|
||
|
||
NSLog(@"🔍 [QQShareManager] 准备分享 %lu 个项目", (unsigned long)shareItems.count);
|
||
|
||
// 获取顶层视图控制器
|
||
UIViewController *topViewController = [self getTopViewController];
|
||
if (!topViewController) {
|
||
NSLog(@"❌ [QQShareManager] 无法获取顶层视图控制器");
|
||
if (completion) {
|
||
completion(NO);
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 创建UIActivityViewController
|
||
UIActivityViewController *activityVC = [[UIActivityViewController alloc]
|
||
initWithActivityItems:shareItems
|
||
applicationActivities:nil];
|
||
|
||
// 排除不需要的分享选项(可选)
|
||
activityVC.excludedActivityTypes = @[
|
||
UIActivityTypeAssignToContact,
|
||
UIActivityTypePostToVimeo,
|
||
UIActivityTypePrint,
|
||
UIActivityTypeAirDrop // 排除AirDrop避免意外分享
|
||
];
|
||
|
||
// 设置完成回调(与shareWithSystemShare保持一致的日志格式)
|
||
activityVC.completionWithItemsHandler = ^(UIActivityType activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
|
||
NSLog(@"🔍 ======== ShareContent分享结果详情 ========");
|
||
NSLog(@"🔍 分享结果: %@", completed ? @"✅ 成功" : @"❌ 取消/失败");
|
||
NSLog(@"🔍 分享到应用: %@", activityType ?: @"(未知)");
|
||
|
||
if (activityError) {
|
||
NSLog(@"🔍 分享错误: %@", activityError.localizedDescription);
|
||
|
||
// 特别处理 RunningBoard 相关错误(与shareWithSystemShare一致)
|
||
if ([activityError.domain isEqualToString:@"RBSServiceErrorDomain"]) {
|
||
NSLog(@"🔍 ⚠️ 检测到RunningBoard权限错误,这通常是系统级权限问题");
|
||
NSLog(@"🔍 ⚠️ 建议检查应用是否在前台,以及设备权限设置");
|
||
}
|
||
|
||
if (completion) {
|
||
completion(NO);
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 特别标记QQ分享(与shareWithSystemShare一致)
|
||
if (activityType && ([activityType containsString:@"QQ"] || [activityType containsString:@"qq"])) {
|
||
NSLog(@"🔍 ✅ QQ分享完成");
|
||
}
|
||
|
||
NSLog(@"🔍 =========================================");
|
||
|
||
if (completion) {
|
||
completion(completed);
|
||
}
|
||
};
|
||
|
||
// iPad适配 - 设置popover
|
||
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
||
if (activityVC.popoverPresentationController) {
|
||
activityVC.popoverPresentationController.sourceView = topViewController.view;
|
||
activityVC.popoverPresentationController.sourceRect = CGRectMake(
|
||
topViewController.view.bounds.size.width / 2,
|
||
topViewController.view.bounds.size.height / 2,
|
||
1, 1
|
||
);
|
||
activityVC.popoverPresentationController.permittedArrowDirections = 0;
|
||
}
|
||
}
|
||
|
||
// 呈现分享面板
|
||
[topViewController presentViewController:activityVC animated:YES completion:^{
|
||
NSLog(@"🔍 [QQShareManager] 系统分享面板已呈现");
|
||
}];
|
||
}
|
||
|
||
@end |