diff --git a/README.md b/README.md index d15bfe3..eeda1ed 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,9 @@ flutter版腾讯(QQ)SDK * [Flutter版深度链接](https://github.com/RxReader/link_kit) * [Flutter版walle渠道打包工具](https://github.com/RxReader/walle_kit) -## dart/flutter 私服 +## Dart/Flutter Pub 私服 -* [simple_pub_server](https://github.com/rxreader/simple_pub_server) +* [simple_pub_server](https://github.com/RxReader/simple_pub_server) ## 相关文档 @@ -25,13 +25,9 @@ flutter版腾讯(QQ)SDK * [QQ 创建、填写及校验UniversalLinks](https://wiki.connect.qq.com/%E5%A1%AB%E5%86%99%E5%8F%8A%E6%A0%A1%E9%AA%8Cuniversallinks) * [Apple Universal Links](https://developer.apple.com/library/archive/documentation/General/Conceptual/AppSearch/UniversalLinks.html) -## FEATURE - -* 5.x.y 通过配置 pubspec.yaml 和 cli 抹平 Android/iOS 平台的复杂配置 - ## 开始使用 -### android +### Android * 接入 @@ -49,93 +45,52 @@ android { # 混淆已打入 Library,随 Library 引用,自动添加到 apk 打包混淆 ``` -### ios - -* 申明 +### iOS ``` -出于插件的基本需求,将 SDK 的 module.modulemap 内容修改 +# 不需要做任何额外接入工作 +# 配置已集成到脚本里 +``` -改前 -module TencentOpenApi{ - umbrella header "TencentOpenApiUmbrellaHeader.h" - export * -} +* Universal Links -改后 -framework module TencentOpenApi{ - umbrella header "TencentOpenApiUmbrellaHeader.h" - export * +apple-app-site-association - 通过 https://${your applinks domain}/.well-known/apple-app-site-association 链接可访问 + +示例: + +https://${your applinks domain}/universal_link/${example_app}/qq_conn/${appId} + +```json +{ + "applinks": { + "apps": [], + "details": [ + { + "appID": "${your team id}.${your app bundle id}", + "paths": [ + "/universal_link/${example_app}/qq_conn/${your tencent app id}/*" + ] + } + ] + } } ``` -* 接入 +> ⚠️ 很多 SDK 都会用到 universal_link,可为不同 SDK 分配不同的 path 以作区分 -``` -在Xcode中,选择你的工程设置项,选中“TARGETS”一栏,在“info”标签栏的“URL type“添加“URL scheme”为你所注册的应用程序id - -URL Types -tencent: identifier=tencent schemes=tencent${appId} -``` - -``` -iOS 9系统策略更新,限制了http协议的访问,此外应用需要在“Info.plist”中将要使用的URL Schemes列为白名单,才可正常检查其他应用是否安装。 - - LSApplicationQueriesSchemes - - tim - mqq - mqqapi - mqqbrowser - mttbrowser - mqqOpensdkSSoLogin - mqqopensdkapiV2 - mqqopensdkapiV4 - mqzone - mqzoneopensdk - mqzoneopensdkapi - mqzoneopensdkapi19 - mqzoneopensdkapiV2 - mqqapiwallet - mqqopensdkfriend - mqqopensdkavatar - mqqopensdkminiapp - mqqopensdkdataline - mqqgamebindinggroup - mqqopensdkgrouptribeshare - tencentapi.qq.reqContent - tencentapi.qzone.reqContent - mqqthirdappgroup - mqqopensdklaunchminiapp - mqqopensdkproxylogin - mqqopensdknopasteboard - mqqopensdkcheckauth - -``` - -``` -Universal Links - -Capabilities -> Associated Domain -> Domain -> applinks:${your applinks} -``` - -### flutter +### Flutter |分享类型|说说(图/文/视频)|文本|图片|音乐|视频|网页| |:---:|:---:|:---:|:---:|:---:|:---:|:---:| |QQ|不支持|不支持|支持|支持|不支持|支持| |QZone|支持|不支持|不支持|不支持|不支持|支持| -* ⚠️⚠️⚠️ registerApp 前必须先调用 setIsPermissionGranted [issues/60](https://github.com/RxReader/tencent_kit/issues/60) [issues/79](https://github.com/RxReader/tencent_kit/issues/79) +* 注意 -* break change - * 4.0.0: 按标准插件书写重构 - * 3.1.0: 新增 setIsPermissionGranted 函数,设置是否已授权获取设备信息/是否同意隐私协议 - * 3.0.0: 重构 - * 2.1.0: nullsafety & 不再支持 Android embedding v1 & Tencent 单例 +⚠️⚠️⚠️ registerApp 前必须先调用 setIsPermissionGranted [issues/60](https://github.com/RxReader/tencent_kit/issues/60) [issues/79](https://github.com/RxReader/tencent_kit/issues/79) -* compat - * flutter 2.5 兼容问题 [issues/54](https://github.com/RxReader/tencent_kit/issues/54) +* 兼容 + flutter 2.5 兼容问题 [issues/54](https://github.com/RxReader/tencent_kit/issues/54) ``` post_install do |installer| installer.pods_project.targets.each do |target| @@ -149,23 +104,21 @@ Capabilities -> Associated Domain -> Domain -> applinks:${your applinks} end ``` -* snapshot +* 配置 -``` -dependencies: - tencent_kit: - git: - url: https://github.com/rxreader/tencent_kit.git -``` - -* release - -``` +```yaml dependencies: tencent_kit: ^${latestTag} +# tencent_kit: +# git: +# url: https://github.com/rxreader/tencent_kit.git + +tencent_kit: + app_id: ${your tencent app id} + universal_link: https://${your applinks domain}/universal_link/${example_app}/qq_conn/${your tencent app id}/ ``` -* example +## 示例 [示例](./example/lib/main.dart) diff --git a/example/lib/main.dart b/example/lib/main.dart index e72a8f5..7b80312 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -9,7 +9,8 @@ import 'package:tencent_kit_example/api/model/tencent_api_resp.dart'; import 'package:tencent_kit_example/api/model/tencent_unionid_resp.dart'; import 'package:tencent_kit_example/api/tencent_api.dart'; -const String _TENCENT_APPID = 'your tencent appId'; +const String _TENCENT_APPID = 'your tencent app id'; +const String _UNIVERSAL_LINK = 'your tencent universal link'; void main() { runApp(MyApp()); @@ -85,7 +86,7 @@ class _HomeState extends State { ListTile( title: Text('注册APP'), onTap: () async { - await TencentKitPlatform.instance.registerApp(appId: _TENCENT_APPID); + await TencentKitPlatform.instance.registerApp(appId: _TENCENT_APPID, universalLink: _UNIVERSAL_LINK); _showTips('注册APP', '注册成功'); }, ), diff --git a/example/pubspec.yaml b/example/pubspec.yaml index fcefbd7..9cee770 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -91,3 +91,7 @@ flutter: # # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages + +tencent_kit: + app_id: 123456789 + universal_link: https://www.yourdomain.com/universal_link/example_app/qq_conn/123456789/ diff --git a/ios/tencent_kit.podspec b/ios/tencent_kit.podspec index 6d12825..971b4ca 100644 --- a/ios/tencent_kit.podspec +++ b/ios/tencent_kit.podspec @@ -6,14 +6,23 @@ pubspec = YAML.load_file(File.join('..', 'pubspec.yaml')) library_version = pubspec['version'].gsub('+', '-') +current_dir = Dir.pwd +calling_dir = File.dirname(__FILE__) +project_dir = calling_dir.slice(0..(calling_dir.index('/.symlinks'))) +root_project_dir = calling_dir.slice(0..(calling_dir.index('/ios/.symlinks'))) +cfg = YAML.load_file(File.join(root_project_dir, 'pubspec.yaml')) +if cfg['tencent_kit'] && (cfg['tencent_kit']['app_id'] && cfg['tencent_kit']['universal_link']) + app_id = cfg['tencent_kit']['app_id'] + universal_link = cfg['tencent_kit']['universal_link'] + system("ruby #{current_dir}/tencent_setup.rb -a #{app_id} -u #{universal_link} -p #{project_dir} -n Runner.xcodeproj") +end + Pod::Spec.new do |s| s.name = 'tencent_kit' s.version = library_version - s.summary = 'A powerful Flutter plugin allowing developers to auth/share with natvie Android & iOS Tencent SDKs.' - s.description = <<-DESC -A powerful Flutter plugin allowing developers to auth/share with natvie Android & iOS Tencent SDKs. - DESC - s.homepage = 'http://example.com' + s.summary = pubspec['description'] + s.description = pubspec['description'] + s.homepage = pubspec['homepage'] s.license = { :file => '../LICENSE' } s.author = { 'Your Company' => 'email@example.com' } s.source = { :path => '.' } diff --git a/ios/tencent_setup.rb b/ios/tencent_setup.rb new file mode 100644 index 0000000..20a4a5e --- /dev/null +++ b/ios/tencent_setup.rb @@ -0,0 +1,156 @@ +# +# 参考文献 +# https://github.com/firebase/flutterfire/blob/master/packages/firebase_crashlytics/firebase_crashlytics/ios/crashlytics_add_upload_symbols +# https://github.com/MagicalWater/Base-APP-Env/blob/master/fastlane/actions/xcode_parse.rb +# + +require 'xcodeproj' +require 'plist' +require 'optparse' + +# Dictionary to hold command line arguments +options_dict = {} + +# Parse command line arguments into options_dict +OptionParser.new do |options| + options.banner = "Setup the QQ to an Xcode target." + + options.on("-p", "--projectDirectory=DIRECTORY", String, "Directory of the Xcode project") do |dir| + options_dict[:project_dir] = dir + end + + options.on("-n", "--projectName=NAME", String, "Name of the Xcode project (ex: Runner.xcodeproj)") do |name| + options_dict[:project_name] = name + end + + options.on("-a", "--appId=APPID", String, "App ID for QQ") do |opts| + options_dict[:app_id] = opts + end + + options.on("-u", "--universalLink=UNIVERSALLINK", String, "Universal Link for QQ") do |opts| + options_dict[:universal_link] = opts + end +end.parse! + +# Minimum required arguments are a project directory and project name +unless (options_dict[:project_dir] and options_dict[:project_name]) + abort("Must provide a project directory and project name.\n") +end + +# Path to the Xcode project to modify +project_path = File.join(options_dict[:project_dir], options_dict[:project_name]) + +unless (File.exist?(project_path)) + abort("Project at #{project_path} does not exist. Please check paths manually.\n"); +end + +# Actually open and modify the project +project = Xcodeproj::Project.open(project_path) +project.targets.each do |target| + if (target.name == "Runner") + app_id = options_dict[:app_id] + universal_link = options_dict[:universal_link] + applinks = "applinks:#{URI.parse(universal_link).host}" + + sectionObject = {} + project.objects.each do |object| + if object.uuid == target.uuid + sectionObject = object + break + end + end + sectionObject.build_configurations.each do |config| + infoplist = config.build_settings["INFOPLIST_FILE"] + if (!infoplist) + puts("INFOPLIST_FILE is not exist") + exit(0) + end + infoplistFile = File.join(options_dict[:project_dir], infoplist) + if !File.exist?(infoplistFile) + puts("#{infoplist} is not exist") + exit(0) + end + result = Plist.parse_xml(infoplistFile, marshal: false) + if (!result) + result = {} + end + urlTypes = result["CFBundleURLTypes"] + if (!urlTypes) + urlTypes = [] + result["CFBundleURLTypes"] = urlTypes + end + isUrlTypeExist = urlTypes.any? { |urlType| urlType["CFBundleURLSchemes"] && (urlType["CFBundleURLSchemes"].include? "tencent#{app_id}") } + if (!isUrlTypeExist) + urlTypes << { + "CFBundleTypeRole": "Editor", + "CFBundleURLName": "tencent", + "CFBundleURLSchemes": [ "tencent#{app_id}" ] + } + File.write(infoplistFile, Plist::Emit.dump(result)) + end + queriesSchemes = result["LSApplicationQueriesSchemes"] + if (!queriesSchemes) + queriesSchemes = [] + result["LSApplicationQueriesSchemes"] = queriesSchemes + end + tencentSchemes = [ + "mqq", + "mqqapi", + "tim", + "mqqopensdknopasteboard", + "mqqopensdknopasteboardios16", + "mqqopensdkapiV2", + "mqqgamebindinggroup", + "mqqopensdkavatar", + "mqqopensdkfriend", + "mqqopensdklaunchminiapp", + "mqzone", + "tencentapi.qq.reqContent", + "tencentapi.qzone.reqContent", + "mqqthirdappgroup", + "mqqopensdkminiapp", + ] + if (tencentSchemes.any? { |scheme| !(queriesSchemes.include? scheme)} ) + tencentSchemes.each do |scheme| + if (!(queriesSchemes.include? scheme)) + queriesSchemes << scheme + end + end + File.write(infoplistFile, Plist::Emit.dump(result)) + end + end + sectionObject.build_configurations.each do |config| + codeSignEntitlements = config.build_settings["CODE_SIGN_ENTITLEMENTS"] + if (!codeSignEntitlements) + codeSignEntitlements = "Runner/Runner.entitlements" + config.build_settings["CODE_SIGN_ENTITLEMENTS"] = codeSignEntitlements + project.save() + end + codeSignEntitlementsFile = File.join(options_dict[:project_dir], codeSignEntitlements) + if !File.exist?(codeSignEntitlementsFile) + content = Plist::Emit.dump({}) + File.write(codeSignEntitlementsFile, content) + end + runnerTargetMainGroup = project.main_group.find_subpath('Runner', false) + isRefExist = runnerTargetMainGroup.files.any? { |file| file.path.include? File.basename(codeSignEntitlementsFile) } + if !isRefExist + runnerTargetMainGroup.new_reference(File.basename(codeSignEntitlementsFile)) + project.save() + end + result = Plist.parse_xml(codeSignEntitlementsFile, marshal: false) + if (!result) + result = {} + end + domains = result["com.apple.developer.associated-domains"] + if (!domains) + domains = [] + result["com.apple.developer.associated-domains"] = domains + end + isApplinksExist = domains.include? applinks + if (!isApplinksExist) + domains << applinks + File.write(codeSignEntitlementsFile, Plist::Emit.dump(result)) + end + end + end +end