├── ios ├── Assets │ └── .gitkeep ├── Classes │ ├── UIApplication+Hook.h │ ├── TencentKitPlugin.h │ ├── UIApplication+Hook.m │ └── TencentKitPlugin.m ├── Libraries │ └── TencentOpenAPI.framework │ │ ├── TencentOpenAPI │ │ └── Headers │ │ ├── module.modulemap │ │ ├── TencentOpenApiUmbrellaHeader.h │ │ ├── QQApiInterface.h │ │ ├── SDKDef.h │ │ └── TencentOAuth.h ├── .gitignore ├── .clang-format ├── tencent_kit.podspec └── tencent_setup.rb ├── android ├── consumer-rules.pro ├── settings.gradle ├── consumer-vendor-rules.pro ├── libs │ └── open_sdk_3.5.17.3_r75955a58_lite.jar ├── .gitignore ├── src │ ├── main │ │ ├── java │ │ │ └── io │ │ │ │ └── github │ │ │ │ └── v7lin │ │ │ │ └── tencent_kit │ │ │ │ └── TencentKitFileProvider.java │ │ ├── res │ │ │ └── xml │ │ │ │ └── tencent_kit_file_paths.xml │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── io │ │ │ └── github │ │ │ └── v7lin │ │ │ └── tencent_kit │ │ │ └── TencentKitPluginTest.java │ └── vendor │ │ └── AndroidManifest.xml └── build.gradle ├── .gitattributes ├── example ├── ohos │ ├── entry │ │ ├── .gitignore │ │ ├── src │ │ │ ├── main │ │ │ │ ├── resources │ │ │ │ │ ├── base │ │ │ │ │ │ ├── profile │ │ │ │ │ │ │ ├── main_pages.json │ │ │ │ │ │ │ └── buildinfo.json5 │ │ │ │ │ │ ├── media │ │ │ │ │ │ │ └── icon.png │ │ │ │ │ │ └── element │ │ │ │ │ │ │ ├── color.json │ │ │ │ │ │ │ └── string.json │ │ │ │ │ ├── zh_CN │ │ │ │ │ │ └── element │ │ │ │ │ │ │ └── string.json │ │ │ │ │ └── en_US │ │ │ │ │ │ └── element │ │ │ │ │ │ └── string.json │ │ │ │ ├── ets │ │ │ │ │ ├── entryability │ │ │ │ │ │ └── EntryAbility.ets │ │ │ │ │ └── pages │ │ │ │ │ │ └── Index.ets │ │ │ │ └── module.json5 │ │ │ └── ohosTest │ │ │ │ ├── resources │ │ │ │ └── base │ │ │ │ │ ├── profile │ │ │ │ │ └── test_pages.json │ │ │ │ │ ├── media │ │ │ │ │ └── icon.png │ │ │ │ │ └── element │ │ │ │ │ ├── color.json │ │ │ │ │ └── string.json │ │ │ │ ├── ets │ │ │ │ ├── test │ │ │ │ │ ├── List.test.ets │ │ │ │ │ └── Ability.test.ets │ │ │ │ ├── testability │ │ │ │ │ ├── pages │ │ │ │ │ │ └── Index.ets │ │ │ │ │ └── TestAbility.ets │ │ │ │ └── testrunner │ │ │ │ │ └── OpenHarmonyTestRunner.ts │ │ │ │ └── module.json5 │ │ ├── oh-package.json5 │ │ ├── hvigorfile.ts │ │ └── build-profile.json5 │ ├── AppScope │ │ ├── resources │ │ │ └── base │ │ │ │ ├── media │ │ │ │ └── app_icon.png │ │ │ │ └── element │ │ │ │ └── string.json │ │ └── app.json5 │ ├── .gitignore │ ├── hvigor │ │ └── hvigor-config.json5 │ ├── build-profile.json5 │ ├── hvigorfile.ts │ └── oh-package.json5 ├── ios │ ├── Flutter │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── AppFrameworkInfo.plist │ ├── Runner │ │ ├── AppDelegate.h │ │ ├── Assets.xcassets │ │ │ ├── LaunchImage.imageset │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ ├── README.md │ │ │ │ └── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ ├── Icon-App-20x20@1x.png │ │ │ │ ├── Icon-App-20x20@2x.png │ │ │ │ ├── Icon-App-20x20@3x.png │ │ │ │ ├── Icon-App-29x29@1x.png │ │ │ │ ├── Icon-App-29x29@2x.png │ │ │ │ ├── Icon-App-29x29@3x.png │ │ │ │ ├── Icon-App-40x40@1x.png │ │ │ │ ├── Icon-App-40x40@2x.png │ │ │ │ ├── Icon-App-40x40@3x.png │ │ │ │ ├── Icon-App-60x60@2x.png │ │ │ │ ├── Icon-App-60x60@3x.png │ │ │ │ ├── Icon-App-76x76@1x.png │ │ │ │ ├── Icon-App-76x76@2x.png │ │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ │ └── Contents.json │ │ ├── main.m │ │ ├── Runner.entitlements │ │ ├── AppDelegate.m │ │ ├── Base.lproj │ │ │ ├── Main.storyboard │ │ │ └── LaunchScreen.storyboard │ │ └── Info.plist │ ├── Runner.xcodeproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ ├── .gitignore │ ├── RunnerTests │ │ └── RunnerTests.m │ ├── Podfile │ └── Podfile.lock ├── android │ ├── app │ │ ├── src │ │ │ ├── main │ │ │ │ ├── res │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── drawable │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── drawable-v21 │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── values │ │ │ │ │ │ └── styles.xml │ │ │ │ │ └── values-night │ │ │ │ │ │ └── styles.xml │ │ │ │ ├── java │ │ │ │ │ └── io │ │ │ │ │ │ └── github │ │ │ │ │ │ └── v7lin │ │ │ │ │ │ └── tencent_kit_example │ │ │ │ │ │ └── MainActivity.java │ │ │ │ └── AndroidManifest.xml │ │ │ ├── debug │ │ │ │ └── AndroidManifest.xml │ │ │ └── profile │ │ │ │ └── AndroidManifest.xml │ │ └── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ ├── .gitignore │ ├── build.gradle │ └── settings.gradle ├── analysis_options.yaml ├── build.yaml ├── test │ └── widget_test.dart ├── README.md ├── pubspec.yaml ├── .gitignore ├── integration_test │ └── plugin_integration_test.dart └── lib │ ├── api │ ├── model │ │ ├── tencent_unionid_resp.g.dart │ │ ├── tencent_unionid_resp.dart │ │ ├── tencent_api_resp.g.dart │ │ └── tencent_api_resp.dart │ └── tencent_api.dart │ └── main.dart ├── ohos ├── src │ ├── libs │ │ └── QQOpenSdk.har │ └── main │ │ ├── module.json5 │ │ └── ets │ │ └── components │ │ └── plugin │ │ └── TencentKitPlugin.ets ├── hvigorfile.ts ├── build-profile.json5 ├── .gitignore ├── oh-package.json5 └── index.ets ├── lib ├── tencent_kit.dart ├── tencent_kit_platform_interface.dart └── src │ ├── model │ ├── resp.g.dart │ └── resp.dart │ ├── constant.dart │ ├── tencent_kit_platform_interface.dart │ └── tencent_kit_method_channel.dart ├── .gitignore ├── pubspec.yaml ├── .github └── workflows │ ├── publish.yml │ └── build.yml ├── test ├── tencent_kit_method_channel_test.dart └── tencent_kit_test.dart ├── LICENSE ├── analysis_options.yaml ├── .metadata ├── CHANGELOG.md └── README.md /ios/Assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /android/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * linguist-language=Dart -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'tencent_kit' 2 | -------------------------------------------------------------------------------- /android/consumer-vendor-rules.pro: -------------------------------------------------------------------------------- 1 | # --- QQ --- 2 | 3 | -keep class com.tencent.** {*;} 4 | -------------------------------------------------------------------------------- /example/ohos/entry/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /node_modules 3 | /oh_modules 4 | /.preview 5 | /build 6 | /.cxx 7 | /.test -------------------------------------------------------------------------------- /ios/Classes/UIApplication+Hook.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface UIApplication (Hook) 4 | @end 5 | -------------------------------------------------------------------------------- /ohos/src/libs/QQOpenSdk.har: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/ohos/src/libs/QQOpenSdk.har -------------------------------------------------------------------------------- /example/ohos/entry/src/main/resources/base/profile/main_pages.json: -------------------------------------------------------------------------------- 1 | { 2 | "src": [ 3 | "pages/Index" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /ios/Classes/TencentKitPlugin.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface TencentKitPlugin : NSObject 4 | @end 5 | -------------------------------------------------------------------------------- /example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json: -------------------------------------------------------------------------------- 1 | { 2 | "src": [ 3 | "testability/pages/Index" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /android/libs/open_sdk_3.5.17.3_r75955a58_lite.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/android/libs/open_sdk_3.5.17.3_r75955a58_lite.jar -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /ios/Libraries/TencentOpenAPI.framework/TencentOpenAPI: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/ios/Libraries/TencentOpenAPI.framework/TencentOpenAPI -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .cxx 10 | -------------------------------------------------------------------------------- /example/ohos/AppScope/resources/base/media/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/example/ohos/AppScope/resources/base/media/app_icon.png -------------------------------------------------------------------------------- /example/ohos/entry/src/main/resources/base/media/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/example/ohos/entry/src/main/resources/base/media/icon.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/ohos/entry/src/ohosTest/resources/base/media/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/example/ohos/entry/src/ohosTest/resources/base/media/icon.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /example/ohos/entry/src/main/resources/base/profile/buildinfo.json5: -------------------------------------------------------------------------------- 1 | { 2 | "string": [ 3 | { 4 | "name": "enable_impeller", 5 | "value": "true" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /ios/Libraries/TencentOpenAPI.framework/Headers/module.modulemap: -------------------------------------------------------------------------------- 1 | framework module TencentOpenApi{ 2 | umbrella header "TencentOpenApiUmbrellaHeader.h" 3 | export * 4 | } 5 | -------------------------------------------------------------------------------- /ohos/hvigorfile.ts: -------------------------------------------------------------------------------- 1 | // Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. 2 | export { harTasks } from '@ohos/hvigor-ohos-plugin'; -------------------------------------------------------------------------------- /example/ohos/AppScope/resources/base/element/string.json: -------------------------------------------------------------------------------- 1 | { 2 | "string": [ 3 | { 4 | "name": "app_name", 5 | "value": "tencent_kit_example" 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /example/ohos/entry/src/main/resources/base/element/color.json: -------------------------------------------------------------------------------- 1 | { 2 | "color": [ 3 | { 4 | "name": "start_window_background", 5 | "value": "#FFFFFF" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /ohos/build-profile.json5: -------------------------------------------------------------------------------- 1 | { 2 | "apiType": "stageMode", 3 | "buildOption": { 4 | }, 5 | "targets": [ 6 | { 7 | "name": "default" 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /example/ohos/entry/src/ohosTest/resources/base/element/color.json: -------------------------------------------------------------------------------- 1 | { 2 | "color": [ 3 | { 4 | "name": "start_window_background", 5 | "value": "#FFFFFF" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ohos/src/main/module.json5: -------------------------------------------------------------------------------- 1 | { 2 | "module": { 3 | "name": "tencent_kit", 4 | "type": "har", 5 | "deviceTypes": [ 6 | "default", 7 | "tablet" 8 | ] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxReader/tencent_kit/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ohos/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /oh_modules 3 | /.preview 4 | /.idea 5 | /build 6 | /.cxx 7 | /.test 8 | /BuildProfile.ets 9 | /oh-package-lock.json5 10 | /local.properties 11 | *.har 12 | !/src/libs/QQOpenSdk.har -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # Take our settings from the repo's main analysis_options.yaml file, but include 2 | # an additional rule to validate that public members are documented. 3 | 4 | include: ../analysis_options.yaml 5 | -------------------------------------------------------------------------------- /lib/tencent_kit.dart: -------------------------------------------------------------------------------- 1 | /// tencent_kit 2 | // ignore: unnecessary_library_name 3 | library tencent_kit; 4 | 5 | export 'src/constant.dart'; 6 | export 'src/model/resp.dart'; 7 | export 'src/tencent_kit_platform_interface.dart'; 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/src/main/java/io/github/v7lin/tencent_kit/TencentKitFileProvider.java: -------------------------------------------------------------------------------- 1 | package io.github.v7lin.tencent_kit; 2 | 3 | import androidx.core.content.FileProvider; 4 | 5 | public final class TencentKitFileProvider extends FileProvider { 6 | } 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/io/github/v7lin/tencent_kit_example/MainActivity.java: -------------------------------------------------------------------------------- 1 | package io.github.v7lin.tencent_kit_example; 2 | 3 | import io.flutter.embedding.android.FlutterActivity; 4 | 5 | public class MainActivity extends FlutterActivity { 6 | } 7 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip 6 | -------------------------------------------------------------------------------- /example/ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /example/ohos/entry/oh-package.json5: -------------------------------------------------------------------------------- 1 | { 2 | "name": "entry", 3 | "version": "1.0.0", 4 | "description": "Please describe the basic information.", 5 | "main": "", 6 | "author": "", 7 | "license": "", 8 | "dependencies": { 9 | "tencent_kit": "../../../ohos", 10 | } 11 | } -------------------------------------------------------------------------------- /example/ohos/AppScope/app.json5: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "bundleName": "io.github.v7lin.tencent_kit_example", 4 | "vendor": "example", 5 | "versionCode": 1000000, 6 | "versionName": "1.0.0", 7 | "icon": "$media:app_icon", 8 | "label": "$string:app_name" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /example/build.yaml: -------------------------------------------------------------------------------- 1 | targets: 2 | $default: 3 | builders: 4 | json_serializable: 5 | options: 6 | any_map: true 7 | explicit_to_json: true 8 | field_rename: snake 9 | generate_for: 10 | exclude: 11 | - lib/**/*.g.dart 12 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/src/main/res/xml/tencent_kit_file_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /lib/tencent_kit_platform_interface.dart: -------------------------------------------------------------------------------- 1 | /// tencent_kit_platform_interface 2 | // ignore: unnecessary_library_name 3 | library tencent_kit_platform_interface; 4 | 5 | export 'src/constant.dart'; 6 | export 'src/model/resp.dart'; 7 | export 'src/tencent_kit_method_channel.dart'; 8 | export 'src/tencent_kit_platform_interface.dart'; 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /example/ohos/entry/src/main/resources/zh_CN/element/string.json: -------------------------------------------------------------------------------- 1 | { 2 | "string": [ 3 | { 4 | "name": "module_desc", 5 | "value": "模块描述" 6 | }, 7 | { 8 | "name": "EntryAbility_desc", 9 | "value": "description" 10 | }, 11 | { 12 | "name": "EntryAbility_label", 13 | "value": "tencent_kit_example" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /example/ios/Runner/Runner.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.associated-domains 6 | 7 | applinks:www.yourdomain.com 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ohos/oh-package.json5: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tencent_kit", 3 | "version": "1.0.0", 4 | "description": "Please describe the basic information.", 5 | "main": "index.ets", 6 | "author": "", 7 | "license": "Apache-2.0", 8 | "dependencies": { 9 | "@ohos/flutter_ohos": "file:./har/flutter.har", 10 | "@tencent/qq-open-sdk": "file:./src/libs/QQOpenSdk.har" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /example/ohos/entry/src/main/resources/base/element/string.json: -------------------------------------------------------------------------------- 1 | { 2 | "string": [ 3 | { 4 | "name": "module_desc", 5 | "value": "module description" 6 | }, 7 | { 8 | "name": "EntryAbility_desc", 9 | "value": "description" 10 | }, 11 | { 12 | "name": "EntryAbility_label", 13 | "value": "tencent_kit_example" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /example/ohos/entry/src/main/resources/en_US/element/string.json: -------------------------------------------------------------------------------- 1 | { 2 | "string": [ 3 | { 4 | "name": "module_desc", 5 | "value": "module description" 6 | }, 7 | { 8 | "name": "EntryAbility_desc", 9 | "value": "description" 10 | }, 11 | { 12 | "name": "EntryAbility_label", 13 | "value": "tencent_kit_example" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /example/ohos/entry/src/ohosTest/resources/base/element/string.json: -------------------------------------------------------------------------------- 1 | { 2 | "string": [ 3 | { 4 | "name": "module_test_desc", 5 | "value": "test ability description" 6 | }, 7 | { 8 | "name": "TestAbility_desc", 9 | "value": "the test ability" 10 | }, 11 | { 12 | "name": "TestAbility_label", 13 | "value": "test label" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | rootProject.buildDir = '../build' 9 | subprojects { 10 | project.buildDir = "${rootProject.buildDir}/${project.name}" 11 | } 12 | subprojects { 13 | project.evaluationDependsOn(':app') 14 | } 15 | 16 | tasks.register("clean", Delete) { 17 | delete rootProject.buildDir 18 | } -------------------------------------------------------------------------------- /example/ohos/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /oh_modules 3 | /local.properties 4 | /.idea 5 | **/build 6 | /.hvigor 7 | .cxx 8 | /.clangd 9 | /.clang-format 10 | /.clang-tidy 11 | **/.test 12 | *.har 13 | **/BuildProfile.ets 14 | **/oh-package-lock.json5 15 | 16 | **/src/main/resources/rawfile/flutter_assets/ 17 | **/libs/arm64-v8a/libapp.so 18 | **/libs/arm64-v8a/libflutter.so 19 | **/libs/arm64-v8a/libvmservice_snapshot.so 20 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | #import "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application 7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 8 | [GeneratedPluginRegistrant registerWithRegistry:self]; 9 | // Override point for customization after application launch. 10 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility in the flutter_test package. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter_test/flutter_test.dart'; 9 | 10 | void main() { 11 | testWidgets('smoke test', (WidgetTester tester) async {}); 12 | } 13 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/Generated.xcconfig 37 | /Flutter/ephemeral/ 38 | /Flutter/flutter_export_environment.sh -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 10 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # tencent_kit_example 2 | 3 | Demonstrates how to use the tencent_kit plugin. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) 13 | 14 | For help getting started with Flutter development, view the 15 | [online documentation](https://docs.flutter.dev/), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 26 | /pubspec.lock 27 | **/doc/api/ 28 | .dart_tool/ 29 | .packages 30 | build/ 31 | 32 | *.sh 33 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /ios/Libraries/TencentOpenAPI.framework/Headers/TencentOpenApiUmbrellaHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // TencentOpenApiUmbrellaHeader.h 3 | // TencentOpenApi_IOS 4 | // 5 | // Created by jyukeizhang(张储祺) on 2020/7/27. 6 | // Copyright © 2020 Tencent. All rights reserved. 7 | // 8 | 9 | #ifndef TencentOpenApiUmbrellaHeader_h 10 | #define TencentOpenApiUmbrellaHeader_h 11 | 12 | #import 13 | 14 | FOUNDATION_EXPORT double StaticLibraryModuleVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char StaticLibraryModuleVersionString[]; 16 | 17 | #import "QQApiInterface.h" 18 | #import "QQApiInterfaceObject.h" 19 | #import "SDKDef.h" 20 | #import "TencentOAuth.h" 21 | 22 | 23 | #endif /* TencentOpenApiUmbrellaHeader_h */ 24 | 25 | -------------------------------------------------------------------------------- /example/ohos/hvigor/hvigor-config.json5: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | { 17 | "modelVersion": "5.0.0", 18 | "dependencies": { 19 | } 20 | } -------------------------------------------------------------------------------- /ohos/index.ets: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | import TencentKitPlugin from './src/main/ets/components/plugin/TencentKitPlugin'; 17 | export default TencentKitPlugin; 18 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: tencent_kit_example 2 | description: Demonstrates how to use the tencent_kit plugin. 3 | publish_to: 'none' 4 | 5 | environment: 6 | sdk: '>=3.0.2 <4.0.0' 7 | flutter: ">=3.3.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | 13 | tencent_kit: 14 | path: ../ 15 | 16 | cupertino_icons: ^1.0.2 17 | flutter_cache_manager: ^3.3.0 18 | json_annotation: ^4.8.1 19 | 20 | dev_dependencies: 21 | integration_test: 22 | sdk: flutter 23 | flutter_test: 24 | sdk: flutter 25 | flutter_lints: any 26 | 27 | build_runner: ^2.0.0 28 | json_serializable: ^6.7.0 29 | 30 | flutter: 31 | uses-material-design: true 32 | 33 | tencent_kit: 34 | app_id: 123456789 35 | universal_link: https://www.yourdomain.com/universal_link/example_app/qq_conn/123456789/ # 可选项目 36 | -------------------------------------------------------------------------------- /ios/.clang-format: -------------------------------------------------------------------------------- 1 | # 基础样式 2 | BasedOnStyle: LLVM 3 | 4 | # 缩进宽度 5 | IndentWidth: 4 6 | 7 | # 圆括号的换行方式 8 | BreakBeforeBraces: Attach 9 | 10 | # 是否允许循环单行 11 | AllowShortLoopsOnASingleLine: false 12 | 13 | # 支持一行的if 14 | AllowShortIfStatementsOnASingleLine: false 15 | 16 | # switch的case缩进 17 | IndentCaseLabels: true 18 | 19 | # 针对OC的block的缩进宽度 20 | ObjCBlockIndentWidth: 4 21 | 22 | # 针对OC,属性名后加空格 23 | ObjCSpaceAfterProperty: true 24 | 25 | # 每行字符的长度 26 | ColumnLimit: 0 27 | 28 | # 注释对齐 29 | AlignTrailingComments: true 30 | 31 | # 括号后加空格 32 | SpaceAfterCStyleCast: false 33 | 34 | # 不在小括号里加空格 35 | SpacesInParentheses: false 36 | 37 | # 不在中括号里加空格 38 | SpacesInSquareBrackets: false 39 | 40 | AllowShortBlocksOnASingleLine: false 41 | 42 | AllowShortCaseLabelsOnASingleLine: false 43 | 44 | AllowShortFunctionsOnASingleLine: false 45 | -------------------------------------------------------------------------------- /example/ohos/entry/src/ohosTest/ets/test/List.test.ets: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | import abilityTest from './Ability.test' 17 | 18 | export default function testsuite() { 19 | abilityTest() 20 | } -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | }() 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" // apply true 21 | id "com.android.application" version "8.3.1" apply false 22 | id "org.jetbrains.kotlin.android" version "1.8.10" apply false 23 | } 24 | 25 | include ':app' -------------------------------------------------------------------------------- /example/ohos/entry/hvigorfile.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | // Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. 17 | export { hapTasks } from '@ohos/hvigor-ohos-plugin'; 18 | -------------------------------------------------------------------------------- /example/ohos/build-profile.json5: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "signingConfigs": [], 4 | "products": [ 5 | { 6 | "name": "default", 7 | "signingConfig": "default", 8 | "compatibleSdkVersion": "5.0.0(12)", 9 | "runtimeOS": "HarmonyOS" 10 | } 11 | ] 12 | }, 13 | "modules": [ 14 | { 15 | "name": "entry", 16 | "srcPath": "./entry", 17 | "targets": [ 18 | { 19 | "name": "default", 20 | "applyToProducts": [ 21 | "default" 22 | ] 23 | } 24 | ] 25 | }, 26 | { 27 | "tencent_kit": { 28 | "srcPath": "../../ohos", 29 | "targets": [ 30 | { 31 | "name": "default", 32 | "applyToProducts": [ 33 | "default" 34 | ] 35 | } 36 | ] 37 | } 38 | } 39 | ] 40 | } -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 11.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .packages 31 | .pub-cache/ 32 | .pub/ 33 | /build/ 34 | pubspec.lock 35 | 36 | # Symbolication related 37 | app.*.symbols 38 | 39 | # Obfuscation related 40 | app.*.map.json 41 | 42 | # Android Studio will place build artifacts here 43 | /android/app/debug 44 | /android/app/profile 45 | /android/app/release 46 | -------------------------------------------------------------------------------- /example/ohos/entry/build-profile.json5: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | { 17 | "apiType": 'stageMode', 18 | "buildOption": { 19 | }, 20 | "targets": [ 21 | { 22 | "name": "default", 23 | "runtimeOS": "HarmonyOS" 24 | }, 25 | { 26 | "name": "ohosTest", 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /example/ohos/hvigorfile.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | import { appTasks } from '@ohos/hvigor-ohos-plugin'; 17 | 18 | export default { 19 | system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ 20 | plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ 21 | } -------------------------------------------------------------------------------- /example/integration_test/plugin_integration_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter integration test. 2 | // 3 | // Since integration tests run in a full Flutter application, they can interact 4 | // with the host side of a plugin implementation, unlike Dart unit tests. 5 | // 6 | // For more information about Flutter integration tests, please see 7 | // https://docs.flutter.dev/cookbook/testing/integration/introduction 8 | 9 | import 'package:flutter_test/flutter_test.dart'; 10 | import 'package:integration_test/integration_test.dart'; 11 | 12 | import 'package:tencent_kit/tencent_kit.dart'; 13 | 14 | void main() { 15 | IntegrationTestWidgetsFlutterBinding.ensureInitialized(); 16 | 17 | testWidgets('isQQInstalled test', (WidgetTester tester) async { 18 | // The version string depends on the host platform running the test, so 19 | // just assert that some non-empty string is returned. 20 | expect(await TencentKitPlatform.instance.isQQInstalled(), false); 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: tencent_kit 2 | description: A powerful Flutter plugin allowing developers to auth/share with natvie Android & iOS & HarmonyOS Tencent SDKs. 3 | version: 6.2.0 4 | homepage: https://github.com/RxReader/tencent_kit 5 | repository: https://github.com/RxReader/tencent_kit 6 | issue_tracker: https://github.com/RxReader/tencent_kit/issues 7 | 8 | environment: 9 | sdk: '>=3.0.2 <4.0.0' 10 | flutter: ">=3.3.0" 11 | 12 | dependencies: 13 | flutter: 14 | sdk: flutter 15 | 16 | plugin_platform_interface: ^2.0.2 17 | json_annotation: ^4.8.1 18 | 19 | dev_dependencies: 20 | flutter_test: 21 | sdk: flutter 22 | flutter_lints: 23 | 24 | build_runner: ^2.0.0 25 | json_serializable: ^6.7.0 26 | 27 | flutter: 28 | plugin: 29 | platforms: 30 | android: 31 | package: io.github.v7lin.tencent_kit 32 | pluginClass: TencentKitPlugin 33 | ios: 34 | pluginClass: TencentKitPlugin 35 | ohos: 36 | pluginClass: TencentKitPlugin 37 | -------------------------------------------------------------------------------- /example/lib/api/model/tencent_unionid_resp.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'tencent_unionid_resp.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | TencentUnionidResp _$TencentUnionidRespFromJson(Map json) => TencentUnionidResp( 10 | error: (json['error'] as num?)?.toInt() ?? 0, 11 | errorDescription: json['error_description'] as String?, 12 | clientId: json['client_id'] as String?, 13 | openid: json['openid'] as String?, 14 | unionid: json['unionid'] as String?, 15 | ); 16 | 17 | Map _$TencentUnionidRespToJson(TencentUnionidResp instance) => 18 | { 19 | 'error': instance.error, 20 | 'error_description': instance.errorDescription, 21 | 'client_id': instance.clientId, 22 | 'openid': instance.openid, 23 | 'unionid': instance.unionid, 24 | }; 25 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: publish 2 | 3 | on: 4 | workflow_dispatch: 5 | release: 6 | types: [published] 7 | 8 | jobs: 9 | publish: 10 | name: Publish 11 | runs-on: macos-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: subosito/flutter-action@v2 15 | with: 16 | channel: 'stable' 17 | - name: Run pub.dev/inject-credentials@shell 18 | env: 19 | # Mac: ~/Library/Application\ Support/dart/pub-credentials.json 20 | # Linux: $HOME/.config/dart/pub-credentials.json 21 | # Windows: %APPDATA%/dart/pub-credentials.json 22 | # Windows: %LOCALAPPDATA%/dart/pub-credentials.json 23 | CREDENTIALS: ${{ secrets.CREDENTIALS_JSON }} 24 | run: | 25 | mkdir -p ~/Library/Application\ Support/dart 26 | echo $CREDENTIALS > ~/Library/Application\ Support/dart/pub-credentials.json 27 | - run: flutter --version 28 | - run: flutter pub get 29 | - run: dart format --set-exit-if-changed . 30 | - run: echo "y" | flutter pub publish 31 | -------------------------------------------------------------------------------- /test/tencent_kit_method_channel_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | import 'package:tencent_kit/src/tencent_kit_method_channel.dart'; 4 | 5 | void main() { 6 | TestWidgetsFlutterBinding.ensureInitialized(); 7 | 8 | final MethodChannelTencentKit platform = MethodChannelTencentKit(); 9 | const MethodChannel channel = MethodChannel('v7lin.github.io/tencent_kit'); 10 | 11 | setUp(() { 12 | TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger 13 | .setMockMethodCallHandler( 14 | channel, 15 | (MethodCall methodCall) async { 16 | switch (methodCall.method) { 17 | case 'isQQInstalled': 18 | return true; 19 | } 20 | return null; 21 | }, 22 | ); 23 | }); 24 | 25 | tearDown(() { 26 | TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger 27 | .setMockMethodCallHandler(channel, null); 28 | }); 29 | 30 | test('isQQInstalled', () async { 31 | expect(await platform.isQQInstalled(), true); 32 | }); 33 | } 34 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/lib/api/model/tencent_unionid_resp.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:json_annotation/json_annotation.dart'; 4 | 5 | part 'tencent_unionid_resp.g.dart'; 6 | 7 | @JsonSerializable( 8 | explicitToJson: true, 9 | fieldRename: FieldRename.snake, 10 | ) 11 | class TencentUnionidResp { 12 | const TencentUnionidResp({ 13 | required this.error, 14 | this.errorDescription, 15 | this.clientId, 16 | this.openid, 17 | this.unionid, 18 | }); 19 | 20 | factory TencentUnionidResp.fromJson(Map json) => 21 | _$TencentUnionidRespFromJson(json); 22 | 23 | @JsonKey( 24 | defaultValue: kErrorSuccess, 25 | ) 26 | final int error; 27 | final String? errorDescription; 28 | final String? clientId; 29 | final String? openid; 30 | final String? unionid; 31 | 32 | static const int kErrorSuccess = 0; 33 | 34 | bool get isSuccessful => error == kErrorSuccess; 35 | 36 | Map toJson() => _$TencentUnionidRespToJson(this); 37 | 38 | @override 39 | String toString() => const JsonEncoder.withIndent(' ').convert(toJson()); 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /example/ohos/entry/src/main/ets/entryability/EntryAbility.ets: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | import { FlutterAbility, FlutterEngine } from '@ohos/flutter_ohos'; 17 | import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant'; 18 | 19 | export default class EntryAbility extends FlutterAbility { 20 | configureFlutterEngine(flutterEngine: FlutterEngine) { 21 | super.configureFlutterEngine(flutterEngine) 22 | GeneratedPluginRegistrant.registerWith(flutterEngine) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /android/src/test/java/io/github/v7lin/tencent_kit/TencentKitPluginTest.java: -------------------------------------------------------------------------------- 1 | package io.github.v7lin.tencent_kit; 2 | 3 | import static org.mockito.Mockito.mock; 4 | import static org.mockito.Mockito.verify; 5 | 6 | import io.flutter.plugin.common.MethodCall; 7 | import io.flutter.plugin.common.MethodChannel; 8 | import org.junit.Test; 9 | 10 | /** 11 | * This demonstrates a simple unit test of the Java portion of this plugin's implementation. 12 | * 13 | * Once you have built the plugin's example app, you can run these tests from the command 14 | * line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or 15 | * you can run them directly from IDEs that support JUnit such as Android Studio. 16 | */ 17 | 18 | public class TencentKitPluginTest { 19 | @Test 20 | public void onMethodCall_isQQInstalled_returnsExpectedValue() { 21 | TencentKitPlugin plugin = new TencentKitPlugin(); 22 | 23 | final MethodCall call = new MethodCall("isQQInstalled", null); 24 | MethodChannel.Result mockResult = mock(MethodChannel.Result.class); 25 | plugin.onMethodCall(call, mockResult); 26 | 27 | verify(mockResult).success(false); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /example/ohos/oh-package.json5: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | { 17 | "modelVersion": "5.0.0", 18 | "name": "tencent_kit_example", 19 | "version": "1.0.0", 20 | "description": "Please describe the basic information.", 21 | "main": "", 22 | "author": "", 23 | "license": "", 24 | "dependencies": { 25 | "@ohos/flutter_ohos": "file:./har/flutter.har" 26 | }, 27 | "devDependencies": { 28 | "@ohos/hypium": "1.0.6" 29 | }, 30 | "overrides": { 31 | "@ohos/flutter_ohos": "file:./har/flutter.har", 32 | "tencent_kit": "../../ohos", 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /example/ios/RunnerTests/RunnerTests.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | 5 | @import tencent_kit; 6 | 7 | // This demonstrates a simple unit test of the Objective-C portion of this plugin's implementation. 8 | // 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | 11 | @interface RunnerTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation RunnerTests 16 | 17 | - (void)testExample { 18 | TencentKitPlugin *plugin = [[TencentKitPlugin alloc] init]; 19 | 20 | FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"isQQInstalled" 21 | arguments:nil]; 22 | XCTestExpectation *expectation = [self expectationWithDescription:@"result block must be called"]; 23 | [plugin handleMethodCall:call 24 | result:^(id result) { 25 | NSNumber *expected = [NSNumber numberWithBool:NO]; 26 | XCTAssertEqualObjects(result, expected); 27 | [expectation fulfill]; 28 | }]; 29 | [self waitForExpectationsWithTimeout:1 handler:nil]; 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | analyzer: 4 | errors: 5 | deprecated_member_use_from_same_package: ignore 6 | exclude: 7 | - "lib/**/*.g.dart" 8 | 9 | formatter: 10 | trailing_commas: preserve 11 | 12 | linter: 13 | rules: 14 | always_declare_return_types: true 15 | always_put_control_body_on_new_line: true 16 | avoid_print: true 17 | avoid_renaming_method_parameters: true 18 | avoid_unnecessary_containers: true 19 | avoid_void_async: true 20 | curly_braces_in_flow_control_structures: true 21 | directives_ordering: true 22 | flutter_style_todos: true 23 | library_private_types_in_public_api: false 24 | overridden_fields: false 25 | prefer_const_constructors: true 26 | prefer_const_constructors_in_immutables: false 27 | prefer_final_fields: true 28 | prefer_final_in_for_each: true 29 | prefer_final_locals: true 30 | prefer_single_quotes: true 31 | sort_child_properties_last: true 32 | sort_constructors_first: true 33 | sort_unnamed_constructors_first: true 34 | unnecessary_await_in_return: true 35 | unnecessary_breaks: true 36 | unnecessary_late: true 37 | unnecessary_parenthesis: true 38 | use_build_context_synchronously: false 39 | void_checks: true 40 | -------------------------------------------------------------------------------- /example/ohos/entry/src/main/ets/pages/Index.ets: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | import common from '@ohos.app.ability.common'; 17 | import { FlutterPage } from '@ohos/flutter_ohos' 18 | 19 | let storage = LocalStorage.getShared() 20 | const EVENT_BACK_PRESS = 'EVENT_BACK_PRESS' 21 | 22 | @Entry(storage) 23 | @Component 24 | struct Index { 25 | private context = getContext(this) as common.UIAbilityContext 26 | @LocalStorageLink('viewId') viewId: string = ""; 27 | 28 | build() { 29 | Column() { 30 | FlutterPage({ viewId: this.viewId }) 31 | } 32 | } 33 | 34 | onBackPress(): boolean { 35 | this.context.eventHub.emit(EVENT_BACK_PRESS) 36 | return true 37 | } 38 | } -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled. 5 | 6 | version: 7 | revision: 9cd3d0d9ff05768afa249e036acc66e8abe93bff 8 | channel: stable 9 | 10 | project_type: plugin 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 9cd3d0d9ff05768afa249e036acc66e8abe93bff 17 | base_revision: 9cd3d0d9ff05768afa249e036acc66e8abe93bff 18 | - platform: android 19 | create_revision: 9cd3d0d9ff05768afa249e036acc66e8abe93bff 20 | base_revision: 9cd3d0d9ff05768afa249e036acc66e8abe93bff 21 | - platform: ios 22 | create_revision: 9cd3d0d9ff05768afa249e036acc66e8abe93bff 23 | base_revision: 9cd3d0d9ff05768afa249e036acc66e8abe93bff 24 | - platform: ohos 25 | create_revision: 9cd3d0d9ff05768afa249e036acc66e8abe93bff 26 | base_revision: 9cd3d0d9ff05768afa249e036acc66e8abe93bff 27 | 28 | # User provided section 29 | 30 | # List of Local paths (relative to this file) that should be 31 | # ignored by the migrate tool. 32 | # 33 | # Files that are not part of the templates will be ignored by default. 34 | unmanaged_files: 35 | - 'lib/main.dart' 36 | - 'ios/Runner.xcodeproj/project.pbxproj' 37 | -------------------------------------------------------------------------------- /lib/src/model/resp.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'resp.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | TencentLoginResp _$TencentLoginRespFromJson(Map json) => TencentLoginResp( 10 | ret: (json['ret'] as num?)?.toInt() ?? 0, 11 | msg: json['msg'] as String?, 12 | openid: json['openid'] as String?, 13 | accessToken: json['access_token'] as String?, 14 | expiresIn: (json['expires_in'] as num?)?.toInt(), 15 | createAt: (json['create_at'] as num?)?.toInt(), 16 | ); 17 | 18 | Map _$TencentLoginRespToJson(TencentLoginResp instance) => 19 | { 20 | 'ret': instance.ret, 21 | 'msg': instance.msg, 22 | 'openid': instance.openid, 23 | 'access_token': instance.accessToken, 24 | 'expires_in': instance.expiresIn, 25 | 'create_at': instance.createAt, 26 | }; 27 | 28 | TencentShareMsgResp _$TencentShareMsgRespFromJson(Map json) => 29 | TencentShareMsgResp( 30 | ret: (json['ret'] as num?)?.toInt() ?? 0, 31 | msg: json['msg'] as String?, 32 | ); 33 | 34 | Map _$TencentShareMsgRespToJson( 35 | TencentShareMsgResp instance) => 36 | { 37 | 'ret': instance.ret, 38 | 'msg': instance.msg, 39 | }; 40 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '11.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 32 | target 'RunnerTests' do 33 | inherit! :search_paths 34 | end 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /ios/Classes/UIApplication+Hook.m: -------------------------------------------------------------------------------- 1 | #import "UIApplication+Hook.h" 2 | #import 3 | 4 | @implementation UIApplication (Hook) 5 | 6 | + (void)load { 7 | static dispatch_once_t onceToken; 8 | dispatch_once(&onceToken, ^{ 9 | Class class = [self class]; 10 | SEL originalSelector = @selector(openURL:); 11 | SEL swizzledSelector = @selector(g_openURL:); 12 | Method originalMethod = class_getInstanceMethod(class, originalSelector); 13 | Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); 14 | if (originalMethod && swizzledMethod) { 15 | method_exchangeImplementations(originalMethod, swizzledMethod); 16 | NSLog(@"openURL: 方法交换成功!"); 17 | } else { 18 | NSLog(@"openURL: 方法交换失败!"); 19 | } 20 | }); 21 | } 22 | 23 | - (BOOL)g_openURL:(NSURL *)url { 24 | NSLog(@"拦截到 openURL: %@", url); 25 | // 注意:由于方法交换,调用 g_openURL: 实际上会调用原 openURL: 实现 26 | // 这里我们调用新版 API 27 | if ([UIApplication.sharedApplication respondsToSelector:@selector(openURL:options:completionHandler:)]) { 28 | [UIApplication.sharedApplication 29 | openURL:url 30 | options:@{} 31 | completionHandler:^(BOOL success) { 32 | NSLog(@"openURL:options:completionHandler: 返回 %d", success); 33 | }]; 34 | } else { 35 | // 如果低于 iOS 10,调用原始实现 36 | return [self g_openURL:url]; // 注意:由于方法交换,这里 g_openURL: 实际上是原 openURL: 37 | } 38 | return YES; 39 | } 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | import hilog from '@ohos.hilog'; 17 | 18 | @Entry 19 | @Component 20 | struct Index { 21 | aboutToAppear() { 22 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility index aboutToAppear'); 23 | } 24 | @State message: string = 'Hello World' 25 | build() { 26 | Row() { 27 | Column() { 28 | Text(this.message) 29 | .fontSize(50) 30 | .fontWeight(FontWeight.Bold) 31 | Button() { 32 | Text('next page') 33 | .fontSize(20) 34 | .fontWeight(FontWeight.Bold) 35 | }.type(ButtonType.Capsule) 36 | .margin({ 37 | top: 20 38 | }) 39 | .backgroundColor('#0D9FFB') 40 | .width('35%') 41 | .height('5%') 42 | .onClick(()=>{ 43 | }) 44 | } 45 | .width('100%') 46 | } 47 | .height('100%') 48 | } 49 | } -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | id "dev.flutter.flutter-gradle-plugin" 5 | } 6 | 7 | def localProperties = new Properties() 8 | def localPropertiesFile = rootProject.file('local.properties') 9 | if (localPropertiesFile.exists()) { 10 | localPropertiesFile.withReader('UTF-8') { reader -> 11 | localProperties.load(reader) 12 | } 13 | } 14 | 15 | android { 16 | namespace "io.github.v7lin.tencent_kit_example" 17 | compileSdkVersion flutter.compileSdkVersion 18 | ndkVersion flutter.ndkVersion 19 | 20 | compileOptions { 21 | sourceCompatibility JavaVersion.VERSION_17 22 | targetCompatibility JavaVersion.VERSION_17 23 | } 24 | 25 | defaultConfig { 26 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 27 | applicationId "io.github.v7lin.tencent_kit_example" 28 | // You can update the following values to match your application needs. 29 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. 30 | minSdkVersion flutter.minSdkVersion 31 | targetSdkVersion flutter.targetSdkVersion 32 | versionCode flutter.versionCode 33 | versionName flutter.versionName 34 | } 35 | 36 | buildTypes { 37 | release { 38 | // TODO: Add your own signing config for the release build. 39 | // Signing with the debug keys for now, so `flutter run --release` works. 40 | signingConfig signingConfigs.debug 41 | } 42 | } 43 | } 44 | 45 | flutter { 46 | source '../..' 47 | } 48 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - FMDB (2.7.5): 4 | - FMDB/standard (= 2.7.5) 5 | - FMDB/standard (2.7.5) 6 | - integration_test (0.0.1): 7 | - Flutter 8 | - path_provider_foundation (0.0.1): 9 | - Flutter 10 | - FlutterMacOS 11 | - sqflite (0.0.3): 12 | - Flutter 13 | - FMDB (>= 2.7.5) 14 | - tencent_kit (6.0.0): 15 | - Flutter 16 | - tencent_kit/vendor (= 6.0.0) 17 | - tencent_kit/vendor (6.0.0): 18 | - Flutter 19 | 20 | DEPENDENCIES: 21 | - Flutter (from `Flutter`) 22 | - integration_test (from `.symlinks/plugins/integration_test/ios`) 23 | - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) 24 | - sqflite (from `.symlinks/plugins/sqflite/ios`) 25 | - tencent_kit (from `.symlinks/plugins/tencent_kit/ios`) 26 | 27 | SPEC REPOS: 28 | trunk: 29 | - FMDB 30 | 31 | EXTERNAL SOURCES: 32 | Flutter: 33 | :path: Flutter 34 | integration_test: 35 | :path: ".symlinks/plugins/integration_test/ios" 36 | path_provider_foundation: 37 | :path: ".symlinks/plugins/path_provider_foundation/darwin" 38 | sqflite: 39 | :path: ".symlinks/plugins/sqflite/ios" 40 | tencent_kit: 41 | :path: ".symlinks/plugins/tencent_kit/ios" 42 | 43 | SPEC CHECKSUMS: 44 | Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 45 | FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a 46 | integration_test: 13825b8a9334a850581300559b8839134b124670 47 | path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8 48 | sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a 49 | tencent_kit: bde4e986d63a30a0bed0d6c17aaac956a964b1ec 50 | 51 | PODFILE CHECKSUM: 02caaa843f6501172c0d470d80e72f61175c8b93 52 | 53 | COCOAPODS: 1.12.1 54 | -------------------------------------------------------------------------------- /example/ohos/entry/src/ohosTest/module.json5: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | { 17 | "module": { 18 | "name": "entry_test", 19 | "type": "feature", 20 | "description": "$string:module_test_desc", 21 | "mainElement": "TestAbility", 22 | "deviceTypes": [ 23 | "phone" 24 | ], 25 | "deliveryWithInstall": true, 26 | "installationFree": false, 27 | "pages": "$profile:test_pages", 28 | "abilities": [ 29 | { 30 | "name": "TestAbility", 31 | "srcEntry": "./ets/testability/TestAbility.ets", 32 | "description": "$string:TestAbility_desc", 33 | "icon": "$media:icon", 34 | "label": "$string:TestAbility_label", 35 | "exported": true, 36 | "startWindowIcon": "$media:icon", 37 | "startWindowBackground": "$color:start_window_background", 38 | "skills": [ 39 | { 40 | "actions": [ 41 | "action.system.home" 42 | ], 43 | "entities": [ 44 | "entity.system.home" 45 | ] 46 | } 47 | ] 48 | } 49 | ] 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/src/constant.dart: -------------------------------------------------------------------------------- 1 | abstract final class TencentScope { 2 | /// 发表一条说说到QQ空间(需要申请权限) 3 | static const String kAddTopic = 'add_topic'; 4 | 5 | /// 发表一篇日志到QQ空间(需要申请权限) 6 | static const String kAddOneBlog = 'add_one_blog'; 7 | 8 | /// 创建一个QQ空间相册(需要申请权限) 9 | static const String kAddAlbum = 'add_album'; 10 | 11 | /// 上传一张照片到QQ空间相册(需要申请权限) 12 | static const String kUploadPic = 'upload_pic'; 13 | 14 | /// 获取用户QQ空间相册列表(需要申请权限) 15 | static const String kListAlbum = 'list_album'; 16 | 17 | /// 同步分享到QQ空间、腾讯微博 18 | static const String kAddShare = 'add_share'; 19 | 20 | /// 验证是否认证空间粉丝 21 | static const String kCheckPageFans = 'check_page_fans'; 22 | 23 | /// 获取登录用户自己的详细信息 24 | static const String kGetInfo = 'get_info'; 25 | 26 | /// 获取其他用户的详细信息 27 | static const String kGetOtherInfo = 'get_other_info'; 28 | 29 | /// 获取会员用户基本信息 30 | static const String kGetVipInfo = 'get_vip_info'; 31 | 32 | /// 获取会员用户详细信息 33 | static const String kGetVipRichInfo = 'get_vip_rich_info'; 34 | 35 | /// 获取用户信息 36 | static const String kGetUserInfo = 'get_user_info'; 37 | 38 | /// 移动端获取用户信息 39 | static const String kGetSimpleUserInfo = 'get_simple_userinfo'; 40 | 41 | /// 所有权限 42 | static const String kAll = 'all'; 43 | } 44 | 45 | abstract final class TencentScene { 46 | /// QQ 47 | // ignore: constant_identifier_names 48 | static const int kScene_QQ = 0; 49 | 50 | /// QZone 51 | // ignore: constant_identifier_names 52 | static const int kScene_QZone = 1; 53 | } 54 | 55 | abstract final class TencentQZoneFlag { 56 | /// 默认是不隐藏分享到QZone按钮且不自动打开分享到QZone的对话框 57 | static const int kDefault = 0; 58 | 59 | /// 分享时自动打开分享到QZone的对话框 60 | static const int kAutoOpen = 1; 61 | 62 | /// 分享时隐藏分享到QZone按钮 63 | static const int kItemHide = 2; 64 | } 65 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 14 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /example/ohos/entry/src/main/module.json5: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | { 16 | "module": { 17 | "name": "entry", 18 | "type": "entry", 19 | "description": "$string:module_desc", 20 | "mainElement": "EntryAbility", 21 | "deviceTypes": [ 22 | "phone" 23 | ], 24 | "deliveryWithInstall": true, 25 | "installationFree": false, 26 | "pages": "$profile:main_pages", 27 | "abilities": [ 28 | { 29 | "name": "EntryAbility", 30 | "srcEntry": "./ets/entryability/EntryAbility.ets", 31 | "description": "$string:EntryAbility_desc", 32 | "icon": "$media:icon", 33 | "label": "$string:EntryAbility_label", 34 | "startWindowIcon": "$media:icon", 35 | "startWindowBackground": "$color:start_window_background", 36 | "exported": true, 37 | "skills": [ 38 | { 39 | "entities": [ 40 | "entity.system.home" 41 | ], 42 | "actions": [ 43 | "action.system.home" 44 | ] 45 | } 46 | ] 47 | } 48 | ], 49 | "requestPermissions": [ 50 | {"name" : "ohos.permission.INTERNET"}, 51 | ] 52 | } 53 | } -------------------------------------------------------------------------------- /lib/src/model/resp.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:json_annotation/json_annotation.dart'; 4 | 5 | part 'resp.g.dart'; 6 | 7 | abstract class TencentResp { 8 | const TencentResp({ 9 | required this.ret, 10 | this.msg, 11 | }); 12 | 13 | /// 网络请求成功发送至服务器,并且服务器返回数据格式正确 14 | /// 这里包括所请求业务操作失败的情况,例如没有授权等原因导致 15 | static const int kRetSuccess = 0; 16 | 17 | /// 网络异常,或服务器返回的数据格式不正确导致无法解析 18 | static const int kRetFailed = 1; 19 | 20 | static const int kRetCommon = -1; 21 | 22 | static const int kRetUserCancel = -2; 23 | 24 | @JsonKey( 25 | defaultValue: kRetSuccess, 26 | ) 27 | final int ret; 28 | final String? msg; 29 | 30 | bool get isSuccessful => ret == kRetSuccess; 31 | 32 | bool get isCancelled => ret == kRetUserCancel; 33 | 34 | Map toJson(); 35 | 36 | @override 37 | String toString() => const JsonEncoder.withIndent(' ').convert(toJson()); 38 | } 39 | 40 | @JsonSerializable() 41 | class TencentLoginResp extends TencentResp { 42 | const TencentLoginResp({ 43 | required super.ret, 44 | super.msg, 45 | this.openid, 46 | this.accessToken, 47 | this.expiresIn, 48 | this.createAt, 49 | }); 50 | 51 | factory TencentLoginResp.fromJson(Map json) => 52 | _$TencentLoginRespFromJson(json); 53 | 54 | final String? openid; 55 | final String? accessToken; 56 | final int? expiresIn; 57 | final int? createAt; 58 | 59 | bool? get isExpired => isSuccessful 60 | ? DateTime.now().millisecondsSinceEpoch - createAt! >= expiresIn! * 1000 61 | : null; 62 | 63 | @override 64 | Map toJson() => _$TencentLoginRespToJson(this); 65 | } 66 | 67 | @JsonSerializable() 68 | class TencentShareMsgResp extends TencentResp { 69 | const TencentShareMsgResp({ 70 | required super.ret, 71 | super.msg, 72 | }); 73 | 74 | factory TencentShareMsgResp.fromJson(Map json) => 75 | _$TencentShareMsgRespFromJson(json); 76 | 77 | @override 78 | Map toJson() => _$TencentShareMsgRespToJson(this); 79 | } 80 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 6.2.0 2 | 3 | * HarmonyOS: 升级 QQ SDK 到 v1.0.3, 优化文档 4 | 5 | ## 6.1.0 6 | 7 | * 新增: 对 `HarmonyOS` 的支持, 受限于 SDK, 仅支持部分功能 8 | * 新增: Server-Side Code 模式登录 9 | * 新增: 二维码授权登录 10 | 11 | ## 6.0.1 12 | 13 | * iOS: 优化 ruby 脚本 14 | 15 | ## 6.0.0 16 | 17 | * Flutter 3.10.2 && Dart 3.0 18 | 19 | ## 5.0.2 20 | 21 | * 优化 22 | 23 | ## 5.0.1 24 | 25 | * iOS: 优化 ruby 脚本 26 | * 优化 Github Action 27 | 28 | ## 5.0.0 29 | 30 | * break change 31 | * Android: 提高新手配置友好度,不在需要手动配置 32 | * iOS: 提高新手配置友好度,不在需要手动配置 33 | * Flutter: 提高类名辨识,所有类类名前缀为 "Tencent" 34 | 35 | ## 4.0.0 36 | 37 | * 升级 Flutter 3.0 38 | * 升级 Android/iOS SDK 39 | * break change: 官方标准化插件 40 | 41 | ## 3.1.0 42 | 43 | * 升级 Android/iOS SDK 44 | * 新增 setIsPermissionGranted 函数 45 | 46 | ## 3.0.0 47 | 48 | * 重构 49 | 50 | ## 2.1.1 51 | 52 | * 升级SDK 53 | 54 | ## 2.1.0 55 | 56 | * nullsafety 57 | * 不再支持 Android embedding v1 58 | * Tencent 单例 59 | 60 | ## 2.0.1 61 | 62 | * 优化 63 | 64 | ## 2.0.0 65 | 66 | * 升级 Android/iOS 接口 67 | * Android 添加 FileProvider 支持 68 | 69 | ## 1.1.2 70 | 71 | * [#37](https://github.com/rxreader/tencent_kit/pull/37) fix ios register failed 72 | 73 | ## 1.1.1 74 | 75 | * 优化 76 | 77 | ## 1.1.0 78 | 79 | * 升级 V2 插件 80 | * 升级 Android/iOS SDK 81 | 82 | ## 1.0.4 83 | 84 | * 适配 Android Q 本地分享适配(别问为什么,腾讯有毒!) 85 | 86 | ## 1.0.3 87 | 88 | * 升级 Android SDK 89 | * 适配 Android Q 本地分享(targetSdkVersion >= 29,且不豁免外部存储沙箱限制) 90 | 91 | ## 1.0.2 92 | 93 | * 优化 94 | * Objective-C 代码格式化 95 | 96 | ## 1.0.1 97 | 98 | * 优化 99 | 100 | ## 1.0.0 101 | 102 | * 更名 tencent_kit 103 | * 升级 Android/iOS SDK 104 | * 添加 Android 代码静态检查 105 | * 获取用户信息改走API接口 106 | * 新增获取UnionId功能 107 | 108 | ## 0.3.4 109 | 110 | * gradle兼容 111 | 112 | ## 0.3.3 113 | 114 | * 简化 115 | 116 | ## 0.3.2 117 | 118 | * AndroidX兼容 119 | 120 | ## 0.3.1 121 | 122 | * 修正网页分享无描述 123 | 124 | ## 0.3.0 125 | 126 | * 优化 127 | * 自动化发布 128 | 129 | ## 0.2.0 130 | 131 | * 不再自动设置 AccessToken 132 | 133 | ## 0.1.0 134 | 135 | * 规范 fake_tencent 代码 136 | 137 | ## 0.0.1 138 | 139 | * android/ios tencent 140 | -------------------------------------------------------------------------------- /android/src/vendor/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 25 | 26 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/tencent_kit.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. 3 | # Run `pod lib lint tencent_kit.podspec` to validate before publishing. 4 | # 5 | 6 | pubspec = YAML.load_file(File.join('..', 'pubspec.yaml')) 7 | library_version = pubspec['version'].gsub('+', '-') 8 | 9 | current_dir = Dir.pwd 10 | calling_dir = File.dirname(__FILE__) 11 | project_dir = calling_dir.slice(0..(calling_dir.index('/.symlinks'))) 12 | flutter_project_dir = calling_dir.slice(0..(calling_dir.index('/ios/.symlinks'))) 13 | 14 | psych_version_gte_500 = Gem::Version.new(Psych::VERSION) >= Gem::Version.new('5.0.0') 15 | if psych_version_gte_500 == true 16 | cfg = YAML.load_file(File.join(flutter_project_dir, 'pubspec.yaml'), aliases: true) 17 | else 18 | cfg = YAML.load_file(File.join(flutter_project_dir, 'pubspec.yaml')) 19 | end 20 | 21 | if cfg['tencent_kit'] && cfg['tencent_kit']['app_id'] 22 | app_id = cfg['tencent_kit']['app_id'] 23 | universal_link = cfg['tencent_kit']['universal_link'] 24 | options = "" 25 | if universal_link 26 | options = "-u #{universal_link}" 27 | end 28 | system("ruby #{current_dir}/tencent_setup.rb -a #{app_id} #{options} -p #{project_dir} -n Runner.xcodeproj") 29 | else 30 | abort("tencent app_id/universal_link is null, add code in pubspec.yaml:\ntencent_kit:\n app_id: ${your tencent app id}\n universal_link: https://${your applinks domain}/universal_link/${example_app}/qq_conn/${your tencent app id}/ # 可选项目\n") 31 | end 32 | 33 | Pod::Spec.new do |s| 34 | s.name = 'tencent_kit' 35 | s.version = library_version 36 | s.summary = pubspec['description'] 37 | s.description = pubspec['description'] 38 | s.homepage = pubspec['homepage'] 39 | s.license = { :file => '../LICENSE' } 40 | s.author = { 'Your Company' => 'email@example.com' } 41 | s.source = { :path => '.' } 42 | s.source_files = 'Classes/**/*' 43 | s.public_header_files = 'Classes/**/*.h' 44 | s.dependency 'Flutter' 45 | s.platform = :ios, '11.0' 46 | 47 | # v3.5.11 48 | s.subspec 'vendor' do |sp| 49 | sp.vendored_frameworks = 'Libraries/*.framework' 50 | sp.frameworks = 'Security', 'SystemConfiguration', 'CoreGraphics', 'CoreTelephony', 'WebKit' 51 | sp.libraries = 'iconv', 'sqlite3', 'stdc++', 'z' 52 | sp.requires_arc = true 53 | end 54 | 55 | # Flutter.framework does not contain a i386 slice. 56 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } 57 | end 58 | -------------------------------------------------------------------------------- /example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | import hilog from '@ohos.hilog'; 17 | import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' 18 | 19 | export default function abilityTest() { 20 | describe('ActsAbilityTest', function () { 21 | // Defines a test suite. Two parameters are supported: test suite name and test suite function. 22 | beforeAll(function () { 23 | // Presets an action, which is performed only once before all test cases of the test suite start. 24 | // This API supports only one parameter: preset action function. 25 | }) 26 | beforeEach(function () { 27 | // Presets an action, which is performed before each unit test case starts. 28 | // The number of execution times is the same as the number of test cases defined by **it**. 29 | // This API supports only one parameter: preset action function. 30 | }) 31 | afterEach(function () { 32 | // Presets a clear action, which is performed after each unit test case ends. 33 | // The number of execution times is the same as the number of test cases defined by **it**. 34 | // This API supports only one parameter: clear action function. 35 | }) 36 | afterAll(function () { 37 | // Presets a clear action, which is performed after all test cases of the test suite end. 38 | // This API supports only one parameter: clear action function. 39 | }) 40 | it('assertContain',0, function () { 41 | // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. 42 | hilog.info(0x0000, 'testTag', '%{public}s', 'it begin'); 43 | let a = 'abc' 44 | let b = 'b' 45 | // Defines a variety of assertion methods, which are used to declare expected boolean conditions. 46 | expect(a).assertContain(b) 47 | expect(a).assertEqual(a) 48 | }) 49 | }) 50 | } -------------------------------------------------------------------------------- /example/lib/api/model/tencent_api_resp.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'tencent_api_resp.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | TencentUserInfoResp _$TencentUserInfoRespFromJson(Map json) => 10 | TencentUserInfoResp( 11 | ret: (json['ret'] as num?)?.toInt() ?? 0, 12 | msg: json['msg'] as String?, 13 | isLost: (json['is_lost'] as num?)?.toInt(), 14 | nickname: json['nickname'] as String?, 15 | gender: json['gender'] as String?, 16 | genderType: (json['gender_type'] as num?)?.toInt(), 17 | province: json['province'] as String?, 18 | city: json['city'] as String?, 19 | year: json['year'] as String?, 20 | constellation: json['constellation'] as String?, 21 | figureurl: json['figureurl'] as String?, 22 | figureurl1: json['figureurl_1'] as String?, 23 | figureurl2: json['figureurl_2'] as String?, 24 | figureurlQq: json['figureurl_qq'] as String?, 25 | figureurlQq1: json['figureurl_qq_1'] as String?, 26 | figureurlQq2: json['figureurl_qq_2'] as String?, 27 | figureurlType: json['figureurl_type'] as String?, 28 | isYellowVip: json['is_yellow_vip'] as String?, 29 | vip: json['vip'] as String?, 30 | yellowVipLevel: json['yellow_vip_level'] as String?, 31 | level: json['level'] as String?, 32 | isYellowYearVip: json['is_yellow_year_vip'] as String?, 33 | ); 34 | 35 | Map _$TencentUserInfoRespToJson( 36 | TencentUserInfoResp instance) => 37 | { 38 | 'ret': instance.ret, 39 | 'msg': instance.msg, 40 | 'is_lost': instance.isLost, 41 | 'nickname': instance.nickname, 42 | 'gender': instance.gender, 43 | 'gender_type': instance.genderType, 44 | 'province': instance.province, 45 | 'city': instance.city, 46 | 'year': instance.year, 47 | 'constellation': instance.constellation, 48 | 'figureurl': instance.figureurl, 49 | 'figureurl_1': instance.figureurl1, 50 | 'figureurl_2': instance.figureurl2, 51 | 'figureurl_qq': instance.figureurlQq, 52 | 'figureurl_qq_1': instance.figureurlQq1, 53 | 'figureurl_qq_2': instance.figureurlQq2, 54 | 'figureurl_type': instance.figureurlType, 55 | 'is_yellow_vip': instance.isYellowVip, 56 | 'vip': instance.vip, 57 | 'yellow_vip_level': instance.yellowVipLevel, 58 | 'level': instance.level, 59 | 'is_yellow_year_vip': instance.isYellowYearVip, 60 | }; 61 | -------------------------------------------------------------------------------- /example/lib/api/tencent_api.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | import 'dart:io'; 4 | 5 | import 'package:tencent_kit_example/api/model/tencent_api_resp.dart'; 6 | import 'package:tencent_kit_example/api/model/tencent_unionid_resp.dart'; 7 | 8 | class TencentApi { 9 | TencentApi._(); 10 | 11 | /// 用户信息 12 | /// https://wiki.connect.qq.com/get_user_info 13 | static Future getUserInfo({ 14 | required String appId, 15 | required String openid, 16 | required String accessToken, 17 | }) { 18 | return HttpClient() 19 | .getUrl(Uri.parse( 20 | 'https://graph.qq.com/user/get_user_info?access_token=$accessToken&oauth_consumer_key=$appId&openid=$openid')) 21 | .then((HttpClientRequest request) { 22 | return request.close(); 23 | }).then((HttpClientResponse response) async { 24 | if (response.statusCode == HttpStatus.ok) { 25 | final ContentType? contentType = response.headers.contentType; 26 | final Encoding encoding = 27 | Encoding.getByName(contentType?.charset) ?? utf8; 28 | final String content = await encoding.decodeStream(response); 29 | return TencentUserInfoResp.fromJson( 30 | json.decode(content) as Map); 31 | } 32 | throw HttpException( 33 | 'HttpResponse statusCode: ${response.statusCode}, reasonPhrase: ${response.reasonPhrase}.'); 34 | }); 35 | } 36 | 37 | /// UnionID 38 | /// https://wiki.connect.qq.com/unionid%E4%BB%8B%E7%BB%8D 39 | static Future getUnionId({ 40 | required String accessToken, 41 | String unionid = '1', 42 | }) { 43 | return HttpClient() 44 | .getUrl(Uri.parse( 45 | 'https://graph.qq.com/oauth2.0/me?access_token=$accessToken&unionid=$unionid')) 46 | .then((HttpClientRequest request) { 47 | return request.close(); 48 | }).then((HttpClientResponse response) async { 49 | if (response.statusCode == HttpStatus.ok) { 50 | final ContentType? contentType = response.headers.contentType; 51 | final Encoding encoding = 52 | Encoding.getByName(contentType?.charset) ?? utf8; 53 | final String callback = await encoding.decodeStream(response); 54 | // 腾讯有毒 callback( $json ); 55 | final RegExp exp = RegExp(r'callback\( (.*) \)\;'); 56 | final Match? match = exp.firstMatch(callback); 57 | if (match?.groupCount == 1) { 58 | final String? content = match!.group(1); 59 | if (content != null) { 60 | return TencentUnionidResp.fromJson( 61 | json.decode(content) as Map); 62 | } 63 | } 64 | } 65 | throw HttpException( 66 | 'HttpResponse statusCode: ${response.statusCode}, reasonPhrase: ${response.reasonPhrase}.'); 67 | }); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | import UIAbility from '@ohos.app.ability.UIAbility'; 17 | import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; 18 | import hilog from '@ohos.hilog'; 19 | import { Hypium } from '@ohos/hypium'; 20 | import testsuite from '../test/List.test'; 21 | import window from '@ohos.window'; 22 | 23 | export default class TestAbility extends UIAbility { 24 | onCreate(want, launchParam) { 25 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate'); 26 | hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? ''); 27 | hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:'+ JSON.stringify(launchParam) ?? ''); 28 | var abilityDelegator: any 29 | abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator() 30 | var abilityDelegatorArguments: any 31 | abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments() 32 | hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!'); 33 | Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite) 34 | } 35 | 36 | onDestroy() { 37 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy'); 38 | } 39 | 40 | onWindowStageCreate(windowStage: window.WindowStage) { 41 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate'); 42 | windowStage.loadContent('testability/pages/Index', (err, data) => { 43 | if (err.code) { 44 | hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); 45 | return; 46 | } 47 | hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', 48 | JSON.stringify(data) ?? ''); 49 | }); 50 | } 51 | 52 | onWindowStageDestroy() { 53 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy'); 54 | } 55 | 56 | onForeground() { 57 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground'); 58 | } 59 | 60 | onBackground() { 61 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground'); 62 | } 63 | } -------------------------------------------------------------------------------- /example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | import hilog from '@ohos.hilog'; 17 | import TestRunner from '@ohos.application.testRunner'; 18 | import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; 19 | 20 | var abilityDelegator = undefined 21 | var abilityDelegatorArguments = undefined 22 | 23 | async function onAbilityCreateCallback() { 24 | hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback'); 25 | } 26 | 27 | async function addAbilityMonitorCallback(err: any) { 28 | hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? ''); 29 | } 30 | 31 | export default class OpenHarmonyTestRunner implements TestRunner { 32 | constructor() { 33 | } 34 | 35 | onPrepare() { 36 | hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare '); 37 | } 38 | 39 | async onRun() { 40 | hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun run'); 41 | abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments() 42 | abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator() 43 | var testAbilityName = abilityDelegatorArguments.bundleName + '.TestAbility' 44 | let lMonitor = { 45 | abilityName: testAbilityName, 46 | onAbilityCreate: onAbilityCreateCallback, 47 | }; 48 | abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback) 49 | var cmd = 'aa start -d 0 -a TestAbility' + ' -b ' + abilityDelegatorArguments.bundleName 50 | var debug = abilityDelegatorArguments.parameters['-D'] 51 | if (debug == 'true') 52 | { 53 | cmd += ' -D' 54 | } 55 | hilog.info(0x0000, 'testTag', 'cmd : %{public}s', cmd); 56 | abilityDelegator.executeShellCommand(cmd, 57 | (err: any, d: any) => { 58 | hilog.info(0x0000, 'testTag', 'executeShellCommand : err : %{public}s', JSON.stringify(err) ?? ''); 59 | hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.stdResult ?? ''); 60 | hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.exitCode ?? ''); 61 | }) 62 | hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end'); 63 | } 64 | } -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | File pubspec = new File(project.projectDir.parentFile, 'pubspec.yaml') 2 | String yaml = pubspec.text 3 | // Using \s*['|"]?([^\n|'|"]*)['|"]? to extract version number. 4 | java.util.regex.Matcher versionMatcher = java.util.regex.Pattern.compile("^version:\\s*['|\"]?([^\\n|'|\"]*)['|\"]?\$", java.util.regex.Pattern.MULTILINE).matcher(yaml) 5 | versionMatcher.find() 6 | String library_version = versionMatcher.group(1).replaceAll("\\+", "-") 7 | 8 | File flutter_project_dir = rootProject.projectDir.parentFile 9 | def cfg = new org.yaml.snakeyaml.Yaml().load(new File(flutter_project_dir, 'pubspec.yaml').text) 10 | String app_id = cfg.tencent_kit?.app_id 11 | if (app_id == null) { 12 | throw new IllegalArgumentException("tencent app id is null, add code in pubspec.yaml:\ntencent_kit:" + 13 | "\n app_id: \${your tencent app id}" + 14 | "\n universal_link: https://\${your applinks domain}/universal_link/\${example_app}/qq_conn/\${your tencent app id}/ # 可选项目") 15 | } 16 | 17 | group 'io.github.v7lin.tencent_kit' 18 | version library_version 19 | 20 | buildscript { 21 | repositories { 22 | google() 23 | mavenCentral() 24 | } 25 | 26 | dependencies { 27 | classpath 'com.android.tools.build:gradle:7.3.0' 28 | classpath 'org.yaml:snakeyaml:1.17' 29 | } 30 | } 31 | 32 | rootProject.allprojects { 33 | repositories { 34 | google() 35 | mavenCentral() 36 | } 37 | } 38 | 39 | apply plugin: 'com.android.library' 40 | 41 | android { 42 | if (project.android.hasProperty('namespace')) { 43 | namespace 'io.github.v7lin.tencent_kit' 44 | } 45 | 46 | compileSdkVersion 31 47 | 48 | compileOptions { 49 | sourceCompatibility JavaVersion.VERSION_1_8 50 | targetCompatibility JavaVersion.VERSION_1_8 51 | } 52 | 53 | // useLibrary 'org.apache.http.legacy' 54 | 55 | resourcePrefix 'tencent_kit' 56 | 57 | defaultConfig { 58 | minSdkVersion 16 59 | 60 | // library 混淆 -> 随 library 引用,自动添加到 apk 打包混淆 61 | consumerProguardFiles 'consumer-rules.pro' 62 | 63 | manifestPlaceholders += [ 64 | TENCENT_APP_ID: app_id 65 | ] 66 | } 67 | 68 | flavorDimensions 'vendor' 69 | 70 | productFlavors { 71 | vendor { 72 | dimension 'vendor' 73 | 74 | // library 混淆 -> 随 library 引用,自动添加到 apk 打包混淆 75 | consumerProguardFiles 'consumer-vendor-rules.pro' 76 | } 77 | } 78 | 79 | dependencies { 80 | vendorImplementation fileTree(include: ['*.jar'], dir: 'libs') 81 | 82 | testImplementation 'junit:junit:4.13.2' 83 | testImplementation 'org.mockito:mockito-core:5.0.0' 84 | } 85 | 86 | testOptions { 87 | unitTests.all { 88 | unitTests.returnDefaultValues = true 89 | testLogging { 90 | events "passed", "skipped", "failed", "standardOut", "standardError" 91 | outputs.upToDateWhen {false} 92 | showStandardStreams = true 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CADisableMinimumFrameDurationOnPhone 6 | 7 | CFBundleDevelopmentRegion 8 | $(DEVELOPMENT_LANGUAGE) 9 | CFBundleDisplayName 10 | Tencent Kit 11 | CFBundleExecutable 12 | $(EXECUTABLE_NAME) 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | tencent_kit_example 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | $(FLUTTER_BUILD_NAME) 23 | CFBundleSignature 24 | ???? 25 | CFBundleURLTypes 26 | 27 | 28 | CFBundleTypeRole 29 | Editor 30 | CFBundleURLName 31 | tencent 32 | CFBundleURLSchemes 33 | 34 | tencent123456789 35 | 36 | 37 | 38 | CFBundleVersion 39 | $(FLUTTER_BUILD_NUMBER) 40 | LSApplicationQueriesSchemes 41 | 42 | mqq 43 | mqqapi 44 | tim 45 | mqqopensdknopasteboard 46 | mqqopensdknopasteboardios16 47 | mqqopensdkapiV2 48 | mqqgamebindinggroup 49 | mqqopensdkavatar 50 | mqqopensdkfriend 51 | mqqopensdklaunchminiapp 52 | mqzone 53 | tencentapi.qq.reqContent 54 | tencentapi.qzone.reqContent 55 | mqqthirdappgroup 56 | mqqopensdkminiapp 57 | 58 | LSRequiresIPhoneOS 59 | 60 | NSAppTransportSecurity 61 | 62 | NSAllowsArbitraryLoads 63 | 64 | NSAllowsArbitraryLoadsInWebContent 65 | 66 | 67 | UIApplicationSupportsIndirectInputEvents 68 | 69 | UILaunchStoryboardName 70 | LaunchScreen 71 | UIMainStoryboardFile 72 | Main 73 | UISupportedInterfaceOrientations 74 | 75 | UIInterfaceOrientationPortrait 76 | UIInterfaceOrientationLandscapeLeft 77 | UIInterfaceOrientationLandscapeRight 78 | 79 | UISupportedInterfaceOrientations~ipad 80 | 81 | UIInterfaceOrientationPortrait 82 | UIInterfaceOrientationPortraitUpsideDown 83 | UIInterfaceOrientationLandscapeLeft 84 | UIInterfaceOrientationLandscapeRight 85 | 86 | UIViewControllerBasedStatusBarAppearance 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | concurrency: 4 | group: ${{ github.workflow }}-${{ github.ref }} 5 | cancel-in-progress: true 6 | 7 | on: 8 | push: 9 | branches: 10 | - main 11 | - master 12 | paths-ignore: 13 | - '**.md' 14 | pull_request: 15 | branches: 16 | - main 17 | - master 18 | paths-ignore: 19 | - '**.md' 20 | 21 | jobs: 22 | analyze: 23 | name: Analyze on ${{ matrix.os }} 24 | runs-on: ${{ matrix.os }} 25 | strategy: 26 | matrix: 27 | os: [ubuntu-latest] 28 | steps: 29 | - uses: actions/checkout@v4 30 | - uses: actions/setup-java@v4 31 | with: 32 | distribution: 'zulu' 33 | java-version: '17' 34 | - uses: subosito/flutter-action@v2 35 | with: 36 | channel: "stable" 37 | cache: true 38 | - name: Log Dart/Flutter versions 39 | run: | 40 | dart --version 41 | flutter --version 42 | - run: flutter doctor -v 43 | - name: Prepare dependencies 44 | run: flutter pub get 45 | - name: Check Dart code formatting 46 | run: dart format . -o none --set-exit-if-changed 47 | - name: Analyze lib & test 48 | run: flutter analyze lib test 49 | - name: Analyze example 50 | run: flutter analyze example 51 | - name: Run tests 52 | run: flutter test 53 | - name: Dry run docs generate 54 | run: dart doc --dry-run . 55 | continue-on-error: true 56 | 57 | build_ios: 58 | needs: analyze 59 | name: Build iOS on ${{ matrix.os }} 60 | runs-on: ${{ matrix.os }} 61 | strategy: 62 | matrix: 63 | os: [macos-latest] 64 | steps: 65 | - uses: actions/checkout@v3 66 | - uses: subosito/flutter-action@v2 67 | with: 68 | channel: 'stable' 69 | cache: true 70 | - name: Run llvm/clang-format@shell 71 | if: matrix.os == 'macos-latest' 72 | run: | 73 | brew install clang-format 74 | - name: Run ruby/plist@shell 75 | if: matrix.os == 'macos-latest' 76 | run: | 77 | gem install plist 78 | - name: Run clang-format 79 | if: matrix.os == 'macos-latest' 80 | run: clang-format -style=file -i ios/Classes/*.h ios/Classes/*.m --dry-run --Werror 81 | - run: flutter pub get 82 | - run: cd example; flutter test; flutter build ios --no-codesign 83 | 84 | build_android: 85 | needs: analyze 86 | name: Build Android on ${{ matrix.os }} 87 | runs-on: ${{ matrix.os }} 88 | strategy: 89 | fail-fast: false 90 | matrix: 91 | os: [ubuntu-latest] 92 | steps: 93 | - uses: actions/checkout@v3 94 | - uses: actions/setup-java@v2 95 | with: 96 | distribution: 'zulu' 97 | java-version: '17' 98 | - uses: subosito/flutter-action@v2 99 | with: 100 | channel: 'stable' 101 | cache: true 102 | - run: flutter pub get 103 | - run: cd example; flutter test; flutter build apk --debug 104 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/lib/api/model/tencent_api_resp.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:json_annotation/json_annotation.dart'; 4 | 5 | part 'tencent_api_resp.g.dart'; 6 | 7 | abstract class TencentApiResp { 8 | const TencentApiResp({ 9 | required this.ret, 10 | this.msg, 11 | }); 12 | 13 | /// 网络请求成功发送至服务器,并且服务器返回数据格式正确 14 | /// 这里包括所请求业务操作失败的情况,例如没有授权等原因导致 15 | static const int kRetSuccess = 0; 16 | 17 | @JsonKey( 18 | defaultValue: kRetSuccess, 19 | ) 20 | final int ret; 21 | final String? msg; 22 | 23 | bool get isSuccessful => ret == kRetSuccess; 24 | 25 | Map toJson(); 26 | 27 | @override 28 | String toString() => const JsonEncoder.withIndent(' ').convert(toJson()); 29 | } 30 | 31 | @JsonSerializable( 32 | explicitToJson: true, 33 | fieldRename: FieldRename.snake, 34 | ) 35 | class TencentUserInfoResp extends TencentApiResp { 36 | const TencentUserInfoResp({ 37 | required super.ret, 38 | super.msg, 39 | this.isLost, 40 | this.nickname, 41 | this.gender, 42 | this.genderType, 43 | this.province, 44 | this.city, 45 | this.year, 46 | this.constellation, 47 | this.figureurl, 48 | this.figureurl1, 49 | this.figureurl2, 50 | this.figureurlQq, 51 | this.figureurlQq1, 52 | this.figureurlQq2, 53 | this.figureurlType, 54 | this.isYellowVip, 55 | this.vip, 56 | this.yellowVipLevel, 57 | this.level, 58 | this.isYellowYearVip, 59 | }); 60 | 61 | factory TencentUserInfoResp.fromJson(Map json) => 62 | _$TencentUserInfoRespFromJson(json); 63 | 64 | final int? isLost; 65 | final String? nickname; 66 | final String? gender; // 男/女 67 | final int? genderType; // 男/女 - 1 68 | final String? province; 69 | final String? city; 70 | final String? year; 71 | final String? constellation; 72 | final String? figureurl; 73 | @JsonKey( 74 | name: 'figureurl_1', 75 | ) 76 | final String? figureurl1; 77 | @JsonKey( 78 | name: 'figureurl_2', 79 | ) 80 | final String? figureurl2; 81 | final String? figureurlQq; // 140 * 140 82 | @JsonKey( 83 | name: 'figureurl_qq_1', 84 | ) 85 | final String? figureurlQq1; // 大小为40×40像素的QQ头像URL。 86 | @JsonKey( 87 | name: 'figureurl_qq_2', 88 | ) 89 | final String? 90 | figureurlQq2; // 大小为100×100像素的QQ头像URL。需要注意,不是所有的用户都拥有QQ的100x100的头像,但40x40像素则是一定会有。 91 | final String? figureurlType; 92 | final String? isYellowVip; 93 | final String? vip; 94 | final String? yellowVipLevel; 95 | final String? level; 96 | final String? isYellowYearVip; 97 | 98 | bool get isMale => gender == '男'; 99 | 100 | bool get isFemale => gender == '女'; 101 | 102 | String? get headImgUrl { 103 | if (figureurlQq?.isNotEmpty ?? false) { 104 | return figureurlQq; 105 | } 106 | if (figureurlQq2?.isNotEmpty ?? false) { 107 | return figureurlQq2; 108 | } 109 | if (figureurlQq1?.isNotEmpty ?? false) { 110 | return figureurlQq1; 111 | } 112 | if (figureurl2?.isNotEmpty ?? false) { 113 | return figureurl2; 114 | } 115 | if (figureurl1?.isNotEmpty ?? false) { 116 | return figureurl1; 117 | } 118 | return figureurl; 119 | } 120 | 121 | @override 122 | Map toJson() => _$TencentUserInfoRespToJson(this); 123 | } 124 | -------------------------------------------------------------------------------- /test/tencent_kit_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | import 'package:plugin_platform_interface/plugin_platform_interface.dart'; 3 | import 'package:tencent_kit/src/constant.dart'; 4 | import 'package:tencent_kit/src/model/resp.dart'; 5 | import 'package:tencent_kit/src/tencent_kit_method_channel.dart'; 6 | import 'package:tencent_kit/src/tencent_kit_platform_interface.dart'; 7 | 8 | class MockTencentKitPlatform 9 | with MockPlatformInterfaceMixin 10 | implements TencentKitPlatform { 11 | @override 12 | Future setIsPermissionGranted({ 13 | required bool granted, 14 | String? buildModel, 15 | }) { 16 | throw UnimplementedError(); 17 | } 18 | 19 | @override 20 | Future registerApp({ 21 | required String appId, 22 | String? universalLink, 23 | }) async { 24 | throw UnimplementedError(); 25 | } 26 | 27 | @override 28 | Future isQQInstalled() { 29 | return Future.value(true); 30 | } 31 | 32 | @override 33 | Future isTIMInstalled() { 34 | throw UnimplementedError(); 35 | } 36 | 37 | @override 38 | Stream respStream() { 39 | throw UnimplementedError(); 40 | } 41 | 42 | @override 43 | Future login({ 44 | required List scope, 45 | bool qrcode = false, 46 | }) { 47 | throw UnimplementedError(); 48 | } 49 | 50 | @override 51 | Future loginServerSide({ 52 | required List scope, 53 | bool qrcode = false, 54 | }) { 55 | throw UnimplementedError(); 56 | } 57 | 58 | @override 59 | Future logout() { 60 | throw UnimplementedError(); 61 | } 62 | 63 | @override 64 | Future shareImage({ 65 | required int scene, 66 | required Uri imageUri, 67 | String? appName, 68 | int extInt = TencentQZoneFlag.kDefault, 69 | }) { 70 | throw UnimplementedError(); 71 | } 72 | 73 | @override 74 | Future shareMood({ 75 | required int scene, 76 | String? summary, 77 | List? imageUris, 78 | Uri? videoUri, 79 | }) { 80 | throw UnimplementedError(); 81 | } 82 | 83 | @override 84 | Future shareMusic({ 85 | required int scene, 86 | required String title, 87 | String? summary, 88 | Uri? imageUri, 89 | required String musicUrl, 90 | required String targetUrl, 91 | String? appName, 92 | int extInt = TencentQZoneFlag.kDefault, 93 | }) { 94 | throw UnimplementedError(); 95 | } 96 | 97 | @override 98 | Future shareText({ 99 | required int scene, 100 | required String summary, 101 | }) { 102 | throw UnimplementedError(); 103 | } 104 | 105 | @override 106 | Future shareWebpage({ 107 | required int scene, 108 | required String title, 109 | String? summary, 110 | Uri? imageUri, 111 | required String targetUrl, 112 | String? appName, 113 | int extInt = TencentQZoneFlag.kDefault, 114 | }) { 115 | throw UnimplementedError(); 116 | } 117 | } 118 | 119 | void main() { 120 | final TencentKitPlatform initialPlatform = TencentKitPlatform.instance; 121 | 122 | test('$MethodChannelTencentKit is the default instance', () { 123 | expect(initialPlatform, isInstanceOf()); 124 | }); 125 | 126 | test('isQQInstalled', () async { 127 | final MockTencentKitPlatform fakePlatform = MockTencentKitPlatform(); 128 | TencentKitPlatform.instance = fakePlatform; 129 | 130 | expect(await TencentKitPlatform.instance.isQQInstalled(), true); 131 | }); 132 | } 133 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /lib/src/tencent_kit_platform_interface.dart: -------------------------------------------------------------------------------- 1 | import 'package:plugin_platform_interface/plugin_platform_interface.dart'; 2 | import 'package:tencent_kit/src/constant.dart'; 3 | import 'package:tencent_kit/src/model/resp.dart'; 4 | import 'package:tencent_kit/src/tencent_kit_method_channel.dart'; 5 | 6 | abstract class TencentKitPlatform extends PlatformInterface { 7 | /// Constructs a TencentKitPlatform. 8 | TencentKitPlatform() : super(token: _token); 9 | 10 | static final Object _token = Object(); 11 | 12 | static TencentKitPlatform _instance = MethodChannelTencentKit(); 13 | 14 | /// The default instance of [TencentKitPlatform] to use. 15 | /// 16 | /// Defaults to [MethodChannelTencentKit]. 17 | static TencentKitPlatform get instance => _instance; 18 | 19 | /// Platform-specific implementations should set this with their own 20 | /// platform-specific class that extends [TencentKitPlatform] when 21 | /// they register themselves. 22 | static set instance(TencentKitPlatform instance) { 23 | PlatformInterface.verifyToken(instance, _token); 24 | _instance = instance; 25 | } 26 | 27 | /// 设置是否已授权获取设备信息/是否同意隐私协议 28 | Future setIsPermissionGranted({ 29 | required bool granted, 30 | String? buildModel /* android.os.Build.MODEL */, 31 | }) { 32 | throw UnimplementedError( 33 | 'setIsPermissionGranted({required granted, buildModel}) has not been implemented.'); 34 | } 35 | 36 | /// 向 Open_SDK 注册 37 | Future registerApp({ 38 | required String appId, 39 | String? universalLink, 40 | }) { 41 | throw UnimplementedError( 42 | 'registerApp({required appId, universalLink}) has not been implemented.'); 43 | } 44 | 45 | /// 46 | Stream respStream() { 47 | throw UnimplementedError('respStream() has not been implemented.'); 48 | } 49 | 50 | /// 检查QQ是否已安装 51 | Future isQQInstalled() { 52 | throw UnimplementedError('isQQInstalled() has not been implemented.'); 53 | } 54 | 55 | /// 检查QQ是否已安装 56 | Future isTIMInstalled() { 57 | throw UnimplementedError('isTIMInstalled() has not been implemented.'); 58 | } 59 | 60 | /// 登录 61 | Future login({ 62 | required List scope, 63 | bool qrcode = false, 64 | }) { 65 | throw UnimplementedError( 66 | 'login({required scope}) has not been implemented.'); 67 | } 68 | 69 | /// 登录(Server-Side) 70 | Future loginServerSide({ 71 | required List scope, 72 | bool qrcode = false, 73 | }) { 74 | throw UnimplementedError( 75 | 'loginServerSide({required scope}) has not been implemented.'); 76 | } 77 | 78 | /// 登出 79 | Future logout() { 80 | throw UnimplementedError('logout() has not been implemented.'); 81 | } 82 | 83 | /// 分享 - 说说 84 | Future shareMood({ 85 | required int scene, 86 | String? summary, 87 | List? imageUris, 88 | Uri? videoUri, 89 | }) { 90 | throw UnimplementedError( 91 | 'shareMood({required scene, summary, imageUris, videoUri}) has not been implemented.'); 92 | } 93 | 94 | /// 分享 - 文本(Android调用的是系统API,故而不会有回调) 95 | Future shareText({ 96 | required int scene, 97 | required String summary, 98 | }) { 99 | throw UnimplementedError( 100 | 'shareText({required scene, required summary}) has not been implemented.'); 101 | } 102 | 103 | /// 分享 - 图片 104 | Future shareImage({ 105 | required int scene, 106 | required Uri imageUri, 107 | String? appName, 108 | int extInt = TencentQZoneFlag.kDefault, 109 | }) { 110 | throw UnimplementedError( 111 | 'shareImage({required scene, required imageUri, appName, extInt}) has not been implemented.'); 112 | } 113 | 114 | /// 分享 - 音乐 115 | Future shareMusic({ 116 | required int scene, 117 | required String title, 118 | String? summary, 119 | Uri? imageUri, 120 | required String musicUrl, 121 | required String targetUrl, 122 | String? appName, 123 | int extInt = TencentQZoneFlag.kDefault, 124 | }) { 125 | throw UnimplementedError( 126 | 'shareMusic({required scene, required title, summary, imageUri, required musicUrl, required targetUrl, appName, extInt}) has not been implemented.'); 127 | } 128 | 129 | /// 分享 - 网页 130 | Future shareWebpage({ 131 | required int scene, 132 | required String title, 133 | String? summary, 134 | Uri? imageUri, 135 | required String targetUrl, 136 | String? appName, 137 | int extInt = TencentQZoneFlag.kDefault, 138 | }) { 139 | throw UnimplementedError( 140 | 'shareWebpage({required scene, required title, summary, imageUri, required targetUrl, appName, extInt}) has not been implemented.'); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tencent_kit 2 | 3 | [![Pub Package](https://img.shields.io/pub/v/tencent_kit.svg)](https://pub.dev/packages/tencent_kit) 4 | [![License](https://img.shields.io/github/license/RxReader/tencent_kit)](https://github.com/RxReader/tencent_kit/blob/master/LICENSE) 5 | 6 | Flutter 版腾讯(QQ)SDK 7 | 8 | ## 相关工具 9 | 10 | * [Flutter版微信SDK](https://github.com/RxReader/wechat_kit) 11 | * [Flutter版腾讯(QQ)SDK](https://github.com/RxReader/tencent_kit) 12 | * [Flutter版新浪微博SDK](https://github.com/RxReader/weibo_kit) 13 | * [Flutter版支付宝SDK](https://github.com/RxReader/alipay_kit) 14 | * [Flutter版深度链接](https://github.com/RxReader/link_kit) 15 | * [Flutter版walle渠道打包工具](https://github.com/RxReader/walle_kit) 16 | 17 | ## Dart/Flutter Pub 私服 18 | 19 | * [simple_pub_server](https://github.com/RxReader/simple_pub_server) 20 | 21 | ## 相关文档 22 | 23 | * [腾讯开放平台](https://open.tencent.com/) 24 | * [QQ互联](http://wiki.connect.qq.com/) 25 | * [QQ 创建、填写及校验UniversalLinks](https://wiki.connect.qq.com/%E5%A1%AB%E5%86%99%E5%8F%8A%E6%A0%A1%E9%AA%8Cuniversallinks) 26 | * [Apple Universal Links](https://developer.apple.com/library/archive/documentation/General/Conceptual/AppSearch/UniversalLinks.html) 27 | 28 | ## 开始使用 29 | 30 | ### Android 31 | 32 | ```txt 33 | # 不需要做任何额外接入工作 34 | # 配置已集成到脚本里 35 | # 混淆已打入 Library,随 Library 引用,自动添加到 apk 打包混淆 36 | ``` 37 | 38 | ### iOS 39 | 40 | > 暂不支持 SceneDelegate,详见文档 [iOS_SDK环境搭建](https://wiki.connect.qq.com/ios_sdk%e7%8e%af%e5%a2%83%e6%90%ad%e5%bb%ba) 41 | 42 | ```txt 43 | # 不需要做任何额外接入工作 44 | # 配置已集成到脚本里 45 | ``` 46 | 47 | * Universal Links 48 | 49 | apple-app-site-association - 通过 https://${your applinks domain}/.well-known/apple-app-site-association 链接可访问 50 | 51 | 示例: 52 | 53 | https://${your applinks domain}/universal_link/${example_app}/qq_conn/${appId} 54 | 55 | ```json 56 | { 57 | "applinks": { 58 | "apps": [], 59 | "details": [ 60 | { 61 | "appID": "${your team id}.${your app bundle id}", 62 | "paths": [ 63 | "/universal_link/${example_app}/qq_conn/${your tencent app id}/*" 64 | ] 65 | } 66 | ] 67 | } 68 | } 69 | ``` 70 | 71 | > ⚠️ 很多 SDK 都会用到 universal_link,可为不同 SDK 分配不同的 path 以作区分 72 | 73 | ### HarmonyOS 74 | 75 | * 当前在 `HarmonyOS` 平台, 仅支持 `setIsPermissionGranted/registerApp/isQQInstalled/loginServerSide` 76 | * 由于 SDK 限制,当前仅支持 Server-Side 模式登录,请调用 `loginServerSide` 方法登录, 支持拉起 App 授权或 H5 授权 (qrcode 为 true 即可) 77 | * 为了 API 统一, Server-Side 模式授权返回的 auth code 存储在 `TencentLoginResp.accessToken` (不要当成客户端的 token 使用) 78 | * 详情阅读官方文档: [harmonyos_sdk 环境搭建](https://wiki.connect.qq.com/harmonyos_sdk%e7%8e%af%e5%a2%83%e6%90%ad%e5%bb%ba), 并阅读最后的说明 79 | * 关于在后端使用 code 换取 access_token 的问题, 请参考官方文档: [通过Authorization Code获取Access Token](https://wiki.connect.qq.com/%E4%BD%BF%E7%94%A8authorization_code%E8%8E%B7%E5%8F%96access_token#:~:text=Step2%EF%BC%9A%E9%80%9A%E8%BF%87Authorization%20Code%E8%8E%B7%E5%8F%96Access%20Token) , `redirect_uri` 一般为 `auth://tauth.qq.com/` 80 | 81 | 项目中 module.json5 的 "module" 节点下配置 querySchemes 82 | 83 | ```json5 84 | "querySchemes": [ 85 | "https", 86 | "qqopenapi" 87 | ] 88 | ``` 89 | 90 | 在 Ability 的 skills 节点中配置 scheme 91 | 92 | ```json5 93 | "skills": [ 94 | { 95 | "entities": [ 96 | "entity.system.browsable" 97 | ], 98 | "actions": [ 99 | "ohos.want.action.viewData" 100 | ], 101 | "uris": [ 102 | { 103 | "scheme": "qqopenapi", // 接收 QQ 回调数据 104 | "host": "102061317", // 业务申请的互联 appId 105 | "path": "auth", 106 | "linkFeature": "Login", 107 | } 108 | ] 109 | } 110 | ] 111 | ``` 112 | 113 | ### Flutter 114 | 115 | |分享类型|说说(图/文/视频)|文本|图片|音乐|视频|网页| 116 | |:---:|:---:|:---:|:---:|:---:|:---:|:---:| 117 | |QQ|不支持|不支持|支持|支持|不支持|支持| 118 | |QZone|支持|不支持|不支持|不支持|不支持|支持| 119 | 120 | * 注意 121 | 122 | ⚠️⚠️⚠️ registerApp 前必须先调用 setIsPermissionGranted [issues/60](https://github.com/RxReader/tencent_kit/issues/60) [issues/79](https://github.com/RxReader/tencent_kit/issues/79) 123 | 124 | * 兼容 125 | 126 | flutter 2.5 兼容问题 [issues/54](https://github.com/RxReader/tencent_kit/issues/54) 127 | 128 | ```ruby 129 | post_install do |installer| 130 | installer.pods_project.targets.each do |target| 131 | flutter_additional_ios_build_settings(target) 132 | # 兼容 Flutter 2.5 133 | target.build_configurations.each do |config| 134 | # config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0' 135 | config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'i386 arm64' 136 | end 137 | end 138 | end 139 | ``` 140 | 141 | * 配置 142 | 143 | ```yaml 144 | dependencies: 145 | tencent_kit: ^${latestTag} 146 | # tencent_kit: 147 | # git: 148 | # url: https://github.com/RxReader/tencent_kit.git 149 | 150 | tencent_kit: 151 | app_id: ${your tencent app id} 152 | universal_link: https://${your applinks domain}/universal_link/${example_app}/qq_conn/${your tencent app id}/ # 可选项目 153 | ``` 154 | 155 | * 安装(仅iOS) 156 | 157 | ```shell 158 | # step.1 安装必要依赖 159 | sudo gem install plist 160 | # step.2 切换工作目录,插件里为 example/ios/,普通项目为 ios/ 161 | cd example/ios/ 162 | # step.3 执行脚本 163 | pod install 164 | ``` 165 | 166 | ## 示例 167 | 168 | [示例](./example/lib/main.dart) 169 | 170 | ## Star History 171 | 172 | ![stars](https://starchart.cc/rxreader/tencent_kit.svg) 173 | -------------------------------------------------------------------------------- /ohos/src/main/ets/components/plugin/TencentKitPlugin.ets: -------------------------------------------------------------------------------- 1 | import { 2 | AbilityAware, 3 | AbilityPluginBinding, 4 | Any, 5 | FlutterPlugin, 6 | FlutterPluginBinding, 7 | MethodCall, 8 | MethodCallHandler, 9 | MethodChannel, 10 | MethodResult, 11 | NewWantListener, 12 | } from '@ohos/flutter_ohos'; 13 | import { 14 | IQQOpenApi, 15 | QQOpenApiFactory, 16 | ApiCallback, 17 | AuthResponse, 18 | } from '@tencent/qq-open-sdk' 19 | import { AbilityConstant, Want } from '@kit.AbilityKit'; 20 | 21 | enum TencentRetCode { 22 | RET_SUCCESS = 0, 23 | RET_FAILED = 1, 24 | RET_COMMON = -1, 25 | RET_CANCEL = -2 26 | } 27 | 28 | const KEY_RET_CODE = "ret"; 29 | const KEY_RET_MSG = "msg"; 30 | 31 | /** TencentKitPlugin **/ 32 | export default class TencentKitPlugin implements FlutterPlugin, MethodCallHandler, AbilityAware, NewWantListener { 33 | private channel: MethodChannel | null = null; 34 | private binding: AbilityPluginBinding | null = null; 35 | private tencent: IQQOpenApi | null = null; 36 | private modeServerSide: boolean = false; 37 | 38 | getUniqueClassName(): string { 39 | return "TencentKitPlugin" 40 | } 41 | 42 | onAttachedToEngine(binding: FlutterPluginBinding): void { 43 | this.channel = new MethodChannel(binding.getBinaryMessenger(), "v7lin.github.io/tencent_kit"); 44 | this.channel.setMethodCallHandler(this) 45 | } 46 | 47 | onDetachedFromEngine(binding: FlutterPluginBinding): void { 48 | if (this.channel != null) { 49 | this.channel.setMethodCallHandler(null) 50 | } 51 | this.channel = null; 52 | } 53 | 54 | onAttachedToAbility(binding: AbilityPluginBinding): void { 55 | this.binding = binding; 56 | binding.addOnNewWantListener(this); 57 | } 58 | 59 | onDetachedFromAbility(): void { 60 | this.binding = null; 61 | } 62 | 63 | onNewWant(want: Want, launchParams: AbilityConstant.LaunchParam): void { 64 | this.tencent?.handleResult(want) 65 | } 66 | 67 | authListener: ApiCallback = { 68 | onComplete: (response: AuthResponse): void => { 69 | const resp: Map = new Map(); 70 | try { 71 | if (response.ret == TencentRetCode.RET_SUCCESS) { 72 | // 服务端模式时, access_token 设置为为 authCode, 当前 SDK 不支持客户端模式(因此 null) 73 | const accessToken: string | null = this.modeServerSide ? response.authCode : null; 74 | resp.set(KEY_RET_CODE, TencentRetCode.RET_SUCCESS); 75 | resp.set("openid", response.openId); 76 | resp.set("access_token", accessToken); 77 | resp.set("expires_in", response.expiresIn); 78 | resp.set("create_at", response.authTime); 79 | } else { 80 | resp.set("ret", TencentRetCode.RET_COMMON); 81 | } 82 | } catch (e) { 83 | resp.set(KEY_RET_CODE, TencentRetCode.RET_COMMON); 84 | resp.set(KEY_RET_MSG, `${e}`); 85 | } 86 | this.channel?.invokeMethod("onLoginResp", resp); 87 | }, 88 | onError: (msg: string | null): void => { 89 | const resp: Map = new Map(); 90 | resp.set(KEY_RET_CODE, TencentRetCode.RET_FAILED); 91 | resp.set(KEY_RET_MSG, msg); 92 | this.channel?.invokeMethod("onLoginResp", resp); 93 | }, 94 | onCancel: (msg: string | null): void => { 95 | const resp: Map = new Map(); 96 | resp.set(KEY_RET_CODE, TencentRetCode.RET_CANCEL); 97 | resp.set(KEY_RET_MSG, msg); 98 | this.channel?.invokeMethod("onLoginResp", resp); 99 | } 100 | } 101 | 102 | onMethodCall(call: MethodCall, result: MethodResult): void { 103 | switch (call.method) { 104 | case "setIsPermissionGranted": 105 | // 当前 SDK 不支持此方法,暂时返回 null 106 | result.success(null); 107 | break; 108 | case "registerApp": 109 | this.registerApp(call, result); 110 | break; 111 | case "isQQInstalled": 112 | result.success(this.tencent != null && this.tencent.isQQInstalled()); 113 | break; 114 | case "isTIMInstalled": 115 | // TIM 并未适配 harmonyOS, 暂时返回 false 116 | result.success(false); 117 | break; 118 | case "login": 119 | this.login(call, result); 120 | break; 121 | case "loginServerSide": 122 | this.loginServerSide(call, result); 123 | break; 124 | default: 125 | result.notImplemented(); 126 | } 127 | } 128 | 129 | private registerApp(call: MethodCall, result: MethodResult) { 130 | const appId: number = parseInt(call.argument("appId") as string); 131 | this.tencent = QQOpenApiFactory.createApi(appId, { 132 | autoHandleAuthResult: true, 133 | forceEnableWeb: false, 134 | }); 135 | result.success(null); 136 | } 137 | 138 | private login(call: MethodCall, result: MethodResult) { 139 | // this.modeServerSide = false; // 客户端授权 140 | // 当前 SDK 仅支持 ServerSide 授权,暂时不处理 141 | result.success(null); 142 | } 143 | 144 | private loginServerSide(call: MethodCall, result: MethodResult) { 145 | this.modeServerSide = true; // 服务端授权 146 | const scope: string = call.argument("scope"); 147 | const qrcode: boolean = call.argument("qrcode"); 148 | this.tencent?.login({ 149 | scope: scope, 150 | useQrCode: qrcode, 151 | forceWebLogin: qrcode || !this.tencent!.isQQInstalled(), // H5 授权, 二维码登录或 QQ 未安装时强制启用 152 | networkTimeout: 0, // 不限制, 由 SDK 自行决定 153 | }, this.authListener); 154 | result.success(null); 155 | } 156 | } -------------------------------------------------------------------------------- /ios/Libraries/TencentOpenAPI.framework/Headers/QQApiInterface.h: -------------------------------------------------------------------------------- 1 | /// 2 | /// \file QQApiInterface.h 3 | /// \brief QQApi接口简化封装 4 | /// 5 | /// Created by Tencent on 12-5-15. 6 | /// Copyright (c) 2012年 Tencent. All rights reserved. 7 | /// 8 | 9 | #import 10 | #import "QQApiInterfaceObject.h" 11 | 12 | typedef void (^sendResultBlock)(NSDictionary *result); 13 | 14 | // 发送消息回调是否发送成功 15 | typedef void(^QQApiInterfaceSendMessageResultBlock)(QQApiSendResultCode sendResultCode, NSString *message); 16 | 17 | /** 18 | \brief 处理来至QQ的请求及响应的回调协议 19 | */ 20 | @protocol QQApiInterfaceDelegate 21 | 22 | /** 23 | 处理来至QQ的请求 24 | */ 25 | - (void)onReq:(QQBaseReq *)req; 26 | 27 | /** 28 | 处理来至QQ的响应 29 | */ 30 | - (void)onResp:(QQBaseResp *)resp; 31 | 32 | /** 33 | 处理QQ在线状态的回调 34 | */ 35 | - (void)isOnlineResponse:(NSDictionary *)response; 36 | 37 | @end 38 | 39 | /** 40 | \brief 对QQApi的简单封装类 41 | */ 42 | @interface QQApiInterface : NSObject 43 | 44 | /** 45 | 处理由手Q唤起的普通跳转请求 46 | \param url 待处理的url跳转请求 47 | \param delegate 第三方应用用于处理来至QQ请求及响应的委托对象 48 | \return 跳转请求处理结果,YES表示成功处理,NO表示不支持的请求协议或处理失败 49 | */ 50 | + (BOOL)handleOpenURL:(NSURL *)url delegate:(id)delegate; 51 | 52 | /** 53 | 处理由手Q唤起的universallink跳转请求 54 | \param universallink 待处理的universallink跳转请求 55 | \param delegate 第三方应用用于处理来至QQ请求及响应的委托对象 56 | \return 跳转请求处理结果,YES表示成功处理,NO表示不支持的请求协议或处理失败 57 | */ 58 | + (BOOL)handleOpenUniversallink:(NSURL*)universallink delegate:(id)delegate; 59 | 60 | /** 61 | 向手Q发起分享请求 62 | \param req 分享内容的请求 63 | \return 请求发送结果码 64 | */ 65 | + (QQApiSendResultCode)sendReq:(QQBaseReq *)req; 66 | 67 | 68 | /** 69 | 向手Q QZone结合版发起分享请求 70 | \note H5分享只支持单张网络图片的传递 71 | \param req 分享内容的请求 72 | \return 请求发送结果码 73 | */ 74 | + (QQApiSendResultCode)SendReqToQZone:(QQBaseReq *)req; 75 | 76 | /** 77 | 向手Q发起设置QQ头像 78 | \param req 分享内容的请求 79 | \return 请求发送结果码 80 | */ 81 | + (QQApiSendResultCode)sendMessageToQQAvatarWithReq:(QQBaseReq*)req; 82 | 83 | 84 | + (QQApiSendResultCode)sendMessageToQQAuthWithReq:(QQBaseReq*)req; 85 | 86 | 87 | /** 88 | 向手Q发起绑群请求 89 | \param req 请求的内容 90 | \param resultBlock 请求回调 91 | */ 92 | + (void)sendThirdAppBindGroupReq:(QQBaseReq *)req resultBlock:(sendResultBlock)resultBlock; 93 | 94 | /** 95 | 向手Q发起加群请求 96 | \param req 请求的内容 97 | \param resultBlock 请求回调 98 | */ 99 | + (void)sendThirdAppJoinGroupReq:(QQBaseReq *)req resultBlock:(sendResultBlock)resultBlock; 100 | 101 | /** 102 | 向手Q发起解绑群请求 103 | \param req 请求的内容 104 | \param resultBlock 请求回调 105 | */ 106 | + (void)sendThirdAppUnBindGroupReq:(QQBaseReq *)req resultBlock:(sendResultBlock)resultBlock; 107 | 108 | /** 109 | 向手Q发起创建QQ频道的请求 110 | \param req 请求的内容 111 | \param resultBlock 回调发送结果 112 | \return void 113 | */ 114 | + (void)sendMessageToCreateQQGroupProWithMessageRequest:(SendMessageToQQReq *)messageRequest sendResultBlock:(QQApiInterfaceSendMessageResultBlock)sendResultBlock; 115 | 116 | 117 | /** 118 | 向手Q发起加入QQ频道的请求 119 | \param req 请求的内容 120 | \param resultBlock 回调发送结果 121 | \return void 122 | */ 123 | + (void)sendMessageToJoinQQGroupProWithMessageRequest:(SendMessageToQQReq *)messageRequest sendResultBlock:(QQApiInterfaceSendMessageResultBlock)sendResultBlock; 124 | 125 | 126 | /** 127 | 向手Q发起查询QQ频道openID的请求 128 | \param req 请求的内容 129 | \param resultBlock 请求回调 130 | */ 131 | + (void)sendQueryQQGroupProInfo:(QQBaseReq *)req resultBlock:(sendResultBlock)resultBlock; 132 | 133 | /** 134 | 向手Q发起组图分享到表情收藏 135 | \param req 分享内容的请求 136 | \return 请求发送结果码 137 | */ 138 | + (QQApiSendResultCode)sendMessageToFaceCollectionWithReq:(QQBaseReq*)req; 139 | 140 | /** 141 | 检测是否已安装QQ 142 | \return 如果QQ已安装则返回YES,否则返回NO 143 | 144 | \note SDK目前已经支持QQ、TIM授权登录及分享功能, 会按照QQ>TIM的顺序进行调用。 145 | 只要用户安装了QQ、TIM中任意一个应用,都可为第三方应用进行授权登录、分享功能。 146 | 第三方应用在接入SDK时不需要判断是否安装QQ、TIM。若有判断安装QQ、TIM的逻辑建议移除。 147 | */ 148 | + (BOOL)isQQInstalled; 149 | 150 | /** 151 | 检测是否已安装TIM 152 | \return 如果TIM已安装则返回YES,否则返回NO 153 | 154 | \note SDK目前已经支持QQ、TIM授权登录及分享功能, 会按照QQ>TIM的顺序进行调用。 155 | 只要用户安装了QQ、TIM中任意一个应用,都可为第三方应用进行授权登录、分享功能。 156 | 第三方应用在接入SDK时不需要判断是否安装QQ、TIM。若有判断安装QQ、TIM的逻辑建议移除。 157 | */ 158 | + (BOOL)isTIMInstalled; 159 | 160 | /** 161 | 检测QQ是否支持API调用 162 | \return 如果当前安装QQ版本支持API调用则返回YES,否则返回NO 163 | */ 164 | + (BOOL)isQQSupportApi; 165 | 166 | /** 167 | 检测TIM是否支持API调用 168 | \return 如果当前安装TIM版本支持API调用则返回YES,否则返回NO 169 | */ 170 | + (BOOL)isTIMSupportApi __attribute__((deprecated("已过期, 建议删除调用,调用地方用YES替代。"))); 171 | 172 | /** 173 | 检测是否支持分享 174 | \return 如果当前已安装QQ且QQ版本支持API调用 或者 当前已安装TIM且TIM版本支持API调用则返回YES,否则返回NO 175 | */ 176 | + (BOOL)isSupportShareToQQ; 177 | 178 | /** 179 | 检测是否支持分享到QQ结合版QZone 180 | \return 如果当前已安装QQ且QQ版本支持API调用则返回YES,否则返回NO 181 | */ 182 | + (BOOL)isSupportPushToQZone; 183 | 184 | /** 185 | 启动QQ 186 | \return 成功返回YES,否则返回NO 187 | */ 188 | + (BOOL)openQQ; 189 | 190 | /** 191 | 启动TIM 192 | \return 成功返回YES,否则返回NO 193 | */ 194 | + (BOOL)openTIM; 195 | 196 | /** 197 | 获取QQ下载地址 198 | 199 | 如果App通过QQApiInterface#isQQInstalledQQApiInterface#isQQSupportApi检测发现QQ没安装或当前版本QQ不支持API调用,可引导用户通过打开此链接下载最新版QQ。 200 | \return iPhoneQQ下载地址 201 | */ 202 | + (NSString *)getQQInstallUrl; 203 | 204 | /** 205 | 获取TIM下载地址 206 | 207 | 如果App通过QQApiInterface#isTIMInstalled检测发现TIM没安装或当前版本TIM不支持API调用,可引导用户通过打开此链接下载最新版TIM。 208 | \return iPhoneTIM下载地址 209 | */ 210 | + (NSString *)getTIMInstallUrl; 211 | 212 | #pragma mark - Log 213 | 214 | /*! @brief 调用此函数可以导出QQSDK的Log到第三方中,用于定位问题 215 | 216 | 注意1:SDK会强引用这个block,注意不要导致内存泄漏,注意不要导致内存泄漏 217 | 注意2:调用过一次startLog by block之后,如果再调用一次任意方式的startLoad,会释放上一次logBlock,不再回调上一个logBlock 218 | * 219 | * @param logBlock 打印log的回调block 220 | */ 221 | + (void)startLogWithBlock:(QQApiLogBolock)logBlock; 222 | 223 | ///停止回调打印 224 | + (void)stopLog; 225 | 226 | ///设置打印日志到文件开关on/off,如果不设置,默认不打印到文件 227 | + (void)setSwitchPrintLogToFile:(BOOL)on; 228 | 229 | ///日志文件目录 230 | + (NSString *)getLogFilePath; 231 | 232 | @end 233 | -------------------------------------------------------------------------------- /lib/src/tencent_kit_method_channel.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:tencent_kit/src/constant.dart'; 6 | import 'package:tencent_kit/src/model/resp.dart'; 7 | import 'package:tencent_kit/src/tencent_kit_platform_interface.dart'; 8 | 9 | /// An implementation of [TencentKitPlatform] that uses method channels. 10 | class MethodChannelTencentKit extends TencentKitPlatform { 11 | /// The method channel used to interact with the native platform. 12 | @visibleForTesting 13 | late final MethodChannel methodChannel = 14 | const MethodChannel('v7lin.github.io/tencent_kit') 15 | ..setMethodCallHandler(_handleMethod); 16 | 17 | final StreamController _respStreamController = 18 | StreamController.broadcast(); 19 | 20 | Future _handleMethod(MethodCall call) async { 21 | switch (call.method) { 22 | case 'onLoginResp': 23 | _respStreamController.add(TencentLoginResp.fromJson( 24 | (call.arguments as Map).cast())); 25 | case 'onShareResp': 26 | _respStreamController.add(TencentShareMsgResp.fromJson( 27 | (call.arguments as Map).cast())); 28 | } 29 | } 30 | 31 | @override 32 | Future setIsPermissionGranted({ 33 | required bool granted, 34 | String? buildModel /* android.os.Build.MODEL */, 35 | }) { 36 | return methodChannel.invokeMethod( 37 | 'setIsPermissionGranted', 38 | { 39 | 'granted': granted, 40 | if (buildModel?.isNotEmpty ?? false) 'build_model': buildModel, 41 | }, 42 | ); 43 | } 44 | 45 | @override 46 | Future registerApp({ 47 | required String appId, 48 | String? universalLink, 49 | }) { 50 | return methodChannel.invokeMethod( 51 | 'registerApp', 52 | { 53 | 'appId': appId, 54 | if (universalLink?.isNotEmpty ?? false) 'universalLink': universalLink, 55 | }, 56 | ); 57 | } 58 | 59 | @override 60 | Stream respStream() { 61 | return _respStreamController.stream; 62 | } 63 | 64 | @override 65 | Future isQQInstalled() async { 66 | return await methodChannel.invokeMethod('isQQInstalled') ?? false; 67 | } 68 | 69 | @override 70 | Future isTIMInstalled() async { 71 | return await methodChannel.invokeMethod('isTIMInstalled') ?? false; 72 | } 73 | 74 | @override 75 | Future login({ 76 | required List scope, 77 | bool qrcode = false, 78 | }) { 79 | return methodChannel.invokeMethod( 80 | 'login', 81 | { 82 | 'scope': scope.join(','), 83 | 'qrcode': qrcode, 84 | }, 85 | ); 86 | } 87 | 88 | @override 89 | Future loginServerSide({ 90 | required List scope, 91 | bool qrcode = false, 92 | }) { 93 | return methodChannel.invokeMethod( 94 | 'loginServerSide', 95 | { 96 | 'scope': scope.join(','), 97 | 'qrcode': qrcode, 98 | }, 99 | ); 100 | } 101 | 102 | @override 103 | Future logout() { 104 | return methodChannel.invokeMethod('logout'); 105 | } 106 | 107 | @override 108 | Future shareMood({ 109 | required int scene, 110 | String? summary, 111 | List? imageUris, 112 | Uri? videoUri, 113 | }) { 114 | assert(scene == TencentScene.kScene_QZone); 115 | assert((summary?.isNotEmpty ?? false) || 116 | ((imageUris?.isNotEmpty ?? false) && 117 | imageUris!.every((Uri element) => element.isScheme('file'))) || 118 | (videoUri != null && videoUri.isScheme('file'))); 119 | return methodChannel.invokeMethod( 120 | 'shareMood', 121 | { 122 | 'scene': scene, 123 | if (summary?.isNotEmpty ?? false) 'summary': summary, 124 | if (imageUris?.isNotEmpty ?? false) 125 | 'imageUris': 126 | imageUris!.map((Uri imageUri) => imageUri.toString()).toList(), 127 | if (videoUri != null) 'videoUri': videoUri.toString(), 128 | }, 129 | ); 130 | } 131 | 132 | @override 133 | Future shareText({ 134 | required int scene, 135 | required String summary, 136 | }) { 137 | assert(scene == TencentScene.kScene_QQ); 138 | return methodChannel.invokeMethod( 139 | 'shareText', 140 | { 141 | 'scene': scene, 142 | 'summary': summary, 143 | }, 144 | ); 145 | } 146 | 147 | @override 148 | Future shareImage({ 149 | required int scene, 150 | required Uri imageUri, 151 | String? appName, 152 | int extInt = TencentQZoneFlag.kDefault, 153 | }) { 154 | assert(scene == TencentScene.kScene_QQ); 155 | assert(imageUri.isScheme('file')); 156 | return methodChannel.invokeMethod( 157 | 'shareImage', 158 | { 159 | 'scene': scene, 160 | 'imageUri': imageUri.toString(), 161 | if (appName?.isNotEmpty ?? false) 'appName': appName, 162 | 'extInt': extInt, 163 | }, 164 | ); 165 | } 166 | 167 | @override 168 | Future shareMusic({ 169 | required int scene, 170 | required String title, 171 | String? summary, 172 | Uri? imageUri, 173 | required String musicUrl, 174 | required String targetUrl, 175 | String? appName, 176 | int extInt = TencentQZoneFlag.kDefault, 177 | }) { 178 | assert(scene == TencentScene.kScene_QQ); 179 | return methodChannel.invokeMethod( 180 | 'shareMusic', 181 | { 182 | 'scene': scene, 183 | 'title': title, 184 | if (summary?.isNotEmpty ?? false) 'summary': summary, 185 | if (imageUri != null) 'imageUri': imageUri.toString(), 186 | 'musicUrl': musicUrl, 187 | 'targetUrl': targetUrl, 188 | if (appName?.isNotEmpty ?? false) 'appName': appName, 189 | 'extInt': extInt, 190 | }, 191 | ); 192 | } 193 | 194 | @override 195 | Future shareWebpage({ 196 | required int scene, 197 | required String title, 198 | String? summary, 199 | Uri? imageUri, 200 | required String targetUrl, 201 | String? appName, 202 | int extInt = TencentQZoneFlag.kDefault, 203 | }) { 204 | return methodChannel.invokeMethod( 205 | 'shareWebpage', 206 | { 207 | 'scene': scene, 208 | 'title': title, 209 | if (summary?.isNotEmpty ?? false) 'summary': summary, 210 | if (imageUri != null) 'imageUri': imageUri.toString(), 211 | 'targetUrl': targetUrl, 212 | if (appName?.isNotEmpty ?? false) 'appName': appName, 213 | 'extInt': extInt, 214 | }, 215 | ); 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_cache_manager/flutter_cache_manager.dart'; 6 | import 'package:tencent_kit/tencent_kit.dart'; 7 | import 'package:tencent_kit_example/api/model/tencent_api_resp.dart'; 8 | import 'package:tencent_kit_example/api/model/tencent_unionid_resp.dart'; 9 | import 'package:tencent_kit_example/api/tencent_api.dart'; 10 | 11 | const String _kTencentAppID = 'your tencent app id'; 12 | const String _kUniversalLink = 'your tencent universal link'; // 可选项目 13 | 14 | void main() { 15 | runApp(const MyApp()); 16 | } 17 | 18 | class MyApp extends StatelessWidget { 19 | const MyApp({ 20 | super.key, 21 | }); 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return const MaterialApp( 26 | home: Home(), 27 | ); 28 | } 29 | } 30 | 31 | class Home extends StatefulWidget { 32 | const Home({ 33 | super.key, 34 | }); 35 | 36 | @override 37 | State createState() { 38 | return _HomeState(); 39 | } 40 | } 41 | 42 | class _HomeState extends State { 43 | late final StreamSubscription _respSubs; 44 | 45 | TencentLoginResp? _loginResp; 46 | 47 | @override 48 | void initState() { 49 | super.initState(); 50 | _respSubs = TencentKitPlatform.instance.respStream().listen(_listenLogin); 51 | } 52 | 53 | void _listenLogin(TencentResp resp) { 54 | if (resp is TencentLoginResp) { 55 | _loginResp = resp; 56 | final String content = 'login: ${resp.openid} - ${resp.accessToken}'; 57 | _showTips('登录', content); 58 | } else if (resp is TencentShareMsgResp) { 59 | final String content = 'share: ${resp.ret} - ${resp.msg}'; 60 | _showTips('分享', content); 61 | } 62 | } 63 | 64 | @override 65 | void dispose() { 66 | _respSubs.cancel(); 67 | super.dispose(); 68 | } 69 | 70 | @override 71 | Widget build(BuildContext context) { 72 | return Scaffold( 73 | appBar: AppBar( 74 | title: const Text('Tencent Kit Demo'), 75 | ), 76 | body: ListView( 77 | children: [ 78 | ListTile( 79 | title: const Text('3.1.0 之后的版本请先获取权限'), 80 | onTap: () async { 81 | await TencentKitPlatform.instance 82 | .setIsPermissionGranted(granted: true); 83 | _showTips('授权', '已授权获取设备信息/同意隐私协议'); 84 | }, 85 | ), 86 | ListTile( 87 | title: const Text('注册APP'), 88 | onTap: () async { 89 | await TencentKitPlatform.instance.registerApp( 90 | appId: _kTencentAppID, universalLink: _kUniversalLink); 91 | _showTips('注册APP', '注册成功'); 92 | }, 93 | ), 94 | ListTile( 95 | title: const Text('环境检查'), 96 | onTap: () async { 97 | final String content = 98 | 'QQ install: ${await TencentKitPlatform.instance.isQQInstalled()}\nTIM install: ${await TencentKitPlatform.instance.isTIMInstalled()}'; 99 | _showTips('环境检查', content); 100 | }, 101 | ), 102 | ListTile( 103 | title: const Text('登录'), 104 | onTap: () { 105 | TencentKitPlatform.instance.login( 106 | scope: [TencentScope.kGetSimpleUserInfo], 107 | ); 108 | }, 109 | ), 110 | ListTile( 111 | title: const Text('登录(Server-Side)'), 112 | onTap: () { 113 | TencentKitPlatform.instance.loginServerSide( 114 | scope: [TencentScope.kGetUserInfo], 115 | ); 116 | }, 117 | ), 118 | ListTile( 119 | title: const Text('获取用户信息'), 120 | onTap: () async { 121 | if ((_loginResp?.isSuccessful ?? false) && 122 | !(_loginResp!.isExpired ?? true)) { 123 | final TencentUserInfoResp userInfo = 124 | await TencentApi.getUserInfo( 125 | appId: _kTencentAppID, 126 | openid: _loginResp!.openid!, 127 | accessToken: _loginResp!.accessToken!, 128 | ); 129 | if (userInfo.isSuccessful) { 130 | _showTips('用户信息', 131 | '${userInfo.nickname} - ${userInfo.gender} - ${userInfo.genderType}'); 132 | } else { 133 | _showTips('用户信息', '${userInfo.ret} - ${userInfo.msg}'); 134 | } 135 | } 136 | }, 137 | ), 138 | ListTile( 139 | title: const Text('获取UnionID'), 140 | onTap: () async { 141 | if ((_loginResp?.isSuccessful ?? false) && 142 | !(_loginResp!.isExpired ?? true)) { 143 | final TencentUnionidResp unionid = await TencentApi.getUnionId( 144 | accessToken: _loginResp!.accessToken!, 145 | ); 146 | if (unionid.isSuccessful) { 147 | _showTips('UnionID', 148 | '${unionid.clientId} - ${unionid.openid} - ${unionid.unionid}'); 149 | } else { 150 | _showTips('UnionID', 151 | '${unionid.error} - ${unionid.errorDescription}'); 152 | } 153 | } 154 | }, 155 | ), 156 | ListTile( 157 | title: const Text('分享说说'), 158 | onTap: () { 159 | TencentKitPlatform.instance.shareMood( 160 | scene: TencentScene.kScene_QZone, 161 | summary: '分享测试', 162 | ); 163 | }, 164 | ), 165 | ListTile( 166 | title: const Text('文本分享'), 167 | onTap: () { 168 | TencentKitPlatform.instance.shareText( 169 | scene: TencentScene.kScene_QQ, 170 | summary: '分享测试', 171 | ); 172 | }, 173 | ), 174 | ListTile( 175 | title: const Text('图片分享'), 176 | onTap: () async { 177 | final File file = await DefaultCacheManager().getSingleFile( 178 | 'https://www.baidu.com/img/bd_logo1.png?where=super'); 179 | await TencentKitPlatform.instance.shareImage( 180 | scene: TencentScene.kScene_QQ, 181 | imageUri: Uri.file(file.path), 182 | ); 183 | }, 184 | ), 185 | ListTile( 186 | title: const Text('网页分享'), 187 | onTap: () { 188 | TencentKitPlatform.instance.shareWebpage( 189 | scene: TencentScene.kScene_QQ, 190 | title: 'title', 191 | targetUrl: 'https://www.baidu.com/', 192 | ); 193 | }, 194 | ), 195 | ], 196 | ), 197 | ); 198 | } 199 | 200 | void _showTips(String title, String content) { 201 | showDialog( 202 | context: context, 203 | builder: (BuildContext context) { 204 | return AlertDialog( 205 | title: Text(title), 206 | content: Text(content), 207 | ); 208 | }, 209 | ); 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /ios/tencent_setup.rb: -------------------------------------------------------------------------------- 1 | # 2 | # 参考文献 3 | # https://github.com/firebase/flutterfire/blob/master/packages/firebase_crashlytics/firebase_crashlytics/ios/crashlytics_add_upload_symbols 4 | # https://github.com/MagicalWater/Base-APP-Env/blob/master/fastlane/actions/xcode_parse.rb 5 | # 6 | 7 | require 'xcodeproj' 8 | require 'plist' 9 | require 'optparse' 10 | require 'uri' 11 | 12 | # Dictionary to hold command line arguments 13 | options_dict = {} 14 | 15 | # Parse command line arguments into options_dict 16 | OptionParser.new do |options| 17 | options.banner = "Setup the Tencent to an Xcode target." 18 | 19 | options.on("-p", "--projectDirectory=DIRECTORY", String, "Directory of the Xcode project") do |dir| 20 | options_dict[:project_dir] = dir 21 | end 22 | 23 | options.on("-n", "--projectName=NAME", String, "Name of the Xcode project (ex: Runner.xcodeproj)") do |name| 24 | options_dict[:project_name] = name 25 | end 26 | 27 | options.on("-a", "--appId=APPID", String, "App ID for Tencent") do |opts| 28 | options_dict[:app_id] = opts 29 | end 30 | 31 | options.on("-u", "--universalLink=UNIVERSALLINK", String, "Universal Link for Tencent") do |opts| 32 | options_dict[:universal_link] = opts 33 | end 34 | end.parse! 35 | 36 | # Minimum required arguments are a project directory and project name 37 | unless (options_dict[:project_dir] and options_dict[:project_name]) 38 | abort("Must provide a project directory and project name.\n") 39 | end 40 | 41 | # Path to the Xcode project to modify 42 | project_path = File.join(options_dict[:project_dir], options_dict[:project_name]) 43 | 44 | unless (File.exist?(project_path)) 45 | abort("Project at #{project_path} does not exist. Please check paths manually.\n"); 46 | end 47 | 48 | # Actually open and modify the project 49 | project = Xcodeproj::Project.open(project_path) 50 | project.targets.each do |target| 51 | if target.name == "Runner" 52 | app_id = options_dict[:app_id] 53 | universal_link = options_dict[:universal_link] 54 | 55 | sectionObject = {} 56 | project.objects.each do |object| 57 | if object.uuid == target.uuid 58 | sectionObject = object 59 | break 60 | end 61 | end 62 | sectionObject.build_configurations.each do |config| 63 | infoplist = config.build_settings["INFOPLIST_FILE"] 64 | if !infoplist 65 | abort("INFOPLIST_FILE is not exist\n") 66 | end 67 | infoplistFile = File.join(options_dict[:project_dir], infoplist) 68 | if !File.exist?(infoplistFile) 69 | abort("#{infoplist} is not exist\n") 70 | end 71 | result = Plist.parse_xml(infoplistFile, marshal: false) 72 | if !result 73 | result = {} 74 | end 75 | urlTypes = result["CFBundleURLTypes"] 76 | if !urlTypes 77 | urlTypes = [] 78 | result["CFBundleURLTypes"] = urlTypes 79 | end 80 | isUrlTypeExist = urlTypes.any? { |urlType| urlType["CFBundleURLSchemes"] && (urlType["CFBundleURLSchemes"].include? "tencent#{app_id}") } 81 | if !isUrlTypeExist 82 | urlTypes << { 83 | "CFBundleTypeRole": "Editor", 84 | "CFBundleURLName": "tencent", 85 | "CFBundleURLSchemes": [ "tencent#{app_id}" ] 86 | } 87 | File.write(infoplistFile, Plist::Emit.dump(result)) 88 | end 89 | queriesSchemes = result["LSApplicationQueriesSchemes"] 90 | if !queriesSchemes 91 | queriesSchemes = [] 92 | result["LSApplicationQueriesSchemes"] = queriesSchemes 93 | end 94 | tencentQueriesSchemes = [ 95 | "mqq", 96 | "mqqapi", 97 | "tim", 98 | "mqqopensdknopasteboard", 99 | "mqqopensdknopasteboardios16", 100 | "mqqopensdkapiV2", 101 | "mqqgamebindinggroup", 102 | "mqqopensdkavatar", 103 | "mqqopensdkfriend", 104 | "mqqopensdklaunchminiapp", 105 | "mqzone", 106 | "tencentapi.qq.reqContent", 107 | "tencentapi.qzone.reqContent", 108 | "mqqthirdappgroup", 109 | "mqqopensdkminiapp", 110 | ] 111 | if tencentQueriesSchemes.any? { |queriesScheme| !(queriesSchemes.include? queriesScheme) } 112 | tencentQueriesSchemes.each do |queriesScheme| 113 | if !(queriesSchemes.include? queriesScheme) 114 | queriesSchemes << queriesScheme 115 | end 116 | end 117 | File.write(infoplistFile, Plist::Emit.dump(result)) 118 | end 119 | security = result["NSAppTransportSecurity"] 120 | if !security 121 | security = {} 122 | result["NSAppTransportSecurity"] = security 123 | end 124 | if security["NSAllowsArbitraryLoads"] != true 125 | security["NSAllowsArbitraryLoads"] = true 126 | File.write(infoplistFile, Plist::Emit.dump(result)) 127 | end 128 | # if security["NSAllowsArbitraryLoadsInWebContent"] != true 129 | # security["NSAllowsArbitraryLoadsInWebContent"] = true 130 | # File.write(infoplistFile, Plist::Emit.dump(result)) 131 | # end 132 | end 133 | if universal_link 134 | applinks = "applinks:#{URI.parse(universal_link).host}" 135 | sectionObject.build_configurations.each do |config| 136 | codeSignEntitlements = config.build_settings["CODE_SIGN_ENTITLEMENTS"] 137 | if !codeSignEntitlements 138 | codeSignEntitlements = "Runner/Runner.entitlements" 139 | config.build_settings["CODE_SIGN_ENTITLEMENTS"] = codeSignEntitlements 140 | project.save() 141 | end 142 | codeSignEntitlementsFile = File.join(options_dict[:project_dir], codeSignEntitlements) 143 | if !File.exist?(codeSignEntitlementsFile) 144 | content = Plist::Emit.dump({}) 145 | File.write(codeSignEntitlementsFile, content) 146 | end 147 | runnerTargetMainGroup = project.main_group.find_subpath('Runner', false) 148 | isRefExist = runnerTargetMainGroup.files.any? { |file| file.path.include? File.basename(codeSignEntitlementsFile) } 149 | if !isRefExist 150 | runnerTargetMainGroup.new_reference(File.basename(codeSignEntitlementsFile)) 151 | project.save() 152 | end 153 | result = Plist.parse_xml(codeSignEntitlementsFile, marshal: false) 154 | if !result 155 | result = {} 156 | end 157 | domains = result["com.apple.developer.associated-domains"] 158 | if !domains 159 | domains = [] 160 | result["com.apple.developer.associated-domains"] = domains 161 | end 162 | isApplinksExist = domains.include? applinks 163 | if !isApplinksExist 164 | domains << applinks 165 | File.write(codeSignEntitlementsFile, Plist::Emit.dump(result)) 166 | end 167 | end 168 | end 169 | end 170 | end 171 | -------------------------------------------------------------------------------- /ios/Libraries/TencentOpenAPI.framework/Headers/SDKDef.h: -------------------------------------------------------------------------------- 1 | /// 2 | /// \file sdkdef.h 3 | /// \brief SDK中相关常量定义 4 | /// 5 | /// Created by Tencent on 12-12-25. 6 | /// Copyright (c) 2012年 Tencent. All rights reserved. 7 | /// 8 | 9 | #import 10 | #import 11 | 12 | /** 13 | * \brief 设置sdk的log等级 14 | */ 15 | typedef enum { 16 | TCOLogLevel_Disabled = -1, // 关闭所有log 17 | TCOLogLevel_Error = 0, 18 | TCOLogLevel_Warning, 19 | TCOLogLevel_Info, 20 | TCOLogLevel_Debug, 21 | } TCOLogLevel; 22 | 23 | /** 24 | * \breif 授权/分享 方式 25 | */ 26 | typedef enum TencentAuthShareType { 27 | AuthShareType_QQ, 28 | AuthShareType_TIM, 29 | }TencentAuthShareType; 30 | 31 | /** 32 | * \brief APIResponse.retCode可能的枚举常量 33 | */ 34 | typedef enum 35 | { 36 | URLREQUEST_SUCCEED = 0, /**< 网络请求成功发送至服务器,并且服务器返回数据格式正确 37 | * \note 这里包括所请求业务操作失败的情况,例如没有授权等原因导致 38 | */ 39 | 40 | URLREQUEST_FAILED = 1, /**< 网络异常,或服务器返回的数据格式不正确导致无法解析 */ 41 | } REPONSE_RESULT; 42 | 43 | /** 44 | * \brief 增量授权失败原因 45 | * 46 | * \note 增量授权失败不影响原token的有效性(原token已失效的情况除外) 47 | */ 48 | typedef enum 49 | { 50 | kUpdateFailUnknown = 1, ///< 未知原因 51 | kUpdateFailUserCancel, ///< 用户取消 52 | kUpdateFailNetwork, ///< 网络问题 53 | } UpdateFailType; 54 | 55 | /** 56 | * \brief 封装服务器返回的结果 57 | * 58 | * APIResponse用于封装所有请求的返回结果,包括错误码、错误信息、原始返回数据以及返回数据的json格式字典 59 | */ 60 | @interface APIResponse : NSObject { 61 | int _detailRetCode; 62 | int _retCode; 63 | int _seq; 64 | NSString *_errorMsg; 65 | NSDictionary *_jsonResponse; 66 | NSString *_message; 67 | id _userData; 68 | } 69 | 70 | /** 71 | * 新增的详细错误码\n 72 | * detailRetCode主要用于区分不同的错误情况,参见\ref OpenSDKError 73 | */ 74 | @property (nonatomic, assign) int detailRetCode; 75 | 76 | /** 77 | * 网络请求是否成功送达服务器,以及服务器返回的数据格式是否正确\n 78 | * retCode具体取值可参考\ref REPONSE_RESULT 79 | */ 80 | @property (nonatomic, assign) int retCode; 81 | 82 | /** 83 | * 网络请求对应的递增序列号,方便内部管理 84 | */ 85 | @property (nonatomic, assign) int seq; 86 | 87 | /** 88 | * 错误提示语 89 | */ 90 | @property (nonatomic, retain) NSString *errorMsg; 91 | 92 | /** 93 | * 服务器返回数据的json格式字典\n 94 | * 字典内具体参数的命名和含义请参考\ref api_spec 95 | */ 96 | @property (nonatomic, retain) NSDictionary *jsonResponse; 97 | 98 | /** 99 | * 服务器返回的原始数据字符串 100 | */ 101 | @property (nonatomic, retain) NSString *message; 102 | 103 | /** 104 | * 用户保留数据 105 | */ 106 | @property (nonatomic, retain) id userData; 107 | 108 | @end 109 | 110 | 111 | /** 112 | * 用户自定义的保留字段 113 | */ 114 | FOUNDATION_EXTERN NSString * const PARAM_USER_DATA; 115 | 116 | /** 117 | * \name 应用邀请参数字段定义 118 | */ 119 | ///@{ 120 | 121 | /** 应用邀请展示图片url的key */ 122 | FOUNDATION_EXTERN NSString * const PARAM_APP_ICON; 123 | 124 | /** 应用邀请描述文本的key */ 125 | FOUNDATION_EXTERN NSString * const PARAM_APP_DESC; 126 | 127 | /** 应用邀请好友列表的key */ 128 | FOUNDATION_EXTERN NSString * const PARAM_APP_INVITED_OPENIDS; 129 | 130 | ///@} 131 | 132 | /** 133 | * \name sendStory新分享参数字段定义 134 | */ 135 | ///@{ 136 | 137 | /** 预填入接受人列表的key */ 138 | FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_RECEIVER; 139 | 140 | /** 分享feeds标题的key */ 141 | FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_TITLE; 142 | 143 | /** 分享feeds评论内容的key */ 144 | FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_COMMENT; 145 | 146 | /** 分享feeds摘要的key */ 147 | FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_SUMMARY; 148 | 149 | /** 分享feeds展示图片url的key */ 150 | FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_IMAGE; 151 | 152 | /** 分享feeds跳转链接url的key */ 153 | FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_URL; 154 | 155 | /** 分享feeds点击操作默认行为的key */ 156 | FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_ACT; 157 | 158 | ///@} 159 | 160 | /** 161 | * \name 设置头像参数字段定义 162 | */ 163 | ///@{ 164 | 165 | /** 头像图片数据的key */ 166 | FOUNDATION_EXTERN NSString * const PARAM_SETUSERHEAD_PIC; 167 | 168 | /** 头像图片文件名的key */ 169 | FOUNDATION_EXTERN NSString * const PARAM_SETUSERHEAD_FILENAME; 170 | 171 | ///@} 172 | 173 | /** 174 | * \name 服务器返回数据的参数字段定义 175 | */ 176 | ///@{ 177 | 178 | /** 服务器返回码的key */ 179 | FOUNDATION_EXTERN NSString * const PARAM_RETCODE; 180 | 181 | /** 服务器返回错误信息的key */ 182 | FOUNDATION_EXTERN NSString * const PARAM_MESSAGE; 183 | 184 | /** 服务器返回额外数据的key */ 185 | FOUNDATION_EXTERN NSString * const PARAM_DATA; 186 | 187 | ///@} 188 | 189 | /** 190 | * \name 错误信息相关常量定义 191 | */ 192 | ///@{ 193 | 194 | /** 详细错误信息字典中额外信息的key */ 195 | FOUNDATION_EXTERN NSString * const TCOpenSDKErrorKeyExtraInfo; 196 | 197 | /** 详细错误信息字典中返回码的key */ 198 | FOUNDATION_EXTERN NSString * const TCOpenSDKErrorKeyRetCode; 199 | 200 | /** 详细错误信息字典中错误语句的key */ 201 | FOUNDATION_EXTERN NSString * const TCOpenSDKErrorKeyMsg; 202 | 203 | /** 不支持的接口 */ 204 | FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgUnsupportedAPI; 205 | 206 | /** 操作成功 */ 207 | FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgSuccess; 208 | 209 | /** 未知错误 */ 210 | FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgUnknown; 211 | 212 | /** 用户取消 */ 213 | FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgUserCancel; 214 | 215 | /** 请重新登录 */ 216 | FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgReLogin; 217 | 218 | /** 应用没有操作权限 */ 219 | FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgOperationDeny; 220 | 221 | /** 网络异常或没有网络 */ 222 | FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgNetwork; 223 | 224 | /** URL格式或协议错误 */ 225 | FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgURL; 226 | 227 | /** 解析数据出错 */ 228 | FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgDataParse; 229 | 230 | /** 传入参数有误 */ 231 | FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgParam; 232 | 233 | /** 连接超时 */ 234 | FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgTimeout; 235 | 236 | /** 安全问题 */ 237 | FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgSecurity; 238 | 239 | /** 文件读写错误 */ 240 | FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgIO; 241 | 242 | /** 服务器端错误 */ 243 | FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgServer; 244 | 245 | /** 页面错误 */ 246 | FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgWebPage; 247 | 248 | /** 设置头像图片过大 */ 249 | FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgUserHeadPicLarge; 250 | 251 | /** 用户未同意授权隐私协议 */ 252 | FOUNDATION_EXPORT NSString * const TCOpenSDKErrorMsgUserNotAgreedAuthorization; 253 | 254 | ///@} 255 | 256 | /** 257 | * \brief SDK新增详细错误常量 258 | */ 259 | typedef enum 260 | { 261 | kOpenSDKInvalid = -1, ///< 无效的错误码 262 | kOpenSDKErrorUnsupportedAPI = -2, ///< 不支持的接口 263 | 264 | /** 265 | * \name CommonErrorCode 266 | * 公共错误码 267 | */ 268 | ///@{ 269 | kOpenSDKErrorSuccess = 0, ///< 成功 270 | kOpenSDKErrorUnknown, ///< 未知错误 271 | kOpenSDKErrorUserCancel, ///< 用户取消 272 | kOpenSDKErrorReLogin, ///< token无效或用户未授权相应权限需要重新登录 273 | kOpenSDKErrorOperationDeny, ///< 第三方应用没有该api操作的权限 274 | ///@} 275 | 276 | /** 277 | * \name NetworkRelatedErrorCode 278 | * 网络相关错误码 279 | */ 280 | ///@{ 281 | kOpenSDKErrorNetwork, ///< 网络错误,网络不通或连接不到服务器 282 | kOpenSDKErrorURL, ///< URL格式或协议错误 283 | kOpenSDKErrorDataParse, ///< 数据解析错误,服务器返回的数据解析出错 284 | kOpenSDKErrorParam, ///< 传入参数错误 285 | kOpenSDKErrorConnTimeout, ///< http连接超时 286 | kOpenSDKErrorSecurity, ///< 安全问题 287 | kOpenSDKErrorIO, ///< 下载和文件IO错误 288 | kOpenSDKErrorServer, ///< 服务器端错误 289 | ///@} 290 | 291 | /** 292 | * \name WebViewRelatedError 293 | * webview特有错误 294 | */ 295 | ///@{ 296 | kOpenSDKErrorWebPage, ///< 页面错误 297 | ///@} 298 | 299 | /** 300 | * \name SetUserHeadRelatedErrorCode 301 | * 设置头像自定义错误码段 302 | */ 303 | ///@{ 304 | kOpenSDKErrorUserHeadPicLarge = 0x010000, ///< 图片过大 设置头像自定义错误码 305 | ///@} 306 | } OpenSDKError; 307 | 308 | /** 309 | * \name SDK版本(v1.3)支持的授权列表常量 310 | */ 311 | ///@{ 312 | 313 | /** 发表一条说说到QQ空间(需要申请权限) */ 314 | FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_ADD_TOPIC; 315 | 316 | /** 创建一个QQ空间相册(需要申请权限) */ 317 | FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_ADD_ALBUM; 318 | 319 | /** 上传一张照片到QQ空间相册(需要申请权限) */ 320 | FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_UPLOAD_PIC; 321 | 322 | /** 获取用户QQ空间相册列表(需要申请权限) */ 323 | FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_LIST_ALBUM; 324 | 325 | /** 验证是否认证空间粉丝 */ 326 | FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_CHECK_PAGE_FANS; 327 | 328 | /** 获取登录用户自己的详细信息 */ 329 | FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_INFO; 330 | 331 | /** 获取其他用户的详细信息 */ 332 | FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_OTHER_INFO; 333 | 334 | /** 获取会员用户基本信息 */ 335 | FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_VIP_INFO; 336 | 337 | /** 获取会员用户详细信息 */ 338 | FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_VIP_RICH_INFO; 339 | 340 | /** 获取用户信息 */ 341 | FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_USER_INFO; 342 | 343 | /** 移动端获取用户信息 */ 344 | FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_SIMPLE_USER_INFO; 345 | ///@} 346 | 347 | 348 | /** 349 | * \name CGI接口相关参数类型定义 350 | */ 351 | 352 | /** 必填的字符串类型参数 */ 353 | typedef NSString *TCRequiredStr; 354 | 355 | /** 必填的UIImage类型参数 */ 356 | typedef UIImage *TCRequiredImage; 357 | 358 | /** 必填的整型参数 */ 359 | typedef NSInteger TCRequiredInt; 360 | 361 | /** 必填的数字类型 */ 362 | typedef NSNumber *TCRequiredNumber; 363 | 364 | /** 必填的NSData参数 */ 365 | typedef NSData *TCRequiredData; 366 | 367 | /** 可选的字符串类型参数 */ 368 | typedef NSString *TCOptionalStr; 369 | 370 | /** 可选的UIImage类型参数 */ 371 | typedef UIImage *TCOptionalImage; 372 | 373 | /** 可选的整型参数 */ 374 | typedef NSInteger TCOptionalInt; 375 | 376 | /** 可选的数字类型 */ 377 | typedef NSNumber *TCOptionalNumber; 378 | 379 | /** 可选的不定类型参数 */ 380 | typedef id TCRequiredId; 381 | ///@} 382 | 383 | 384 | /** 385 | * \brief CGI请求的参数字典封装辅助基类 386 | * 387 | * 将相应属性的值以key-value的形式保存到参数字典中 388 | */ 389 | @interface TCAPIRequest : NSMutableDictionary 390 | 391 | /** CGI请求的URL地址 */ 392 | @property (nonatomic, readonly) NSURL *apiURL; 393 | 394 | /** CGI请求方式:"GET","POST" */ 395 | @property (nonatomic, readonly) NSString *method; 396 | 397 | /** 398 | * API参数中的保留字段,可以塞入任意字典支持的类型,再调用完成后会带回给调用方 399 | */ 400 | @property (nonatomic, retain) TCRequiredId paramUserData; 401 | 402 | /** 403 | * APIResponse,API的返回结果 404 | */ 405 | @property (nonatomic, readonly) APIResponse *response; 406 | 407 | /** 取消相应的CGI请求任务 */ 408 | - (void)cancel; 409 | 410 | @end 411 | 412 | @protocol TCAPIRequestDelegate 413 | @optional 414 | - (void)cgiRequest:(TCAPIRequest *)request didResponse:(APIResponse *)response; 415 | 416 | @end 417 | 418 | -------------------------------------------------------------------------------- /ios/Libraries/TencentOpenAPI.framework/Headers/TencentOAuth.h: -------------------------------------------------------------------------------- 1 | /// 2 | /// \file TencentOAuth.h 3 | /// \brief QQ互联开放平台授权登录及相关开放接口实现类 4 | /// 5 | /// Created by Tencent on 12-12-21. 6 | /// Copyright (c) 2012年 Tencent. All rights reserved. 7 | /// 8 | 9 | #import 10 | #import "SDKDef.h" 11 | 12 | @protocol TencentSessionDelegate; 13 | @protocol TencentLoginDelegate; 14 | @protocol TencentApiInterfaceDelegate; 15 | @protocol TencentWebViewDelegate; 16 | 17 | @class TencentApiReq; 18 | @class TencentApiResp; 19 | 20 | typedef NS_ENUM(NSUInteger, TencentAuthorizeState) { 21 | kTencentNotAuthorizeState, 22 | kTencentSSOAuthorizeState, 23 | kTencentWebviewAuthorzieState, 24 | }; 25 | 26 | typedef NS_ENUM(NSUInteger, TencentAuthMode) { 27 | kAuthModeClientSideToken, 28 | kAuthModeServerSideCode, 29 | }; 30 | 31 | #pragma mark - TencentOAuth(授权登录及相关开放接口调用) 32 | 33 | /** 34 | * \brief TencentOpenAPI授权登录及相关开放接口调用 35 | * 36 | * TencentOAuth实现授权登录逻辑以及相关开放接口的请求调用 37 | */ 38 | @interface TencentOAuth : NSObject 39 | { 40 | NSMutableDictionary *_apiRequests; 41 | NSString *_accessToken; 42 | NSDate *_expirationDate; 43 | id _sessionDelegate; 44 | NSString *_localAppId; 45 | NSString *_openId; 46 | NSString *_redirectURI; 47 | NSArray *_permissions; 48 | } 49 | 50 | /** Access Token凭证,用于后续访问各开放接口 */ 51 | @property(nonatomic, copy) NSString *accessToken; 52 | 53 | /** Access Token的失效期 */ 54 | @property(nonatomic, copy) NSDate *expirationDate; 55 | 56 | /** 已实现的开放接口的回调委托对象 */ 57 | @property(nonatomic, weak) id sessionDelegate; 58 | 59 | /** 第三方应用在开发过程中设置的URLSchema,用于浏览器登录后后跳到第三方应用 */ 60 | @property(nonatomic, copy) NSString *localAppId; 61 | 62 | /** 用户授权登录后对该用户的唯一标识 */ 63 | @property(nonatomic, copy) NSString *openId; 64 | 65 | /** 用户登录成功过后的跳转页面地址 */ 66 | @property(nonatomic, copy) NSString *redirectURI; 67 | 68 | /** 第三方应用在互联开放平台申请的appID */ 69 | @property(nonatomic, retain) NSString *appId; 70 | 71 | /** 第三方应用在互联开放平台注册的UniversalLink */ 72 | @property(nonatomic, retain) NSString *universalLink; 73 | 74 | /** 主要是互娱的游戏设置uin */ 75 | @property(nonatomic, retain) NSString *uin; 76 | 77 | /** 主要是互娱的游戏设置鉴定票据 */ 78 | @property(nonatomic, retain) NSString *skey; 79 | 80 | /** 登陆透传的数据 */ 81 | @property(nonatomic, copy) NSDictionary *passData; 82 | 83 | /** 授权方式(Client Side Token或者Server Side Code) */ 84 | @property(nonatomic, assign) TencentAuthMode authMode; 85 | 86 | /** union id */ 87 | @property(nonatomic, retain) NSString *unionid; 88 | 89 | /** 第三方在授权登录/分享 时选择 QQ,还是TIM 。在授权前一定要指定其中一个类型*/ 90 | @property(nonatomic, assign) TencentAuthShareType authShareType; 91 | 92 | /** SDK打开web登录页,支持自动填充账号 */ 93 | @property (nonatomic, copy) NSString *defaultUin; 94 | 95 | /** 96 | * 获取上次登录得到的token 97 | * 98 | **/ 99 | - (NSString *)getCachedToken; 100 | 101 | /** 102 | * 获取上次登录得到的openid 103 | * 104 | **/ 105 | - (NSString *)getCachedOpenID; 106 | 107 | /** 108 | * 获取上次登录的token过期日期 109 | * 110 | **/ 111 | - (NSDate *)getCachedExpirationDate; 112 | 113 | /** 114 | * 上次登录的token是否过期(本地判断) 115 | **/ 116 | - (BOOL)isCachedTokenValid; 117 | 118 | /** 119 | * 删除上次登录登录的token信息 120 | * 121 | **/ 122 | - (BOOL)deleteCachedToken; 123 | 124 | /** 125 | * 用来获得当前sdk的版本号 126 | * \return 返回sdk版本号 127 | **/ 128 | 129 | + (NSString *)sdkVersion; 130 | 131 | /** 132 | * 用来获得当前sdk的小版本号 133 | * \return 返回sdk小版本号 134 | **/ 135 | 136 | + (NSString *)sdkSubVersion; 137 | 138 | /** 139 | * 用来获得当前sdk的是否精简版 140 | * \return 返回YES表示精简版 141 | **/ 142 | 143 | + (BOOL)isLiteSDK; 144 | 145 | /** 146 | * 主要是用来帮助判断是否有登陆被发起,但是还没有过返回结果 147 | * \return 148 | * kTencentNotAuthorizeState:无授权 149 | * kTencentSSOAuthorizeState:有人发起了sso授权但无返回 150 | * kTencentWebviewAuthorzieState:有人发起了webview授权还未返回 151 | **/ 152 | 153 | + (TencentAuthorizeState *)authorizeState; 154 | 155 | /** 156 | * 初始化TencentOAuth对象 157 | * \param appId 不可为nil,第三方应用在互联开放平台申请的唯一标识 158 | * \param delegate 不可为nil,第三方应用用于接收请求返回结果的委托对象 159 | * \return 初始化后的授权登录对象 160 | */ 161 | - (id)initWithAppId:(NSString *)appId 162 | andDelegate:(id)delegate; 163 | 164 | /** 165 | * 初始化TencentOAuth对象(>=3.3.7) 166 | * \param appId 不可为nil,第三方应用在互联开放平台申请的唯一标识 167 | * \param universalLink 可以为nil,第三方应用在互联开放平台注册的UniversalLink,和bundleID一一对应(当为nil时,互联平台会按规则生成universallink,详见官网说明) 168 | * \param delegate 不可为nil,第三方应用用于接收请求返回结果的委托对象 169 | * \return 初始化后的授权登录对象 170 | * 171 | ****【使用说明】***** 172 | * 1、支持BundleId与UniversalLink的一一对应,主要目的“是为了解决应用的iPhone版本和iPad HD版本共用同一个AppId,导致同时安装情况下的跳转问题"。 173 | * 2 、由于手Q版本在 >=8.1.8 后才支持了这种对应方式,所以一旦使用,“务必做到”及时知会用户升级手Q版本。 174 | **** 175 | */ 176 | - (id)initWithAppId:(NSString *)appId 177 | andUniversalLink:(NSString *)universalLink 178 | andDelegate:(id)delegate; 179 | 180 | /** 181 | * 初始化TencentOAuth对象(>=3.3.8) 182 | * \param appId 不可为nil,第三方应用在互联开放平台申请的唯一标识 183 | * \param enabled 默认为NO,第三方应用是否将sdk和手机QQ的交互方式切换为UniversalLink方式,启用后则在iOS9及以上的系统都会生效UniversalLink方式;否则,默认仅在iOS13及以上的系统生效UniversalLink方式。 184 | * \param universalLink 可以为nil,第三方应用在互联开放平台注册的UniversalLink,和bundleID一一对应(当为nil时,互联平台会按规则生成UniversalLink,详见官网说明) 185 | * \param delegate 不可为nil,第三方应用用于接收请求返回结果的委托对象 186 | * \return 初始化后的授权登录对象 187 | * 188 | *****【使用说明】***** 189 | * 1、支持sdk与手Q的交互切换为UniversalLink模式,主要目的"是为了避免手Q的UrlScheme被其他应用抢注后,导致sdk接口功能受到影响"。 190 | * 2 、由于手Q版本在 >=8.1.3 后才适配了UniversalLink,所以一旦开启了enabled开关,“务必做到”及时知会用户升级手Q版本。 191 | ***** 192 | */ 193 | - (id)initWithAppId:(NSString *)appId 194 | enableUniveralLink:(BOOL)enabled 195 | universalLink:(NSString *)universalLink 196 | delegate:(id)delegate; 197 | 198 | /** 199 | * 设置用户是否已经授权同意授权隐私协议,在主体应用中,用户同意授权隐私协议后再初始化互联SDK,默认未同意授权 200 | * 注意:如未同意授权隐私协议,则互联SDK的所有功能都无法使用,包括初始化!!! 201 | * 从3.5.7版本开始支持该方法 202 | * 203 | * @param isAgreedAuthorization 是否已经授权,isAgreedAuthorization=YES, 表示已经同意授权;isAgreedAuthorization=NO,表示未同意授权,互联SDK的所有功能都无法使用 204 | */ 205 | + (void)setIsUserAgreedAuthorization:(BOOL)isUserAgreedAuthorization; 206 | 207 | /** 208 | * 获取当前用户是否已经同意授权隐私协议 209 | * 从3.5.7版本开始支持该方法 210 | */ 211 | + (BOOL)isUserAgreedAuthorization; 212 | 213 | /** 214 | * 判断用户手机上是否安装手机QQ 215 | * \return YES:安装 NO:没安装 216 | * 217 | * \note SDK目前已经支持QQ、TIM授权登录及分享功能, 会按照QQ>TIM的顺序进行调用。 218 | * 只要用户安装了QQ、TIM中任意一个应用,都可为第三方应用进行授权登录、分享功能。 219 | * 第三方应用在接入SDK时不需要判断是否安装QQ、TIM。若有判断安装QQ、TIM的逻辑建议移除。 220 | */ 221 | + (BOOL)iphoneQQInstalled; 222 | 223 | /** 224 | * 判断用户手机上是否安装手机TIM 225 | * \return YES:安装 NO:没安装 226 | * 227 | * \note SDK目前已经支持QQ、TIM授权登录及分享功能, 会按照QQ>TIM的顺序进行调用。 228 | * 只要用户安装了QQ、TIM中任意一个应用,都可为第三方应用进行授权登录、分享功能。 229 | * 第三方应用在接入SDK时不需要判断是否安装QQ、TIM。若有判断安装QQ、TIM的逻辑建议移除。 230 | */ 231 | + (BOOL)iphoneTIMInstalled; 232 | 233 | /** 234 | * 登录授权 235 | * 236 | * \param permissions 授权信息列 237 | */ 238 | - (BOOL)authorize:(NSArray *)permissions; 239 | 240 | /** 241 | * 登录授权 242 | * \param permissions 授权信息列表 243 | * \param localAppId 应用APPID 244 | */ 245 | - (BOOL)authorize:(NSArray *)permissions 246 | localAppId:(NSString *)localAppId; 247 | 248 | /** 249 | * 登录授权 250 | * 251 | * \param permissions 授权信息列 252 | */ 253 | - (BOOL)authorizeWithQRlogin:(NSArray *)permissions; 254 | 255 | /** 256 | * 增量授权,因用户没有授予相应接口调用的权限,需要用户确认是否授权 257 | * \param permissions 需增量授权的信息列表 258 | * \return 增量授权调用是否成功 259 | */ 260 | - (BOOL)incrAuthWithPermissions:(NSArray *)permissions; 261 | 262 | /** 263 | * 重新授权,因token废除或失效导致接口调用失败,需用户重新授权 264 | * \param permissions 授权信息列表,同登录授权 265 | * \return 授权调用是否成功 266 | */ 267 | - (BOOL)reauthorizeWithPermissions:(NSArray *)permissions; 268 | 269 | /** 270 | * 获取UnindID,可以根据UnindID的比较来确定OpenID是否属于同一个用户 271 | * \return NO未登录,信息不足;YES条件满足,发送请求成功,请等待回调 272 | */ 273 | - (BOOL)RequestUnionId; 274 | 275 | /** 276 | * (静态方法)处理应用拉起协议 277 | * \param url 处理被其他应用呼起时的逻辑 278 | * \return 处理结果,YES表示成功,NO表示失败 279 | */ 280 | + (BOOL)HandleOpenURL:(NSURL *)url; 281 | 282 | /** 283 | * (静态方法)sdk是否可以处理应用拉起协议 284 | * \param url 处理被其他应用呼起时的逻辑 285 | * \return 处理结果,YES表示可以 NO表示不行 286 | */ 287 | + (BOOL)CanHandleOpenURL:(NSURL *)url; 288 | 289 | /** 290 | * (静态方法)处理应用的UniversalLink拉起协议 291 | * \param url 处理被其他应用呼起时的逻辑 292 | * \return 处理结果,YES表示成功,NO表示失败 293 | */ 294 | + (BOOL)HandleUniversalLink:(NSURL *)url; 295 | 296 | /** 297 | * (静态方法)sdk是否可以处理应用的Universallink拉起协议 298 | * \param url 处理被其他应用呼起时的逻辑(应用的Universallink链接须满足官网注册时的格式要求) 299 | * \return 处理结果,YES表示可以 NO表示不行 300 | * 注:在调用其他Universallink相关处理接口之前,均需进行此项判断 301 | */ 302 | + (BOOL)CanHandleUniversalLink:(NSURL *)url; 303 | 304 | /** 305 | * (静态方法)获取TencentOAuth调用的上一次错误信息 306 | */ 307 | + (NSString *)getLastErrorMsg; 308 | 309 | /** 310 | * 以Server Side Code模式授权登录时,通过此接口获取返回的code值; 311 | * 以Client Side Token模式授权登录时,忽略此接口。 312 | */ 313 | - (NSString *)getServerSideCode; 314 | 315 | /** 316 | * 退出登录(退出登录后,TecentOAuth失效,需要重新初始化) 317 | * \param delegate 第三方应用用于接收请求返回结果的委托对象 318 | */ 319 | - (void)logout:(id)delegate; 320 | 321 | /** 322 | * 判断登录态是否有效 323 | * \return 处理结果,YES表示有效,NO表示无效,请用户重新登录授权 324 | */ 325 | - (BOOL)isSessionValid; 326 | 327 | /** 328 | * 获取用户个人信息 329 | * \return 处理结果,YES表示API调用成功,NO表示API调用失败,登录态失败,重新登录 330 | */ 331 | - (BOOL)getUserInfo; 332 | 333 | /** 334 | * 退出指定API调用 335 | * \param userData 用户调用某条API的时候传入的保留参数 336 | * \return 处理结果,YES表示成功 NO表示失败 337 | */ 338 | - (BOOL)cancel:(id)userData; 339 | 340 | /** 341 | * CGI类任务创建接口 342 | * \param apiURL CGI请求的URL地址 343 | * \param method CGI请求方式:"GET","POST" 344 | * \param params CGI请求参数字典 345 | * \param callback CGI请求结果的回调接口对象 346 | * \return CGI请求任务实例,用于取消任务,返回nil代表任务创建失败 347 | */ 348 | - (TCAPIRequest *)cgiRequestWithURL:(NSURL *)apiURL method:(NSString *)method params:(NSDictionary *)params callback:(id)callback; 349 | 350 | /** 351 | * TencentOpenApi发送任务统一接口 352 | * \param request 请求发送的任务 353 | * \param callback 任务发送后的回调地址 354 | */ 355 | - (BOOL)sendAPIRequest:(TCAPIRequest *)request callback:(id)callback; 356 | 357 | - (NSString *)getUserOpenID; 358 | 359 | @end 360 | 361 | #pragma mark - TencentLoginDelegate(授权登录回调协议) 362 | 363 | /** 364 | * \brief TencentLoginDelegate iOS Open SDK 1.3 API回调协议 365 | * 366 | * 第三方应用实现登录的回调协议 367 | */ 368 | @protocol TencentLoginDelegate 369 | 370 | @required 371 | 372 | /** 373 | * 登录成功后的回调 374 | */ 375 | - (void)tencentDidLogin; 376 | 377 | /** 378 | * 登录失败后的回调 379 | * \param cancelled 代表用户是否主动退出登录 380 | */ 381 | - (void)tencentDidNotLogin:(BOOL)cancelled; 382 | 383 | /** 384 | * 登录时网络有问题的回调 385 | */ 386 | - (void)tencentDidNotNetWork; 387 | 388 | @optional 389 | /** 390 | * 登录时权限信息的获得 391 | */ 392 | - (NSArray *)getAuthorizedPermissions:(NSArray *)permissions withExtraParams:(NSDictionary *)extraParams __attribute__((deprecated("该接口已过期, 建议删除调用"))); 393 | 394 | /** 395 | * unionID获得 396 | */ 397 | - (void)didGetUnionID; 398 | 399 | /** 400 | * 强制网页登录,包括账号密码登录和二维码登录 401 | * return YES时,就算本地有手Q也会打开web界面 402 | */ 403 | - (BOOL)forceWebLogin; 404 | @end 405 | 406 | #pragma mark - TencentSessionDelegate(开放接口回调协议) 407 | 408 | /** 409 | * \brief TencentSessionDelegate iOS Open SDK 1.3 API回调协议 410 | * 411 | * 第三方应用需要实现每条需要调用的API的回调协议 412 | */ 413 | @protocol TencentSessionDelegate 415 | 416 | @optional 417 | 418 | /** 419 | * 退出登录的回调 420 | */ 421 | - (void)tencentDidLogout; 422 | 423 | /** 424 | * 因用户未授予相应权限而需要执行增量授权。在用户调用某个api接口时,如果服务器返回操作未被授权,则触发该回调协议接口,由第三方决定是否跳转到增量授权页面,让用户重新授权。 425 | * \param tencentOAuth 登录授权对象。 426 | * \param permissions 需增量授权的权限列表。 427 | * \return 是否仍然回调返回原始的api请求结果。 428 | * \note 不实现该协议接口则默认为不开启增量授权流程。若需要增量授权请调用\ref TencentOAuth#incrAuthWithPermissions: \n注意:增量授权时用户可能会修改登录的帐号 429 | */ 430 | - (BOOL)tencentNeedPerformIncrAuth:(TencentOAuth *)tencentOAuth withPermissions:(NSArray *)permissions; 431 | 432 | /** 433 | * [该逻辑未实现]因token失效而需要执行重新登录授权。在用户调用某个api接口时,如果服务器返回token失效,则触发该回调协议接口,由第三方决定是否跳转到登录授权页面,让用户重新授权。 434 | * \param tencentOAuth 登录授权对象。 435 | * \return 是否仍然回调返回原始的api请求结果。 436 | * \note 不实现该协议接口则默认为不开启重新登录授权流程。若需要重新登录授权请调用\ref TencentOAuth#reauthorizeWithPermissions: \n注意:重新登录授权时用户可能会修改登录的帐号 437 | */ 438 | - (BOOL)tencentNeedPerformReAuth:(TencentOAuth *)tencentOAuth; 439 | 440 | /** 441 | * 用户通过增量授权流程重新授权登录,token及有效期限等信息已被更新。 442 | * \param tencentOAuth token及有效期限等信息更新后的授权实例对象 443 | * \note 第三方应用需更新已保存的token及有效期限等信息。 444 | */ 445 | - (void)tencentDidUpdate:(TencentOAuth *)tencentOAuth; 446 | 447 | /** 448 | * 用户增量授权过程中因取消或网络问题导致授权失败 449 | * \param reason 授权失败原因,具体失败原因参见sdkdef.h文件中\ref UpdateFailType 450 | */ 451 | - (void)tencentFailedUpdate:(UpdateFailType)reason; 452 | 453 | /** 454 | * 获取用户个人信息回调 455 | * \param response API返回结果,具体定义参见sdkdef.h文件中\ref APIResponse 456 | * \remarks 正确返回示例: \snippet example/getUserInfoResponse.exp success 457 | * 错误返回示例: \snippet example/getUserInfoResponse.exp fail 458 | */ 459 | - (void)getUserInfoResponse:(APIResponse*) response; 460 | 461 | /** 462 | * 社交API统一回调接口 463 | * \param response API返回结果,具体定义参见sdkdef.h文件中\ref APIResponse 464 | * \param message 响应的消息,目前支持‘SendStory’,‘AppInvitation’,‘AppChallenge’,‘AppGiftRequest’ 465 | */ 466 | - (void)responseDidReceived:(APIResponse*)response forMessage:(NSString *)message; 467 | 468 | /** 469 | * post请求的上传进度 470 | * \param tencentOAuth 返回回调的tencentOAuth对象 471 | * \param bytesWritten 本次回调上传的数据字节数 472 | * \param totalBytesWritten 总共已经上传的字节数 473 | * \param totalBytesExpectedToWrite 总共需要上传的字节数 474 | * \param userData 用户自定义数据 475 | */ 476 | - (void)tencentOAuth:(TencentOAuth *)tencentOAuth didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite userData:(id)userData; 477 | 478 | 479 | /** 480 | * 通知第三方界面需要被关闭 481 | * \param tencentOAuth 返回回调的tencentOAuth对象 482 | * \param viewController 需要关闭的viewController 483 | */ 484 | - (void)tencentOAuth:(TencentOAuth *)tencentOAuth doCloseViewController:(UIViewController *)viewController; 485 | 486 | @end 487 | 488 | #pragma mark - TencentWebViewDelegate(H5登录webview旋转方向回调) 489 | 490 | /** 491 | * \brief TencentWebViewDelegate: H5登录webview旋转方向回调协议 492 | * 493 | * 第三方应用可以根据自己APP的旋转方向限制,通过此协议设置 494 | */ 495 | @protocol TencentWebViewDelegate 496 | @optional 497 | - (BOOL) tencentWebViewShouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation; 498 | - (NSUInteger) tencentWebViewSupportedInterfaceOrientationsWithWebkit; 499 | - (BOOL) tencentWebViewShouldAutorotateWithWebkit; 500 | @end 501 | -------------------------------------------------------------------------------- /ios/Classes/TencentKitPlugin.m: -------------------------------------------------------------------------------- 1 | #import "TencentKitPlugin.h" 2 | #import 3 | #import 4 | 5 | enum TencentScene { 6 | SCENE_QQ = 0, 7 | SCENE_QZONE = 1, 8 | }; 9 | 10 | enum TencentRetCode { 11 | // 网络请求成功发送至服务器,并且服务器返回数据格式正确 12 | // 这里包括所请求业务操作失败的情况,例如没有授权等原因导致 13 | RET_SUCCESS = 0, 14 | // 网络异常,或服务器返回的数据格式不正确导致无法解析 15 | RET_FAILED = 1, 16 | RET_COMMON = -1, 17 | RET_USERCANCEL = -2, 18 | }; 19 | 20 | @interface TencentKitPlugin () 21 | 22 | @end 23 | 24 | @implementation TencentKitPlugin { 25 | FlutterMethodChannel *_channel; 26 | TencentOAuth *_oauth; 27 | BOOL _forceWebLogin; 28 | } 29 | 30 | + (void)registerWithRegistrar:(NSObject *)registrar { 31 | FlutterMethodChannel *channel = 32 | [FlutterMethodChannel methodChannelWithName:@"v7lin.github.io/tencent_kit" 33 | binaryMessenger:[registrar messenger]]; 34 | TencentKitPlugin *instance = 35 | [[TencentKitPlugin alloc] initWithChannel:channel]; 36 | [registrar addApplicationDelegate:instance]; 37 | [registrar addMethodCallDelegate:instance channel:channel]; 38 | } 39 | 40 | - (instancetype)initWithChannel:(FlutterMethodChannel *)channel { 41 | self = [super init]; 42 | if (self) { 43 | _channel = channel; 44 | } 45 | _forceWebLogin = NO; 46 | return self; 47 | } 48 | 49 | - (void)handleMethodCall:(FlutterMethodCall *)call 50 | result:(FlutterResult)result { 51 | if ([@"setIsPermissionGranted" isEqualToString:call.method]) { 52 | NSNumber *granted = call.arguments[@"granted"]; 53 | [TencentOAuth setIsUserAgreedAuthorization:[granted boolValue]]; 54 | result(nil); 55 | } else if ([@"registerApp" isEqualToString:call.method]) { 56 | NSString *appId = call.arguments[@"appId"]; 57 | NSString *universalLink = call.arguments[@"universalLink"]; 58 | if (universalLink != nil) { 59 | _oauth = [[TencentOAuth alloc] initWithAppId:appId 60 | andUniversalLink:universalLink 61 | andDelegate:self]; 62 | } else { 63 | _oauth = [[TencentOAuth alloc] initWithAppId:appId andDelegate:self]; 64 | } 65 | result(nil); 66 | } else if ([@"isQQInstalled" isEqualToString:call.method]) { 67 | result([NSNumber numberWithBool:[TencentOAuth iphoneQQInstalled]]); 68 | } else if ([@"isTIMInstalled" isEqualToString:call.method]) { 69 | result([NSNumber numberWithBool:[TencentOAuth iphoneTIMInstalled]]); 70 | } else if ([@"login" isEqualToString:call.method]) { 71 | [self login:call result:result]; 72 | } else if ([@"loginServerSide" isEqualToString:call.method]) { 73 | [self loginServerSide:call result:result]; 74 | } else if ([@"logout" isEqualToString:call.method]) { 75 | [self logout:call result:result]; 76 | } else if ([@"shareMood" isEqualToString:call.method]) { 77 | [self shareMood:call result:result]; 78 | } else if ([@"shareText" isEqualToString:call.method]) { 79 | [self shareText:call result:result]; 80 | } else if ([@"shareImage" isEqualToString:call.method]) { 81 | [self shareImage:call result:result]; 82 | } else if ([@"shareMusic" isEqualToString:call.method]) { 83 | [self shareMusic:call result:result]; 84 | } else if ([@"shareWebpage" isEqualToString:call.method]) { 85 | [self shareWebpage:call result:result]; 86 | } else { 87 | result(FlutterMethodNotImplemented); 88 | } 89 | } 90 | 91 | - (void)login:(FlutterMethodCall *)call result:(FlutterResult)result { 92 | if (_oauth != nil) { 93 | NSString *scope = call.arguments[@"scope"]; 94 | NSArray *permissions = [scope componentsSeparatedByString:@","]; 95 | NSNumber *qrcode = call.arguments[@"qrcode"]; 96 | _oauth.authMode = kAuthModeClientSideToken; 97 | if ([qrcode boolValue]) { 98 | _forceWebLogin = YES; 99 | [_oauth authorizeWithQRlogin:permissions]; 100 | } else { 101 | _forceWebLogin = NO; 102 | [_oauth authorize:permissions]; 103 | } 104 | } 105 | result(nil); 106 | } 107 | 108 | - (void)loginServerSide:(FlutterMethodCall *)call result:(FlutterResult)result { 109 | if (_oauth != nil) { 110 | NSString *scope = call.arguments[@"scope"]; 111 | NSArray *permissions = [scope componentsSeparatedByString:@","]; 112 | NSNumber *qrcode = call.arguments[@"qrcode"]; 113 | _oauth.authMode = kAuthModeServerSideCode; 114 | if ([qrcode boolValue]) { 115 | _forceWebLogin = YES; 116 | [_oauth authorizeWithQRlogin:permissions]; 117 | } else { 118 | _forceWebLogin = NO; 119 | [_oauth authorize:permissions]; 120 | } 121 | } 122 | result(nil); 123 | } 124 | 125 | - (void)logout:(FlutterMethodCall *)call result:(FlutterResult)result { 126 | if (_oauth != nil) { 127 | [_oauth logout:self]; 128 | } 129 | result(nil); 130 | } 131 | 132 | - (void)shareMood:(FlutterMethodCall *)call result:(FlutterResult)result { 133 | NSNumber *scene = call.arguments[@"scene"]; 134 | if (scene.intValue == SCENE_QZONE) { 135 | NSString *summary = call.arguments[@"summary"]; 136 | NSArray *imageUris = call.arguments[@"imageUris"]; 137 | NSString *videoUri = call.arguments[@"videoUri"]; 138 | 139 | if (videoUri == nil || videoUri.length == 0) { 140 | NSMutableArray *imageDatas = [NSMutableArray array]; 141 | if (imageUris != nil && imageUris.count > 0) { 142 | for (NSString *imageUri in imageUris) { 143 | NSURL *imageUrl = [NSURL URLWithString:imageUri]; 144 | NSData *imageData = [NSData dataWithContentsOfFile:imageUrl.path]; 145 | [imageDatas addObject:imageData]; 146 | } 147 | } 148 | QQApiImageArrayForQZoneObject *object = 149 | [QQApiImageArrayForQZoneObject objectWithimageDataArray:imageDatas 150 | title:summary 151 | extMap:nil]; 152 | SendMessageToQQReq *req = [SendMessageToQQReq reqWithContent:object]; 153 | [QQApiInterface sendReq:req]; 154 | } else { 155 | QQApiVideoForQZoneObject *object = 156 | [QQApiVideoForQZoneObject objectWithAssetURL:videoUri 157 | title:summary 158 | extMap:nil]; 159 | SendMessageToQQReq *req = [SendMessageToQQReq reqWithContent:object]; 160 | [QQApiInterface sendReq:req]; 161 | } 162 | } 163 | result(nil); 164 | } 165 | 166 | - (void)shareText:(FlutterMethodCall *)call result:(FlutterResult)result { 167 | NSNumber *scene = call.arguments[@"scene"]; 168 | if (scene.intValue == SCENE_QQ) { 169 | NSString *summary = call.arguments[@"summary"]; 170 | QQApiTextObject *object = [QQApiTextObject objectWithText:summary]; 171 | SendMessageToQQReq *req = [SendMessageToQQReq reqWithContent:object]; 172 | [QQApiInterface sendReq:req]; 173 | } 174 | result(nil); 175 | } 176 | 177 | - (void)shareImage:(FlutterMethodCall *)call result:(FlutterResult)result { 178 | NSNumber *scene = call.arguments[@"scene"]; 179 | if (scene.intValue == SCENE_QQ) { 180 | NSString *imageUri = call.arguments[@"imageUri"]; 181 | // NSString *appName = call.arguments[@"appName"]; 182 | // NSNumber *extInt = call.arguments[@"extInt"]; 183 | 184 | NSURL *imageUrl = [NSURL URLWithString:imageUri]; 185 | NSData *imageData = [NSData dataWithContentsOfFile:imageUrl.path]; 186 | QQApiImageObject *object = [QQApiImageObject objectWithData:imageData 187 | previewImageData:nil 188 | title:nil 189 | description:nil]; 190 | SendMessageToQQReq *req = [SendMessageToQQReq reqWithContent:object]; 191 | [QQApiInterface sendReq:req]; 192 | } 193 | result(nil); 194 | } 195 | 196 | - (void)shareMusic:(FlutterMethodCall *)call result:(FlutterResult)result { 197 | NSNumber *scene = call.arguments[@"scene"]; 198 | NSString *title = call.arguments[@"title"]; 199 | NSString *summary = call.arguments[@"summary"]; 200 | NSString *imageUri = call.arguments[@"imageUri"]; 201 | NSString *musicUrl = call.arguments[@"musicUrl"]; 202 | NSString *targetUrl = call.arguments[@"targetUrl"]; 203 | // NSString *appName = call.arguments[@"appName"]; 204 | // NSNumber *extInt = call.arguments[@"extInt"]; 205 | if (scene.intValue == SCENE_QQ) { 206 | QQApiAudioObject *object = nil; 207 | NSURL *imageUrl = [NSURL URLWithString:imageUri]; 208 | if ([@"file" isEqualToString:imageUrl.scheme]) { 209 | NSData *imageData = [NSData dataWithContentsOfFile:imageUrl.path]; 210 | object = [QQApiAudioObject objectWithURL:[NSURL URLWithString:targetUrl] 211 | title:title 212 | description:summary 213 | previewImageData:imageData]; 214 | } else { 215 | object = [QQApiAudioObject objectWithURL:[NSURL URLWithString:targetUrl] 216 | title:title 217 | description:summary 218 | previewImageURL:imageUrl]; 219 | } 220 | object.flashURL = [NSURL URLWithString:musicUrl]; 221 | SendMessageToQQReq *req = [SendMessageToQQReq reqWithContent:object]; 222 | [QQApiInterface sendReq:req]; 223 | } 224 | result(nil); 225 | } 226 | 227 | - (void)shareWebpage:(FlutterMethodCall *)call result:(FlutterResult)result { 228 | NSNumber *scene = call.arguments[@"scene"]; 229 | NSString *title = call.arguments[@"title"]; 230 | NSString *summary = call.arguments[@"summary"]; 231 | NSString *imageUri = call.arguments[@"imageUri"]; 232 | NSString *targetUrl = call.arguments[@"targetUrl"]; 233 | // NSString *appName = call.arguments[@"appName"]; 234 | // NSNumber *extInt = call.arguments[@"extInt"]; 235 | 236 | QQApiNewsObject *object = nil; 237 | NSURL *imageUrl = [NSURL URLWithString:imageUri]; 238 | if ([@"file" isEqualToString:imageUrl.scheme]) { 239 | NSData *imageData = [NSData dataWithContentsOfFile:imageUrl.path]; 240 | object = [QQApiNewsObject objectWithURL:[NSURL URLWithString:targetUrl] 241 | title:title 242 | description:summary 243 | previewImageData:imageData]; 244 | } else { 245 | object = [QQApiNewsObject objectWithURL:[NSURL URLWithString:targetUrl] 246 | title:title 247 | description:summary 248 | previewImageURL:imageUrl]; 249 | } 250 | SendMessageToQQReq *req = [SendMessageToQQReq reqWithContent:object]; 251 | if (scene.intValue == SCENE_QQ) { 252 | [QQApiInterface sendReq:req]; 253 | } else if (scene.intValue == SCENE_QZONE) { 254 | [QQApiInterface SendReqToQZone:req]; 255 | } 256 | result(nil); 257 | } 258 | 259 | #pragma mark - AppDelegate 260 | 261 | - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url { 262 | return 263 | [QQApiInterface handleOpenURL:url 264 | delegate:self] || 265 | ([TencentOAuth CanHandleOpenURL:url] && [TencentOAuth HandleOpenURL:url]); 266 | } 267 | 268 | - (BOOL)application:(UIApplication *)application 269 | openURL:(NSURL *)url 270 | sourceApplication:(NSString *)sourceApplication 271 | annotation:(id)annotation { 272 | return 273 | [QQApiInterface handleOpenURL:url 274 | delegate:self] || 275 | ([TencentOAuth CanHandleOpenURL:url] && [TencentOAuth HandleOpenURL:url]); 276 | } 277 | 278 | - (BOOL)application:(UIApplication *)application 279 | openURL:(NSURL *)url 280 | options: 281 | (NSDictionary *)options { 282 | return 283 | [QQApiInterface handleOpenURL:url 284 | delegate:self] || 285 | ([TencentOAuth CanHandleOpenURL:url] && [TencentOAuth HandleOpenURL:url]); 286 | } 287 | 288 | - (BOOL)application:(UIApplication *)application 289 | continueUserActivity:(NSUserActivity *)userActivity 290 | restorationHandler:(void (^)(NSArray *_Nonnull))restorationHandler { 291 | if ([userActivity.activityType 292 | isEqualToString:NSUserActivityTypeBrowsingWeb]) { 293 | NSURL *url = userActivity.webpageURL; 294 | if (url != nil) { 295 | return [QQApiInterface handleOpenUniversallink:url delegate:self] || 296 | ([TencentOAuth CanHandleUniversalLink:url] && 297 | [TencentOAuth HandleUniversalLink:url]); 298 | } 299 | } 300 | return NO; 301 | } 302 | 303 | #pragma mark - TencentSessionDelegate 304 | 305 | - (BOOL)forceWebLogin { 306 | return _forceWebLogin; 307 | } 308 | 309 | - (void)tencentDidLogin { 310 | NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; 311 | if (_oauth.accessToken != nil && _oauth.accessToken.length > 0) { 312 | NSString *openId = _oauth.openId; 313 | NSString *accessToken = _oauth.accessToken; 314 | if (_oauth.authMode == kAuthModeServerSideCode) { 315 | // 将 code 的值赋给 accessToken, 避免字段功能混淆 316 | // 同时官方文档也有说明通过此接口获取的 code 实际上就是 accessToken 317 | accessToken = [_oauth getServerSideCode]; 318 | } 319 | long long expiresIn = 320 | ceil(_oauth.expirationDate.timeIntervalSinceNow); // 向上取整 321 | long long createAt = [[NSDate date] timeIntervalSince1970] * 1000.0; 322 | [dictionary setValue:[NSNumber numberWithInt:RET_SUCCESS] 323 | forKey:@"ret"]; 324 | [dictionary setValue:openId forKey:@"openid"]; 325 | [dictionary setValue:accessToken forKey:@"access_token"]; 326 | [dictionary setValue:[NSNumber numberWithLongLong:expiresIn] 327 | forKey:@"expires_in"]; 328 | [dictionary setValue:[NSNumber numberWithLongLong:createAt] 329 | forKey:@"create_at"]; 330 | } else { 331 | // 登录失败 332 | [dictionary setValue:[NSNumber numberWithInt:RET_COMMON] 333 | forKey:@"ret"]; 334 | } 335 | [_channel invokeMethod:@"onLoginResp" arguments:dictionary]; 336 | } 337 | 338 | - (void)tencentDidNotLogin:(BOOL)cancelled { 339 | NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; 340 | if (cancelled) { 341 | // 取消登录 342 | [dictionary setValue:[NSNumber numberWithInt:RET_USERCANCEL] 343 | forKey:@"ret"]; 344 | } else { 345 | // 登录失败 346 | [dictionary setValue:[NSNumber numberWithInt:RET_COMMON] 347 | forKey:@"ret"]; 348 | } 349 | [_channel invokeMethod:@"onLoginResp" arguments:dictionary]; 350 | } 351 | 352 | - (void)tencentDidNotNetWork { 353 | // 登录失败 354 | NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; 355 | [dictionary setValue:[NSNumber numberWithInt:RET_COMMON] 356 | forKey:@"ret"]; 357 | [_channel invokeMethod:@"onLoginResp" arguments:dictionary]; 358 | } 359 | 360 | #pragma mark - QQApiInterfaceDelegate 361 | 362 | - (void)onReq:(QQBaseReq *)req { 363 | } 364 | 365 | - (void)onResp:(QQBaseResp *)resp { 366 | NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; 367 | if ([resp isKindOfClass:[SendMessageToQQResp class]]) { 368 | switch (resp.result.intValue) { 369 | case 0: 370 | // 分享成功 371 | [dictionary setValue:[NSNumber numberWithInt:RET_SUCCESS] 372 | forKey:@"ret"]; 373 | break; 374 | case -4: 375 | // 用户取消 376 | [dictionary setValue:[NSNumber numberWithInt:RET_USERCANCEL] 377 | forKey:@"ret"]; 378 | break; 379 | default: 380 | [dictionary setValue:[NSNumber numberWithInt:RET_COMMON] 381 | forKey:@"ret"]; 382 | NSString *errorMsg = 383 | [NSString stringWithFormat:@"result: %@, description: %@.", 384 | resp.result, resp.errorDescription]; 385 | [dictionary setValue:errorMsg forKey:@"msg"]; 386 | break; 387 | } 388 | [_channel invokeMethod:@"onShareResp" arguments:dictionary]; 389 | } 390 | } 391 | 392 | - (void)isOnlineResponse:(NSDictionary *)response { 393 | } 394 | 395 | @end 396 | --------------------------------------------------------------------------------