diff --git a/.drone.yml b/.drone.yml index 9333f4e..a77f99b 100644 --- a/.drone.yml +++ b/.drone.yml @@ -3,7 +3,7 @@ name: default steps: - name: prepare - image: v7lin/flutter:1.7.8-hotfix.3-stable + image: v7lin/flutter:1.9.1-hotfix.6-stable volumes: - name: pub-cache path: /opt/flutter/.pub-cache @@ -11,15 +11,26 @@ steps: - flutter packages get #- name: build_runner -# image: v7lin/flutter:1.7.8-hotfix.3-stable +# image: v7lin/flutter:1.9.1-hotfix.6-stable # volumes: # - name: pub-cache # path: /opt/flutter/.pub-cache # commands: -# - flutter packages pub run build_runner build +# - flutter packages pub run build_runner build --delete-conflicting-outputs + +- name: android-check + image: v7lin/flutter:1.9.1-hotfix.6-stable + volumes: + - name: pub-cache + path: /opt/flutter/.pub-cache + - name: gradle + path: /root/.gradle + commands: + - cd example/android/ + - ./gradlew :tencent_kit:check - name: format - image: v7lin/flutter:1.7.8-hotfix.3-stable + image: v7lin/flutter:1.9.1-hotfix.6-stable volumes: - name: pub-cache path: /opt/flutter/.pub-cache @@ -27,7 +38,7 @@ steps: - flutter format --dry-run --set-exit-if-changed . - name: analyze - image: v7lin/flutter:1.7.8-hotfix.3-stable + image: v7lin/flutter:1.9.1-hotfix.6-stable volumes: - name: pub-cache path: /opt/flutter/.pub-cache @@ -35,17 +46,17 @@ steps: - flutter analyze - name: test - image: v7lin/flutter:1.7.8-hotfix.3-stable + image: v7lin/flutter:1.9.1-hotfix.6-stable volumes: - name: pub-cache path: /opt/flutter/.pub-cache commands: - - flutter test --coverage + - flutter test - cd example/ - flutter test - name: proguard - image: v7lin/flutter:1.7.8-hotfix.3-stable + image: v7lin/flutter:1.9.1-hotfix.6-stable volumes: - name: pub-cache path: /opt/flutter/.pub-cache @@ -68,7 +79,7 @@ steps: - pull_request - name: publish-check - image: v7lin/flutter:1.7.8-hotfix.3-stable + image: v7lin/flutter:1.9.1-hotfix.6-stable volumes: - name: pub-cache path: /opt/flutter/.pub-cache @@ -111,7 +122,7 @@ steps: - rm $FLUTTER_HOME/credentials.json.enc - name: publish - image: v7lin/flutter:1.7.8-hotfix.3-stable + image: v7lin/flutter:1.9.1-hotfix.6-stable volumes: - name: pub-cache path: /opt/flutter/.pub-cache diff --git a/.gitignore b/.gitignore index c9e2ce8..9eb7525 100644 --- a/.gitignore +++ b/.gitignore @@ -3,9 +3,10 @@ .packages .pub/ -pubspec.lock build/ -.idea/ *.iml + +.idea/ +pubspec.lock \ No newline at end of file diff --git a/.metadata b/.metadata index 2517d63..8536f75 100644 --- a/.metadata +++ b/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: 5391447fae6209bb21a89e6a5a6583cac1af9b4b + revision: 68587a0916366e9512a78df22c44163d041dd5f3 channel: stable project_type: plugin diff --git a/CHANGELOG.md b/CHANGELOG.md index e219294..b9468f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## 1.0.0 - 2019.11.14 + +* 更名 tencent_kit +* 升级 Android/iOS SDK +* 添加 Android 代码静态检查 +* 获取用户信息改走API接口 +* 新增获取UnionId功能 + ## 0.3.4 - 2019.9.10 * gradle兼容 diff --git a/README.md b/README.md index 73a8580..f4a9a6f 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ -# fake_tencent +# tencent_kit [![Build Status](https://cloud.drone.io/api/badges/v7lin/fake_tencent/status.svg)](https://cloud.drone.io/v7lin/fake_tencent) [![Codecov](https://codecov.io/gh/v7lin/fake_tencent/branch/master/graph/badge.svg)](https://codecov.io/gh/v7lin/fake_tencent) [![GitHub Tag](https://img.shields.io/github/tag/v7lin/fake_tencent.svg)](https://github.com/v7lin/fake_tencent/releases) -[![Pub Package](https://img.shields.io/pub/v/fake_tencent.svg)](https://pub.dartlang.org/packages/fake_tencent) +[![Pub Package](https://img.shields.io/pub/v/tencent_kit.svg)](https://pub.dartlang.org/packages/tencent_kit) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/v7lin/fake_tencent/blob/master/LICENSE) flutter版腾讯(QQ)SDK -## fake 系列 libraries +## flutter toolkit * [flutter版微信SDK](https://github.com/v7lin/fake_wechat) * [flutter版腾讯(QQ)SDK](https://github.com/v7lin/fake_tencent) @@ -25,10 +25,11 @@ flutter版腾讯(QQ)SDK * [腾讯开放平台](https://open.tencent.com/) * [QQ互联](http://wiki.connect.qq.com/) +* [Universal Links](https://developer.apple.com/library/archive/documentation/General/Conceptual/AppSearch/UniversalLinks.html) ## android -```` +``` ... android { ... @@ -40,22 +41,22 @@ android { ... } ... -```` +``` -```` +``` # 混淆已打入 Library,随 Library 引用,自动添加到 apk 打包混淆 -```` +``` ## ios -```` +``` 在Xcode中,选择你的工程设置项,选中“TARGETS”一栏,在“info”标签栏的“URL type“添加“URL scheme”为你所注册的应用程序id URL Types tencent: identifier=tencent schemes=tencent${appId} -```` +``` -```` +``` iOS 9系统策略更新,限制了http协议的访问,此外应用需要在“Info.plist”中将要使用的URL Schemes列为白名单,才可正常检查其他应用是否安装。 LSApplicationQueriesSchemes @@ -72,7 +73,13 @@ iOS 9系统策略更新,限制了http协议的访问,此外应用需要在 NSAllowsArbitraryLoads -```` +``` + +``` +Universal Links + +Capabilities -> Associated Domain -> Domain -> applinks:${your applinks} +``` ## flutter @@ -83,19 +90,19 @@ iOS 9系统策略更新,限制了http协议的访问,此外应用需要在 * snapshot -```` +``` dependencies: - fake_tencent: + tencent_kit: git: url: https://github.com/v7lin/fake_tencent.git -```` +``` * release -```` +``` dependencies: - fake_tencent: ^${latestTag} -```` + tencent_kit: ^${latestTag} +``` * example @@ -104,10 +111,10 @@ dependencies: ## Getting Started This project is a starting point for a Flutter -[plug-in package](https://flutter.io/developing-packages/), +[plug-in package](https://flutter.dev/developing-packages/), a specialized package that includes platform-specific implementation code for Android and/or iOS. For help getting started with Flutter, view our -[online documentation](https://flutter.io/docs), which offers tutorials, +[online documentation](https://flutter.dev/docs), which offers tutorials, samples, guidance on mobile development, and a full API reference. diff --git a/analysis_options.yaml b/analysis_options.yaml index cd9690c..8d4684b 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -19,114 +19,117 @@ analyzer: # 'super_goes_last' is a deprecated lint rule and should not be used • included_file_warning included_file_warning: ignore - # 过滤 jaguar_serializer + # 过滤 json_serializable exclude: - - "**/*.jser.dart" + - "**/*.g.dart" linter: rules: - # these rules are documented on and in the same order as - # the Dart Lint rules page to make maintenance easier - # http://dart-lang.github.io/linter/lints/ + # these rules are documented on and in the same order as + # the Dart Lint rules page to make maintenance easier + # http://dart-lang.github.io/linter/lints/ - # === error rules === - - avoid_empty_else - - avoid_slow_async_io - - cancel_subscriptions - # - close_sinks # https://github.com/flutter/flutter/issues/5789 - # - comment_references # blocked on https://github.com/dart-lang/dartdoc/issues/1153 - - control_flow_in_finally - - empty_statements - - hash_and_equals - # - invariant_booleans # https://github.com/flutter/flutter/issues/5790 - - iterable_contains_unrelated_type - - list_remove_unrelated_type - # - literal_only_boolean_expressions # https://github.com/flutter/flutter/issues/5791 - - no_adjacent_strings_in_list - - no_duplicate_case_values - - test_types_in_equals - - throw_in_finally - - unrelated_type_equality_checks - - valid_regexps + # === error rules === + - avoid_empty_else + - avoid_slow_async_io + - cancel_subscriptions + # - close_sinks # https://github.com/flutter/flutter/issues/5789 + # - comment_references # blocked on https://github.com/dart-lang/dartdoc/issues/1153 + - control_flow_in_finally + - empty_statements + - hash_and_equals + # - invariant_booleans # https://github.com/flutter/flutter/issues/5790 + - iterable_contains_unrelated_type + - list_remove_unrelated_type + # - literal_only_boolean_expressions # https://github.com/flutter/flutter/issues/5791 + - no_adjacent_strings_in_list + - no_duplicate_case_values + - test_types_in_equals + - throw_in_finally + - unrelated_type_equality_checks + - valid_regexps - # === style rules === - - always_declare_return_types - # - always_put_control_body_on_new_line - - always_require_non_null_named_parameters - - always_specify_types - - annotate_overrides - # - avoid_annotating_with_dynamic # not yet tested - # - avoid_as # 2019-01-30 removed for no-implicit-casts - # - avoid_catches_without_on_clauses # not yet tested - # - avoid_catching_errors # not yet tested - # - avoid_classes_with_only_static_members # not yet tested - # - avoid_function_literals_in_foreach_calls # not yet tested - - avoid_init_to_null - - avoid_null_checks_in_equality_operators - # - avoid_positional_boolean_parameters # not yet tested - - avoid_return_types_on_setters - # - avoid_returning_null # not yet tested - # - avoid_returning_this # not yet tested - # - avoid_setters_without_getters # not yet tested - # - avoid_types_on_closure_parameters # not yet tested - - await_only_futures - - camel_case_types - # - cascade_invocations # not yet tested - # - constant_identifier_names # https://github.com/dart-lang/linter/issues/204 - - directives_ordering - - empty_catches - - empty_constructor_bodies - - implementation_imports - # - join_return_with_assignment # not yet tested - - library_names - - library_prefixes - - non_constant_identifier_names - # - omit_local_variable_types # opposite of always_specify_types - # - one_member_abstracts # too many false positives - # - only_throw_errors # https://github.com/flutter/flutter/issues/5792 - - overridden_fields - - package_api_docs - - package_prefixed_library_names - # - parameter_assignments # we do this commonly - - prefer_adjacent_string_concatenation - - prefer_collection_literals - # - prefer_conditional_assignment # not yet tested - - prefer_const_constructors - # - prefer_constructors_over_static_methods # not yet tested - - prefer_contains - - prefer_equal_for_default_values - # - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods - # - prefer_final_fields # https://github.com/dart-lang/linter/issues/506 - # - prefer_final_locals - # - prefer_foreach # not yet tested - # - prefer_function_declarations_over_variables # not yet tested - - prefer_initializing_formals - # - prefer_interpolation_to_compose_strings # not yet tested - - prefer_is_empty - - prefer_is_not_empty - - prefer_void_to_null - # - recursive_getters # https://github.com/dart-lang/linter/issues/452 - - slash_for_doc_comments - - sort_constructors_first - - sort_unnamed_constructors_first - # - super_goes_last - # - type_annotate_public_apis # subset of always_specify_types - - type_init_formals - # - unawaited_futures # https://github.com/flutter/flutter/issues/5793 - - unnecessary_brace_in_string_interps - - unnecessary_const - - unnecessary_getters_setters - # - unnecessary_lambdas # https://github.com/dart-lang/linter/issues/498 - - unnecessary_new - - unnecessary_null_aware_assignments - - unnecessary_null_in_if_null_operators - # - unnecessary_overrides # https://github.com/dart-lang/linter/issues/626 and https://github.com/dart-lang/linter/issues/627 - - unnecessary_statements - - unnecessary_this - - use_rethrow_when_possible - # - use_setters_to_change_properties # not yet tested - # - use_string_buffers # https://github.com/dart-lang/linter/pull/664 - # - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review + # === style rules === + - always_declare_return_types + # - always_put_control_body_on_new_line + - always_require_non_null_named_parameters + - always_specify_types + - annotate_overrides + # - avoid_annotating_with_dynamic # not yet tested + # - avoid_as # 2019-01-30 removed for no-implicit-casts + # - avoid_catches_without_on_clauses # not yet tested + # - avoid_catching_errors # not yet tested + # - avoid_classes_with_only_static_members # not yet tested + # - avoid_function_literals_in_foreach_calls # not yet tested + - avoid_init_to_null + - avoid_null_checks_in_equality_operators + # - avoid_positional_boolean_parameters # not yet tested + - avoid_return_types_on_setters + # - avoid_returning_null # not yet tested + # - avoid_returning_this # not yet tested + # - avoid_setters_without_getters # not yet tested + # - avoid_types_on_closure_parameters # not yet tested + - await_only_futures + - camel_case_types + # - cascade_invocations # not yet tested + # - constant_identifier_names # https://github.com/dart-lang/linter/issues/204 + - directives_ordering + - empty_catches + - empty_constructor_bodies + - implementation_imports + # - join_return_with_assignment # not yet tested + - library_names + - library_prefixes + - non_constant_identifier_names + # - omit_local_variable_types # opposite of always_specify_types + # - one_member_abstracts # too many false positives + # - only_throw_errors # https://github.com/flutter/flutter/issues/5792 + - overridden_fields + - package_api_docs + - package_prefixed_library_names + # - parameter_assignments # we do this commonly + - prefer_adjacent_string_concatenation + - prefer_collection_literals + # - prefer_conditional_assignment # not yet tested + - prefer_const_constructors + # - prefer_constructors_over_static_methods # not yet tested + - prefer_contains + - prefer_equal_for_default_values + # - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods + # - prefer_final_fields # https://github.com/dart-lang/linter/issues/506 + # - prefer_final_locals + # - prefer_foreach # not yet tested + # - prefer_function_declarations_over_variables # not yet tested + - prefer_initializing_formals + # - prefer_interpolation_to_compose_strings # not yet tested + - prefer_is_empty + - prefer_is_not_empty + - prefer_void_to_null + # - recursive_getters # https://github.com/dart-lang/linter/issues/452 + - slash_for_doc_comments + - sort_constructors_first + - sort_unnamed_constructors_first + # - super_goes_last + # - type_annotate_public_apis # subset of always_specify_types + - type_init_formals + # - unawaited_futures # https://github.com/flutter/flutter/issues/5793 + - unnecessary_brace_in_string_interps + - unnecessary_const + - unnecessary_getters_setters + # - unnecessary_lambdas # https://github.com/dart-lang/linter/issues/498 + - unnecessary_new + - unnecessary_null_aware_assignments + - unnecessary_null_in_if_null_operators + # - unnecessary_overrides # https://github.com/dart-lang/linter/issues/626 and https://github.com/dart-lang/linter/issues/627 + - unnecessary_statements + - unnecessary_this + - use_rethrow_when_possible + # - use_setters_to_change_properties # not yet tested + # - use_string_buffers # https://github.com/dart-lang/linter/pull/664 + # - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review - # === pub rules === - - package_names \ No newline at end of file + # === pub rules === + - package_names + + # === doc rules === + # - public_member_api_docs diff --git a/android/build.gradle b/android/build.gradle index 5f72f16..20a509f 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ -group 'io.github.v7lin.faketencent' -version '1.0-SNAPSHOT' +group 'io.github.v7lin.tencent_kit' +version '1.0.0' buildscript { repositories { @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:3.5.2' } } @@ -20,13 +20,18 @@ rootProject.allprojects { } apply plugin: 'com.android.library' +apply from: './quality.gradle' android { compileSdkVersion 28 defaultConfig { minSdkVersion 16 - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + + // library 混淆 -> 随 library 引用,自动添加到 apk 打包混淆 + consumerProguardFiles 'consumer-proguard-rules.pro' + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } lintOptions { disable 'InvalidPackage' @@ -36,5 +41,5 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'io.github.v7lin:tencent-android:3.3.3+3' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' } diff --git a/android/checkstyle.xml b/android/checkstyle.xml new file mode 100644 index 0000000..90627f2 --- /dev/null +++ b/android/checkstyle.xml @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/consumer-proguard-rules.pro b/android/consumer-proguard-rules.pro new file mode 100644 index 0000000..93985ed --- /dev/null +++ b/android/consumer-proguard-rules.pro @@ -0,0 +1,3 @@ +# --- open_sdk --- + +-keep class com.tencent.** {*;} diff --git a/android/gradle.properties b/android/gradle.properties index 8bd86f6..1515360 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1 +1,5 @@ org.gradle.jvmargs=-Xmx1536M + +android.useAndroidX=true +android.enableJetifier=true +android.enableR8=true diff --git a/android/libs/open_sdk_r6199_lite.jar b/android/libs/open_sdk_r6199_lite.jar new file mode 100644 index 0000000..975a81a Binary files /dev/null and b/android/libs/open_sdk_r6199_lite.jar differ diff --git a/android/quality.gradle b/android/quality.gradle new file mode 100644 index 0000000..9b0abf2 --- /dev/null +++ b/android/quality.gradle @@ -0,0 +1,17 @@ +apply plugin: 'checkstyle' + +check.dependsOn 'checkstyle' + +checkstyle { +// toolVersion = "6.15" +} + +task checkstyle(type: Checkstyle) { + configFile project.file('checkstyle.xml') + source 'src/main/java' + ignoreFailures false + showViolations true + include '**/*.java' + + classpath = files() +} diff --git a/android/settings.gradle b/android/settings.gradle index 4e2a747..1e61309 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1 +1 @@ -rootProject.name = 'fake_tencent' +rootProject.name = 'tencent_kit' diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 39d7c33..f1788cc 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,3 +1,34 @@ + package="io.github.v7lin.tencent_kit"> + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/src/main/java/io/github/v7lin/faketencent/FakeTencentPlugin.java b/android/src/main/java/io/github/v7lin/tencent_kit/TencentKitPlugin.java similarity index 78% rename from android/src/main/java/io/github/v7lin/faketencent/FakeTencentPlugin.java rename to android/src/main/java/io/github/v7lin/tencent_kit/TencentKitPlugin.java index 5f7744e..3ec144e 100644 --- a/android/src/main/java/io/github/v7lin/faketencent/FakeTencentPlugin.java +++ b/android/src/main/java/io/github/v7lin/tencent_kit/TencentKitPlugin.java @@ -1,4 +1,4 @@ -package io.github.v7lin.faketencent; +package io.github.v7lin.tencent_kit; import android.content.Intent; import android.content.pm.PackageInfo; @@ -7,7 +7,8 @@ import android.net.Uri; import android.os.Bundle; import android.text.TextUtils; -import com.tencent.connect.UserInfo; +import androidx.annotation.NonNull; + import com.tencent.connect.common.Constants; import com.tencent.connect.share.QQShare; import com.tencent.connect.share.QzonePublish; @@ -21,7 +22,6 @@ import org.json.JSONObject; import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; @@ -33,55 +33,49 @@ import io.flutter.plugin.common.PluginRegistry; import io.flutter.plugin.common.PluginRegistry.Registrar; /** - * FakeTencentPlugin + * TencentKitPlugin */ -public class FakeTencentPlugin implements MethodCallHandler, PluginRegistry.ActivityResultListener { +public class TencentKitPlugin implements MethodCallHandler, PluginRegistry.ActivityResultListener { /** * Plugin registration. */ public static void registerWith(Registrar registrar) { - final MethodChannel channel = new MethodChannel(registrar.messenger(), "v7lin.github.io/fake_tencent"); - FakeTencentPlugin plugin = new FakeTencentPlugin(registrar, channel); + final MethodChannel channel = new MethodChannel(registrar.messenger(), "v7lin.github.io/tencent_kit"); + TencentKitPlugin plugin = new TencentKitPlugin(registrar, channel); registrar.addActivityResultListener(plugin); channel.setMethodCallHandler(plugin); } private static class TencentScene { - public static final int SCENE_QQ = 0; - public static final int SCENE_QZONE = 1; + static final int SCENE_QQ = 0; + static final int SCENE_QZONE = 1; } private static class TencentRetCode { // 网络请求成功发送至服务器,并且服务器返回数据格式正确 // 这里包括所请求业务操作失败的情况,例如没有授权等原因导致 - public static final int RET_SUCCESS = 0; + static final int RET_SUCCESS = 0; // 网络异常,或服务器返回的数据格式不正确导致无法解析 - public static final int RET_FAILED = 1; - public static final int RET_COMMON = -1; - public static final int RET_USERCANCEL = -2; + static final int RET_FAILED = 1; + static final int RET_COMMON = -1; + static final int RET_USERCANCEL = -2; } private static final String METHOD_REGISTERAPP = "registerApp"; - private static final String METHOD_ISQQINSTALLED = "isQQInstalled"; - private static final String METHOD_ISQQSUPPORTSSOLOGIN = "isQQSupportSSOLogin"; + private static final String METHOD_ISINSTALLED = "isInstalled"; private static final String METHOD_LOGIN = "login"; private static final String METHOD_LOGOUT = "logout"; - private static final String METHOD_GETUSERINFO = "getUserInfo"; private static final String METHOD_SHAREMOOD = "shareMood"; private static final String METHOD_SHAREIMAGE = "shareImage"; private static final String METHOD_SHAREMUSIC = "shareMusic"; private static final String METHOD_SHAREWEBPAGE = "shareWebpage"; private static final String METHOD_ONLOGINRESP = "onLoginResp"; - private static final String METHOD_ONGETUSERINFORESP = "onGetUserInfoResp"; private static final String METHOD_ONSHARERESP = "onShareResp"; private static final String ARGUMENT_KEY_APPID = "appId"; + // private static final String ARGUMENT_KEY_UNIVERSALLINK = "universalLink"; private static final String ARGUMENT_KEY_SCOPE = "scope"; - private static final String ARGUMENT_KEY_OPENID = "openId"; - private static final String ARGUMENT_KEY_ACCESSTOKEN = "accessToken"; - private static final String ARGUMENT_KEY_EXPIRESIN = "expiresIn"; - private static final String ARGUMENT_KEY_CREATEAT = "createAt"; private static final String ARGUMENT_KEY_SCENE = "scene"; private static final String ARGUMENT_KEY_TITLE = "title"; private static final String ARGUMENT_KEY_SUMMARY = "summary"; @@ -107,34 +101,31 @@ public class FakeTencentPlugin implements MethodCallHandler, PluginRegistry.Acti private Tencent tencent; - private FakeTencentPlugin(Registrar registrar, MethodChannel channel) { + private TencentKitPlugin(Registrar registrar, MethodChannel channel) { this.registrar = registrar; this.channel = channel; } @Override - public void onMethodCall(MethodCall call, Result result) { + public void onMethodCall(MethodCall call, @NonNull Result result) { if (METHOD_REGISTERAPP.equals(call.method)) { - String appId = call.argument(ARGUMENT_KEY_APPID); + final String appId = call.argument(ARGUMENT_KEY_APPID); +// final String universalLink = call.argument(ARGUMENT_KEY_UNIVERSALLINK); tencent = Tencent.createInstance(appId, registrar.context().getApplicationContext()); result.success(null); - } else if (METHOD_ISQQINSTALLED.equals(call.method)) { + } else if (METHOD_ISINSTALLED.equals(call.method)) { boolean isQQInstalled = false; try { final PackageManager packageManager = registrar.context().getPackageManager(); - PackageInfo info = packageManager.getPackageInfo("com.tencent.mobileqq", PackageManager.GET_SIGNATURES); + PackageInfo info = packageManager.getPackageInfo("com.tencent.mobileqq", 0); isQQInstalled = info != null; - } catch (PackageManager.NameNotFoundException e) { + } catch (PackageManager.NameNotFoundException ignore) { } result.success(isQQInstalled); - } else if (METHOD_ISQQSUPPORTSSOLOGIN.equals(call.method)) { - result.success(tencent.isSupportSSOLogin(registrar.activity())); } else if (METHOD_LOGIN.equals(call.method)) { login(call, result); } else if (METHOD_LOGOUT.equals(call.method)) { logout(call, result); - } else if (METHOD_GETUSERINFO.equals(call.method)) { - getUserInfo(call, result); } else if (METHOD_SHAREMOOD.equals(call.method)) { shareMood(call, result); } else if (METHOD_SHAREIMAGE.equals(call.method)) { @@ -217,62 +208,6 @@ public class FakeTencentPlugin implements MethodCallHandler, PluginRegistry.Acti } } - private void getUserInfo(MethodCall call, Result result) { - if (tencent != null) { - String openId = call.argument(ARGUMENT_KEY_OPENID); - tencent.setOpenId(openId); - String accessToken = call.argument(ARGUMENT_KEY_ACCESSTOKEN); - int expiresIn = call.argument(ARGUMENT_KEY_EXPIRESIN); - long createAt = call.argument(ARGUMENT_KEY_CREATEAT); - tencent.setAccessToken(accessToken, String.valueOf(expiresIn - (System.currentTimeMillis() - createAt) / 1000)); - UserInfo info = new UserInfo(registrar.context().getApplicationContext(), tencent.getQQToken()); - info.getUserInfo(userInfoListener); - } - result.success(null); - } - - private IUiListener userInfoListener = new IUiListener() { - @Override - public void onComplete(Object o) { - Map map = new HashMap<>(); - try { - if (o != null && o instanceof JSONObject) { - JSONObject object = (JSONObject) o; - int ret = !object.isNull(ARGUMENT_KEY_RESULT_RET) ? object.getInt(ARGUMENT_KEY_RESULT_RET) : TencentRetCode.RET_FAILED; - String msg = !object.isNull(ARGUMENT_KEY_RESULT_MSG) ? object.getString(ARGUMENT_KEY_RESULT_MSG) : null; - if (ret == TencentRetCode.RET_SUCCESS) { - map.put(ARGUMENT_KEY_RESULT_RET, TencentRetCode.RET_SUCCESS); - Iterator keys = object.keys(); - while (keys.hasNext()) { - String key = keys.next(); - map.put(key, object.get(key)); - } - } else { - map.put(ARGUMENT_KEY_RESULT_RET, TencentRetCode.RET_COMMON); - map.put(ARGUMENT_KEY_RESULT_MSG, msg); - } - } - } catch (JSONException e) { - map.put(ARGUMENT_KEY_RESULT_RET, TencentRetCode.RET_COMMON); - map.put(ARGUMENT_KEY_RESULT_MSG, e.getMessage()); - } - channel.invokeMethod(METHOD_ONGETUSERINFORESP, map); - } - - @Override - public void onError(UiError error) { - Map map = new HashMap<>(); - map.put(ARGUMENT_KEY_RESULT_RET, TencentRetCode.RET_COMMON); - map.put(ARGUMENT_KEY_RESULT_MSG, error.errorMessage); - channel.invokeMethod(METHOD_ONGETUSERINFORESP, map); - } - - @Override - public void onCancel() { - // do nothing - } - }; - private void shareMood(MethodCall call, Result result) { if (tencent != null) { int scene = call.argument(ARGUMENT_KEY_SCENE); @@ -416,6 +351,8 @@ public class FakeTencentPlugin implements MethodCallHandler, PluginRegistry.Acti params.putString(QzoneShare.SHARE_TO_QQ_TARGET_URL, targetUrl); tencent.shareToQzone(registrar.activity(), params, shareListener); break; + default: + break; } } result.success(null); @@ -465,23 +402,12 @@ public class FakeTencentPlugin implements MethodCallHandler, PluginRegistry.Acti @Override public boolean onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { - case Constants.REQUEST_API: - // unused - break; case Constants.REQUEST_LOGIN: return Tencent.onActivityResultData(requestCode, resultCode, data, loginListener); case Constants.REQUEST_QQ_SHARE: - return Tencent.onActivityResultData(requestCode, resultCode, data, shareListener); case Constants.REQUEST_QZONE_SHARE: return Tencent.onActivityResultData(requestCode, resultCode, data, shareListener); - case Constants.REQUEST_QQ_FAVORITES: - // unused - break; - case Constants.REQUEST_SEND_TO_MY_COMPUTER: - // unused - break; - case Constants.REQUEST_SHARE_TO_TROOP_BAR: - // unused + default: break; } return false; diff --git a/example/.gitignore b/example/.gitignore index bfaabe0..2ddde2a 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -1,6 +1,5 @@ # Miscellaneous *.class -*.lock *.log *.pyc *.swp @@ -16,8 +15,10 @@ *.iws .idea/ -# Visual Studio Code related -.vscode/ +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ # Flutter/Dart/Pub related **/doc/api/ @@ -26,7 +27,7 @@ .packages .pub-cache/ .pub/ -build/ +/build/ # Android related **/android/**/gradle-wrapper.jar @@ -60,10 +61,9 @@ build/ **/ios/Flutter/app.flx **/ios/Flutter/app.zip **/ios/Flutter/flutter_assets/ +**/ios/Flutter/flutter_export_environment.sh **/ios/ServiceDefinitions.json **/ios/Runner/GeneratedPluginRegistrant.* -**/ios/Frameworks/ -**/ios/Runner.xcworkspace/xcshareddata/ # Exceptions to above rules. !**/ios/**/default.mode1v3 diff --git a/example/.metadata b/example/.metadata index 460bc20..fea404f 100644 --- a/example/.metadata +++ b/example/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: 5391447fae6209bb21a89e6a5a6583cac1af9b4b + revision: 68587a0916366e9512a78df22c44163d041dd5f3 channel: stable project_type: app diff --git a/example/README.md b/example/README.md index 52628e9..7f6c3ea 100644 --- a/example/README.md +++ b/example/README.md @@ -1,6 +1,6 @@ -# fake_tencent_example +# tencent_kit_example -Demonstrates how to use the fake_tencent plugin. +Demonstrates how to use the tencent_kit plugin. ## Getting Started @@ -8,9 +8,9 @@ This project is a starting point for a Flutter application. A few resources to get you started if this is your first Flutter project: -- [Lab: Write your first Flutter app](https://flutter.io/docs/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://flutter.io/docs/cookbook) +- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) -For help getting started with Flutter, view our -[online documentation](https://flutter.io/docs), which offers tutorials, +For help getting started with Flutter, view our +[online documentation](https://flutter.dev/docs), which offers tutorials, samples, guidance on mobile development, and a full API reference. diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml index a8aa282..45f730b 100644 --- a/example/analysis_options.yaml +++ b/example/analysis_options.yaml @@ -10,23 +10,31 @@ analyzer: linter: rules: - - iterable_contains_unrelated_type - - list_remove_unrelated_type - - test_types_in_equals - - unrelated_type_equality_checks - - valid_regexps - - annotate_overrides - - hash_and_equals - - prefer_is_not_empty - - avoid_empty_else - - cancel_subscriptions - - close_sinks - - always_declare_return_types - - camel_case_types - - empty_constructor_bodies - - avoid_init_to_null -# - constant_identifier_names - - one_member_abstracts - - slash_for_doc_comments - - sort_constructors_first - - unnecessary_new \ No newline at end of file + - iterable_contains_unrelated_type + - list_remove_unrelated_type + - test_types_in_equals + - unrelated_type_equality_checks + - valid_regexps + - annotate_overrides + - hash_and_equals + - prefer_is_not_empty + - avoid_empty_else + - cancel_subscriptions + - close_sinks + - always_declare_return_types + - camel_case_types + - empty_constructor_bodies + - avoid_init_to_null + # - constant_identifier_names + - one_member_abstracts + - slash_for_doc_comments + - sort_constructors_first + - unnecessary_brace_in_string_interps + + # === doc rules === + # - public_member_api_docs + + # ignore specify types + # - always_specify_types + # ignore const for now + # - prefer_const_constructors diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index ebed9bb..b920805 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -33,7 +33,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "io.github.v7lin.faketencentexample" + applicationId "com.tencent.sample" minSdkVersion 16 targetSdkVersion 28 versionCode flutterVersionCode.toInteger() @@ -41,7 +41,13 @@ android { manifestPlaceholders = [TENCENT_APP_ID: "222222"] - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + signingConfigs { + debug { + storeFile file('debug.keystore') + } } buildTypes { @@ -51,7 +57,6 @@ android { signingConfig signingConfigs.debug minifyEnabled true - useProguard true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } @@ -64,6 +69,6 @@ flutter { dependencies { testImplementation 'junit:junit:4.12' - androidTestImplementation 'com.android.support.test:runner:1.0.2' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' + androidTestImplementation 'androidx.test:runner:1.1.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' } diff --git a/example/android/app/debug.keystore b/example/android/app/debug.keystore new file mode 100644 index 0000000..434a0a3 Binary files /dev/null and b/example/android/app/debug.keystore differ diff --git a/example/android/app/proguard-rules.pro b/example/android/app/proguard-rules.pro index a52c689..0b0a4c7 100644 --- a/example/android/app/proguard-rules.pro +++ b/example/android/app/proguard-rules.pro @@ -1,8 +1,9 @@ -#Flutter Wrapper +# Flutter Wrapper + -keep class io.flutter.app.** { *; } -keep class io.flutter.plugin.** { *; } -keep class io.flutter.util.** { *; } -keep class io.flutter.view.** { *; } -keep class io.flutter.** { *; } -keep class io.flutter.plugins.** { *; } --dontwarn io.flutter.** \ No newline at end of file +-dontwarn io.flutter.** diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..7b2e0c9 --- /dev/null +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 3011826..6869819 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -1,11 +1,5 @@ - - - + package="io.github.v7lin.tencent_kit_example"> + + diff --git a/example/android/app/src/release/AndroidManifest.xml b/example/android/app/src/release/AndroidManifest.xml new file mode 100644 index 0000000..dd6ab1a --- /dev/null +++ b/example/android/app/src/release/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + diff --git a/example/android/build.gradle b/example/android/build.gradle index e0d7ae2..9e4e79b 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:3.5.2' } } diff --git a/example/android/gradle.properties b/example/android/gradle.properties index 8bd86f6..1515360 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -1 +1,5 @@ org.gradle.jvmargs=-Xmx1536M + +android.useAndroidX=true +android.enableJetifier=true +android.enableR8=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index d2db9dd..1ca6bcb 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,4 @@ -#Tue Sep 10 20:41:08 CST 2019 +#Wed Nov 06 18:22:26 CST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/example/images/icon/2.0x/timg.gif b/example/images/icon/2.0x/timg.gif deleted file mode 100644 index 76d4904..0000000 Binary files a/example/images/icon/2.0x/timg.gif and /dev/null differ diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index 9367d48..6b4c0f7 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -3,7 +3,7 @@ CFBundleDevelopmentRegion - en + $(DEVELOPMENT_LANGUAGE) CFBundleExecutable App CFBundleIdentifier diff --git a/example/ios/Podfile b/example/ios/Podfile index d077b08..64ba749 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -41,7 +41,7 @@ target 'Runner' do # Flutter Pods generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig') if generated_xcode_build_settings.empty? - puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first." + puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first." end generated_xcode_build_settings.map { |p| if p[:name] == 'FLUTTER_FRAMEWORK_DIR' @@ -60,6 +60,9 @@ target 'Runner' do } end +# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. +install! 'cocoapods', :disable_input_output_paths => true + post_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock new file mode 100644 index 0000000..4614280 --- /dev/null +++ b/example/ios/Podfile.lock @@ -0,0 +1,28 @@ +PODS: + - Flutter (1.0.0) + - path_provider (0.0.1): + - Flutter + - tencent_kit (1.0.0): + - Flutter + +DEPENDENCIES: + - Flutter (from `.symlinks/flutter/ios`) + - path_provider (from `.symlinks/plugins/path_provider/ios`) + - tencent_kit (from `.symlinks/plugins/tencent_kit/ios`) + +EXTERNAL SOURCES: + Flutter: + :path: ".symlinks/flutter/ios" + path_provider: + :path: ".symlinks/plugins/path_provider/ios" + tencent_kit: + :path: ".symlinks/plugins/tencent_kit/ios" + +SPEC CHECKSUMS: + Flutter: 0e3d915762c693b495b44d77113d4970485de6ec + path_provider: fb74bd0465e96b594bb3b5088ee4a4e7bb1f2a9d + tencent_kit: ef18a7780c8d8503b448f7d8f25b48abd2706e11 + +PODFILE CHECKSUM: 7fb83752f59ead6285236625b82473f90b1cb932 + +COCOAPODS: 1.8.0 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index e644986..cc37a84 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -11,7 +11,7 @@ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 9422253582A6C9D275F8D188 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EC2B9EE613E5EDCF95CDB72B /* libPods-Runner.a */; }; + 6CE270E48E48FA2184491F5B /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 22AA494CBAC5718A9A5B59FE /* libPods-Runner.a */; }; 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; @@ -40,8 +40,11 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 22AA494CBAC5718A9A5B59FE /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 34BDC42E441A1D281667DFB2 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; + 6E97E3F72373BCD2002BA53E /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -54,7 +57,8 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - EC2B9EE613E5EDCF95CDB72B /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + C01AB4EEE4214A456A0AED3C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + C907325B6974A5E7E013AA51 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -64,18 +68,21 @@ files = ( 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, - 9422253582A6C9D275F8D188 /* libPods-Runner.a in Frameworks */, + 6CE270E48E48FA2184491F5B /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 5C6AC5BA9442E9AF04E7447D /* Pods */ = { + 15DE2C788FC69000FD6D9F20 /* Pods */ = { isa = PBXGroup; children = ( + C907325B6974A5E7E013AA51 /* Pods-Runner.debug.xcconfig */, + C01AB4EEE4214A456A0AED3C /* Pods-Runner.release.xcconfig */, + 34BDC42E441A1D281667DFB2 /* Pods-Runner.profile.xcconfig */, ); - name = Pods; + path = Pods; sourceTree = ""; }; 9740EEB11CF90186004384FC /* Flutter */ = { @@ -97,8 +104,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, - 5C6AC5BA9442E9AF04E7447D /* Pods */, - D579DE886E1BD687DF6DBF83 /* Frameworks */, + 15DE2C788FC69000FD6D9F20 /* Pods */, + D53051BB3AA8D8CE3C0019AC /* Frameworks */, ); sourceTree = ""; }; @@ -113,6 +120,7 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( + 6E97E3F72373BCD2002BA53E /* Runner.entitlements */, 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, 97C146FA1CF9000F007C117D /* Main.storyboard */, @@ -134,10 +142,10 @@ name = "Supporting Files"; sourceTree = ""; }; - D579DE886E1BD687DF6DBF83 /* Frameworks */ = { + D53051BB3AA8D8CE3C0019AC /* Frameworks */ = { isa = PBXGroup; children = ( - EC2B9EE613E5EDCF95CDB72B /* libPods-Runner.a */, + 22AA494CBAC5718A9A5B59FE /* libPods-Runner.a */, ); name = Frameworks; sourceTree = ""; @@ -149,14 +157,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 0D238ADF129651F8319C4F7F /* [CP] Check Pods Manifest.lock */, + 3ACA0EBF42EA843298FCA536 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 7175A6E06323B12A711474E5 /* [CP] Embed Pods Frameworks */, + 44F7BC7464E022FF09A3806E /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -173,18 +181,19 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0910; + LastUpgradeCheck = 1020; ORGANIZATIONNAME = "The Chromium Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; DevelopmentTeam = 78W43A3TE2; + ProvisioningStyle = Automatic; }; }; }; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -216,16 +225,20 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 0D238ADF129651F8319C4F7F /* [CP] Check Pods Manifest.lock */ = { + 3ACA0EBF42EA843298FCA536 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( "${PODS_PODFILE_DIR_PATH}/Podfile.lock", "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); outputPaths = ( "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); @@ -246,24 +259,21 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin\n"; }; - 7175A6E06323B12A711474E5 /* [CP] Embed Pods Frameworks */ = { + 44F7BC7464E022FF09A3806E /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; 9740EEB61CF901F6004384FC /* Run Script */ = { @@ -278,7 +288,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; }; /* End PBXShellScriptBuildPhase section */ @@ -329,12 +339,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -368,6 +380,9 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 78W43A3TE2; ENABLE_BITCODE = NO; @@ -381,8 +396,9 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = io.github.v7lin.fakeTencentExample; + PRODUCT_BUNDLE_IDENTIFIER = io.github.v7lin.tencentKitExample; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; VERSIONING_SYSTEM = "apple-generic"; }; name = Profile; @@ -401,12 +417,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -455,12 +473,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -494,6 +514,9 @@ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 78W43A3TE2; ENABLE_BITCODE = NO; @@ -507,8 +530,9 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = io.github.v7lin.fakeTencentExample; + PRODUCT_BUNDLE_IDENTIFIER = io.github.v7lin.tencentKitExample; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; @@ -518,6 +542,9 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 78W43A3TE2; ENABLE_BITCODE = NO; @@ -531,8 +558,9 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = io.github.v7lin.fakeTencentExample; + PRODUCT_BUNDLE_IDENTIFIER = io.github.v7lin.tencentKitExample; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index e5e8f02..a28140c 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ CFBundleDevelopmentRegion - en + $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -11,7 +11,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleName - fake_tencent_example + tencent_kit_example CFBundlePackageType APPL CFBundleShortVersionString diff --git a/example/lib/main.dart b/example/lib/main.dart index fe4598e..670dbac 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -2,25 +2,12 @@ import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:fake_path_provider/fake_path_provider.dart'; -import 'package:fake_tencent/fake_tencent.dart'; import 'package:path/path.dart' as path; +import 'package:path_provider/path_provider.dart' as path_provider; +import 'package:tencent_kit/tencent_kit.dart'; +import 'package:okhttp_kit/okhttp_kit.dart'; -void main() { - runZoned(() { - runApp(MyApp()); - }, onError: (Object error, StackTrace stack) { - print(error); - print(stack); - }); - - if (Platform.isAndroid) { - SystemUiOverlayStyle systemUiOverlayStyle = - SystemUiOverlayStyle(statusBarColor: Colors.transparent); - SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle); - } -} +void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override @@ -44,7 +31,6 @@ class _HomeState extends State { Tencent _tencent = Tencent()..registerApp(appId: _TENCENT_APPID); StreamSubscription _login; - StreamSubscription _userInfo; StreamSubscription _share; TencentLoginResp _loginResp; @@ -53,7 +39,6 @@ class _HomeState extends State { void initState() { super.initState(); _login = _tencent.loginResp().listen(_listenLogin); - _userInfo = _tencent.userInfoResp().listen(_listenUserInfo); _share = _tencent.shareResp().listen(_listenShare); } @@ -63,11 +48,6 @@ class _HomeState extends State { _showTips('登录', content); } - void _listenUserInfo(TencentUserInfoResp resp) { - String content = 'user info: ${resp.nickname} - ${resp.gender}'; - _showTips('用户', content); - } - void _listenShare(TencentShareResp resp) { String content = 'share: ${resp.ret} - ${resp.msg}'; _showTips('分享', content); @@ -78,9 +58,6 @@ class _HomeState extends State { if (_login != null) { _login.cancel(); } - if (_userInfo != null) { - _userInfo.cancel(); - } if (_share != null) { _share.cancel(); } @@ -91,15 +68,14 @@ class _HomeState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('Fake Tencent Demo'), + title: const Text('TencentKit Demo'), ), body: ListView( children: [ ListTile( title: const Text('环境检查'), onTap: () async { - String content = - 'tencent: ${await _tencent.isQQInstalled()} - ${await _tencent.isQQSupportSSOLogin()}'; + String content = 'tencent: ${await _tencent.isInstalled()}'; _showTips('环境检查', content); }, ), @@ -113,18 +89,39 @@ class _HomeState extends State { ), ListTile( title: const Text('获取用户信息'), - onTap: () { + onTap: () async { if (_loginResp != null && - _loginResp.ret == TencentResp.RET_SUCCESS) { - if (DateTime.now().millisecondsSinceEpoch - - _loginResp.createAt < - _loginResp.expiresIn * 1000) { - _tencent.getUserInfo( - openId: _loginResp.openid, - accessToken: _loginResp.accessToken, - expiresIn: _loginResp.expiresIn, - createAt: _loginResp.createAt, - ); + _loginResp.isSuccessful() && + !_loginResp.isExpired()) { + TencentUserInfoResp userInfo = await _tencent.getUserInfo( + appId: _TENCENT_APPID, + openid: _loginResp.openid, + accessToken: _loginResp.accessToken, + ); + if (userInfo.isSuccessful()) { + _showTips('用户信息', + '${userInfo.nickname} - ${userInfo.gender} - ${userInfo.genderType}'); + } else { + _showTips('用户信息', '${userInfo.ret} - ${userInfo.msg}'); + } + } + }, + ), + ListTile( + title: const Text('获取UnionID'), + onTap: () async { + if (_loginResp != null && + _loginResp.isSuccessful() && + !_loginResp.isExpired()) { + TencentUnionidResp unionid = await _tencent.getUnionId( + accessToken: _loginResp.accessToken, + ); + if (unionid.isSuccessful()) { + _showTips('UnionID', + '${unionid.clientId} - ${unionid.openid} - ${unionid.unionid}'); + } else { + _showTips('UnionID', + '${unionid.error} - ${unionid.errorDescription}'); } } }, @@ -141,21 +138,31 @@ class _HomeState extends State { ListTile( title: const Text('图片分享'), onTap: () async { - AssetImage image = const AssetImage('images/icon/timg.gif'); - AssetBundleImageKey key = - await image.obtainKey(createLocalImageConfiguration(context)); - ByteData imageData = await key.bundle.load(key.name); - Directory saveDir = await PathProvider.getDocumentsDirectory(); - File saveFile = File('${saveDir.path}${path.separator}timg.gif'); - if (!saveFile.existsSync()) { - saveFile.createSync(recursive: true); - saveFile.writeAsBytesSync(imageData.buffer.asUint8List(), - flush: true); + OkHttpClient client = OkHttpClientBuilder().build(); + Response resp = await client + .newCall(RequestBuilder() + .get() + .url(HttpUrl.parse( + 'https://www.baidu.com/img/bd_logo1.png?where=super')) + .build()) + .enqueue(); + if (resp.isSuccessful()) { + Directory saveDir = Platform.isIOS + ? await path_provider.getApplicationDocumentsDirectory() + : await path_provider.getExternalStorageDirectory(); + File saveFile = File(path.join(saveDir.path, 'timg.gif')); + if (!saveFile.existsSync()) { + saveFile.createSync(recursive: true); + saveFile.writeAsBytesSync( + await resp.body().bytes(), + flush: true, + ); + } + await _tencent.shareImage( + scene: TencentScene.SCENE_QQ, + imageUri: Uri.file(saveFile.path), + ); } - await _tencent.shareImage( - scene: TencentScene.SCENE_QQ, - imageUri: Uri.file(saveFile.path), - ); }, ), ListTile( diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 58b9d3b..04ba073 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,5 +1,5 @@ -name: fake_tencent_example -description: Demonstrates how to use the fake_tencent plugin. +name: tencent_kit_example +description: Demonstrates how to use the tencent_kit plugin. publish_to: 'none' environment: @@ -13,19 +13,20 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^0.1.2 - path: ^1.6.2 - fake_path_provider: ^0.0.1 - fake_tencent: - path: ../ + path: ^1.6.4 + path_provider: ^1.4.0 + + okhttp_kit: ^1.0.0 dev_dependencies: flutter_test: sdk: flutter - pedantic: '>=1.4.0 <3.0.0' + tencent_kit: + path: ../ # For information on the generic Dart part of this file, see the -# following page: https://www.dartlang.org/tools/pub/pubspec +# following page: https://dart.dev/tools/pub/pubspec # The following section is specific to Flutter. flutter: @@ -36,14 +37,15 @@ flutter: uses-material-design: true # To add assets to your application, add an assets section, like this: - assets: - - images/icon/timg.gif + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.io/assets-and-images/#resolution-aware. + # https://flutter.dev/assets-and-images/#resolution-aware. # For details regarding adding assets from package dependencies, see - # https://flutter.io/assets-and-images/#from-packages + # https://flutter.dev/assets-and-images/#from-packages # To add custom fonts to your application, add a fonts section here, # in this "flutter" section. Each entry in this list should have a @@ -63,4 +65,4 @@ flutter: # weight: 700 # # For details regarding fonts from package dependencies, - # see https://flutter.io/custom-fonts/#from-packages + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart index 570e0e4..a850067 100644 --- a/example/test/widget_test.dart +++ b/example/test/widget_test.dart @@ -5,4 +5,13 @@ // gestures. You can also use WidgetTester to find child widgets in the widget // tree, read text, and verify that the values of widget properties are correct. -void main() {} +import 'package:flutter_test/flutter_test.dart'; + +import 'package:tencent_kit_example/main.dart'; + +void main() { + testWidgets('smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(MyApp()); + }); +} diff --git a/ios/.gitignore b/ios/.gitignore index 710ec6c..aa479fd 100644 --- a/ios/.gitignore +++ b/ios/.gitignore @@ -34,3 +34,4 @@ Icon? .tags* /Flutter/Generated.xcconfig +/Flutter/flutter_export_environment.sh \ No newline at end of file diff --git a/ios/Classes/FakeTencentPlugin.h b/ios/Classes/FakeTencentPlugin.h deleted file mode 100644 index 556f283..0000000 --- a/ios/Classes/FakeTencentPlugin.h +++ /dev/null @@ -1,4 +0,0 @@ -#import - -@interface FakeTencentPlugin : NSObject -@end diff --git a/ios/Classes/FakeTencentPlugin.m b/ios/Classes/FakeTencentPlugin.m deleted file mode 100644 index d5519b6..0000000 --- a/ios/Classes/FakeTencentPlugin.m +++ /dev/null @@ -1,334 +0,0 @@ -#import "FakeTencentPlugin.h" -#import -#import - -@interface FakeTencentPlugin () - -@end - -enum FakeTencentScene { - SCENE_QQ = 0, - SCENE_QZONE = 1, -}; - -enum FakeTencentRetCode { - // 网络请求成功发送至服务器,并且服务器返回数据格式正确 - // 这里包括所请求业务操作失败的情况,例如没有授权等原因导致 - RET_SUCCESS = 0, - // 网络异常,或服务器返回的数据格式不正确导致无法解析 - RET_FAILED = 1, - RET_COMMON = -1, - RET_USERCANCEL = -2, -}; - -@implementation FakeTencentPlugin { - FlutterMethodChannel * _channel; - TencentOAuth * _oauth; -} - -+ (void)registerWithRegistrar:(NSObject*)registrar { - FlutterMethodChannel* channel = [FlutterMethodChannel - methodChannelWithName:@"v7lin.github.io/fake_tencent" - binaryMessenger:[registrar messenger]]; - FakeTencentPlugin* instance = [[FakeTencentPlugin alloc] initWithChannel:channel]; - [registrar addApplicationDelegate:instance]; - [registrar addMethodCallDelegate:instance channel:channel]; -} - -static NSString * const METHOD_REGISTERAPP = @"registerApp"; -static NSString * const METHOD_ISQQINSTALLED = @"isQQInstalled"; -static NSString * const METHOD_ISQQSUPPORTSSOLOGIN = @"isQQSupportSSOLogin"; -static NSString * const METHOD_LOGIN = @"login"; -static NSString * const METHOD_LOGOUT = @"logout"; -static NSString * const METHOD_GETUSERINFO = @"getUserInfo"; -static NSString * const METHOD_SHAREMOOD = @"shareMood"; -static NSString * const METHOD_SHAREIMAGE = @"shareImage"; -static NSString * const METHOD_SHAREMUSIC = @"shareMusic"; -static NSString * const METHOD_SHAREWEBPAGE = @"shareWebpage"; - -static NSString * const METHOD_ONLOGINRESP = @"onLoginResp"; -static NSString * const METHOD_ONGETUSERINFORESP = @"onGetUserInfoResp"; -static NSString * const METHOD_ONSHARERESP = @"onShareResp"; - -static NSString * const ARGUMENT_KEY_APPID = @"appId"; -static NSString * const ARGUMENT_KEY_SCOPE = @"scope"; -static NSString * const ARGUMENT_KEY_OPENID = @"openId"; -static NSString * const ARGUMENT_KEY_ACCESSTOKEN = @"accessToken"; -static NSString * const ARGUMENT_KEY_EXPIRESIN = @"expiresIn"; -static NSString * const ARGUMENT_KEY_CREATEAT = @"createAt"; -static NSString * const ARGUMENT_KEY_SCENE = @"scene"; -static NSString * const ARGUMENT_KEY_TITLE = @"title"; -static NSString * const ARGUMENT_KEY_SUMMARY = @"summary"; -static NSString * const ARGUMENT_KEY_IMAGEURI = @"imageUri"; -static NSString * const ARGUMENT_KEY_IMAGEURIS = @"imageUris"; -static NSString * const ARGUMENT_KEY_VIDEOURI = @"videoUri"; -static NSString * const ARGUMENT_KEY_MUSICURL = @"musicUrl"; -static NSString * const ARGUMENT_KEY_TARGETURL = @"targetUrl"; -static NSString * const ARGUMENT_KEY_APPNAME = @"appName"; -static NSString * const ARGUMENT_KEY_EXTINT = @"extInt"; - -static NSString * const ARGUMENT_KEY_RESULT_RET = @"ret"; -static NSString * const ARGUMENT_KEY_RESULT_MSG = @"msg"; -static NSString * const ARGUMENT_KEY_RESULT_OPENID = @"openid"; -static NSString * const ARGUMENT_KEY_RESULT_ACCESS_TOKEN = @"access_token"; -static NSString * const ARGUMENT_KEY_RESULT_EXPIRES_IN = @"expires_in"; -static NSString * const ARGUMENT_KEY_RESULT_CREATE_AT = @"create_at"; - -static NSString * const SCHEME_FILE = @"file"; - --(instancetype)initWithChannel:(FlutterMethodChannel *)channel { - self = [super init]; - if (self) { - _channel = channel; - } - return self; -} - -- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { - if ([METHOD_REGISTERAPP isEqualToString:call.method]) { - NSString * appId = call.arguments[ARGUMENT_KEY_APPID]; - _oauth = [[TencentOAuth alloc] initWithAppId:appId andDelegate:self]; - result(nil); - } else if ([METHOD_ISQQINSTALLED isEqualToString:call.method]) { - result([NSNumber numberWithBool:[TencentOAuth iphoneQQInstalled]]); - } else if ([METHOD_ISQQSUPPORTSSOLOGIN isEqualToString:call.method]) { - result([NSNumber numberWithBool:[TencentOAuth iphoneQQSupportSSOLogin]]); - } else if ([METHOD_LOGIN isEqualToString:call.method]) { - [self login:call result:result]; - } else if ([METHOD_LOGOUT isEqualToString:call.method]) { - [self logout:call result:result]; - } else if ([METHOD_GETUSERINFO isEqualToString:call.method]) { - [self getUserInfo:call result:result]; - } else if ([METHOD_SHAREMOOD isEqualToString:call.method]) { - [self shareMood:call result:result]; - } else if ([METHOD_SHAREIMAGE isEqualToString:call.method]) { - [self shareImage:call result:result]; - } else if ([METHOD_SHAREMUSIC isEqualToString:call.method]) { - [self shareMusic:call result:result]; - } else if ([METHOD_SHAREWEBPAGE isEqualToString:call.method]) { - [self shareWebpage:call result:result]; - } else { - result(FlutterMethodNotImplemented); - } -} - --(void)login:(FlutterMethodCall*)call result:(FlutterResult)result { - NSString * scope = call.arguments[ARGUMENT_KEY_SCOPE]; - NSArray * permissions = [scope componentsSeparatedByString:@","]; - [_oauth authorize:permissions]; - result(nil); -} - --(void)logout:(FlutterMethodCall*)call result:(FlutterResult)result { - [_oauth logout:self]; - result(nil); -} - --(void)getUserInfo:(FlutterMethodCall*)call result:(FlutterResult)result { - NSString * openId = call.arguments[ARGUMENT_KEY_OPENID]; - NSString * accessToken = call.arguments[ARGUMENT_KEY_ACCESSTOKEN]; - NSNumber * expiresIn = call.arguments[ARGUMENT_KEY_EXPIRESIN]; - NSNumber * createAt = call.arguments[ARGUMENT_KEY_CREATEAT]; - NSTimeInterval secs = createAt.longLongValue / 1000.0 + expiresIn.longLongValue; - [_oauth setOpenId:openId]; - [_oauth setAccessToken:accessToken]; - [_oauth setExpirationDate:[NSDate dateWithTimeIntervalSince1970:secs]]; - [_oauth getUserInfo]; - result(nil); -} - --(void)shareMood:(FlutterMethodCall*)call result:(FlutterResult)result { - NSNumber * scene = call.arguments[ARGUMENT_KEY_SCENE]; - if (scene.intValue == SCENE_QZONE) { - NSString * summary = call.arguments[ARGUMENT_KEY_SUMMARY]; - NSArray * imageUris = call.arguments[ARGUMENT_KEY_IMAGEURIS]; - NSString * videoUri = call.arguments[ARGUMENT_KEY_VIDEOURI]; - - if (videoUri == nil || videoUri.length == 0) { - NSMutableArray * imageDatas = [NSMutableArray array]; - if (imageUris != nil && imageUris.count > 0) { - for (NSString * imageUri in imageUris) { - NSURL * imageUrl = [NSURL URLWithString:imageUri]; - NSData * imageData = [NSData dataWithContentsOfFile:imageUrl.path]; - [imageDatas addObject:imageData]; - } - } - QQApiImageArrayForQZoneObject * object = [QQApiImageArrayForQZoneObject objectWithimageDataArray:imageDatas title:summary extMap:nil]; - SendMessageToQQReq * req = [SendMessageToQQReq reqWithContent: object]; - [QQApiInterface sendReq:req]; - } else { - QQApiVideoForQZoneObject * object = [QQApiVideoForQZoneObject objectWithAssetURL:videoUri title:summary extMap:nil]; - SendMessageToQQReq * req = [SendMessageToQQReq reqWithContent: object]; - [QQApiInterface sendReq:req]; - } - } - result(nil); -} - --(void)shareImage:(FlutterMethodCall*)call result:(FlutterResult)result { - NSNumber * scene = call.arguments[ARGUMENT_KEY_SCENE]; - if (scene.intValue == SCENE_QQ) { - NSString * imageUri = call.arguments[ARGUMENT_KEY_IMAGEURI]; -// NSString * appName = call.arguments[ARGUMENT_KEY_APPNAME]; -// NSNumber * extInt = call.arguments[ARGUMENT_KEY_EXTINT]; - - NSURL * imageUrl = [NSURL URLWithString:imageUri]; - NSData * imageData = [NSData dataWithContentsOfFile:imageUrl.path]; - QQApiImageObject * object = [QQApiImageObject objectWithData:imageData previewImageData:nil title:nil description:nil]; - SendMessageToQQReq * req = [SendMessageToQQReq reqWithContent: object]; - [QQApiInterface sendReq:req]; - } - result(nil); -} - --(void)shareMusic:(FlutterMethodCall*)call result:(FlutterResult)result { - NSNumber * scene = call.arguments[ARGUMENT_KEY_SCENE]; - NSString * title = call.arguments[ARGUMENT_KEY_TITLE]; - NSString * summary = call.arguments[ARGUMENT_KEY_SUMMARY]; - NSString * imageUri = call.arguments[ARGUMENT_KEY_IMAGEURI]; - NSString * musicUrl = call.arguments[ARGUMENT_KEY_MUSICURL]; - NSString * targetUrl = call.arguments[ARGUMENT_KEY_TARGETURL]; -// NSString * appName = call.arguments[ARGUMENT_KEY_APPNAME]; -// NSNumber * extInt = call.arguments[ARGUMENT_KEY_EXTINT]; - if (scene.intValue == SCENE_QQ) { - QQApiAudioObject * object = nil; - NSURL * imageUrl = [NSURL URLWithString:imageUri]; - if ([SCHEME_FILE isEqualToString:imageUrl.scheme]) { - NSData * imageData = [NSData dataWithContentsOfFile:imageUrl.path]; - object = [QQApiAudioObject objectWithURL:[NSURL URLWithString:targetUrl] title:title description:summary previewImageData:imageData]; - } else { - object = [QQApiAudioObject objectWithURL:[NSURL URLWithString:targetUrl] title:title description:summary previewImageURL:imageUrl]; - } - object.flashURL = [NSURL URLWithString:musicUrl]; - SendMessageToQQReq * req = [SendMessageToQQReq reqWithContent: object]; - [QQApiInterface sendReq:req]; - } - result(nil); -} - --(void)shareWebpage:(FlutterMethodCall*)call result:(FlutterResult)result { - NSNumber * scene = call.arguments[ARGUMENT_KEY_SCENE]; - NSString * title = call.arguments[ARGUMENT_KEY_TITLE]; - NSString * summary = call.arguments[ARGUMENT_KEY_SUMMARY]; - NSString * imageUri = call.arguments[ARGUMENT_KEY_IMAGEURI]; - NSString * targetUrl = call.arguments[ARGUMENT_KEY_TARGETURL]; -// NSString * appName = call.arguments[ARGUMENT_KEY_APPNAME]; -// NSNumber * extInt = call.arguments[ARGUMENT_KEY_EXTINT]; - - QQApiNewsObject * object = nil; - NSURL * imageUrl = [NSURL URLWithString:imageUri]; - if ([SCHEME_FILE isEqualToString:imageUrl.scheme]) { - NSData * imageData = [NSData dataWithContentsOfFile:imageUrl.path]; - object = [QQApiNewsObject objectWithURL:[NSURL URLWithString:targetUrl] title:title description:summary previewImageData:imageData]; - } else { - object = [QQApiNewsObject objectWithURL:[NSURL URLWithString:targetUrl] title:title description:summary previewImageURL:imageUrl]; - } - SendMessageToQQReq * req = [SendMessageToQQReq reqWithContent: object]; - if (scene.intValue == SCENE_QQ) { - [QQApiInterface sendReq:req]; - } else if (scene.intValue == SCENE_QZONE) { - [QQApiInterface SendReqToQZone:req]; - } - result(nil); -} - -# pragma mark - AppDelegate - --(BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url { - return [QQApiInterface handleOpenURL:url delegate:self] || ([TencentOAuth CanHandleOpenURL:url] && [TencentOAuth HandleOpenURL:url]); -} - --(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { - return [QQApiInterface handleOpenURL:url delegate:self] || ([TencentOAuth CanHandleOpenURL:url] && [TencentOAuth HandleOpenURL:url]); -} - --(BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options { - return [QQApiInterface handleOpenURL:url delegate:self] || ([TencentOAuth CanHandleOpenURL:url] && [TencentOAuth HandleOpenURL:url]); -} - -# pragma mark - TencentSessionDelegate - --(void)tencentDidLogin { - NSMutableDictionary * dictionary = [NSMutableDictionary dictionary]; - if (_oauth.accessToken != nil && _oauth.accessToken.length > 0) { - NSString * openId = _oauth.openId; - NSString * accessToken = _oauth.accessToken; - long long expiresIn = ceil(_oauth.expirationDate.timeIntervalSinceNow);// 向上取整 - long long createAt = [[NSDate date] timeIntervalSince1970] * 1000.0; - [dictionary setValue:[NSNumber numberWithInt:RET_SUCCESS] forKey:ARGUMENT_KEY_RESULT_RET]; - [dictionary setValue:openId forKey:ARGUMENT_KEY_RESULT_OPENID]; - [dictionary setValue:accessToken forKey:ARGUMENT_KEY_RESULT_ACCESS_TOKEN]; - [dictionary setValue:[NSNumber numberWithLongLong:expiresIn] forKey:ARGUMENT_KEY_RESULT_EXPIRES_IN]; - [dictionary setValue:[NSNumber numberWithLongLong:createAt] forKey:ARGUMENT_KEY_RESULT_CREATE_AT]; - } else { - // 登录失败 - [dictionary setValue:[NSNumber numberWithInt:RET_COMMON] forKey:ARGUMENT_KEY_RESULT_RET]; - } - [_channel invokeMethod:METHOD_ONLOGINRESP arguments:dictionary]; -} - --(void)tencentDidNotLogin:(BOOL)cancelled { - NSMutableDictionary * dictionary = [NSMutableDictionary dictionary]; - if (cancelled) { - // 取消登录 - [dictionary setValue:[NSNumber numberWithInt:RET_USERCANCEL] forKey:ARGUMENT_KEY_RESULT_RET]; - } else { - // 登录失败 - [dictionary setValue:[NSNumber numberWithInt:RET_COMMON] forKey:ARGUMENT_KEY_RESULT_RET]; - } - [_channel invokeMethod:METHOD_ONLOGINRESP arguments:dictionary]; -} - --(void)tencentDidNotNetWork { - // 登录失败 - NSMutableDictionary * dictionary = [NSMutableDictionary dictionary]; - [dictionary setValue:[NSNumber numberWithInt:RET_COMMON] forKey:ARGUMENT_KEY_RESULT_RET]; - [_channel invokeMethod:METHOD_ONLOGINRESP arguments:dictionary]; -} - --(void)getUserInfoResponse:(APIResponse *)response { - NSMutableDictionary * dictionary = [NSMutableDictionary dictionary]; - if (response.retCode == URLREQUEST_SUCCEED) { - [dictionary setValue:[NSNumber numberWithInt:RET_SUCCESS] forKey:ARGUMENT_KEY_RESULT_RET]; - NSDictionary * json = response.jsonResponse; - [dictionary addEntriesFromDictionary:json]; - } else { - [dictionary setValue:[NSNumber numberWithInt:RET_COMMON] forKey:ARGUMENT_KEY_RESULT_RET]; - [dictionary setValue:response.errorMsg forKey:ARGUMENT_KEY_RESULT_MSG]; - } - [_channel invokeMethod:METHOD_ONGETUSERINFORESP arguments:dictionary]; -} - -# pragma mark - QQApiInterfaceDelegate - --(void)onReq:(QQBaseReq *)req { - -} - -- (void)onResp:(QQBaseResp *)resp { - NSMutableDictionary * dictionary = [NSMutableDictionary dictionary]; - if ([resp isKindOfClass:[SendMessageToQQResp class]]) { - switch (resp.result.intValue) { - case 0: - // 分享成功 - [dictionary setValue:[NSNumber numberWithInt:RET_SUCCESS] forKey:ARGUMENT_KEY_RESULT_RET]; - break; - case -4: - // 用户取消 - [dictionary setValue:[NSNumber numberWithInt:RET_USERCANCEL] forKey:ARGUMENT_KEY_RESULT_RET]; - break; - default: - [dictionary setValue:[NSNumber numberWithInt:RET_COMMON] forKey:ARGUMENT_KEY_RESULT_RET]; - NSString * errorMsg = [NSString stringWithFormat:@"result: %@, description: %@.", resp.result, resp.errorDescription]; - [dictionary setValue:errorMsg forKey:ARGUMENT_KEY_RESULT_MSG]; - break; - } - [_channel invokeMethod:METHOD_ONSHARERESP arguments:dictionary]; - } -} - --(void)isOnlineResponse:(NSDictionary *)response { - -} - -@end diff --git a/ios/Classes/TencentKitPlugin.h b/ios/Classes/TencentKitPlugin.h new file mode 100644 index 0000000..f33aa5a --- /dev/null +++ b/ios/Classes/TencentKitPlugin.h @@ -0,0 +1,4 @@ +#import + +@interface TencentKitPlugin : NSObject +@end diff --git a/ios/Classes/TencentKitPlugin.m b/ios/Classes/TencentKitPlugin.m new file mode 100644 index 0000000..4cb8001 --- /dev/null +++ b/ios/Classes/TencentKitPlugin.m @@ -0,0 +1,313 @@ +#import "TencentKitPlugin.h" +#import +#import + +enum TencentScene { + SCENE_QQ = 0, + SCENE_QZONE = 1, +}; + +enum TencentRetCode { + // 网络请求成功发送至服务器,并且服务器返回数据格式正确 + // 这里包括所请求业务操作失败的情况,例如没有授权等原因导致 + RET_SUCCESS = 0, + // 网络异常,或服务器返回的数据格式不正确导致无法解析 + RET_FAILED = 1, + RET_COMMON = -1, + RET_USERCANCEL = -2, +}; + +@interface TencentKitPlugin () + +@end + +@implementation TencentKitPlugin { + FlutterMethodChannel* _channel; + TencentOAuth* _oauth; +} + ++ (void)registerWithRegistrar:(NSObject*)registrar { + FlutterMethodChannel* channel = [FlutterMethodChannel + methodChannelWithName:@"v7lin.github.io/tencent_kit" + binaryMessenger:[registrar messenger]]; + TencentKitPlugin* instance = [[TencentKitPlugin alloc] initWithChannel:channel]; + [registrar addApplicationDelegate:instance]; + [registrar addMethodCallDelegate:instance channel:channel]; +} + +static NSString* const METHOD_REGISTERAPP = @"registerApp"; +static NSString* const METHOD_ISINSTALLED = @"isInstalled"; +static NSString* const METHOD_LOGIN = @"login"; +static NSString* const METHOD_LOGOUT = @"logout"; +static NSString* const METHOD_SHAREMOOD = @"shareMood"; +static NSString* const METHOD_SHAREIMAGE = @"shareImage"; +static NSString* const METHOD_SHAREMUSIC = @"shareMusic"; +static NSString* const METHOD_SHAREWEBPAGE = @"shareWebpage"; + +static NSString* const METHOD_ONLOGINRESP = @"onLoginResp"; +static NSString* const METHOD_ONSHARERESP = @"onShareResp"; + +static NSString* const ARGUMENT_KEY_APPID = @"appId"; +static NSString* const ARGUMENT_KEY_UNIVERSALLINK = @"universalLink"; +static NSString* const ARGUMENT_KEY_SCOPE = @"scope"; +static NSString* const ARGUMENT_KEY_SCENE = @"scene"; +static NSString* const ARGUMENT_KEY_TITLE = @"title"; +static NSString* const ARGUMENT_KEY_SUMMARY = @"summary"; +static NSString* const ARGUMENT_KEY_IMAGEURI = @"imageUri"; +static NSString* const ARGUMENT_KEY_IMAGEURIS = @"imageUris"; +static NSString* const ARGUMENT_KEY_VIDEOURI = @"videoUri"; +static NSString* const ARGUMENT_KEY_MUSICURL = @"musicUrl"; +static NSString* const ARGUMENT_KEY_TARGETURL = @"targetUrl"; +static NSString* const ARGUMENT_KEY_APPNAME = @"appName"; +static NSString* const ARGUMENT_KEY_EXTINT = @"extInt"; + +static NSString* const ARGUMENT_KEY_RESULT_RET = @"ret"; +static NSString* const ARGUMENT_KEY_RESULT_MSG = @"msg"; +static NSString* const ARGUMENT_KEY_RESULT_OPENID = @"openid"; +static NSString* const ARGUMENT_KEY_RESULT_ACCESS_TOKEN = @"access_token"; +static NSString* const ARGUMENT_KEY_RESULT_EXPIRES_IN = @"expires_in"; +static NSString* const ARGUMENT_KEY_RESULT_CREATE_AT = @"create_at"; + +static NSString* const SCHEME_FILE = @"file"; + +-(instancetype)initWithChannel:(FlutterMethodChannel *)channel { + self = [super init]; + if (self) { + _channel = channel; + } + return self; +} + +- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { + if ([METHOD_REGISTERAPP isEqualToString:call.method]) { + NSString* appId = call.arguments[ARGUMENT_KEY_APPID]; + NSString* universalLink = call.arguments[ARGUMENT_KEY_UNIVERSALLINK]; + if (universalLink != nil) { + _oauth = [[TencentOAuth alloc] initWithAppId:appId andUniversalLink:universalLink andDelegate:self]; + } else { + _oauth = [[TencentOAuth alloc] initWithAppId:appId andDelegate:self]; + } + result(nil); + } else if ([METHOD_ISINSTALLED isEqualToString:call.method]) { + result([NSNumber numberWithBool:[TencentOAuth iphoneQQInstalled]]); + } else if ([METHOD_LOGIN isEqualToString:call.method]) { + [self login:call result:result]; + } else if ([METHOD_LOGOUT isEqualToString:call.method]) { + [self logout:call result:result]; + } else if ([METHOD_SHAREMOOD isEqualToString:call.method]) { + [self shareMood:call result:result]; + } else if ([METHOD_SHAREIMAGE isEqualToString:call.method]) { + [self shareImage:call result:result]; + } else if ([METHOD_SHAREMUSIC isEqualToString:call.method]) { + [self shareMusic:call result:result]; + } else if ([METHOD_SHAREWEBPAGE isEqualToString:call.method]) { + [self shareWebpage:call result:result]; + } else { + result(FlutterMethodNotImplemented); + } +} + +-(void)login:(FlutterMethodCall*)call result:(FlutterResult)result { + NSString* scope = call.arguments[ARGUMENT_KEY_SCOPE]; + NSArray* permissions = [scope componentsSeparatedByString:@","]; + [_oauth authorize:permissions]; + result(nil); +} + +-(void)logout:(FlutterMethodCall*)call result:(FlutterResult)result { + [_oauth logout:self]; + result(nil); +} + +-(void)shareMood:(FlutterMethodCall*)call result:(FlutterResult)result { + NSNumber* scene = call.arguments[ARGUMENT_KEY_SCENE]; + if (scene.intValue == SCENE_QZONE) { + NSString* summary = call.arguments[ARGUMENT_KEY_SUMMARY]; + NSArray* imageUris = call.arguments[ARGUMENT_KEY_IMAGEURIS]; + NSString* videoUri = call.arguments[ARGUMENT_KEY_VIDEOURI]; + + if (videoUri == nil || videoUri.length == 0) { + NSMutableArray* imageDatas = [NSMutableArray array]; + if (imageUris != nil && imageUris.count > 0) { + for (NSString* imageUri in imageUris) { + NSURL* imageUrl = [NSURL URLWithString:imageUri]; + NSData* imageData = [NSData dataWithContentsOfFile:imageUrl.path]; + [imageDatas addObject:imageData]; + } + } + QQApiImageArrayForQZoneObject* object = [QQApiImageArrayForQZoneObject objectWithimageDataArray:imageDatas title:summary extMap:nil]; + SendMessageToQQReq* req = [SendMessageToQQReq reqWithContent: object]; + [QQApiInterface sendReq:req]; + } else { + QQApiVideoForQZoneObject* object = [QQApiVideoForQZoneObject objectWithAssetURL:videoUri title:summary extMap:nil]; + SendMessageToQQReq* req = [SendMessageToQQReq reqWithContent: object]; + [QQApiInterface sendReq:req]; + } + } + result(nil); +} + +-(void)shareImage:(FlutterMethodCall*)call result:(FlutterResult)result { + NSNumber* scene = call.arguments[ARGUMENT_KEY_SCENE]; + if (scene.intValue == SCENE_QQ) { + NSString* imageUri = call.arguments[ARGUMENT_KEY_IMAGEURI]; +// NSString* appName = call.arguments[ARGUMENT_KEY_APPNAME]; +// NSNumber* extInt = call.arguments[ARGUMENT_KEY_EXTINT]; + + NSURL* imageUrl = [NSURL URLWithString:imageUri]; + NSData* imageData = [NSData dataWithContentsOfFile:imageUrl.path]; + QQApiImageObject* object = [QQApiImageObject objectWithData:imageData previewImageData:nil title:nil description:nil]; + SendMessageToQQReq* req = [SendMessageToQQReq reqWithContent: object]; + [QQApiInterface sendReq:req]; + } + result(nil); +} + +-(void)shareMusic:(FlutterMethodCall*)call result:(FlutterResult)result { + NSNumber* scene = call.arguments[ARGUMENT_KEY_SCENE]; + NSString* title = call.arguments[ARGUMENT_KEY_TITLE]; + NSString* summary = call.arguments[ARGUMENT_KEY_SUMMARY]; + NSString* imageUri = call.arguments[ARGUMENT_KEY_IMAGEURI]; + NSString* musicUrl = call.arguments[ARGUMENT_KEY_MUSICURL]; + NSString* targetUrl = call.arguments[ARGUMENT_KEY_TARGETURL]; +// NSString* appName = call.arguments[ARGUMENT_KEY_APPNAME]; +// NSNumber* extInt = call.arguments[ARGUMENT_KEY_EXTINT]; + if (scene.intValue == SCENE_QQ) { + QQApiAudioObject* object = nil; + NSURL* imageUrl = [NSURL URLWithString:imageUri]; + if ([SCHEME_FILE isEqualToString:imageUrl.scheme]) { + NSData* imageData = [NSData dataWithContentsOfFile:imageUrl.path]; + object = [QQApiAudioObject objectWithURL:[NSURL URLWithString:targetUrl] title:title description:summary previewImageData:imageData]; + } else { + object = [QQApiAudioObject objectWithURL:[NSURL URLWithString:targetUrl] title:title description:summary previewImageURL:imageUrl]; + } + object.flashURL = [NSURL URLWithString:musicUrl]; + SendMessageToQQReq* req = [SendMessageToQQReq reqWithContent: object]; + [QQApiInterface sendReq:req]; + } + result(nil); +} + +-(void)shareWebpage:(FlutterMethodCall*)call result:(FlutterResult)result { + NSNumber* scene = call.arguments[ARGUMENT_KEY_SCENE]; + NSString* title = call.arguments[ARGUMENT_KEY_TITLE]; + NSString* summary = call.arguments[ARGUMENT_KEY_SUMMARY]; + NSString* imageUri = call.arguments[ARGUMENT_KEY_IMAGEURI]; + NSString* targetUrl = call.arguments[ARGUMENT_KEY_TARGETURL]; +// NSString* appName = call.arguments[ARGUMENT_KEY_APPNAME]; +// NSNumber* extInt = call.arguments[ARGUMENT_KEY_EXTINT]; + + QQApiNewsObject* object = nil; + NSURL* imageUrl = [NSURL URLWithString:imageUri]; + if ([SCHEME_FILE isEqualToString:imageUrl.scheme]) { + NSData* imageData = [NSData dataWithContentsOfFile:imageUrl.path]; + object = [QQApiNewsObject objectWithURL:[NSURL URLWithString:targetUrl] title:title description:summary previewImageData:imageData]; + } else { + object = [QQApiNewsObject objectWithURL:[NSURL URLWithString:targetUrl] title:title description:summary previewImageURL:imageUrl]; + } + SendMessageToQQReq* req = [SendMessageToQQReq reqWithContent: object]; + if (scene.intValue == SCENE_QQ) { + [QQApiInterface sendReq:req]; + } else if (scene.intValue == SCENE_QZONE) { + [QQApiInterface SendReqToQZone:req]; + } + result(nil); +} + +# pragma mark - AppDelegate + +-(BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url { + return [QQApiInterface handleOpenURL:url delegate:self] || ([TencentOAuth CanHandleOpenURL:url] && [TencentOAuth HandleOpenURL:url]); +} + +-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { + return [QQApiInterface handleOpenURL:url delegate:self] || ([TencentOAuth CanHandleOpenURL:url] && [TencentOAuth HandleOpenURL:url]); +} + +-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options { + return [QQApiInterface handleOpenURL:url delegate:self] || ([TencentOAuth CanHandleOpenURL:url] && [TencentOAuth HandleOpenURL:url]); +} + +- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nonnull))restorationHandler { + if([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) { + NSURL* url = userActivity.webpageURL; + if (url != nil) { + return [QQApiInterface handleOpenUniversallink:url delegate:self] || ([TencentOAuth CanHandleUniversalLink:url] && [TencentOAuth HandleUniversalLink:url]); + } + } + return NO; +} + +# pragma mark - TencentSessionDelegate + +-(void)tencentDidLogin { + NSMutableDictionary* dictionary = [NSMutableDictionary dictionary]; + if (_oauth.accessToken != nil && _oauth.accessToken.length > 0) { + NSString* openId = _oauth.openId; + NSString* accessToken = _oauth.accessToken; + long long expiresIn = ceil(_oauth.expirationDate.timeIntervalSinceNow);// 向上取整 + long long createAt = [[NSDate date] timeIntervalSince1970]* 1000.0; + [dictionary setValue:[NSNumber numberWithInt:RET_SUCCESS] forKey:ARGUMENT_KEY_RESULT_RET]; + [dictionary setValue:openId forKey:ARGUMENT_KEY_RESULT_OPENID]; + [dictionary setValue:accessToken forKey:ARGUMENT_KEY_RESULT_ACCESS_TOKEN]; + [dictionary setValue:[NSNumber numberWithLongLong:expiresIn] forKey:ARGUMENT_KEY_RESULT_EXPIRES_IN]; + [dictionary setValue:[NSNumber numberWithLongLong:createAt] forKey:ARGUMENT_KEY_RESULT_CREATE_AT]; + } else { + // 登录失败 + [dictionary setValue:[NSNumber numberWithInt:RET_COMMON] forKey:ARGUMENT_KEY_RESULT_RET]; + } + [_channel invokeMethod:METHOD_ONLOGINRESP arguments:dictionary]; +} + +-(void)tencentDidNotLogin:(BOOL)cancelled { + NSMutableDictionary* dictionary = [NSMutableDictionary dictionary]; + if (cancelled) { + // 取消登录 + [dictionary setValue:[NSNumber numberWithInt:RET_USERCANCEL] forKey:ARGUMENT_KEY_RESULT_RET]; + } else { + // 登录失败 + [dictionary setValue:[NSNumber numberWithInt:RET_COMMON] forKey:ARGUMENT_KEY_RESULT_RET]; + } + [_channel invokeMethod:METHOD_ONLOGINRESP arguments:dictionary]; +} + +-(void)tencentDidNotNetWork { + // 登录失败 + NSMutableDictionary* dictionary = [NSMutableDictionary dictionary]; + [dictionary setValue:[NSNumber numberWithInt:RET_COMMON] forKey:ARGUMENT_KEY_RESULT_RET]; + [_channel invokeMethod:METHOD_ONLOGINRESP arguments:dictionary]; +} + +# pragma mark - QQApiInterfaceDelegate + +-(void)onReq:(QQBaseReq *)req { + +} + +- (void)onResp:(QQBaseResp *)resp { + NSMutableDictionary* dictionary = [NSMutableDictionary dictionary]; + if ([resp isKindOfClass:[SendMessageToQQResp class]]) { + switch (resp.result.intValue) { + case 0: + // 分享成功 + [dictionary setValue:[NSNumber numberWithInt:RET_SUCCESS] forKey:ARGUMENT_KEY_RESULT_RET]; + break; + case -4: + // 用户取消 + [dictionary setValue:[NSNumber numberWithInt:RET_USERCANCEL] forKey:ARGUMENT_KEY_RESULT_RET]; + break; + default: + [dictionary setValue:[NSNumber numberWithInt:RET_COMMON] forKey:ARGUMENT_KEY_RESULT_RET]; + NSString* errorMsg = [NSString stringWithFormat:@"result: %@, description: %@.", resp.result, resp.errorDescription]; + [dictionary setValue:errorMsg forKey:ARGUMENT_KEY_RESULT_MSG]; + break; + } + [_channel invokeMethod:METHOD_ONSHARERESP arguments:dictionary]; + } +} + +-(void)isOnlineResponse:(NSDictionary *)response { + +} + +@end diff --git a/ios/Libraries/TencentOpenAPI.framework/Headers/QQApiInterface.h b/ios/Libraries/TencentOpenAPI.framework/Headers/QQApiInterface.h new file mode 100644 index 0000000..c52a1ee --- /dev/null +++ b/ios/Libraries/TencentOpenAPI.framework/Headers/QQApiInterface.h @@ -0,0 +1,158 @@ +/// +/// \file QQApiInterface.h +/// \brief QQApi接口简化封装 +/// +/// Created by Tencent on 12-5-15. +/// Copyright (c) 2012年 Tencent. All rights reserved. +/// + +#import +#import "QQApiInterfaceObject.h" + +typedef void (^sendResultBlock)(NSDictionary *result); + +/** + \brief 处理来至QQ的请求及响应的回调协议 + */ +@protocol QQApiInterfaceDelegate + +/** + 处理来至QQ的请求 + */ +- (void)onReq:(QQBaseReq *)req; + +/** + 处理来至QQ的响应 + */ +- (void)onResp:(QQBaseResp *)resp; + +/** + 处理QQ在线状态的回调 + */ +- (void)isOnlineResponse:(NSDictionary *)response; + +@end + +/** + \brief 对QQApi的简单封装类 + */ +@interface QQApiInterface : NSObject + +/** + 处理由手Q唤起的普通跳转请求 + \param url 待处理的url跳转请求 + \param delegate 第三方应用用于处理来至QQ请求及响应的委托对象 + \return 跳转请求处理结果,YES表示成功处理,NO表示不支持的请求协议或处理失败 + */ ++ (BOOL)handleOpenURL:(NSURL *)url delegate:(id)delegate; + +/** + 处理由手Q唤起的universallink跳转请求 + \param universallink 待处理的universallink跳转请求 + \param delegate 第三方应用用于处理来至QQ请求及响应的委托对象 + \return 跳转请求处理结果,YES表示成功处理,NO表示不支持的请求协议或处理失败 + */ ++ (BOOL)handleOpenUniversallink:(NSURL*)universallink delegate:(id)delegate; + +/** + 向手Q发起分享请求 + \param req 分享内容的请求 + \return 请求发送结果码 + */ ++ (QQApiSendResultCode)sendReq:(QQBaseReq *)req; + + +/** + 向手Q QZone结合版发起分享请求 + \note H5分享只支持单张网络图片的传递 + \param req 分享内容的请求 + \return 请求发送结果码 + */ ++ (QQApiSendResultCode)SendReqToQZone:(QQBaseReq *)req; + +/** + 向手Q发起设置QQ头像 + \param req 分享内容的请求 + \return 请求发送结果码 + */ ++ (QQApiSendResultCode)sendMessageToQQAvatarWithReq:(QQBaseReq*)req; + +/** + 向手Q发起组图分享到表情收藏 + \param req 分享内容的请求 + \return 请求发送结果码 + */ ++ (QQApiSendResultCode)sendMessageToFaceCollectionWithReq:(QQBaseReq*)req; + +/** + 检测是否已安装QQ + \return 如果QQ已安装则返回YES,否则返回NO + + \note SDK目前已经支持QQ、TIM授权登录及分享功能, 会按照QQ>TIM的顺序进行调用。 + 只要用户安装了QQ、TIM中任意一个应用,都可为第三方应用进行授权登录、分享功能。 + 第三方应用在接入SDK时不需要判断是否安装QQ、TIM。若有判断安装QQ、TIM的逻辑建议移除。 + */ ++ (BOOL)isQQInstalled; + +/** + 检测是否已安装TIM + \return 如果TIM已安装则返回YES,否则返回NO + + \note SDK目前已经支持QQ、TIM授权登录及分享功能, 会按照QQ>TIM的顺序进行调用。 + 只要用户安装了QQ、TIM中任意一个应用,都可为第三方应用进行授权登录、分享功能。 + 第三方应用在接入SDK时不需要判断是否安装QQ、TIM。若有判断安装QQ、TIM的逻辑建议移除。 + */ ++ (BOOL)isTIMInstalled; + +/** + 检测QQ是否支持API调用 + \return 如果当前安装QQ版本支持API调用则返回YES,否则返回NO + */ ++ (BOOL)isQQSupportApi; + +/** + 检测TIM是否支持API调用 + \return 如果当前安装TIM版本支持API调用则返回YES,否则返回NO + */ ++ (BOOL)isTIMSupportApi __attribute__((deprecated("已过期, 建议删除调用,调用地方用YES替代。"))); + +/** + 检测是否支持分享 + \return 如果当前已安装QQ且QQ版本支持API调用 或者 当前已安装TIM且TIM版本支持API调用则返回YES,否则返回NO + */ ++ (BOOL)isSupportShareToQQ; + +/** + 检测是否支持分享到QQ结合版QZone + \return 如果当前已安装QQ且QQ版本支持API调用则返回YES,否则返回NO + */ ++ (BOOL)isSupportPushToQZone; + +/** + 启动QQ + \return 成功返回YES,否则返回NO + */ ++ (BOOL)openQQ; + +/** + 启动TIM + \return 成功返回YES,否则返回NO + */ ++ (BOOL)openTIM; + +/** + 获取QQ下载地址 + + 如果App通过QQApiInterface#isQQInstalledQQApiInterface#isQQSupportApi检测发现QQ没安装或当前版本QQ不支持API调用,可引导用户通过打开此链接下载最新版QQ。 + \return iPhoneQQ下载地址 + */ ++ (NSString *)getQQInstallUrl; + +/** + 获取TIM下载地址 + + 如果App通过QQApiInterface#isTIMInstalled检测发现TIM没安装或当前版本TIM不支持API调用,可引导用户通过打开此链接下载最新版TIM。 + \return iPhoneTIM下载地址 + */ ++ (NSString *)getTIMInstallUrl; +@end diff --git a/ios/Libraries/TencentOpenAPI.framework/Headers/QQApiInterfaceObject.h b/ios/Libraries/TencentOpenAPI.framework/Headers/QQApiInterfaceObject.h new file mode 100644 index 0000000..3a73da4 --- /dev/null +++ b/ios/Libraries/TencentOpenAPI.framework/Headers/QQApiInterfaceObject.h @@ -0,0 +1,645 @@ +/// +/// \file QQApiInterfaceObject.h +/// \brief QQApiInterface所依赖的请求及应答消息对象封装帮助类 +/// +/// Created by Tencent on 12-5-15. +/// Copyright (c) 2012年 Tencent. All rights reserved. +/// + +#ifndef QQApiInterface_QQAPIOBJECT_h +#define QQApiInterface_QQAPIOBJECT_h + +#import + +typedef NS_ENUM(NSInteger,QQApiSendResultCode) { + EQQAPISENDSUCESS = 0, + EQQAPIQQNOTINSTALLED = 1, //QQ未安装 + EQQAPIQQNOTSUPPORTAPI = 2, // QQ api不支持 + EQQAPIMESSAGETYPEINVALID = 3, + EQQAPIMESSAGECONTENTNULL = 4, + EQQAPIMESSAGECONTENTINVALID = 5, + EQQAPIAPPNOTREGISTED = 6, + EQQAPIAPPSHAREASYNC = 7, + EQQAPIQQNOTSUPPORTAPI_WITH_ERRORSHOW = 8, //QQ api不支持 && SDK显示error提示(已废弃) + EQQAPIMESSAGEARKCONTENTNULL = 9, //ark内容为空 + EQQAPIMESSAGE_MINI_CONTENTNULL = 10, //小程序参数为空 + EQQAPISENDFAILD = -1, //发送失败 + EQQAPISHAREDESTUNKNOWN = -2, //未指定分享到QQ或TIM + EQQAPITIMSENDFAILD = -3, //发送失败 + EQQAPITIMNOTINSTALLED = 11, //TIM未安装 + EQQAPITIMNOTSUPPORTAPI = 12, // TIM api不支持 + EQQAPI_INCOMING_PARAM_ERROR = 13, // 外部传参错误 + EQQAPI_THIRD_APP_GROUP_ERROR_APP_NOT_AUTHORIZIED = 14, // APP未获得授权 + EQQAPI_THIRD_APP_GROUP_ERROR_CGI_FAILED = 15, // CGI请求失败 + EQQAPI_THIRD_APP_GROUP_ERROR_HAS_BINDED = 16, // 该组织已经绑定群聊 + EQQAPI_THIRD_APP_GROUP_ERROR_NOT_BINDED = 17, // 该组织尚未绑定群聊 + EQQAPIQZONENOTSUPPORTTEXT = 10000, //qzone分享不支持text类型分享 + EQQAPIQZONENOTSUPPORTIMAGE = 10001, //qzone分享不支持image类型分享 + EQQAPIVERSIONNEEDUPDATE = 10002, //当前QQ版本太低,需要更新至新版本才可以支持 + ETIMAPIVERSIONNEEDUPDATE = 10004, //当前TIM版本太低,需要更新至新版本才可以支持 +}; + +#pragma mark - QQApiObject(分享对象类型) + +// QQApiObject control flags +typedef NS_ENUM(NSUInteger,kQQAPICtrlFlag) { + kQQAPICtrlFlagQZoneShareOnStart = 0x01, + kQQAPICtrlFlagQZoneShareForbid = 0x02, + kQQAPICtrlFlagQQShare = 0x04, + kQQAPICtrlFlagQQShareFavorites = 0x08, //收藏 + kQQAPICtrlFlagQQShareDataline = 0x10, //数据线 + kQQAPICtrlFlagQQShareEnableArk = 0x20, //支持ARK + kQQAPICtrlFlagQQShareEnableMiniProgram = 0x40, //支持小程序 +}; + +// 分享到QQ或TIM +typedef NS_ENUM(NSUInteger, ShareDestType) { + ShareDestTypeQQ = 0, + ShareDestTypeTIM, +}; + +//小程序的类型 +typedef NS_ENUM(NSUInteger, MiniProgramType) { + MiniProgramType_Develop=0, // 开发版 + MiniProgramType_Test=1, // 测试版 + MiniProgramType_Online=3, // 正式版,默认 + MiniProgramType_Preview=4, // 预览版 +}; + +// QQApiObject +/** \brief 所有在QQ及插件间发送的数据对象的根类。 + */ +__attribute__((visibility("default"))) @interface QQApiObject : NSObject +@property(nonatomic, retain) NSString* title; ///< 标题,最长128个字符 +@property(nonatomic, retain) NSString* description; ///<简要描述,最长512个字符 +@property(nonatomic, retain) NSString* universalLink; ///(>=3.3.7)支持第三方传入在互联开放平台注册的universallink +@property(nonatomic, assign) uint64_t cflag; +/* + * 分享到QQ/TIM + * SDK根据是否安装对应客户端进行判断,判断顺序:QQ > TIM + * 默认分享到QQ,如果QQ未安装检测TIM是否安装 + */ +@property (nonatomic, assign) ShareDestType shareDestType; +@end + +// ArkObject +/** \brief 支持Ark的根类。 + */ +__attribute__((visibility("default"))) @interface ArkObject : NSObject +@property(nonatomic,retain) NSString* arkData; ///< 显示Ark所需的数据,json串,长度暂不限制 +@property(nonatomic,assign) QQApiObject* qqApiObject; ///<原有老版本的QQApiObject + +- (id)initWithData:(NSString *)arkData qqApiObject:(QQApiObject*)qqApiObject; ++ (id)objectWithData:(NSString *)arkData qqApiObject:(QQApiObject*)qqApiObject; +@end + +#pragma mark QQ小程序 +//分享小程序消息 - QQ 8.0.8 +__attribute__((visibility("default"))) @interface QQApiMiniProgramObject : NSObject +@property(nonatomic,retain) QQApiObject* qqApiObject; //原有老版本的QQApiObject +@property(nonatomic,retain) NSString* miniAppID; //必填,小程序的AppId(注:必须在QQ互联平台中,将该小程序与分享的App绑定) +@property(nonatomic,retain) NSString* miniPath; //必填,小程序的展示路径 +@property(nonatomic,retain) NSString* webpageUrl; //必填,兼容低版本的网页链接 +@property(nonatomic,assign) MiniProgramType miniprogramType; //非必填,小程序的类型,默认正式版(3),可选测试版(1)、预览版(4) +@end + +//唤起小程序 - QQ 8.1.8 +__attribute__((visibility("default"))) @interface QQApiLaunchMiniProgramObject : QQApiObject +@property(nonatomic,retain) NSString* miniAppID; //必填,小程序的AppId(注:必须在QQ互联平台中,将该小程序与分享的App绑定) +@property(nonatomic,retain) NSString* miniPath; //必填,小程序的展示路径 +@property(nonatomic,assign) MiniProgramType miniprogramType; //非必填,小程序的类型,默认正式版(3),可选测试版(1)、开发版(0) +@end +// QQApiResultObject +/** \brief 用于请求回应的数据类型。 +

