tencent_kit

This commit is contained in:
v7lin
2019-11-14 22:46:11 +08:00
parent 1a678b85e0
commit 42c6c49154
78 changed files with 3221 additions and 1134 deletions

View File

@ -3,7 +3,7 @@ name: default
steps:
- name: prepare
image: v7lin/flutter:1.7.8-hotfix.3-stable
image: v7lin/flutter:1.9.1-hotfix.6-stable
volumes:
- name: pub-cache
path: /opt/flutter/.pub-cache
@ -11,15 +11,26 @@ steps:
- flutter packages get
#- name: build_runner
# image: v7lin/flutter:1.7.8-hotfix.3-stable
# image: v7lin/flutter:1.9.1-hotfix.6-stable
# volumes:
# - name: pub-cache
# path: /opt/flutter/.pub-cache
# commands:
# - flutter packages pub run build_runner build
# - flutter packages pub run build_runner build --delete-conflicting-outputs
- name: android-check
image: v7lin/flutter:1.9.1-hotfix.6-stable
volumes:
- name: pub-cache
path: /opt/flutter/.pub-cache
- name: gradle
path: /root/.gradle
commands:
- cd example/android/
- ./gradlew :tencent_kit:check
- name: format
image: v7lin/flutter:1.7.8-hotfix.3-stable
image: v7lin/flutter:1.9.1-hotfix.6-stable
volumes:
- name: pub-cache
path: /opt/flutter/.pub-cache
@ -27,7 +38,7 @@ steps:
- flutter format --dry-run --set-exit-if-changed .
- name: analyze
image: v7lin/flutter:1.7.8-hotfix.3-stable
image: v7lin/flutter:1.9.1-hotfix.6-stable
volumes:
- name: pub-cache
path: /opt/flutter/.pub-cache
@ -35,17 +46,17 @@ steps:
- flutter analyze
- name: test
image: v7lin/flutter:1.7.8-hotfix.3-stable
image: v7lin/flutter:1.9.1-hotfix.6-stable
volumes:
- name: pub-cache
path: /opt/flutter/.pub-cache
commands:
- flutter test --coverage
- flutter test
- cd example/
- flutter test
- name: proguard
image: v7lin/flutter:1.7.8-hotfix.3-stable
image: v7lin/flutter:1.9.1-hotfix.6-stable
volumes:
- name: pub-cache
path: /opt/flutter/.pub-cache
@ -68,7 +79,7 @@ steps:
- pull_request
- name: publish-check
image: v7lin/flutter:1.7.8-hotfix.3-stable
image: v7lin/flutter:1.9.1-hotfix.6-stable
volumes:
- name: pub-cache
path: /opt/flutter/.pub-cache
@ -111,7 +122,7 @@ steps:
- rm $FLUTTER_HOME/credentials.json.enc
- name: publish
image: v7lin/flutter:1.7.8-hotfix.3-stable
image: v7lin/flutter:1.9.1-hotfix.6-stable
volumes:
- name: pub-cache
path: /opt/flutter/.pub-cache

5
.gitignore vendored
View File

@ -3,9 +3,10 @@
.packages
.pub/
pubspec.lock
build/
.idea/
*.iml
.idea/
pubspec.lock

View File

@ -4,7 +4,7 @@
# This file should be version controlled and should not be manually edited.
version:
revision: 5391447fae6209bb21a89e6a5a6583cac1af9b4b
revision: 68587a0916366e9512a78df22c44163d041dd5f3
channel: stable
project_type: plugin

View File

@ -1,3 +1,11 @@
## 1.0.0 - 2019.11.14
* tencent_kit
* Android/iOS SDK
* Android
* API接口
* UnionId功能
## 0.3.4 - 2019.9.10
* gradle兼容

View File

