diff --git a/.drone.yml b/.drone.yml index 2c586a3..47aaf68 100755 --- a/.drone.yml +++ b/.drone.yml @@ -16,19 +16,9 @@ steps: # - name: pub-cache # path: /opt/flutter/.pub-cache # commands: +# - flutter pub run build_runner clean # - flutter pub run build_runner build --delete-conflicting-outputs -#- name: android-check -# image: v7lin/flutter:1.17.3-stable -# volumes: -# - name: pub-cache -# path: /opt/flutter/.pub-cache -# - name: gradle -# path: /root/.gradle -# commands: -# - cd example/android/ -# - ./gradlew :wechat_kit:check - # docker run --rm -it -v ${PWD}:/src v7lin/clang:5.0.2-r0 sh -c "clang-format -style=file -i src/Classes/*.h src/Classes/*.m" #- name: ios-format # image: v7lin/clang diff --git a/.gitignore b/.gitignore index 5f2e1f4..95abf25 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,6 @@ build/ -# custom -.idea/ +# *.iml +.idea/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 742324a..b3763a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 2.1.0 + +* nullsafety +* 不再支持 Android embedding v1 +* Tencent 单例 + ## 2.0.1 * 优化 diff --git a/README.md b/README.md index 41bdb0b..9c3d645 100644 --- a/README.md +++ b/README.md @@ -159,6 +159,9 @@ Capabilities -> Associated Domain -> Domain -> applinks:${your applinks} |QQ|不支持|不支持|支持|支持|不支持|支持| |QZone|支持|不支持|不支持|不支持|不支持|支持| +* break change + * 2.1.0: nullsafety & 不再支持 Android embedding v1 & Tencent 单例 + * snapshot ``` diff --git a/analysis_options.yaml b/analysis_options.yaml index 6a73b35..41cde2e 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -37,7 +37,7 @@ analyzer: # Please see https://github.com/flutter/flutter/pull/24528 for details. sdk_version_async_exported_from_core: ignore exclude: - - "**/*.g.dart" + - "lib/*.g.dart" linter: rules: @@ -49,6 +49,7 @@ linter: # - always_put_required_named_parameters_first # we prefer having parameters in the same order as fields https://github.com/flutter/flutter/issues/10219 - always_require_non_null_named_parameters - always_specify_types + - always_use_package_imports # we do this commonly - annotate_overrides # - avoid_annotating_with_dynamic # conflicts with always_specify_types # - avoid_as # required for implicit-casts: true @@ -59,6 +60,7 @@ linter: # - avoid_double_and_int_checks # only useful when targeting JS runtime - avoid_empty_else - avoid_equals_and_hash_code_on_mutable_classes + # - avoid_escaping_inner_quotes # not yet tested - avoid_field_initializers_in_const_classes - avoid_function_literals_in_foreach_calls # - avoid_implementing_value_types # not yet tested @@ -77,9 +79,10 @@ linter: - avoid_returning_null_for_void # - avoid_returning_this # there are plenty of valid reasons to return this # - avoid_setters_without_getters # not yet tested - # - avoid_shadowing_type_parameters # not yet tested + - avoid_shadowing_type_parameters - avoid_single_cascade_in_expression_statements - avoid_slow_async_io + # - avoid_type_to_string # we do this commonly - avoid_types_as_parameter_names # - avoid_types_on_closure_parameters # conflicts with always_specify_types # - avoid_unnecessary_containers # not yet tested @@ -91,65 +94,71 @@ linter: - camel_case_types - cancel_subscriptions # - cascade_invocations # not yet tested + - cast_nullable_to_non_nullable # - close_sinks # not reliable enough # - comment_references # blocked on https://github.com/flutter/flutter/issues/20765 # - constant_identifier_names # needs an opt-out https://github.com/dart-lang/linter/issues/204 - control_flow_in_finally - # - curly_braces_in_flow_control_structures # not yet tested + # - curly_braces_in_flow_control_structures # not required by flutter style # - diagnostic_describe_all_properties # not yet tested - directives_ordering + # - do_not_use_environment # we do this commonly - empty_catches - empty_constructor_bodies - empty_statements - - file_names + - exhaustive_cases + - file_names # not yet tested - flutter_style_todos - hash_and_equals - implementation_imports # - invariant_booleans # too many false positives: https://github.com/dart-lang/linter/issues/811 - iterable_contains_unrelated_type - # - join_return_with_assignment # not yet tested + # - join_return_with_assignment # not required by flutter style + - leading_newlines_in_multiline_strings - library_names - library_prefixes - # - lines_longer_than_80_chars # not yet tested + # - lines_longer_than_80_chars # not required by flutter style - list_remove_unrelated_type # - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/sdk/issues/34181 # - missing_whitespace_between_adjacent_strings # not yet tested - no_adjacent_strings_in_list + # - no_default_cases # too many false positives - no_duplicate_case_values - # - no_logic_in_create_state # not yet tested - # - no_runtimeType_toString # not yet tested + - no_logic_in_create_state + # - no_runtimeType_toString # ok in tests; we enable this only in packages/ - non_constant_identifier_names - # - null_closures # not yet tested + - null_check_on_nullable_type_parameter + # - null_closures # not required by flutter style # - 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_names + # - package_names # non conforming packages in sdk - package_prefixed_library_names # - parameter_assignments # we do this commonly - prefer_adjacent_string_concatenation - prefer_asserts_in_initializer_lists - # - prefer_asserts_with_message # not yet tested + # - prefer_asserts_with_message # not required by flutter style - prefer_collection_literals - prefer_conditional_assignment - prefer_const_constructors - prefer_const_constructors_in_immutables - prefer_const_declarations - prefer_const_literals_to_create_immutables - # - prefer_constructors_over_static_methods # not yet tested + # - prefer_constructors_over_static_methods # far too many false positives - prefer_contains # - prefer_double_quotes # opposite of prefer_single_quotes - 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 - # - prefer_final_in_for_each - # - prefer_final_locals + - prefer_final_in_for_each + - prefer_final_locals - prefer_for_elements_to_map_fromIterable - # - prefer_foreach + - prefer_foreach # - prefer_function_declarations_over_variables # not yet tested - prefer_generic_function_type_aliases - # - prefer_if_elements_to_conditional_expressions + - prefer_if_elements_to_conditional_expressions - prefer_if_null_operators - prefer_initializing_formals - prefer_inlined_adds @@ -169,13 +178,15 @@ linter: # - provide_deprecation_message # not yet tested # - public_member_api_docs # enabled on a case-by-case basis; see e.g. packages/analysis_options.yaml - recursive_getters + # - sized_box_for_whitespace # not yet tested - slash_for_doc_comments # - sort_child_properties_last # not yet tested - sort_constructors_first - - sort_pub_dependencies + # - sort_pub_dependencies # prevents separating pinned transitive dependencies - sort_unnamed_constructors_first - test_types_in_equals - throw_in_finally + - tighten_type_of_initializing_formals # - type_annotate_public_apis # subset of always_specify_types - type_init_formals - unawaited_futures # too many false positives @@ -187,18 +198,24 @@ linter: # - unnecessary_lambdas # has false positives: https://github.com/dart-lang/linter/issues/498 - unnecessary_new - unnecessary_null_aware_assignments + # - unnecessary_null_checks # not yet tested - unnecessary_null_in_if_null_operators + - unnecessary_nullable_for_final_variable_declarations - unnecessary_overrides - unnecessary_parenthesis + # - unnecessary_raw_strings # not yet tested - unnecessary_statements + - unnecessary_string_escapes - unnecessary_string_interpolations - unnecessary_this - unrelated_type_equality_checks # - unsafe_html # not yet tested - use_full_hex_values_for_flutter_colors # - use_function_type_syntax_for_parameters # not yet tested + - use_is_even_rather_than_modulo # - use_key_in_widget_constructors # not yet tested - use_late_for_private_fields_and_variables + - use_raw_strings - use_rethrow_when_possible # - use_setters_to_change_properties # not yet tested # - use_string_buffers # has false positives: https://github.com/dart-lang/sdk/issues/34182 diff --git a/android/build.gradle b/android/build.gradle index 7939290..39265e8 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ group 'io.github.v7lin.tencent_kit' -version '2.0.1' +version '2.1.0' buildscript { repositories { @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.4' + classpath 'com.android.tools.build:gradle:4.1.0' } } diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 01a286e..3c9d085 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip diff --git a/android/src/main/java/io/github/v7lin/tencent_kit/TencentKit.java b/android/src/main/java/io/github/v7lin/tencent_kit/TencentKit.java deleted file mode 100644 index c6ea4b8..0000000 --- a/android/src/main/java/io/github/v7lin/tencent_kit/TencentKit.java +++ /dev/null @@ -1,514 +0,0 @@ -package io.github.v7lin.tencent_kit; - -import android.app.Activity; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.ProviderInfo; -import android.net.Uri; -import android.os.Bundle; -import android.text.TextUtils; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.tencent.connect.common.Constants; -import com.tencent.connect.share.QQShare; -import com.tencent.connect.share.QzonePublish; -import com.tencent.connect.share.QzoneShare; -import com.tencent.tauth.IUiListener; -import com.tencent.tauth.Tencent; -import com.tencent.tauth.UiError; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.plugin.common.MethodCall; -import io.flutter.plugin.common.MethodChannel; -import io.flutter.plugin.common.PluginRegistry; -import io.github.v7lin.tencent_kit.content.TencentKitFileProvider; - -public class TencentKit implements MethodChannel.MethodCallHandler, PluginRegistry.ActivityResultListener { - - private static class TencentScene { - static final int SCENE_QQ = 0; - static final int SCENE_QZONE = 1; - } - - private static class TencentRetCode { - // 网络请求成功发送至服务器,并且服务器返回数据格式正确 - // 这里包括所请求业务操作失败的情况,例如没有授权等原因导致 - static final int RET_SUCCESS = 0; - // 网络异常,或服务器返回的数据格式不正确导致无法解析 - 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_ISTIMINSTALLED = "isTIMInstalled"; - private static final String METHOD_LOGIN = "login"; - private static final String METHOD_LOGOUT = "logout"; - private static final String METHOD_SHAREMOOD = "shareMood"; - private static final String METHOD_SHARETEXT = "shareText"; - 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_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_SCENE = "scene"; - private static final String ARGUMENT_KEY_TITLE = "title"; - private static final String ARGUMENT_KEY_SUMMARY = "summary"; - private static final String ARGUMENT_KEY_IMAGEURI = "imageUri"; - private static final String ARGUMENT_KEY_IMAGEURIS = "imageUris"; - private static final String ARGUMENT_KEY_VIDEOURI = "videoUri"; - private static final String ARGUMENT_KEY_MUSICURL = "musicUrl"; - private static final String ARGUMENT_KEY_TARGETURL = "targetUrl"; - private static final String ARGUMENT_KEY_APPNAME = "appName"; - private static final String ARGUMENT_KEY_EXTINT = "extInt"; - - private static final String ARGUMENT_KEY_RESULT_RET = "ret"; - private static final String ARGUMENT_KEY_RESULT_MSG = "msg"; - private static final String ARGUMENT_KEY_RESULT_OPENID = "openid"; - private static final String ARGUMENT_KEY_RESULT_ACCESS_TOKEN = "access_token"; - private static final String ARGUMENT_KEY_RESULT_EXPIRES_IN = "expires_in"; - private static final String ARGUMENT_KEY_RESULT_CREATE_AT = "create_at"; - - private static final String SCHEME_FILE = "file"; - - // - - private Context applicationContext; - private Activity activity; - - private MethodChannel channel; - private Tencent tencent; - - public TencentKit() { - super(); - } - - public TencentKit(Context applicationContext, Activity activity) { - this.applicationContext = applicationContext; - this.activity = activity; - } - - // - - public void setApplicationContext(@Nullable Context applicationContext) { - this.applicationContext = applicationContext; - } - - public void setActivity(@Nullable Activity activity) { - this.activity = activity; - } - - public void startListening(@NonNull BinaryMessenger messenger) { - channel = new MethodChannel(messenger, "v7lin.github.io/tencent_kit"); - channel.setMethodCallHandler(this); - } - - public void stopListening() { - channel.setMethodCallHandler(null); - channel = null; - } - - // --- MethodCallHandler - - @Override - public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { - if (METHOD_REGISTERAPP.equals(call.method)) { - final String appId = call.argument(ARGUMENT_KEY_APPID); -// final String universalLink = call.argument(ARGUMENT_KEY_UNIVERSALLINK); - String authority = null; - try { - ProviderInfo providerInfo = applicationContext.getPackageManager().getProviderInfo(new ComponentName(applicationContext, TencentKitFileProvider.class), PackageManager.MATCH_DEFAULT_ONLY); - authority = providerInfo.authority; - } catch (PackageManager.NameNotFoundException e) { - // ignore - } - if (!TextUtils.isEmpty(authority)) { - tencent = Tencent.createInstance(appId, applicationContext, authority); - } else { - tencent = Tencent.createInstance(appId, applicationContext); - } - result.success(null); - } else if (METHOD_ISQQINSTALLED.equals(call.method)) { - result.success(isAppInstalled(applicationContext, "com.tencent.mobileqq")); - } else if (METHOD_ISTIMINSTALLED.equals(call.method)) { - result.success(isAppInstalled(applicationContext, "com.tencent.tim")); - } else if (METHOD_LOGIN.equals(call.method)) { - login(call, result); - } else if (METHOD_LOGOUT.equals(call.method)) { - logout(call, result); - } else if (METHOD_SHAREMOOD.equals(call.method)) { - shareMood(call, result); - } else if (METHOD_SHARETEXT.equals(call.method)) { - shareText(call, result); - } else if (METHOD_SHAREIMAGE.equals(call.method)) { - shareImage(call, result); - } else if (METHOD_SHAREMUSIC.equals(call.method)) { - shareMusic(call, result); - } else if (METHOD_SHAREWEBPAGE.equals(call.method)) { - shareWebpage(call, result); - } else { - result.notImplemented(); - } - } - - private void login(MethodCall call, MethodChannel.Result result) { - String scope = call.argument(ARGUMENT_KEY_SCOPE); - if (tencent != null) { - tencent.login(activity, scope, loginListener); - } - result.success(null); - } - - private IUiListener loginListener = 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) { - String openId = !object.isNull(ARGUMENT_KEY_RESULT_OPENID) ? object.getString(ARGUMENT_KEY_RESULT_OPENID) : null; - String accessToken = !object.isNull(ARGUMENT_KEY_RESULT_ACCESS_TOKEN) ? object.getString(ARGUMENT_KEY_RESULT_ACCESS_TOKEN) : null; - int expiresIn = !object.isNull(ARGUMENT_KEY_RESULT_EXPIRES_IN) ? object.getInt(ARGUMENT_KEY_RESULT_EXPIRES_IN) : 0; - long createAt = System.currentTimeMillis(); - if (!TextUtils.isEmpty(openId) && !TextUtils.isEmpty(accessToken)) { - map.put(ARGUMENT_KEY_RESULT_RET, TencentRetCode.RET_SUCCESS); - map.put(ARGUMENT_KEY_RESULT_OPENID, openId); - map.put(ARGUMENT_KEY_RESULT_ACCESS_TOKEN, accessToken); - map.put(ARGUMENT_KEY_RESULT_EXPIRES_IN, expiresIn); - map.put(ARGUMENT_KEY_RESULT_CREATE_AT, createAt); - } else { - map.put(ARGUMENT_KEY_RESULT_RET, TencentRetCode.RET_COMMON); - map.put(ARGUMENT_KEY_RESULT_MSG, "openId or accessToken is null."); - } - } 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()); - } - if (channel != null) { - channel.invokeMethod(METHOD_ONLOGINRESP, map); - } - } - - @Override - public void onError(UiError uiError) { - // 登录失败 - Map map = new HashMap<>(); - map.put(ARGUMENT_KEY_RESULT_RET, TencentRetCode.RET_COMMON); - map.put(ARGUMENT_KEY_RESULT_MSG, uiError.errorMessage); - if (channel != null) { - channel.invokeMethod(METHOD_ONLOGINRESP, map); - } - } - - @Override - public void onCancel() { - // 取消登录 - Map map = new HashMap<>(); - map.put(ARGUMENT_KEY_RESULT_RET, TencentRetCode.RET_USERCANCEL); - if (channel != null) { - channel.invokeMethod(METHOD_ONLOGINRESP, map); - } - } - - @Override - public void onWarning(int code) { - } - }; - - private void logout(MethodCall call, MethodChannel.Result result) { - if (tencent != null) { - tencent.logout(applicationContext); - } - result.success(null); - } - - private void shareMood(MethodCall call, MethodChannel.Result result) { - int scene = call.argument(ARGUMENT_KEY_SCENE); - if (scene == TencentScene.SCENE_QZONE) { - String summary = call.argument(ARGUMENT_KEY_SUMMARY); - List imageUris = call.argument(ARGUMENT_KEY_IMAGEURIS); - String videoUri = call.argument(ARGUMENT_KEY_VIDEOURI); - - Bundle params = new Bundle(); - if (!TextUtils.isEmpty(summary)) { - params.putString(QzonePublish.PUBLISH_TO_QZONE_SUMMARY, summary); - } - if (imageUris != null && !imageUris.isEmpty()) { - ArrayList uris = new ArrayList<>(); - for (String imageUri : imageUris) { - uris.add(Uri.parse(imageUri).getPath()); - } - params.putStringArrayList(QzonePublish.PUBLISH_TO_QZONE_IMAGE_URL, uris); - } - if (!TextUtils.isEmpty(videoUri)) { - String videoPath = Uri.parse(videoUri).getPath(); - params.putString(QzonePublish.PUBLISH_TO_QZONE_VIDEO_PATH, videoPath); - params.putInt(QzonePublish.PUBLISH_TO_QZONE_KEY_TYPE, QzonePublish.PUBLISH_TO_QZONE_TYPE_PUBLISHVIDEO); - } else { - params.putInt(QzonePublish.PUBLISH_TO_QZONE_KEY_TYPE, QzonePublish.PUBLISH_TO_QZONE_TYPE_PUBLISHMOOD); - } - if (tencent != null) { - tencent.publishToQzone(activity, params, shareListener); - } - } - result.success(null); - } - - private void shareText(MethodCall call, MethodChannel.Result result) { - int scene = call.argument(ARGUMENT_KEY_SCENE); - if (scene == TencentScene.SCENE_QQ) { - String summary = call.argument(ARGUMENT_KEY_SUMMARY); - Intent sendIntent = new Intent(); - sendIntent.setAction(Intent.ACTION_SEND); - sendIntent.putExtra(Intent.EXTRA_TEXT, summary); - sendIntent.setType("text/*"); - // 普通大众版 > 办公简洁版 > 急速轻聊版 - PackageManager packageManager = applicationContext.getPackageManager(); - List infos = packageManager.getInstalledPackages(0); - if (infos != null && !infos.isEmpty()) { - for (String packageName : Arrays.asList("com.tencent.mobileqq", "com.tencent.tim", "com.tencent.qqlite")) { - for (PackageInfo info : infos) { - if (packageName.equals(info.packageName)) { - sendIntent.setPackage(packageName); - if (sendIntent.resolveActivity(applicationContext.getPackageManager()) != null) { - sendIntent.setComponent(new ComponentName(packageName, "com.tencent.mobileqq.activity.JumpActivity")); - activity.startActivity(sendIntent); - break; - } - } - } - } - } - } - result.success(null); - } - - private void shareImage(MethodCall call, MethodChannel.Result result) { - int scene = call.argument(ARGUMENT_KEY_SCENE); - if (scene == TencentScene.SCENE_QQ) { - String imageUri = call.argument(ARGUMENT_KEY_IMAGEURI); - String appName = call.argument(ARGUMENT_KEY_APPNAME); - int extInt = call.argument(ARGUMENT_KEY_EXTINT); - - Bundle params = new Bundle(); - params.putInt(QQShare.SHARE_TO_QQ_KEY_TYPE, QQShare.SHARE_TO_QQ_TYPE_IMAGE); - params.putString(QQShare.SHARE_TO_QQ_IMAGE_LOCAL_URL, Uri.parse(imageUri).getPath()); - if (!TextUtils.isEmpty(appName)) { - params.putString(QQShare.SHARE_TO_QQ_APP_NAME, appName); - } - params.putInt(QQShare.SHARE_TO_QQ_EXT_INT, extInt); - if (tencent != null) { - tencent.shareToQQ(activity, params, shareListener); - } - } - result.success(null); - } - - private void shareMusic(MethodCall call, MethodChannel.Result result) { - int scene = call.argument(ARGUMENT_KEY_SCENE); - if (scene == TencentScene.SCENE_QQ) { - String title = call.argument(ARGUMENT_KEY_TITLE); - String summary = call.argument(ARGUMENT_KEY_SUMMARY); - String imageUri = call.argument(ARGUMENT_KEY_IMAGEURI); - String musicUrl = call.argument(ARGUMENT_KEY_MUSICURL); - String targetUrl = call.argument(ARGUMENT_KEY_TARGETURL); - String appName = call.argument(ARGUMENT_KEY_APPNAME); - int extInt = call.argument(ARGUMENT_KEY_EXTINT); - - Bundle params = new Bundle(); - params.putInt(QQShare.SHARE_TO_QQ_KEY_TYPE, QQShare.SHARE_TO_QQ_TYPE_AUDIO); - params.putString(QQShare.SHARE_TO_QQ_TITLE, title); - if (!TextUtils.isEmpty(summary)) { - params.putString(QQShare.SHARE_TO_QQ_SUMMARY, summary); - } - if (!TextUtils.isEmpty(imageUri)) { - Uri uri = Uri.parse(imageUri); - if (TextUtils.equals(SCHEME_FILE, uri.getScheme())) { - params.putString(QQShare.SHARE_TO_QQ_IMAGE_URL, uri.getPath()); - } else { - params.putString(QQShare.SHARE_TO_QQ_IMAGE_URL, imageUri); - } - } - params.putString(QQShare.SHARE_TO_QQ_AUDIO_URL, musicUrl); - params.putString(QQShare.SHARE_TO_QQ_TARGET_URL, targetUrl); - if (!TextUtils.isEmpty(appName)) { - params.putString(QQShare.SHARE_TO_QQ_APP_NAME, appName); - } - params.putInt(QQShare.SHARE_TO_QQ_EXT_INT, extInt); - if (tencent != null) { - tencent.shareToQQ(activity, params, shareListener); - } - } - result.success(null); - } - - private void shareWebpage(MethodCall call, MethodChannel.Result result) { - int scene = call.argument(ARGUMENT_KEY_SCENE); - String title = call.argument(ARGUMENT_KEY_TITLE); - String summary = call.argument(ARGUMENT_KEY_SUMMARY); - String imageUri = call.argument(ARGUMENT_KEY_IMAGEURI); - String targetUrl = call.argument(ARGUMENT_KEY_TARGETURL); - String appName = call.argument(ARGUMENT_KEY_APPNAME); - int extInt = call.argument(ARGUMENT_KEY_EXTINT); - - Bundle params = new Bundle(); - switch (scene) { - case TencentScene.SCENE_QQ: - params.putInt(QQShare.SHARE_TO_QQ_KEY_TYPE, QQShare.SHARE_TO_QQ_TYPE_DEFAULT); - params.putString(QQShare.SHARE_TO_QQ_TITLE, title); - if (!TextUtils.isEmpty(summary)) { - params.putString(QQShare.SHARE_TO_QQ_SUMMARY, summary); - } - if (!TextUtils.isEmpty(imageUri)) { - Uri uri = Uri.parse(imageUri); - if (TextUtils.equals(SCHEME_FILE, uri.getScheme())) { - params.putString(QQShare.SHARE_TO_QQ_IMAGE_URL, uri.getPath()); - } else { - params.putString(QQShare.SHARE_TO_QQ_IMAGE_URL, imageUri); - } - } - params.putString(QQShare.SHARE_TO_QQ_TARGET_URL, targetUrl); - if (!TextUtils.isEmpty(appName)) { - params.putString(QQShare.SHARE_TO_QQ_APP_NAME, appName); - } - params.putInt(QQShare.SHARE_TO_QQ_EXT_INT, extInt); - if (tencent != null) { - tencent.shareToQQ(activity, params, shareListener); - } - break; - case TencentScene.SCENE_QZONE: - params.putInt(QzoneShare.SHARE_TO_QZONE_KEY_TYPE, QzoneShare.SHARE_TO_QZONE_TYPE_IMAGE_TEXT); - params.putString(QzoneShare.SHARE_TO_QQ_TITLE, title); - if (!TextUtils.isEmpty(summary)) { - params.putString(QzoneShare.SHARE_TO_QQ_SUMMARY, summary); - } - if (!TextUtils.isEmpty(imageUri)) { - ArrayList uris = new ArrayList<>(); - Uri uri = Uri.parse(imageUri); - if (TextUtils.equals(SCHEME_FILE, uri.getScheme())) { - uris.add(uri.getPath()); - } else { - uris.add(imageUri); - } - params.putStringArrayList(QzoneShare.SHARE_TO_QQ_IMAGE_URL, uris); - } - params.putString(QzoneShare.SHARE_TO_QQ_TARGET_URL, targetUrl); - if (tencent != null) { - tencent.shareToQzone(activity, params, shareListener); - } - break; - default: - break; - } - result.success(null); - } - - private IUiListener shareListener = 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); - } 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()); - } - if (channel != null) { - channel.invokeMethod(METHOD_ONSHARERESP, 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); - if (channel != null) { - channel.invokeMethod(METHOD_ONSHARERESP, map); - } - } - - @Override - public void onCancel() { - Map map = new HashMap<>(); - map.put(ARGUMENT_KEY_RESULT_RET, TencentRetCode.RET_USERCANCEL); - if (channel != null) { - channel.invokeMethod(METHOD_ONSHARERESP, map); - } - } - - @Override - public void onWarning(int code) { - if (code == Constants.ERROR_NO_AUTHORITY) { - // 如果authorities为空,sdk会回调这个接口,提醒开发者适配FileProvider - } - } - }; - - // --- ActivityResultListener - - @Override - public boolean onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - case Constants.REQUEST_LOGIN: - return Tencent.onActivityResultData(requestCode, resultCode, data, loginListener); - case Constants.REQUEST_QQ_SHARE: - case Constants.REQUEST_QZONE_SHARE: - return Tencent.onActivityResultData(requestCode, resultCode, data, shareListener); - default: - break; - } - return false; - } - - // --- - - private static boolean isAppInstalled(Context context, String packageName) { - PackageInfo packageInfo = null; - try { - packageInfo = context.getPackageManager().getPackageInfo(packageName, 0); - } catch (PackageManager.NameNotFoundException e) { - } - return packageInfo != null; - } -} diff --git a/android/src/main/java/io/github/v7lin/tencent_kit/TencentKitPlugin.java b/android/src/main/java/io/github/v7lin/tencent_kit/TencentKitPlugin.java index 53904fb..bd26da9 100644 --- a/android/src/main/java/io/github/v7lin/tencent_kit/TencentKitPlugin.java +++ b/android/src/main/java/io/github/v7lin/tencent_kit/TencentKitPlugin.java @@ -1,62 +1,135 @@ package io.github.v7lin.tencent_kit; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ProviderInfo; +import android.net.Uri; +import android.os.Bundle; +import android.text.TextUtils; + import androidx.annotation.NonNull; +import com.tencent.connect.common.Constants; +import com.tencent.connect.share.QQShare; +import com.tencent.connect.share.QzonePublish; +import com.tencent.connect.share.QzoneShare; +import com.tencent.tauth.IUiListener; +import com.tencent.tauth.Tencent; +import com.tencent.tauth.UiError; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.embedding.engine.plugins.activity.ActivityAware; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; -import io.flutter.plugin.common.PluginRegistry; +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; +import io.flutter.plugin.common.MethodChannel.MethodCallHandler; +import io.flutter.plugin.common.MethodChannel.Result; +import io.flutter.plugin.common.PluginRegistry.ActivityResultListener; +import io.github.v7lin.tencent_kit.content.TencentKitFileProvider; /** * TencentKitPlugin */ -public class TencentKitPlugin implements FlutterPlugin, ActivityAware { - // This static function is optional and equivalent to onAttachedToEngine. It supports the old - // pre-Flutter-1.12 Android projects. You are encouraged to continue supporting - // plugin registration via this function while apps migrate to use the new Android APIs - // post-flutter-1.12 via https://flutter.dev/go/android-project-migration. - // - // It is encouraged to share logic between onAttachedToEngine and registerWith to keep - // them functionally equivalent. Only one of onAttachedToEngine or registerWith will be called - // depending on the user's project. onAttachedToEngine or registerWith must both be defined - // in the same class. - public static void registerWith(PluginRegistry.Registrar registrar) { - TencentKit tencentKit = new TencentKit(registrar.context(), registrar.activity()); - registrar.addActivityResultListener(tencentKit); - tencentKit.startListening(registrar.messenger()); +public class TencentKitPlugin implements FlutterPlugin, ActivityAware, ActivityResultListener, MethodCallHandler { + + private static class TencentScene { + static final int SCENE_QQ = 0; + static final int SCENE_QZONE = 1; } + private static class TencentRetCode { + // 网络请求成功发送至服务器,并且服务器返回数据格式正确 + // 这里包括所请求业务操作失败的情况,例如没有授权等原因导致 + static final int RET_SUCCESS = 0; + // 网络异常,或服务器返回的数据格式不正确导致无法解析 + 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_ISTIMINSTALLED = "isTIMInstalled"; + private static final String METHOD_LOGIN = "login"; + private static final String METHOD_LOGOUT = "logout"; + private static final String METHOD_SHAREMOOD = "shareMood"; + private static final String METHOD_SHARETEXT = "shareText"; + 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_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_SCENE = "scene"; + private static final String ARGUMENT_KEY_TITLE = "title"; + private static final String ARGUMENT_KEY_SUMMARY = "summary"; + private static final String ARGUMENT_KEY_IMAGEURI = "imageUri"; + private static final String ARGUMENT_KEY_IMAGEURIS = "imageUris"; + private static final String ARGUMENT_KEY_VIDEOURI = "videoUri"; + private static final String ARGUMENT_KEY_MUSICURL = "musicUrl"; + private static final String ARGUMENT_KEY_TARGETURL = "targetUrl"; + private static final String ARGUMENT_KEY_APPNAME = "appName"; + private static final String ARGUMENT_KEY_EXTINT = "extInt"; + + private static final String ARGUMENT_KEY_RESULT_RET = "ret"; + private static final String ARGUMENT_KEY_RESULT_MSG = "msg"; + private static final String ARGUMENT_KEY_RESULT_OPENID = "openid"; + private static final String ARGUMENT_KEY_RESULT_ACCESS_TOKEN = "access_token"; + private static final String ARGUMENT_KEY_RESULT_EXPIRES_IN = "expires_in"; + private static final String ARGUMENT_KEY_RESULT_CREATE_AT = "create_at"; + + private static final String SCHEME_FILE = "file"; + + /// The MethodChannel that will the communication between Flutter and native Android + /// + /// This local reference serves to register the plugin with the Flutter Engine and unregister it + /// when the Flutter Engine is detached from the Activity + private MethodChannel channel; + private Context applicationContext; + private ActivityPluginBinding activityPluginBinding; + + private Tencent tencent; + // --- FlutterPlugin - private final TencentKit tencentKit; - - private ActivityPluginBinding pluginBinding; - - public TencentKitPlugin() { - tencentKit = new TencentKit(); - } - @Override public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { - tencentKit.setApplicationContext(binding.getApplicationContext()); - tencentKit.setActivity(null); - tencentKit.startListening(binding.getBinaryMessenger()); + channel = new MethodChannel(binding.getBinaryMessenger(), "v7lin.github.io/tencent_kit"); + channel.setMethodCallHandler(this); + applicationContext = binding.getApplicationContext(); } @Override public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { - tencentKit.stopListening(); - tencentKit.setActivity(null); - tencentKit.setApplicationContext(null); + channel.setMethodCallHandler(null); + channel = null; + applicationContext = null; } // --- ActivityAware @Override public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { - tencentKit.setActivity(binding.getActivity()); - pluginBinding = binding; - pluginBinding.addActivityResultListener(tencentKit); + activityPluginBinding = binding; + activityPluginBinding.addActivityResultListener(this); } @Override @@ -71,8 +144,389 @@ public class TencentKitPlugin implements FlutterPlugin, ActivityAware { @Override public void onDetachedFromActivity() { - tencentKit.setActivity(null); - pluginBinding.removeActivityResultListener(tencentKit); - pluginBinding = null; + activityPluginBinding.removeActivityResultListener(this); + activityPluginBinding = null; + } + + // --- ActivityResultListener + + @Override + public boolean onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case Constants.REQUEST_LOGIN: + return Tencent.onActivityResultData(requestCode, resultCode, data, loginListener); + case Constants.REQUEST_QQ_SHARE: + case Constants.REQUEST_QZONE_SHARE: + return Tencent.onActivityResultData(requestCode, resultCode, data, shareListener); + default: + break; + } + return false; + } + + // --- MethodCallHandler + + @Override + public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { + if (METHOD_REGISTERAPP.equals(call.method)) { + final String appId = call.argument(ARGUMENT_KEY_APPID); +// final String universalLink = call.argument(ARGUMENT_KEY_UNIVERSALLINK); + String authority = null; + try { + ProviderInfo providerInfo = applicationContext.getPackageManager().getProviderInfo(new ComponentName(applicationContext, TencentKitFileProvider.class), PackageManager.MATCH_DEFAULT_ONLY); + authority = providerInfo.authority; + } catch (PackageManager.NameNotFoundException e) { + // ignore + } + if (!TextUtils.isEmpty(authority)) { + tencent = Tencent.createInstance(appId, applicationContext, authority); + } else { + tencent = Tencent.createInstance(appId, applicationContext); + } + result.success(null); + } else if (METHOD_ISQQINSTALLED.equals(call.method)) { + result.success(isAppInstalled(applicationContext, "com.tencent.mobileqq")); + } else if (METHOD_ISTIMINSTALLED.equals(call.method)) { + result.success(isAppInstalled(applicationContext, "com.tencent.tim")); + } else if (METHOD_LOGIN.equals(call.method)) { + login(call, result); + } else if (METHOD_LOGOUT.equals(call.method)) { + logout(call, result); + } else if (METHOD_SHAREMOOD.equals(call.method)) { + shareMood(call, result); + } else if (METHOD_SHARETEXT.equals(call.method)) { + shareText(call, result); + } else if (METHOD_SHAREIMAGE.equals(call.method)) { + shareImage(call, result); + } else if (METHOD_SHAREMUSIC.equals(call.method)) { + shareMusic(call, result); + } else if (METHOD_SHAREWEBPAGE.equals(call.method)) { + shareWebpage(call, result); + } else { + result.notImplemented(); + } + } + + private void login(@NonNull MethodCall call, @NonNull Result result) { + String scope = call.argument(ARGUMENT_KEY_SCOPE); + if (tencent != null) { + tencent.login(activityPluginBinding.getActivity(), scope, loginListener); + } + result.success(null); + } + + private IUiListener loginListener = 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) { + String openId = !object.isNull(ARGUMENT_KEY_RESULT_OPENID) ? object.getString(ARGUMENT_KEY_RESULT_OPENID) : null; + String accessToken = !object.isNull(ARGUMENT_KEY_RESULT_ACCESS_TOKEN) ? object.getString(ARGUMENT_KEY_RESULT_ACCESS_TOKEN) : null; + int expiresIn = !object.isNull(ARGUMENT_KEY_RESULT_EXPIRES_IN) ? object.getInt(ARGUMENT_KEY_RESULT_EXPIRES_IN) : 0; + long createAt = System.currentTimeMillis(); + if (!TextUtils.isEmpty(openId) && !TextUtils.isEmpty(accessToken)) { + map.put(ARGUMENT_KEY_RESULT_RET, TencentRetCode.RET_SUCCESS); + map.put(ARGUMENT_KEY_RESULT_OPENID, openId); + map.put(ARGUMENT_KEY_RESULT_ACCESS_TOKEN, accessToken); + map.put(ARGUMENT_KEY_RESULT_EXPIRES_IN, expiresIn); + map.put(ARGUMENT_KEY_RESULT_CREATE_AT, createAt); + } else { + map.put(ARGUMENT_KEY_RESULT_RET, TencentRetCode.RET_COMMON); + map.put(ARGUMENT_KEY_RESULT_MSG, "openId or accessToken is null."); + } + } 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()); + } + if (channel != null) { + channel.invokeMethod(METHOD_ONLOGINRESP, map); + } + } + + @Override + public void onError(UiError uiError) { + // 登录失败 + Map map = new HashMap<>(); + map.put(ARGUMENT_KEY_RESULT_RET, TencentRetCode.RET_COMMON); + map.put(ARGUMENT_KEY_RESULT_MSG, uiError.errorMessage); + if (channel != null) { + channel.invokeMethod(METHOD_ONLOGINRESP, map); + } + } + + @Override + public void onCancel() { + // 取消登录 + Map map = new HashMap<>(); + map.put(ARGUMENT_KEY_RESULT_RET, TencentRetCode.RET_USERCANCEL); + if (channel != null) { + channel.invokeMethod(METHOD_ONLOGINRESP, map); + } + } + + @Override + public void onWarning(int code) { + } + }; + + private void logout(@NonNull MethodCall call, @NonNull Result result) { + if (tencent != null) { + tencent.logout(applicationContext); + } + result.success(null); + } + + private void shareMood(@NonNull MethodCall call, @NonNull Result result) { + int scene = call.argument(ARGUMENT_KEY_SCENE); + if (scene == TencentScene.SCENE_QZONE) { + String summary = call.argument(ARGUMENT_KEY_SUMMARY); + List imageUris = call.argument(ARGUMENT_KEY_IMAGEURIS); + String videoUri = call.argument(ARGUMENT_KEY_VIDEOURI); + + Bundle params = new Bundle(); + if (!TextUtils.isEmpty(summary)) { + params.putString(QzonePublish.PUBLISH_TO_QZONE_SUMMARY, summary); + } + if (imageUris != null && !imageUris.isEmpty()) { + ArrayList uris = new ArrayList<>(); + for (String imageUri : imageUris) { + uris.add(Uri.parse(imageUri).getPath()); + } + params.putStringArrayList(QzonePublish.PUBLISH_TO_QZONE_IMAGE_URL, uris); + } + if (!TextUtils.isEmpty(videoUri)) { + String videoPath = Uri.parse(videoUri).getPath(); + params.putString(QzonePublish.PUBLISH_TO_QZONE_VIDEO_PATH, videoPath); + params.putInt(QzonePublish.PUBLISH_TO_QZONE_KEY_TYPE, QzonePublish.PUBLISH_TO_QZONE_TYPE_PUBLISHVIDEO); + } else { + params.putInt(QzonePublish.PUBLISH_TO_QZONE_KEY_TYPE, QzonePublish.PUBLISH_TO_QZONE_TYPE_PUBLISHMOOD); + } + if (tencent != null) { + tencent.publishToQzone(activityPluginBinding.getActivity(), params, shareListener); + } + } + result.success(null); + } + + private void shareText(@NonNull MethodCall call, @NonNull Result result) { + int scene = call.argument(ARGUMENT_KEY_SCENE); + if (scene == TencentScene.SCENE_QQ) { + String summary = call.argument(ARGUMENT_KEY_SUMMARY); + Intent sendIntent = new Intent(); + sendIntent.setAction(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_TEXT, summary); + sendIntent.setType("text/*"); + // 普通大众版 > 办公简洁版 > 急速轻聊版 + PackageManager packageManager = applicationContext.getPackageManager(); + List infos = packageManager.getInstalledPackages(0); + if (infos != null && !infos.isEmpty()) { + for (String packageName : Arrays.asList("com.tencent.mobileqq", "com.tencent.tim", "com.tencent.qqlite")) { + for (PackageInfo info : infos) { + if (packageName.equals(info.packageName)) { + sendIntent.setPackage(packageName); + if (sendIntent.resolveActivity(applicationContext.getPackageManager()) != null) { + sendIntent.setComponent(new ComponentName(packageName, "com.tencent.mobileqq.activity.JumpActivity")); + activityPluginBinding.getActivity().startActivity(sendIntent); + break; + } + } + } + } + } + } + result.success(null); + } + + private void shareImage(@NonNull MethodCall call, @NonNull Result result) { + int scene = call.argument(ARGUMENT_KEY_SCENE); + if (scene == TencentScene.SCENE_QQ) { + String imageUri = call.argument(ARGUMENT_KEY_IMAGEURI); + String appName = call.argument(ARGUMENT_KEY_APPNAME); + int extInt = call.argument(ARGUMENT_KEY_EXTINT); + + Bundle params = new Bundle(); + params.putInt(QQShare.SHARE_TO_QQ_KEY_TYPE, QQShare.SHARE_TO_QQ_TYPE_IMAGE); + params.putString(QQShare.SHARE_TO_QQ_IMAGE_LOCAL_URL, Uri.parse(imageUri).getPath()); + if (!TextUtils.isEmpty(appName)) { + params.putString(QQShare.SHARE_TO_QQ_APP_NAME, appName); + } + params.putInt(QQShare.SHARE_TO_QQ_EXT_INT, extInt); + if (tencent != null) { + tencent.shareToQQ(activityPluginBinding.getActivity(), params, shareListener); + } + } + result.success(null); + } + + private void shareMusic(@NonNull MethodCall call, @NonNull Result result) { + int scene = call.argument(ARGUMENT_KEY_SCENE); + if (scene == TencentScene.SCENE_QQ) { + String title = call.argument(ARGUMENT_KEY_TITLE); + String summary = call.argument(ARGUMENT_KEY_SUMMARY); + String imageUri = call.argument(ARGUMENT_KEY_IMAGEURI); + String musicUrl = call.argument(ARGUMENT_KEY_MUSICURL); + String targetUrl = call.argument(ARGUMENT_KEY_TARGETURL); + String appName = call.argument(ARGUMENT_KEY_APPNAME); + int extInt = call.argument(ARGUMENT_KEY_EXTINT); + + Bundle params = new Bundle(); + params.putInt(QQShare.SHARE_TO_QQ_KEY_TYPE, QQShare.SHARE_TO_QQ_TYPE_AUDIO); + params.putString(QQShare.SHARE_TO_QQ_TITLE, title); + if (!TextUtils.isEmpty(summary)) { + params.putString(QQShare.SHARE_TO_QQ_SUMMARY, summary); + } + if (!TextUtils.isEmpty(imageUri)) { + Uri uri = Uri.parse(imageUri); + if (TextUtils.equals(SCHEME_FILE, uri.getScheme())) { + params.putString(QQShare.SHARE_TO_QQ_IMAGE_URL, uri.getPath()); + } else { + params.putString(QQShare.SHARE_TO_QQ_IMAGE_URL, imageUri); + } + } + params.putString(QQShare.SHARE_TO_QQ_AUDIO_URL, musicUrl); + params.putString(QQShare.SHARE_TO_QQ_TARGET_URL, targetUrl); + if (!TextUtils.isEmpty(appName)) { + params.putString(QQShare.SHARE_TO_QQ_APP_NAME, appName); + } + params.putInt(QQShare.SHARE_TO_QQ_EXT_INT, extInt); + if (tencent != null) { + tencent.shareToQQ(activityPluginBinding.getActivity(), params, shareListener); + } + } + result.success(null); + } + + private void shareWebpage(@NonNull MethodCall call, @NonNull Result result) { + int scene = call.argument(ARGUMENT_KEY_SCENE); + String title = call.argument(ARGUMENT_KEY_TITLE); + String summary = call.argument(ARGUMENT_KEY_SUMMARY); + String imageUri = call.argument(ARGUMENT_KEY_IMAGEURI); + String targetUrl = call.argument(ARGUMENT_KEY_TARGETURL); + String appName = call.argument(ARGUMENT_KEY_APPNAME); + int extInt = call.argument(ARGUMENT_KEY_EXTINT); + + Bundle params = new Bundle(); + switch (scene) { + case TencentScene.SCENE_QQ: + params.putInt(QQShare.SHARE_TO_QQ_KEY_TYPE, QQShare.SHARE_TO_QQ_TYPE_DEFAULT); + params.putString(QQShare.SHARE_TO_QQ_TITLE, title); + if (!TextUtils.isEmpty(summary)) { + params.putString(QQShare.SHARE_TO_QQ_SUMMARY, summary); + } + if (!TextUtils.isEmpty(imageUri)) { + Uri uri = Uri.parse(imageUri); + if (TextUtils.equals(SCHEME_FILE, uri.getScheme())) { + params.putString(QQShare.SHARE_TO_QQ_IMAGE_URL, uri.getPath()); + } else { + params.putString(QQShare.SHARE_TO_QQ_IMAGE_URL, imageUri); + } + } + params.putString(QQShare.SHARE_TO_QQ_TARGET_URL, targetUrl); + if (!TextUtils.isEmpty(appName)) { + params.putString(QQShare.SHARE_TO_QQ_APP_NAME, appName); + } + params.putInt(QQShare.SHARE_TO_QQ_EXT_INT, extInt); + if (tencent != null) { + tencent.shareToQQ(activityPluginBinding.getActivity(), params, shareListener); + } + break; + case TencentScene.SCENE_QZONE: + params.putInt(QzoneShare.SHARE_TO_QZONE_KEY_TYPE, QzoneShare.SHARE_TO_QZONE_TYPE_IMAGE_TEXT); + params.putString(QzoneShare.SHARE_TO_QQ_TITLE, title); + if (!TextUtils.isEmpty(summary)) { + params.putString(QzoneShare.SHARE_TO_QQ_SUMMARY, summary); + } + if (!TextUtils.isEmpty(imageUri)) { + ArrayList uris = new ArrayList<>(); + Uri uri = Uri.parse(imageUri); + if (TextUtils.equals(SCHEME_FILE, uri.getScheme())) { + uris.add(uri.getPath()); + } else { + uris.add(imageUri); + } + params.putStringArrayList(QzoneShare.SHARE_TO_QQ_IMAGE_URL, uris); + } + params.putString(QzoneShare.SHARE_TO_QQ_TARGET_URL, targetUrl); + if (tencent != null) { + tencent.shareToQzone(activityPluginBinding.getActivity(), params, shareListener); + } + break; + default: + break; + } + result.success(null); + } + + private IUiListener shareListener = 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); + } 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()); + } + if (channel != null) { + channel.invokeMethod(METHOD_ONSHARERESP, 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); + if (channel != null) { + channel.invokeMethod(METHOD_ONSHARERESP, map); + } + } + + @Override + public void onCancel() { + Map map = new HashMap<>(); + map.put(ARGUMENT_KEY_RESULT_RET, TencentRetCode.RET_USERCANCEL); + if (channel != null) { + channel.invokeMethod(METHOD_ONSHARERESP, map); + } + } + + @Override + public void onWarning(int code) { + if (code == Constants.ERROR_NO_AUTHORITY) { + // 如果authorities为空,sdk会回调这个接口,提醒开发者适配FileProvider + } + } + }; + + // --- + + private static boolean isAppInstalled(Context context, String packageName) { + PackageInfo packageInfo = null; + try { + packageInfo = context.getPackageManager().getPackageInfo(packageName, 0); + } catch (PackageManager.NameNotFoundException e) { + } + return packageInfo != null; } } diff --git a/example/.gitignore b/example/.gitignore index 1ba9c33..0fa6b67 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -22,6 +22,7 @@ # Flutter/Dart/Pub related **/doc/api/ +**/ios/Flutter/.last_build_id .dart_tool/ .flutter-plugins .flutter-plugins-dependencies @@ -39,5 +40,7 @@ app.*.symbols # Obfuscation related app.*.map.json -# Exceptions to above rules. -!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/example/android/.gitignore b/example/android/.gitignore index bc2100d..0a741cb 100644 --- a/example/android/.gitignore +++ b/example/android/.gitignore @@ -5,3 +5,7 @@ gradle-wrapper.jar /gradlew.bat /local.properties GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties diff --git a/example/android/build.gradle b/example/android/build.gradle index 0baed69..c9e3db0 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.4' + classpath 'com.android.tools.build:gradle:4.1.0' } } diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index 296b146..bc6a58a 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip diff --git a/example/lib/main.dart b/example/lib/main.dart index 248cc55..901dfce 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -2,12 +2,16 @@ import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:okhttp_kit/okhttp_kit.dart'; -import 'package:path/path.dart' as path; -import 'package:path_provider/path_provider.dart' as path_provider; +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:tencent_kit/tencent_kit.dart'; -void main() => runApp(MyApp()); +const String _TENCENT_APPID = 'your tencent appId'; + +void main() { + WidgetsFlutterBinding.ensureInitialized(); + Tencent.instance.registerApp(appId: _TENCENT_APPID); + runApp(MyApp()); +} class MyApp extends StatelessWidget { @override @@ -26,39 +30,33 @@ class Home extends StatefulWidget { } class _HomeState extends State { - static const String _TENCENT_APPID = 'your tencent appId'; + late final StreamSubscription _login = + Tencent.instance.loginResp().listen(_listenLogin); + late final StreamSubscription _share = + Tencent.instance.shareResp().listen(_listenShare); - final Tencent _tencent = Tencent()..registerApp(appId: _TENCENT_APPID); - - StreamSubscription _login; - StreamSubscription _share; - - TencentLoginResp _loginResp; + TencentLoginResp? _loginResp; @override void initState() { super.initState(); - _login = _tencent.loginResp().listen(_listenLogin); - _share = _tencent.shareResp().listen(_listenShare); } void _listenLogin(TencentLoginResp resp) { _loginResp = resp; - String content = 'login: ${resp.openid} - ${resp.accessToken}'; + final String content = 'login: ${resp.openid} - ${resp.accessToken}'; _showTips('登录', content); } - void _listenShare(TencentShareResp resp) { - String content = 'share: ${resp.ret} - ${resp.msg}'; + void _listenShare(TencentSdkResp resp) { + final String content = 'share: ${resp.ret} - ${resp.msg}'; _showTips('分享', content); } @override void dispose() { - _login?.cancel(); - _login = null; - _share?.cancel(); - _share = null; + _login.cancel(); + _share.cancel(); super.dispose(); } @@ -73,15 +71,15 @@ class _HomeState extends State { ListTile( title: const Text('环境检查'), onTap: () async { - String content = - 'QQ install: ${await _tencent.isQQInstalled()}\nTIM install: ${await _tencent.isTIMInstalled()}'; + final String content = + 'QQ install: ${await Tencent.instance.isQQInstalled()}\nTIM install: ${await Tencent.instance.isTIMInstalled()}'; _showTips('环境检查', content); }, ), ListTile( title: const Text('登录'), onTap: () { - _tencent.login( + Tencent.instance.login( scope: [TencentScope.GET_SIMPLE_USERINFO], ); }, @@ -89,13 +87,13 @@ class _HomeState extends State { ListTile( title: const Text('获取用户信息'), onTap: () async { - if (_loginResp != null && - _loginResp.isSuccessful && - !_loginResp.isExpired) { - TencentUserInfoResp userInfo = await _tencent.getUserInfo( + if ((_loginResp?.isSuccessful ?? false) && + !(_loginResp!.isExpired ?? true)) { + final TencentUserInfoResp userInfo = + await Tencent.instance.getUserInfo( appId: _TENCENT_APPID, - openid: _loginResp.openid, - accessToken: _loginResp.accessToken, + openid: _loginResp!.openid!, + accessToken: _loginResp!.accessToken!, ); if (userInfo.isSuccessful) { _showTips('用户信息', @@ -109,11 +107,11 @@ class _HomeState extends State { ListTile( title: const Text('获取UnionID'), onTap: () async { - if (_loginResp != null && - _loginResp.isSuccessful && - !_loginResp.isExpired) { - TencentUnionidResp unionid = await _tencent.getUnionId( - accessToken: _loginResp.accessToken, + if ((_loginResp?.isSuccessful ?? false) && + !(_loginResp!.isExpired ?? true)) { + final TencentUnionidResp unionid = + await Tencent.instance.getUnionId( + accessToken: _loginResp!.accessToken!, ); if (unionid.isSuccessful) { _showTips('UnionID', @@ -128,7 +126,7 @@ class _HomeState extends State { ListTile( title: const Text('分享说说'), onTap: () { - _tencent.shareMood( + Tencent.instance.shareMood( scene: TencentScene.SCENE_QZONE, summary: '分享测试', ); @@ -137,7 +135,7 @@ class _HomeState extends State { ListTile( title: const Text('文本分享'), onTap: () { - _tencent.shareText( + Tencent.instance.shareText( scene: TencentScene.SCENE_QQ, summary: '分享测试', ); @@ -146,37 +144,18 @@ class _HomeState extends State { ListTile( title: const Text('图片分享'), onTap: () async { - 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.png')); - 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), - ); - } + final File file = await DefaultCacheManager().getSingleFile( + 'https://www.baidu.com/img/bd_logo1.png?where=super'); + await Tencent.instance.shareImage( + scene: TencentScene.SCENE_QQ, + imageUri: Uri.file(file.path), + ); }, ), ListTile( title: const Text('网页分享'), onTap: () { - _tencent.shareWebpage( + Tencent.instance.shareWebpage( scene: TencentScene.SCENE_QQ, title: 'title', targetUrl: 'https://www.baidu.com/', diff --git a/example/pubspec.lock b/example/pubspec.lock index f780ecc..3007fb3 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -1,212 +1,233 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + archive: + dependency: transitive + description: + name: archive + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.1.2" async: dependency: transitive description: name: async url: "https://pub.flutter-io.cn" source: hosted - version: "2.5.0-nullsafety.1" + version: "2.5.0" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0" characters: dependency: transitive description: name: characters url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.0-nullsafety.3" + version: "1.1.0" charcode: dependency: transitive description: name: charcode url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" clock: dependency: transitive description: name: clock url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0" collection: dependency: transitive description: name: collection url: "https://pub.flutter-io.cn" source: hosted - version: "1.15.0-nullsafety.3" - convert: - dependency: transitive - description: - name: convert - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.1.1" + version: "1.15.0" crypto: dependency: transitive description: name: crypto url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.5" + version: "3.0.0" cupertino_icons: dependency: "direct main" description: name: cupertino_icons url: "https://pub.flutter-io.cn" source: hosted - version: "0.1.3" + version: "1.0.2" fake_async: dependency: transitive description: name: fake_async url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" ffi: dependency: transitive description: name: ffi url: "https://pub.flutter-io.cn" source: hosted - version: "0.1.3" + version: "1.0.0" file: dependency: transitive description: name: file url: "https://pub.flutter-io.cn" source: hosted - version: "5.2.1" - fixnum: - dependency: transitive - description: - name: fixnum - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.10.11" + version: "6.1.0" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_cache_manager: + dependency: "direct main" + description: + name: flutter_cache_manager + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.0-nullsafety.1" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" - intl: + http: dependency: transitive description: - name: intl + name: http url: "https://pub.flutter-io.cn" source: hosted - version: "0.16.1" + version: "0.13.0" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.0.0" + image: + dependency: transitive + description: + name: image + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.1" json_annotation: dependency: transitive description: name: json_annotation url: "https://pub.flutter-io.cn" source: hosted - version: "3.1.1" + version: "4.0.0" matcher: dependency: transitive description: name: matcher url: "https://pub.flutter-io.cn" source: hosted - version: "0.12.10-nullsafety.1" + version: "0.12.10" meta: dependency: transitive description: name: meta url: "https://pub.flutter-io.cn" source: hosted - version: "1.3.0-nullsafety.3" - okhttp_kit: - dependency: "direct main" - description: - name: okhttp_kit - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.0.2" + version: "1.3.0" path: - dependency: "direct main" + dependency: transitive description: name: path url: "https://pub.flutter-io.cn" source: hosted - version: "1.8.0-nullsafety.1" + version: "1.8.0" path_provider: - dependency: "direct main" + dependency: transitive description: name: path_provider url: "https://pub.flutter-io.cn" source: hosted - version: "1.6.27" + version: "2.0.1" path_provider_linux: dependency: transitive description: name: path_provider_linux url: "https://pub.flutter-io.cn" source: hosted - version: "0.0.1+2" + version: "2.0.0" path_provider_macos: dependency: transitive description: name: path_provider_macos url: "https://pub.flutter-io.cn" source: hosted - version: "0.0.4+8" + version: "2.0.0" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface url: "https://pub.flutter-io.cn" source: hosted - version: "1.0.4" + version: "2.0.1" path_provider_windows: dependency: transitive description: name: path_provider_windows url: "https://pub.flutter-io.cn" source: hosted - version: "0.0.4+3" + version: "2.0.0" pedantic: dependency: "direct dev" description: name: pedantic url: "https://pub.flutter-io.cn" source: hosted - version: "1.9.2" + version: "1.11.0" + petitparser: + dependency: transitive + description: + name: petitparser + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.0.2" platform: dependency: transitive description: name: platform url: "https://pub.flutter-io.cn" source: hosted - version: "2.2.1" + version: "3.0.0" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface url: "https://pub.flutter-io.cn" source: hosted - version: "1.0.3" + version: "2.0.0" process: dependency: transitive description: name: process url: "https://pub.flutter-io.cn" source: hosted - version: "3.0.13" + version: "4.1.0" + rxdart: + dependency: transitive + description: + name: rxdart + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.26.0" sky_engine: dependency: transitive description: flutter @@ -218,77 +239,112 @@ packages: name: source_span url: "https://pub.flutter-io.cn" source: hosted - version: "1.8.0-nullsafety.2" + version: "1.8.0" + sqflite: + dependency: transitive + description: + name: sqflite + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.0+3" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.0+2" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.flutter-io.cn" source: hosted - version: "1.10.0-nullsafety.1" + version: "1.10.0" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0" + synchronized: + dependency: transitive + description: + name: synchronized + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.0" tencent_kit: dependency: "direct main" description: path: ".." relative: true source: path - version: "2.0.1" + version: "2.1.0" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" test_api: dependency: transitive description: name: test_api url: "https://pub.flutter-io.cn" source: hosted - version: "0.2.19-nullsafety.2" + version: "0.2.19" typed_data: dependency: transitive description: name: typed_data url: "https://pub.flutter-io.cn" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.3.0" + uuid: + dependency: transitive + description: + name: uuid + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.1" vector_math: dependency: transitive description: name: vector_math url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.0-nullsafety.3" + version: "2.1.0" win32: dependency: transitive description: name: win32 url: "https://pub.flutter-io.cn" source: hosted - version: "1.7.4+1" + version: "2.0.4" xdg_directories: dependency: transitive description: name: xdg_directories url: "https://pub.flutter-io.cn" source: hosted - version: "0.1.2" + version: "0.2.0" + xml: + dependency: transitive + description: + name: xml + url: "https://pub.flutter-io.cn" + source: hosted + version: "5.0.2" sdks: - dart: ">=2.10.0-110 <2.11.0" - flutter: ">=1.12.13+hotfix.5 <2.0.0" + dart: ">=2.12.0 <3.0.0" + flutter: ">=1.24.0-10" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 32147b1..2368a4a 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,12 +1,14 @@ name: tencent_kit_example description: Demonstrates how to use the tencent_kit plugin. -version: 1.0.0+1000 + # The following line prevents the package from being accidentally published to # pub.dev using `pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev +version: 1.0.0+100 + environment: - sdk: ">=2.7.0 <3.0.0" + sdk: ">=2.12.0 <3.0.0" dependencies: flutter: @@ -22,12 +24,9 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^0.1.3 + cupertino_icons: ^1.0.2 - path: ^1.6.4 - path_provider: ^1.4.0 - - okhttp_kit: ^1.0.0 + flutter_cache_manager: ^3.0.0-nullsafety.1 dev_dependencies: flutter_test: diff --git a/ios/tencent_kit.podspec b/ios/tencent_kit.podspec index 7d7aaa2..c1519b1 100644 --- a/ios/tencent_kit.podspec +++ b/ios/tencent_kit.podspec @@ -4,7 +4,7 @@ # Pod::Spec.new do |s| s.name = 'tencent_kit' - s.version = '2.0.1' + s.version = '2.1.0' s.summary = 'A powerful Flutter plugin allowing developers to auth/share with natvie Android & iOS Tencent SDKs.' s.description = <<-DESC A powerful Flutter plugin allowing developers to auth/share with natvie Android & iOS Tencent SDKs. diff --git a/lib/src/model/api/tencent_api_resp.dart b/lib/src/model/api/tencent_api_resp.dart index e3ae0c3..e9d7c50 100644 --- a/lib/src/model/api/tencent_api_resp.dart +++ b/lib/src/model/api/tencent_api_resp.dart @@ -2,7 +2,7 @@ import 'package:json_annotation/json_annotation.dart'; abstract class TencentApiResp { const TencentApiResp({ - this.ret, + required this.ret, this.msg, }); @@ -14,7 +14,7 @@ abstract class TencentApiResp { defaultValue: RET_SUCCESS, ) final int ret; - final String msg; + final String? msg; bool get isSuccessful => ret == RET_SUCCESS; } diff --git a/lib/src/model/api/tencent_user_info_resp.dart b/lib/src/model/api/tencent_user_info_resp.dart index 9219ad2..1035f6d 100644 --- a/lib/src/model/api/tencent_user_info_resp.dart +++ b/lib/src/model/api/tencent_user_info_resp.dart @@ -9,8 +9,8 @@ part 'tencent_user_info_resp.g.dart'; ) class TencentUserInfoResp extends TencentApiResp { const TencentUserInfoResp({ - int ret, - String msg, + required int ret, + String? msg, this.isLost, this.nickname, this.gender, @@ -36,58 +36,58 @@ class TencentUserInfoResp extends TencentApiResp { 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; + 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; + final String? figureurl1; @JsonKey( name: 'figureurl_2', ) - final String figureurl2; - final String figureurlQq; // 140 * 140 + final String? figureurl2; + final String? figureurlQq; // 140 * 140 @JsonKey( name: 'figureurl_qq_1', ) - final String figureurlQq1; // 大小为40×40像素的QQ头像URL。 + final String? figureurlQq1; // 大小为40×40像素的QQ头像URL。 @JsonKey( name: 'figureurl_qq_2', ) - final String + 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; + final String? figureurlType; + final String? isYellowVip; + final String? vip; + final String? yellowVipLevel; + final String? level; + final String? isYellowYearVip; bool get isMale => gender == '男'; bool get isFemale => gender == '女'; - String get headImgUrl { - if (figureurlQq != null && figureurlQq.isNotEmpty) { + String? get headImgUrl { + if (figureurlQq?.isNotEmpty ?? false) { return figureurlQq; } - if (figureurlQq2 != null && figureurlQq2.isNotEmpty) { + if (figureurlQq2?.isNotEmpty ?? false) { return figureurlQq2; } - if (figureurlQq1 != null && figureurlQq1.isNotEmpty) { + if (figureurlQq1?.isNotEmpty ?? false) { return figureurlQq1; } - if (figureurl2 != null && figureurl2.isNotEmpty) { + if (figureurl2?.isNotEmpty ?? false) { return figureurl2; } - if (figureurl1 != null && figureurl1.isNotEmpty) { + if (figureurl1?.isNotEmpty ?? false) { return figureurl1; } return figureurl; diff --git a/lib/src/model/api/tencent_user_info_resp.g.dart b/lib/src/model/api/tencent_user_info_resp.g.dart index ce5bcc4..e54f602 100644 --- a/lib/src/model/api/tencent_user_info_resp.g.dart +++ b/lib/src/model/api/tencent_user_info_resp.g.dart @@ -8,28 +8,28 @@ part of 'tencent_user_info_resp.dart'; 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, + 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?, ); } diff --git a/lib/src/model/sdk/tencent_login_resp.dart b/lib/src/model/sdk/tencent_login_resp.dart index 2202550..c1adb20 100644 --- a/lib/src/model/sdk/tencent_login_resp.dart +++ b/lib/src/model/sdk/tencent_login_resp.dart @@ -9,8 +9,8 @@ part 'tencent_login_resp.g.dart'; ) class TencentLoginResp extends TencentSdkResp { const TencentLoginResp({ - int ret, - String msg, + required int ret, + String? msg, this.openid, this.accessToken, this.expiresIn, @@ -20,13 +20,15 @@ class TencentLoginResp extends TencentSdkResp { factory TencentLoginResp.fromJson(Map json) => _$TencentLoginRespFromJson(json); - final String openid; - final String accessToken; - final int expiresIn; - final int createAt; + final String? openid; + final String? accessToken; + final int? expiresIn; + final int? createAt; - bool get isExpired => - DateTime.now().millisecondsSinceEpoch - createAt >= expiresIn * 1000; + bool? get isExpired => isSuccessful + ? DateTime.now().millisecondsSinceEpoch - createAt! >= expiresIn! * 1000 + : null; + @override 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 index 399b5dc..c2ec9a9 100644 --- a/lib/src/model/sdk/tencent_login_resp.g.dart +++ b/lib/src/model/sdk/tencent_login_resp.g.dart @@ -8,12 +8,12 @@ part of 'tencent_login_resp.dart'; 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, + 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?, ); } diff --git a/lib/src/model/sdk/tencent_sdk_resp.dart b/lib/src/model/sdk/tencent_sdk_resp.dart index 9c6791e..8c33f91 100644 --- a/lib/src/model/sdk/tencent_sdk_resp.dart +++ b/lib/src/model/sdk/tencent_sdk_resp.dart @@ -1,11 +1,20 @@ import 'package:json_annotation/json_annotation.dart'; -abstract class TencentSdkResp { +part 'tencent_sdk_resp.g.dart'; + +@JsonSerializable( + explicitToJson: true, + fieldRename: FieldRename.snake, +) +class TencentSdkResp { const TencentSdkResp({ - this.ret, + required this.ret, this.msg, }); + factory TencentSdkResp.fromJson(Map json) => + _$TencentSdkRespFromJson(json); + /// 网络请求成功发送至服务器,并且服务器返回数据格式正确 /// 这里包括所请求业务操作失败的情况,例如没有授权等原因导致 static const int RET_SUCCESS = 0; @@ -21,9 +30,11 @@ abstract class TencentSdkResp { defaultValue: RET_SUCCESS, ) final int ret; - final String msg; + final String? msg; bool get isSuccessful => ret == RET_SUCCESS; bool get isCancelled => ret == RET_USERCANCEL; + + Map toJson() => _$TencentSdkRespToJson(this); } diff --git a/lib/src/model/sdk/tencent_share_resp.g.dart b/lib/src/model/sdk/tencent_sdk_resp.g.dart similarity index 53% rename from lib/src/model/sdk/tencent_share_resp.g.dart rename to lib/src/model/sdk/tencent_sdk_resp.g.dart index 56bb334..8ed8f1b 100644 --- a/lib/src/model/sdk/tencent_share_resp.g.dart +++ b/lib/src/model/sdk/tencent_sdk_resp.g.dart @@ -1,19 +1,19 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'tencent_share_resp.dart'; +part of 'tencent_sdk_resp.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** -TencentShareResp _$TencentShareRespFromJson(Map json) { - return TencentShareResp( - ret: json['ret'] as int ?? 0, - msg: json['msg'] as String, +TencentSdkResp _$TencentSdkRespFromJson(Map json) { + return TencentSdkResp( + ret: json['ret'] as int? ?? 0, + msg: json['msg'] as String?, ); } -Map _$TencentShareRespToJson(TencentShareResp instance) => +Map _$TencentSdkRespToJson(TencentSdkResp instance) => { 'ret': instance.ret, 'msg': instance.msg, diff --git a/lib/src/model/sdk/tencent_share_resp.dart b/lib/src/model/sdk/tencent_share_resp.dart deleted file mode 100644 index 0265dc9..0000000 --- a/lib/src/model/sdk/tencent_share_resp.dart +++ /dev/null @@ -1,20 +0,0 @@ -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( - explicitToJson: true, - fieldRename: FieldRename.snake, -) -class TencentShareResp extends TencentSdkResp { - const 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/unionid/tencent_unionid_resp.dart b/lib/src/model/unionid/tencent_unionid_resp.dart index aeeaf51..b7c9deb 100644 --- a/lib/src/model/unionid/tencent_unionid_resp.dart +++ b/lib/src/model/unionid/tencent_unionid_resp.dart @@ -8,7 +8,7 @@ part 'tencent_unionid_resp.g.dart'; ) class TencentUnionidResp { const TencentUnionidResp({ - this.error, + required this.error, this.errorDescription, this.clientId, this.openid, @@ -22,10 +22,10 @@ class TencentUnionidResp { defaultValue: ERROR_SUCCESS, ) final int error; - final String errorDescription; - final String clientId; - final String openid; - final String unionid; + final String? errorDescription; + final String? clientId; + final String? openid; + final String? unionid; static const int ERROR_SUCCESS = 0; diff --git a/lib/src/model/unionid/tencent_unionid_resp.g.dart b/lib/src/model/unionid/tencent_unionid_resp.g.dart index 57147d4..7b4bb07 100644 --- a/lib/src/model/unionid/tencent_unionid_resp.g.dart +++ b/lib/src/model/unionid/tencent_unionid_resp.g.dart @@ -8,11 +8,11 @@ part of 'tencent_unionid_resp.dart'; 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, + 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?, ); } diff --git a/lib/src/tencent.dart b/lib/src/tencent.dart index 635c01b..db82c73 100644 --- a/lib/src/tencent.dart +++ b/lib/src/tencent.dart @@ -2,20 +2,21 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import 'package:flutter/foundation.dart'; import 'package:flutter/services.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/model/sdk/tencent_sdk_resp.dart'; import 'package:tencent_kit/src/model/unionid/tencent_unionid_resp.dart'; import 'package:tencent_kit/src/tencent_constant.dart'; /// class Tencent { /// - Tencent() { - _channel.setMethodCallHandler(_handleMethod); - } + Tencent._(); + + static Tencent get instance => _instance; + + static final Tencent _instance = Tencent._(); static const String _METHOD_REGISTERAPP = 'registerApp'; static const String _METHOD_ISQQINSTALLED = 'isQQInstalled'; @@ -47,14 +48,15 @@ class Tencent { static const String _SCHEME_FILE = 'file'; - final MethodChannel _channel = - const MethodChannel('v7lin.github.io/tencent_kit'); + late final MethodChannel _channel = + const MethodChannel('v7lin.github.io/tencent_kit') + ..setMethodCallHandler(_handleMethod); final StreamController _loginRespStreamController = StreamController.broadcast(); - final StreamController _shareRespStreamController = - StreamController.broadcast(); + final StreamController _shareRespStreamController = + StreamController.broadcast(); Future _handleMethod(MethodCall call) async { switch (call.method) { @@ -63,7 +65,7 @@ class Tencent { (call.arguments as Map).cast())); break; case _METHOD_ONSHARERESP: - _shareRespStreamController.add(TencentShareResp.fromJson( + _shareRespStreamController.add(TencentSdkResp.fromJson( (call.arguments as Map).cast())); break; } @@ -71,10 +73,9 @@ class Tencent { /// 向 Open_SDK 注册 Future registerApp({ - @required String appId, - String universalLink, + required String appId, + String? universalLink, }) { - assert(appId?.isNotEmpty ?? false); return _channel.invokeMethod( _METHOD_REGISTERAPP, { @@ -91,25 +92,24 @@ class Tencent { } /// 分享 - Stream shareResp() { + Stream shareResp() { return _shareRespStreamController.stream; } /// 检查QQ是否已安装 - Future isQQInstalled() { - return _channel.invokeMethod(_METHOD_ISQQINSTALLED); + Future isQQInstalled() async { + return await _channel.invokeMethod(_METHOD_ISQQINSTALLED) ?? false; } /// 检查QQ是否已安装 - Future isTIMInstalled() { - return _channel.invokeMethod(_METHOD_ISTIMINSTALLED); + Future isTIMInstalled() async { + return await _channel.invokeMethod(_METHOD_ISTIMINSTALLED) ?? false; } /// 登录 Future login({ - @required List scope, + required List scope, }) { - assert(scope?.isNotEmpty ?? false); return _channel.invokeMethod( _METHOD_LOGIN, { @@ -126,13 +126,10 @@ class Tencent { /// 用户信息 /// https://wiki.connect.qq.com/get_user_info Future getUserInfo({ - @required String appId, - @required String openid, - @required String accessToken, + required String appId, + required String openid, + required String accessToken, }) { - assert(appId?.isNotEmpty ?? false); - assert(openid?.isNotEmpty ?? false); - assert(accessToken?.isNotEmpty ?? false); return HttpClient() .getUrl(Uri.parse( 'https://graph.qq.com/user/get_user_info?access_token=$accessToken&oauth_consumer_key=$appId&openid=$openid')) @@ -140,9 +137,10 @@ class Tencent { 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); + final ContentType? contentType = response.headers.contentType; + final Encoding encoding = + Encoding.getByName(contentType?.charset) ?? utf8; + final String content = await encoding.decodeStream(response); return TencentUserInfoResp.fromJson( json.decode(content) as Map); } @@ -154,10 +152,9 @@ class Tencent { /// UnionID /// https://wiki.connect.qq.com/unionid%E4%BB%8B%E7%BB%8D Future getUnionId({ - @required String accessToken, + required String accessToken, String unionid = '1', }) { - assert(accessToken?.isNotEmpty ?? false); return HttpClient() .getUrl(Uri.parse( 'https://graph.qq.com/oauth2.0/me?access_token=$accessToken&unionid=$unionid')) @@ -165,16 +162,19 @@ class Tencent { 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); + final ContentType? contentType = response.headers.contentType; + final Encoding encoding = + Encoding.getByName(contentType?.charset) ?? utf8; + final 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); + final RegExp exp = RegExp(r'callback\( (.*) \)\;'); + final Match? match = exp.firstMatch(callback); + if (match?.groupCount == 1) { + final String? content = match!.group(1); + if (content != null) { + return TencentUnionidResp.fromJson( + json.decode(content) as Map); + } } } throw HttpException( @@ -184,18 +184,17 @@ class Tencent { /// 分享 - 说说 Future shareMood({ - @required int scene, - String summary, - List imageUris, - Uri videoUri, + required int scene, + String? summary, + List? imageUris, + Uri? videoUri, }) { assert(scene == TencentScene.SCENE_QZONE); assert((summary?.isNotEmpty ?? false) || - (imageUris?.isNotEmpty ?? false) || - (videoUri != null && videoUri.isScheme(_SCHEME_FILE)) || ((imageUris?.isNotEmpty ?? false) && - imageUris.every((Uri element) => - element != null && element.isScheme(_SCHEME_FILE)))); + imageUris! + .every((Uri element) => element.isScheme(_SCHEME_FILE))) || + (videoUri != null && videoUri.isScheme(_SCHEME_FILE))); return _channel.invokeMethod( _METHOD_SHAREMOOD, { @@ -203,7 +202,7 @@ class Tencent { if (summary?.isNotEmpty ?? false) _ARGUMENT_KEY_SUMMARY: summary, if (imageUris?.isNotEmpty ?? false) _ARGUMENT_KEY_IMAGEURIS: - imageUris.map((Uri imageUri) => imageUri.toString()).toList(), + imageUris!.map((Uri imageUri) => imageUri.toString()).toList(), if (videoUri != null) _ARGUMENT_KEY_VIDEOURI: videoUri.toString(), }, ); @@ -211,11 +210,10 @@ class Tencent { /// 分享 - 文本(Android调用的是系统API,故而不会有回调) Future shareText({ - @required int scene, - @required String summary, + required int scene, + required String summary, }) { assert(scene == TencentScene.SCENE_QQ); - assert(summary?.isNotEmpty ?? false); return _channel.invokeMethod( _METHOD_SHARETEXT, { @@ -227,13 +225,13 @@ class Tencent { /// 分享 - 图片 Future shareImage({ - @required int scene, - @required Uri imageUri, - String appName, + required int scene, + required Uri imageUri, + String? appName, int extInt = TencentQZoneFlag.DEFAULT, }) { assert(scene == TencentScene.SCENE_QQ); - assert(imageUri != null && imageUri.isScheme(_SCHEME_FILE)); + assert(imageUri.isScheme(_SCHEME_FILE)); return _channel.invokeMethod( _METHOD_SHAREIMAGE, { @@ -247,19 +245,16 @@ class Tencent { /// 分享 - 音乐 Future shareMusic({ - @required int scene, - @required String title, - String summary, - Uri imageUri, - @required String musicUrl, - @required String targetUrl, - String appName, + required int scene, + required String title, + String? summary, + Uri? imageUri, + required String musicUrl, + required String targetUrl, + String? appName, int extInt = TencentQZoneFlag.DEFAULT, }) { assert(scene == TencentScene.SCENE_QQ); - assert(title?.isNotEmpty ?? false); - assert(musicUrl?.isNotEmpty ?? false); - assert(targetUrl?.isNotEmpty ?? false); return _channel.invokeMethod( _METHOD_SHAREMUSIC, { @@ -277,16 +272,14 @@ class Tencent { /// 分享 - 网页 Future shareWebpage({ - @required int scene, - @required String title, - String summary, - Uri imageUri, - @required String targetUrl, - String appName, + required int scene, + required String title, + String? summary, + Uri? imageUri, + required String targetUrl, + String? appName, int extInt = TencentQZoneFlag.DEFAULT, }) { - assert(title?.isNotEmpty ?? false); - assert(targetUrl?.isNotEmpty ?? false); return _channel.invokeMethod( _METHOD_SHAREWEBPAGE, { diff --git a/lib/src/tencent_constant.dart b/lib/src/tencent_constant.dart index eff4aed..622309c 100644 --- a/lib/src/tencent_constant.dart +++ b/lib/src/tencent_constant.dart @@ -1,5 +1,5 @@ class TencentScope { - TencentScope._(); + const TencentScope._(); /// 发表一条说说到QQ空间(需要申请权限) static const String OPEN_PERMISSION_ADD_TOPIC = 'add_topic'; @@ -45,7 +45,7 @@ class TencentScope { } class TencentScene { - TencentScene._(); + const TencentScene._(); /// QQ static const int SCENE_QQ = 0; @@ -55,7 +55,7 @@ class TencentScene { } class TencentQZoneFlag { - TencentQZoneFlag._(); + const TencentQZoneFlag._(); /// 默认是不隐藏分享到QZone按钮且不自动打开分享到QZone的对话框 static const int DEFAULT = 0; diff --git a/lib/tencent_kit.dart b/lib/tencent_kit.dart index 03088bc..1e150fa 100644 --- a/lib/tencent_kit.dart +++ b/lib/tencent_kit.dart @@ -4,7 +4,6 @@ export 'src/model/api/tencent_api_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/model/unionid/tencent_unionid_resp.dart'; export 'src/tencent.dart'; export 'src/tencent_constant.dart'; diff --git a/pubspec.lock b/pubspec.lock index 11664c1..efd7c08 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,189 +7,182 @@ packages: name: _fe_analyzer_shared url: "https://pub.flutter-io.cn" source: hosted - version: "14.0.0" + version: "18.0.0" analyzer: dependency: transitive description: name: analyzer url: "https://pub.flutter-io.cn" source: hosted - version: "0.41.2" + version: "1.2.0" args: dependency: transitive description: name: args url: "https://pub.flutter-io.cn" source: hosted - version: "1.6.0" + version: "2.0.0" async: dependency: transitive description: name: async url: "https://pub.flutter-io.cn" source: hosted - version: "2.5.0-nullsafety.1" + version: "2.5.0" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0" build: dependency: transitive description: name: build url: "https://pub.flutter-io.cn" source: hosted - version: "1.6.1" + version: "2.0.0" build_config: dependency: transitive description: name: build_config url: "https://pub.flutter-io.cn" source: hosted - version: "0.4.5" + version: "0.4.7" build_daemon: dependency: transitive description: name: build_daemon url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.6" + version: "2.1.10" build_resolvers: dependency: transitive description: name: build_resolvers url: "https://pub.flutter-io.cn" source: hosted - version: "1.5.2" + version: "2.0.0" build_runner: dependency: "direct dev" description: name: build_runner url: "https://pub.flutter-io.cn" source: hosted - version: "1.11.0" + version: "1.12.2" build_runner_core: dependency: transitive description: name: build_runner_core url: "https://pub.flutter-io.cn" source: hosted - version: "6.1.6" + version: "6.1.12" built_collection: dependency: transitive description: name: built_collection url: "https://pub.flutter-io.cn" source: hosted - version: "4.3.2" + version: "5.0.0" built_value: dependency: transitive description: name: built_value url: "https://pub.flutter-io.cn" source: hosted - version: "7.1.0" + version: "8.0.3" characters: dependency: transitive description: name: characters url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.0-nullsafety.3" + version: "1.1.0" charcode: dependency: transitive description: name: charcode url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" checked_yaml: dependency: transitive description: name: checked_yaml url: "https://pub.flutter-io.cn" source: hosted - version: "1.0.4" + version: "2.0.1" cli_util: dependency: transitive description: name: cli_util url: "https://pub.flutter-io.cn" source: hosted - version: "0.2.0" + version: "0.3.0" clock: dependency: transitive description: name: clock url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0" code_builder: dependency: transitive description: name: code_builder url: "https://pub.flutter-io.cn" source: hosted - version: "3.6.0" + version: "3.7.0" collection: dependency: transitive description: name: collection url: "https://pub.flutter-io.cn" source: hosted - version: "1.15.0-nullsafety.3" + version: "1.15.0" convert: dependency: transitive description: name: convert url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.1" + version: "3.0.0" crypto: dependency: transitive description: name: crypto url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.5" + version: "3.0.0" dart_style: dependency: transitive description: name: dart_style url: "https://pub.flutter-io.cn" source: hosted - version: "1.3.11" + version: "1.3.14" fake_async: dependency: transitive description: name: fake_async url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.0-nullsafety.1" - ffi: - dependency: transitive - description: - name: ffi - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.1.3" + version: "1.2.0" file: dependency: transitive description: name: file url: "https://pub.flutter-io.cn" source: hosted - version: "5.2.1" + version: "6.1.0" fixnum: dependency: transitive description: name: fixnum url: "https://pub.flutter-io.cn" source: hosted - version: "0.10.11" + version: "1.0.0" flutter: dependency: "direct main" description: flutter @@ -206,231 +199,140 @@ packages: name: glob url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.0" + version: "2.0.0" graphs: dependency: transitive description: name: graphs url: "https://pub.flutter-io.cn" source: hosted - version: "0.2.0" + version: "1.0.0" http_multi_server: dependency: transitive description: name: http_multi_server url: "https://pub.flutter-io.cn" source: hosted - version: "2.2.0" + version: "3.0.0" http_parser: dependency: transitive description: name: http_parser url: "https://pub.flutter-io.cn" source: hosted - version: "3.1.4" - intl: - dependency: transitive - description: - name: intl - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.16.1" + version: "4.0.0" io: dependency: transitive description: name: io url: "https://pub.flutter-io.cn" source: hosted - version: "0.3.4" + version: "1.0.0" js: dependency: transitive description: name: js url: "https://pub.flutter-io.cn" source: hosted - version: "0.6.2" + version: "0.6.3" json_annotation: dependency: "direct main" description: name: json_annotation url: "https://pub.flutter-io.cn" source: hosted - version: "3.1.1" + version: "4.0.0" json_serializable: dependency: "direct dev" description: name: json_serializable url: "https://pub.flutter-io.cn" source: hosted - version: "3.5.1" + version: "4.0.3" logging: dependency: transitive description: name: logging url: "https://pub.flutter-io.cn" source: hosted - version: "0.11.4" + version: "1.0.0" matcher: dependency: transitive description: name: matcher url: "https://pub.flutter-io.cn" source: hosted - version: "0.12.10-nullsafety.1" + version: "0.12.10" meta: dependency: transitive description: name: meta url: "https://pub.flutter-io.cn" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.3.0" mime: dependency: transitive description: name: mime url: "https://pub.flutter-io.cn" source: hosted - version: "0.9.7" - node_interop: - dependency: transitive - description: - name: node_interop - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.2.1" - node_io: - dependency: transitive - description: - name: node_io - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.2.0" - okhttp_kit: - dependency: "direct dev" - description: - name: okhttp_kit - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.0.2" + version: "1.0.0" package_config: dependency: transitive description: name: package_config url: "https://pub.flutter-io.cn" source: hosted - version: "1.9.3" + version: "2.0.0" path: - dependency: "direct dev" + dependency: transitive description: name: path url: "https://pub.flutter-io.cn" source: hosted - version: "1.8.0-nullsafety.1" - path_provider: - dependency: "direct dev" - description: - name: path_provider - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.6.27" - path_provider_linux: - dependency: transitive - description: - name: path_provider_linux - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.0.1+2" - path_provider_macos: - dependency: transitive - description: - name: path_provider_macos - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.0.4+8" - path_provider_platform_interface: - dependency: transitive - description: - name: path_provider_platform_interface - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.0.4" - path_provider_windows: - dependency: transitive - description: - name: path_provider_windows - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.0.4+3" + version: "1.8.0" pedantic: dependency: "direct dev" description: name: pedantic url: "https://pub.flutter-io.cn" source: hosted - version: "1.9.2" - platform: - dependency: transitive - description: - name: platform - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.2.1" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.0.3" + version: "1.11.0" pool: dependency: transitive description: name: pool url: "https://pub.flutter-io.cn" source: hosted - version: "1.4.0" - process: - dependency: transitive - description: - name: process - url: "https://pub.flutter-io.cn" - source: hosted - version: "3.0.13" + version: "1.5.0" pub_semver: dependency: transitive description: name: pub_semver url: "https://pub.flutter-io.cn" source: hosted - version: "1.4.4" + version: "2.0.0" pubspec_parse: dependency: transitive description: name: pubspec_parse url: "https://pub.flutter-io.cn" source: hosted - version: "0.1.7" - quiver: - dependency: transitive - description: - name: quiver - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.1.5" + version: "1.0.0" shelf: dependency: transitive description: name: shelf url: "https://pub.flutter-io.cn" source: hosted - version: "0.7.9" + version: "1.1.0" shelf_web_socket: dependency: transitive description: name: shelf_web_socket url: "https://pub.flutter-io.cn" source: hosted - version: "0.2.4" + version: "1.0.1" sky_engine: dependency: transitive description: flutter @@ -442,112 +344,98 @@ packages: name: source_gen url: "https://pub.flutter-io.cn" source: hosted - version: "0.9.10+1" + version: "0.9.10+4" source_span: dependency: transitive description: name: source_span url: "https://pub.flutter-io.cn" source: hosted - version: "1.8.0-nullsafety.2" + version: "1.8.0" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.flutter-io.cn" source: hosted - version: "1.10.0-nullsafety.1" + version: "1.10.0" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0" stream_transform: dependency: transitive description: name: stream_transform url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.0" + version: "2.0.0" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" test_api: dependency: transitive description: name: test_api url: "https://pub.flutter-io.cn" source: hosted - version: "0.2.19-nullsafety.2" + version: "0.2.19" timing: dependency: transitive description: name: timing url: "https://pub.flutter-io.cn" source: hosted - version: "0.1.1+3" + version: "1.0.0" typed_data: dependency: transitive description: name: typed_data url: "https://pub.flutter-io.cn" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.3.0" vector_math: dependency: transitive description: name: vector_math url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.0-nullsafety.3" + version: "2.1.0" watcher: dependency: transitive description: name: watcher url: "https://pub.flutter-io.cn" source: hosted - version: "0.9.7+15" + version: "1.0.0" web_socket_channel: dependency: transitive description: name: web_socket_channel url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.0" - win32: - dependency: transitive - description: - name: win32 - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.7.4+1" - xdg_directories: - dependency: transitive - description: - name: xdg_directories - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.1.2" + version: "2.0.0" yaml: dependency: transitive description: name: yaml url: "https://pub.flutter-io.cn" source: hosted - version: "2.2.1" + version: "3.1.0" sdks: - dart: ">=2.10.0 <2.11.0" - flutter: ">=1.12.13+hotfix.5 <2.0.0" + dart: ">=2.12.0 <3.0.0" + flutter: ">=1.20.0" diff --git a/pubspec.yaml b/pubspec.yaml index 0ec118b..5ff3f6f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,28 +1,23 @@ name: tencent_kit description: A powerful Flutter plugin allowing developers to auth/share with natvie Android & iOS Tencent SDKs. -version: 2.0.1 +version: 2.1.0 # author: v7lin homepage: https://github.com/v7lin/fake_tencent environment: - sdk: ">=2.7.0 <3.0.0" - flutter: ">=1.10.0" + sdk: ">=2.12.0 <3.0.0" + flutter: ">=1.20.0" dependencies: flutter: sdk: flutter - json_annotation: '>=2.0.0 <4.0.0' + json_annotation: ^4.0.0 dev_dependencies: flutter_test: sdk: flutter - path: ^1.6.4 - path_provider: ^1.4.0 - - okhttp_kit: ^1.0.0 - pedantic: build_runner: diff --git a/test/tencent_kit_test.dart b/test/tencent_kit_test.dart index b9387ce..3f85764 100644 --- a/test/tencent_kit_test.dart +++ b/test/tencent_kit_test.dart @@ -10,7 +10,6 @@ void main() { TestWidgetsFlutterBinding.ensureInitialized(); const MethodChannel channel = MethodChannel('v7lin.github.io/tencent_kit'); - final Tencent tencent = Tencent(); setUp(() { channel.setMockMethodCallHandler((MethodCall call) async { @@ -26,7 +25,7 @@ void main() { channel.name, channel.codec.encodeMethodCall( MethodCall('onLoginResp', json.decode('{"ret":-2}'))), - (ByteData data) { + (ByteData? data) { // mock success }, )); @@ -42,7 +41,7 @@ void main() { channel.name, channel.codec.encodeMethodCall( MethodCall('onShareResp', json.decode('{"ret":0}'))), - (ByteData data) { + (ByteData? data) { // mock success }, )); @@ -57,30 +56,30 @@ void main() { }); test('isQQInstalled', () async { - expect(await tencent.isQQInstalled(), true); + expect(await Tencent.instance.isQQInstalled(), true); }); test('isTIMInstalled', () async { - expect(await tencent.isTIMInstalled(), true); + expect(await Tencent.instance.isTIMInstalled(), true); }); test('login', () async { - StreamSubscription sub = - tencent.loginResp().listen((TencentLoginResp resp) { + final StreamSubscription sub = + Tencent.instance.loginResp().listen((TencentLoginResp resp) { expect(resp.ret, TencentSdkResp.RET_USERCANCEL); }); - await tencent.login( + await Tencent.instance.login( scope: [TencentScope.GET_SIMPLE_USERINFO], ); await sub.cancel(); }); test('share', () async { - StreamSubscription sub = - tencent.shareResp().listen((TencentShareResp resp) { + final StreamSubscription sub = + Tencent.instance.shareResp().listen((TencentSdkResp resp) { expect(resp.ret, TencentSdkResp.RET_SUCCESS); }); - await tencent.shareMood( + await Tencent.instance.shareMood( scene: TencentScene.SCENE_QZONE, summary: 'share text', );