可能错误码及描述如下:

+ + + + + + + + +
errorerrorDescription注释
0nil成功
-1param error参数错误
-2group code is invalid该群不在自己的群列表里面
-3upload photo failed上传图片失败
-4user give up the current operation用户放弃当前操作
-5client internal error客户端内部处理错误
+ */ +__attribute__((visibility("default"))) @interface QQApiResultObject : QQApiObject +@property(nonatomic,retain) NSString* error; ///<错误 +@property(nonatomic,retain) NSString* errorDescription; ///<错误描述 +@property(nonatomic,retain) NSString* extendInfo; ///<扩展信息 +@end + +// QQApiTextObject +/** \brief 文本对象 + */ +@interface QQApiTextObject : QQApiObject +@property(nonatomic,retain)NSString* text; ///<文本内容,必填,最长1536个字符 + +-(id)initWithText:(NSString*)text; ///<初始化方法 ++(id)objectWithText:(NSString*)text;///<工厂方法,获取一个QQApiTextObject对象. +@end + +// QQApiURLObject +typedef NS_ENUM(NSUInteger, QQApiURLTargetType) { + QQApiURLTargetTypeNotSpecified = 0x00, + QQApiURLTargetTypeAudio = 0x01, + QQApiURLTargetTypeVideo = 0x02, + QQApiURLTargetTypeNews = 0x03 +}; + +/** @brief URL对象类型。 + + 包括URL地址,URL地址所指向的目标类型及预览图像。 + */ +__attribute__((visibility("default"))) @interface QQApiURLObject : QQApiObject +/** + URL地址所指向的目标类型. + @note 参见QQApi.h 中的 QQApiURLTargetType 定义. + */ +@property(nonatomic)QQApiURLTargetType targetContentType; + +@property(nonatomic,retain)NSURL* url; ///QQApiExtendObject对象 + @param data 数据内容 + @param previewImageData 用于预览的图片 + @param title 标题 + @param description 此对象,分享的描述 + @return + 一个自动释放的QQApiExtendObject实例 + */ ++ (id)objectWithData:(NSData*)data previewImageData:(NSData*)previewImageData title:(NSString*)title description:(NSString*)description; + +/** + helper方法获取一个autorelease的QQApiExtendObject对象 + @param data 数据内容 + @param previewImageData 用于预览的图片 + @param title 标题 + @param description 此对象,分享的描述 + @param imageDataArray 发送的多张图片队列 + @return + 一个自动释放的QQApiExtendObject实例 + */ ++ (id)objectWithData:(NSData*)data previewImageData:(NSData*)previewImageData title:(NSString*)title description:(NSString*)description imageDataArray:(NSArray*)imageDataArray; + +@end + +// QQApiImageObject +/** @brief 图片对象 + 用于分享图片内容的对象,是一个指定为图片类型的QQApiExtendObject + */ +@interface QQApiImageObject : QQApiExtendObject +@end + +// QQApiImageForQQAvatarObject +/** @brief 图片对象 + 用于设置QQ头像内容的对象,是一个指定为图片类型的QQApiExtendObject + */ +@interface QQApiImageForQQAvatarObject : QQApiExtendObject +@end +/** + * @brief 视频对象 + * 用于设置动态头像 + * assetURL可传ALAsset的ALAssetPropertyAssetURL,或者PHAsset的localIdentifier + 从手Q返回的错误码: + //第三方设置动态头像结果 + @"ret=0"//设置成功 + @"ret=-10&error_des=user cancel"//用户取消设置 + @"ret=-11&error_des=pasteboard have no video data"//剪切板没有数据 + @"ret=-12&error_des=export data failed"//从剪切板导出数据到本地失败 + @"ret=-13&error_des=url param invalid"//sdk传递过来的数据有误 + @"ret=-14&error_des=video param invalid"//视频的参数不符合要求(检测第三方视频源方案:1、分辨率跟480*480保持一致;2、视频长度0.5s~8s) + @"ret=-15&error_des=app authorised failed"//应用鉴权失败 + @"ret=-16&error_des=upload video failed"//设置头像,上传到后台失败 + @"ret=-17&error_des=account diff"//账号不一致 + */ +@interface QQApiVideoForQQAvatarObject : QQApiExtendObject +@property(nonatomic, retain) NSString *assetURL; +@end + +// QQApiImageArrayForFaceCollectionObject +/** @brief 图片数组对象 + 用于分享图片组到表情收藏,是一个指定为图片类型的QQApiObject + */ +@interface QQApiImageArrayForFaceCollectionObject : QQApiObject + +@property(nonatomic,retain) NSArray* imageDataArray;///图片数组 + +/** + 初始化方法 + @param imageDataArray 图片数组 + */ +- (id)initWithImageArrayData:(NSArray*)imageDataArray; +/** + helper方法获取一个autorelease的QQApiObject对象 + @param imageDataArray 发送的多张图片队列 + @return + 一个自动释放的QQApiObject实例 + */ ++ (id)objectWithimageDataArray:(NSArray *)imageDataArray; + +@end + +// QQApiImageArrayForQZoneObject +/** @brief 图片对象 + 用于分享图片到空间,走写说说路径,是一个指定为图片类型的,当图片数组为空时,默认走文本写说说QQApiObject + */ +@interface QQApiImageArrayForQZoneObject : QQApiObject + +@property(nonatomic,retain) NSArray* imageDataArray;///图片数组 +@property(nonatomic,retain) NSDictionary* extMap; // 扩展字段 + +/** + 初始化方法 + @param imageDataArray 图片数组 + @param title 写说说的内容,可以为空 + @param extMap 扩展字段 + */ +- (id)initWithImageArrayData:(NSArray*)imageDataArray title:(NSString*)title extMap:(NSDictionary *)extMap; + +/** + helper方法获取一个autorelease的QQApiExtendObject对象 + @param title 写说说的内容,可以为空 + @param imageDataArray 发送的多张图片队列 + @param extMap 扩展字段 + @return + 一个自动释放的QQApiExtendObject实例 + */ ++ (id)objectWithimageDataArray:(NSArray*)imageDataArray title:(NSString*)title extMap:(NSDictionary *)extMap; + +@end + +// QQApiVideoForQZoneObject +/** @brief 视频对象 + 用于分享视频到空间,走写说说路径QQApiObject,assetURL和videoData两个参数必须设置至少一个参数,如果assetURL设置了忽略videoData参数 + @param assetURL可传ALAsset的ALAssetPropertyAssetURL,或者PHAsset的localIdentifier + @param extMap 扩展字段 + @param videoData 视频数据,大小不超过50M + */ +@interface QQApiVideoForQZoneObject : QQApiObject + +@property(nonatomic, retain) NSString *assetURL; +@property(nonatomic,retain) NSDictionary* extMap; // 扩展字段 +@property(nonatomic,retain) NSData* videoData; + +- (id)initWithAssetURL:(NSString*)assetURL title:(NSString*)title extMap:(NSDictionary *)extMap; + ++ (id)objectWithAssetURL:(NSString*)assetURL title:(NSString*)title extMap:(NSDictionary *)extMap; + +- (id)initWithVideoData:(NSData*)videoData title:(NSString*)title extMap:(NSDictionary *)extMap; + ++ (id)objectWithVideoData:(NSData*)videoData title:(NSString*)title extMap:(NSDictionary *)extMap; + +@end + +// QQApiWebImageObject +/** @brief 图片对象 + 用于分享网络图片内容的对象,是一个指定网络图片url的: 该类型只在2.9.0的h5分享中才支持, + 原有的手q分享是不支持该类型的。 + */ +@interface QQApiWebImageObject : QQApiObject + +@property(nonatomic, retain) NSURL *previewImageURL; ///<预览图像URL + +/** + 初始化方法 + @param previewImageURL 用于预览的图片 + @param title 标题 + @param description 此对象,分享的描述 + */ +- (id)initWithPreviewImageURL:(NSURL*)previewImageURL title:(NSString*)title description:(NSString*)description; + +/** + helper方法获取一个autorelease的QQApiWebImageObject对象 + @param previewImageURL 用于预览的图片 + @param title 标题 + @param description 此对象,分享的描述 + */ ++ (id)objectWithPreviewImageURL:(NSURL*)previewImageURL title:(NSString*)title description:(NSString*)description; + +@end + + +//QQApiFileObject +/** @brief 本地文件对象(暂只支持分享到手机QQ数据线功能) + 用于分享文件内容的对象,是一个指定为文件类型的QQApiExtendObject + */ +@interface QQApiFileObject : QQApiExtendObject +{ + NSString* _fileName; +} +@property(nonatomic, retain)NSString* fileName; +@end + +// QQApiAudioObject +/** @brief 音频URL对象 + 用于分享目标内容为音频的URL的对象 + */ +@interface QQApiAudioObject : QQApiURLObject + +@property (nonatomic, retain) NSURL *flashURL; ///<音频URL地址,最长512个字符 + +/** + 获取一个autorelease的QQApiAudioObject + @param url 音频内容的目标URL + @param title 分享内容的标题 + @param description 分享内容的描述 + @param data 分享内容的预览图像 + @note 如果url为空,调用QQApi#sendMessage:时将返回FALSE + */ ++(id)objectWithURL:(NSURL*)url title:(NSString*)title description:(NSString*)description previewImageData:(NSData*)data; + +/** + 获取一个autorelease的QQApiAudioObject + @param url 音频内容的目标URL + @param title 分享内容的标题 + @param description 分享内容的描述 + @param previewURL 分享内容的预览图像URL + @note 如果url为空,调用QQApi#sendMessage:时将返回FALSE + */ ++(id)objectWithURL:(NSURL*)url title:(NSString*)title description:(NSString*)description previewImageURL:(NSURL*)previewURL; + +@end + +// QQApiVideoObject +/** @brief 视频URL对象 + 用于分享目标内容为视频的URL的对象 + + QQApiVideoObject类型的分享,目前在Android和PC QQ上接收消息时,展现有待完善,待手机QQ版本以后更新支持 + 目前如果要分享视频,推荐使用 QQApiNewsObject 类型 + */ +@interface QQApiVideoObject : QQApiURLObject + +@property (nonatomic, retain) NSURL *flashURL; ///<视频URL地址,最长512个字符 + +/** + 获取一个autorelease的QQApiVideoObject + @param url 视频内容的目标URL + @param title 分享内容的标题 + @param description 分享内容的描述 + @param data 分享内容的预览图像 + @note 如果url为空,调用QQApi#sendMessage:时将返回FALSE + */ ++(id)objectWithURL:(NSURL*)url title:(NSString*)title description:(NSString*)description previewImageData:(NSData*)data; + +/** + 获取一个autorelease的QQApiVideoObject + @param url 视频内容的目标URL + @param title 分享内容的标题 + @param description 分享内容的描述 + @param previewURL 分享内容的预览图像URL + @note 如果url为空,调用QQApi#sendMessage:时将返回FALSE + */ ++(id)objectWithURL:(NSURL*)url title:(NSString*)title description:(NSString*)description previewImageURL:(NSURL*)previewURL; + +@end + +// QQApiNewsObject +/** @brief 新闻URL对象 + 用于分享目标内容为新闻的URL的对象 + */ +@interface QQApiNewsObject : QQApiURLObject +/** + 获取一个autorelease的QQApiNewsObject + @param url 视频内容的目标URL + @param title 分享内容的标题 + @param description 分享内容的描述 + @param data 分享内容的预览图像 + @note 如果url为空,调用QQApi#sendMessage:时将返回FALSE + */ ++(id)objectWithURL:(NSURL*)url title:(NSString*)title description:(NSString*)description previewImageData:(NSData*)data; + +/** + 获取一个autorelease的QQApiNewsObject + @param url 视频内容的目标URL + @param title 分享内容的标题 + @param description 分享内容的描述 + @param previewURL 分享内容的预览图像URL + @note 如果url为空,调用QQApi#sendMessage:时将返回FALSE + */ ++(id)objectWithURL:(NSURL*)url title:(NSString*)title description:(NSString*)description previewImageURL:(NSURL*)previewURL; + +@end + +// QQApiCommonContentObject; +/** @brief 通用模板类型对象 + 用于分享一个固定显示模板的图文混排对象 + @note 图片列表和文本列表不能同时为空 + */ +@interface QQApiCommonContentObject : QQApiObject +/** + 预定义的界面布局类型 + */ +@property(nonatomic,assign) unsigned int layoutType; +@property(nonatomic,assign) NSData* previewImageData;///<预览图 +@property(nonatomic,retain) NSArray* textArray;///<文本列表 +@property(nonatomic,retain) NSArray* pictureDataArray;///<图片列表 ++(id)objectWithLayoutType:(int)layoutType textArray:(NSArray*)textArray pictureArray:(NSArray*)pictureArray previewImageData:(NSData*)data; +/** + 将一个NSDictionary对象转化为QQApiCommomContentObject,如果无法转换,则返回空 + */ ++(id)objectWithDictionary:(NSDictionary*)dic; +-(NSDictionary*)toDictionary; +@end + +// QQApiExtraServiceObject; +/** + @brief OpenSDK扩展支持的服务,通用接口,后续会扩充能力 + @param serviceID [必选] 扩展支持的服务类型ID,参考官方文档说明 + @param openID [必选] 授权登录后对该用户的唯一标识 + @param toUin [可选] 对方的QQ号码 + @param extraInfo [可选] 扩展字段 + @note 该接口的使用须先登录 + */ +@interface QQApiExtraServiceObject : QQApiObject +@property (nonatomic,retain) NSString* serviceID; +@property (nonatomic,retain) NSString* openID; +@property (nonatomic,retain) NSString* toUin; +@property (nonatomic,retain) NSDictionary* extraInfo; + +- (id)initWithOpenID:(NSString *)openID serviceID:(NSString *)serviceID; ++ (id)objecWithOpenID:(NSString *)openID serviceID:(NSString *)serviceID; +@end + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Ad item object definition +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** @brief 广告数据对象 + */ +@interface QQApiAdItem : NSObject +@property(nonatomic,retain) NSString* title; ///<名称 +@property(nonatomic,retain) NSString* description;///<描述 +@property(nonatomic,retain) NSData* imageData;///<广告图片 +@property(nonatomic,retain) NSURL* target;///<广告目标链接 +@end + + +#pragma mark - QQApi请求消息类型 + +/** + QQApi请求消息类型 + */ +typedef NS_ENUM(NSUInteger, QQApiInterfaceReqType) { + EGETMESSAGEFROMQQREQTYPE = 0, ///< 手Q -> 第三方应用,请求第三方应用向手Q发送消息 + ESENDMESSAGETOQQREQTYPE = 1, ///< 第三方应用 -> 手Q,第三方应用向手Q分享消息 + ESHOWMESSAGEFROMQQREQTYPE = 2, ///< 手Q -> 第三方应用,请求第三方应用展现消息中的数据 + ESENDMESSAGEARKTOQQREQTYPE = 3, ///< 第三方应用 -> 手Q,第三方应用向手Q分享Ark消息 + ESENDMESSAGE_MINI_TOQQREQTYPE = 4 ///< 第三方应用 -> 手Q,第三方应用向手Q分享小程序消息 +}; + +/** + QQApi应答消息类型 + */ +typedef NS_ENUM(NSUInteger, QQApiInterfaceRespType) { + ESHOWMESSAGEFROMQQRESPTYPE = 0, ///< 第三方应用 -> 手Q,第三方应用应答消息展现结果 + EGETMESSAGEFROMQQRESPTYPE = 1, ///< 第三方应用 -> 手Q,第三方应用回应发往手Q的消息 + ESENDMESSAGETOQQRESPTYPE = 2 ///< 手Q -> 第三方应用,手Q应答处理分享消息的结果 +}; + +/** + QQApi请求消息基类 + */ +@interface QQBaseReq : NSObject + +/** 请求消息类型,参见\ref QQApiInterfaceReqType */ +@property (nonatomic, assign) int type; + +@end + +/** + QQApi应答消息基类 + */ +@interface QQBaseResp : NSObject + +/** 请求处理结果 */ +@property (nonatomic, copy) NSString* result; + +/** 具体错误描述信息 */ +@property (nonatomic, copy) NSString* errorDescription; + +/** 应答消息类型,参见\ref QQApiInterfaceRespType */ +@property (nonatomic, assign) int type; + +/** 扩展信息 */ +@property (nonatomic, assign) NSString* extendInfo; + +@end + +/** + GetMessageFromQQReq请求帮助类 + */ +@interface GetMessageFromQQReq : QQBaseReq + +/** + 创建一个GetMessageFromQQReq请求实例 + */ ++ (GetMessageFromQQReq *)req; + +@end + +@interface SendMessageToQQReq : QQBaseReq + +/** + 创建一个SendMessageToQQReq请求实例 + \param message 具体分享消息实例 + \return 新创建的SendMessageToQQReq请求实例 + */ ++ (SendMessageToQQReq *)reqWithContent:(QQApiObject *)message; + +/** + 创建一个支持Ark的SendMessageToQQReq请求实例 + \param message 具体分享消息实例 + \return 新创建的SendMessageToQQReq请求实例 + */ ++ (SendMessageToQQReq *)reqWithArkContent:(ArkObject *)message; +/** + * 创建一个支持小程序的消息请求实例 + * @param miniMessage 小程序实例对象 + * @return 消息请求实例 + */ ++(SendMessageToQQReq*) reqWithMiniContent:(QQApiMiniProgramObject *)miniMessage; +/** 具体分享消息 */ +@property (nonatomic, retain) QQApiObject *message; + +/** 支持Ark的具体分享消息 */ +@property (nonatomic, retain) ArkObject *arkMessage; +/** 支持小程序的具体分享消息 */ +@property (nonatomic, retain) QQApiMiniProgramObject *miniMessage; +@end + +/** + SendMessageToQQResp应答帮助类 + */ +@interface SendMessageToQQResp : QQBaseResp + +/** + 创建一个SendMessageToQQResp应答实例 + \param result 请求处理结果 + \param errDesp 具体错误描述信息 + \param extendInfo 扩展信息 + \return 新创建的SendMessageToQQResp应答实例 + */ ++ (SendMessageToQQResp *)respWithResult:(NSString *)result errorDescription:(NSString *)errDesp extendInfo:(NSString*)extendInfo; + +@end + +/** + ShowMessageFromQQReq请求帮助类 + */ +@interface ShowMessageFromQQReq : QQBaseReq + +/** + 创建一个ShowMessageFromQQReq请求实例 + \param message 具体待展现消息实例 + \return 新创建的ShowMessageFromQQReq请求实例 + */ ++ (ShowMessageFromQQReq *)reqWithContent:(QQApiObject *)message; + +/** 具体待展现消息 */ +@property (nonatomic, retain) QQApiObject *message; + +@end + + +#endif diff --git a/ios/Libraries/TencentOpenAPI.framework/Headers/TencentOAuth.h b/ios/Libraries/TencentOpenAPI.framework/Headers/TencentOAuth.h new file mode 100644 index 0000000..1bb4189 --- /dev/null +++ b/ios/Libraries/TencentOpenAPI.framework/Headers/TencentOAuth.h @@ -0,0 +1,494 @@ +/// +/// \file TencentOAuth.h +/// \brief QQ互联开放平台授权登录及相关开放接口实现类 +/// +/// Created by Tencent on 12-12-21. +/// Copyright (c) 2012年 Tencent. All rights reserved. +/// + +#import +#import "sdkdef.h" + +@protocol TencentSessionDelegate; +@protocol TencentLoginDelegate; +@protocol TencentApiInterfaceDelegate; +@protocol TencentWebViewDelegate; + +@class TencentApiReq; +@class TencentApiResp; + +typedef NS_ENUM(NSUInteger, TencentAuthorizeState) { + kTencentNotAuthorizeState, + kTencentSSOAuthorizeState, + kTencentWebviewAuthorzieState, +}; + +typedef NS_ENUM(NSUInteger, TencentAuthMode) { + kAuthModeClientSideToken, + kAuthModeServerSideCode, +}; + +#pragma mark - TencentOAuth(授权登录及相关开放接口调用) + +/** + * \brief TencentOpenAPI授权登录及相关开放接口调用 + * + * TencentOAuth实现授权登录逻辑以及相关开放接口的请求调用 + */ +@interface TencentOAuth : NSObject +{ + NSMutableDictionary* _apiRequests; + NSString* _accessToken; + NSDate* _expirationDate; + id _sessionDelegate; + NSString* _localAppId; + NSString* _openId; + NSString* _redirectURI; + NSArray* _permissions; +} + +/** Access Token凭证,用于后续访问各开放接口 */ +@property(nonatomic, copy) NSString* accessToken; + +/** Access Token的失效期 */ +@property(nonatomic, copy) NSDate* expirationDate; + +/** 已实现的开放接口的回调委托对象 */ +@property(nonatomic, assign) id sessionDelegate; + +/** 第三方应用在开发过程中设置的URLSchema,用于浏览器登录后后跳到第三方应用 */ +@property(nonatomic, copy) NSString* localAppId; + +/** 用户授权登录后对该用户的唯一标识 */ +@property(nonatomic, copy) NSString* openId; + +/** 用户登录成功过后的跳转页面地址 */ +@property(nonatomic, copy) NSString* redirectURI; + +/** 第三方应用在互联开放平台申请的appID */ +@property(nonatomic, retain) NSString* appId; + +/** 第三方应用在互联开放平台注册的UniversalLink */ +@property(nonatomic, retain) NSString* universalLink; + +/** 主要是互娱的游戏设置uin */ +@property(nonatomic, retain) NSString* uin; + +/** 主要是互娱的游戏设置鉴定票据 */ +@property(nonatomic, retain) NSString* skey; + +/** 登陆透传的数据 */ +@property(nonatomic, copy) NSDictionary* passData; + +/** 授权方式(Client Side Token或者Server Side Code) */ +@property(nonatomic, assign) TencentAuthMode authMode; + +/** union id */ +@property(nonatomic, retain) NSString* unionid; + +/** 第三方在授权登录/分享 时选择 QQ,还是TIM 。在授权前一定要指定其中一个类型*/ +@property(nonatomic, assign) TencentAuthShareType authShareType; + +/** + * 获取上次登录得到的token + * + **/ +- (NSString *)getCachedToken; + +/** + * 获取上次登录得到的openid + * + **/ +- (NSString *)getCachedOpenID; + +/** + * 获取上次登录的token过期日期 + * + **/ +- (NSDate *)getCachedExpirationDate; + +/** + * 上次登录的token是否过期(本地判断) + **/ +- (BOOL)isCachedTokenValid; + +/** + * 删除上次登录登录的token信息 + * + **/ +- (BOOL)deleteCachedToken; + +/** + * 用来获得当前sdk的版本号 + * \return 返回sdk版本号 + **/ + ++ (NSString*)sdkVersion; + +/** + * 用来获得当前sdk的小版本号 + * \return 返回sdk小版本号 + **/ + ++ (NSString*)sdkSubVersion; + +/** + * 用来获得当前sdk的是否精简版 + * \return 返回YES表示精简版 + **/ + ++ (BOOL)isLiteSDK; + +/** + * 主要是用来帮助判断是否有登陆被发起,但是还没有过返回结果 + * \return + * kTencentNotAuthorizeState:无授权 + * kTencentSSOAuthorizeState:有人发起了sso授权但无返回 + * kTencentWebviewAuthorzieState:有人发起了webview授权还未返回 + **/ + ++ (TencentAuthorizeState *)authorizeState; + +/** + * 用来获得当前手机qq的版本号 + * \return 返回手机qq版本号 + **/ ++ (int)iphoneQQVersion __attribute__((deprecated("已过期, 建议删除调用"))); + + +/** + * 用来获得当前手机TIM的版本号 + * \return 返回手机qq版本号 + **/ ++ (int)iphoneTIMVersion __attribute__((deprecated("已过期, 建议删除调用"))); + +/** + * 初始化TencentOAuth对象 + * \param appId 第三方应用在互联开放平台申请的唯一标识 + * \param delegate 第三方应用用于接收请求返回结果的委托对象 + * \return 初始化后的授权登录对象 + */ +- (id)initWithAppId:(NSString *)appId + andDelegate:(id)delegate; + +/** +* 初始化TencentOAuth对象(>=3.3.7) +* \param appId 第三方应用在互联开放平台申请的唯一标识 +* \param universalLink 第三方应用在互联开放平台注册的universallink,和bundleID一一对应 +* \param delegate 第三方应用用于接收请求返回结果的委托对象 +* \return 初始化后的授权登录对象 +*/ +- (id)initWithAppId:(NSString *)appId + andUniversalLink:(NSString *)universalLink + andDelegate:(id)delegate; + +/** + * 判断用户手机上是否安装手机QQ + * \return YES:安装 NO:没安装 + * + * \note SDK目前已经支持QQ、TIM授权登录及分享功能, 会按照QQ>TIM的顺序进行调用。 + * 只要用户安装了QQ、TIM中任意一个应用,都可为第三方应用进行授权登录、分享功能。 + * 第三方应用在接入SDK时不需要判断是否安装QQ、TIM。若有判断安装QQ、TIM的逻辑建议移除。 + */ ++ (BOOL)iphoneQQInstalled; + +/** + * 判断用户手机上是否安装手机TIM + * \return YES:安装 NO:没安装 + * + * \note SDK目前已经支持QQ、TIM授权登录及分享功能, 会按照QQ>TIM的顺序进行调用。 + * 只要用户安装了QQ、TIM中任意一个应用,都可为第三方应用进行授权登录、分享功能。 + * 第三方应用在接入SDK时不需要判断是否安装QQ、TIM。若有判断安装QQ、TIM的逻辑建议移除。 + */ ++ (BOOL)iphoneTIMInstalled; + +/** + * 判断用户手机上的手机QQ是否支持SSO登录 + * \return YES:支持 NO:不支持 + */ ++ (BOOL)iphoneQQSupportSSOLogin __attribute__((deprecated("QQ版本均支持SSO登录。该接口已过期, 建议删除调用"))); + +/** + * 判断用户手机上的手机TIM是否支持SSO登录 + * \return YES:支持 NO:不支持 + */ ++ (BOOL)iphoneTIMSupportSSOLogin __attribute__((deprecated("TIM版本均支持SSO登录。该接口已过期, 建议删除调用"))); + +/** + * 登录授权 + * + * \param permissions 授权信息列 + */ +- (BOOL)authorize:(NSArray *)permissions; + +/** + * 登录授权 + * \param permissions 授权信息列表 + * \param bInSafari 是否使用safari进行登录.IOS SDK 1.3版本开始此参数废除 + */ +- (BOOL)authorize:(NSArray *)permissions + inSafari:(BOOL)bInSafari; + +/** + * 登录授权 + * \param permissions 授权信息列表 + * \param localAppId 应用APPID + * \param bInSafari 是否使用safari进行登录.IOS SDK 1.3版本开始此参数废除 + */ +- (BOOL)authorize:(NSArray *)permissions + localAppId:(NSString *)localAppId + inSafari:(BOOL)bInSafari; + +/** + * 登录授权 + * + * \param permissions 授权信息列 + */ +- (BOOL)authorizeWithQRlogin:(NSArray *)permissions; + +/** + * 增量授权,因用户没有授予相应接口调用的权限,需要用户确认是否授权 + * \param permissions 需增量授权的信息列表 + * \return 增量授权调用是否成功 + */ +- (BOOL)incrAuthWithPermissions:(NSArray *)permissions; + +/** + * 重新授权,因token废除或失效导致接口调用失败,需用户重新授权 + * \param permissions 授权信息列表,同登录授权 + * \return 授权调用是否成功 + */ +- (BOOL)reauthorizeWithPermissions:(NSArray *)permissions; + +/** + * 获取UnindID,可以根据UnindID的比较来确定OpenID是否属于同一个用户 + * \return NO未登录,信息不足;YES条件满足,发送请求成功,请等待回调 + */ +- (BOOL)RequestUnionId; + +/** + * (静态方法)处理应用拉起协议 + * \param url 处理被其他应用呼起时的逻辑 + * \return 处理结果,YES表示成功,NO表示失败 + */ ++ (BOOL)HandleOpenURL:(NSURL *)url; + +/** + * (静态方法)sdk是否可以处理应用拉起协议 + * \param url 处理被其他应用呼起时的逻辑 + * \return 处理结果,YES表示可以 NO表示不行 + */ ++ (BOOL)CanHandleOpenURL:(NSURL *)url; + +/** + * (静态方法)处理应用的UniversalLink拉起协议 + * \param url 处理被其他应用呼起时的逻辑 + * \return 处理结果,YES表示成功,NO表示失败 + */ ++ (BOOL)HandleUniversalLink:(NSURL *)url; + +/** + * (静态方法)sdk是否可以处理应用的Universallink拉起协议 + * \param url 处理被其他应用呼起时的逻辑(应用的Universallink链接须满足官网注册时的格式要求) + * \return 处理结果,YES表示可以 NO表示不行 + * 注:在调用其他Universallink相关处理接口之前,均需进行此项判断 + */ ++ (BOOL)CanHandleUniversalLink:(NSURL *)url; + +/** + * (静态方法)获取TencentOAuth调用的上一次错误信息 + */ ++ (NSString *)getLastErrorMsg; + +/** + * 以Server Side Code模式授权登录时,通过此接口获取返回的code值; + * 以Client Side Token模式授权登录时,忽略此接口。 + */ +- (NSString *)getServerSideCode; + +/** + * 退出登录(退出登录后,TecentOAuth失效,需要重新初始化) + * \param delegate 第三方应用用于接收请求返回结果的委托对象 + */ +- (void)logout:(id)delegate; + +/** + * 判断登录态是否有效 + * \return 处理结果,YES表示有效,NO表示无效,请用户重新登录授权 + */ +- (BOOL)isSessionValid; + +/** + * 获取用户个人信息 + * \return 处理结果,YES表示API调用成功,NO表示API调用失败,登录态失败,重新登录 + */ +- (BOOL)getUserInfo; + +/** + * 退出指定API调用 + * \param userData 用户调用某条API的时候传入的保留参数 + * \return 处理结果,YES表示成功 NO表示失败 + */ +- (BOOL)cancel:(id)userData; + +/** + * CGI类任务创建接口 + * \param apiURL CGI请求的URL地址 + * \param method CGI请求方式:"GET","POST" + * \param params CGI请求参数字典 + * \param callback CGI请求结果的回调接口对象 + * \return CGI请求任务实例,用于取消任务,返回nil代表任务创建失败 + */ +- (TCAPIRequest *)cgiRequestWithURL:(NSURL *)apiURL method:(NSString *)method params:(NSDictionary *)params callback:(id)callback; + +/** + * TencentOpenApi发送任务统一接口 + * \param request 请求发送的任务 + * \param callback 任务发送后的回调地址 + */ +- (BOOL)sendAPIRequest:(TCAPIRequest *)request callback:(id)callback; + +- (NSString *)getUserOpenID; + +@end + +#pragma mark - TencentLoginDelegate(授权登录回调协议) + +/** + * \brief TencentLoginDelegate iOS Open SDK 1.3 API回调协议 + * + * 第三方应用实现登录的回调协议 + */ +@protocol TencentLoginDelegate + +@required + +/** + * 登录成功后的回调 + */ +- (void)tencentDidLogin; + +/** + * 登录失败后的回调 + * \param cancelled 代表用户是否主动退出登录 + */ +- (void)tencentDidNotLogin:(BOOL)cancelled; + +/** + * 登录时网络有问题的回调 + */ +- (void)tencentDidNotNetWork; + +@optional +/** + * 登录时权限信息的获得 + */ +- (NSArray *)getAuthorizedPermissions:(NSArray *)permissions withExtraParams:(NSDictionary *)extraParams; + +/** + * unionID获得 + */ +- (void)didGetUnionID; + +/** + * 强制网页登录,包括账号密码登录和二维码登录 + * return YES时,就算本地有手Q也会打开web界面 + */ +- (BOOL)forceWebLogin; +@end + +#pragma mark - TencentSessionDelegate(开放接口回调协议) + +/** + * \brief TencentSessionDelegate iOS Open SDK 1.3 API回调协议 + * + * 第三方应用需要实现每条需要调用的API的回调协议 + */ +@protocol TencentSessionDelegate + +@optional + +/** + * 退出登录的回调 + */ +- (void)tencentDidLogout; + +/** + * 因用户未授予相应权限而需要执行增量授权。在用户调用某个api接口时,如果服务器返回操作未被授权,则触发该回调协议接口,由第三方决定是否跳转到增量授权页面,让用户重新授权。 + * \param tencentOAuth 登录授权对象。 + * \param permissions 需增量授权的权限列表。 + * \return 是否仍然回调返回原始的api请求结果。 + * \note 不实现该协议接口则默认为不开启增量授权流程。若需要增量授权请调用\ref TencentOAuth#incrAuthWithPermissions: \n注意:增量授权时用户可能会修改登录的帐号 + */ +- (BOOL)tencentNeedPerformIncrAuth:(TencentOAuth *)tencentOAuth withPermissions:(NSArray *)permissions; + +/** + * [该逻辑未实现]因token失效而需要执行重新登录授权。在用户调用某个api接口时,如果服务器返回token失效,则触发该回调协议接口,由第三方决定是否跳转到登录授权页面,让用户重新授权。 + * \param tencentOAuth 登录授权对象。 + * \return 是否仍然回调返回原始的api请求结果。 + * \note 不实现该协议接口则默认为不开启重新登录授权流程。若需要重新登录授权请调用\ref TencentOAuth#reauthorizeWithPermissions: \n注意:重新登录授权时用户可能会修改登录的帐号 + */ +- (BOOL)tencentNeedPerformReAuth:(TencentOAuth *)tencentOAuth; + +/** + * 用户通过增量授权流程重新授权登录,token及有效期限等信息已被更新。 + * \param tencentOAuth token及有效期限等信息更新后的授权实例对象 + * \note 第三方应用需更新已保存的token及有效期限等信息。 + */ +- (void)tencentDidUpdate:(TencentOAuth *)tencentOAuth; + +/** + * 用户增量授权过程中因取消或网络问题导致授权失败 + * \param reason 授权失败原因,具体失败原因参见sdkdef.h文件中\ref UpdateFailType + */ +- (void)tencentFailedUpdate:(UpdateFailType)reason; + +/** + * 获取用户个人信息回调 + * \param response API返回结果,具体定义参见sdkdef.h文件中\ref APIResponse + * \remarks 正确返回示例: \snippet example/getUserInfoResponse.exp success + * 错误返回示例: \snippet example/getUserInfoResponse.exp fail + */ +- (void)getUserInfoResponse:(APIResponse*) response; + +/** + * 社交API统一回调接口 + * \param response API返回结果,具体定义参见sdkdef.h文件中\ref APIResponse + * \param message 响应的消息,目前支持‘SendStory’,‘AppInvitation’,‘AppChallenge’,‘AppGiftRequest’ + */ +- (void)responseDidReceived:(APIResponse*)response forMessage:(NSString *)message; + +/** + * post请求的上传进度 + * \param tencentOAuth 返回回调的tencentOAuth对象 + * \param bytesWritten 本次回调上传的数据字节数 + * \param totalBytesWritten 总共已经上传的字节数 + * \param totalBytesExpectedToWrite 总共需要上传的字节数 + * \param userData 用户自定义数据 + */ +- (void)tencentOAuth:(TencentOAuth *)tencentOAuth didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite userData:(id)userData; + + +/** + * 通知第三方界面需要被关闭 + * \param tencentOAuth 返回回调的tencentOAuth对象 + * \param viewController 需要关闭的viewController + */ +- (void)tencentOAuth:(TencentOAuth *)tencentOAuth doCloseViewController:(UIViewController *)viewController; + +@end + +#pragma mark - TencentWebViewDelegate(H5登录webview旋转方向回调) + +/** + * \brief TencentWebViewDelegate: H5登录webview旋转方向回调协议 + * + * 第三方应用可以根据自己APP的旋转方向限制,通过此协议设置 + */ +@protocol TencentWebViewDelegate +@optional +- (BOOL) tencentWebViewShouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation; +- (NSUInteger) tencentWebViewSupportedInterfaceOrientationsWithWebkit; +- (BOOL) tencentWebViewShouldAutorotateWithWebkit; +@end diff --git a/ios/Libraries/TencentOpenAPI.framework/Headers/sdkdef.h b/ios/Libraries/TencentOpenAPI.framework/Headers/sdkdef.h new file mode 100644 index 0000000..fc112a7 --- /dev/null +++ b/ios/Libraries/TencentOpenAPI.framework/Headers/sdkdef.h @@ -0,0 +1,414 @@ +/// +/// \file sdkdef.h +/// \brief SDK中相关常量定义 +/// +/// Created by Tencent on 12-12-25. +/// Copyright (c) 2012年 Tencent. All rights reserved. +/// + +#import +#import + +/** + * \brief 设置sdk的log等级 + */ +typedef enum { + TCOLogLevel_Disabled = -1, // 关闭所有log + TCOLogLevel_Error = 0, + TCOLogLevel_Warning, + TCOLogLevel_Info, + TCOLogLevel_Debug, +} TCOLogLevel; + +/** + * \breif 授权/分享 方式 + */ +typedef enum TencentAuthShareType { + AuthShareType_QQ, + AuthShareType_TIM, +}TencentAuthShareType; + +/** + * \brief APIResponse.retCode可能的枚举常量 + */ +typedef enum +{ + URLREQUEST_SUCCEED = 0, /**< 网络请求成功发送至服务器,并且服务器返回数据格式正确 + * \note 这里包括所请求业务操作失败的情况,例如没有授权等原因导致 + */ + + URLREQUEST_FAILED = 1, /**< 网络异常,或服务器返回的数据格式不正确导致无法解析 */ +} REPONSE_RESULT; + +/** + * \brief 增量授权失败原因 + * + * \note 增量授权失败不影响原token的有效性(原token已失效的情况除外) + */ +typedef enum +{ + kUpdateFailUnknown = 1, ///< 未知原因 + kUpdateFailUserCancel, ///< 用户取消 + kUpdateFailNetwork, ///< 网络问题 +} UpdateFailType; + +/** + * \brief 封装服务器返回的结果 + * + * APIResponse用于封装所有请求的返回结果,包括错误码、错误信息、原始返回数据以及返回数据的json格式字典 + */ +@interface APIResponse : NSObject { + int _detailRetCode; + int _retCode; + int _seq; + NSString *_errorMsg; + NSDictionary *_jsonResponse; + NSString *_message; + id _userData; +} + +/** + * 新增的详细错误码\n + * detailRetCode主要用于区分不同的错误情况,参见\ref OpenSDKError + */ +@property (nonatomic, assign) int detailRetCode; + +/** + * 网络请求是否成功送达服务器,以及服务器返回的数据格式是否正确\n + * retCode具体取值可参考\ref REPONSE_RESULT + */ +@property (nonatomic, assign) int retCode; + +/** + * 网络请求对应的递增序列号,方便内部管理 + */ +@property (nonatomic, assign) int seq; + +/** + * 错误提示语 + */ +@property (nonatomic, retain) NSString *errorMsg; + +/** + * 服务器返回数据的json格式字典\n + * 字典内具体参数的命名和含义请参考\ref api_spec + */ +@property (nonatomic, retain) NSDictionary *jsonResponse; + +/** + * 服务器返回的原始数据字符串 + */ +@property (nonatomic, retain) NSString *message; + +/** + * 用户保留数据 + */ +@property (nonatomic, retain) id userData; + +@end + + +/** + * 用户自定义的保留字段 + */ +FOUNDATION_EXTERN NSString * const PARAM_USER_DATA; + +/** + * \name 应用邀请参数字段定义 + */ +///@{ + +/** 应用邀请展示图片url的key */ +FOUNDATION_EXTERN NSString * const PARAM_APP_ICON; + +/** 应用邀请描述文本的key */ +FOUNDATION_EXTERN NSString * const PARAM_APP_DESC; + +/** 应用邀请好友列表的key */ +FOUNDATION_EXTERN NSString * const PARAM_APP_INVITED_OPENIDS; + +///@} + +/** + * \name sendStory新分享参数字段定义 + */ +///@{ + +/** 预填入接受人列表的key */ +FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_RECEIVER; + +/** 分享feeds标题的key */ +FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_TITLE; + +/** 分享feeds评论内容的key */ +FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_COMMENT; + +/** 分享feeds摘要的key */ +FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_SUMMARY; + +/** 分享feeds展示图片url的key */ +FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_IMAGE; + +/** 分享feeds跳转链接url的key */ +FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_URL; + +/** 分享feeds点击操作默认行为的key */ +FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_ACT; + +///@} + +/** + * \name 设置头像参数字段定义 + */ +///@{ + +/** 头像图片数据的key */ +FOUNDATION_EXTERN NSString * const PARAM_SETUSERHEAD_PIC; + +/** 头像图片文件名的key */ +FOUNDATION_EXTERN NSString * const PARAM_SETUSERHEAD_FILENAME; + +///@} + +/** + * \name 服务器返回数据的参数字段定义 + */ +///@{ + +/** 服务器返回码的key */ +FOUNDATION_EXTERN NSString * const PARAM_RETCODE; + +/** 服务器返回错误信息的key */ +FOUNDATION_EXTERN NSString * const PARAM_MESSAGE; + +/** 服务器返回额外数据的key */ +FOUNDATION_EXTERN NSString * const PARAM_DATA; + +///@} + +/** + * \name 错误信息相关常量定义 + */ +///@{ + +/** 详细错误信息字典中额外信息的key */ +FOUNDATION_EXTERN NSString * const TCOpenSDKErrorKeyExtraInfo; + +/** 详细错误信息字典中返回码的key */ +FOUNDATION_EXTERN NSString * const TCOpenSDKErrorKeyRetCode; + +/** 详细错误信息字典中错误语句的key */ +FOUNDATION_EXTERN NSString * const TCOpenSDKErrorKeyMsg; + +/** 不支持的接口 */ +FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgUnsupportedAPI; + +/** 操作成功 */ +FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgSuccess; + +/** 未知错误 */ +FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgUnknown; + +/** 用户取消 */ +FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgUserCancel; + +/** 请重新登录 */ +FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgReLogin; + +/** 应用没有操作权限 */ +FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgOperationDeny; + +/** 网络异常或没有网络 */ +FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgNetwork; + +/** URL格式或协议错误 */ +FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgURL; + +/** 解析数据出错 */ +FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgDataParse; + +/** 传入参数有误 */ +FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgParam; + +/** 连接超时 */ +FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgTimeout; + +/** 安全问题 */ +FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgSecurity; + +/** 文件读写错误 */ +FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgIO; + +/** 服务器端错误 */ +FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgServer; + +/** 页面错误 */ +FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgWebPage; + +/** 设置头像图片过大 */ +FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgUserHeadPicLarge; + +///@} + +/** + * \brief SDK新增详细错误常量 + */ +typedef enum +{ + kOpenSDKInvalid = -1, ///< 无效的错误码 + kOpenSDKErrorUnsupportedAPI = -2, ///< 不支持的接口 + + /** + * \name CommonErrorCode + * 公共错误码 + */ + ///@{ + kOpenSDKErrorSuccess = 0, ///< 成功 + kOpenSDKErrorUnknown, ///< 未知错误 + kOpenSDKErrorUserCancel, ///< 用户取消 + kOpenSDKErrorReLogin, ///< token无效或用户未授权相应权限需要重新登录 + kOpenSDKErrorOperationDeny, ///< 第三方应用没有该api操作的权限 + ///@} + + /** + * \name NetworkRelatedErrorCode + * 网络相关错误码 + */ + ///@{ + kOpenSDKErrorNetwork, ///< 网络错误,网络不通或连接不到服务器 + kOpenSDKErrorURL, ///< URL格式或协议错误 + kOpenSDKErrorDataParse, ///< 数据解析错误,服务器返回的数据解析出错 + kOpenSDKErrorParam, ///< 传入参数错误 + kOpenSDKErrorConnTimeout, ///< http连接超时 + kOpenSDKErrorSecurity, ///< 安全问题 + kOpenSDKErrorIO, ///< 下载和文件IO错误 + kOpenSDKErrorServer, ///< 服务器端错误 + ///@} + + /** + * \name WebViewRelatedError + * webview特有错误 + */ + ///@{ + kOpenSDKErrorWebPage, ///< 页面错误 + ///@} + + /** + * \name SetUserHeadRelatedErrorCode + * 设置头像自定义错误码段 + */ + ///@{ + kOpenSDKErrorUserHeadPicLarge = 0x010000, ///< 图片过大 设置头像自定义错误码 + ///@} +} OpenSDKError; + +/** + * \name SDK版本(v1.3)支持的授权列表常量 + */ +///@{ + +/** 发表一条说说到QQ空间(需要申请权限) */ +FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_ADD_TOPIC; + +/** 创建一个QQ空间相册(需要申请权限) */ +FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_ADD_ALBUM; + +/** 上传一张照片到QQ空间相册(需要申请权限) */ +FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_UPLOAD_PIC; + +/** 获取用户QQ空间相册列表(需要申请权限) */ +FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_LIST_ALBUM; + +/** 验证是否认证空间粉丝 */ +FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_CHECK_PAGE_FANS; + +/** 获取登录用户自己的详细信息 */ +FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_INFO; + +/** 获取其他用户的详细信息 */ +FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_OTHER_INFO; + +/** 获取会员用户基本信息 */ +FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_VIP_INFO; + +/** 获取会员用户详细信息 */ +FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_VIP_RICH_INFO; + +/** 获取用户信息 */ +FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_USER_INFO; + +/** 移动端获取用户信息 */ +FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_SIMPLE_USER_INFO; +///@} + + +/** + * \name CGI接口相关参数类型定义 + */ + +/** 必填的字符串类型参数 */ +typedef NSString *TCRequiredStr; + +/** 必填的UIImage类型参数 */ +typedef UIImage *TCRequiredImage; + +/** 必填的整型参数 */ +typedef NSInteger TCRequiredInt; + +/** 必填的数字类型 */ +typedef NSNumber *TCRequiredNumber; + +/** 必填的NSData参数 */ +typedef NSData *TCRequiredData; + +/** 可选的字符串类型参数 */ +typedef NSString *TCOptionalStr; + +/** 可选的UIImage类型参数 */ +typedef UIImage *TCOptionalImage; + +/** 可选的整型参数 */ +typedef NSInteger TCOptionalInt; + +/** 可选的数字类型 */ +typedef NSNumber *TCOptionalNumber; + +/** 可选的不定类型参数 */ +typedef id TCRequiredId; +///@} + + +/** + * \brief CGI请求的参数字典封装辅助基类 + * + * 将相应属性的值以key-value的形式保存到参数字典中 + */ +@interface TCAPIRequest : NSMutableDictionary + +/** CGI请求的URL地址 */ +@property (nonatomic, readonly) NSURL *apiURL; + +/** CGI请求方式:"GET","POST" */ +@property (nonatomic, readonly) NSString *method; + +/** + * API参数中的保留字段,可以塞入任意字典支持的类型,再调用完成后会带回给调用方 + */ +@property (nonatomic, retain) TCRequiredId paramUserData; + +/** + * APIResponse,API的返回结果 + */ +@property (nonatomic, readonly) APIResponse *response; + +/** 取消相应的CGI请求任务 */ +- (void)cancel; + +@end + +@protocol TCAPIRequestDelegate +@optional +- (void)cgiRequest:(TCAPIRequest *)request didResponse:(APIResponse *)response; + +@end + diff --git a/ios/Libraries/TencentOpenAPI.framework/TencentOpenAPI b/ios/Libraries/TencentOpenAPI.framework/TencentOpenAPI new file mode 100644 index 0000000..d25edee Binary files /dev/null and b/ios/Libraries/TencentOpenAPI.framework/TencentOpenAPI differ diff --git a/ios/fake_tencent.podspec b/ios/tencent_kit.podspec similarity index 53% rename from ios/fake_tencent.podspec rename to ios/tencent_kit.podspec index dc981cc..594c670 100644 --- a/ios/fake_tencent.podspec +++ b/ios/tencent_kit.podspec @@ -2,11 +2,11 @@ # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html # Pod::Spec.new do |s| - s.name = 'fake_tencent' - s.version = '0.0.1' - s.summary = 'A new Flutter plugin.' + s.name = 'tencent_kit' + s.version = '1.0.0' + s.summary = 'A powerful Flutter plugin allowing developers to share with natvie android & iOS Tencent SDKs.' s.description = <<-DESC -A new Flutter plugin. +A powerful Flutter plugin allowing developers to share with natvie android & iOS Tencent SDKs. DESC s.homepage = 'http://example.com' s.license = { :file => '../LICENSE' } @@ -15,9 +15,10 @@ A new Flutter plugin. s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' s.dependency 'Flutter' - # 腾讯(QQ) - s.static_framework = true - s.dependency 'FakeTencent', '~> 3.3.3' + s.vendored_frameworks = 'Libraries/*.framework' + s.frameworks = 'SystemConfiguration', 'WebKit' + # s.libraries = 'stdc++' + # s.requires_arc = true s.ios.deployment_target = '8.0' end diff --git a/lib/fake_tencent.dart b/lib/fake_tencent.dart deleted file mode 100644 index 0097815..0000000 --- a/lib/fake_tencent.dart +++ /dev/null @@ -1,11 +0,0 @@ -library fake_tencent; - -export 'src/domain/tencent_login_resp.dart' hide TencentLoginRespSerializer; -export 'src/domain/tencent_resp.dart'; -export 'src/domain/tencent_share_resp.dart' hide TencentShareRespSerializer; -export 'src/domain/tencent_user_info_resp.dart' - hide TencentUserInfoRespSerializer; -export 'src/tencent.dart'; -export 'src/tencent_qzone_flag.dart'; -export 'src/tencent_scene.dart'; -export 'src/tencent_scope.dart'; diff --git a/lib/src/domain/tencent_login_resp.dart b/lib/src/domain/tencent_login_resp.dart deleted file mode 100644 index 4c01c91..0000000 --- a/lib/src/domain/tencent_login_resp.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:jaguar_serializer/jaguar_serializer.dart'; -import 'package:fake_tencent/src/domain/tencent_resp.dart'; - -part 'tencent_login_resp.jser.dart'; - -@GenSerializer(nameFormatter: toSnakeCase) -class TencentLoginRespSerializer extends Serializer - with _$TencentLoginRespSerializer {} - -class TencentLoginResp extends TencentResp { - TencentLoginResp({ - int ret, - String msg, - this.openid, - this.accessToken, - this.expiresIn, - this.createAt, - }) : super(ret: ret, msg: msg); - - final String openid; - final String accessToken; - final int expiresIn; - final int createAt; -} diff --git a/lib/src/domain/tencent_login_resp.jser.dart b/lib/src/domain/tencent_login_resp.jser.dart deleted file mode 100644 index 249c5f5..0000000 --- a/lib/src/domain/tencent_login_resp.jser.dart +++ /dev/null @@ -1,37 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'tencent_login_resp.dart'; - -// ************************************************************************** -// JaguarSerializerGenerator -// ************************************************************************** - -abstract class _$TencentLoginRespSerializer - implements Serializer { - @override - Map toMap(TencentLoginResp model) { - if (model == null) return null; - Map ret = {}; - setMapValue(ret, 'openid', model.openid); - setMapValue(ret, 'access_token', model.accessToken); - setMapValue(ret, 'expires_in', model.expiresIn); - setMapValue(ret, 'create_at', model.createAt); - setMapValue(ret, 'ret', model.ret); - setMapValue(ret, 'msg', model.msg); - return ret; - } - - @override - TencentLoginResp fromMap(Map map) { - if (map == null) return null; - final obj = new TencentLoginResp( - ret: map['ret'] as int ?? getJserDefault('ret'), - msg: map['msg'] as String ?? getJserDefault('msg'), - openid: map['openid'] as String ?? getJserDefault('openid'), - accessToken: - map['access_token'] as String ?? getJserDefault('accessToken'), - expiresIn: map['expires_in'] as int ?? getJserDefault('expiresIn'), - createAt: map['create_at'] as int ?? getJserDefault('createAt')); - return obj; - } -} diff --git a/lib/src/domain/tencent_share_resp.dart b/lib/src/domain/tencent_share_resp.dart deleted file mode 100644 index bd70986..0000000 --- a/lib/src/domain/tencent_share_resp.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:jaguar_serializer/jaguar_serializer.dart'; -import 'package:fake_tencent/src/domain/tencent_resp.dart'; - -part 'tencent_share_resp.jser.dart'; - -@GenSerializer() -class TencentShareRespSerializer extends Serializer - with _$TencentShareRespSerializer {} - -class TencentShareResp extends TencentResp { - TencentShareResp({ - int ret, - String msg, - }) : super(ret: ret, msg: msg); -} diff --git a/lib/src/domain/tencent_share_resp.jser.dart b/lib/src/domain/tencent_share_resp.jser.dart deleted file mode 100644 index 6c486ad..0000000 --- a/lib/src/domain/tencent_share_resp.jser.dart +++ /dev/null @@ -1,28 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'tencent_share_resp.dart'; - -// ************************************************************************** -// JaguarSerializerGenerator -// ************************************************************************** - -abstract class _$TencentShareRespSerializer - implements Serializer { - @override - Map toMap(TencentShareResp model) { - if (model == null) return null; - Map ret = {}; - setMapValue(ret, 'ret', model.ret); - setMapValue(ret, 'msg', model.msg); - return ret; - } - - @override - TencentShareResp fromMap(Map map) { - if (map == null) return null; - final obj = new TencentShareResp( - ret: map['ret'] as int ?? getJserDefault('ret'), - msg: map['msg'] as String ?? getJserDefault('msg')); - return obj; - } -} diff --git a/lib/src/domain/tencent_user_info_resp.dart b/lib/src/domain/tencent_user_info_resp.dart deleted file mode 100644 index de315bf..0000000 --- a/lib/src/domain/tencent_user_info_resp.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:fake_tencent/src/domain/tencent_resp.dart'; -import 'package:jaguar_serializer/jaguar_serializer.dart'; - -part 'tencent_user_info_resp.jser.dart'; - -@GenSerializer(nameFormatter: toSnakeCase) -class TencentUserInfoRespSerializer extends Serializer - with _$TencentUserInfoRespSerializer {} - -class TencentUserInfoResp extends TencentResp { - TencentUserInfoResp({ - int ret, - String msg, - this.nickname, - this.gender, - this.figureurlQq1, - this.figureurlQq2, - this.figureurl1, - this.figureurl2, - this.figureurl, - this.vip, - this.level, - this.isYellowVip, - this.isYellowYearVip, - this.yellowVipLevel, - }) : super(ret: ret, msg: msg); - - final String nickname; - - /// 男/女 - final String gender; - final String figureurlQq1; - final String figureurlQq2; - final String figureurl1; - final String figureurl2; - final String figureurl; - final String vip; - final String level; - final String isYellowVip; - final String isYellowYearVip; - final String yellowVipLevel; - - bool isMale() { - return gender == '男'; - } - - bool isFemale() { - return gender == '女'; - } - - String headImgUrl() { - if (figureurlQq2 != null && figureurlQq2.isNotEmpty) { - return figureurlQq2; - } - if (figureurlQq1 != null && figureurlQq1.isNotEmpty) { - return figureurlQq1; - } - if (figureurl2 != null && figureurl2.isNotEmpty) { - return figureurl2; - } - if (figureurl1 != null && figureurl1.isNotEmpty) { - return figureurl1; - } - return figureurl; - } -} diff --git a/lib/src/domain/tencent_user_info_resp.jser.dart b/lib/src/domain/tencent_user_info_resp.jser.dart deleted file mode 100644 index 0eef802..0000000 --- a/lib/src/domain/tencent_user_info_resp.jser.dart +++ /dev/null @@ -1,59 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'tencent_user_info_resp.dart'; - -// ************************************************************************** -// JaguarSerializerGenerator -// ************************************************************************** - -abstract class _$TencentUserInfoRespSerializer - implements Serializer { - @override - Map toMap(TencentUserInfoResp model) { - if (model == null) return null; - Map ret = {}; - setMapValue(ret, 'nickname', model.nickname); - setMapValue(ret, 'gender', model.gender); - setMapValue(ret, 'figureurl_qq_1', model.figureurlQq1); - setMapValue(ret, 'figureurl_qq_2', model.figureurlQq2); - setMapValue(ret, 'figureurl_1', model.figureurl1); - setMapValue(ret, 'figureurl_2', model.figureurl2); - setMapValue(ret, 'figureurl', model.figureurl); - setMapValue(ret, 'vip', model.vip); - setMapValue(ret, 'level', model.level); - setMapValue(ret, 'is_yellow_vip', model.isYellowVip); - setMapValue(ret, 'is_yellow_year_vip', model.isYellowYearVip); - setMapValue(ret, 'yellow_vip_level', model.yellowVipLevel); - setMapValue(ret, 'ret', model.ret); - setMapValue(ret, 'msg', model.msg); - return ret; - } - - @override - TencentUserInfoResp fromMap(Map map) { - if (map == null) return null; - final obj = new TencentUserInfoResp( - ret: map['ret'] as int ?? getJserDefault('ret'), - msg: map['msg'] as String ?? getJserDefault('msg'), - nickname: map['nickname'] as String ?? getJserDefault('nickname'), - gender: map['gender'] as String ?? getJserDefault('gender'), - figureurlQq1: - map['figureurl_qq_1'] as String ?? getJserDefault('figureurlQq1'), - figureurlQq2: - map['figureurl_qq_2'] as String ?? getJserDefault('figureurlQq2'), - figureurl1: - map['figureurl_1'] as String ?? getJserDefault('figureurl1'), - figureurl2: - map['figureurl_2'] as String ?? getJserDefault('figureurl2'), - figureurl: map['figureurl'] as String ?? getJserDefault('figureurl'), - vip: map['vip'] as String ?? getJserDefault('vip'), - level: map['level'] as String ?? getJserDefault('level'), - isYellowVip: - map['is_yellow_vip'] as String ?? getJserDefault('isYellowVip'), - isYellowYearVip: map['is_yellow_year_vip'] as String ?? - getJserDefault('isYellowYearVip'), - yellowVipLevel: map['yellow_vip_level'] as String ?? - getJserDefault('yellowVipLevel')); - return obj; - } -} diff --git a/lib/src/model/api/tencent_api_resp.dart b/lib/src/model/api/tencent_api_resp.dart new file mode 100644 index 0000000..a95e642 --- /dev/null +++ b/lib/src/model/api/tencent_api_resp.dart @@ -0,0 +1,20 @@ +import 'package:json_annotation/json_annotation.dart'; + +abstract class TencentApiResp { + TencentApiResp({ + this.ret, + this.msg, + }); + + /// 网络请求成功发送至服务器,并且服务器返回数据格式正确 + /// 这里包括所请求业务操作失败的情况,例如没有授权等原因导致 + static const int RET_SUCCESS = 0; + + @JsonKey( + defaultValue: RET_SUCCESS, + ) + final int ret; + final String msg; + + bool isSuccessful() => ret == RET_SUCCESS; +} diff --git a/lib/src/model/api/tencent_unionid_resp.dart b/lib/src/model/api/tencent_unionid_resp.dart new file mode 100644 index 0000000..31a320e --- /dev/null +++ b/lib/src/model/api/tencent_unionid_resp.dart @@ -0,0 +1,36 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'tencent_unionid_resp.g.dart'; + +@JsonSerializable( + anyMap: true, + explicitToJson: true, + fieldRename: FieldRename.snake, +) +class TencentUnionidResp { + TencentUnionidResp({ + this.error, + this.errorDescription, + this.clientId, + this.openid, + this.unionid, + }); + + factory TencentUnionidResp.fromJson(Map json) => + _$TencentUnionidRespFromJson(json); + + @JsonKey( + defaultValue: ERROR_SUCCESS, + ) + final int error; + final String errorDescription; + final String clientId; + final String openid; + final String unionid; + + static const int ERROR_SUCCESS = 0; + + bool isSuccessful() => error == ERROR_SUCCESS; + + Map toJson() => _$TencentUnionidRespToJson(this); +} diff --git a/lib/src/model/api/tencent_unionid_resp.g.dart b/lib/src/model/api/tencent_unionid_resp.g.dart new file mode 100644 index 0000000..7c07ba6 --- /dev/null +++ b/lib/src/model/api/tencent_unionid_resp.g.dart @@ -0,0 +1,26 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'tencent_unionid_resp.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +TencentUnionidResp _$TencentUnionidRespFromJson(Map json) { + return TencentUnionidResp( + error: json['error'] as int ?? 0, + errorDescription: json['error_description'] as String, + clientId: json['client_id'] as String, + openid: json['openid'] as String, + unionid: json['unionid'] as String, + ); +} + +Map _$TencentUnionidRespToJson(TencentUnionidResp instance) => + { + 'error': instance.error, + 'error_description': instance.errorDescription, + 'client_id': instance.clientId, + 'openid': instance.openid, + 'unionid': instance.unionid, + }; diff --git a/lib/src/model/api/tencent_user_info_resp.dart b/lib/src/model/api/tencent_user_info_resp.dart new file mode 100644 index 0000000..50fae19 --- /dev/null +++ b/lib/src/model/api/tencent_user_info_resp.dart @@ -0,0 +1,99 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:tencent_kit/src/model/api/tencent_api_resp.dart'; + +part 'tencent_user_info_resp.g.dart'; + +@JsonSerializable( + anyMap: true, + explicitToJson: true, + fieldRename: FieldRename.snake, +) +class TencentUserInfoResp extends TencentApiResp { + TencentUserInfoResp({ + int ret, + String msg, + this.isLost, + this.nickname, + this.gender, + this.genderType, + this.province, + this.city, + this.year, + this.constellation, + this.figureurl, + this.figureurl1, + this.figureurl2, + this.figureurlQq, + this.figureurlQq1, + this.figureurlQq2, + this.figureurlType, + this.isYellowVip, + this.vip, + this.yellowVipLevel, + this.level, + this.isYellowYearVip, + }) : super(ret: ret, msg: msg); + + factory TencentUserInfoResp.fromJson(Map json) => + _$TencentUserInfoRespFromJson(json); + + final int isLost; + final String nickname; + final String gender; // 男/女 + final int genderType; // 男/女 - 1 + final String province; + final String city; + final String year; + final String constellation; + final String figureurl; + @JsonKey( + name: 'figureurl_1', + ) + final String figureurl1; + @JsonKey( + name: 'figureurl_2', + ) + final String figureurl2; + final String figureurlQq; // 140 * 140 + @JsonKey( + name: 'figureurl_qq_1', + ) + final String figureurlQq1; // 大小为40×40像素的QQ头像URL。 + @JsonKey( + name: 'figureurl_qq_2', + ) + final String + figureurlQq2; // 大小为100×100像素的QQ头像URL。需要注意,不是所有的用户都拥有QQ的100x100的头像,但40x40像素则是一定会有。 + final String figureurlType; + final String isYellowVip; + final String vip; + final String yellowVipLevel; + final String level; + final String isYellowYearVip; + + bool isMale() { + return gender == '男'; + } + + bool isFemale() { + return gender == '女'; + } + + String headImgUrl() { + if (figureurlQq2 != null && figureurlQq2.isNotEmpty) { + return figureurlQq2; + } + if (figureurlQq1 != null && figureurlQq1.isNotEmpty) { + return figureurlQq1; + } + if (figureurl2 != null && figureurl2.isNotEmpty) { + return figureurl2; + } + if (figureurl1 != null && figureurl1.isNotEmpty) { + return figureurl1; + } + return figureurl; + } + + Map toJson() => _$TencentUserInfoRespToJson(this); +} diff --git a/lib/src/model/api/tencent_user_info_resp.g.dart b/lib/src/model/api/tencent_user_info_resp.g.dart new file mode 100644 index 0000000..cb688ff --- /dev/null +++ b/lib/src/model/api/tencent_user_info_resp.g.dart @@ -0,0 +1,61 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'tencent_user_info_resp.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +TencentUserInfoResp _$TencentUserInfoRespFromJson(Map json) { + return TencentUserInfoResp( + ret: json['ret'] as int ?? 0, + msg: json['msg'] as String, + isLost: json['is_lost'] as int, + nickname: json['nickname'] as String, + gender: json['gender'] as String, + genderType: json['gender_type'] as int, + province: json['province'] as String, + city: json['city'] as String, + year: json['year'] as String, + constellation: json['constellation'] as String, + figureurl: json['figureurl'] as String, + figureurl1: json['figureurl_1'] as String, + figureurl2: json['figureurl_2'] as String, + figureurlQq: json['figureurl_qq'] as String, + figureurlQq1: json['figureurl_qq_1'] as String, + figureurlQq2: json['figureurl_qq_2'] as String, + figureurlType: json['figureurl_type'] as String, + isYellowVip: json['is_yellow_vip'] as String, + vip: json['vip'] as String, + yellowVipLevel: json['yellow_vip_level'] as String, + level: json['level'] as String, + isYellowYearVip: json['is_yellow_year_vip'] as String, + ); +} + +Map _$TencentUserInfoRespToJson( + TencentUserInfoResp instance) => + { + 'ret': instance.ret, + 'msg': instance.msg, + 'is_lost': instance.isLost, + 'nickname': instance.nickname, + 'gender': instance.gender, + 'gender_type': instance.genderType, + 'province': instance.province, + 'city': instance.city, + 'year': instance.year, + 'constellation': instance.constellation, + 'figureurl': instance.figureurl, + 'figureurl_1': instance.figureurl1, + 'figureurl_2': instance.figureurl2, + 'figureurl_qq': instance.figureurlQq, + 'figureurl_qq_1': instance.figureurlQq1, + 'figureurl_qq_2': instance.figureurlQq2, + 'figureurl_type': instance.figureurlType, + 'is_yellow_vip': instance.isYellowVip, + 'vip': instance.vip, + 'yellow_vip_level': instance.yellowVipLevel, + 'level': instance.level, + 'is_yellow_year_vip': instance.isYellowYearVip, + }; diff --git a/lib/src/model/sdk/tencent_login_resp.dart b/lib/src/model/sdk/tencent_login_resp.dart new file mode 100644 index 0000000..39bb7be --- /dev/null +++ b/lib/src/model/sdk/tencent_login_resp.dart @@ -0,0 +1,34 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:tencent_kit/src/model/sdk/tencent_sdk_resp.dart'; + +part 'tencent_login_resp.g.dart'; + +@JsonSerializable( + anyMap: true, + explicitToJson: true, + fieldRename: FieldRename.snake, +) +class TencentLoginResp extends TencentSdkResp { + TencentLoginResp({ + int ret, + String msg, + this.openid, + this.accessToken, + this.expiresIn, + this.createAt, + }) : super(ret: ret, msg: msg); + + factory TencentLoginResp.fromJson(Map json) => + _$TencentLoginRespFromJson(json); + + final String openid; + final String accessToken; + final int expiresIn; + final int createAt; + + bool isExpired() { + return DateTime.now().millisecondsSinceEpoch - createAt >= expiresIn * 1000; + } + + Map toJson() => _$TencentLoginRespToJson(this); +} diff --git a/lib/src/model/sdk/tencent_login_resp.g.dart b/lib/src/model/sdk/tencent_login_resp.g.dart new file mode 100644 index 0000000..f057281 --- /dev/null +++ b/lib/src/model/sdk/tencent_login_resp.g.dart @@ -0,0 +1,28 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'tencent_login_resp.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +TencentLoginResp _$TencentLoginRespFromJson(Map json) { + return TencentLoginResp( + ret: json['ret'] as int ?? 0, + msg: json['msg'] as String, + openid: json['openid'] as String, + accessToken: json['access_token'] as String, + expiresIn: json['expires_in'] as int, + createAt: json['create_at'] as int, + ); +} + +Map _$TencentLoginRespToJson(TencentLoginResp instance) => + { + 'ret': instance.ret, + 'msg': instance.msg, + 'openid': instance.openid, + 'access_token': instance.accessToken, + 'expires_in': instance.expiresIn, + 'create_at': instance.createAt, + }; diff --git a/lib/src/domain/tencent_resp.dart b/lib/src/model/sdk/tencent_sdk_resp.dart similarity index 67% rename from lib/src/domain/tencent_resp.dart rename to lib/src/model/sdk/tencent_sdk_resp.dart index 4aacdb5..c6a9d6c 100644 --- a/lib/src/domain/tencent_resp.dart +++ b/lib/src/model/sdk/tencent_sdk_resp.dart @@ -1,8 +1,10 @@ -abstract class TencentResp { - TencentResp({ - int ret, +import 'package:json_annotation/json_annotation.dart'; + +abstract class TencentSdkResp { + TencentSdkResp({ + this.ret, this.msg, - }) : ret = ret ?? RET_SUCCESS; + }); /// 网络请求成功发送至服务器,并且服务器返回数据格式正确 /// 这里包括所请求业务操作失败的情况,例如没有授权等原因导致 @@ -15,6 +17,11 @@ abstract class TencentResp { static const int RET_USERCANCEL = -2; + @JsonKey( + defaultValue: RET_SUCCESS, + ) final int ret; final String msg; + + bool isSuccessful() => ret == RET_SUCCESS; } diff --git a/lib/src/model/sdk/tencent_share_resp.dart b/lib/src/model/sdk/tencent_share_resp.dart new file mode 100644 index 0000000..5a8c0f4 --- /dev/null +++ b/lib/src/model/sdk/tencent_share_resp.dart @@ -0,0 +1,21 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:tencent_kit/src/model/sdk/tencent_sdk_resp.dart'; + +part 'tencent_share_resp.g.dart'; + +@JsonSerializable( + anyMap: true, + explicitToJson: true, + fieldRename: FieldRename.snake, +) +class TencentShareResp extends TencentSdkResp { + TencentShareResp({ + int ret, + String msg, + }) : super(ret: ret, msg: msg); + + factory TencentShareResp.fromJson(Map json) => + _$TencentShareRespFromJson(json); + + Map toJson() => _$TencentShareRespToJson(this); +} diff --git a/lib/src/model/sdk/tencent_share_resp.g.dart b/lib/src/model/sdk/tencent_share_resp.g.dart new file mode 100644 index 0000000..7d8d51d --- /dev/null +++ b/lib/src/model/sdk/tencent_share_resp.g.dart @@ -0,0 +1,20 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'tencent_share_resp.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +TencentShareResp _$TencentShareRespFromJson(Map json) { + return TencentShareResp( + ret: json['ret'] as int ?? 0, + msg: json['msg'] as String, + ); +} + +Map _$TencentShareRespToJson(TencentShareResp instance) => + { + 'ret': instance.ret, + 'msg': instance.msg, + }; diff --git a/lib/src/tencent.dart b/lib/src/tencent.dart index bfcfe45..c3b1686 100644 --- a/lib/src/tencent.dart +++ b/lib/src/tencent.dart @@ -1,39 +1,37 @@ import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; -import 'package:fake_tencent/src/domain/tencent_login_resp.dart'; -import 'package:fake_tencent/src/domain/tencent_share_resp.dart'; -import 'package:fake_tencent/src/domain/tencent_user_info_resp.dart'; -import 'package:fake_tencent/src/tencent_qzone_flag.dart'; -import 'package:fake_tencent/src/tencent_scene.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; -import 'package:meta/meta.dart'; +import 'package:tencent_kit/src/model/api/tencent_unionid_resp.dart'; +import 'package:tencent_kit/src/model/api/tencent_user_info_resp.dart'; +import 'package:tencent_kit/src/model/sdk/tencent_login_resp.dart'; +import 'package:tencent_kit/src/model/sdk/tencent_share_resp.dart'; +import 'package:tencent_kit/src/tencent_constant.dart'; +/// class Tencent { + /// Tencent() { _channel.setMethodCallHandler(_handleMethod); } static const String _METHOD_REGISTERAPP = 'registerApp'; - static const String _METHOD_ISQQINSTALLED = 'isQQInstalled'; - static const String _METHOD_ISQQSUPPORTSSOLOGIN = 'isQQSupportSSOLogin'; + static const String _METHOD_ISINSTALLED = 'isInstalled'; static const String _METHOD_LOGIN = 'login'; static const String _METHOD_LOGOUT = 'logout'; - static const String _METHOD_GETUSERINFO = 'getUserInfo'; static const String _METHOD_SHAREMOOD = 'shareMood'; static const String _METHOD_SHAREIMAGE = 'shareImage'; static const String _METHOD_SHAREMUSIC = 'shareMusic'; static const String _METHOD_SHAREWEBPAGE = 'shareWebpage'; static const String _METHOD_ONLOGINRESP = 'onLoginResp'; - static const String _METHOD_ONGETUSERINFORESP = 'onGetUserInfoResp'; static const String _METHOD_ONSHARERESP = "onShareResp"; static const String _ARGUMENT_KEY_APPID = 'appId'; + static const String _ARGUMENT_KEY_UNIVERSALLINK = 'universalLink'; static const String _ARGUMENT_KEY_SCOPE = 'scope'; - static const String _ARGUMENT_KEY_OPENID = 'openId'; - static const String _ARGUMENT_KEY_ACCESSTOKEN = 'accessToken'; - static const String _ARGUMENT_KEY_EXPIRESIN = 'expiresIn'; - static const String _ARGUMENT_KEY_CREATEAT = 'createAt'; static const String _ARGUMENT_KEY_SCENE = 'scene'; static const String _ARGUMENT_KEY_TITLE = 'title'; static const String _ARGUMENT_KEY_SUMMARY = 'summary'; @@ -48,67 +46,62 @@ class Tencent { static const String _SCHEME_FILE = 'file'; final MethodChannel _channel = - const MethodChannel('v7lin.github.io/fake_tencent'); + const MethodChannel('v7lin.github.io/tencent_kit'); final StreamController _loginRespStreamController = StreamController.broadcast(); - final StreamController _userInfoRespStreamController = - StreamController.broadcast(); - final StreamController _shareRespStreamController = StreamController.broadcast(); - Future registerApp({ - @required String appId, - }) { - assert(appId != null && appId.isNotEmpty); - return _channel.invokeMethod( - _METHOD_REGISTERAPP, - { - _ARGUMENT_KEY_APPID: appId, - }, - ); - } - Future _handleMethod(MethodCall call) async { switch (call.method) { case _METHOD_ONLOGINRESP: - _loginRespStreamController.add(TencentLoginRespSerializer() - .fromMap(call.arguments as Map)); - break; - case _METHOD_ONGETUSERINFORESP: - _userInfoRespStreamController.add(TencentUserInfoRespSerializer() - .fromMap(call.arguments as Map)); + _loginRespStreamController.add( + TencentLoginResp.fromJson(call.arguments as Map)); break; case _METHOD_ONSHARERESP: - _shareRespStreamController.add(TencentShareRespSerializer() - .fromMap(call.arguments as Map)); + _shareRespStreamController.add( + TencentShareResp.fromJson(call.arguments as Map)); break; } } + /// 向 Open_SDK 注册 + Future registerApp({ + @required String appId, + String universalLink, + }) { + assert(appId != null && appId.isNotEmpty); + assert(universalLink == null || universalLink.isNotEmpty); + final Map map = { + _ARGUMENT_KEY_APPID: appId, +// _ARGUMENT_KEY_UNIVERSALLINK: universalLink, + }; + if (universalLink != null) { + map[_ARGUMENT_KEY_UNIVERSALLINK] = universalLink; + } + + /// 兼容 iOS 空安全 -> NSNull + return _channel.invokeMethod( + _METHOD_REGISTERAPP, + map, + ); + } + /// 登录 Stream loginResp() { return _loginRespStreamController.stream; } - /// 用户信息 - Stream userInfoResp() { - return _userInfoRespStreamController.stream; - } - /// 分享 Stream shareResp() { return _shareRespStreamController.stream; } - Future isQQInstalled() async { - return (await _channel.invokeMethod(_METHOD_ISQQINSTALLED)) as bool; - } - - Future isQQSupportSSOLogin() async { - return (await _channel.invokeMethod(_METHOD_ISQQSUPPORTSSOLOGIN)) as bool; + /// 检查QQ是否已安装 + Future isInstalled() async { + return (await _channel.invokeMethod(_METHOD_ISINSTALLED)) as bool; } /// 登录 @@ -130,24 +123,62 @@ class Tencent { } /// 用户信息 - Future getUserInfo({ - @required String openId, + /// https://wiki.connect.qq.com/get_user_info + Future getUserInfo({ + @required String appId, + @required String openid, @required String accessToken, - @required int expiresIn, - @required int createAt, }) { - assert(openId != null && openId.isNotEmpty); + assert(appId != null && appId.isNotEmpty); + assert(openid != null && openid.isNotEmpty); assert(accessToken != null && accessToken.isNotEmpty); - assert(expiresIn != null && expiresIn > 0); - return _channel.invokeMethod( - _METHOD_GETUSERINFO, - { - _ARGUMENT_KEY_OPENID: openId, - _ARGUMENT_KEY_ACCESSTOKEN: accessToken, - _ARGUMENT_KEY_EXPIRESIN: expiresIn, - _ARGUMENT_KEY_CREATEAT: createAt, - }, - ); + return HttpClient() + .getUrl(Uri.parse( + 'https://graph.qq.com/user/get_user_info?access_token=$accessToken&oauth_consumer_key=$appId&openid=$openid')) + .then((HttpClientRequest request) { + return request.close(); + }).then((HttpClientResponse response) async { + if (response.statusCode == HttpStatus.ok) { + ContentType contentType = response.headers.contentType; + Encoding encoding = Encoding.getByName(contentType?.charset) ?? utf8; + String content = await encoding.decodeStream(response); + return TencentUserInfoResp.fromJson( + json.decode(content) as Map); + } + throw HttpException( + 'HttpResponse statusCode: ${response.statusCode}, reasonPhrase: ${response.reasonPhrase}.'); + }); + } + + /// UnionID + /// https://wiki.connect.qq.com/unionid%E4%BB%8B%E7%BB%8D + Future getUnionId({ + @required String accessToken, + String unionid = '1', + }) { + assert(accessToken != null && accessToken.isNotEmpty); + return HttpClient() + .getUrl(Uri.parse( + 'https://graph.qq.com/oauth2.0/me?access_token=$accessToken&unionid=$unionid')) + .then((HttpClientRequest request) { + return request.close(); + }).then((HttpClientResponse response) async { + if (response.statusCode == HttpStatus.ok) { + ContentType contentType = response.headers.contentType; + Encoding encoding = Encoding.getByName(contentType?.charset) ?? utf8; + String callback = await encoding.decodeStream(response); + // 腾讯有毒 callback( $json ); + RegExp exp = RegExp(r'callback\( (.*) \)\;'); + Match match = exp.firstMatch(callback); + if (match.groupCount == 1) { + String content = match.group(1); + return TencentUnionidResp.fromJson( + json.decode(content) as Map); + } + } + throw HttpException( + 'HttpResponse statusCode: ${response.statusCode}, reasonPhrase: ${response.reasonPhrase}.'); + }); } /// 分享 - 说说 @@ -166,7 +197,7 @@ class Tencent { assert(imageUri != null && imageUri.isScheme(_SCHEME_FILE)); }); } - Map map = { + final Map map = { _ARGUMENT_KEY_SCENE: scene, // _ARGUMENT_KEY_SUMMARY: summary, // _ARGUMENT_KEY_IMAGEURIS: imageUris != null ? new List.generate(imageUris.length, (int index) { @@ -177,17 +208,14 @@ class Tencent { /// 兼容 iOS 空安全 -> NSNull if (summary != null && summary.isNotEmpty) { - map.putIfAbsent(_ARGUMENT_KEY_SUMMARY, () => summary); + map[_ARGUMENT_KEY_SUMMARY] = summary; } if (imageUris != null && imageUris.isNotEmpty) { - map.putIfAbsent( - _ARGUMENT_KEY_IMAGEURIS, - () => List.generate(imageUris.length, (int index) { - return imageUris[index].toString(); - })); + map[_ARGUMENT_KEY_IMAGEURIS] = + imageUris.map((Uri imageUri) => imageUri.toString()).toList(); } if (videoUri != null) { - map.putIfAbsent(_ARGUMENT_KEY_VIDEOURI, () => videoUri.toString()); + map[_ARGUMENT_KEY_VIDEOURI] = videoUri.toString(); } return _channel.invokeMethod(_METHOD_SHAREMOOD, map); } @@ -201,7 +229,7 @@ class Tencent { }) { assert(scene == TencentScene.SCENE_QQ); assert(imageUri != null && imageUri.isScheme(_SCHEME_FILE)); - Map map = { + final Map map = { _ARGUMENT_KEY_SCENE: scene, _ARGUMENT_KEY_IMAGEURI: imageUri.toString(), // _ARGUMENT_KEY_APPNAME: appName, @@ -210,7 +238,7 @@ class Tencent { /// 兼容 iOS 空安全 -> NSNull if (appName != null && appName.isNotEmpty) { - map.putIfAbsent(_ARGUMENT_KEY_APPNAME, () => appName); + map[_ARGUMENT_KEY_APPNAME] = appName; } return _channel.invokeMethod(_METHOD_SHAREIMAGE, map); } @@ -230,7 +258,7 @@ class Tencent { assert(title != null && title.isNotEmpty); assert(musicUrl != null && musicUrl.isNotEmpty); assert(targetUrl != null && targetUrl.isNotEmpty); - Map map = { + final Map map = { _ARGUMENT_KEY_SCENE: scene, _ARGUMENT_KEY_TITLE: title, // _ARGUMENT_KEY_SUMMARY: summary, @@ -243,13 +271,13 @@ class Tencent { /// 兼容 iOS 空安全 -> NSNull if (summary != null && summary.isNotEmpty) { - map.putIfAbsent(_ARGUMENT_KEY_SUMMARY, () => summary); + map[_ARGUMENT_KEY_SUMMARY] = summary; } if (imageUri != null) { - map.putIfAbsent(_ARGUMENT_KEY_IMAGEURI, () => imageUri.toString()); + map[_ARGUMENT_KEY_IMAGEURI] = imageUri.toString(); } if (appName != null && appName.isNotEmpty) { - map.putIfAbsent(_ARGUMENT_KEY_APPNAME, () => appName); + map[_ARGUMENT_KEY_APPNAME] = appName; } return _channel.invokeMethod(_METHOD_SHAREMUSIC, map); } @@ -266,7 +294,7 @@ class Tencent { }) { assert(title != null && title.isNotEmpty); assert(targetUrl != null && targetUrl.isNotEmpty); - Map map = { + final Map map = { _ARGUMENT_KEY_SCENE: scene, _ARGUMENT_KEY_TITLE: title, // _ARGUMENT_KEY_SUMMARY: summary, @@ -278,13 +306,13 @@ class Tencent { /// 兼容 iOS 空安全 -> NSNull if (summary != null && summary.isNotEmpty) { - map.putIfAbsent(_ARGUMENT_KEY_SUMMARY, () => summary); + map[_ARGUMENT_KEY_SUMMARY] = summary; } if (imageUri != null) { - map.putIfAbsent(_ARGUMENT_KEY_IMAGEURI, () => imageUri.toString()); + map[_ARGUMENT_KEY_IMAGEURI] = imageUri.toString(); } if (appName != null && appName.isNotEmpty) { - map.putIfAbsent(_ARGUMENT_KEY_APPNAME, () => appName); + map[_ARGUMENT_KEY_APPNAME] = appName; } return _channel.invokeMethod(_METHOD_SHAREWEBPAGE, map); } diff --git a/lib/src/tencent_scope.dart b/lib/src/tencent_constant.dart similarity index 76% rename from lib/src/tencent_scope.dart rename to lib/src/tencent_constant.dart index e2f400b..eff4aed 100644 --- a/lib/src/tencent_scope.dart +++ b/lib/src/tencent_constant.dart @@ -43,3 +43,26 @@ class TencentScope { /// 所有权限 static const String ALL = 'all'; } + +class TencentScene { + TencentScene._(); + + /// QQ + static const int SCENE_QQ = 0; + + /// QZone + static const int SCENE_QZONE = 1; +} + +class TencentQZoneFlag { + TencentQZoneFlag._(); + + /// 默认是不隐藏分享到QZone按钮且不自动打开分享到QZone的对话框 + static const int DEFAULT = 0; + + /// 分享时自动打开分享到QZone的对话框 + static const int AUTO_OPEN = 1; + + /// 分享时隐藏分享到QZone按钮 + static const int ITEM_HIDE = 2; +} diff --git a/lib/src/tencent_qzone_flag.dart b/lib/src/tencent_qzone_flag.dart deleted file mode 100644 index a9c0d69..0000000 --- a/lib/src/tencent_qzone_flag.dart +++ /dev/null @@ -1,12 +0,0 @@ -class TencentQZoneFlag { - TencentQZoneFlag._(); - - /// 默认是不隐藏分享到QZone按钮且不自动打开分享到QZone的对话框 - static const int DEFAULT = 0; - - /// 分享时自动打开分享到QZone的对话框 - static const int AUTO_OPEN = 1; - - /// 分享时隐藏分享到QZone按钮 - static const int ITEM_HIDE = 2; -} diff --git a/lib/src/tencent_scene.dart b/lib/src/tencent_scene.dart deleted file mode 100644 index 60d5bc4..0000000 --- a/lib/src/tencent_scene.dart +++ /dev/null @@ -1,9 +0,0 @@ -class TencentScene { - TencentScene._(); - - /// QQ - static const int SCENE_QQ = 0; - - /// QZone - static const int SCENE_QZONE = 1; -} diff --git a/lib/tencent_kit.dart b/lib/tencent_kit.dart new file mode 100644 index 0000000..72abd70 --- /dev/null +++ b/lib/tencent_kit.dart @@ -0,0 +1,10 @@ +library tencent_kit; + +export 'src/model/api/tencent_api_resp.dart'; +export 'src/model/api/tencent_unionid_resp.dart'; +export 'src/model/api/tencent_user_info_resp.dart'; +export 'src/model/sdk/tencent_login_resp.dart'; +export 'src/model/sdk/tencent_sdk_resp.dart'; +export 'src/model/sdk/tencent_share_resp.dart'; +export 'src/tencent.dart'; +export 'src/tencent_constant.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index e742795..d46d6fe 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ -name: fake_tencent -description: A powerful tencent plugin for Flutter. -version: 0.3.4 +name: tencent_kit +description: A powerful Flutter plugin allowing developers to share or authorize with natvie android & iOS Tencent SDKs. +version: 1.0.0 author: v7lin homepage: https://github.com/v7lin/fake_tencent @@ -11,24 +11,24 @@ dependencies: flutter: sdk: flutter - meta: '>=1.1.6 <2.0.0' - - jaguar_serializer: '>=2.2.12 <3.0.0' + json_annotation: '>=2.0.0 <4.0.0' dev_dependencies: flutter_test: sdk: flutter - pedantic: '>=1.4.0 <3.0.0' + path: ^1.6.4 + path_provider: ^1.4.0 - path: ^1.6.2 - fake_path_provider: ^0.0.1 + okhttp_kit: ^1.0.0 + + pedantic: build_runner: - jaguar_serializer_cli: + json_serializable: # For information on the generic Dart part of this file, see the -# following page: https://www.dartlang.org/tools/pub/pubspec +# following page: https://dart.dev/tools/pub/pubspec # The following section is specific to Flutter. flutter: @@ -37,8 +37,8 @@ flutter: # be modified. They are used by the tooling to maintain consistency when # adding or updating assets for this project. plugin: - androidPackage: io.github.v7lin.faketencent - pluginClass: FakeTencentPlugin + androidPackage: io.github.v7lin.tencent_kit + pluginClass: TencentKitPlugin # To add assets to your plugin package, add an assets section, like this: # assets: @@ -46,10 +46,10 @@ flutter: # - images/a_dot_ham.jpeg # # For details regarding assets in packages, see - # https://flutter.io/assets-and-images/#from-packages + # https://flutter.dev/assets-and-images/#from-packages # # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.io/assets-and-images/#resolution-aware. + # https://flutter.dev/assets-and-images/#resolution-aware. # To add custom fonts to your plugin package, add a fonts section here, # in this "flutter" section. Each entry in this list should have a @@ -69,4 +69,4 @@ flutter: # weight: 700 # # For details regarding fonts in packages, see - # https://flutter.io/custom-fonts/#from-packages + # https://flutter.dev/custom-fonts/#from-packages diff --git a/test/fake_tencent_test.dart b/test/fake_tencent_test.dart deleted file mode 100644 index ab73b3a..0000000 --- a/test/fake_tencent_test.dart +++ /dev/null @@ -1 +0,0 @@ -void main() {} diff --git a/test/jaguar_test.dart b/test/jaguar_test.dart deleted file mode 100644 index 8b6df56..0000000 --- a/test/jaguar_test.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'dart:convert'; - -import 'package:fake_tencent/src/domain/tencent_login_resp.dart'; -import 'package:fake_tencent/src/domain/tencent_share_resp.dart'; -import 'package:fake_tencent/src/domain/tencent_user_info_resp.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() { - test('smoke test - jaguar_serializer', () { - TencentLoginResp loginResp = TencentLoginRespSerializer().fromMap(json.decode( - '{"ret":0,"access_token":"D3A4D352A06D897D6FD4DED2B8BEEA46","openid":"A13514A5E6385A1D56E1D845303C5FDE","expires_in":7776000}') - as Map); - expect(loginResp.ret, equals(0)); - expect(loginResp.openid, equals('A13514A5E6385A1D56E1D845303C5FDE')); - expect(loginResp.accessToken, equals('D3A4D352A06D897D6FD4DED2B8BEEA46')); - expect(loginResp.expiresIn, equals(7776000)); - - TencentUserInfoResp userInfoResp = TencentUserInfoRespSerializer().fromMap( - json.decode( - '{"ret":0,"msg":"","is_lost":1,"gender":"男","is_yellow_vip":"0","city":"福州","year":"1989","level":"0","figureurl_2":"http://qzapp.qlogo.cn/qzapp/222222/942FEA70050EEAFBD4DCE2C1FC775E56/100","figureurl_1":"http://qzapp.qlogo.cn/qzapp/222222/942FEA70050EEAFBD4DCE2C1FC775E56/50","is_yellow_year_vip":"0","province":"福建","constellation":"","figureurl":"http://qzapp.qlogo.cn/qzapp/222222/942FEA70050EEAFBD4DCE2C1FC775E56/30","figureurl_type":"1","figureurl_qq":"http://thirdqq.qlogo.cn/g?b=oidb&k=aicFesDxFa5P0dImvuYicSGw&s=140","nickname":"qquser","yellow_vip_level":"0","figureurl_qq_1":"http://thirdqq.qlogo.cn/g?b=oidb&k=aicFesDxFa5P0dImvuYicSGw&s=40","vip":"0","figureurl_qq_2":"http://thirdqq.qlogo.cn/g?b=oidb&k=aicFesDxFa5P0dImvuYicSGw&s=100"}') - as Map); - expect(userInfoResp.ret, equals(0)); - expect(userInfoResp.nickname, equals('qquser')); - expect( - userInfoResp.figureurlQq1, - equals( - 'http://thirdqq.qlogo.cn/g?b=oidb&k=aicFesDxFa5P0dImvuYicSGw&s=40')); - - TencentShareResp shareResp = TencentShareRespSerializer() - .fromMap(json.decode('{"msg":""}') as Map); - expect(shareResp.ret, equals(0)); - }); -} diff --git a/test/tencent_kit_test.dart b/test/tencent_kit_test.dart new file mode 100644 index 0000000..957d909 --- /dev/null +++ b/test/tencent_kit_test.dart @@ -0,0 +1,18 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + const MethodChannel channel = MethodChannel('v7lin.github.io/tencent_kit'); + + setUp(() { + channel.setMockMethodCallHandler((MethodCall methodCall) async {}); + }); + + tearDown(() { + channel.setMockMethodCallHandler(null); + }); + + test('smoke test', () async {}); +}