@ -1,14 +1,14 @@
# fake_tencent
# tencent_kit
[![Build Status](https://cloud.drone.io/api/badges/v7lin/fake_tencent/status.svg)](https://cloud.drone.io/v7lin/fake_tencent)
[![Codecov](https://codecov.io/gh/v7lin/fake_tencent/branch/master/graph/badge.svg)](https://codecov.io/gh/v7lin/fake_tencent)
[![GitHub Tag](https://img.shields.io/github/tag/v7lin/fake_tencent.svg)](https://github.com/v7lin/fake_tencent/releases)
[![Pub Package](https://img.shields.io/pub/v/fake_tencent.svg)](https://pub.dartlang.org/packages/fake_tencent)
[![Pub Package](https://img.shields.io/pub/v/tencent_kit.svg)](https://pub.dartlang.org/packages/tencent_kit)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/v7lin/fake_tencent/blob/master/LICENSE)
flutter版腾讯(QQ)SDK
## fake libraries
## flutter toolkit
* [flutter版微信SDK](https://github.com/v7lin/fake_wechat)
* [flutter版腾讯(QQ)SDK](https://github.com/v7lin/fake_tencent)
@ -25,10 +25,11 @@ flutter版腾讯(QQ)SDK
* [](https://open.tencent.com/)
* [QQ互联](http://wiki.connect.qq.com/)
* [Universal Links](https://developer.apple.com/library/archive/documentation/General/Conceptual/AppSearch/UniversalLinks.html)
## android
````
```
...
android {
...
@ -40,22 +41,22 @@ android {
...
}
...
````
```
````
```
# Library Library apk
````
```
## ios
````
```
Xcode中TARGETSinfoURL typeURL schemeid
URL Types
tencent: identifier=tencent schemes=tencent${appId}
````
```
````
```
iOS 9http协议的访问Info.plist使URL Schemes列为白名单
<key>LSApplicationQueriesSchemes</key>
@ -72,7 +73,13 @@ iOS 9系统策略更新限制了http协议的访问此外应用需要在
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
````
```
```
Universal Links
Capabilities -> Associated Domain -> Domain -> applinks:${your applinks}
```
## flutter
@ -83,19 +90,19 @@ iOS 9系统策略更新限制了http协议的访问此外应用需要在
* snapshot
````
```
dependencies:
fake_tencent:
tencent_kit:
git:
url: https://github.com/v7lin/fake_tencent.git
````
```
* release
````
```
dependencies:
fake_tencent: ^${latestTag}
````
tencent_kit: ^${latestTag}
```
* example
@ -104,10 +111,10 @@ dependencies:
## Getting Started
This project is a starting point for a Flutter
[plug-in package](https://flutter.io/developing-packages/),
[plug-in package](https://flutter.dev/developing-packages/),
a specialized package that includes platform-specific implementation code for
Android and/or iOS.
For help getting started with Flutter, view our
[online documentation](https://flutter.io/docs), which offers tutorials,
[online documentation](https://flutter.dev/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

View File

@ -19,114 +19,117 @@ analyzer:
# 'super_goes_last' is a deprecated lint rule and should not be used included_file_warning
included_file_warning: ignore
# jaguar_serializer
# json_serializable
exclude:
- "**/*.jser.dart"
- "**/*.g.dart"
linter:
rules:
# these rules are documented on and in the same order as
# the Dart Lint rules page to make maintenance easier
# http://dart-lang.github.io/linter/lints/
# these rules are documented on and in the same order as
# the Dart Lint rules page to make maintenance easier
# http://dart-lang.github.io/linter/lints/
# === error rules ===
- avoid_empty_else
- avoid_slow_async_io
- cancel_subscriptions
# - close_sinks # https://github.com/flutter/flutter/issues/5789
# - comment_references # blocked on https://github.com/dart-lang/dartdoc/issues/1153
- control_flow_in_finally
- empty_statements
- hash_and_equals
# - invariant_booleans # https://github.com/flutter/flutter/issues/5790
- iterable_contains_unrelated_type
- list_remove_unrelated_type
# - literal_only_boolean_expressions # https://github.com/flutter/flutter/issues/5791
- no_adjacent_strings_in_list
- no_duplicate_case_values
- test_types_in_equals
- throw_in_finally
- unrelated_type_equality_checks
- valid_regexps
# === error rules ===
- avoid_empty_else
- avoid_slow_async_io
- cancel_subscriptions
# - close_sinks # https://github.com/flutter/flutter/issues/5789
# - comment_references # blocked on https://github.com/dart-lang/dartdoc/issues/1153
- control_flow_in_finally
- empty_statements
- hash_and_equals
# - invariant_booleans # https://github.com/flutter/flutter/issues/5790
- iterable_contains_unrelated_type
- list_remove_unrelated_type
# - literal_only_boolean_expressions # https://github.com/flutter/flutter/issues/5791
- no_adjacent_strings_in_list
- no_duplicate_case_values
- test_types_in_equals
- throw_in_finally
- unrelated_type_equality_checks
- valid_regexps
# === style rules ===
- always_declare_return_types
# - always_put_control_body_on_new_line
- always_require_non_null_named_parameters
- always_specify_types
- annotate_overrides
# - avoid_annotating_with_dynamic # not yet tested
# - avoid_as # 2019-01-30 removed for no-implicit-casts
# - avoid_catches_without_on_clauses # not yet tested
# - avoid_catching_errors # not yet tested
# - avoid_classes_with_only_static_members # not yet tested
# - avoid_function_literals_in_foreach_calls # not yet tested
- avoid_init_to_null
- avoid_null_checks_in_equality_operators
# - avoid_positional_boolean_parameters # not yet tested
- avoid_return_types_on_setters
# - avoid_returning_null # not yet tested
# - avoid_returning_this # not yet tested
# - avoid_setters_without_getters # not yet tested
# - avoid_types_on_closure_parameters # not yet tested
- await_only_futures
- camel_case_types
# - cascade_invocations # not yet tested
# - constant_identifier_names # https://github.com/dart-lang/linter/issues/204
- directives_ordering
- empty_catches
- empty_constructor_bodies
- implementation_imports
# - join_return_with_assignment # not yet tested
- library_names
- library_prefixes
- non_constant_identifier_names
# - omit_local_variable_types # opposite of always_specify_types
# - one_member_abstracts # too many false positives
# - only_throw_errors # https://github.com/flutter/flutter/issues/5792
- overridden_fields
- package_api_docs
- package_prefixed_library_names
# - parameter_assignments # we do this commonly
- prefer_adjacent_string_concatenation
- prefer_collection_literals
# - prefer_conditional_assignment # not yet tested
- prefer_const_constructors
# - prefer_constructors_over_static_methods # not yet tested
- prefer_contains
- prefer_equal_for_default_values
# - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods
# - prefer_final_fields # https://github.com/dart-lang/linter/issues/506
# - prefer_final_locals
# - prefer_foreach # not yet tested
# - prefer_function_declarations_over_variables # not yet tested
- prefer_initializing_formals
# - prefer_interpolation_to_compose_strings # not yet tested
- prefer_is_empty
- prefer_is_not_empty
- prefer_void_to_null
# - recursive_getters # https://github.com/dart-lang/linter/issues/452
- slash_for_doc_comments
- sort_constructors_first
- sort_unnamed_constructors_first
# - super_goes_last
# - type_annotate_public_apis # subset of always_specify_types
- type_init_formals
# - unawaited_futures # https://github.com/flutter/flutter/issues/5793
- unnecessary_brace_in_string_interps
- unnecessary_const
- unnecessary_getters_setters
# - unnecessary_lambdas # https://github.com/dart-lang/linter/issues/498
- unnecessary_new
- unnecessary_null_aware_assignments
- unnecessary_null_in_if_null_operators
# - unnecessary_overrides # https://github.com/dart-lang/linter/issues/626 and https://github.com/dart-lang/linter/issues/627
- unnecessary_statements
- unnecessary_this
- use_rethrow_when_possible
# - use_setters_to_change_properties # not yet tested
# - use_string_buffers # https://github.com/dart-lang/linter/pull/664
# - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review
# === style rules ===
- always_declare_return_types
# - always_put_control_body_on_new_line
- always_require_non_null_named_parameters
- always_specify_types
- annotate_overrides
# - avoid_annotating_with_dynamic # not yet tested
# - avoid_as # 2019-01-30 removed for no-implicit-casts
# - avoid_catches_without_on_clauses # not yet tested
# - avoid_catching_errors # not yet tested
# - avoid_classes_with_only_static_members # not yet tested
# - avoid_function_literals_in_foreach_calls # not yet tested
- avoid_init_to_null
- avoid_null_checks_in_equality_operators
# - avoid_positional_boolean_parameters # not yet tested
- avoid_return_types_on_setters
# - avoid_returning_null # not yet tested
# - avoid_returning_this # not yet tested
# - avoid_setters_without_getters # not yet tested
# - avoid_types_on_closure_parameters # not yet tested
- await_only_futures
- camel_case_types
# - cascade_invocations # not yet tested
# - constant_identifier_names # https://github.com/dart-lang/linter/issues/204
- directives_ordering
- empty_catches
- empty_constructor_bodies
- implementation_imports
# - join_return_with_assignment # not yet tested
- library_names
- library_prefixes
- non_constant_identifier_names
# - omit_local_variable_types # opposite of always_specify_types
# - one_member_abstracts # too many false positives
# - only_throw_errors # https://github.com/flutter/flutter/issues/5792
- overridden_fields
- package_api_docs
- package_prefixed_library_names
# - parameter_assignments # we do this commonly
- prefer_adjacent_string_concatenation
- prefer_collection_literals
# - prefer_conditional_assignment # not yet tested
- prefer_const_constructors
# - prefer_constructors_over_static_methods # not yet tested
- prefer_contains
- prefer_equal_for_default_values
# - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods
# - prefer_final_fields # https://github.com/dart-lang/linter/issues/506
# - prefer_final_locals
# - prefer_foreach # not yet tested
# - prefer_function_declarations_over_variables # not yet tested
- prefer_initializing_formals
# - prefer_interpolation_to_compose_strings # not yet tested
- prefer_is_empty
- prefer_is_not_empty
- prefer_void_to_null
# - recursive_getters # https://github.com/dart-lang/linter/issues/452
- slash_for_doc_comments
- sort_constructors_first
- sort_unnamed_constructors_first
# - super_goes_last
# - type_annotate_public_apis # subset of always_specify_types
- type_init_formals
# - unawaited_futures # https://github.com/flutter/flutter/issues/5793
- unnecessary_brace_in_string_interps
- unnecessary_const
- unnecessary_getters_setters
# - unnecessary_lambdas # https://github.com/dart-lang/linter/issues/498
- unnecessary_new
- unnecessary_null_aware_assignments
- unnecessary_null_in_if_null_operators
# - unnecessary_overrides # https://github.com/dart-lang/linter/issues/626 and https://github.com/dart-lang/linter/issues/627
- unnecessary_statements
- unnecessary_this
- use_rethrow_when_possible
# - use_setters_to_change_properties # not yet tested
# - use_string_buffers # https://github.com/dart-lang/linter/pull/664
# - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review
# === pub rules ===
- package_names
# === pub rules ===
- package_names
# === doc rules ===
# - public_member_api_docs

View File

@ -1,5 +1,5 @@
group 'io.github.v7lin.faketencent'
version '1.0-SNAPSHOT'
group 'io.github.v7lin.tencent_kit'
version '1.0.0'
buildscript {
repositories {
@ -8,7 +8,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
classpath 'com.android.tools.build:gradle:3.5.2'
}
}
@ -20,13 +20,18 @@ rootProject.allprojects {
}
apply plugin: 'com.android.library'
apply from: './quality.gradle'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 16
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
// library 混淆 -> 随 library 引用,自动添加到 apk 打包混淆
consumerProguardFiles 'consumer-proguard-rules.pro'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
lintOptions {
disable 'InvalidPackage'
@ -36,5 +41,5 @@ android {
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'io.github.v7lin:tencent-android:3.3.3+3'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
}

135
android/checkstyle.xml Normal file
View File

@ -0,0 +1,135 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
"http://checkstyle.sourceforge.net/dtds/configuration_1_3.dtd">
<module name="Checker">
<!--module name="NewlineAtEndOfFile"/-->
<module name="FileLength"/>
<module name="FileTabCharacter"/>
<!-- Trailing spaces -->
<module name="RegexpSingleline">
<property name="format" value="\s+$"/>
<property name="message" value="Line has trailing spaces."/>
</module>
<!-- Space after 'for' and 'if' -->
<module name="RegexpSingleline">
<property name="format" value="^\s*(for|if)[^ ]\("/>
<property name="message" value="Space needed before opening parenthesis."/>
</module>
<!-- For each spacing -->
<module name="RegexpSingleline">
<property name="format" value="^\s*for \(.*?([^ ]:|:[^ ])"/>
<property name="message" value="Space needed around ':' character."/>
</module>
<module name="TreeWalker">
<!--<property name="cacheFile" value="${checkstyle.cache.file}"/>-->
<!-- Checks for Javadoc comments. -->
<!-- See http://checkstyle.sf.net/config_javadoc.html -->
<!--module name="JavadocMethod"/-->
<!--module name="JavadocType"/-->
<!--module name="JavadocVariable"/-->
<!--module name="JavadocStyle"/-->
<!-- Checks for Naming Conventions. -->
<!-- See http://checkstyle.sf.net/config_naming.html -->
<!--<module name="ConstantName"/>-->
<module name="LocalFinalVariableName"/>
<module name="LocalVariableName"/>
<module name="MemberName"/>
<module name="MethodName">
<property name="format" value="^[a-z][a-zA-Z0-9_]*$"/>
</module>
<module name="PackageName"/>
<module name="ParameterName"/>
<module name="StaticVariableName"/>
<module name="TypeName"/>
<!-- Checks for imports -->
<!-- See http://checkstyle.sf.net/config_import.html -->
<module name="AvoidStarImport"/>
<module name="IllegalImport"/>
<module name="RedundantImport"/>
<module name="UnusedImports">
<property name="processJavadoc" value="true"/>
</module>
<!-- Checks for Size Violations. -->
<!-- See http://checkstyle.sf.net/config_sizes.html -->
<module name="LineLength">
<property name="max" value="200"/>
</module>
<module name="MethodLength"/>
<!--module name="ParameterNumber"/-->
<!-- Checks for whitespace -->
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
<module name="GenericWhitespace"/>
<module name="EmptyForIteratorPad"/>
<module name="MethodParamPad"/>
<!--<module name="NoWhitespaceAfter"/>-->
<module name="NoWhitespaceBefore"/>
<module name="OperatorWrap"/>
<module name="ParenPad"/>
<module name="TypecastParenPad"/>
<module name="WhitespaceAfter"/>
<module name="WhitespaceAround"/>
<!-- Modifier Checks -->
<!-- See http://checkstyle.sf.net/config_modifiers.html -->
<module name="ModifierOrder"/>
<module name="RedundantModifier"/>
<!-- Checks for blocks. You know, those {}'s -->
<!-- See http://checkstyle.sf.net/config_blocks.html -->
<!--module name="AvoidNestedBlocks"/-->
<!--module name="EmptyBlock"/-->
<module name="LeftCurly"/>
<!--module name="NeedBraces"/-->
<module name="RightCurly"/>
<!-- Checks for common coding problems -->
<!-- See http://checkstyle.sf.net/config_coding.html -->
<!--module name="AvoidInlineConditionals"/-->
<module name="CovariantEquals"/>
<module name="EmptyStatement"/>
<!--<module name="EqualsAvoidNull"/>-->
<module name="EqualsHashCode"/>
<!--module name="HiddenField"/-->
<module name="IllegalInstantiation"/>
<!--<module name="InnerAssignment"/>-->
<!--module name="MagicNumber"/-->
<module name="MissingSwitchDefault"/>
<!--<module name="RedundantThrows"/>-->
<module name="SimplifyBooleanExpression"/>
<module name="SimplifyBooleanReturn"/>
<!-- Checks for class design -->
<!-- See http://checkstyle.sf.net/config_design.html -->
<!--module name="DesignForExtension"/-->
<!--module name="FinalClass"/-->
<!--module name="HideUtilityClassConstructor"/-->
<!--module name="InterfaceIsType"/-->
<!--module name="VisibilityModifier"/-->
<!-- Miscellaneous other checks. -->
<!-- See http://checkstyle.sf.net/config_misc.html -->
<!--module name="ArrayTypeStyle"/-->
<!--module name="FinalParameters"/-->
<!--module name="TodoComment"/-->
<module name="UpperEll"/>
</module>
</module>

View File

@ -0,0 +1,3 @@
# --- open_sdk ---
-keep class com.tencent.** {*;}

View File

@ -1 +1,5 @@
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
android.enableR8=true

Binary file not shown.

17
android/quality.gradle Normal file
View File

@ -0,0 +1,17 @@
apply plugin: 'checkstyle'
check.dependsOn 'checkstyle'
checkstyle {
// toolVersion = "6.15"
}
task checkstyle(type: Checkstyle) {
configFile project.file('checkstyle.xml')
source 'src/main/java'
ignoreFailures false
showViolations true
include '**/*.java'
classpath = files()
}

View File

@ -1 +1 @@
rootProject.name = 'fake_tencent'
rootProject.name = 'tencent_kit'

View File

@ -1,3 +1,34 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.github.v7lin.faketencent">
package="io.github.v7lin.tencent_kit">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!--<uses-permission android:name="android.permission.WRITE_APN_SETTINGS"/>-->
<application android:usesCleartextTraffic="true">
<uses-library
android:name="org.apache.http.legacy"
android:required="false" />
<activity
android:name="com.tencent.tauth.AuthActivity"
android:launchMode="singleTask"
android:noHistory="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="tencent${TENCENT_APP_ID}" />
</intent-filter>
</activity>
<activity
android:name="com.tencent.connect.common.AssistActivity"
android:configChanges="orientation|keyboardHidden"
android:screenOrientation="behind"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
</application>
</manifest>

View File

@ -1,4 +1,4 @@
package io.github.v7lin.faketencent;
package io.github.v7lin.tencent_kit;
import android.content.Intent;
import android.content.pm.PackageInfo;
@ -7,7 +7,8 @@ import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import com.tencent.connect.UserInfo;
import androidx.annotation.NonNull;
import com.tencent.connect.common.Constants;
import com.tencent.connect.share.QQShare;
import com.tencent.connect.share.QzonePublish;
@ -21,7 +22,6 @@ import org.json.JSONObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@ -33,55 +33,49 @@ import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.common.PluginRegistry.Registrar;
/**
* FakeTencentPlugin
* TencentKitPlugin
*/
public class FakeTencentPlugin implements MethodCallHandler, PluginRegistry.ActivityResultListener {
public class TencentKitPlugin implements MethodCallHandler, PluginRegistry.ActivityResultListener {
/**
* Plugin registration.
*/
public static void registerWith(Registrar registrar) {
final MethodChannel channel = new MethodChannel(registrar.messenger(), "v7lin.github.io/fake_tencent");
FakeTencentPlugin plugin = new FakeTencentPlugin(registrar, channel);
final MethodChannel channel = new MethodChannel(registrar.messenger(), "v7lin.github.io/tencent_kit");
TencentKitPlugin plugin = new TencentKitPlugin(registrar, channel);
registrar.addActivityResultListener(plugin);
channel.setMethodCallHandler(plugin);
}
private static class TencentScene {
public static final int SCENE_QQ = 0;
public static final int SCENE_QZONE = 1;
static final int SCENE_QQ = 0;
static final int SCENE_QZONE = 1;
}
private static class TencentRetCode {
//
//
public static final int RET_SUCCESS = 0;
static final int RET_SUCCESS = 0;
//
public static final int RET_FAILED = 1;
public static final int RET_COMMON = -1;
public static final int RET_USERCANCEL = -2;
static final int RET_FAILED = 1;
static final int RET_COMMON = -1;
static final int RET_USERCANCEL = -2;
}
private static final String METHOD_REGISTERAPP = "registerApp";
private static final String METHOD_ISQQINSTALLED = "isQQInstalled";
private static final String METHOD_ISQQSUPPORTSSOLOGIN = "isQQSupportSSOLogin";
private static final String METHOD_ISINSTALLED = "isInstalled";
private static final String METHOD_LOGIN = "login";
private static final String METHOD_LOGOUT = "logout";
private static final String METHOD_GETUSERINFO = "getUserInfo";
private static final String METHOD_SHAREMOOD = "shareMood";
private static final String METHOD_SHAREIMAGE = "shareImage";
private static final String METHOD_SHAREMUSIC = "shareMusic";
private static final String METHOD_SHAREWEBPAGE = "shareWebpage";
private static final String METHOD_ONLOGINRESP = "onLoginResp";
private static final String METHOD_ONGETUSERINFORESP = "onGetUserInfoResp";
private static final String METHOD_ONSHARERESP = "onShareResp";
private static final String ARGUMENT_KEY_APPID = "appId";
// private static final String ARGUMENT_KEY_UNIVERSALLINK = "universalLink";
private static final String ARGUMENT_KEY_SCOPE = "scope";
private static final String ARGUMENT_KEY_OPENID = "openId";
private static final String ARGUMENT_KEY_ACCESSTOKEN = "accessToken";
private static final String ARGUMENT_KEY_EXPIRESIN = "expiresIn";
private static final String ARGUMENT_KEY_CREATEAT = "createAt";
private static final String ARGUMENT_KEY_SCENE = "scene";
private static final String ARGUMENT_KEY_TITLE = "title";
private static final String ARGUMENT_KEY_SUMMARY = "summary";
@ -107,34 +101,31 @@ public class FakeTencentPlugin implements MethodCallHandler, PluginRegistry.Acti
private Tencent tencent;
private FakeTencentPlugin(Registrar registrar, MethodChannel channel) {
private TencentKitPlugin(Registrar registrar, MethodChannel channel) {
this.registrar = registrar;
this.channel = channel;
}
@Override
public void onMethodCall(MethodCall call, Result result) {
public void onMethodCall(MethodCall call, @NonNull Result result) {
if (METHOD_REGISTERAPP.equals(call.method)) {
String appId = call.argument(ARGUMENT_KEY_APPID);
final String appId = call.argument(ARGUMENT_KEY_APPID);
// final String universalLink = call.argument(ARGUMENT_KEY_UNIVERSALLINK);
tencent = Tencent.createInstance(appId, registrar.context().getApplicationContext());
result.success(null);
} else if (METHOD_ISQQINSTALLED.equals(call.method)) {
} else if (METHOD_ISINSTALLED.equals(call.method)) {
boolean isQQInstalled = false;
try {
final PackageManager packageManager = registrar.context().getPackageManager();
PackageInfo info = packageManager.getPackageInfo("com.tencent.mobileqq", PackageManager.GET_SIGNATURES);
PackageInfo info = packageManager.getPackageInfo("com.tencent.mobileqq", 0);
isQQInstalled = info != null;
} catch (PackageManager.NameNotFoundException e) {
} catch (PackageManager.NameNotFoundException ignore) {
}
result.success(isQQInstalled);
} else if (METHOD_ISQQSUPPORTSSOLOGIN.equals(call.method)) {
result.success(tencent.isSupportSSOLogin(registrar.activity()));
} else if (METHOD_LOGIN.equals(call.method)) {
login(call, result);
} else if (METHOD_LOGOUT.equals(call.method)) {
logout(call, result);
} else if (METHOD_GETUSERINFO.equals(call.method)) {
getUserInfo(call, result);
} else if (METHOD_SHAREMOOD.equals(call.method)) {
shareMood(call, result);
} else if (METHOD_SHAREIMAGE.equals(call.method)) {
@ -217,62 +208,6 @@ public class FakeTencentPlugin implements MethodCallHandler, PluginRegistry.Acti
}
}
private void getUserInfo(MethodCall call, Result result) {
if (tencent != null) {
String openId = call.argument(ARGUMENT_KEY_OPENID);
tencent.setOpenId(openId);
String accessToken = call.argument(ARGUMENT_KEY_ACCESSTOKEN);
int expiresIn = call.argument(ARGUMENT_KEY_EXPIRESIN);
long createAt = call.argument(ARGUMENT_KEY_CREATEAT);
tencent.setAccessToken(accessToken, String.valueOf(expiresIn - (System.currentTimeMillis() - createAt) / 1000));
UserInfo info = new UserInfo(registrar.context().getApplicationContext(), tencent.getQQToken());
info.getUserInfo(userInfoListener);
}
result.success(null);
}
private IUiListener userInfoListener = new IUiListener() {
@Override
public void onComplete(Object o) {
Map<String, Object> map = new HashMap<>();
try {
if (o != null && o instanceof JSONObject) {
JSONObject object = (JSONObject) o;
int ret = !object.isNull(ARGUMENT_KEY_RESULT_RET) ? object.getInt(ARGUMENT_KEY_RESULT_RET) : TencentRetCode.RET_FAILED;
String msg = !object.isNull(ARGUMENT_KEY_RESULT_MSG) ? object.getString(ARGUMENT_KEY_RESULT_MSG) : null;
if (ret == TencentRetCode.RET_SUCCESS) {
map.put(ARGUMENT_KEY_RESULT_RET, TencentRetCode.RET_SUCCESS);
Iterator<String> keys = object.keys();
while (keys.hasNext()) {
String key = keys.next();
map.put(key, object.get(key));
}
} else {
map.put(ARGUMENT_KEY_RESULT_RET, TencentRetCode.RET_COMMON);
map.put(ARGUMENT_KEY_RESULT_MSG, msg);
}
}
} catch (JSONException e) {
map.put(ARGUMENT_KEY_RESULT_RET, TencentRetCode.RET_COMMON);
map.put(ARGUMENT_KEY_RESULT_MSG, e.getMessage());
}
channel.invokeMethod(METHOD_ONGETUSERINFORESP, map);
}
@Override
public void onError(UiError error) {
Map<String, Object> map = new HashMap<>();
map.put(ARGUMENT_KEY_RESULT_RET, TencentRetCode.RET_COMMON);
map.put(ARGUMENT_KEY_RESULT_MSG, error.errorMessage);
channel.invokeMethod(METHOD_ONGETUSERINFORESP, map);
}
@Override
public void onCancel() {
// do nothing
}
};
private void shareMood(MethodCall call, Result result) {
if (tencent != null) {
int scene = call.argument(ARGUMENT_KEY_SCENE);
@ -416,6 +351,8 @@ public class FakeTencentPlugin implements MethodCallHandler, PluginRegistry.Acti
params.putString(QzoneShare.SHARE_TO_QQ_TARGET_URL, targetUrl);
tencent.shareToQzone(registrar.activity(), params, shareListener);
break;
default:
break;
}
}
result.success(null);
@ -465,23 +402,12 @@ public class FakeTencentPlugin implements MethodCallHandler, PluginRegistry.Acti
@Override
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case Constants.REQUEST_API:
// unused
break;
case Constants.REQUEST_LOGIN:
return Tencent.onActivityResultData(requestCode, resultCode, data, loginListener);
case Constants.REQUEST_QQ_SHARE:
return Tencent.onActivityResultData(requestCode, resultCode, data, shareListener);
case Constants.REQUEST_QZONE_SHARE:
return Tencent.onActivityResultData(requestCode, resultCode, data, shareListener);
case Constants.REQUEST_QQ_FAVORITES:
// unused
break;
case Constants.REQUEST_SEND_TO_MY_COMPUTER:
// unused
break;
case Constants.REQUEST_SHARE_TO_TROOP_BAR:
// unused
default:
break;
}
return false;

12
example/.gitignore vendored
View File

@ -1,6 +1,5 @@
# Miscellaneous
*.class
*.lock
*.log
*.pyc
*.swp
@ -16,8 +15,10 @@
*.iws
.idea/
# Visual Studio Code related
.vscode/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
@ -26,7 +27,7 @@
.packages
.pub-cache/
.pub/
build/
/build/
# Android related
**/android/**/gradle-wrapper.jar
@ -60,10 +61,9 @@ build/
**/ios/Flutter/app.flx
**/ios/Flutter/app.zip
**/ios/Flutter/flutter_assets/
**/ios/Flutter/flutter_export_environment.sh
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*
**/ios/Frameworks/
**/ios/Runner.xcworkspace/xcshareddata/
# Exceptions to above rules.
!**/ios/**/default.mode1v3

View File

@ -4,7 +4,7 @@
# This file should be version controlled and should not be manually edited.
version:
revision: 5391447fae6209bb21a89e6a5a6583cac1af9b4b
revision: 68587a0916366e9512a78df22c44163d041dd5f3
channel: stable
project_type: app

View File

@ -1,6 +1,6 @@
# fake_tencent_example
# tencent_kit_example
Demonstrates how to use the fake_tencent plugin.
Demonstrates how to use the tencent_kit plugin.
## Getting Started
@ -8,9 +8,9 @@ This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://flutter.io/docs/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://flutter.io/docs/cookbook)
- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
For help getting started with Flutter, view our
[online documentation](https://flutter.io/docs), which offers tutorials,
For help getting started with Flutter, view our
[online documentation](https://flutter.dev/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

View File

@ -10,23 +10,31 @@ analyzer:
linter:
rules:
- iterable_contains_unrelated_type
- list_remove_unrelated_type
- test_types_in_equals
- unrelated_type_equality_checks
- valid_regexps
- annotate_overrides
- hash_and_equals
- prefer_is_not_empty
- avoid_empty_else
- cancel_subscriptions
- close_sinks
- always_declare_return_types
- camel_case_types
- empty_constructor_bodies
- avoid_init_to_null
# - constant_identifier_names
- one_member_abstracts
- slash_for_doc_comments
- sort_constructors_first
- unnecessary_new
- iterable_contains_unrelated_type
- list_remove_unrelated_type
- test_types_in_equals
- unrelated_type_equality_checks
- valid_regexps
- annotate_overrides
- hash_and_equals
- prefer_is_not_empty
- avoid_empty_else
- cancel_subscriptions
- close_sinks
- always_declare_return_types
- camel_case_types
- empty_constructor_bodies
- avoid_init_to_null
# - constant_identifier_names
- one_member_abstracts
- slash_for_doc_comments
- sort_constructors_first
- unnecessary_brace_in_string_interps
# === doc rules ===
# - public_member_api_docs
# ignore specify types
# - always_specify_types
# ignore const for now
# - prefer_const_constructors

View File

@ -33,7 +33,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "io.github.v7lin.faketencentexample"
applicationId "com.tencent.sample"
minSdkVersion 16
targetSdkVersion 28
versionCode flutterVersionCode.toInteger()
@ -41,7 +41,13 @@ android {
manifestPlaceholders = [TENCENT_APP_ID: "222222"]
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
signingConfigs {
debug {
storeFile file('debug.keystore')
}
}
buildTypes {
@ -51,7 +57,6 @@ android {
signingConfig signingConfigs.debug
minifyEnabled true
useProguard true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
@ -64,6 +69,6 @@ flutter {
dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}

Binary file not shown.

View File

@ -1,8 +1,9 @@
#Flutter Wrapper
# Flutter Wrapper
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }
-dontwarn io.flutter.**
-dontwarn io.flutter.**

View File

@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.github.v7lin.tencent_kit_example">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View File

@ -1,11 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.github.v7lin.faketencentexample">
<!-- The INTERNET permission is required for development. Specifically,
flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
package="io.github.v7lin.tencent_kit_example">
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
@ -14,13 +8,13 @@
FlutterApplication and put your custom class here. -->
<application
android:name="io.flutter.app.FlutterApplication"
android:label="fake_tencent_example"
android:label="tencent_kit_example"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- This keeps the window background of the activity showing

View File

@ -1,4 +1,4 @@
package io.github.v7lin.faketencentexample;
package io.github.v7lin.tencent_kit_example;
import android.os.Bundle;
import io.flutter.app.FlutterActivity;

View File

@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.github.v7lin.tencent_kit_example">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View File

@ -0,0 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.github.v7lin.tencent_kit_example">
<!-- network request -->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View File

@ -5,7 +5,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath 'com.android.tools.build:gradle:3.5.2'
}
}

View File

@ -1 +1,5 @@
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
android.enableR8=true

View File

@ -1,4 +1,4 @@
#Tue Sep 10 20:41:08 CST 2019
#Wed Nov 06 18:22:26 CST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 KiB

View File

@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>

View File

@ -41,7 +41,7 @@ target 'Runner' do
# Flutter Pods
generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig')
if generated_xcode_build_settings.empty?
puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first."
puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first."
end
generated_xcode_build_settings.map { |p|
if p[:name] == 'FLUTTER_FRAMEWORK_DIR'
@ -60,6 +60,9 @@ target 'Runner' do
}
end
# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system.
install! 'cocoapods', :disable_input_output_paths => true
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|

28
example/ios/Podfile.lock Normal file
View File

@ -0,0 +1,28 @@
PODS:
- Flutter (1.0.0)
- path_provider (0.0.1):
- Flutter
- tencent_kit (1.0.0):
- Flutter
DEPENDENCIES:
- Flutter (from `.symlinks/flutter/ios`)
- path_provider (from `.symlinks/plugins/path_provider/ios`)
- tencent_kit (from `.symlinks/plugins/tencent_kit/ios`)
EXTERNAL SOURCES:
Flutter:
:path: ".symlinks/flutter/ios"
path_provider:
:path: ".symlinks/plugins/path_provider/ios"
tencent_kit:
:path: ".symlinks/plugins/tencent_kit/ios"
SPEC CHECKSUMS:
Flutter: 0e3d915762c693b495b44d77113d4970485de6ec
path_provider: fb74bd0465e96b594bb3b5088ee4a4e7bb1f2a9d
tencent_kit: ef18a7780c8d8503b448f7d8f25b48abd2706e11
PODFILE CHECKSUM: 7fb83752f59ead6285236625b82473f90b1cb932
COCOAPODS: 1.8.0

View File

@ -11,7 +11,7 @@
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
9422253582A6C9D275F8D188 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EC2B9EE613E5EDCF95CDB72B /* libPods-Runner.a */; };
6CE270E48E48FA2184491F5B /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 22AA494CBAC5718A9A5B59FE /* libPods-Runner.a */; };
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; };
@ -40,8 +40,11 @@
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
22AA494CBAC5718A9A5B59FE /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
34BDC42E441A1D281667DFB2 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
6E97E3F72373BCD2002BA53E /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
@ -54,7 +57,8 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
EC2B9EE613E5EDCF95CDB72B /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
C01AB4EEE4214A456A0AED3C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
C907325B6974A5E7E013AA51 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -64,18 +68,21 @@
files = (
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
9422253582A6C9D275F8D188 /* libPods-Runner.a in Frameworks */,
6CE270E48E48FA2184491F5B /* libPods-Runner.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
5C6AC5BA9442E9AF04E7447D /* Pods */ = {
15DE2C788FC69000FD6D9F20 /* Pods */ = {
isa = PBXGroup;
children = (
C907325B6974A5E7E013AA51 /* Pods-Runner.debug.xcconfig */,
C01AB4EEE4214A456A0AED3C /* Pods-Runner.release.xcconfig */,
34BDC42E441A1D281667DFB2 /* Pods-Runner.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
@ -97,8 +104,8 @@
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
5C6AC5BA9442E9AF04E7447D /* Pods */,
D579DE886E1BD687DF6DBF83 /* Frameworks */,
15DE2C788FC69000FD6D9F20 /* Pods */,
D53051BB3AA8D8CE3C0019AC /* Frameworks */,
);
sourceTree = "<group>";
};
@ -113,6 +120,7 @@
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
6E97E3F72373BCD2002BA53E /* Runner.entitlements */,
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
97C146FA1CF9000F007C117D /* Main.storyboard */,
@ -134,10 +142,10 @@
name = "Supporting Files";
sourceTree = "<group>";
};
D579DE886E1BD687DF6DBF83 /* Frameworks */ = {
D53051BB3AA8D8CE3C0019AC /* Frameworks */ = {
isa = PBXGroup;
children = (
EC2B9EE613E5EDCF95CDB72B /* libPods-Runner.a */,
22AA494CBAC5718A9A5B59FE /* libPods-Runner.a */,
);
name = Frameworks;
sourceTree = "<group>";
@ -149,14 +157,14 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
0D238ADF129651F8319C4F7F /* [CP] Check Pods Manifest.lock */,
3ACA0EBF42EA843298FCA536 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
7175A6E06323B12A711474E5 /* [CP] Embed Pods Frameworks */,
44F7BC7464E022FF09A3806E /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@ -173,18 +181,19 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0910;
LastUpgradeCheck = 1020;
ORGANIZATIONNAME = "The Chromium Authors";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
DevelopmentTeam = 78W43A3TE2;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
@ -216,16 +225,20 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
0D238ADF129651F8319C4F7F /* [CP] Check Pods Manifest.lock */ = {
3ACA0EBF42EA843298FCA536 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
@ -246,24 +259,21 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin\n";
};
7175A6E06323B12A711474E5 /* [CP] Embed Pods Frameworks */ = {
44F7BC7464E022FF09A3806E /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
"${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
@ -278,7 +288,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
};
/* End PBXShellScriptBuildPhase section */
@ -329,12 +339,14 @@
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
@ -368,6 +380,9 @@
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 78W43A3TE2;
ENABLE_BITCODE = NO;
@ -381,8 +396,9 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = io.github.v7lin.fakeTencentExample;
PRODUCT_BUNDLE_IDENTIFIER = io.github.v7lin.tencentKitExample;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
@ -401,12 +417,14 @@
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
@ -455,12 +473,14 @@
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
@ -494,6 +514,9 @@
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 78W43A3TE2;
ENABLE_BITCODE = NO;
@ -507,8 +530,9 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = io.github.v7lin.fakeTencentExample;
PRODUCT_BUNDLE_IDENTIFIER = io.github.v7lin.tencentKitExample;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
@ -518,6 +542,9 @@
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 78W43A3TE2;
ENABLE_BITCODE = NO;
@ -531,8 +558,9 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = io.github.v7lin.fakeTencentExample;
PRODUCT_BUNDLE_IDENTIFIER = io.github.v7lin.tencentKitExample;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0910"
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
@ -11,7 +11,7 @@
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>fake_tencent_example</string>
<string>tencent_kit_example</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>

View File

@ -2,25 +2,12 @@ import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fake_path_provider/fake_path_provider.dart';
import 'package:fake_tencent/fake_tencent.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart' as path_provider;
import 'package:tencent_kit/tencent_kit.dart';
import 'package:okhttp_kit/okhttp_kit.dart';
void main() {
runZoned(() {
runApp(MyApp());
}, onError: (Object error, StackTrace stack) {
print(error);
print(stack);
});
if (Platform.isAndroid) {
SystemUiOverlayStyle systemUiOverlayStyle =
SystemUiOverlayStyle(statusBarColor: Colors.transparent);
SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);
}
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
@ -44,7 +31,6 @@ class _HomeState extends State<Home> {
Tencent _tencent = Tencent()..registerApp(appId: _TENCENT_APPID);
StreamSubscription<TencentLoginResp> _login;
StreamSubscription<TencentUserInfoResp> _userInfo;
StreamSubscription<TencentShareResp> _share;
TencentLoginResp _loginResp;
@ -53,7 +39,6 @@ class _HomeState extends State<Home> {
void initState() {
super.initState();
_login = _tencent.loginResp().listen(_listenLogin);
_userInfo = _tencent.userInfoResp().listen(_listenUserInfo);
_share = _tencent.shareResp().listen(_listenShare);
}
@ -63,11 +48,6 @@ class _HomeState extends State<Home> {
_showTips('登录', content);
}
void _listenUserInfo(TencentUserInfoResp resp) {
String content = 'user info: ${resp.nickname} - ${resp.gender}';
_showTips('用户', content);
}
void _listenShare(TencentShareResp resp) {
String content = 'share: ${resp.ret} - ${resp.msg}';
_showTips('分享', content);
@ -78,9 +58,6 @@ class _HomeState extends State<Home> {
if (_login != null) {
_login.cancel();
}
if (_userInfo != null) {
_userInfo.cancel();
}
if (_share != null) {
_share.cancel();
}
@ -91,15 +68,14 @@ class _HomeState extends State<Home> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Fake Tencent Demo'),
title: const Text('TencentKit Demo'),
),
body: ListView(
children: <Widget>[
ListTile(
title: const Text('环境检查'),
onTap: () async {
String content =
'tencent: ${await _tencent.isQQInstalled()} - ${await _tencent.isQQSupportSSOLogin()}';
String content = 'tencent: ${await _tencent.isInstalled()}';
_showTips('环境检查', content);
},
),
@ -113,18 +89,39 @@ class _HomeState extends State<Home> {
),
ListTile(
title: const Text('获取用户信息'),
onTap: () {
onTap: () async {
if (_loginResp != null &&
_loginResp.ret == TencentResp.RET_SUCCESS) {
if (DateTime.now().millisecondsSinceEpoch -
_loginResp.createAt <
_loginResp.expiresIn * 1000) {
_tencent.getUserInfo(
openId: _loginResp.openid,
accessToken: _loginResp.accessToken,
expiresIn: _loginResp.expiresIn,
createAt: _loginResp.createAt,
);
_loginResp.isSuccessful() &&
!_loginResp.isExpired()) {
TencentUserInfoResp userInfo = await _tencent.getUserInfo(
appId: _TENCENT_APPID,
openid: _loginResp.openid,
accessToken: _loginResp.accessToken,
);
if (userInfo.isSuccessful()) {
_showTips('用户信息',
'${userInfo.nickname} - ${userInfo.gender} - ${userInfo.genderType}');
} else {
_showTips('用户信息', '${userInfo.ret} - ${userInfo.msg}');
}
}
},
),
ListTile(
title: const Text('获取UnionID'),
onTap: () async {
if (_loginResp != null &&
_loginResp.isSuccessful() &&
!_loginResp.isExpired()) {
TencentUnionidResp unionid = await _tencent.getUnionId(
accessToken: _loginResp.accessToken,
);
if (unionid.isSuccessful()) {
_showTips('UnionID',
'${unionid.clientId} - ${unionid.openid} - ${unionid.unionid}');
} else {
_showTips('UnionID',
'${unionid.error} - ${unionid.errorDescription}');
}
}
},
@ -141,21 +138,31 @@ class _HomeState extends State<Home> {
ListTile(
title: const Text('图片分享'),
onTap: () async {
AssetImage image = const AssetImage('images/icon/timg.gif');
AssetBundleImageKey key =
await image.obtainKey(createLocalImageConfiguration(context));
ByteData imageData = await key.bundle.load(key.name);
Directory saveDir = await PathProvider.getDocumentsDirectory();
File saveFile = File('${saveDir.path}${path.separator}timg.gif');
if (!saveFile.existsSync()) {
saveFile.createSync(recursive: true);
saveFile.writeAsBytesSync(imageData.buffer.asUint8List(),
flush: true);
OkHttpClient client = OkHttpClientBuilder().build();
Response resp = await client
.newCall(RequestBuilder()
.get()
.url(HttpUrl.parse(
'https://www.baidu.com/img/bd_logo1.png?where=super'))
.build())
.enqueue();
if (resp.isSuccessful()) {
Directory saveDir = Platform.isIOS
? await path_provider.getApplicationDocumentsDirectory()
: await path_provider.getExternalStorageDirectory();
File saveFile = File(path.join(saveDir.path, 'timg.gif'));
if (!saveFile.existsSync()) {
saveFile.createSync(recursive: true);
saveFile.writeAsBytesSync(
await resp.body().bytes(),
flush: true,
);
}
await _tencent.shareImage(
scene: TencentScene.SCENE_QQ,
imageUri: Uri.file(saveFile.path),
);
}
await _tencent.shareImage(
scene: TencentScene.SCENE_QQ,
imageUri: Uri.file(saveFile.path),
);
},
),
ListTile(

View File

@ -1,5 +1,5 @@
name: fake_tencent_example
description: Demonstrates how to use the fake_tencent plugin.
name: tencent_kit_example
description: Demonstrates how to use the tencent_kit plugin.
publish_to: 'none'
environment:
@ -13,19 +13,20 @@ dependencies:
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2
path: ^1.6.2
fake_path_provider: ^0.0.1
fake_tencent:
path: ../
path: ^1.6.4
path_provider: ^1.4.0
okhttp_kit: ^1.0.0
dev_dependencies:
flutter_test:
sdk: flutter
pedantic: '>=1.4.0 <3.0.0'
tencent_kit:
path: ../
# For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
@ -36,14 +37,15 @@ flutter:
uses-material-design: true
# To add assets to your application, add an assets section, like this:
assets:
- images/icon/timg.gif
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.io/assets-and-images/#resolution-aware.
# https://flutter.dev/assets-and-images/#resolution-aware.
# For details regarding adding assets from package dependencies, see
# https://flutter.io/assets-and-images/#from-packages
# https://flutter.dev/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
@ -63,4 +65,4 @@ flutter:
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.io/custom-fonts/#from-packages
# see https://flutter.dev/custom-fonts/#from-packages

View File

@ -5,4 +5,13 @@
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
void main() {}
import 'package:flutter_test/flutter_test.dart';
import 'package:tencent_kit_example/main.dart';
void main() {
testWidgets('smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(MyApp());
});
}

1
ios/.gitignore vendored
View File

@ -34,3 +34,4 @@ Icon?
.tags*
/Flutter/Generated.xcconfig
/Flutter/flutter_export_environment.sh

View File

@ -1,4 +0,0 @@
#import <Flutter/Flutter.h>
@interface FakeTencentPlugin : NSObject<FlutterPlugin>
@end

View File

@ -1,334 +0,0 @@
#import "FakeTencentPlugin.h"
#import <TencentOpenAPI/TencentOAuth.h>
#import <TencentOpenAPI/QQApiInterface.h>
@interface FakeTencentPlugin () <TencentSessionDelegate, QQApiInterfaceDelegate>
@end
enum FakeTencentScene {
SCENE_QQ = 0,
SCENE_QZONE = 1,
};
enum FakeTencentRetCode {
// 网络请求成功发送至服务器,并且服务器返回数据格式正确
// 这里包括所请求业务操作失败的情况,例如没有授权等原因导致
RET_SUCCESS = 0,
// 网络异常,或服务器返回的数据格式不正确导致无法解析
RET_FAILED = 1,
RET_COMMON = -1,
RET_USERCANCEL = -2,
};
@implementation FakeTencentPlugin {
FlutterMethodChannel * _channel;
TencentOAuth * _oauth;
}
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
FlutterMethodChannel* channel = [FlutterMethodChannel
methodChannelWithName:@"v7lin.github.io/fake_tencent"
binaryMessenger:[registrar messenger]];
FakeTencentPlugin* instance = [[FakeTencentPlugin alloc] initWithChannel:channel];
[registrar addApplicationDelegate:instance];
[registrar addMethodCallDelegate:instance channel:channel];
}
static NSString * const METHOD_REGISTERAPP = @"registerApp";
static NSString * const METHOD_ISQQINSTALLED = @"isQQInstalled";
static NSString * const METHOD_ISQQSUPPORTSSOLOGIN = @"isQQSupportSSOLogin";
static NSString * const METHOD_LOGIN = @"login";
static NSString * const METHOD_LOGOUT = @"logout";
static NSString * const METHOD_GETUSERINFO = @"getUserInfo";
static NSString * const METHOD_SHAREMOOD = @"shareMood";
static NSString * const METHOD_SHAREIMAGE = @"shareImage";
static NSString * const METHOD_SHAREMUSIC = @"shareMusic";
static NSString * const METHOD_SHAREWEBPAGE = @"shareWebpage";
static NSString * const METHOD_ONLOGINRESP = @"onLoginResp";
static NSString * const METHOD_ONGETUSERINFORESP = @"onGetUserInfoResp";
static NSString * const METHOD_ONSHARERESP = @"onShareResp";
static NSString * const ARGUMENT_KEY_APPID = @"appId";
static NSString * const ARGUMENT_KEY_SCOPE = @"scope";
static NSString * const ARGUMENT_KEY_OPENID = @"openId";
static NSString * const ARGUMENT_KEY_ACCESSTOKEN = @"accessToken";
static NSString * const ARGUMENT_KEY_EXPIRESIN = @"expiresIn";
static NSString * const ARGUMENT_KEY_CREATEAT = @"createAt";
static NSString * const ARGUMENT_KEY_SCENE = @"scene";
static NSString * const ARGUMENT_KEY_TITLE = @"title";
static NSString * const ARGUMENT_KEY_SUMMARY = @"summary";
static NSString * const ARGUMENT_KEY_IMAGEURI = @"imageUri";
static NSString * const ARGUMENT_KEY_IMAGEURIS = @"imageUris";
static NSString * const ARGUMENT_KEY_VIDEOURI = @"videoUri";
static NSString * const ARGUMENT_KEY_MUSICURL = @"musicUrl";
static NSString * const ARGUMENT_KEY_TARGETURL = @"targetUrl";
static NSString * const ARGUMENT_KEY_APPNAME = @"appName";
static NSString * const ARGUMENT_KEY_EXTINT = @"extInt";
static NSString * const ARGUMENT_KEY_RESULT_RET = @"ret";
static NSString * const ARGUMENT_KEY_RESULT_MSG = @"msg";
static NSString * const ARGUMENT_KEY_RESULT_OPENID = @"openid";
static NSString * const ARGUMENT_KEY_RESULT_ACCESS_TOKEN = @"access_token";
static NSString * const ARGUMENT_KEY_RESULT_EXPIRES_IN = @"expires_in";
static NSString * const ARGUMENT_KEY_RESULT_CREATE_AT = @"create_at";
static NSString * const SCHEME_FILE = @"file";
-(instancetype)initWithChannel:(FlutterMethodChannel *)channel {
self = [super init];
if (self) {
_channel = channel;
}
return self;
}
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
if ([METHOD_REGISTERAPP isEqualToString:call.method]) {
NSString * appId = call.arguments[ARGUMENT_KEY_APPID];
_oauth = [[TencentOAuth alloc] initWithAppId:appId andDelegate:self];
result(nil);
} else if ([METHOD_ISQQINSTALLED isEqualToString:call.method]) {
result([NSNumber numberWithBool:[TencentOAuth iphoneQQInstalled]]);
} else if ([METHOD_ISQQSUPPORTSSOLOGIN isEqualToString:call.method]) {
result([NSNumber numberWithBool:[TencentOAuth iphoneQQSupportSSOLogin]]);
} else if ([METHOD_LOGIN isEqualToString:call.method]) {
[self login:call result:result];
} else if ([METHOD_LOGOUT isEqualToString:call.method]) {
[self logout:call result:result];
} else if ([METHOD_GETUSERINFO isEqualToString:call.method]) {
[self getUserInfo:call result:result];
} else if ([METHOD_SHAREMOOD isEqualToString:call.method]) {
[self shareMood:call result:result];
} else if ([METHOD_SHAREIMAGE isEqualToString:call.method]) {
[self shareImage:call result:result];
} else if ([METHOD_SHAREMUSIC isEqualToString:call.method]) {
[self shareMusic:call result:result];
} else if ([METHOD_SHAREWEBPAGE isEqualToString:call.method]) {
[self shareWebpage:call result:result];
} else {
result(FlutterMethodNotImplemented);
}
}
-(void)login:(FlutterMethodCall*)call result:(FlutterResult)result {
NSString * scope = call.arguments[ARGUMENT_KEY_SCOPE];
NSArray * permissions = [scope componentsSeparatedByString:@","];
[_oauth authorize:permissions];
result(nil);
}
-(void)logout:(FlutterMethodCall*)call result:(FlutterResult)result {
[_oauth logout:self];
result(nil);
}
-(void)getUserInfo:(FlutterMethodCall*)call result:(FlutterResult)result {
NSString * openId = call.arguments[ARGUMENT_KEY_OPENID];
NSString * accessToken = call.arguments[ARGUMENT_KEY_ACCESSTOKEN];
NSNumber * expiresIn = call.arguments[ARGUMENT_KEY_EXPIRESIN];
NSNumber * createAt = call.arguments[ARGUMENT_KEY_CREATEAT];
NSTimeInterval secs = createAt.longLongValue / 1000.0 + expiresIn.longLongValue;
[_oauth setOpenId:openId];
[_oauth setAccessToken:accessToken];
[_oauth setExpirationDate:[NSDate dateWithTimeIntervalSince1970:secs]];
[_oauth getUserInfo];
result(nil);
}
-(void)shareMood:(FlutterMethodCall*)call result:(FlutterResult)result {
NSNumber * scene = call.arguments[ARGUMENT_KEY_SCENE];
if (scene.intValue == SCENE_QZONE) {
NSString * summary = call.arguments[ARGUMENT_KEY_SUMMARY];
NSArray * imageUris = call.arguments[ARGUMENT_KEY_IMAGEURIS];
NSString * videoUri = call.arguments[ARGUMENT_KEY_VIDEOURI];
if (videoUri == nil || videoUri.length == 0) {
NSMutableArray * imageDatas = [NSMutableArray array];
if (imageUris != nil && imageUris.count > 0) {
for (NSString * imageUri in imageUris) {
NSURL * imageUrl = [NSURL URLWithString:imageUri];
NSData * imageData = [NSData dataWithContentsOfFile:imageUrl.path];
[imageDatas addObject:imageData];
}
}
QQApiImageArrayForQZoneObject * object = [QQApiImageArrayForQZoneObject objectWithimageDataArray:imageDatas title:summary extMap:nil];
SendMessageToQQReq * req = [SendMessageToQQReq reqWithContent: object];
[QQApiInterface sendReq:req];
} else {
QQApiVideoForQZoneObject * object = [QQApiVideoForQZoneObject objectWithAssetURL:videoUri title:summary extMap:nil];
SendMessageToQQReq * req = [SendMessageToQQReq reqWithContent: object];
[QQApiInterface sendReq:req];
}
}
result(nil);
}
-(void)shareImage:(FlutterMethodCall*)call result:(FlutterResult)result {
NSNumber * scene = call.arguments[ARGUMENT_KEY_SCENE];
if (scene.intValue == SCENE_QQ) {
NSString * imageUri = call.arguments[ARGUMENT_KEY_IMAGEURI];
// NSString * appName = call.arguments[ARGUMENT_KEY_APPNAME];
// NSNumber * extInt = call.arguments[ARGUMENT_KEY_EXTINT];
NSURL * imageUrl = [NSURL URLWithString:imageUri];
NSData * imageData = [NSData dataWithContentsOfFile:imageUrl.path];
QQApiImageObject * object = [QQApiImageObject objectWithData:imageData previewImageData:nil title:nil description:nil];
SendMessageToQQReq * req = [SendMessageToQQReq reqWithContent: object];
[QQApiInterface sendReq:req];
}
result(nil);
}
-(void)shareMusic:(FlutterMethodCall*)call result:(FlutterResult)result {
NSNumber * scene = call.arguments[ARGUMENT_KEY_SCENE];
NSString * title = call.arguments[ARGUMENT_KEY_TITLE];
NSString * summary = call.arguments[ARGUMENT_KEY_SUMMARY];
NSString * imageUri = call.arguments[ARGUMENT_KEY_IMAGEURI];
NSString * musicUrl = call.arguments[ARGUMENT_KEY_MUSICURL];
NSString * targetUrl = call.arguments[ARGUMENT_KEY_TARGETURL];
// NSString * appName = call.arguments[ARGUMENT_KEY_APPNAME];
// NSNumber * extInt = call.arguments[ARGUMENT_KEY_EXTINT];
if (scene.intValue == SCENE_QQ) {
QQApiAudioObject * object = nil;
NSURL * imageUrl = [NSURL URLWithString:imageUri];
if ([SCHEME_FILE isEqualToString:imageUrl.scheme]) {
NSData * imageData = [NSData dataWithContentsOfFile:imageUrl.path];
object = [QQApiAudioObject objectWithURL:[NSURL URLWithString:targetUrl] title:title description:summary previewImageData:imageData];
} else {
object = [QQApiAudioObject objectWithURL:[NSURL URLWithString:targetUrl] title:title description:summary previewImageURL:imageUrl];
}
object.flashURL = [NSURL URLWithString:musicUrl];
SendMessageToQQReq * req = [SendMessageToQQReq reqWithContent: object];
[QQApiInterface sendReq:req];
}
result(nil);
}
-(void)shareWebpage:(FlutterMethodCall*)call result:(FlutterResult)result {
NSNumber * scene = call.arguments[ARGUMENT_KEY_SCENE];
NSString * title = call.arguments[ARGUMENT_KEY_TITLE];
NSString * summary = call.arguments[ARGUMENT_KEY_SUMMARY];
NSString * imageUri = call.arguments[ARGUMENT_KEY_IMAGEURI];
NSString * targetUrl = call.arguments[ARGUMENT_KEY_TARGETURL];
// NSString * appName = call.arguments[ARGUMENT_KEY_APPNAME];
// NSNumber * extInt = call.arguments[ARGUMENT_KEY_EXTINT];
QQApiNewsObject * object = nil;
NSURL * imageUrl = [NSURL URLWithString:imageUri];
if ([SCHEME_FILE isEqualToString:imageUrl.scheme]) {
NSData * imageData = [NSData dataWithContentsOfFile:imageUrl.path];
object = [QQApiNewsObject objectWithURL:[NSURL URLWithString:targetUrl] title:title description:summary previewImageData:imageData];
} else {
object = [QQApiNewsObject objectWithURL:[NSURL URLWithString:targetUrl] title:title description:summary previewImageURL:imageUrl];
}
SendMessageToQQReq * req = [SendMessageToQQReq reqWithContent: object];
if (scene.intValue == SCENE_QQ) {
[QQApiInterface sendReq:req];
} else if (scene.intValue == SCENE_QZONE) {
[QQApiInterface SendReqToQZone:req];
}
result(nil);
}
# pragma mark - AppDelegate
-(BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
return [QQApiInterface handleOpenURL:url delegate:self] || ([TencentOAuth CanHandleOpenURL:url] && [TencentOAuth HandleOpenURL:url]);
}
-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
return [QQApiInterface handleOpenURL:url delegate:self] || ([TencentOAuth CanHandleOpenURL:url] && [TencentOAuth HandleOpenURL:url]);
}
-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
return [QQApiInterface handleOpenURL:url delegate:self] || ([TencentOAuth CanHandleOpenURL:url] && [TencentOAuth HandleOpenURL:url]);
}
# pragma mark - TencentSessionDelegate
-(void)tencentDidLogin {
NSMutableDictionary * dictionary = [NSMutableDictionary dictionary];
if (_oauth.accessToken != nil && _oauth.accessToken.length > 0) {
NSString * openId = _oauth.openId;
NSString * accessToken = _oauth.accessToken;
long long expiresIn = ceil(_oauth.expirationDate.timeIntervalSinceNow);// 向上取整
long long createAt = [[NSDate date] timeIntervalSince1970] * 1000.0;
[dictionary setValue:[NSNumber numberWithInt:RET_SUCCESS] forKey:ARGUMENT_KEY_RESULT_RET];
[dictionary setValue:openId forKey:ARGUMENT_KEY_RESULT_OPENID];
[dictionary setValue:accessToken forKey:ARGUMENT_KEY_RESULT_ACCESS_TOKEN];
[dictionary setValue:[NSNumber numberWithLongLong:expiresIn] forKey:ARGUMENT_KEY_RESULT_EXPIRES_IN];
[dictionary setValue:[NSNumber numberWithLongLong:createAt] forKey:ARGUMENT_KEY_RESULT_CREATE_AT];
} else {
// 登录失败
[dictionary setValue:[NSNumber numberWithInt:RET_COMMON] forKey:ARGUMENT_KEY_RESULT_RET];
}
[_channel invokeMethod:METHOD_ONLOGINRESP arguments:dictionary];
}
-(void)tencentDidNotLogin:(BOOL)cancelled {
NSMutableDictionary * dictionary = [NSMutableDictionary dictionary];
if (cancelled) {
// 取消登录
[dictionary setValue:[NSNumber numberWithInt:RET_USERCANCEL] forKey:ARGUMENT_KEY_RESULT_RET];
} else {
// 登录失败
[dictionary setValue:[NSNumber numberWithInt:RET_COMMON] forKey:ARGUMENT_KEY_RESULT_RET];
}
[_channel invokeMethod:METHOD_ONLOGINRESP arguments:dictionary];
}
-(void)tencentDidNotNetWork {
// 登录失败
NSMutableDictionary * dictionary = [NSMutableDictionary dictionary];
[dictionary setValue:[NSNumber numberWithInt:RET_COMMON] forKey:ARGUMENT_KEY_RESULT_RET];
[_channel invokeMethod:METHOD_ONLOGINRESP arguments:dictionary];
}
-(void)getUserInfoResponse:(APIResponse *)response {
NSMutableDictionary * dictionary = [NSMutableDictionary dictionary];
if (response.retCode == URLREQUEST_SUCCEED) {
[dictionary setValue:[NSNumber numberWithInt:RET_SUCCESS] forKey:ARGUMENT_KEY_RESULT_RET];
NSDictionary * json = response.jsonResponse;
[dictionary addEntriesFromDictionary:json];
} else {
[dictionary setValue:[NSNumber numberWithInt:RET_COMMON] forKey:ARGUMENT_KEY_RESULT_RET];
[dictionary setValue:response.errorMsg forKey:ARGUMENT_KEY_RESULT_MSG];
}
[_channel invokeMethod:METHOD_ONGETUSERINFORESP arguments:dictionary];
}
# pragma mark - QQApiInterfaceDelegate
-(void)onReq:(QQBaseReq *)req {
}
- (void)onResp:(QQBaseResp *)resp {
NSMutableDictionary * dictionary = [NSMutableDictionary dictionary];
if ([resp isKindOfClass:[SendMessageToQQResp class]]) {
switch (resp.result.intValue) {
case 0:
// 分享成功
[dictionary setValue:[NSNumber numberWithInt:RET_SUCCESS] forKey:ARGUMENT_KEY_RESULT_RET];
break;
case -4:
// 用户取消
[dictionary setValue:[NSNumber numberWithInt:RET_USERCANCEL] forKey:ARGUMENT_KEY_RESULT_RET];
break;
default:
[dictionary setValue:[NSNumber numberWithInt:RET_COMMON] forKey:ARGUMENT_KEY_RESULT_RET];
NSString * errorMsg = [NSString stringWithFormat:@"result: %@, description: %@.", resp.result, resp.errorDescription];
[dictionary setValue:errorMsg forKey:ARGUMENT_KEY_RESULT_MSG];
break;
}
[_channel invokeMethod:METHOD_ONSHARERESP arguments:dictionary];
}
}
-(void)isOnlineResponse:(NSDictionary *)response {
}
@end

View File

@ -0,0 +1,4 @@
#import <Flutter/Flutter.h>
@interface TencentKitPlugin : NSObject<FlutterPlugin>
@end

View File

@ -0,0 +1,313 @@
#import "TencentKitPlugin.h"
#import <TencentOpenAPI/TencentOAuth.h>
#import <TencentOpenAPI/QQApiInterface.h>
enum TencentScene {
SCENE_QQ = 0,
SCENE_QZONE = 1,
};
enum TencentRetCode {
// 网络请求成功发送至服务器,并且服务器返回数据格式正确
// 这里包括所请求业务操作失败的情况,例如没有授权等原因导致
RET_SUCCESS = 0,
// 网络异常,或服务器返回的数据格式不正确导致无法解析
RET_FAILED = 1,
RET_COMMON = -1,
RET_USERCANCEL = -2,
};
@interface TencentKitPlugin () <TencentSessionDelegate, QQApiInterfaceDelegate>
@end
@implementation TencentKitPlugin {
FlutterMethodChannel* _channel;
TencentOAuth* _oauth;
}
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
FlutterMethodChannel* channel = [FlutterMethodChannel
methodChannelWithName:@"v7lin.github.io/tencent_kit"
binaryMessenger:[registrar messenger]];
TencentKitPlugin* instance = [[TencentKitPlugin alloc] initWithChannel:channel];
[registrar addApplicationDelegate:instance];
[registrar addMethodCallDelegate:instance channel:channel];
}
static NSString* const METHOD_REGISTERAPP = @"registerApp";
static NSString* const METHOD_ISINSTALLED = @"isInstalled";
static NSString* const METHOD_LOGIN = @"login";
static NSString* const METHOD_LOGOUT = @"logout";
static NSString* const METHOD_SHAREMOOD = @"shareMood";
static NSString* const METHOD_SHAREIMAGE = @"shareImage";
static NSString* const METHOD_SHAREMUSIC = @"shareMusic";
static NSString* const METHOD_SHAREWEBPAGE = @"shareWebpage";
static NSString* const METHOD_ONLOGINRESP = @"onLoginResp";
static NSString* const METHOD_ONSHARERESP = @"onShareResp";
static NSString* const ARGUMENT_KEY_APPID = @"appId";
static NSString* const ARGUMENT_KEY_UNIVERSALLINK = @"universalLink";
static NSString* const ARGUMENT_KEY_SCOPE = @"scope";
static NSString* const ARGUMENT_KEY_SCENE = @"scene";
static NSString* const ARGUMENT_KEY_TITLE = @"title";
static NSString* const ARGUMENT_KEY_SUMMARY = @"summary";
static NSString* const ARGUMENT_KEY_IMAGEURI = @"imageUri";
static NSString* const ARGUMENT_KEY_IMAGEURIS = @"imageUris";
static NSString* const ARGUMENT_KEY_VIDEOURI = @"videoUri";
static NSString* const ARGUMENT_KEY_MUSICURL = @"musicUrl";
static NSString* const ARGUMENT_KEY_TARGETURL = @"targetUrl";
static NSString* const ARGUMENT_KEY_APPNAME = @"appName";
static NSString* const ARGUMENT_KEY_EXTINT = @"extInt";
static NSString* const ARGUMENT_KEY_RESULT_RET = @"ret";
static NSString* const ARGUMENT_KEY_RESULT_MSG = @"msg";
static NSString* const ARGUMENT_KEY_RESULT_OPENID = @"openid";
static NSString* const ARGUMENT_KEY_RESULT_ACCESS_TOKEN = @"access_token";
static NSString* const ARGUMENT_KEY_RESULT_EXPIRES_IN = @"expires_in";
static NSString* const ARGUMENT_KEY_RESULT_CREATE_AT = @"create_at";
static NSString* const SCHEME_FILE = @"file";
-(instancetype)initWithChannel:(FlutterMethodChannel *)channel {
self = [super init];
if (self) {
_channel = channel;
}
return self;
}
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
if ([METHOD_REGISTERAPP isEqualToString:call.method]) {
NSString* appId = call.arguments[ARGUMENT_KEY_APPID];
NSString* universalLink = call.arguments[ARGUMENT_KEY_UNIVERSALLINK];
if (universalLink != nil) {
_oauth = [[TencentOAuth alloc] initWithAppId:appId andUniversalLink:universalLink andDelegate:self];
} else {
_oauth = [[TencentOAuth alloc] initWithAppId:appId andDelegate:self];
}
result(nil);
} else if ([METHOD_ISINSTALLED isEqualToString:call.method]) {
result([NSNumber numberWithBool:[TencentOAuth iphoneQQInstalled]]);
} else if ([METHOD_LOGIN isEqualToString:call.method]) {
[self login:call result:result];
} else if ([METHOD_LOGOUT isEqualToString:call.method]) {
[self logout:call result:result];
} else if ([METHOD_SHAREMOOD isEqualToString:call.method]) {
[self shareMood:call result:result];
} else if ([METHOD_SHAREIMAGE isEqualToString:call.method]) {
[self shareImage:call result:result];
} else if ([METHOD_SHAREMUSIC isEqualToString:call.method]) {
[self shareMusic:call result:result];
} else if ([METHOD_SHAREWEBPAGE isEqualToString:call.method]) {
[self shareWebpage:call result:result];
} else {
result(FlutterMethodNotImplemented);
}
}
-(void)login:(FlutterMethodCall*)call result:(FlutterResult)result {
NSString* scope = call.arguments[ARGUMENT_KEY_SCOPE];
NSArray* permissions = [scope componentsSeparatedByString:@","];
[_oauth authorize:permissions];
result(nil);
}
-(void)logout:(FlutterMethodCall*)call result:(FlutterResult)result {
[_oauth logout:self];
result(nil);
}
-(void)shareMood:(FlutterMethodCall*)call result:(FlutterResult)result {
NSNumber* scene = call.arguments[ARGUMENT_KEY_SCENE];
if (scene.intValue == SCENE_QZONE) {
NSString* summary = call.arguments[ARGUMENT_KEY_SUMMARY];
NSArray* imageUris = call.arguments[ARGUMENT_KEY_IMAGEURIS];
NSString* videoUri = call.arguments[ARGUMENT_KEY_VIDEOURI];
if (videoUri == nil || videoUri.length == 0) {
NSMutableArray* imageDatas = [NSMutableArray array];
if (imageUris != nil && imageUris.count > 0) {
for (NSString* imageUri in imageUris) {
NSURL* imageUrl = [NSURL URLWithString:imageUri];
NSData* imageData = [NSData dataWithContentsOfFile:imageUrl.path];
[imageDatas addObject:imageData];
}
}
QQApiImageArrayForQZoneObject* object = [QQApiImageArrayForQZoneObject objectWithimageDataArray:imageDatas title:summary extMap:nil];
SendMessageToQQReq* req = [SendMessageToQQReq reqWithContent: object];
[QQApiInterface sendReq:req];
} else {
QQApiVideoForQZoneObject* object = [QQApiVideoForQZoneObject objectWithAssetURL:videoUri title:summary extMap:nil];
SendMessageToQQReq* req = [SendMessageToQQReq reqWithContent: object];
[QQApiInterface sendReq:req];
}
}
result(nil);
}
-(void)shareImage:(FlutterMethodCall*)call result:(FlutterResult)result {
NSNumber* scene = call.arguments[ARGUMENT_KEY_SCENE];
if (scene.intValue == SCENE_QQ) {
NSString* imageUri = call.arguments[ARGUMENT_KEY_IMAGEURI];
// NSString* appName = call.arguments[ARGUMENT_KEY_APPNAME];
// NSNumber* extInt = call.arguments[ARGUMENT_KEY_EXTINT];
NSURL* imageUrl = [NSURL URLWithString:imageUri];
NSData* imageData = [NSData dataWithContentsOfFile:imageUrl.path];
QQApiImageObject* object = [QQApiImageObject objectWithData:imageData previewImageData:nil title:nil description:nil];
SendMessageToQQReq* req = [SendMessageToQQReq reqWithContent: object];
[QQApiInterface sendReq:req];
}
result(nil);
}
-(void)shareMusic:(FlutterMethodCall*)call result:(FlutterResult)result {
NSNumber* scene = call.arguments[ARGUMENT_KEY_SCENE];
NSString* title = call.arguments[ARGUMENT_KEY_TITLE];
NSString* summary = call.arguments[ARGUMENT_KEY_SUMMARY];
NSString* imageUri = call.arguments[ARGUMENT_KEY_IMAGEURI];
NSString* musicUrl = call.arguments[ARGUMENT_KEY_MUSICURL];
NSString* targetUrl = call.arguments[ARGUMENT_KEY_TARGETURL];
// NSString* appName = call.arguments[ARGUMENT_KEY_APPNAME];
// NSNumber* extInt = call.arguments[ARGUMENT_KEY_EXTINT];
if (scene.intValue == SCENE_QQ) {
QQApiAudioObject* object = nil;
NSURL* imageUrl = [NSURL URLWithString:imageUri];
if ([SCHEME_FILE isEqualToString:imageUrl.scheme]) {
NSData* imageData = [NSData dataWithContentsOfFile:imageUrl.path];
object = [QQApiAudioObject objectWithURL:[NSURL URLWithString:targetUrl] title:title description:summary previewImageData:imageData];
} else {
object = [QQApiAudioObject objectWithURL:[NSURL URLWithString:targetUrl] title:title description:summary previewImageURL:imageUrl];
}
object.flashURL = [NSURL URLWithString:musicUrl];
SendMessageToQQReq* req = [SendMessageToQQReq reqWithContent: object];
[QQApiInterface sendReq:req];
}
result(nil);
}
-(void)shareWebpage:(FlutterMethodCall*)call result:(FlutterResult)result {
NSNumber* scene = call.arguments[ARGUMENT_KEY_SCENE];
NSString* title = call.arguments[ARGUMENT_KEY_TITLE];
NSString* summary = call.arguments[ARGUMENT_KEY_SUMMARY];
NSString* imageUri = call.arguments[ARGUMENT_KEY_IMAGEURI];
NSString* targetUrl = call.arguments[ARGUMENT_KEY_TARGETURL];
// NSString* appName = call.arguments[ARGUMENT_KEY_APPNAME];
// NSNumber* extInt = call.arguments[ARGUMENT_KEY_EXTINT];
QQApiNewsObject* object = nil;
NSURL* imageUrl = [NSURL URLWithString:imageUri];
if ([SCHEME_FILE isEqualToString:imageUrl.scheme]) {
NSData* imageData = [NSData dataWithContentsOfFile:imageUrl.path];
object = [QQApiNewsObject objectWithURL:[NSURL URLWithString:targetUrl] title:title description:summary previewImageData:imageData];
} else {
object = [QQApiNewsObject objectWithURL:[NSURL URLWithString:targetUrl] title:title description:summary previewImageURL:imageUrl];
}
SendMessageToQQReq* req = [SendMessageToQQReq reqWithContent: object];
if (scene.intValue == SCENE_QQ) {
[QQApiInterface sendReq:req];
} else if (scene.intValue == SCENE_QZONE) {
[QQApiInterface SendReqToQZone:req];
}
result(nil);
}
# pragma mark - AppDelegate
-(BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
return [QQApiInterface handleOpenURL:url delegate:self] || ([TencentOAuth CanHandleOpenURL:url] && [TencentOAuth HandleOpenURL:url]);
}
-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
return [QQApiInterface handleOpenURL:url delegate:self] || ([TencentOAuth CanHandleOpenURL:url] && [TencentOAuth HandleOpenURL:url]);
}
-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
return [QQApiInterface handleOpenURL:url delegate:self] || ([TencentOAuth CanHandleOpenURL:url] && [TencentOAuth HandleOpenURL:url]);
}
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nonnull))restorationHandler {
if([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
NSURL* url = userActivity.webpageURL;
if (url != nil) {
return [QQApiInterface handleOpenUniversallink:url delegate:self] || ([TencentOAuth CanHandleUniversalLink:url] && [TencentOAuth HandleUniversalLink:url]);
}
}
return NO;
}
# pragma mark - TencentSessionDelegate
-(void)tencentDidLogin {
NSMutableDictionary* dictionary = [NSMutableDictionary dictionary];
if (_oauth.accessToken != nil && _oauth.accessToken.length > 0) {
NSString* openId = _oauth.openId;
NSString* accessToken = _oauth.accessToken;
long long expiresIn = ceil(_oauth.expirationDate.timeIntervalSinceNow);// 向上取整
long long createAt = [[NSDate date] timeIntervalSince1970]* 1000.0;
[dictionary setValue:[NSNumber numberWithInt:RET_SUCCESS] forKey:ARGUMENT_KEY_RESULT_RET];
[dictionary setValue:openId forKey:ARGUMENT_KEY_RESULT_OPENID];
[dictionary setValue:accessToken forKey:ARGUMENT_KEY_RESULT_ACCESS_TOKEN];
[dictionary setValue:[NSNumber numberWithLongLong:expiresIn] forKey:ARGUMENT_KEY_RESULT_EXPIRES_IN];
[dictionary setValue:[NSNumber numberWithLongLong:createAt] forKey:ARGUMENT_KEY_RESULT_CREATE_AT];
} else {
// 登录失败
[dictionary setValue:[NSNumber numberWithInt:RET_COMMON] forKey:ARGUMENT_KEY_RESULT_RET];
}
[_channel invokeMethod:METHOD_ONLOGINRESP arguments:dictionary];
}
-(void)tencentDidNotLogin:(BOOL)cancelled {
NSMutableDictionary* dictionary = [NSMutableDictionary dictionary];
if (cancelled) {
// 取消登录
[dictionary setValue:[NSNumber numberWithInt:RET_USERCANCEL] forKey:ARGUMENT_KEY_RESULT_RET];
} else {
// 登录失败
[dictionary setValue:[NSNumber numberWithInt:RET_COMMON] forKey:ARGUMENT_KEY_RESULT_RET];
}
[_channel invokeMethod:METHOD_ONLOGINRESP arguments:dictionary];
}
-(void)tencentDidNotNetWork {
// 登录失败
NSMutableDictionary* dictionary = [NSMutableDictionary dictionary];
[dictionary setValue:[NSNumber numberWithInt:RET_COMMON] forKey:ARGUMENT_KEY_RESULT_RET];
[_channel invokeMethod:METHOD_ONLOGINRESP arguments:dictionary];
}
# pragma mark - QQApiInterfaceDelegate
-(void)onReq:(QQBaseReq *)req {
}
- (void)onResp:(QQBaseResp *)resp {
NSMutableDictionary* dictionary = [NSMutableDictionary dictionary];
if ([resp isKindOfClass:[SendMessageToQQResp class]]) {
switch (resp.result.intValue) {
case 0:
// 分享成功
[dictionary setValue:[NSNumber numberWithInt:RET_SUCCESS] forKey:ARGUMENT_KEY_RESULT_RET];
break;
case -4:
// 用户取消
[dictionary setValue:[NSNumber numberWithInt:RET_USERCANCEL] forKey:ARGUMENT_KEY_RESULT_RET];
break;
default:
[dictionary setValue:[NSNumber numberWithInt:RET_COMMON] forKey:ARGUMENT_KEY_RESULT_RET];
NSString* errorMsg = [NSString stringWithFormat:@"result: %@, description: %@.", resp.result, resp.errorDescription];
[dictionary setValue:errorMsg forKey:ARGUMENT_KEY_RESULT_MSG];
break;
}
[_channel invokeMethod:METHOD_ONSHARERESP arguments:dictionary];
}
}
-(void)isOnlineResponse:(NSDictionary *)response {
}
@end

View File

@ -0,0 +1,158 @@
///
/// \file QQApiInterface.h
/// \brief QQApi接口简化封装
///
/// Created by Tencent on 12-5-15.
/// Copyright (c) 2012年 Tencent. All rights reserved.
///
#import <Foundation/Foundation.h>
#import "QQApiInterfaceObject.h"
typedef void (^sendResultBlock)(NSDictionary *result);
/**
\brief 处理来至QQ的请求及响应的回调协议
*/
@protocol QQApiInterfaceDelegate <NSObject>
/**
处理来至QQ的请求
*/
- (void)onReq:(QQBaseReq *)req;
/**
处理来至QQ的响应
*/
- (void)onResp:(QQBaseResp *)resp;
/**
处理QQ在线状态的回调
*/
- (void)isOnlineResponse:(NSDictionary *)response;
@end
/**
\brief 对QQApi的简单封装类
*/
@interface QQApiInterface : NSObject
/**
处理由手Q唤起的普通跳转请求
\param url 待处理的url跳转请求
\param delegate 第三方应用用于处理来至QQ请求及响应的委托对象
\return 跳转请求处理结果YES表示成功处理NO表示不支持的请求协议或处理失败
*/
+ (BOOL)handleOpenURL:(NSURL *)url delegate:(id<QQApiInterfaceDelegate>)delegate;
/**
处理由手Q唤起的universallink跳转请求
\param universallink 待处理的universallink跳转请求
\param delegate 第三方应用用于处理来至QQ请求及响应的委托对象
\return 跳转请求处理结果YES表示成功处理NO表示不支持的请求协议或处理失败
*/
+ (BOOL)handleOpenUniversallink:(NSURL*)universallink delegate:(id<QQApiInterfaceDelegate>)delegate;
/**
向手Q发起分享请求
\param req 分享内容的请求
\return 请求发送结果码
*/
+ (QQApiSendResultCode)sendReq:(QQBaseReq *)req;
/**
向手Q QZone结合版发起分享请求
\note H5分享只支持单张网络图片的传递
\param req 分享内容的请求
\return 请求发送结果码
*/
+ (QQApiSendResultCode)SendReqToQZone:(QQBaseReq *)req;
/**
向手Q发起设置QQ头像
\param req 分享内容的请求
\return 请求发送结果码
*/
+ (QQApiSendResultCode)sendMessageToQQAvatarWithReq:(QQBaseReq*)req;
/**
向手Q发起组图分享到表情收藏
\param req 分享内容的请求
\return 请求发送结果码
*/
+ (QQApiSendResultCode)sendMessageToFaceCollectionWithReq:(QQBaseReq*)req;
/**
检测是否已安装QQ
\return 如果QQ已安装则返回YES否则返回NO
\note SDK目前已经支持QQ、TIM授权登录及分享功能 会按照QQ>TIM的顺序进行调用。
只要用户安装了QQ、TIM中任意一个应用都可为第三方应用进行授权登录、分享功能。
第三方应用在接入SDK时不需要判断是否安装QQ、TIM。若有判断安装QQ、TIM的逻辑建议移除。
*/
+ (BOOL)isQQInstalled;
/**
检测是否已安装TIM
\return 如果TIM已安装则返回YES否则返回NO
\note SDK目前已经支持QQ、TIM授权登录及分享功能 会按照QQ>TIM的顺序进行调用。
只要用户安装了QQ、TIM中任意一个应用都可为第三方应用进行授权登录、分享功能。
第三方应用在接入SDK时不需要判断是否安装QQ、TIM。若有判断安装QQ、TIM的逻辑建议移除。
*/
+ (BOOL)isTIMInstalled;
/**
检测QQ是否支持API调用
\return 如果当前安装QQ版本支持API调用则返回YES否则返回NO
*/
+ (BOOL)isQQSupportApi;
/**
检测TIM是否支持API调用
\return 如果当前安装TIM版本支持API调用则返回YES否则返回NO
*/
+ (BOOL)isTIMSupportApi __attribute__((deprecated("已过期, 建议删除调用调用地方用YES替代。")));
/**
检测是否支持分享
\return 如果当前已安装QQ且QQ版本支持API调用 或者 当前已安装TIM且TIM版本支持API调用则返回YES否则返回NO
*/
+ (BOOL)isSupportShareToQQ;
/**
检测是否支持分享到QQ结合版QZone
\return 如果当前已安装QQ且QQ版本支持API调用则返回YES否则返回NO
*/
+ (BOOL)isSupportPushToQZone;
/**
启动QQ
\return 成功返回YES否则返回NO
*/
+ (BOOL)openQQ;
/**
启动TIM
\return 成功返回YES否则返回NO
*/
+ (BOOL)openTIM;
/**
获取QQ下载地址
如果App通过<code>QQApiInterface#isQQInstalled</code>和<code>QQApiInterface#isQQSupportApi</code>检测发现QQ没安装或当前版本QQ不支持API调用可引导用户通过打开此链接下载最新版QQ。
\return iPhoneQQ下载地址
*/
+ (NSString *)getQQInstallUrl;
/**
获取TIM下载地址
如果App通过<code>QQApiInterface#isTIMInstalled</code>检测发现TIM没安装或当前版本TIM不支持API调用可引导用户通过打开此链接下载最新版TIM。
\return iPhoneTIM下载地址
*/
+ (NSString *)getTIMInstallUrl;
@end

View File

@ -0,0 +1,645 @@
///
/// \file QQApiInterfaceObject.h
/// \brief QQApiInterface所依赖的请求及应答消息对象封装帮助类
///
/// Created by Tencent on 12-5-15.
/// Copyright (c) 2012年 Tencent. All rights reserved.
///
#ifndef QQApiInterface_QQAPIOBJECT_h
#define QQApiInterface_QQAPIOBJECT_h
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger,QQApiSendResultCode) {
EQQAPISENDSUCESS = 0,
EQQAPIQQNOTINSTALLED = 1, //QQ未安装
EQQAPIQQNOTSUPPORTAPI = 2, // QQ api不支持
EQQAPIMESSAGETYPEINVALID = 3,
EQQAPIMESSAGECONTENTNULL = 4,
EQQAPIMESSAGECONTENTINVALID = 5,
EQQAPIAPPNOTREGISTED = 6,
EQQAPIAPPSHAREASYNC = 7,
EQQAPIQQNOTSUPPORTAPI_WITH_ERRORSHOW = 8, //QQ api不支持 && SDK显示error提示
EQQAPIMESSAGEARKCONTENTNULL = 9, //ark内容为空
EQQAPIMESSAGE_MINI_CONTENTNULL = 10, //
EQQAPISENDFAILD = -1, //
EQQAPISHAREDESTUNKNOWN = -2, //QQ或TIM
EQQAPITIMSENDFAILD = -3, //
EQQAPITIMNOTINSTALLED = 11, //TIM未安装
EQQAPITIMNOTSUPPORTAPI = 12, // TIM api不支持
EQQAPI_INCOMING_PARAM_ERROR = 13, //
EQQAPI_THIRD_APP_GROUP_ERROR_APP_NOT_AUTHORIZIED = 14, // APP未获得授权
EQQAPI_THIRD_APP_GROUP_ERROR_CGI_FAILED = 15, // CGI请求失败
EQQAPI_THIRD_APP_GROUP_ERROR_HAS_BINDED = 16, //
EQQAPI_THIRD_APP_GROUP_ERROR_NOT_BINDED = 17, //
EQQAPIQZONENOTSUPPORTTEXT = 10000, //qzone分享不支持text类型分享
EQQAPIQZONENOTSUPPORTIMAGE = 10001, //qzone分享不支持image类型分享
EQQAPIVERSIONNEEDUPDATE = 10002, //QQ版本太低
ETIMAPIVERSIONNEEDUPDATE = 10004, //TIM版本太低
};
#pragma mark - QQApiObject()
// QQApiObject control flags
typedef NS_ENUM(NSUInteger,kQQAPICtrlFlag) {
kQQAPICtrlFlagQZoneShareOnStart = 0x01,
kQQAPICtrlFlagQZoneShareForbid = 0x02,
kQQAPICtrlFlagQQShare = 0x04,
kQQAPICtrlFlagQQShareFavorites = 0x08, //收藏
kQQAPICtrlFlagQQShareDataline = 0x10, //数据线
kQQAPICtrlFlagQQShareEnableArk = 0x20, //支持ARK
kQQAPICtrlFlagQQShareEnableMiniProgram = 0x40, //支持小程序
};
// 分享到QQ或TIM
typedef NS_ENUM(NSUInteger, ShareDestType) {
ShareDestTypeQQ = 0,
ShareDestTypeTIM,
};
//小程序的类型
typedef NS_ENUM(NSUInteger, MiniProgramType) {
MiniProgramType_Develop=0, // 开发版
MiniProgramType_Test=1, // 测试版
MiniProgramType_Online=3, // 正式版,默认
MiniProgramType_Preview=4, // 预览版
};
// QQApiObject
/** \brief 所有在QQ及插件间发送的数据对象的根类。
*/
__attribute__((visibility("default"))) @interface QQApiObject : NSObject
@property(nonatomic, retain) NSString* title; ///< 标题最长128个字符
@property(nonatomic, retain) NSString* description; ///<简要描述最长512个字符
@property(nonatomic, retain) NSString* universalLink; ///(>=3.3.7)支持第三方传入在互联开放平台注册的universallink
@property(nonatomic, assign) uint64_t cflag;
/*
* 分享到QQ/TIM
* SDK根据是否安装对应客户端进行判断判断顺序QQ > TIM
* 默认分享到QQ如果QQ未安装检测TIM是否安装
*/
@property (nonatomic, assign) ShareDestType shareDestType;
@end
// ArkObject
/** \brief 支持Ark的根类。
*/
__attribute__((visibility("default"))) @interface ArkObject : NSObject
@property(nonatomic,retain) NSString* arkData; ///< 显示Ark所需的数据json串长度暂不限制
@property(nonatomic,assign) QQApiObject* qqApiObject; ///<原有老版本的QQApiObject
- (id)initWithData:(NSString *)arkData qqApiObject:(QQApiObject*)qqApiObject;
+ (id)objectWithData:(NSString *)arkData qqApiObject:(QQApiObject*)qqApiObject;
@end
#pragma mark QQ小程序
//分享小程序消息 - QQ 8.0.8
__attribute__((visibility("default"))) @interface QQApiMiniProgramObject : NSObject
@property(nonatomic,retain) QQApiObject* qqApiObject; //原有老版本的QQApiObject
@property(nonatomic,retain) NSString* miniAppID; //必填小程序的AppId必须在QQ互联平台中将该小程序与分享的App绑定
@property(nonatomic,retain) NSString* miniPath; //必填,小程序的展示路径
@property(nonatomic,retain) NSString* webpageUrl; //必填,兼容低版本的网页链接
@property(nonatomic,assign) MiniProgramType miniprogramType; //非必填,小程序的类型,默认正式版(3),可选测试版(1)、预览版(4)
@end
//唤起小程序 - QQ 8.1.8
__attribute__((visibility("default"))) @interface QQApiLaunchMiniProgramObject : QQApiObject
@property(nonatomic,retain) NSString* miniAppID; //必填小程序的AppId必须在QQ互联平台中将该小程序与分享的App绑定
@property(nonatomic,retain) NSString* miniPath; //必填,小程序的展示路径
@property(nonatomic,assign) MiniProgramType miniprogramType; //非必填,小程序的类型,默认正式版(3),可选测试版(1)、开发版(0)
@end
// QQApiResultObject
/** \brief 用于请求回应的数据类型。
<h3>可能错误码及描述如下:</h3>
<TABLE>
<TR><TD>error</TD><TD>errorDescription</TD><TD>注释</TD></TR>
<TR><TD>0</TD><TD>nil</TD><TD>成功</TD></TR>
<TR><TD>-1</TD><TD>param error</TD><TD>参数错误</TD></TR>
<TR><TD>-2</TD><TD>group code is invalid</TD><TD>该群不在自己的群列表里面</TD></TR>
<TR><TD>-3</TD><TD>upload photo failed</TD><TD>上传图片失败</TD></TR>
<TR><TD>-4</TD><TD>user give up the current operation</TD><TD>用户放弃当前操作</TD></TR>
<TR><TD>-5</TD><TD>client internal error</TD><TD>客户端内部处理错误</TD></TR>
</TABLE>
*/
__attribute__((visibility("default"))) @interface QQApiResultObject : QQApiObject
@property(nonatomic,retain) NSString* error; ///<错误
@property(nonatomic,retain) NSString* errorDescription; ///<错误描述
@property(nonatomic,retain) NSString* extendInfo; ///<扩展信息
@end
// QQApiTextObject
/** \brief 文本对象
*/
@interface QQApiTextObject : QQApiObject
@property(nonatomic,retain)NSString* text; ///<文本内容必填最长1536个字符
-(id)initWithText:(NSString*)text; ///<初始化方法
+(id)objectWithText:(NSString*)text;///<工厂方法获取一个QQApiTextObject对象.
@end
// QQApiURLObject
typedef NS_ENUM(NSUInteger, QQApiURLTargetType) {
QQApiURLTargetTypeNotSpecified = 0x00,
QQApiURLTargetTypeAudio = 0x01,
QQApiURLTargetTypeVideo = 0x02,
QQApiURLTargetTypeNews = 0x03
};
/** @brief URL对象类型。
包括URL地址URL地址所指向的目标类型及预览图像。
*/
__attribute__((visibility("default"))) @interface QQApiURLObject : QQApiObject
/**
URL地址所指向的目标类型.
@note 参见QQApi.h 中的 QQApiURLTargetType 定义.
*/
@property(nonatomic)QQApiURLTargetType targetContentType;
@property(nonatomic,retain)NSURL* url; ///<URL地址,必填最长512个字符
@property(nonatomic,retain)NSData* previewImageData;///<预览图像数据最大1M字节
@property(nonatomic, retain) NSURL *previewImageURL; ///<预览图像URL **预览图像数据与预览图像URL可二选一
/**
初始化方法
*/
-(id)initWithURL:(NSURL*)url title:(NSString*)title description:(NSString*)description previewImageData:(NSData*)data targetContentType:(QQApiURLTargetType)targetContentType;
-(id)initWithURL:(NSURL*)url title:(NSString*)title description:(NSString*)description previewImageURL:(NSURL*)previewURL targetContentType:(QQApiURLTargetType)targetContentType;
/**
工厂方法,获取一个QQApiURLObject对象
*/
+(id)objectWithURL:(NSURL*)url title:(NSString*)title description:(NSString*)description previewImageData:(NSData*)data targetContentType:(QQApiURLTargetType)targetContentType;
+(id)objectWithURL:(NSURL*)url title:(NSString*)title description:(NSString*)description previewImageURL:(NSURL*)previewURL targetContentType:(QQApiURLTargetType)targetContentType;
@end
// QQApiExtendObject
/** @brief 扩展数据类型
*/
@interface QQApiExtendObject : QQApiObject
@property(nonatomic,retain) NSData* data;///<具体数据内容必填最大5M字节
@property(nonatomic,retain) NSData* previewImageData;///<预览图像最大1M字节
@property(nonatomic,retain) NSArray* imageDataArray;///图片数组(多图暂只支持分享到手机QQ收藏功能)
/**
初始化方法
@param data 数据内容
@param previewImageData 用于预览的图片
@param title 标题
@param description 此对象,分享的描述
*/
- (id)initWithData:(NSData*)data previewImageData:(NSData*)previewImageData title:(NSString*)title description:(NSString*)description;
/**
初始化方法
@param data 数据内容
@param title 标题
@param description 此对象,分享的描述
@param imageDataArray 发送的多张图片队列
*/
- (id)initWithData:(NSData *)data previewImageData:(NSData*)previewImageData title:(NSString *)title description:(NSString *)description imageDataArray:(NSArray *)imageDataArray;
/**
helper方法获取一个autorelease的<code>QQApiExtendObject</code>对象
@param data 数据内容
@param previewImageData 用于预览的图片
@param title 标题
@param description 此对象,分享的描述
@return
一个自动释放的<code>QQApiExtendObject</code>实例
*/
+ (id)objectWithData:(NSData*)data previewImageData:(NSData*)previewImageData title:(NSString*)title description:(NSString*)description;
/**
helper方法获取一个autorelease的<code>QQApiExtendObject</code>对象
@param data 数据内容
@param previewImageData 用于预览的图片
@param title 标题
@param description 此对象,分享的描述
@param imageDataArray 发送的多张图片队列
@return
一个自动释放的<code>QQApiExtendObject</code>实例
*/
+ (id)objectWithData:(NSData*)data previewImageData:(NSData*)previewImageData title:(NSString*)title description:(NSString*)description imageDataArray:(NSArray*)imageDataArray;
@end
// QQApiImageObject
/** @brief 图片对象
用于分享图片内容的对象,是一个指定为图片类型的<code>QQApiExtendObject</code>
*/
@interface QQApiImageObject : QQApiExtendObject
@end
// QQApiImageForQQAvatarObject
/** @brief 图片对象
用于设置QQ头像内容的对象是一个指定为图片类型的<code>QQApiExtendObject</code>
*/
@interface QQApiImageForQQAvatarObject : QQApiExtendObject
@end
/**
* @brief 视频对象
* 用于设置动态头像
* assetURL可传ALAsset的ALAssetPropertyAssetURL或者PHAsset的localIdentifier
从手Q返回的错误码
//第三方设置动态头像结果
@"ret=0"//设置成功
@"ret=-10&error_des=user cancel"//用户取消设置
@"ret=-11&error_des=pasteboard have no video data"//剪切板没有数据
@"ret=-12&error_des=export data failed"//从剪切板导出数据到本地失败
@"ret=-13&error_des=url param invalid"//sdk传递过来的数据有误
@"ret=-14&error_des=video param invalid"//视频的参数不符合要求检测第三方视频源方案1、分辨率跟480*480保持一致2、视频长度0.5s8s
@"ret=-15&error_des=app authorised failed"//应用鉴权失败
@"ret=-16&error_des=upload video failed"//设置头像,上传到后台失败
@"ret=-17&error_des=account diff"//账号不一致
*/
@interface QQApiVideoForQQAvatarObject : QQApiExtendObject
@property(nonatomic, retain) NSString *assetURL;
@end
// QQApiImageArrayForFaceCollectionObject
/** @brief 图片数组对象
用于分享图片组到表情收藏,是一个指定为图片类型的<code>QQApiObject</code>
*/
@interface QQApiImageArrayForFaceCollectionObject : QQApiObject
@property(nonatomic,retain) NSArray* imageDataArray;///图片数组
/**
初始化方法
@param imageDataArray 图片数组
*/
- (id)initWithImageArrayData:(NSArray*)imageDataArray;
/**
helper方法获取一个autorelease的<code>QQApiObject</code>对象
@param imageDataArray 发送的多张图片队列
@return
一个自动释放的<code>QQApiObject</code>实例
*/
+ (id)objectWithimageDataArray:(NSArray *)imageDataArray;
@end
// QQApiImageArrayForQZoneObject
/** @brief 图片对象
用于分享图片到空间,走写说说路径,是一个指定为图片类型的,当图片数组为空时,默认走文本写说说<code>QQApiObject</code>
*/
@interface QQApiImageArrayForQZoneObject : QQApiObject
@property(nonatomic,retain) NSArray* imageDataArray;///图片数组
@property(nonatomic,retain) NSDictionary* extMap; // 扩展字段
/**
初始化方法
@param imageDataArray 图片数组
@param title 写说说的内容,可以为空
@param extMap 扩展字段
*/
- (id)initWithImageArrayData:(NSArray*)imageDataArray title:(NSString*)title extMap:(NSDictionary *)extMap;
/**
helper方法获取一个autorelease的<code>QQApiExtendObject</code>对象
@param title 写说说的内容,可以为空
@param imageDataArray 发送的多张图片队列
@param extMap 扩展字段
@return
一个自动释放的<code>QQApiExtendObject</code>实例
*/
+ (id)objectWithimageDataArray:(NSArray*)imageDataArray title:(NSString*)title extMap:(NSDictionary *)extMap;
@end
// QQApiVideoForQZoneObject
/** @brief 视频对象
用于分享视频到空间,走写说说路径<code>QQApiObject</code>,assetURL和videoData两个参数必须设置至少一个参数如果assetURL设置了忽略videoData参数
@param assetURL可传ALAsset的ALAssetPropertyAssetURL或者PHAsset的localIdentifier
@param extMap 扩展字段
@param videoData 视频数据大小不超过50M
*/
@interface QQApiVideoForQZoneObject : QQApiObject
@property(nonatomic, retain) NSString *assetURL;
@property(nonatomic,retain) NSDictionary* extMap; // 扩展字段
@property(nonatomic,retain) NSData* videoData;
- (id)initWithAssetURL:(NSString*)assetURL title:(NSString*)title extMap:(NSDictionary *)extMap;
+ (id)objectWithAssetURL:(NSString*)assetURL title:(NSString*)title extMap:(NSDictionary *)extMap;
- (id)initWithVideoData:(NSData*)videoData title:(NSString*)title extMap:(NSDictionary *)extMap;
+ (id)objectWithVideoData:(NSData*)videoData title:(NSString*)title extMap:(NSDictionary *)extMap;
@end
// QQApiWebImageObject
/** @brief 图片对象
用于分享网络图片内容的对象是一个指定网络图片url的: 该类型只在2.9.0的h5分享中才支持
原有的手q分享是不支持该类型的。
*/
@interface QQApiWebImageObject : QQApiObject
@property(nonatomic, retain) NSURL *previewImageURL; ///<预览图像URL
/**
初始化方法
@param previewImageURL 用于预览的图片
@param title 标题
@param description 此对象,分享的描述
*/
- (id)initWithPreviewImageURL:(NSURL*)previewImageURL title:(NSString*)title description:(NSString*)description;
/**
helper方法获取一个autorelease的<code>QQApiWebImageObject</code>对象
@param previewImageURL 用于预览的图片
@param title 标题
@param description 此对象,分享的描述
*/
+ (id)objectWithPreviewImageURL:(NSURL*)previewImageURL title:(NSString*)title description:(NSString*)description;
@end
//QQApiFileObject
/** @brief 本地文件对象(暂只支持分享到手机QQ数据线功能)
用于分享文件内容的对象,是一个指定为文件类型的<code>QQApiExtendObject</code>
*/
@interface QQApiFileObject : QQApiExtendObject
{
NSString* _fileName;
}
@property(nonatomic, retain)NSString* fileName;
@end
// QQApiAudioObject
/** @brief 音频URL对象
用于分享目标内容为音频的URL的对象
*/
@interface QQApiAudioObject : QQApiURLObject
@property (nonatomic, retain) NSURL *flashURL; ///<音频URL地址最长512个字符
/**
获取一个autorelease的<code>QQApiAudioObject</code>
@param url 音频内容的目标URL
@param title 分享内容的标题
@param description 分享内容的描述
@param data 分享内容的预览图像
@note 如果url为空调用<code>QQApi#sendMessage:</code>时将返回FALSE
*/
+(id)objectWithURL:(NSURL*)url title:(NSString*)title description:(NSString*)description previewImageData:(NSData*)data;
/**
获取一个autorelease的<code>QQApiAudioObject</code>
@param url 音频内容的目标URL
@param title 分享内容的标题
@param description 分享内容的描述
@param previewURL 分享内容的预览图像URL
@note 如果url为空调用<code>QQApi#sendMessage:</code>时将返回FALSE
*/
+(id)objectWithURL:(NSURL*)url title:(NSString*)title description:(NSString*)description previewImageURL:(NSURL*)previewURL;
@end
// QQApiVideoObject
/** @brief 视频URL对象
用于分享目标内容为视频的URL的对象
QQApiVideoObject类型的分享目前在Android和PC QQ上接收消息时展现有待完善待手机QQ版本以后更新支持
目前如果要分享视频,推荐使用 QQApiNewsObject 类型
*/
@interface QQApiVideoObject : QQApiURLObject
@property (nonatomic, retain) NSURL *flashURL; ///<视频URL地址最长512个字符
/**
获取一个autorelease的<code>QQApiVideoObject</code>
@param url 视频内容的目标URL
@param title 分享内容的标题
@param description 分享内容的描述
@param data 分享内容的预览图像
@note 如果url为空调用<code>QQApi#sendMessage:</code>时将返回FALSE
*/
+(id)objectWithURL:(NSURL*)url title:(NSString*)title description:(NSString*)description previewImageData:(NSData*)data;
/**
获取一个autorelease的<code>QQApiVideoObject</code>
@param url 视频内容的目标URL
@param title 分享内容的标题
@param description 分享内容的描述
@param previewURL 分享内容的预览图像URL
@note 如果url为空调用<code>QQApi#sendMessage:</code>时将返回FALSE
*/
+(id)objectWithURL:(NSURL*)url title:(NSString*)title description:(NSString*)description previewImageURL:(NSURL*)previewURL;
@end
// QQApiNewsObject
/** @brief 新闻URL对象
用于分享目标内容为新闻的URL的对象
*/
@interface QQApiNewsObject : QQApiURLObject
/**
获取一个autorelease的<code>QQApiNewsObject</code>
@param url 视频内容的目标URL
@param title 分享内容的标题
@param description 分享内容的描述
@param data 分享内容的预览图像
@note 如果url为空调用<code>QQApi#sendMessage:</code>时将返回FALSE
*/
+(id)objectWithURL:(NSURL*)url title:(NSString*)title description:(NSString*)description previewImageData:(NSData*)data;
/**
获取一个autorelease的<code>QQApiNewsObject</code>
@param url 视频内容的目标URL
@param title 分享内容的标题
@param description 分享内容的描述
@param previewURL 分享内容的预览图像URL
@note 如果url为空调用<code>QQApi#sendMessage:</code>时将返回FALSE
*/
+(id)objectWithURL:(NSURL*)url title:(NSString*)title description:(NSString*)description previewImageURL:(NSURL*)previewURL;
@end
// QQApiCommonContentObject;
/** @brief 通用模板类型对象
用于分享一个固定显示模板的图文混排对象
@note 图片列表和文本列表不能同时为空
*/
@interface QQApiCommonContentObject : QQApiObject
/**
预定义的界面布局类型
*/
@property(nonatomic,assign) unsigned int layoutType;
@property(nonatomic,assign) NSData* previewImageData;///<预览图
@property(nonatomic,retain) NSArray* textArray;///<文本列表
@property(nonatomic,retain) NSArray* pictureDataArray;///<图片列表
+(id)objectWithLayoutType:(int)layoutType textArray:(NSArray*)textArray pictureArray:(NSArray*)pictureArray previewImageData:(NSData*)data;
/**
将一个NSDictionary对象转化为QQApiCommomContentObject如果无法转换则返回空
*/
+(id)objectWithDictionary:(NSDictionary*)dic;
-(NSDictionary*)toDictionary;
@end
// QQApiExtraServiceObject;
/**
@brief OpenSDK扩展支持的服务通用接口后续会扩充能力
@param serviceID [必选] 扩展支持的服务类型ID参考官方文档说明
@param openID [必选] 授权登录后对该用户的唯一标识
@param toUin [可选] 对方的QQ号码
@param extraInfo [可选] 扩展字段
@note 该接口的使用须先登录
*/
@interface QQApiExtraServiceObject : QQApiObject
@property (nonatomic,retain) NSString* serviceID;
@property (nonatomic,retain) NSString* openID;
@property (nonatomic,retain) NSString* toUin;
@property (nonatomic,retain) NSDictionary* extraInfo;
- (id)initWithOpenID:(NSString *)openID serviceID:(NSString *)serviceID;
+ (id)objecWithOpenID:(NSString *)openID serviceID:(NSString *)serviceID;
@end
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Ad item object definition
////////////////////////////////////////////////////////////////////////////////////////////////////////////
/** @brief 广告数据对象
*/
@interface QQApiAdItem : NSObject
@property(nonatomic,retain) NSString* title; ///<名称
@property(nonatomic,retain) NSString* description;///<描述
@property(nonatomic,retain) NSData* imageData;///<广告图片
@property(nonatomic,retain) NSURL* target;///<广告目标链接
@end
#pragma mark - QQApi请求消息类型
/**
QQApi请求消息类型
*/
typedef NS_ENUM(NSUInteger, QQApiInterfaceReqType) {
EGETMESSAGEFROMQQREQTYPE = 0, ///< 手Q -> 第三方应用请求第三方应用向手Q发送消息
ESENDMESSAGETOQQREQTYPE = 1, ///< 第三方应用 -> 手Q第三方应用向手Q分享消息
ESHOWMESSAGEFROMQQREQTYPE = 2, ///< 手Q -> 第三方应用,请求第三方应用展现消息中的数据
ESENDMESSAGEARKTOQQREQTYPE = 3, ///< 第三方应用 -> 手Q第三方应用向手Q分享Ark消息
ESENDMESSAGE_MINI_TOQQREQTYPE = 4 ///< 第三方应用 -> 手Q第三方应用向手Q分享小程序消息
};
/**
QQApi应答消息类型
*/
typedef NS_ENUM(NSUInteger, QQApiInterfaceRespType) {
ESHOWMESSAGEFROMQQRESPTYPE = 0, ///< 第三方应用 -> 手Q第三方应用应答消息展现结果
EGETMESSAGEFROMQQRESPTYPE = 1, ///< 第三方应用 -> 手Q第三方应用回应发往手Q的消息
ESENDMESSAGETOQQRESPTYPE = 2 ///< 手Q -> 第三方应用手Q应答处理分享消息的结果
};
/**
QQApi请求消息基类
*/
@interface QQBaseReq : NSObject
/** 请求消息类型,参见\ref QQApiInterfaceReqType */
@property (nonatomic, assign) int type;
@end
/**
QQApi应答消息基类
*/
@interface QQBaseResp : NSObject
/** 请求处理结果 */
@property (nonatomic, copy) NSString* result;
/** 具体错误描述信息 */
@property (nonatomic, copy) NSString* errorDescription;
/** 应答消息类型,参见\ref QQApiInterfaceRespType */
@property (nonatomic, assign) int type;
/** 扩展信息 */
@property (nonatomic, assign) NSString* extendInfo;
@end
/**
GetMessageFromQQReq请求帮助类
*/
@interface GetMessageFromQQReq : QQBaseReq
/**
创建一个GetMessageFromQQReq请求实例
*/
+ (GetMessageFromQQReq *)req;
@end
@interface SendMessageToQQReq : QQBaseReq
/**
创建一个SendMessageToQQReq请求实例
\param message 具体分享消息实例
\return 新创建的SendMessageToQQReq请求实例
*/
+ (SendMessageToQQReq *)reqWithContent:(QQApiObject *)message;
/**
创建一个支持Ark的SendMessageToQQReq请求实例
\param message 具体分享消息实例
\return 新创建的SendMessageToQQReq请求实例
*/
+ (SendMessageToQQReq *)reqWithArkContent:(ArkObject *)message;
/**
* 创建一个支持小程序的消息请求实例
* @param miniMessage 小程序实例对象
* @return 消息请求实例
*/
+(SendMessageToQQReq*) reqWithMiniContent:(QQApiMiniProgramObject *)miniMessage;
/** 具体分享消息 */
@property (nonatomic, retain) QQApiObject *message;
/** 支持Ark的具体分享消息 */
@property (nonatomic, retain) ArkObject *arkMessage;
/** 支持小程序的具体分享消息 */
@property (nonatomic, retain) QQApiMiniProgramObject *miniMessage;
@end
/**
SendMessageToQQResp应答帮助类
*/
@interface SendMessageToQQResp : QQBaseResp
/**
创建一个SendMessageToQQResp应答实例
\param result 请求处理结果
\param errDesp 具体错误描述信息
\param extendInfo 扩展信息
\return 新创建的SendMessageToQQResp应答实例
*/
+ (SendMessageToQQResp *)respWithResult:(NSString *)result errorDescription:(NSString *)errDesp extendInfo:(NSString*)extendInfo;
@end
/**
ShowMessageFromQQReq请求帮助类
*/
@interface ShowMessageFromQQReq : QQBaseReq
/**
创建一个ShowMessageFromQQReq请求实例
\param message 具体待展现消息实例
\return 新创建的ShowMessageFromQQReq请求实例
*/
+ (ShowMessageFromQQReq *)reqWithContent:(QQApiObject *)message;
/** 具体待展现消息 */
@property (nonatomic, retain) QQApiObject *message;
@end
#endif

View File

@ -0,0 +1,494 @@
///
/// \file TencentOAuth.h
/// \brief QQ互联开放平台授权登录及相关开放接口实现类
///
/// Created by Tencent on 12-12-21.
/// Copyright (c) 2012年 Tencent. All rights reserved.
///
#import <UIKit/UIKit.h>
#import "sdkdef.h"
@protocol TencentSessionDelegate;
@protocol TencentLoginDelegate;
@protocol TencentApiInterfaceDelegate;
@protocol TencentWebViewDelegate;
@class TencentApiReq;
@class TencentApiResp;
typedef NS_ENUM(NSUInteger, TencentAuthorizeState) {
kTencentNotAuthorizeState,
kTencentSSOAuthorizeState,
kTencentWebviewAuthorzieState,
};
typedef NS_ENUM(NSUInteger, TencentAuthMode) {
kAuthModeClientSideToken,
kAuthModeServerSideCode,
};
#pragma mark - TencentOAuth()
/**
* \brief TencentOpenAPI授权登录及相关开放接口调用
*
* TencentOAuth实现授权登录逻辑以及相关开放接口的请求调用
*/
@interface TencentOAuth : NSObject
{
NSMutableDictionary* _apiRequests;
NSString* _accessToken;
NSDate* _expirationDate;
id<TencentSessionDelegate> _sessionDelegate;
NSString* _localAppId;
NSString* _openId;
NSString* _redirectURI;
NSArray* _permissions;
}
/** Access Token凭证用于后续访问各开放接口 */
@property(nonatomic, copy) NSString* accessToken;
/** Access Token的失效期 */
@property(nonatomic, copy) NSDate* expirationDate;
/** 已实现的开放接口的回调委托对象 */
@property(nonatomic, assign) id<TencentSessionDelegate> sessionDelegate;
/** 第三方应用在开发过程中设置的URLSchema用于浏览器登录后后跳到第三方应用 */
@property(nonatomic, copy) NSString* localAppId;
/** 用户授权登录后对该用户的唯一标识 */
@property(nonatomic, copy) NSString* openId;
/** 用户登录成功过后的跳转页面地址 */
@property(nonatomic, copy) NSString* redirectURI;
/** 第三方应用在互联开放平台申请的appID */
@property(nonatomic, retain) NSString* appId;
/** 第三方应用在互联开放平台注册的UniversalLink */
@property(nonatomic, retain) NSString* universalLink;
/** 主要是互娱的游戏设置uin */
@property(nonatomic, retain) NSString* uin;
/** 主要是互娱的游戏设置鉴定票据 */
@property(nonatomic, retain) NSString* skey;
/** 登陆透传的数据 */
@property(nonatomic, copy) NSDictionary* passData;
/** 授权方式(Client Side Token或者Server Side Code) */
@property(nonatomic, assign) TencentAuthMode authMode;
/** union id */
@property(nonatomic, retain) NSString* unionid;
/** 第三方在授权登录/分享 时选择 QQ还是TIM 。在授权前一定要指定其中一个类型*/
@property(nonatomic, assign) TencentAuthShareType authShareType;
/**
* 获取上次登录得到的token
*
**/
- (NSString *)getCachedToken;
/**
* 获取上次登录得到的openid
*
**/
- (NSString *)getCachedOpenID;
/**
* 获取上次登录的token过期日期
*
**/
- (NSDate *)getCachedExpirationDate;
/**
* 上次登录的token是否过期(本地判断)
**/
- (BOOL)isCachedTokenValid;
/**
* 删除上次登录登录的token信息
*
**/
- (BOOL)deleteCachedToken;
/**
* 用来获得当前sdk的版本号
* \return 返回sdk版本号
**/
+ (NSString*)sdkVersion;
/**
* 用来获得当前sdk的小版本号
* \return 返回sdk小版本号
**/
+ (NSString*)sdkSubVersion;
/**
* 用来获得当前sdk的是否精简版
* \return 返回YES表示精简版
**/
+ (BOOL)isLiteSDK;
/**
* 主要是用来帮助判断是否有登陆被发起,但是还没有过返回结果
* \return
* kTencentNotAuthorizeState:无授权
* kTencentSSOAuthorizeState:有人发起了sso授权但无返回
* kTencentWebviewAuthorzieState:有人发起了webview授权还未返回
**/
+ (TencentAuthorizeState *)authorizeState;
/**
* 用来获得当前手机qq的版本号
* \return 返回手机qq版本号
**/
+ (int)iphoneQQVersion __attribute__((deprecated("已过期, 建议删除调用")));
/**
* 用来获得当前手机TIM的版本号
* \return 返回手机qq版本号
**/
+ (int)iphoneTIMVersion __attribute__((deprecated("已过期, 建议删除调用")));
/**
* 初始化TencentOAuth对象
* \param appId 第三方应用在互联开放平台申请的唯一标识
* \param delegate 第三方应用用于接收请求返回结果的委托对象
* \return 初始化后的授权登录对象
*/
- (id)initWithAppId:(NSString *)appId
andDelegate:(id<TencentSessionDelegate>)delegate;
/**
* 初始化TencentOAuth对象>=3.3.7
* \param appId 第三方应用在互联开放平台申请的唯一标识
* \param universalLink 第三方应用在互联开放平台注册的universallink和bundleID一一对应
* \param delegate 第三方应用用于接收请求返回结果的委托对象
* \return 初始化后的授权登录对象
*/
- (id)initWithAppId:(NSString *)appId
andUniversalLink:(NSString *)universalLink
andDelegate:(id<TencentSessionDelegate>)delegate;
/**
* 判断用户手机上是否安装手机QQ
* \return YES:安装 NO:没安装
*
* \note SDK目前已经支持QQ、TIM授权登录及分享功能 会按照QQ>TIM的顺序进行调用。
* 只要用户安装了QQ、TIM中任意一个应用都可为第三方应用进行授权登录、分享功能。
* 第三方应用在接入SDK时不需要判断是否安装QQ、TIM。若有判断安装QQ、TIM的逻辑建议移除。
*/
+ (BOOL)iphoneQQInstalled;
/**
* 判断用户手机上是否安装手机TIM
* \return YES:安装 NO:没安装
*
* \note SDK目前已经支持QQ、TIM授权登录及分享功能 会按照QQ>TIM的顺序进行调用。
* 只要用户安装了QQ、TIM中任意一个应用都可为第三方应用进行授权登录、分享功能。
* 第三方应用在接入SDK时不需要判断是否安装QQ、TIM。若有判断安装QQ、TIM的逻辑建议移除。
*/
+ (BOOL)iphoneTIMInstalled;
/**
* 判断用户手机上的手机QQ是否支持SSO登录
* \return YES:支持 NO:不支持
*/
+ (BOOL)iphoneQQSupportSSOLogin __attribute__((deprecated("QQ版本均支持SSO登录。该接口已过期, 建议删除调用")));
/**
* 判断用户手机上的手机TIM是否支持SSO登录
* \return YES:支持 NO:不支持
*/
+ (BOOL)iphoneTIMSupportSSOLogin __attribute__((deprecated("TIM版本均支持SSO登录。该接口已过期, 建议删除调用")));
/**
* 登录授权
*
* \param permissions 授权信息列
*/
- (BOOL)authorize:(NSArray *)permissions;
/**
* 登录授权
* \param permissions 授权信息列表
* \param bInSafari 是否使用safari进行登录.<b>IOS SDK 1.3版本开始此参数废除</b>
*/
- (BOOL)authorize:(NSArray *)permissions
inSafari:(BOOL)bInSafari;
/**
* 登录授权
* \param permissions 授权信息列表
* \param localAppId 应用APPID
* \param bInSafari 是否使用safari进行登录.<b>IOS SDK 1.3版本开始此参数废除</b>
*/
- (BOOL)authorize:(NSArray *)permissions
localAppId:(NSString *)localAppId
inSafari:(BOOL)bInSafari;
/**
* 登录授权<web为二维码扫码方式>
*
* \param permissions 授权信息列
*/
- (BOOL)authorizeWithQRlogin:(NSArray *)permissions;
/**
* 增量授权,因用户没有授予相应接口调用的权限,需要用户确认是否授权
* \param permissions 需增量授权的信息列表
* \return 增量授权调用是否成功
*/
- (BOOL)incrAuthWithPermissions:(NSArray *)permissions;
/**
* 重新授权因token废除或失效导致接口调用失败需用户重新授权
* \param permissions 授权信息列表,同登录授权
* \return 授权调用是否成功
*/
- (BOOL)reauthorizeWithPermissions:(NSArray *)permissions;
/**
* 获取UnindID,可以根据UnindID的比较来确定OpenID是否属于同一个用户
* \return NO未登录信息不足YES条件满足发送请求成功请等待回调
*/
- (BOOL)RequestUnionId;
/**
* (静态方法)处理应用拉起协议
* \param url 处理被其他应用呼起时的逻辑
* \return 处理结果YES表示成功NO表示失败
*/
+ (BOOL)HandleOpenURL:(NSURL *)url;
/**
* (静态方法)sdk是否可以处理应用拉起协议
* \param url 处理被其他应用呼起时的逻辑
* \return 处理结果YES表示可以 NO表示不行
*/
+ (BOOL)CanHandleOpenURL:(NSURL *)url;
/**
* (静态方法)处理应用的UniversalLink拉起协议
* \param url 处理被其他应用呼起时的逻辑
* \return 处理结果YES表示成功NO表示失败
*/
+ (BOOL)HandleUniversalLink:(NSURL *)url;
/**
* (静态方法)sdk是否可以处理应用的Universallink拉起协议
* \param url 处理被其他应用呼起时的逻辑(应用的Universallink链接须满足官网注册时的格式要求)
* \return 处理结果YES表示可以 NO表示不行
* 注在调用其他Universallink相关处理接口之前均需进行此项判断
*/
+ (BOOL)CanHandleUniversalLink:(NSURL *)url;
/**
* (静态方法)获取TencentOAuth调用的上一次错误信息
*/
+ (NSString *)getLastErrorMsg;
/**
* 以Server Side Code模式授权登录时通过此接口获取返回的code值;
* 以Client Side Token模式授权登录时忽略此接口。
*/
- (NSString *)getServerSideCode;
/**
* 退出登录(退出登录后TecentOAuth失效需要重新初始化)
* \param delegate 第三方应用用于接收请求返回结果的委托对象
*/
- (void)logout:(id<TencentSessionDelegate>)delegate;
/**
* 判断登录态是否有效
* \return 处理结果YES表示有效NO表示无效请用户重新登录授权
*/
- (BOOL)isSessionValid;
/**
* 获取用户个人信息
* \return 处理结果YES表示API调用成功NO表示API调用失败登录态失败重新登录
*/
- (BOOL)getUserInfo;
/**
* 退出指定API调用
* \param userData 用户调用某条API的时候传入的保留参数
* \return 处理结果YES表示成功 NO表示失败
*/
- (BOOL)cancel:(id)userData;
/**
* CGI类任务创建接口
* \param apiURL CGI请求的URL地址
* \param method CGI请求方式"GET""POST"
* \param params CGI请求参数字典
* \param callback CGI请求结果的回调接口对象
* \return CGI请求任务实例用于取消任务返回nil代表任务创建失败
*/
- (TCAPIRequest *)cgiRequestWithURL:(NSURL *)apiURL method:(NSString *)method params:(NSDictionary *)params callback:(id<TCAPIRequestDelegate>)callback;
/**
* TencentOpenApi发送任务统一接口
* \param request 请求发送的任务
* \param callback 任务发送后的回调地址
*/
- (BOOL)sendAPIRequest:(TCAPIRequest *)request callback:(id<TCAPIRequestDelegate>)callback;
- (NSString *)getUserOpenID;
@end
#pragma mark - TencentLoginDelegate()
/**
* \brief TencentLoginDelegate iOS Open SDK 1.3 API回调协议
*
* 第三方应用实现登录的回调协议
*/
@protocol TencentLoginDelegate <NSObject>
@required
/**
* 登录成功后的回调
*/
- (void)tencentDidLogin;
/**
* 登录失败后的回调
* \param cancelled 代表用户是否主动退出登录
*/
- (void)tencentDidNotLogin:(BOOL)cancelled;
/**
* 登录时网络有问题的回调
*/
- (void)tencentDidNotNetWork;
@optional
/**
* 登录时权限信息的获得
*/
- (NSArray *)getAuthorizedPermissions:(NSArray *)permissions withExtraParams:(NSDictionary *)extraParams;
/**
* unionID获得
*/
- (void)didGetUnionID;
/**
* 强制网页登录,包括账号密码登录和二维码登录
* return YES时就算本地有手Q也会打开web界面
*/
- (BOOL)forceWebLogin;
@end
#pragma mark - TencentSessionDelegate()
/**
* \brief TencentSessionDelegate iOS Open SDK 1.3 API回调协议
*
* 第三方应用需要实现每条需要调用的API的回调协议
*/
@protocol TencentSessionDelegate<NSObject, TencentLoginDelegate,
TencentWebViewDelegate>
@optional
/**
* 退出登录的回调
*/
- (void)tencentDidLogout;
/**
* 因用户未授予相应权限而需要执行增量授权。在用户调用某个api接口时如果服务器返回操作未被授权则触发该回调协议接口由第三方决定是否跳转到增量授权页面让用户重新授权。
* \param tencentOAuth 登录授权对象。
* \param permissions 需增量授权的权限列表。
* \return 是否仍然回调返回原始的api请求结果。
* \note 不实现该协议接口则默认为不开启增量授权流程。若需要增量授权请调用\ref TencentOAuth#incrAuthWithPermissions: \n注意增量授权时用户可能会修改登录的帐号
*/
- (BOOL)tencentNeedPerformIncrAuth:(TencentOAuth *)tencentOAuth withPermissions:(NSArray *)permissions;
/**
* [该逻辑未实现]因token失效而需要执行重新登录授权。在用户调用某个api接口时如果服务器返回token失效则触发该回调协议接口由第三方决定是否跳转到登录授权页面让用户重新授权。
* \param tencentOAuth 登录授权对象。
* \return 是否仍然回调返回原始的api请求结果。
* \note 不实现该协议接口则默认为不开启重新登录授权流程。若需要重新登录授权请调用\ref TencentOAuth#reauthorizeWithPermissions: \n注意重新登录授权时用户可能会修改登录的帐号
*/
- (BOOL)tencentNeedPerformReAuth:(TencentOAuth *)tencentOAuth;
/**
* 用户通过增量授权流程重新授权登录token及有效期限等信息已被更新。
* \param tencentOAuth token及有效期限等信息更新后的授权实例对象
* \note 第三方应用需更新已保存的token及有效期限等信息。
*/
- (void)tencentDidUpdate:(TencentOAuth *)tencentOAuth;
/**
* 用户增量授权过程中因取消或网络问题导致授权失败
* \param reason 授权失败原因具体失败原因参见sdkdef.h文件中\ref UpdateFailType
*/
- (void)tencentFailedUpdate:(UpdateFailType)reason;
/**
* 获取用户个人信息回调
* \param response API返回结果具体定义参见sdkdef.h文件中\ref APIResponse
* \remarks 正确返回示例: \snippet example/getUserInfoResponse.exp success
* 错误返回示例: \snippet example/getUserInfoResponse.exp fail
*/
- (void)getUserInfoResponse:(APIResponse*) response;
/**
* 社交API统一回调接口
* \param response API返回结果具体定义参见sdkdef.h文件中\ref APIResponse
* \param message 响应的消息目前支持SendStory,AppInvitationAppChallengeAppGiftRequest
*/
- (void)responseDidReceived:(APIResponse*)response forMessage:(NSString *)message;
/**
* post请求的上传进度
* \param tencentOAuth 返回回调的tencentOAuth对象
* \param bytesWritten 本次回调上传的数据字节数
* \param totalBytesWritten 总共已经上传的字节数
* \param totalBytesExpectedToWrite 总共需要上传的字节数
* \param userData 用户自定义数据
*/
- (void)tencentOAuth:(TencentOAuth *)tencentOAuth didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite userData:(id)userData;
/**
* 通知第三方界面需要被关闭
* \param tencentOAuth 返回回调的tencentOAuth对象
* \param viewController 需要关闭的viewController
*/
- (void)tencentOAuth:(TencentOAuth *)tencentOAuth doCloseViewController:(UIViewController *)viewController;
@end
#pragma mark - TencentWebViewDelegate(H5登录webview旋转方向回调)
/**
* \brief TencentWebViewDelegate: H5登录webview旋转方向回调协议
*
* 第三方应用可以根据自己APP的旋转方向限制通过此协议设置
*/
@protocol TencentWebViewDelegate <NSObject>
@optional
- (BOOL) tencentWebViewShouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation;
- (NSUInteger) tencentWebViewSupportedInterfaceOrientationsWithWebkit;
- (BOOL) tencentWebViewShouldAutorotateWithWebkit;
@end

View File

@ -0,0 +1,414 @@
///
/// \file sdkdef.h
/// \brief SDK中相关常量定义
///
/// Created by Tencent on 12-12-25.
/// Copyright (c) 2012年 Tencent. All rights reserved.
///
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
/**
* \brief sdk的log等级
*/
typedef enum {
TCOLogLevel_Disabled = -1, // log
TCOLogLevel_Error = 0,
TCOLogLevel_Warning,
TCOLogLevel_Info,
TCOLogLevel_Debug,
} TCOLogLevel;
/**
* \breif 授权/分享 方式
*/
typedef enum TencentAuthShareType {
AuthShareType_QQ,
AuthShareType_TIM,
}TencentAuthShareType;
/**
* \brief APIResponse.retCode可能的枚举常量
*/
typedef enum
{
URLREQUEST_SUCCEED = 0, /**< 网络请求成功发送至服务器,并且服务器返回数据格式正确
* \note 这里包括所请求业务操作失败的情况,例如没有授权等原因导致
*/
URLREQUEST_FAILED = 1, /**< 网络异常,或服务器返回的数据格式不正确导致无法解析 */
} REPONSE_RESULT;
/**
* \brief 增量授权失败原因
*
* \note 增量授权失败不影响原token的有效性原token已失效的情况除外
*/
typedef enum
{
kUpdateFailUnknown = 1, ///< 未知原因
kUpdateFailUserCancel, ///< 用户取消
kUpdateFailNetwork, ///< 网络问题
} UpdateFailType;
/**
* \brief 封装服务器返回的结果
*
* APIResponse用于封装所有请求的返回结果包括错误码、错误信息、原始返回数据以及返回数据的json格式字典
*/
@interface APIResponse : NSObject<NSCoding> {
int _detailRetCode;
int _retCode;
int _seq;
NSString *_errorMsg;
NSDictionary *_jsonResponse;
NSString *_message;
id _userData;
}
/**
* 新增的详细错误码\n
* detailRetCode主要用于区分不同的错误情况参见\ref OpenSDKError
*/
@property (nonatomic, assign) int detailRetCode;
/**
* 网络请求是否成功送达服务器,以及服务器返回的数据格式是否正确\n
* retCode具体取值可参考\ref REPONSE_RESULT
*/
@property (nonatomic, assign) int retCode;
/**
* 网络请求对应的递增序列号,方便内部管理
*/
@property (nonatomic, assign) int seq;
/**
* 错误提示语
*/
@property (nonatomic, retain) NSString *errorMsg;
/**
* 服务器返回数据的json格式字典\n
* 字典内具体参数的命名和含义请参考\ref api_spec
*/
@property (nonatomic, retain) NSDictionary *jsonResponse;
/**
* 服务器返回的原始数据字符串
*/
@property (nonatomic, retain) NSString *message;
/**
* 用户保留数据
*/
@property (nonatomic, retain) id userData;
@end
/**
* 用户自定义的保留字段
*/
FOUNDATION_EXTERN NSString * const PARAM_USER_DATA;
/**
* \name 应用邀请参数字段定义
*/
///@{
/** 应用邀请展示图片url的key */
FOUNDATION_EXTERN NSString * const PARAM_APP_ICON;
/** 应用邀请描述文本的key */
FOUNDATION_EXTERN NSString * const PARAM_APP_DESC;
/** 应用邀请好友列表的key */
FOUNDATION_EXTERN NSString * const PARAM_APP_INVITED_OPENIDS;
///@}
/**
* \name sendStory新分享参数字段定义
*/
///@{
/** 预填入接受人列表的key */
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_RECEIVER;
/** 分享feeds标题的key */
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_TITLE;
/** 分享feeds评论内容的key */
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_COMMENT;
/** 分享feeds摘要的key */
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_SUMMARY;
/** 分享feeds展示图片url的key */
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_IMAGE;
/** 分享feeds跳转链接url的key */
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_URL;
/** 分享feeds点击操作默认行为的key */
FOUNDATION_EXTERN NSString * const PARAM_SENDSTORY_ACT;
///@}
/**
* \name 设置头像参数字段定义
*/
///@{
/** 头像图片数据的key */
FOUNDATION_EXTERN NSString * const PARAM_SETUSERHEAD_PIC;
/** 头像图片文件名的key */
FOUNDATION_EXTERN NSString * const PARAM_SETUSERHEAD_FILENAME;
///@}
/**
* \name 服务器返回数据的参数字段定义
*/
///@{
/** 服务器返回码的key */
FOUNDATION_EXTERN NSString * const PARAM_RETCODE;
/** 服务器返回错误信息的key */
FOUNDATION_EXTERN NSString * const PARAM_MESSAGE;
/** 服务器返回额外数据的key */
FOUNDATION_EXTERN NSString * const PARAM_DATA;
///@}
/**
* \name 错误信息相关常量定义
*/
///@{
/** 详细错误信息字典中额外信息的key */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorKeyExtraInfo;
/** 详细错误信息字典中返回码的key */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorKeyRetCode;
/** 详细错误信息字典中错误语句的key */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorKeyMsg;
/** 不支持的接口 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgUnsupportedAPI;
/** 操作成功 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgSuccess;
/** 未知错误 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgUnknown;
/** 用户取消 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgUserCancel;
/** 请重新登录 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgReLogin;
/** 应用没有操作权限 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgOperationDeny;
/** 网络异常或没有网络 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgNetwork;
/** URL格式或协议错误 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgURL;
/** 解析数据出错 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgDataParse;
/** 传入参数有误 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgParam;
/** 连接超时 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgTimeout;
/** 安全问题 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgSecurity;
/** 文件读写错误 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgIO;
/** 服务器端错误 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgServer;
/** 页面错误 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgWebPage;
/** 设置头像图片过大 */
FOUNDATION_EXTERN NSString * const TCOpenSDKErrorMsgUserHeadPicLarge;
///@}
/**
* \brief SDK新增详细错误常量
*/
typedef enum
{
kOpenSDKInvalid = -1, ///< 无效的错误码
kOpenSDKErrorUnsupportedAPI = -2, ///< 不支持的接口
/**
* \name CommonErrorCode
* 公共错误码
*/
///@{
kOpenSDKErrorSuccess = 0, ///< 成功
kOpenSDKErrorUnknown, ///< 未知错误
kOpenSDKErrorUserCancel, ///< 用户取消
kOpenSDKErrorReLogin, ///< token无效或用户未授权相应权限需要重新登录
kOpenSDKErrorOperationDeny, ///< 第三方应用没有该api操作的权限
///@}
/**
* \name NetworkRelatedErrorCode
* 网络相关错误码
*/
///@{
kOpenSDKErrorNetwork, ///< 网络错误,网络不通或连接不到服务器
kOpenSDKErrorURL, ///< URL格式或协议错误
kOpenSDKErrorDataParse, ///< 数据解析错误,服务器返回的数据解析出错
kOpenSDKErrorParam, ///< 传入参数错误
kOpenSDKErrorConnTimeout, ///< http连接超时
kOpenSDKErrorSecurity, ///< 安全问题
kOpenSDKErrorIO, ///< 下载和文件IO错误
kOpenSDKErrorServer, ///< 服务器端错误
///@}
/**
* \name WebViewRelatedError
* webview特有错误
*/
///@{
kOpenSDKErrorWebPage, ///< 页面错误
///@}
/**
* \name SetUserHeadRelatedErrorCode
* 设置头像自定义错误码段
*/
///@{
kOpenSDKErrorUserHeadPicLarge = 0x010000, ///< 图片过大 设置头像自定义错误码
///@}
} OpenSDKError;
/**
* \name SDK版本(v1.3)支持的授权列表常量
*/
///@{
/** 发表一条说说到QQ空间(<b>需要申请权限</b>) */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_ADD_TOPIC;
/** 创建一个QQ空间相册(<b>需要申请权限</b>) */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_ADD_ALBUM;
/** 上传一张照片到QQ空间相册(<b>需要申请权限</b>) */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_UPLOAD_PIC;
/** 获取用户QQ空间相册列表(<b>需要申请权限</b>) */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_LIST_ALBUM;
/** 验证是否认证空间粉丝 */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_CHECK_PAGE_FANS;
/** 获取登录用户自己的详细信息 */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_INFO;
/** 获取其他用户的详细信息 */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_OTHER_INFO;
/** 获取会员用户基本信息 */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_VIP_INFO;
/** 获取会员用户详细信息 */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_VIP_RICH_INFO;
/** 获取用户信息 */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_USER_INFO;
/** 移动端获取用户信息 */
FOUNDATION_EXTERN NSString *const kOPEN_PERMISSION_GET_SIMPLE_USER_INFO;
///@}
/**
* \name CGI接口相关参数类型定义
*/
/** 必填的字符串类型参数 */
typedef NSString *TCRequiredStr;
/** 必填的UIImage类型参数 */
typedef UIImage *TCRequiredImage;
/** 必填的整型参数 */
typedef NSInteger TCRequiredInt;
/** 必填的数字类型 */
typedef NSNumber *TCRequiredNumber;
/** 必填的NSData参数 */
typedef NSData *TCRequiredData;
/** 可选的字符串类型参数 */
typedef NSString *TCOptionalStr;
/** 可选的UIImage类型参数 */
typedef UIImage *TCOptionalImage;
/** 可选的整型参数 */
typedef NSInteger TCOptionalInt;
/** 可选的数字类型 */
typedef NSNumber *TCOptionalNumber;
/** 可选的不定类型参数 */
typedef id TCRequiredId;
///@}
/**
* \brief CGI请求的参数字典封装辅助基类
*
* 将相应属性的值以key-value的形式保存到参数字典中
*/
@interface TCAPIRequest : NSMutableDictionary
/** CGI请求的URL地址 */
@property (nonatomic, readonly) NSURL *apiURL;
/** CGI请求方式"GET""POST" */
@property (nonatomic, readonly) NSString *method;
/**
* API参数中的保留字段可以塞入任意字典支持的类型再调用完成后会带回给调用方
*/
@property (nonatomic, retain) TCRequiredId paramUserData;
/**
* APIResponse,API的返回结果
*/
@property (nonatomic, readonly) APIResponse *response;
/** 取消相应的CGI请求任务 */
- (void)cancel;
@end
@protocol TCAPIRequestDelegate <NSObject>
@optional
- (void)cgiRequest:(TCAPIRequest *)request didResponse:(APIResponse *)response;
@end

Binary file not shown.

View File

@ -2,11 +2,11 @@
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
#
Pod::Spec.new do |s|
s.name = 'fake_tencent'
s.version = '0.0.1'
s.summary = 'A new Flutter plugin.'
s.name = 'tencent_kit'
s.version = '1.0.0'
s.summary = 'A powerful Flutter plugin allowing developers to share with natvie android & iOS Tencent SDKs.'
s.description = <<-DESC
A new Flutter plugin.
A powerful Flutter plugin allowing developers to share with natvie android & iOS Tencent SDKs.
DESC
s.homepage = 'http://example.com'
s.license = { :file => '../LICENSE' }
@ -15,9 +15,10 @@ A new Flutter plugin.
s.source_files = 'Classes/**/*'
s.public_header_files = 'Classes/**/*.h'
s.dependency 'Flutter'
# QQ
s.static_framework = true
s.dependency 'FakeTencent', '~> 3.3.3'
s.vendored_frameworks = 'Libraries/*.framework'
s.frameworks = 'SystemConfiguration', 'WebKit'
# s.libraries = 'stdc++'
# s.requires_arc = true
s.ios.deployment_target = '8.0'
end

View File

@ -1,11 +0,0 @@
library fake_tencent;
export 'src/domain/tencent_login_resp.dart' hide TencentLoginRespSerializer;
export 'src/domain/tencent_resp.dart';
export 'src/domain/tencent_share_resp.dart' hide TencentShareRespSerializer;
export 'src/domain/tencent_user_info_resp.dart'
hide TencentUserInfoRespSerializer;
export 'src/tencent.dart';
export 'src/tencent_qzone_flag.dart';
export 'src/tencent_scene.dart';
export 'src/tencent_scope.dart';

View File

@ -1,24 +0,0 @@
import 'package:jaguar_serializer/jaguar_serializer.dart';
import 'package:fake_tencent/src/domain/tencent_resp.dart';
part 'tencent_login_resp.jser.dart';
@GenSerializer(nameFormatter: toSnakeCase)
class TencentLoginRespSerializer extends Serializer<TencentLoginResp>
with _$TencentLoginRespSerializer {}
class TencentLoginResp extends TencentResp {
TencentLoginResp({
int ret,
String msg,
this.openid,
this.accessToken,
this.expiresIn,
this.createAt,
}) : super(ret: ret, msg: msg);
final String openid;
final String accessToken;
final int expiresIn;
final int createAt;
}

View File

@ -1,37 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'tencent_login_resp.dart';
// **************************************************************************
// JaguarSerializerGenerator
// **************************************************************************
abstract class _$TencentLoginRespSerializer
implements Serializer<TencentLoginResp> {
@override
Map<String, dynamic> toMap(TencentLoginResp model) {
if (model == null) return null;
Map<String, dynamic> ret = <String, dynamic>{};
setMapValue(ret, 'openid', model.openid);
setMapValue(ret, 'access_token', model.accessToken);
setMapValue(ret, 'expires_in', model.expiresIn);
setMapValue(ret, 'create_at', model.createAt);
setMapValue(ret, 'ret', model.ret);
setMapValue(ret, 'msg', model.msg);
return ret;
}
@override
TencentLoginResp fromMap(Map map) {
if (map == null) return null;
final obj = new TencentLoginResp(
ret: map['ret'] as int ?? getJserDefault('ret'),
msg: map['msg'] as String ?? getJserDefault('msg'),
openid: map['openid'] as String ?? getJserDefault('openid'),
accessToken:
map['access_token'] as String ?? getJserDefault('accessToken'),
expiresIn: map['expires_in'] as int ?? getJserDefault('expiresIn'),
createAt: map['create_at'] as int ?? getJserDefault('createAt'));
return obj;
}
}

View File

@ -1,15 +0,0 @@
import 'package:jaguar_serializer/jaguar_serializer.dart';
import 'package:fake_tencent/src/domain/tencent_resp.dart';
part 'tencent_share_resp.jser.dart';
@GenSerializer()
class TencentShareRespSerializer extends Serializer<TencentShareResp>
with _$TencentShareRespSerializer {}
class TencentShareResp extends TencentResp {
TencentShareResp({
int ret,
String msg,
}) : super(ret: ret, msg: msg);
}

View File

@ -1,28 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'tencent_share_resp.dart';
// **************************************************************************
// JaguarSerializerGenerator
// **************************************************************************
abstract class _$TencentShareRespSerializer
implements Serializer<TencentShareResp> {
@override
Map<String, dynamic> toMap(TencentShareResp model) {
if (model == null) return null;
Map<String, dynamic> ret = <String, dynamic>{};
setMapValue(ret, 'ret', model.ret);
setMapValue(ret, 'msg', model.msg);
return ret;
}
@override
TencentShareResp fromMap(Map map) {
if (map == null) return null;
final obj = new TencentShareResp(
ret: map['ret'] as int ?? getJserDefault('ret'),
msg: map['msg'] as String ?? getJserDefault('msg'));
return obj;
}
}

View File

@ -1,66 +0,0 @@
import 'package:fake_tencent/src/domain/tencent_resp.dart';
import 'package:jaguar_serializer/jaguar_serializer.dart';
part 'tencent_user_info_resp.jser.dart';
@GenSerializer(nameFormatter: toSnakeCase)
class TencentUserInfoRespSerializer extends Serializer<TencentUserInfoResp>
with _$TencentUserInfoRespSerializer {}
class TencentUserInfoResp extends TencentResp {
TencentUserInfoResp({
int ret,
String msg,
this.nickname,
this.gender,
this.figureurlQq1,
this.figureurlQq2,
this.figureurl1,
this.figureurl2,
this.figureurl,
this.vip,
this.level,
this.isYellowVip,
this.isYellowYearVip,
this.yellowVipLevel,
}) : super(ret: ret, msg: msg);
final String nickname;
/// 男/女
final String gender;
final String figureurlQq1;
final String figureurlQq2;
final String figureurl1;
final String figureurl2;
final String figureurl;
final String vip;
final String level;
final String isYellowVip;
final String isYellowYearVip;
final String yellowVipLevel;
bool isMale() {
return gender == '';
}
bool isFemale() {
return gender == '';
}
String headImgUrl() {
if (figureurlQq2 != null && figureurlQq2.isNotEmpty) {
return figureurlQq2;
}
if (figureurlQq1 != null && figureurlQq1.isNotEmpty) {
return figureurlQq1;
}
if (figureurl2 != null && figureurl2.isNotEmpty) {
return figureurl2;
}
if (figureurl1 != null && figureurl1.isNotEmpty) {
return figureurl1;
}
return figureurl;
}
}

View File

@ -1,59 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'tencent_user_info_resp.dart';
// **************************************************************************
// JaguarSerializerGenerator
// **************************************************************************
abstract class _$TencentUserInfoRespSerializer
implements Serializer<TencentUserInfoResp> {
@override
Map<String, dynamic> toMap(TencentUserInfoResp model) {
if (model == null) return null;
Map<String, dynamic> ret = <String, dynamic>{};
setMapValue(ret, 'nickname', model.nickname);
setMapValue(ret, 'gender', model.gender);
setMapValue(ret, 'figureurl_qq_1', model.figureurlQq1);
setMapValue(ret, 'figureurl_qq_2', model.figureurlQq2);
setMapValue(ret, 'figureurl_1', model.figureurl1);
setMapValue(ret, 'figureurl_2', model.figureurl2);
setMapValue(ret, 'figureurl', model.figureurl);
setMapValue(ret, 'vip', model.vip);
setMapValue(ret, 'level', model.level);
setMapValue(ret, 'is_yellow_vip', model.isYellowVip);
setMapValue(ret, 'is_yellow_year_vip', model.isYellowYearVip);
setMapValue(ret, 'yellow_vip_level', model.yellowVipLevel);
setMapValue(ret, 'ret', model.ret);
setMapValue(ret, 'msg', model.msg);
return ret;
}
@override
TencentUserInfoResp fromMap(Map map) {
if (map == null) return null;
final obj = new TencentUserInfoResp(
ret: map['ret'] as int ?? getJserDefault('ret'),
msg: map['msg'] as String ?? getJserDefault('msg'),
nickname: map['nickname'] as String ?? getJserDefault('nickname'),
gender: map['gender'] as String ?? getJserDefault('gender'),
figureurlQq1:
map['figureurl_qq_1'] as String ?? getJserDefault('figureurlQq1'),
figureurlQq2:
map['figureurl_qq_2'] as String ?? getJserDefault('figureurlQq2'),
figureurl1:
map['figureurl_1'] as String ?? getJserDefault('figureurl1'),
figureurl2:
map['figureurl_2'] as String ?? getJserDefault('figureurl2'),
figureurl: map['figureurl'] as String ?? getJserDefault('figureurl'),
vip: map['vip'] as String ?? getJserDefault('vip'),
level: map['level'] as String ?? getJserDefault('level'),
isYellowVip:
map['is_yellow_vip'] as String ?? getJserDefault('isYellowVip'),
isYellowYearVip: map['is_yellow_year_vip'] as String ??
getJserDefault('isYellowYearVip'),
yellowVipLevel: map['yellow_vip_level'] as String ??
getJserDefault('yellowVipLevel'));
return obj;
}
}

View File

@ -0,0 +1,20 @@
import 'package:json_annotation/json_annotation.dart';
abstract class TencentApiResp {
TencentApiResp({
this.ret,
this.msg,
});
/// 网络请求成功发送至服务器,并且服务器返回数据格式正确
/// 这里包括所请求业务操作失败的情况,例如没有授权等原因导致
static const int RET_SUCCESS = 0;
@JsonKey(
defaultValue: RET_SUCCESS,
)
final int ret;
final String msg;
bool isSuccessful() => ret == RET_SUCCESS;
}

View File

@ -0,0 +1,36 @@
import 'package:json_annotation/json_annotation.dart';
part 'tencent_unionid_resp.g.dart';
@JsonSerializable(
anyMap: true,
explicitToJson: true,
fieldRename: FieldRename.snake,
)
class TencentUnionidResp {
TencentUnionidResp({
this.error,
this.errorDescription,
this.clientId,
this.openid,
this.unionid,
});
factory TencentUnionidResp.fromJson(Map<dynamic, dynamic> json) =>
_$TencentUnionidRespFromJson(json);
@JsonKey(
defaultValue: ERROR_SUCCESS,
)
final int error;
final String errorDescription;
final String clientId;
final String openid;
final String unionid;
static const int ERROR_SUCCESS = 0;
bool isSuccessful() => error == ERROR_SUCCESS;
Map<dynamic, dynamic> toJson() => _$TencentUnionidRespToJson(this);
}

View File

@ -0,0 +1,26 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'tencent_unionid_resp.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
TencentUnionidResp _$TencentUnionidRespFromJson(Map json) {
return TencentUnionidResp(
error: json['error'] as int ?? 0,
errorDescription: json['error_description'] as String,
clientId: json['client_id'] as String,
openid: json['openid'] as String,
unionid: json['unionid'] as String,
);
}
Map<String, dynamic> _$TencentUnionidRespToJson(TencentUnionidResp instance) =>
<String, dynamic>{
'error': instance.error,
'error_description': instance.errorDescription,
'client_id': instance.clientId,
'openid': instance.openid,
'unionid': instance.unionid,
};

View File

@ -0,0 +1,99 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:tencent_kit/src/model/api/tencent_api_resp.dart';
part 'tencent_user_info_resp.g.dart';
@JsonSerializable(
anyMap: true,
explicitToJson: true,
fieldRename: FieldRename.snake,
)
class TencentUserInfoResp extends TencentApiResp {
TencentUserInfoResp({
int ret,
String msg,
this.isLost,
this.nickname,
this.gender,
this.genderType,
this.province,
this.city,
this.year,
this.constellation,
this.figureurl,
this.figureurl1,
this.figureurl2,
this.figureurlQq,
this.figureurlQq1,
this.figureurlQq2,
this.figureurlType,
this.isYellowVip,
this.vip,
this.yellowVipLevel,
this.level,
this.isYellowYearVip,
}) : super(ret: ret, msg: msg);
factory TencentUserInfoResp.fromJson(Map<dynamic, dynamic> json) =>
_$TencentUserInfoRespFromJson(json);
final int isLost;
final String nickname;
final String gender; // 男/女
final int genderType; // 男/女 - 1
final String province;
final String city;
final String year;
final String constellation;
final String figureurl;
@JsonKey(
name: 'figureurl_1',
)
final String figureurl1;
@JsonKey(
name: 'figureurl_2',
)
final String figureurl2;
final String figureurlQq; // 140 * 140
@JsonKey(
name: 'figureurl_qq_1',
)
final String figureurlQq1; // 大小为40×40像素的QQ头像URL。
@JsonKey(
name: 'figureurl_qq_2',
)
final String
figureurlQq2; // 大小为100×100像素的QQ头像URL。需要注意不是所有的用户都拥有QQ的100x100的头像但40x40像素则是一定会有。
final String figureurlType;
final String isYellowVip;
final String vip;
final String yellowVipLevel;
final String level;
final String isYellowYearVip;
bool isMale() {
return gender == '';
}
bool isFemale() {
return gender == '';
}
String headImgUrl() {
if (figureurlQq2 != null && figureurlQq2.isNotEmpty) {
return figureurlQq2;
}
if (figureurlQq1 != null && figureurlQq1.isNotEmpty) {
return figureurlQq1;
}
if (figureurl2 != null && figureurl2.isNotEmpty) {
return figureurl2;
}
if (figureurl1 != null && figureurl1.isNotEmpty) {
return figureurl1;
}
return figureurl;
}
Map<dynamic, dynamic> toJson() => _$TencentUserInfoRespToJson(this);
}

View File

@ -0,0 +1,61 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'tencent_user_info_resp.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
TencentUserInfoResp _$TencentUserInfoRespFromJson(Map json) {
return TencentUserInfoResp(
ret: json['ret'] as int ?? 0,
msg: json['msg'] as String,
isLost: json['is_lost'] as int,
nickname: json['nickname'] as String,
gender: json['gender'] as String,
genderType: json['gender_type'] as int,
province: json['province'] as String,
city: json['city'] as String,
year: json['year'] as String,
constellation: json['constellation'] as String,
figureurl: json['figureurl'] as String,
figureurl1: json['figureurl_1'] as String,
figureurl2: json['figureurl_2'] as String,
figureurlQq: json['figureurl_qq'] as String,
figureurlQq1: json['figureurl_qq_1'] as String,
figureurlQq2: json['figureurl_qq_2'] as String,
figureurlType: json['figureurl_type'] as String,
isYellowVip: json['is_yellow_vip'] as String,
vip: json['vip'] as String,
yellowVipLevel: json['yellow_vip_level'] as String,
level: json['level'] as String,
isYellowYearVip: json['is_yellow_year_vip'] as String,
);
}
Map<String, dynamic> _$TencentUserInfoRespToJson(
TencentUserInfoResp instance) =>
<String, dynamic>{
'ret': instance.ret,
'msg': instance.msg,
'is_lost': instance.isLost,
'nickname': instance.nickname,
'gender': instance.gender,
'gender_type': instance.genderType,
'province': instance.province,
'city': instance.city,
'year': instance.year,
'constellation': instance.constellation,
'figureurl': instance.figureurl,
'figureurl_1': instance.figureurl1,
'figureurl_2': instance.figureurl2,
'figureurl_qq': instance.figureurlQq,
'figureurl_qq_1': instance.figureurlQq1,
'figureurl_qq_2': instance.figureurlQq2,
'figureurl_type': instance.figureurlType,
'is_yellow_vip': instance.isYellowVip,
'vip': instance.vip,
'yellow_vip_level': instance.yellowVipLevel,
'level': instance.level,
'is_yellow_year_vip': instance.isYellowYearVip,
};

View File

@ -0,0 +1,34 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:tencent_kit/src/model/sdk/tencent_sdk_resp.dart';
part 'tencent_login_resp.g.dart';
@JsonSerializable(
anyMap: true,
explicitToJson: true,
fieldRename: FieldRename.snake,
)
class TencentLoginResp extends TencentSdkResp {
TencentLoginResp({
int ret,
String msg,
this.openid,
this.accessToken,
this.expiresIn,
this.createAt,
}) : super(ret: ret, msg: msg);
factory TencentLoginResp.fromJson(Map<dynamic, dynamic> json) =>
_$TencentLoginRespFromJson(json);
final String openid;
final String accessToken;
final int expiresIn;
final int createAt;
bool isExpired() {
return DateTime.now().millisecondsSinceEpoch - createAt >= expiresIn * 1000;
}
Map<dynamic, dynamic> toJson() => _$TencentLoginRespToJson(this);
}

View File

@ -0,0 +1,28 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'tencent_login_resp.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
TencentLoginResp _$TencentLoginRespFromJson(Map json) {
return TencentLoginResp(
ret: json['ret'] as int ?? 0,
msg: json['msg'] as String,
openid: json['openid'] as String,
accessToken: json['access_token'] as String,
expiresIn: json['expires_in'] as int,
createAt: json['create_at'] as int,
);
}
Map<String, dynamic> _$TencentLoginRespToJson(TencentLoginResp instance) =>
<String, dynamic>{
'ret': instance.ret,
'msg': instance.msg,
'openid': instance.openid,
'access_token': instance.accessToken,
'expires_in': instance.expiresIn,
'create_at': instance.createAt,
};

View File

@ -1,8 +1,10 @@
abstract class TencentResp {
TencentResp({
int ret,
import 'package:json_annotation/json_annotation.dart';
abstract class TencentSdkResp {
TencentSdkResp({
this.ret,
this.msg,
}) : ret = ret ?? RET_SUCCESS;
});
///
///
@ -15,6 +17,11 @@ abstract class TencentResp {
static const int RET_USERCANCEL = -2;
@JsonKey(
defaultValue: RET_SUCCESS,
)
final int ret;
final String msg;
bool isSuccessful() => ret == RET_SUCCESS;
}

View File

@ -0,0 +1,21 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:tencent_kit/src/model/sdk/tencent_sdk_resp.dart';
part 'tencent_share_resp.g.dart';
@JsonSerializable(
anyMap: true,
explicitToJson: true,
fieldRename: FieldRename.snake,
)
class TencentShareResp extends TencentSdkResp {
TencentShareResp({
int ret,
String msg,
}) : super(ret: ret, msg: msg);
factory TencentShareResp.fromJson(Map<dynamic, dynamic> json) =>
_$TencentShareRespFromJson(json);
Map<dynamic, dynamic> toJson() => _$TencentShareRespToJson(this);
}

View File

@ -0,0 +1,20 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'tencent_share_resp.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
TencentShareResp _$TencentShareRespFromJson(Map json) {
return TencentShareResp(
ret: json['ret'] as int ?? 0,
msg: json['msg'] as String,
);
}
Map<String, dynamic> _$TencentShareRespToJson(TencentShareResp instance) =>
<String, dynamic>{
'ret': instance.ret,
'msg': instance.msg,
};

View File

@ -1,39 +1,37 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:fake_tencent/src/domain/tencent_login_resp.dart';
import 'package:fake_tencent/src/domain/tencent_share_resp.dart';
import 'package:fake_tencent/src/domain/tencent_user_info_resp.dart';
import 'package:fake_tencent/src/tencent_qzone_flag.dart';
import 'package:fake_tencent/src/tencent_scene.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:meta/meta.dart';
import 'package:tencent_kit/src/model/api/tencent_unionid_resp.dart';
import 'package:tencent_kit/src/model/api/tencent_user_info_resp.dart';
import 'package:tencent_kit/src/model/sdk/tencent_login_resp.dart';
import 'package:tencent_kit/src/model/sdk/tencent_share_resp.dart';
import 'package:tencent_kit/src/tencent_constant.dart';
///
class Tencent {
///
Tencent() {
_channel.setMethodCallHandler(_handleMethod);
}
static const String _METHOD_REGISTERAPP = 'registerApp';
static const String _METHOD_ISQQINSTALLED = 'isQQInstalled';
static const String _METHOD_ISQQSUPPORTSSOLOGIN = 'isQQSupportSSOLogin';
static const String _METHOD_ISINSTALLED = 'isInstalled';
static const String _METHOD_LOGIN = 'login';
static const String _METHOD_LOGOUT = 'logout';
static const String _METHOD_GETUSERINFO = 'getUserInfo';
static const String _METHOD_SHAREMOOD = 'shareMood';
static const String _METHOD_SHAREIMAGE = 'shareImage';
static const String _METHOD_SHAREMUSIC = 'shareMusic';
static const String _METHOD_SHAREWEBPAGE = 'shareWebpage';
static const String _METHOD_ONLOGINRESP = 'onLoginResp';
static const String _METHOD_ONGETUSERINFORESP = 'onGetUserInfoResp';
static const String _METHOD_ONSHARERESP = "onShareResp";
static const String _ARGUMENT_KEY_APPID = 'appId';
static const String _ARGUMENT_KEY_UNIVERSALLINK = 'universalLink';
static const String _ARGUMENT_KEY_SCOPE = 'scope';
static const String _ARGUMENT_KEY_OPENID = 'openId';
static const String _ARGUMENT_KEY_ACCESSTOKEN = 'accessToken';
static const String _ARGUMENT_KEY_EXPIRESIN = 'expiresIn';
static const String _ARGUMENT_KEY_CREATEAT = 'createAt';
static const String _ARGUMENT_KEY_SCENE = 'scene';
static const String _ARGUMENT_KEY_TITLE = 'title';
static const String _ARGUMENT_KEY_SUMMARY = 'summary';
@ -48,67 +46,62 @@ class Tencent {
static const String _SCHEME_FILE = 'file';
final MethodChannel _channel =
const MethodChannel('v7lin.github.io/fake_tencent');
const MethodChannel('v7lin.github.io/tencent_kit');
final StreamController<TencentLoginResp> _loginRespStreamController =
StreamController<TencentLoginResp>.broadcast();
final StreamController<TencentUserInfoResp> _userInfoRespStreamController =
StreamController<TencentUserInfoResp>.broadcast();
final StreamController<TencentShareResp> _shareRespStreamController =
StreamController<TencentShareResp>.broadcast();
Future<void> registerApp({
@required String appId,
}) {
assert(appId != null && appId.isNotEmpty);
return _channel.invokeMethod(
_METHOD_REGISTERAPP,
<String, dynamic>{
_ARGUMENT_KEY_APPID: appId,
},
);
}
Future<dynamic> _handleMethod(MethodCall call) async {
switch (call.method) {
case _METHOD_ONLOGINRESP:
_loginRespStreamController.add(TencentLoginRespSerializer()
.fromMap(call.arguments as Map<dynamic, dynamic>));
break;
case _METHOD_ONGETUSERINFORESP:
_userInfoRespStreamController.add(TencentUserInfoRespSerializer()
.fromMap(call.arguments as Map<dynamic, dynamic>));
_loginRespStreamController.add(
TencentLoginResp.fromJson(call.arguments as Map<dynamic, dynamic>));
break;
case _METHOD_ONSHARERESP:
_shareRespStreamController.add(TencentShareRespSerializer()
.fromMap(call.arguments as Map<dynamic, dynamic>));
_shareRespStreamController.add(
TencentShareResp.fromJson(call.arguments as Map<dynamic, dynamic>));
break;
}
}
/// 向 Open_SDK 注册
Future<void> registerApp({
@required String appId,
String universalLink,
}) {
assert(appId != null && appId.isNotEmpty);
assert(universalLink == null || universalLink.isNotEmpty);
final Map<String, dynamic> map = <String, dynamic>{
_ARGUMENT_KEY_APPID: appId,
// _ARGUMENT_KEY_UNIVERSALLINK: universalLink,
};
if (universalLink != null) {
map[_ARGUMENT_KEY_UNIVERSALLINK] = universalLink;
}
/// 兼容 iOS 空安全 -> NSNull
return _channel.invokeMethod(
_METHOD_REGISTERAPP,
map,
);
}
/// 登录
Stream<TencentLoginResp> loginResp() {
return _loginRespStreamController.stream;
}
/// 用户信息
Stream<TencentUserInfoResp> userInfoResp() {
return _userInfoRespStreamController.stream;
}
/// 分享
Stream<TencentShareResp> shareResp() {
return _shareRespStreamController.stream;
}
Future<bool> isQQInstalled() async {
return (await _channel.invokeMethod(_METHOD_ISQQINSTALLED)) as bool;
}
Future<bool> isQQSupportSSOLogin() async {
return (await _channel.invokeMethod(_METHOD_ISQQSUPPORTSSOLOGIN)) as bool;
/// 检查QQ是否已安装
Future<bool> isInstalled() async {
return (await _channel.invokeMethod(_METHOD_ISINSTALLED)) as bool;
}
/// 登录
@ -130,24 +123,62 @@ class Tencent {
}
/// 用户信息
Future<void> getUserInfo({
@required String openId,
/// https://wiki.connect.qq.com/get_user_info
Future<TencentUserInfoResp> getUserInfo({
@required String appId,
@required String openid,
@required String accessToken,
@required int expiresIn,
@required int createAt,
}) {
assert(openId != null && openId.isNotEmpty);
assert(appId != null && appId.isNotEmpty);
assert(openid != null && openid.isNotEmpty);
assert(accessToken != null && accessToken.isNotEmpty);
assert(expiresIn != null && expiresIn > 0);
return _channel.invokeMethod(
_METHOD_GETUSERINFO,
<String, dynamic>{
_ARGUMENT_KEY_OPENID: openId,
_ARGUMENT_KEY_ACCESSTOKEN: accessToken,
_ARGUMENT_KEY_EXPIRESIN: expiresIn,
_ARGUMENT_KEY_CREATEAT: createAt,
},
);
return HttpClient()
.getUrl(Uri.parse(
'https://graph.qq.com/user/get_user_info?access_token=$accessToken&oauth_consumer_key=$appId&openid=$openid'))
.then((HttpClientRequest request) {
return request.close();
}).then((HttpClientResponse response) async {
if (response.statusCode == HttpStatus.ok) {
ContentType contentType = response.headers.contentType;
Encoding encoding = Encoding.getByName(contentType?.charset) ?? utf8;
String content = await encoding.decodeStream(response);
return TencentUserInfoResp.fromJson(
json.decode(content) as Map<dynamic, dynamic>);
}
throw HttpException(
'HttpResponse statusCode: ${response.statusCode}, reasonPhrase: ${response.reasonPhrase}.');
});
}
/// UnionID
/// https://wiki.connect.qq.com/unionid%E4%BB%8B%E7%BB%8D
Future<TencentUnionidResp> getUnionId({
@required String accessToken,
String unionid = '1',
}) {
assert(accessToken != null && accessToken.isNotEmpty);
return HttpClient()
.getUrl(Uri.parse(
'https://graph.qq.com/oauth2.0/me?access_token=$accessToken&unionid=$unionid'))
.then((HttpClientRequest request) {
return request.close();
}).then((HttpClientResponse response) async {
if (response.statusCode == HttpStatus.ok) {
ContentType contentType = response.headers.contentType;
Encoding encoding = Encoding.getByName(contentType?.charset) ?? utf8;
String callback = await encoding.decodeStream(response);
// 腾讯有毒 callback( $json );
RegExp exp = RegExp(r'callback\( (.*) \)\;');
Match match = exp.firstMatch(callback);
if (match.groupCount == 1) {
String content = match.group(1);
return TencentUnionidResp.fromJson(
json.decode(content) as Map<dynamic, dynamic>);
}
}
throw HttpException(
'HttpResponse statusCode: ${response.statusCode}, reasonPhrase: ${response.reasonPhrase}.');
});
}
/// 分享 - 说说
@ -166,7 +197,7 @@ class Tencent {
assert(imageUri != null && imageUri.isScheme(_SCHEME_FILE));
});
}
Map<String, dynamic> map = <String, dynamic>{
final Map<String, dynamic> map = <String, dynamic>{
_ARGUMENT_KEY_SCENE: scene,
// _ARGUMENT_KEY_SUMMARY: summary,
// _ARGUMENT_KEY_IMAGEURIS: imageUris != null ? new List.generate(imageUris.length, (int index) {
@ -177,17 +208,14 @@ class Tencent {
/// 兼容 iOS 空安全 -> NSNull
if (summary != null && summary.isNotEmpty) {
map.putIfAbsent(_ARGUMENT_KEY_SUMMARY, () => summary);
map[_ARGUMENT_KEY_SUMMARY] = summary;
}
if (imageUris != null && imageUris.isNotEmpty) {
map.putIfAbsent(
_ARGUMENT_KEY_IMAGEURIS,
() => List<String>.generate(imageUris.length, (int index) {
return imageUris[index].toString();
}));
map[_ARGUMENT_KEY_IMAGEURIS] =
imageUris.map((Uri imageUri) => imageUri.toString()).toList();
}
if (videoUri != null) {
map.putIfAbsent(_ARGUMENT_KEY_VIDEOURI, () => videoUri.toString());
map[_ARGUMENT_KEY_VIDEOURI] = videoUri.toString();
}
return _channel.invokeMethod(_METHOD_SHAREMOOD, map);
}
@ -201,7 +229,7 @@ class Tencent {
}) {
assert(scene == TencentScene.SCENE_QQ);
assert(imageUri != null && imageUri.isScheme(_SCHEME_FILE));
Map<String, dynamic> map = <String, dynamic>{
final Map<String, dynamic> map = <String, dynamic>{
_ARGUMENT_KEY_SCENE: scene,
_ARGUMENT_KEY_IMAGEURI: imageUri.toString(),
// _ARGUMENT_KEY_APPNAME: appName,
@ -210,7 +238,7 @@ class Tencent {
/// 兼容 iOS 空安全 -> NSNull
if (appName != null && appName.isNotEmpty) {
map.putIfAbsent(_ARGUMENT_KEY_APPNAME, () => appName);
map[_ARGUMENT_KEY_APPNAME] = appName;
}
return _channel.invokeMethod(_METHOD_SHAREIMAGE, map);
}
@ -230,7 +258,7 @@ class Tencent {
assert(title != null && title.isNotEmpty);
assert(musicUrl != null && musicUrl.isNotEmpty);
assert(targetUrl != null && targetUrl.isNotEmpty);
Map<String, dynamic> map = <String, dynamic>{
final Map<String, dynamic> map = <String, dynamic>{
_ARGUMENT_KEY_SCENE: scene,
_ARGUMENT_KEY_TITLE: title,
// _ARGUMENT_KEY_SUMMARY: summary,
@ -243,13 +271,13 @@ class Tencent {
/// 兼容 iOS 空安全 -> NSNull
if (summary != null && summary.isNotEmpty) {
map.putIfAbsent(_ARGUMENT_KEY_SUMMARY, () => summary);
map[_ARGUMENT_KEY_SUMMARY] = summary;
}
if (imageUri != null) {
map.putIfAbsent(_ARGUMENT_KEY_IMAGEURI, () => imageUri.toString());
map[_ARGUMENT_KEY_IMAGEURI] = imageUri.toString();
}
if (appName != null && appName.isNotEmpty) {
map.putIfAbsent(_ARGUMENT_KEY_APPNAME, () => appName);
map[_ARGUMENT_KEY_APPNAME] = appName;
}
return _channel.invokeMethod(_METHOD_SHAREMUSIC, map);
}
@ -266,7 +294,7 @@ class Tencent {
}) {
assert(title != null && title.isNotEmpty);
assert(targetUrl != null && targetUrl.isNotEmpty);
Map<String, dynamic> map = <String, dynamic>{
final Map<String, dynamic> map = <String, dynamic>{
_ARGUMENT_KEY_SCENE: scene,
_ARGUMENT_KEY_TITLE: title,
// _ARGUMENT_KEY_SUMMARY: summary,
@ -278,13 +306,13 @@ class Tencent {
/// 兼容 iOS 空安全 -> NSNull
if (summary != null && summary.isNotEmpty) {
map.putIfAbsent(_ARGUMENT_KEY_SUMMARY, () => summary);
map[_ARGUMENT_KEY_SUMMARY] = summary;
}
if (imageUri != null) {
map.putIfAbsent(_ARGUMENT_KEY_IMAGEURI, () => imageUri.toString());
map[_ARGUMENT_KEY_IMAGEURI] = imageUri.toString();
}
if (appName != null && appName.isNotEmpty) {
map.putIfAbsent(_ARGUMENT_KEY_APPNAME, () => appName);
map[_ARGUMENT_KEY_APPNAME] = appName;
}
return _channel.invokeMethod(_METHOD_SHAREWEBPAGE, map);
}

View File

@ -43,3 +43,26 @@ class TencentScope {
///
static const String ALL = 'all';
}
class TencentScene {
TencentScene._();
/// QQ
static const int SCENE_QQ = 0;
/// QZone
static const int SCENE_QZONE = 1;
}
class TencentQZoneFlag {
TencentQZoneFlag._();
/// QZone按钮且不自动打开分享到QZone的对话框
static const int DEFAULT = 0;
/// QZone的对话框
static const int AUTO_OPEN = 1;
/// QZone按钮
static const int ITEM_HIDE = 2;
}

View File

@ -1,12 +0,0 @@
class TencentQZoneFlag {
TencentQZoneFlag._();
/// 默认是不隐藏分享到QZone按钮且不自动打开分享到QZone的对话框
static const int DEFAULT = 0;
/// 分享时自动打开分享到QZone的对话框
static const int AUTO_OPEN = 1;
/// 分享时隐藏分享到QZone按钮
static const int ITEM_HIDE = 2;
}

View File

@ -1,9 +0,0 @@
class TencentScene {
TencentScene._();
/// QQ
static const int SCENE_QQ = 0;
/// QZone
static const int SCENE_QZONE = 1;
}

10
lib/tencent_kit.dart Normal file
View File

@ -0,0 +1,10 @@
library tencent_kit;
export 'src/model/api/tencent_api_resp.dart';
export 'src/model/api/tencent_unionid_resp.dart';
export 'src/model/api/tencent_user_info_resp.dart';
export 'src/model/sdk/tencent_login_resp.dart';
export 'src/model/sdk/tencent_sdk_resp.dart';
export 'src/model/sdk/tencent_share_resp.dart';
export 'src/tencent.dart';
export 'src/tencent_constant.dart';

View File

@ -1,6 +1,6 @@
name: fake_tencent
description: A powerful tencent plugin for Flutter.
version: 0.3.4
name: tencent_kit
description: A powerful Flutter plugin allowing developers to share or authorize with natvie android & iOS Tencent SDKs.
version: 1.0.0
author: v7lin <v7lin@qq.com>
homepage: https://github.com/v7lin/fake_tencent
@ -11,24 +11,24 @@ dependencies:
flutter:
sdk: flutter
meta: '>=1.1.6 <2.0.0'
jaguar_serializer: '>=2.2.12 <3.0.0'
json_annotation: '>=2.0.0 <4.0.0'
dev_dependencies:
flutter_test:
sdk: flutter
pedantic: '>=1.4.0 <3.0.0'
path: ^1.6.4
path_provider: ^1.4.0
path: ^1.6.2
fake_path_provider: ^0.0.1
okhttp_kit: ^1.0.0
pedantic:
build_runner:
jaguar_serializer_cli:
json_serializable:
# For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
@ -37,8 +37,8 @@ flutter:
# be modified. They are used by the tooling to maintain consistency when
# adding or updating assets for this project.
plugin:
androidPackage: io.github.v7lin.faketencent
pluginClass: FakeTencentPlugin
androidPackage: io.github.v7lin.tencent_kit
pluginClass: TencentKitPlugin
# To add assets to your plugin package, add an assets section, like this:
# assets:
@ -46,10 +46,10 @@ flutter:
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.io/assets-and-images/#from-packages
# https://flutter.dev/assets-and-images/#from-packages
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.io/assets-and-images/#resolution-aware.
# https://flutter.dev/assets-and-images/#resolution-aware.
# To add custom fonts to your plugin package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
@ -69,4 +69,4 @@ flutter:
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.io/custom-fonts/#from-packages
# https://flutter.dev/custom-fonts/#from-packages

View File

@ -1 +0,0 @@
void main() {}

View File

@ -1,33 +0,0 @@
import 'dart:convert';
import 'package:fake_tencent/src/domain/tencent_login_resp.dart';
import 'package:fake_tencent/src/domain/tencent_share_resp.dart';
import 'package:fake_tencent/src/domain/tencent_user_info_resp.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
test('smoke test - jaguar_serializer', () {
TencentLoginResp loginResp = TencentLoginRespSerializer().fromMap(json.decode(
'{"ret":0,"access_token":"D3A4D352A06D897D6FD4DED2B8BEEA46","openid":"A13514A5E6385A1D56E1D845303C5FDE","expires_in":7776000}')
as Map<dynamic, dynamic>);
expect(loginResp.ret, equals(0));
expect(loginResp.openid, equals('A13514A5E6385A1D56E1D845303C5FDE'));
expect(loginResp.accessToken, equals('D3A4D352A06D897D6FD4DED2B8BEEA46'));
expect(loginResp.expiresIn, equals(7776000));
TencentUserInfoResp userInfoResp = TencentUserInfoRespSerializer().fromMap(
json.decode(
'{"ret":0,"msg":"","is_lost":1,"gender":"男","is_yellow_vip":"0","city":"福州","year":"1989","level":"0","figureurl_2":"http://qzapp.qlogo.cn/qzapp/222222/942FEA70050EEAFBD4DCE2C1FC775E56/100","figureurl_1":"http://qzapp.qlogo.cn/qzapp/222222/942FEA70050EEAFBD4DCE2C1FC775E56/50","is_yellow_year_vip":"0","province":"福建","constellation":"","figureurl":"http://qzapp.qlogo.cn/qzapp/222222/942FEA70050EEAFBD4DCE2C1FC775E56/30","figureurl_type":"1","figureurl_qq":"http://thirdqq.qlogo.cn/g?b=oidb&k=aicFesDxFa5P0dImvuYicSGw&s=140","nickname":"qquser","yellow_vip_level":"0","figureurl_qq_1":"http://thirdqq.qlogo.cn/g?b=oidb&k=aicFesDxFa5P0dImvuYicSGw&s=40","vip":"0","figureurl_qq_2":"http://thirdqq.qlogo.cn/g?b=oidb&k=aicFesDxFa5P0dImvuYicSGw&s=100"}')
as Map<dynamic, dynamic>);
expect(userInfoResp.ret, equals(0));
expect(userInfoResp.nickname, equals('qquser'));
expect(
userInfoResp.figureurlQq1,
equals(
'http://thirdqq.qlogo.cn/g?b=oidb&k=aicFesDxFa5P0dImvuYicSGw&s=40'));
TencentShareResp shareResp = TencentShareRespSerializer()
.fromMap(json.decode('{"msg":""}') as Map<dynamic, dynamic>);
expect(shareResp.ret, equals(0));
});
}

View File

@ -0,0 +1,18 @@
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
const MethodChannel channel = MethodChannel('v7lin.github.io/tencent_kit');
setUp(() {
channel.setMockMethodCallHandler((MethodCall methodCall) async {});
});
tearDown(() {
channel.setMockMethodCallHandler(null);
});
test('smoke test', () async {});
}