commit 9b896c3d0a121947ab231f949006cf1d9c488f18 Author: v7lin Date: Fri Mar 1 17:58:17 2019 +0800 清理历史 diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..57fb4d9 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,40 @@ +kind: pipeline +name: default + +steps: +- name: prepare + image: nathansamson/flutter-builder-docker:v1.0.0 + volumes: + - name: pub-cache + path: /opt/flutter/.pub-cache + commands: + - flutter packages get + +- name: analyze + image: nathansamson/flutter-builder-docker:v1.0.0 + volumes: + - name: pub-cache + path: /opt/flutter/.pub-cache + commands: + - flutter analyze + +- name: test + image: nathansamson/flutter-builder-docker:v1.0.0 + volumes: + - name: pub-cache + path: /opt/flutter/.pub-cache + commands: + - pushd example/ + - flutter test + +- name: publish-check + image: nathansamson/flutter-builder-docker:v1.0.0 + volumes: + - name: pub-cache + path: /opt/flutter/.pub-cache + commands: + - flutter packages pub publish --dry-run + +volumes: +- name: pub-cache + temp: {} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..ff1b95c --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* linguist-language=Dart \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c9e2ce8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +.DS_Store +.dart_tool/ + +.packages +.pub/ +pubspec.lock + +build/ + +.idea/ +*.iml diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..2517d63 --- /dev/null +++ b/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 5391447fae6209bb21a89e6a5a6583cac1af9b4b + channel: stable + +project_type: plugin diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..30f0831 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 - 2019.3.1 + +* android/ios tencent diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..38ae56a --- /dev/null +++ b/README.md @@ -0,0 +1,103 @@ +# fake_tencent + +[![Build Status](https://cloud.drone.io/api/badges/v7lin/fake_tencent/status.svg)](https://cloud.drone.io/v7lin/fake_tencent) +[![GitHub tag](https://img.shields.io/github/tag/v7lin/fake_tencent.svg)](https://github.com/v7lin/fake_tencent/releases) + +flutter版腾讯(QQ)SDK + +## Fake 系列 Library + +1. [flutter版okhttp3](https://github.com/v7lin/fake_http) +2. [flutter版微信SDK](https://github.com/v7lin/fake_wechat) +3. [flutter版腾讯(QQ)SDK](https://github.com/v7lin/fake_tencent) +4. [flutter版新浪微博SDK](https://github.com/v7lin/fake_weibo) +5. [flutter版支付宝SDK](https://github.com/v7lin/fake_alipay) +6. [flutter版百度移动统计SDK](https://github.com/v7lin/fake_analytics) +7. [flutter版百度移动推送SDK](https://github.com/v7lin/fake_push) + +## android + +```` +... +android { + ... + defaultConfig{ + ... + manifestPlaceholders = [TENCENT_APP_ID: "${appId}"] + ... + } + ... +} +... +```` + +```` +# 混淆已打入 Library,随 Library 引用,自动添加到 apk 打包混淆 +```` + +## ios + +```` +在Xcode中,选择你的工程设置项,选中“TARGETS”一栏,在“info”标签栏的“URL type“添加“URL scheme”为你所注册的应用程序id + +URL Types +tencent: identifier=tencent schemes=tencent${appId} +```` + +```` +iOS 9系统策略更新,限制了http协议的访问,此外应用需要在“Info.plist”中将要使用的URL Schemes列为白名单,才可正常检查其他应用是否安装。 + +LSApplicationQueriesSchemes + + wtloginmqq2 + mqqopensdkapiV3 + mqqwpa + mqqopensdkapiV2 + mqqOpensdkSSoLogin + mqq + +NSAppTransportSecurity + + NSAllowsArbitraryLoads + + +```` + +## flutter + +|分享类型|说说|图片|音乐|视频|网页| +|:---:|:---:|:---:|:---:|:---:|:---:| +|QQ|不支持|支持|支持|不支持|支持| +|QZone|支持|不支持|不支持|不支持|支持| + +### snapshot +```` +dependencies: + fake_tencent: + git: + url: https://github.com/v7lin/fake_tencent.git +```` + +### release +```` +latestVersion = 0.0.1 +```` + +```` +dependencies: + fake_tencent: ^${latestVersion} +```` + +### example +[示例](./example/lib/main.dart) + +## Getting Started + +This project is a starting point for a Flutter +[plug-in package](https://flutter.io/developing-packages/), +a specialized package that includes platform-specific implementation code for +Android and/or iOS. + +For help getting started with Flutter, view our +[online documentation](https://flutter.io/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..8383c4a --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,128 @@ +# Defines a default set of lint rules enforced for +# projects at Google. For details and rationale, +# see https://github.com/dart-lang/pedantic#enabled-lints. +include: package:pedantic/analysis_options.yaml + +analyzer: + strong-mode: + implicit-casts: false + # implicit-dynamic: false + + errors: + # treat missing required parameters as a warning (not a hint) + missing_required_param: warning + # treat missing returns as a warning (not a hint) + missing_return: warning + # allow having TODOs in the code + todo: ignore + # Ignore errors like + # 'super_goes_last' is a deprecated lint rule and should not be used • included_file_warning + included_file_warning: ignore + +linter: + rules: + # these rules are documented on and in the same order as + # the Dart Lint rules page to make maintenance easier + # http://dart-lang.github.io/linter/lints/ + + # === error rules === + - avoid_empty_else + - avoid_slow_async_io + - cancel_subscriptions + # - close_sinks # https://github.com/flutter/flutter/issues/5789 + # - comment_references # blocked on https://github.com/dart-lang/dartdoc/issues/1153 + - control_flow_in_finally + - empty_statements + - hash_and_equals + # - invariant_booleans # https://github.com/flutter/flutter/issues/5790 + - iterable_contains_unrelated_type + - list_remove_unrelated_type + # - literal_only_boolean_expressions # https://github.com/flutter/flutter/issues/5791 + - no_adjacent_strings_in_list + - no_duplicate_case_values + - test_types_in_equals + - throw_in_finally + - unrelated_type_equality_checks + - valid_regexps + + # === style rules === + - always_declare_return_types + # - always_put_control_body_on_new_line + - always_require_non_null_named_parameters + - always_specify_types + - annotate_overrides + # - avoid_annotating_with_dynamic # not yet tested + # - avoid_as # 2019-01-30 removed for no-implicit-casts + # - avoid_catches_without_on_clauses # not yet tested + # - avoid_catching_errors # not yet tested + # - avoid_classes_with_only_static_members # not yet tested + # - avoid_function_literals_in_foreach_calls # not yet tested + - avoid_init_to_null + - avoid_null_checks_in_equality_operators + # - avoid_positional_boolean_parameters # not yet tested + - avoid_return_types_on_setters + # - avoid_returning_null # not yet tested + # - avoid_returning_this # not yet tested + # - avoid_setters_without_getters # not yet tested + # - avoid_types_on_closure_parameters # not yet tested + - await_only_futures + - camel_case_types + # - cascade_invocations # not yet tested + # - constant_identifier_names # https://github.com/dart-lang/linter/issues/204 + - directives_ordering + - empty_catches + - empty_constructor_bodies + - implementation_imports + # - join_return_with_assignment # not yet tested + - library_names + - library_prefixes + - non_constant_identifier_names + # - omit_local_variable_types # opposite of always_specify_types + # - one_member_abstracts # too many false positives + # - only_throw_errors # https://github.com/flutter/flutter/issues/5792 + - overridden_fields + - package_api_docs + - package_prefixed_library_names + # - parameter_assignments # we do this commonly + - prefer_adjacent_string_concatenation + - prefer_collection_literals + # - prefer_conditional_assignment # not yet tested + - prefer_const_constructors + # - prefer_constructors_over_static_methods # not yet tested + - prefer_contains + - prefer_equal_for_default_values + # - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods + # - prefer_final_fields # https://github.com/dart-lang/linter/issues/506 + # - prefer_final_locals + # - prefer_foreach # not yet tested + # - prefer_function_declarations_over_variables # not yet tested + - prefer_initializing_formals + # - prefer_interpolation_to_compose_strings # not yet tested + - prefer_is_empty + - prefer_is_not_empty + - prefer_void_to_null + # - recursive_getters # https://github.com/dart-lang/linter/issues/452 + - slash_for_doc_comments + - sort_constructors_first + - sort_unnamed_constructors_first + # - super_goes_last + # - type_annotate_public_apis # subset of always_specify_types + - type_init_formals + # - unawaited_futures # https://github.com/flutter/flutter/issues/5793 + - unnecessary_brace_in_string_interps + - unnecessary_const + - unnecessary_getters_setters + # - unnecessary_lambdas # https://github.com/dart-lang/linter/issues/498 + - unnecessary_new + - unnecessary_null_aware_assignments + - unnecessary_null_in_if_null_operators + # - unnecessary_overrides # https://github.com/dart-lang/linter/issues/626 and https://github.com/dart-lang/linter/issues/627 + - unnecessary_statements + - unnecessary_this + - use_rethrow_when_possible + # - use_setters_to_change_properties # not yet tested + # - use_string_buffers # https://github.com/dart-lang/linter/pull/664 + # - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review + + # === pub rules === + - package_names \ No newline at end of file diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..c6cbe56 --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,8 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..74570d7 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,40 @@ +group 'io.github.v7lin.faketencent' +version '1.0-SNAPSHOT' + +buildscript { + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.2.1' + } +} + +rootProject.allprojects { + repositories { + google() + jcenter() + } +} + +apply plugin: 'com.android.library' + +android { + compileSdkVersion 27 + + defaultConfig { + minSdkVersion 16 + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + lintOptions { + disable 'InvalidPackage' + } +} + +dependencies { +// implementation fileTree(include: ['*.jar'], dir: 'libs') + + implementation 'io.github.v7lin:tencent-android:1.0.0' +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..8bd86f6 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1 @@ +org.gradle.jvmargs=-Xmx1536M diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 0000000..4e2a747 --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'fake_tencent' diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000..39d7c33 --- /dev/null +++ b/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/android/src/main/java/io/github/v7lin/faketencent/FakeTencentPlugin.java b/android/src/main/java/io/github/v7lin/faketencent/FakeTencentPlugin.java new file mode 100644 index 0000000..21d02f9 --- /dev/null +++ b/android/src/main/java/io/github/v7lin/faketencent/FakeTencentPlugin.java @@ -0,0 +1,505 @@ +package io.github.v7lin.faketencent; + +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Bundle; +import android.text.TextUtils; + +import com.tencent.connect.UserInfo; +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.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +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; +import io.flutter.plugin.common.PluginRegistry.Registrar; + +/** + * FakeTencentPlugin + */ +public class FakeTencentPlugin implements MethodCallHandler, PluginRegistry.ActivityResultListener { + /** + * Plugin registration. + */ + public static void registerWith(Registrar registrar) { + final MethodChannel channel = new MethodChannel(registrar.messenger(), "v7lin.github.io/fake_tencent"); + FakeTencentPlugin plugin = new FakeTencentPlugin(registrar, channel); + registrar.addActivityResultListener(plugin); + channel.setMethodCallHandler(plugin); + } + + private static class FakeTencentScene { + public static final int SCENE_QQ = 0; + public static final int SCENE_QZONE = 1; + } + + private static class FakeTencentErrorCode { + public static final int ERRORCODE_SUCCESS = 0; + public static final int ERRORCODE_COMMON = -1; + public static final int ERRORCODE_USERCANCEL = -2; + } + + private static final String METHOD_REGISTERAPP = "registerApp"; + private static final String METHOD_ISQQINSTALLED = "isQQInstalled"; + private static final String METHOD_ISQQSUPPORTSSOLOGIN = "isQQSupportSSOLogin"; + private static final String METHOD_SETACCESSTOKEN = "setAccessToken"; + private static final String METHOD_LOGIN = "login"; + private static final String METHOD_LOGOUT = "logout"; + private static final String METHOD_GETUSERINFO = "getUserInfo"; + private static final String METHOD_SHAREMOOD = "shareMood"; + private static final String METHOD_SHAREIMAGE = "shareImage"; + private static final String METHOD_SHAREMUSIC = "shareMusic"; + private static final String METHOD_SHAREWEBPAGE = "shareWebpage"; + + private static final String METHOD_ONLOGINRESP = "onLoginResp"; + private static final String METHOD_ONGETUSERINFORESP = "onGetUserInfoResp"; + private static final String METHOD_ONSHARERESP = "onShareResp"; + + private static final String ARGUMENT_KEY_APPID = "appId"; + private static final String ARGUMENT_KEY_OPENID = "openId"; + private static final String ARGUMENT_KEY_ACCESSTOKEN = "accessToken"; + private static final String ARGUMENT_KEY_EXPIRATIONDATE = "expirationDate"; + 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_ERRORCODE = "errorCode"; + private static final String ARGUMENT_KEY_RESULT_ERRORMSG = "errorMsg"; + private static final String ARGUMENT_KEY_RESULT_OPENID = "openId"; + private static final String ARGUMENT_KEY_RESULT_ACCESSTOKEN = "accessToken"; + private static final String ARGUMENT_KEY_RESULT_EXPIRATIONDATE = "expirationDate"; + + private static final String URLREQUEST_KEY_RET = "ret"; + private static final String URLREQUEST_KEY_MSG = "msg"; + // 网络请求成功发送至服务器,并且服务器返回数据格式正确 + // 这里包括所请求业务操作失败的情况,例如没有授权等原因导致 + private static final int URLREQUEST_SUCCEED = 0; + // 网络异常,或服务器返回的数据格式不正确导致无法解析 + private static final int URLREQUEST_FAILED = 1; + + private static final String SCHEME_FILE = "file"; + + private final Registrar registrar; + private final MethodChannel channel; + + private Tencent tencent; + + private FakeTencentPlugin(Registrar registrar, MethodChannel channel) { + this.registrar = registrar; + this.channel = channel; + } + + @Override + public void onMethodCall(MethodCall call, Result result) { + if (METHOD_REGISTERAPP.equals(call.method)) { + String appId = call.argument(ARGUMENT_KEY_APPID); + tencent = Tencent.createInstance(appId, registrar.context().getApplicationContext()); + result.success(null); + } else if (METHOD_ISQQINSTALLED.equals(call.method)) { + boolean isQQInstalled = false; + try { + final PackageManager packageManager = registrar.context().getPackageManager(); + PackageInfo info = packageManager.getPackageInfo("com.tencent.mobileqq", PackageManager.GET_SIGNATURES); + isQQInstalled = info != null; + } catch (PackageManager.NameNotFoundException e) { + } + result.success(isQQInstalled); + } else if (METHOD_ISQQSUPPORTSSOLOGIN.equals(call.method)) { + result.success(tencent.isSupportSSOLogin(registrar.activity())); + } else if (METHOD_SETACCESSTOKEN.equals(call.method)) { + setAccessToken(call, result); + } else if (METHOD_LOGIN.equals(call.method)) { + login(call, result); + } else if (METHOD_LOGOUT.equals(call.method)) { + logout(call, result); + } else if (METHOD_GETUSERINFO.equals(call.method)) { + getUserInfo(call, result); + } else if (METHOD_SHAREMOOD.equals(call.method)) { + shareMood(call, result); + } else if (METHOD_SHAREIMAGE.equals(call.method)) { + 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 setAccessToken(MethodCall call, Result result) { + if (tencent != null) { + String openId = call.argument(ARGUMENT_KEY_OPENID); + String accessToken = call.argument(ARGUMENT_KEY_ACCESSTOKEN); + long expirationDate = call.argument(ARGUMENT_KEY_EXPIRATIONDATE); + long expiresIn = TimeUnit.MILLISECONDS.toSeconds(expirationDate - System.currentTimeMillis()); + if (expiresIn > 0) { + tencent.setOpenId(openId); + tencent.setAccessToken(accessToken, String.valueOf(expiresIn)); + } + } + result.success(null); + } + + private void login(MethodCall call, Result result) { + if (tencent != null) { + String scope = call.argument(ARGUMENT_KEY_SCOPE); + tencent.login(registrar.activity(), scope, loginListener); + } + result.success(null); + } + + private IUiListener loginListener = new IUiListener() { + private static final String KEY_OPENID = "openid"; + private static final String KEY_ACCESS_TOKEN = "access_token"; + private static final String KEY_EXPIRES_IN = "expires_in"; + + @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(URLREQUEST_KEY_RET) ? object.getInt(URLREQUEST_KEY_RET) : URLREQUEST_FAILED; + String msg = !object.isNull(URLREQUEST_KEY_MSG) ? object.getString(URLREQUEST_KEY_MSG) : null; + if (ret == URLREQUEST_SUCCEED) { + String openId = !object.isNull(KEY_OPENID) ? object.getString(KEY_OPENID) : null; + String accessToken = !object.isNull(KEY_ACCESS_TOKEN) ? object.getString(KEY_ACCESS_TOKEN) : null; + final long expiresIn = !object.isNull(KEY_EXPIRES_IN) ? object.getLong(KEY_EXPIRES_IN) : 0; + if (!TextUtils.isEmpty(openId) && !TextUtils.isEmpty(accessToken)) { + tencent.setOpenId(openId); + tencent.setAccessToken(accessToken, String.valueOf(expiresIn)); + map.put(ARGUMENT_KEY_RESULT_ERRORCODE, FakeTencentErrorCode.ERRORCODE_SUCCESS); + map.put(ARGUMENT_KEY_RESULT_OPENID, tencent.getOpenId()); + map.put(ARGUMENT_KEY_RESULT_ACCESSTOKEN, tencent.getAccessToken()); + map.put(ARGUMENT_KEY_RESULT_EXPIRATIONDATE, tencent.getExpiresIn()); + } else { + map.put(ARGUMENT_KEY_RESULT_ERRORCODE, FakeTencentErrorCode.ERRORCODE_COMMON); + map.put(ARGUMENT_KEY_RESULT_ERRORMSG, "openId or accessToken is null."); + } + } else { + map.put(ARGUMENT_KEY_RESULT_ERRORCODE, FakeTencentErrorCode.ERRORCODE_COMMON); + map.put(ARGUMENT_KEY_RESULT_ERRORMSG, msg); + } + } + } catch (JSONException e) { + map.put(ARGUMENT_KEY_RESULT_ERRORCODE, FakeTencentErrorCode.ERRORCODE_COMMON); + map.put(ARGUMENT_KEY_RESULT_ERRORMSG, e.getMessage()); + } + channel.invokeMethod(METHOD_ONLOGINRESP, map); + } + + @Override + public void onError(UiError uiError) { + // 登录失败 + Map map = new HashMap<>(); + map.put(ARGUMENT_KEY_RESULT_ERRORCODE, FakeTencentErrorCode.ERRORCODE_COMMON); + map.put(ARGUMENT_KEY_RESULT_ERRORMSG, uiError.errorMessage); + channel.invokeMethod(METHOD_ONLOGINRESP, map); + } + + @Override + public void onCancel() { + // 取消登录 + Map map = new HashMap<>(); + map.put(ARGUMENT_KEY_RESULT_ERRORCODE, FakeTencentErrorCode.ERRORCODE_USERCANCEL); + channel.invokeMethod(METHOD_ONLOGINRESP, map); + } + }; + + private void logout(MethodCall call, Result result) { + if (tencent != null) { + tencent.logout(registrar.context()); + result.success(null); + } + } + + private void getUserInfo(MethodCall call, Result result) { + if (tencent != null) { + UserInfo info = new UserInfo(registrar.context().getApplicationContext(), tencent.getQQToken()); + info.getUserInfo(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(URLREQUEST_KEY_RET) ? object.getInt(URLREQUEST_KEY_RET) : URLREQUEST_FAILED; + String msg = !object.isNull(URLREQUEST_KEY_MSG) ? object.getString(URLREQUEST_KEY_MSG) : null; + if (ret == URLREQUEST_SUCCEED) { + map.put(ARGUMENT_KEY_RESULT_ERRORCODE, FakeTencentErrorCode.ERRORCODE_SUCCESS); + Iterator keys = object.keys(); + while (keys.hasNext()) { + String key = keys.next(); + map.put(key, object.get(key)); + } + } else { + map.put(ARGUMENT_KEY_RESULT_ERRORCODE, FakeTencentErrorCode.ERRORCODE_COMMON); + map.put(ARGUMENT_KEY_RESULT_ERRORMSG, msg); + } + } + } catch (JSONException e) { + map.put(ARGUMENT_KEY_RESULT_ERRORCODE, FakeTencentErrorCode.ERRORCODE_COMMON); + map.put(ARGUMENT_KEY_RESULT_ERRORMSG, e.getMessage()); + } + channel.invokeMethod(METHOD_ONGETUSERINFORESP, map); + } + + @Override + public void onError(UiError error) { + Map map = new HashMap<>(); + map.put(ARGUMENT_KEY_RESULT_ERRORCODE, FakeTencentErrorCode.ERRORCODE_COMMON); + map.put(ARGUMENT_KEY_RESULT_ERRORMSG, error.errorMessage); + channel.invokeMethod(METHOD_ONGETUSERINFORESP, map); + } + + @Override + public void onCancel() { + // do nothing + } + }); + } + result.success(null); + } + + private void shareMood(MethodCall call, Result result) { + if (tencent != null) { + final int scene = call.argument(ARGUMENT_KEY_SCENE); + if (scene == FakeTencentScene.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); + } + tencent.publishToQzone(registrar.activity(), params, shareListener); + } + } + result.success(null); + } + + private void shareImage(MethodCall call, Result result) { + if (tencent != null) { + final int scene = call.argument(ARGUMENT_KEY_SCENE); + if (scene == FakeTencentScene.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); + tencent.shareToQQ(registrar.activity(), params, shareListener); + } + } + result.success(null); + } + + private void shareMusic(MethodCall call, Result result) { + if (tencent != null) { + final int scene = call.argument(ARGUMENT_KEY_SCENE); + if (scene == FakeTencentScene.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); + tencent.shareToQQ(registrar.activity(), params, shareListener); + } + } + result.success(null); + } + + private void shareWebpage(MethodCall call, Result result) { + if (tencent != null) { + final 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 FakeTencentScene.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); + tencent.shareToQQ(registrar.activity(), params, shareListener); + break; + case FakeTencentScene.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); + tencent.shareToQzone(registrar.activity(), params, shareListener); + 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(URLREQUEST_KEY_RET) ? object.getInt(URLREQUEST_KEY_RET) : URLREQUEST_FAILED; + String msg = !object.isNull(URLREQUEST_KEY_MSG) ? object.getString(URLREQUEST_KEY_MSG) : null; + if (ret == URLREQUEST_SUCCEED) { + map.put(ARGUMENT_KEY_RESULT_ERRORCODE, FakeTencentErrorCode.ERRORCODE_SUCCESS); + } else { + map.put(ARGUMENT_KEY_RESULT_ERRORCODE, FakeTencentErrorCode.ERRORCODE_COMMON); + map.put(ARGUMENT_KEY_RESULT_ERRORMSG, msg); + } + } + } catch (JSONException e) { + map.put(ARGUMENT_KEY_RESULT_ERRORCODE, FakeTencentErrorCode.ERRORCODE_COMMON); + map.put(ARGUMENT_KEY_RESULT_ERRORMSG, e.getMessage()); + } + channel.invokeMethod(METHOD_ONSHARERESP, map); + } + + @Override + public void onError(UiError error) { + Map map = new HashMap<>(); + map.put(ARGUMENT_KEY_RESULT_ERRORCODE, FakeTencentErrorCode.ERRORCODE_COMMON); + map.put(ARGUMENT_KEY_RESULT_ERRORMSG, error.errorMessage); + channel.invokeMethod(METHOD_ONSHARERESP, map); + } + + @Override + public void onCancel() { + Map map = new HashMap<>(); + map.put(ARGUMENT_KEY_RESULT_ERRORCODE, FakeTencentErrorCode.ERRORCODE_USERCANCEL); + channel.invokeMethod(METHOD_ONSHARERESP, map); + } + }; + + // --- ActivityResultListener + + @Override + public boolean onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case Constants.REQUEST_API: + // unused + break; + case Constants.REQUEST_LOGIN: + return Tencent.onActivityResultData(requestCode, resultCode, data, loginListener); + case Constants.REQUEST_QQ_SHARE: + return Tencent.onActivityResultData(requestCode, resultCode, data, shareListener); + case Constants.REQUEST_QZONE_SHARE: + return Tencent.onActivityResultData(requestCode, resultCode, data, shareListener); + case Constants.REQUEST_QQ_FAVORITES: + // unused + break; + case Constants.REQUEST_SEND_TO_MY_COMPUTER: + // unused + break; + case Constants.REQUEST_SHARE_TO_TROOP_BAR: + // unused + break; + } + return false; + } +} diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 0000000..bfaabe0 --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,73 @@ +# Miscellaneous +*.class +*.lock +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# Visual Studio Code related +.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +.dart_tool/ +.flutter-plugins +.packages +.pub-cache/ +.pub/ +build/ + +# Android related +**/android/**/gradle-wrapper.jar +**/android/.gradle +**/android/captures/ +**/android/gradlew +**/android/gradlew.bat +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java + +# iOS/XCode related +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* +**/ios/Frameworks/ +**/ios/Runner.xcworkspace/xcshareddata/ + +# Exceptions to above rules. +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages diff --git a/example/.metadata b/example/.metadata new file mode 100644 index 0000000..460bc20 --- /dev/null +++ b/example/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 5391447fae6209bb21a89e6a5a6583cac1af9b4b + channel: stable + +project_type: app diff --git a/example/README.md b/example/README.md new file mode 100644 index 0000000..52628e9 --- /dev/null +++ b/example/README.md @@ -0,0 +1,16 @@ +# fake_tencent_example + +Demonstrates how to use the fake_tencent plugin. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://flutter.io/docs/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://flutter.io/docs/cookbook) + +For help getting started with Flutter, view our +[online documentation](https://flutter.io/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml new file mode 100644 index 0000000..c4c4b52 --- /dev/null +++ b/example/analysis_options.yaml @@ -0,0 +1,31 @@ +# Defines a default set of lint rules enforced for +# projects at Google. For details and rationale, +# see https://github.com/dart-lang/pedantic#enabled-lints. +include: package:pedantic/analysis_options.yaml + +analyzer: + strong-mode: + implicit-casts: false + # implicit-dynamic: false + +linter: + rules: + - iterable_contains_unrelated_type + - list_remove_unrelated_type + - test_types_in_equals + - unrelated_type_equality_checks + - valid_regexps + - annotate_overrides + - hash_and_equals + - prefer_is_not_empty + - avoid_empty_else + - cancel_subscriptions + - close_sinks + - always_declare_return_types + - camel_case_types + - empty_constructor_bodies + - avoid_init_to_null + - constant_identifier_names + - one_member_abstracts + - slash_for_doc_comments + - sort_constructors_first \ No newline at end of file diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle new file mode 100644 index 0000000..49146b0 --- /dev/null +++ b/example/android/app/build.gradle @@ -0,0 +1,64 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 27 + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "io.github.v7lin.faketencentexample" + minSdkVersion 16 + targetSdkVersion 27 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + + manifestPlaceholders = [TENCENT_APP_ID: "222222"] + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + testImplementation 'junit:junit:4.12' + androidTestImplementation 'com.android.support.test:runner:1.0.2' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' +} diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..3011826 --- /dev/null +++ b/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + diff --git a/example/android/app/src/main/java/io/github/v7lin/faketencentexample/MainActivity.java b/example/android/app/src/main/java/io/github/v7lin/faketencentexample/MainActivity.java new file mode 100644 index 0000000..7c1d0e9 --- /dev/null +++ b/example/android/app/src/main/java/io/github/v7lin/faketencentexample/MainActivity.java @@ -0,0 +1,13 @@ +package io.github.v7lin.faketencentexample; + +import android.os.Bundle; +import io.flutter.app.FlutterActivity; +import io.flutter.plugins.GeneratedPluginRegistrant; + +public class MainActivity extends FlutterActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + GeneratedPluginRegistrant.registerWith(this); + } +} diff --git a/example/android/app/src/main/res/drawable/launch_background.xml b/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..db77bb4 Binary files /dev/null and b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..17987b7 Binary files /dev/null and b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..09d4391 Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..d5f1c8d Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..4d6372e Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..00fa441 --- /dev/null +++ b/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + diff --git a/example/android/build.gradle b/example/android/build.gradle new file mode 100644 index 0000000..bb8a303 --- /dev/null +++ b/example/android/build.gradle @@ -0,0 +1,29 @@ +buildscript { + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.2.1' + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/example/android/gradle.properties b/example/android/gradle.properties new file mode 100644 index 0000000..8bd86f6 --- /dev/null +++ b/example/android/gradle.properties @@ -0,0 +1 @@ +org.gradle.jvmargs=-Xmx1536M diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..2819f02 --- /dev/null +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle new file mode 100644 index 0000000..5a2f14f --- /dev/null +++ b/example/android/settings.gradle @@ -0,0 +1,15 @@ +include ':app' + +def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + +def plugins = new Properties() +def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') +if (pluginsFile.exists()) { + pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } +} + +plugins.each { name, path -> + def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() + include ":$name" + project(":$name").projectDir = pluginDirectory +} diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..9367d48 --- /dev/null +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 8.0 + + diff --git a/example/ios/Flutter/Debug.xcconfig b/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..e8efba1 --- /dev/null +++ b/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/example/ios/Flutter/Release.xcconfig b/example/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..399e934 --- /dev/null +++ b/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/example/ios/Podfile b/example/ios/Podfile new file mode 100644 index 0000000..d077b08 --- /dev/null +++ b/example/ios/Podfile @@ -0,0 +1,69 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def parse_KV_file(file, separator='=') + file_abs_path = File.expand_path(file) + if !File.exists? file_abs_path + return []; + end + pods_ary = [] + skip_line_start_symbols = ["#", "/"] + File.foreach(file_abs_path) { |line| + next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } + plugin = line.split(pattern=separator) + if plugin.length == 2 + podname = plugin[0].strip() + path = plugin[1].strip() + podpath = File.expand_path("#{path}", file_abs_path) + pods_ary.push({:name => podname, :path => podpath}); + else + puts "Invalid plugin specification: #{line}" + end + } + return pods_ary +end + +target 'Runner' do + # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock + # referring to absolute paths on developers' machines. + system('rm -rf .symlinks') + system('mkdir -p .symlinks/plugins') + + # Flutter Pods + generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig') + if generated_xcode_build_settings.empty? + puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first." + end + generated_xcode_build_settings.map { |p| + if p[:name] == 'FLUTTER_FRAMEWORK_DIR' + symlink = File.join('.symlinks', 'flutter') + File.symlink(File.dirname(p[:path]), symlink) + pod 'Flutter', :path => File.join(symlink, File.basename(p[:path])) + end + } + + # Plugin Pods + plugin_pods = parse_KV_file('../.flutter-plugins') + plugin_pods.map { |p| + symlink = File.join('.symlinks', 'plugins', p[:name]) + File.symlink(p[:path], symlink) + pod p[:name], :path => File.join(symlink, 'ios') + } +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings['ENABLE_BITCODE'] = 'NO' + end + end +end diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..ebe36aa --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,570 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 2D5378251FAA1A9400D5DBA9 /* flutter_assets */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; + 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 9422253582A6C9D275F8D188 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EC2B9EE613E5EDCF95CDB72B /* libPods-Runner.a */; }; + 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; + 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; + 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, + 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = Flutter/flutter_assets; sourceTree = SOURCE_ROOT; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + EC2B9EE613E5EDCF95CDB72B /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, + 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, + 9422253582A6C9D275F8D188 /* libPods-Runner.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 5C6AC5BA9442E9AF04E7447D /* Pods */ = { + isa = PBXGroup; + children = ( + ); + name = Pods; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 2D5378251FAA1A9400D5DBA9 /* flutter_assets */, + 3B80C3931E831B6300D905FE /* App.framework */, + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEBA1CF902C7004384FC /* Flutter.framework */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 5C6AC5BA9442E9AF04E7447D /* Pods */, + D579DE886E1BD687DF6DBF83 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 97C146F11CF9000F007C117D /* Supporting Files */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 97C146F21CF9000F007C117D /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + D579DE886E1BD687DF6DBF83 /* Frameworks */ = { + isa = PBXGroup; + children = ( + EC2B9EE613E5EDCF95CDB72B /* libPods-Runner.a */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 0D238ADF129651F8319C4F7F /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 7175A6E06323B12A711474E5 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0910; + ORGANIZATIONNAME = "The Chromium Authors"; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + DevelopmentTeam = 78W43A3TE2; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 0D238ADF129651F8319C4F7F /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; + }; + 7175A6E06323B12A711474E5 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, + 97C146F31CF9000F007C117D /* main.m in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 78W43A3TE2; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.github.v7lin.fakeTencentExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 78W43A3TE2; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.github.v7lin.fakeTencentExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 78W43A3TE2; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.github.v7lin.fakeTencentExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..e5e8f02 --- /dev/null +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/example/ios/Runner/AppDelegate.h b/example/ios/Runner/AppDelegate.h new file mode 100644 index 0000000..36e21bb --- /dev/null +++ b/example/ios/Runner/AppDelegate.h @@ -0,0 +1,6 @@ +#import +#import + +@interface AppDelegate : FlutterAppDelegate + +@end diff --git a/example/ios/Runner/AppDelegate.m b/example/ios/Runner/AppDelegate.m new file mode 100644 index 0000000..59a72e9 --- /dev/null +++ b/example/ios/Runner/AppDelegate.m @@ -0,0 +1,13 @@ +#include "AppDelegate.h" +#include "GeneratedPluginRegistrant.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [GeneratedPluginRegistrant registerWithRegistry:self]; + // Override point for customization after application launch. + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +@end diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000..3d43d11 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000..28c6bf0 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000..2ccbfd9 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000..f091b6b Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000..4cde121 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000..d0ef06e Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 0000000..dcdc230 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 0000000..2ccbfd9 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000..c8f9ed8 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000..a6d6b86 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000..a6d6b86 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000..75b2d16 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000..c4df70d Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 0000000..6a84f41 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000..d0e1f58 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..0bedcf2 --- /dev/null +++ b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/Runner/Base.lproj/Main.storyboard b/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist new file mode 100644 index 0000000..b36866a --- /dev/null +++ b/example/ios/Runner/Info.plist @@ -0,0 +1,72 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + fake_tencent_example + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + tencent + CFBundleURLSchemes + + tencent222222 + + + + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSApplicationQueriesSchemes + + wtloginmqq2 + mqqopensdkapiV3 + mqqwpa + mqqopensdkapiV2 + mqqOpensdkSSoLogin + mqq + + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/example/ios/Runner/main.m b/example/ios/Runner/main.m new file mode 100644 index 0000000..dff6597 --- /dev/null +++ b/example/ios/Runner/main.m @@ -0,0 +1,9 @@ +#import +#import +#import "AppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/example/lib/main.dart b/example/lib/main.dart new file mode 100644 index 0000000..3d82383 --- /dev/null +++ b/example/lib/main.dart @@ -0,0 +1,158 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:fake_tencent/fake_tencent.dart'; + +void main() { + runZoned(() { + runApp(MyApp()); + }, onError: (Object error, StackTrace stack) { + print(error); + print(stack); + }); + + if (Platform.isAndroid) { + SystemUiOverlayStyle systemUiOverlayStyle = + SystemUiOverlayStyle(statusBarColor: Colors.transparent); + SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle); + } +} + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + FakeTencent tencent = FakeTencent(); + tencent.registerApp(appId: '222222'); + return FakeTencentProvider( + tencent: tencent, + child: MaterialApp( + home: Home(tencent: tencent), + ), + ); + } +} + +class Home extends StatefulWidget { + Home({ + Key key, + @required this.tencent, + }) : super(key: key); + + final FakeTencent tencent; + + @override + State createState() { + return _HomeState(); + } +} + +class _HomeState extends State { + StreamSubscription _login; + StreamSubscription _userInfo; + StreamSubscription _share; + + @override + void initState() { + super.initState(); + _login = widget.tencent.loginResp().listen(_listenLogin); + _userInfo = widget.tencent.userInfoResp().listen(_listenUserInfo); + _share = widget.tencent.shareResp().listen(_listenShare); + } + + void _listenLogin(FakeTencentLoginResp resp) { + String content = 'login: ${resp.openId} - ${resp.accessToken}'; + _showTips('登录', content); + } + + void _listenUserInfo(FakeTencentUserInfoResp resp) { + String content = 'user info: ${resp.nickName} - ${resp.gender}'; + _showTips('用户', content); + } + + void _listenShare(FakeTencentShareResp resp) { + String content = 'share: ${resp.errorCode} - ${resp.errorMsg}'; + _showTips('分享', content); + } + + @override + void dispose() { + if (_login != null) { + _login.cancel(); + } + if (_userInfo != null) { + _userInfo.cancel(); + } + if (_share != null) { + _share.cancel(); + } + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Fake Tencent Demo'), + ), + body: ListView( + children: [ + ListTile( + title: const Text('环境检查'), + onTap: () async { + String content = + 'tencent: ${await widget.tencent.isQQInstalled()} - ${await widget.tencent.isQQSupportSSOLogin()}'; + _showTips('环境检查', content); + }, + ), + ListTile( + title: const Text('登录'), + onTap: () { + widget.tencent.login( + scope: [FakeTencentScope.GET_SIMPLE_USERINFO], + ); + }, + ), + ListTile( + title: const Text('获取用户信息'), + onTap: () { + widget.tencent.getUserInfo(); + }, + ), + ListTile( + title: const Text('分享文字'), + onTap: () { + widget.tencent.shareMood( + scene: FakeTencentScene.SCENE_QZONE, + summary: '分享测试', + ); + }, + ), + ListTile( + title: const Text('分享链接'), + onTap: () { + widget.tencent.shareWebpage( + scene: FakeTencentScene.SCENE_QQ, + title: 'title', + targetUrl: 'https://www.baidu.com/', + ); + }, + ), + ], + ), + ); + } + + void _showTips(String title, String content) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text(title), + content: Text(content), + ); + }, + ); + } +} diff --git a/example/pubspec.yaml b/example/pubspec.yaml new file mode 100644 index 0000000..c0fac6a --- /dev/null +++ b/example/pubspec.yaml @@ -0,0 +1,65 @@ +name: fake_tencent_example +description: Demonstrates how to use the fake_tencent plugin. +publish_to: 'none' + +environment: + sdk: ">=2.0.0-dev.68.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^0.1.2 + + fake_tencent: + path: ../ + +dev_dependencies: + flutter_test: + sdk: flutter + + pedantic: '>=1.4.0 <3.0.0' + +# For information on the generic Dart part of this file, see the +# following page: https://www.dartlang.org/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.io/assets-and-images/#resolution-aware. + + # For details regarding adding assets from package dependencies, see + # https://flutter.io/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.io/custom-fonts/#from-packages diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart new file mode 100644 index 0000000..baa5890 --- /dev/null +++ b/example/test/widget_test.dart @@ -0,0 +1,10 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility that Flutter provides. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +void main() { + +} diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 0000000..710ec6c --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,36 @@ +.idea/ +.vagrant/ +.sconsign.dblite +.svn/ + +.DS_Store +*.swp +profile + +DerivedData/ +build/ +GeneratedPluginRegistrant.h +GeneratedPluginRegistrant.m + +.generated/ + +*.pbxuser +*.mode1v3 +*.mode2v3 +*.perspectivev3 + +!default.pbxuser +!default.mode1v3 +!default.mode2v3 +!default.perspectivev3 + +xcuserdata + +*.moved-aside + +*.pyc +*sync/ +Icon? +.tags* + +/Flutter/Generated.xcconfig diff --git a/ios/Assets/.gitkeep b/ios/Assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/ios/Classes/FakeTencentPlugin.h b/ios/Classes/FakeTencentPlugin.h new file mode 100644 index 0000000..556f283 --- /dev/null +++ b/ios/Classes/FakeTencentPlugin.h @@ -0,0 +1,4 @@ +#import + +@interface FakeTencentPlugin : NSObject +@end diff --git a/ios/Classes/FakeTencentPlugin.m b/ios/Classes/FakeTencentPlugin.m new file mode 100644 index 0000000..ff35e12 --- /dev/null +++ b/ios/Classes/FakeTencentPlugin.m @@ -0,0 +1,349 @@ +#import "FakeTencentPlugin.h" +#import +#import + +@interface FakeTencentPlugin () + +@end + +enum FakeTencentScene { + SCENE_QQ = 0, + SCENE_QZONE = 1, +}; + +enum FakeTencentErrorCode { + ERRORCODE_SUCCESS = 0, + ERRORCODE_COMMON = -1, + ERRORCODE_USERCANCEL = -2, +}; + +@implementation FakeTencentPlugin { + FlutterMethodChannel * _channel; + TencentOAuth * _oauth; +} + ++ (void)registerWithRegistrar:(NSObject*)registrar { + FlutterMethodChannel* channel = [FlutterMethodChannel + methodChannelWithName:@"v7lin.github.io/fake_tencent" + binaryMessenger:[registrar messenger]]; + FakeTencentPlugin* instance = [[FakeTencentPlugin alloc] initWithChannel:channel]; + [registrar addApplicationDelegate:instance]; + [registrar addMethodCallDelegate:instance channel:channel]; +} + +static NSString * const METHOD_REGISTERAPP = @"registerApp"; +static NSString * const METHOD_ISQQINSTALLED = @"isQQInstalled"; +static NSString * const METHOD_ISQQSUPPORTSSOLOGIN = @"isQQSupportSSOLogin"; +static NSString * const METHOD_SETACCESSTOKEN = @"setAccessToken"; +static NSString * const METHOD_LOGIN = @"login"; +static NSString * const METHOD_LOGOUT = @"logout"; +static NSString * const METHOD_GETUSERINFO = @"getUserInfo"; +static NSString * const METHOD_SHAREMOOD = @"shareMood"; +static NSString * const METHOD_SHAREIMAGE = @"shareImage"; +static NSString * const METHOD_SHAREMUSIC = @"shareMusic"; +static NSString * const METHOD_SHAREWEBPAGE = @"shareWebpage"; + +static NSString * const METHOD_ONLOGINRESP = @"onLoginResp"; +static NSString * const METHOD_ONGETUSERINFORESP = @"onGetUserInfoResp"; +static NSString * const METHOD_ONSHARERESP = @"onShareResp"; + +static NSString * const ARGUMENT_KEY_APPID = @"appId"; +static NSString * const ARGUMENT_KEY_OPENID = @"openId"; +static NSString * const ARGUMENT_KEY_ACCESSTOKEN = @"accessToken"; +static NSString * const ARGUMENT_KEY_EXPIRATIONDATE = @"expirationDate"; +static NSString * const ARGUMENT_KEY_SCOPE = @"scope"; +static NSString * const ARGUMENT_KEY_SCENE = @"scene"; +static NSString * const ARGUMENT_KEY_TITLE = @"title"; +static NSString * const ARGUMENT_KEY_SUMMARY = @"summary"; +static NSString * const ARGUMENT_KEY_IMAGEURI = @"imageUri"; +static NSString * const ARGUMENT_KEY_IMAGEURIS = @"imageUris"; +static NSString * const ARGUMENT_KEY_VIDEOURI = @"videoUri"; +static NSString * const ARGUMENT_KEY_MUSICURL = @"musicUrl"; +static NSString * const ARGUMENT_KEY_TARGETURL = @"targetUrl"; +static NSString * const ARGUMENT_KEY_APPNAME = @"appName"; +static NSString * const ARGUMENT_KEY_EXTINT = @"extInt"; + +static NSString * const ARGUMENT_KEY_RESULT_ERRORCODE = @"errorCode"; +static NSString * const ARGUMENT_KEY_RESULT_ERRORMSG = @"errorMsg"; +static NSString * const ARGUMENT_KEY_RESULT_OPENID = @"openId"; +static NSString * const ARGUMENT_KEY_RESULT_ACCESSTOKEN = @"accessToken"; +static NSString * const ARGUMENT_KEY_RESULT_EXPIRATIONDATE = @"expirationDate"; + +static NSString * const SCHEME_FILE = @"file"; + +-(instancetype)initWithChannel:(FlutterMethodChannel *)channel { + self = [super init]; + if (self) { + _channel = channel; + } + return self; +} + +- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { + if ([METHOD_REGISTERAPP isEqualToString:call.method]) { + NSString * appId = call.arguments[ARGUMENT_KEY_APPID]; + _oauth = [[TencentOAuth alloc] initWithAppId:appId andDelegate:self]; + result(nil); + } else if ([METHOD_ISQQINSTALLED isEqualToString:call.method]) { + result([NSNumber numberWithBool:[TencentOAuth iphoneQQInstalled]]); + } else if ([METHOD_ISQQSUPPORTSSOLOGIN isEqualToString:call.method]) { + result([NSNumber numberWithBool:[TencentOAuth iphoneQQSupportSSOLogin]]); + } else if ([METHOD_SETACCESSTOKEN isEqualToString:call.method]) { + [self setAccessToken:call result:result]; + } else if ([METHOD_LOGIN isEqualToString:call.method]) { + [self login:call result:result]; + } else if ([METHOD_LOGOUT isEqualToString:call.method]) { + [self logout:call result:result]; + } else if ([METHOD_GETUSERINFO isEqualToString:call.method]) { + [self getUserInfo:call result:result]; + } else if ([METHOD_SHAREMOOD isEqualToString:call.method]) { + [self shareMood:call result:result]; + } else if ([METHOD_SHAREIMAGE isEqualToString:call.method]) { + [self shareImage:call result:result]; + } else if ([METHOD_SHAREMUSIC isEqualToString:call.method]) { + [self shareMusic:call result:result]; + } else if ([METHOD_SHAREWEBPAGE isEqualToString:call.method]) { + [self shareWebpage:call result:result]; + } else { + result(FlutterMethodNotImplemented); + } +} + +-(void)setAccessToken:(FlutterMethodCall*)call result:(FlutterResult)result { + NSString * openId = call.arguments[ARGUMENT_KEY_OPENID]; + NSString * accessToken = call.arguments[ARGUMENT_KEY_ACCESSTOKEN]; + NSNumber * expirationDate = call.arguments[ARGUMENT_KEY_EXPIRATIONDATE]; + NSTimeInterval seconds = expirationDate.longLongValue / 1000.0; + [_oauth setOpenId:openId]; + [_oauth setAccessToken:accessToken]; + [_oauth setExpirationDate:[NSDate dateWithTimeIntervalSince1970:seconds]]; + result(nil); +} + +-(void)login:(FlutterMethodCall*)call result:(FlutterResult)result { + NSString * scope = call.arguments[ARGUMENT_KEY_SCOPE]; + NSArray * permissions = [scope componentsSeparatedByString:@","]; + [_oauth authorize:permissions]; + result(nil); +} + +-(void)logout:(FlutterMethodCall*)call result:(FlutterResult)result { + [_oauth logout:self]; + result(nil); +} + +-(void)getUserInfo:(FlutterMethodCall*)call result:(FlutterResult)result { + [_oauth getUserInfo]; + result(nil); +} + +-(void)shareMood:(FlutterMethodCall*)call result:(FlutterResult)result { + NSNumber * scene = call.arguments[ARGUMENT_KEY_SCENE]; + if (scene.intValue == SCENE_QZONE) { + NSString * summary = call.arguments[ARGUMENT_KEY_SUMMARY]; + NSArray * imageUris = call.arguments[ARGUMENT_KEY_IMAGEURIS]; + NSString * videoUri = call.arguments[ARGUMENT_KEY_VIDEOURI]; + + if (videoUri == nil || videoUri.length == 0) { + NSMutableArray * imageDatas = [NSMutableArray array]; + if (imageUris != nil && imageUris.count > 0) { + for (NSString * imageUri in imageUris) { + UIImage *image = [UIImage imageWithContentsOfFile:[NSURL URLWithString:imageUri].path]; + NSData * imageData = UIImagePNGRepresentation(image); + if (imageData == nil) { + imageData = UIImageJPEGRepresentation(image, 1); + } + [imageDatas addObject:imageData]; + } + } + QQApiImageArrayForQZoneObject * object = [QQApiImageArrayForQZoneObject objectWithimageDataArray:imageDatas title:summary extMap:nil]; + SendMessageToQQReq * req = [SendMessageToQQReq reqWithContent: object]; + [QQApiInterface sendReq:req]; + } else { + QQApiVideoForQZoneObject * object = [QQApiVideoForQZoneObject objectWithAssetURL:videoUri title:summary extMap:nil]; + SendMessageToQQReq * req = [SendMessageToQQReq reqWithContent: object]; + [QQApiInterface sendReq:req]; + } + } + result(nil); +} + +-(void)shareImage:(FlutterMethodCall*)call result:(FlutterResult)result { + NSNumber * scene = call.arguments[ARGUMENT_KEY_SCENE]; + if (scene.intValue == SCENE_QQ) { + NSString * imageUri = call.arguments[ARGUMENT_KEY_IMAGEURI]; +// NSString * appName = call.arguments[ARGUMENT_KEY_APPNAME]; +// NSNumber * extInt = call.arguments[ARGUMENT_KEY_EXTINT]; + + UIImage *image = [UIImage imageWithContentsOfFile:[NSURL URLWithString:imageUri].path]; + NSData * imageData = UIImagePNGRepresentation(image); + NSData * thumbData = nil; + if (imageData == nil) { + imageData = UIImageJPEGRepresentation(image, 1); + thumbData = UIImageJPEGRepresentation(image, 0.2); + } else { + thumbData = imageData; + } + QQApiImageObject * object = [QQApiImageObject objectWithData:imageData previewImageData:thumbData title:nil description:nil]; + SendMessageToQQReq * req = [SendMessageToQQReq reqWithContent: object]; + [QQApiInterface sendReq:req]; + } + result(nil); +} + +-(void)shareMusic:(FlutterMethodCall*)call result:(FlutterResult)result { + NSNumber * scene = call.arguments[ARGUMENT_KEY_SCENE]; + NSString * title = call.arguments[ARGUMENT_KEY_TITLE]; + NSString * summary = call.arguments[ARGUMENT_KEY_SUMMARY]; + NSString * imageUri = call.arguments[ARGUMENT_KEY_IMAGEURI]; + NSString * musicUrl = call.arguments[ARGUMENT_KEY_MUSICURL]; + NSString * targetUrl = call.arguments[ARGUMENT_KEY_TARGETURL]; +// NSString * appName = call.arguments[ARGUMENT_KEY_APPNAME]; +// NSNumber * extInt = call.arguments[ARGUMENT_KEY_EXTINT]; + if (scene.intValue == SCENE_QQ) { + QQApiAudioObject * object = nil; + NSURL * uri = [NSURL URLWithString:imageUri]; + if ([SCHEME_FILE isEqualToString:uri.scheme]) { + UIImage *image = [UIImage imageWithContentsOfFile:uri.path]; + NSData * imageData = UIImagePNGRepresentation(image); + if (imageData == nil) { + imageData = UIImageJPEGRepresentation(image, 1); + } + object = [QQApiAudioObject objectWithURL:[NSURL URLWithString:targetUrl] title:title description:summary previewImageData:imageData]; + } else { + object = [QQApiAudioObject objectWithURL:[NSURL URLWithString:targetUrl] title:title description:summary previewImageURL:[NSURL URLWithString:musicUrl]]; + } + SendMessageToQQReq * req = [SendMessageToQQReq reqWithContent: object]; + [QQApiInterface sendReq:req]; + } + result(nil); +} + +-(void)shareWebpage:(FlutterMethodCall*)call result:(FlutterResult)result { + NSNumber * scene = call.arguments[ARGUMENT_KEY_SCENE]; + NSString * title = call.arguments[ARGUMENT_KEY_TITLE]; + NSString * summary = call.arguments[ARGUMENT_KEY_SUMMARY]; + NSString * imageUri = call.arguments[ARGUMENT_KEY_IMAGEURI]; + NSString * targetUrl = call.arguments[ARGUMENT_KEY_TARGETURL]; +// NSString * appName = call.arguments[ARGUMENT_KEY_APPNAME]; +// NSNumber * extInt = call.arguments[ARGUMENT_KEY_EXTINT]; + + QQApiNewsObject * object = nil; + NSURL * uri = [NSURL URLWithString:imageUri]; + if ([SCHEME_FILE isEqualToString:uri.scheme]) { + UIImage *image = [UIImage imageWithContentsOfFile:uri.path]; + NSData * imageData = UIImagePNGRepresentation(image); + if (imageData == nil) { + imageData = UIImageJPEGRepresentation(image, 1); + } + object = [QQApiNewsObject objectWithURL:[NSURL URLWithString:targetUrl] title:title description:summary previewImageData:imageData]; + } else { + object = [QQApiNewsObject objectWithURL:[NSURL URLWithString:targetUrl] title:title description:summary previewImageURL:[NSURL URLWithString:imageUri]]; + } + SendMessageToQQReq * req = [SendMessageToQQReq reqWithContent: object]; + if (scene.intValue == SCENE_QQ) { + [QQApiInterface sendReq:req]; + } else if (scene.intValue == SCENE_QZONE) { + [QQApiInterface SendReqToQZone:req]; + } + result(nil); +} + +# pragma mark - AppDelegate + +-(BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url { + return [QQApiInterface handleOpenURL:url delegate:self] || ([TencentOAuth CanHandleOpenURL:url] && [TencentOAuth HandleOpenURL:url]); +} + +-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { + return [QQApiInterface handleOpenURL:url delegate:self] || ([TencentOAuth CanHandleOpenURL:url] && [TencentOAuth HandleOpenURL:url]); +} + +-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options { + return [QQApiInterface handleOpenURL:url delegate:self] || ([TencentOAuth CanHandleOpenURL:url] && [TencentOAuth HandleOpenURL:url]); +} + +# pragma mark - TencentSessionDelegate + +-(void)tencentDidLogin { + NSMutableDictionary * dictionary = [NSMutableDictionary dictionary]; + if (_oauth.accessToken != nil && _oauth.accessToken.length > 0) { + NSString * openId = _oauth.openId; + NSString * accessToken = _oauth.accessToken; + long long expirationDate = _oauth.expirationDate.timeIntervalSince1970 * 1000; + [dictionary setValue:[NSNumber numberWithInt:ERRORCODE_SUCCESS] forKey:ARGUMENT_KEY_RESULT_ERRORCODE]; + [dictionary setValue:openId forKey:ARGUMENT_KEY_RESULT_OPENID]; + [dictionary setValue:accessToken forKey:ARGUMENT_KEY_RESULT_ACCESSTOKEN]; + [dictionary setValue:[NSNumber numberWithLongLong:expirationDate] forKey:ARGUMENT_KEY_RESULT_EXPIRATIONDATE]; + } else { + // 登录失败 + [dictionary setValue:[NSNumber numberWithInt:ERRORCODE_COMMON] forKey:ARGUMENT_KEY_RESULT_ERRORCODE]; + } + [_channel invokeMethod:METHOD_ONLOGINRESP arguments:dictionary]; +} + +-(void)tencentDidNotLogin:(BOOL)cancelled { + NSMutableDictionary * dictionary = [NSMutableDictionary dictionary]; + if (cancelled) { + // 取消登录 + [dictionary setValue:[NSNumber numberWithInt:ERRORCODE_USERCANCEL] forKey:ARGUMENT_KEY_RESULT_ERRORCODE]; + } else { + // 登录失败 + [dictionary setValue:[NSNumber numberWithInt:ERRORCODE_COMMON] forKey:ARGUMENT_KEY_RESULT_ERRORCODE]; + } + [_channel invokeMethod:METHOD_ONLOGINRESP arguments:dictionary]; +} + +-(void)tencentDidNotNetWork { + // 登录失败 + NSMutableDictionary * dictionary = [NSMutableDictionary dictionary]; + [dictionary setValue:[NSNumber numberWithInt:ERRORCODE_COMMON] forKey:ARGUMENT_KEY_RESULT_ERRORCODE]; + [_channel invokeMethod:METHOD_ONLOGINRESP arguments:dictionary]; +} + +-(void)getUserInfoResponse:(APIResponse *)response { + NSMutableDictionary * dictionary = [NSMutableDictionary dictionary]; + if (response.retCode == URLREQUEST_SUCCEED) { + [dictionary setValue:[NSNumber numberWithInt:ERRORCODE_SUCCESS] forKey:ARGUMENT_KEY_RESULT_ERRORCODE]; + NSDictionary * json = response.jsonResponse; + [dictionary addEntriesFromDictionary:json]; + } else { + [dictionary setValue:[NSNumber numberWithInt:ERRORCODE_COMMON] forKey:ARGUMENT_KEY_RESULT_ERRORCODE]; + [dictionary setValue:response.errorMsg forKey:ARGUMENT_KEY_RESULT_ERRORMSG]; + } + [_channel invokeMethod:METHOD_ONGETUSERINFORESP arguments:dictionary]; +} + +# pragma mark - QQApiInterfaceDelegate + +-(void)onReq:(QQBaseReq *)req { + +} + +- (void)onResp:(QQBaseResp *)resp { + NSMutableDictionary * dictionary = [NSMutableDictionary dictionary]; + if ([resp isKindOfClass:[SendMessageToQQResp class]]) { + switch (resp.result.intValue) { + case 0: + // 分享成功 + [dictionary setValue:[NSNumber numberWithInt:ERRORCODE_SUCCESS] forKey:ARGUMENT_KEY_RESULT_ERRORCODE]; + break; + case -4: + // 用户取消 + [dictionary setValue:[NSNumber numberWithInt:ERRORCODE_USERCANCEL] forKey:ARGUMENT_KEY_RESULT_ERRORCODE]; + break; + default: + [dictionary setValue:[NSNumber numberWithInt:ERRORCODE_COMMON] forKey:ARGUMENT_KEY_RESULT_ERRORCODE]; + NSString * errorMsg = [NSString stringWithFormat:@"result: %@, description: %@.", resp.result, resp.errorDescription]; + [dictionary setValue:errorMsg forKey:ARGUMENT_KEY_RESULT_ERRORMSG]; + break; + } + [_channel invokeMethod:METHOD_ONSHARERESP arguments:dictionary]; + } +} + +-(void)isOnlineResponse:(NSDictionary *)response { + +} + +@end diff --git a/ios/fake_tencent.podspec b/ios/fake_tencent.podspec new file mode 100644 index 0000000..6e3732e --- /dev/null +++ b/ios/fake_tencent.podspec @@ -0,0 +1,22 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html +# +Pod::Spec.new do |s| + s.name = 'fake_tencent' + s.version = '0.0.1' + s.summary = 'A new Flutter plugin.' + s.description = <<-DESC +A new Flutter plugin. + DESC + s.homepage = 'http://example.com' + s.license = { :file => '../LICENSE' } + s.author = { 'Your Company' => 'email@example.com' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.public_header_files = 'Classes/**/*.h' + s.dependency 'Flutter' + s.dependency 'FakeTencent', '~> 0.0.1' + + s.ios.deployment_target = '8.0' +end + diff --git a/lib/fake_tencent.dart b/lib/fake_tencent.dart new file mode 100644 index 0000000..ce393bd --- /dev/null +++ b/lib/fake_tencent.dart @@ -0,0 +1,514 @@ +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:meta/meta.dart'; + +class FakeTencentScope { + FakeTencentScope._(); + + /// 发表一条说说到QQ空间(需要申请权限) + static const String OPEN_PERMISSION_ADD_TOPIC = 'add_topic'; + + /// 发表一篇日志到QQ空间(需要申请权限) + static const String OPEN_PERMISSION_ADD_ONE_BLOG = 'add_one_blog'; + + /// 创建一个QQ空间相册(需要申请权限) + static const String OPEN_PERMISSION_ADD_ALBUM = 'add_album'; + + /// 上传一张照片到QQ空间相册(需要申请权限) + static const String OPEN_PERMISSION_UPLOAD_PIC = 'upload_pic'; + + /// 获取用户QQ空间相册列表(需要申请权限) + static const String OPEN_PERMISSION_LIST_ALBUM = 'list_album'; + + /// 同步分享到QQ空间、腾讯微博 + static const String OPEN_PERMISSION_ADD_SHARE = 'add_share'; + + /// 验证是否认证空间粉丝 + static const String OPEN_PERMISSION_CHECK_PAGE_FANS = 'check_page_fans'; + + /// 获取登录用户自己的详细信息 + static const String OPEN_PERMISSION_GET_INFO = 'get_info'; + + /// 获取其他用户的详细信息 + static const String OPEN_PERMISSION_GET_OTHER_INFO = 'get_other_info'; + + /// 获取会员用户基本信息 + static const String OPEN_PERMISSION_GET_VIP_INFO = 'get_vip_info'; + + /// 获取会员用户详细信息 + static const String OPEN_PERMISSION_GET_VIP_RICH_INFO = 'get_vip_rich_info'; + + /// 获取用户信息 + static const String GET_USER_INFO = 'get_user_info'; + + /// 移动端获取用户信息 + static const String GET_SIMPLE_USERINFO = 'get_simple_userinfo'; + + /// 所有权限 + static const String ALL = 'all'; +} + +class FakeTencentScene { + FakeTencentScene._(); + + /// QQ + static const int SCENE_QQ = 0; + + /// QZone + static const int SCENE_QZONE = 1; +} + +class FakeTencentQZoneFlag { + FakeTencentQZoneFlag._(); + + /// 默认是不隐藏分享到QZone按钮且不自动打开分享到QZone的对话框 + static const int DEFAULT = 0; + + /// 分享时自动打开分享到QZone的对话框 + static const int AUTO_OPEN = 1; + + /// 分享时隐藏分享到QZone按钮 + static const int ITEM_HIDE = 2; +} + +class FakeTencentErrorCode { + FakeTencentErrorCode._(); + + static const int ERRORCODE_SUCCESS = 0; + static const int ERRORCODE_COMMON = -1; + static const int ERRORCODE_USERCANCEL = -2; +} + +abstract class FakeTencentBaseResp { + FakeTencentBaseResp({ + @required this.errorCode, + @required this.errorMsg, + }); + + final int errorCode; + final String errorMsg; +} + +class FakeTencentLoginResp extends FakeTencentBaseResp { + FakeTencentLoginResp._({ + @required int errorCode, + @required String errorMsg, + this.openId, + this.accessToken, + this.expirationDate, + }) : super(errorCode: errorCode, errorMsg: errorMsg); + + final String openId; + final String accessToken; + final int expirationDate; +} + +class FakeTencentUserInfoResp extends FakeTencentBaseResp { + FakeTencentUserInfoResp._({ + @required int errorCode, + @required String errorMsg, + this.nickName, + this.gender, + this.figureurlQQ1, + this.figureurlQQ2, + this.figureurl1, + this.figureurl2, + this.figureurl, + this.vip, + this.level, + this.isYellowVip, + this.isYellowYearVip, + this.yellowVipLevel, + }) : super(errorCode: errorCode, errorMsg: errorMsg); + + final String nickName; + + /// 男/女 + final String gender; + final String figureurlQQ1; + final String figureurlQQ2; + final String figureurl1; + final String figureurl2; + final String figureurl; + final String vip; + final String level; + final String isYellowVip; + final String isYellowYearVip; + final String yellowVipLevel; + + bool isMale() { + return gender == '男'; + } + + bool isFemale() { + return gender == '女'; + } + + String headImgUrl() { + if (figureurlQQ2 != null && figureurlQQ2.isNotEmpty) { + return figureurlQQ2; + } + if (figureurlQQ1 != null && figureurlQQ1.isNotEmpty) { + return figureurlQQ1; + } + if (figureurl2 != null && figureurl2.isNotEmpty) { + return figureurl2; + } + if (figureurl1 != null && figureurl1.isNotEmpty) { + return figureurl1; + } + return figureurl; + } +} + +class FakeTencentShareResp extends FakeTencentBaseResp { + FakeTencentShareResp._({ + @required int errorCode, + @required String errorMsg, + }) : super(errorCode: errorCode, errorMsg: errorMsg); +} + +class FakeTencent { + static const String _METHOD_REGISTERAPP = 'registerApp'; + static const String _METHOD_ISQQINSTALLED = 'isQQInstalled'; + static const String _METHOD_ISQQSUPPORTSSOLOGIN = 'isQQSupportSSOLogin'; + static const String _METHOD_SETACCESSTOKEN = 'setAccessToken'; + static const String _METHOD_LOGIN = 'login'; + static const String _METHOD_LOGOUT = 'logout'; + static const String _METHOD_GETUSERINFO = 'getUserInfo'; + static const String _METHOD_SHAREMOOD = 'shareMood'; + static const String _METHOD_SHAREIMAGE = 'shareImage'; + static const String _METHOD_SHAREMUSIC = 'shareMusic'; + static const String _METHOD_SHAREWEBPAGE = 'shareWebpage'; + + static const String _METHOD_ONLOGINRESP = 'onLoginResp'; + static const String _METHOD_ONGETUSERINFORESP = 'onGetUserInfoResp'; + static const String _METHOD_ONSHARERESP = "onShareResp"; + + static const String _ARGUMENT_KEY_APPID = 'appId'; + static const String _ARGUMENT_KEY_OPENID = 'openId'; + static const String _ARGUMENT_KEY_ACCESSTOKEN = 'accessToken'; + static const String _ARGUMENT_KEY_EXPIRATIONDATE = 'expirationDate'; + static const String _ARGUMENT_KEY_SCOPE = 'scope'; + static const String _ARGUMENT_KEY_SCENE = 'scene'; + static const String _ARGUMENT_KEY_TITLE = 'title'; + static const String _ARGUMENT_KEY_SUMMARY = 'summary'; + static const String _ARGUMENT_KEY_IMAGEURI = 'imageUri'; + static const String _ARGUMENT_KEY_IMAGEURIS = 'imageUris'; + static const String _ARGUMENT_KEY_VIDEOURI = 'videoUri'; + static const String _ARGUMENT_KEY_MUSICURL = 'musicUrl'; + static const String _ARGUMENT_KEY_TARGETURL = 'targetUrl'; + static const String _ARGUMENT_KEY_APPNAME = 'appName'; + static const String _ARGUMENT_KEY_EXTINT = 'extInt'; + + static const String _ARGUMENT_KEY_RESULT_ERRORCODE = 'errorCode'; + static const String _ARGUMENT_KEY_RESULT_ERRORMSG = 'errorMsg'; + static const String _ARGUMENT_KEY_RESULT_OPENID = 'openId'; + static const String _ARGUMENT_KEY_RESULT_ACCESSTOKEN = 'accessToken'; + static const String _ARGUMENT_KEY_RESULT_EXPIRATIONDATE = 'expirationDate'; + + static const String _ARGUMENT_KEY_RESULT_NICKNAME = 'nickname'; + static const String _ARGUMENT_KEY_RESULT_GENDER = 'gender'; + static const String _ARGUMENT_KEY_RESULT_FIGUREURL_QQ_1 = 'figureurl_qq_1'; + static const String _ARGUMENT_KEY_RESULT_FIGUREURL_QQ_2 = 'figureurl_qq_2'; + static const String _ARGUMENT_KEY_RESULT_FIGUREURL_1 = 'figureurl_1'; + static const String _ARGUMENT_KEY_RESULT_FIGUREURL_2 = 'figureurl_2'; + static const String _ARGUMENT_KEY_RESULT_FIGUREURL = 'figureurl'; + static const String _ARGUMENT_KEY_RESULT_VIP = 'vip'; + static const String _ARGUMENT_KEY_RESULT_LEVEL = 'level'; + static const String _ARGUMENT_KEY_RESULT_IS_YELLOW_VIP = 'is_yellow_vip'; + static const String _ARGUMENT_KEY_RESULT_IS_YELLOW_YEAR_VIP = + 'is_yellow_year_vip'; + static const String _ARGUMENT_KEY_RESULT_YELLOW_VIP_LEVEL = + 'yellow_vip_level'; + + static const String _SCHEME_FILE = 'file'; + + static const MethodChannel _channel = + MethodChannel('v7lin.github.io/fake_tencent'); + + final StreamController _loginRespStreamController = + StreamController.broadcast(); + + final StreamController + _userInfoRespStreamController = + StreamController.broadcast(); + + final StreamController _shareRespStreamController = + StreamController.broadcast(); + + Future registerApp({ + @required String appId, + }) { + assert(appId != null && appId.isNotEmpty); + _channel.setMethodCallHandler(_handleMethod); + return _channel.invokeMethod( + _METHOD_REGISTERAPP, + { + _ARGUMENT_KEY_APPID: appId, + }, + ); + } + + Future _handleMethod(MethodCall call) async { + switch (call.method) { + case _METHOD_ONLOGINRESP: + _loginRespStreamController.add(FakeTencentLoginResp._( + errorCode: call.arguments[_ARGUMENT_KEY_RESULT_ERRORCODE] as int, + errorMsg: call.arguments[_ARGUMENT_KEY_RESULT_ERRORMSG] as String, + openId: call.arguments[_ARGUMENT_KEY_RESULT_OPENID] as String, + accessToken: + call.arguments[_ARGUMENT_KEY_RESULT_ACCESSTOKEN] as String, + expirationDate: + call.arguments[_ARGUMENT_KEY_RESULT_EXPIRATIONDATE] as int, + )); + break; + case _METHOD_ONGETUSERINFORESP: + _userInfoRespStreamController.add(FakeTencentUserInfoResp._( + errorCode: call.arguments[_ARGUMENT_KEY_RESULT_ERRORCODE] as int, + errorMsg: call.arguments[_ARGUMENT_KEY_RESULT_ERRORMSG] as String, + nickName: call.arguments[_ARGUMENT_KEY_RESULT_NICKNAME] as String, + gender: call.arguments[_ARGUMENT_KEY_RESULT_GENDER] as String, + figureurlQQ1: + call.arguments[_ARGUMENT_KEY_RESULT_FIGUREURL_QQ_1] as String, + figureurlQQ2: + call.arguments[_ARGUMENT_KEY_RESULT_FIGUREURL_QQ_2] as String, + figureurl1: + call.arguments[_ARGUMENT_KEY_RESULT_FIGUREURL_1] as String, + figureurl2: + call.arguments[_ARGUMENT_KEY_RESULT_FIGUREURL_2] as String, + figureurl: call.arguments[_ARGUMENT_KEY_RESULT_FIGUREURL] as String, + vip: call.arguments[_ARGUMENT_KEY_RESULT_VIP] as String, + level: call.arguments[_ARGUMENT_KEY_RESULT_LEVEL] as String, + isYellowVip: + call.arguments[_ARGUMENT_KEY_RESULT_IS_YELLOW_VIP] as String, + isYellowYearVip: + call.arguments[_ARGUMENT_KEY_RESULT_IS_YELLOW_YEAR_VIP] as String, + yellowVipLevel: + call.arguments[_ARGUMENT_KEY_RESULT_YELLOW_VIP_LEVEL] as String, + )); + break; + case _METHOD_ONSHARERESP: + _shareRespStreamController.add(FakeTencentShareResp._( + errorCode: call.arguments[_ARGUMENT_KEY_RESULT_ERRORCODE] as int, + errorMsg: call.arguments[_ARGUMENT_KEY_RESULT_ERRORMSG] as String, + )); + break; + } + } + + Stream loginResp() { + return _loginRespStreamController.stream; + } + + Stream userInfoResp() { + return _userInfoRespStreamController.stream; + } + + Stream shareResp() { + return _shareRespStreamController.stream; + } + + Future isQQInstalled() async { + return (await _channel.invokeMethod(_METHOD_ISQQINSTALLED)) as bool; + } + + Future isQQSupportSSOLogin() async { + return (await _channel.invokeMethod(_METHOD_ISQQSUPPORTSSOLOGIN)) as bool; + } + + Future setAccessToken({ + @required String openId, + @required String accessToken, + @required int expirationDate, + }) { + assert(openId != null && openId.isNotEmpty); + assert(accessToken != null && accessToken.isNotEmpty); + return _channel.invokeMethod( + _METHOD_SETACCESSTOKEN, + { + _ARGUMENT_KEY_OPENID: openId, + _ARGUMENT_KEY_ACCESSTOKEN: accessToken, + _ARGUMENT_KEY_EXPIRATIONDATE: expirationDate, + }, + ); + } + + Future login({ + @required List scope, + }) { + assert(scope != null && scope.isNotEmpty); + return _channel.invokeMethod( + _METHOD_LOGIN, + { + _ARGUMENT_KEY_SCOPE: scope.join(','), + }, + ); + } + + Future logout() { + return _channel.invokeMethod(_METHOD_LOGOUT); + } + + Future getUserInfo() { + return _channel.invokeMethod(_METHOD_GETUSERINFO); + } + + Future shareMood({ + @required int scene, + String summary, + List imageUris, + Uri videoUri, + }) { + assert(scene == FakeTencentScene.SCENE_QZONE); + assert((summary != null && summary.isNotEmpty) || + (imageUris != null && imageUris.isNotEmpty) || + (videoUri != null && videoUri.isScheme(_SCHEME_FILE))); + if (imageUris != null && imageUris.isNotEmpty) { + imageUris.forEach((Uri imageUri) { + assert(imageUri != null && imageUri.isScheme(_SCHEME_FILE)); + }); + } + Map map = { + _ARGUMENT_KEY_SCENE: scene, +// _ARGUMENT_KEY_SUMMARY: summary, +// _ARGUMENT_KEY_IMAGEURIS: imageUris != null ? new List.generate(imageUris.length, (int index) { +// return imageUris[index].toString(); +// }) : null, +// _ARGUMENT_KEY_VIDEOURI: videoUri != null ? videoUri.toString() : null, + }; + + /// 兼容 iOS 空安全 -> NSNull + if (summary != null && summary.isNotEmpty) { + map.putIfAbsent(_ARGUMENT_KEY_SUMMARY, () => summary); + } + if (imageUris != null && imageUris.isNotEmpty) { + map.putIfAbsent( + _ARGUMENT_KEY_IMAGEURIS, + () => List.generate(imageUris.length, (int index) { + return imageUris[index].toString(); + })); + } + if (videoUri != null) { + map.putIfAbsent(_ARGUMENT_KEY_VIDEOURI, () => videoUri.toString()); + } + return _channel.invokeMethod(_METHOD_SHAREMOOD, map); + } + + Future shareImage({ + @required int scene, + @required Uri imageUri, + String appName, + int extInt = FakeTencentQZoneFlag.DEFAULT, + }) { + assert(scene == FakeTencentScene.SCENE_QQ); + assert(imageUri != null && imageUri.isScheme(_SCHEME_FILE)); + Map map = { + _ARGUMENT_KEY_SCENE: scene, + _ARGUMENT_KEY_IMAGEURI: imageUri.toString(), +// _ARGUMENT_KEY_APPNAME: appName, + _ARGUMENT_KEY_EXTINT: extInt, + }; + + /// 兼容 iOS 空安全 -> NSNull + if (appName != null && appName.isNotEmpty) { + map.putIfAbsent(_ARGUMENT_KEY_APPNAME, () => appName); + } + return _channel.invokeMethod(_METHOD_SHAREIMAGE, map); + } + + Future shareMusic({ + @required int scene, + @required String title, + String summary, + Uri imageUri, + @required String musicUrl, + @required String targetUrl, + String appName, + int extInt = FakeTencentQZoneFlag.DEFAULT, + }) { + assert(scene == FakeTencentScene.SCENE_QQ); + assert(title != null && title.isNotEmpty); + assert(musicUrl != null && musicUrl.isNotEmpty); + assert(targetUrl != null && targetUrl.isNotEmpty); + Map map = { + _ARGUMENT_KEY_SCENE: scene, + _ARGUMENT_KEY_TITLE: title, +// _ARGUMENT_KEY_SUMMARY: summary, +// _ARGUMENT_KEY_IMAGEURI: imageUri != null ? imageUri.toString() : null, + _ARGUMENT_KEY_MUSICURL: musicUrl, + _ARGUMENT_KEY_TARGETURL: targetUrl, +// _ARGUMENT_KEY_APPNAME: appName, + _ARGUMENT_KEY_EXTINT: extInt, + }; + + /// 兼容 iOS 空安全 -> NSNull + if (summary != null && summary.isNotEmpty) { + map.putIfAbsent(_ARGUMENT_KEY_SUMMARY, () => summary); + } + if (imageUri != null) { + map.putIfAbsent(_ARGUMENT_KEY_IMAGEURI, () => imageUri.toString()); + } + if (appName != null && appName.isNotEmpty) { + map.putIfAbsent(_ARGUMENT_KEY_APPNAME, () => appName); + } + return _channel.invokeMethod(_METHOD_SHAREMUSIC, map); + } + + Future shareWebpage({ + @required int scene, + @required String title, + String summary, + Uri imageUri, + @required String targetUrl, + String appName, + int extInt = FakeTencentQZoneFlag.DEFAULT, + }) { + assert(title != null && title.isNotEmpty); + assert(targetUrl != null && targetUrl.isNotEmpty); + Map map = { + _ARGUMENT_KEY_SCENE: scene, + _ARGUMENT_KEY_TITLE: title, +// _ARGUMENT_KEY_SUMMARY: summary, +// _ARGUMENT_KEY_IMAGEURI: imageUri != null ? imageUri.toString() : null, + _ARGUMENT_KEY_TARGETURL: targetUrl, +// _ARGUMENT_KEY_APPNAME: appName, + _ARGUMENT_KEY_EXTINT: extInt, + }; + + /// 兼容 iOS 空安全 -> NSNull + if (appName != null && appName.isNotEmpty) { + map.putIfAbsent(_ARGUMENT_KEY_APPNAME, () => appName); + } + if (imageUri != null) { + map.putIfAbsent(_ARGUMENT_KEY_IMAGEURI, () => imageUri.toString()); + } + if (appName != null && appName.isNotEmpty) { + map.putIfAbsent(_ARGUMENT_KEY_APPNAME, () => appName); + } + return _channel.invokeMethod(_METHOD_SHAREWEBPAGE, map); + } +} + +class FakeTencentProvider extends InheritedWidget { + FakeTencentProvider({ + Key key, + @required this.tencent, + @required Widget child, + }) : super(key: key, child: child); + + final FakeTencent tencent; + + @override + bool updateShouldNotify(InheritedWidget oldWidget) { + FakeTencentProvider oldProvider = oldWidget as FakeTencentProvider; + return tencent != oldProvider.tencent; + } + + static FakeTencentProvider of(BuildContext context) { + return context.inheritFromWidgetOfExactType(FakeTencentProvider) + as FakeTencentProvider; + } +} diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..89fe3e4 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,64 @@ +name: fake_tencent +description: A powerful tencent plugin for Flutter. +version: 0.0.1 +author: v7lin +homepage: https://github.com/v7lin/fake_tencent + +environment: + sdk: ">=2.0.0-dev.68.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + + meta: ^1.1.6 + +dev_dependencies: + flutter_test: + sdk: flutter + + pedantic: '>=1.4.0 <3.0.0' + +# For information on the generic Dart part of this file, see the +# following page: https://www.dartlang.org/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + # This section identifies this Flutter project as a plugin project. + # The androidPackage and pluginClass identifiers should not ordinarily + # be modified. They are used by the tooling to maintain consistency when + # adding or updating assets for this project. + plugin: + androidPackage: io.github.v7lin.faketencent + pluginClass: FakeTencentPlugin + + # To add assets to your plugin package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.io/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.io/assets-and-images/#resolution-aware. + + # To add custom fonts to your plugin package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.io/custom-fonts/#from-packages