fake_weibo 0.1.0

This commit is contained in:
v7lin 2019-03-19 19:06:18 +08:00
parent 9538c6faa8
commit 59d1407e74
17 changed files with 576 additions and 574 deletions

View File

@ -74,7 +74,7 @@ dependencies:
### release
````
latestVersion = 0.0.1+1
latestVersion = 0.1.0
````
````

View File

@ -41,7 +41,7 @@ public class FakeWeiboPlugin implements MethodCallHandler, PluginRegistry.Activi
channel.setMethodCallHandler(plugin);
}
private static class FakeWeiboErrorCode {
private static class WeiboErrorCode {
public static final int SUCCESS = 0;//
public static final int USERCANCEL = -1;//
public static final int SENT_FAIL = -2;//
@ -79,7 +79,7 @@ public class FakeWeiboPlugin implements MethodCallHandler, PluginRegistry.Activi
private static final String ARGUMENT_KEY_RESULT_USERID = "userId";
private static final String ARGUMENT_KEY_RESULT_ACCESSTOKEN = "accessToken";
private static final String ARGUMENT_KEY_RESULT_REFRESHTOKEN = "refreshToken";
private static final String ARGUMENT_KEY_RESULT_EXPIRATIONDATE = "expirationDate";
private static final String ARGUMENT_KEY_RESULT_EXPIRESIN = "expiresIn";
private final Registrar registrar;
private final MethodChannel channel;
@ -125,13 +125,14 @@ public class FakeWeiboPlugin implements MethodCallHandler, PluginRegistry.Activi
public void onSuccess(Oauth2AccessToken oauth2AccessToken) {
Map<String, Object> map = new HashMap<>();
if (oauth2AccessToken.isSessionValid()) {
map.put(ARGUMENT_KEY_RESULT_ERRORCODE, FakeWeiboErrorCode.SUCCESS);
map.put(ARGUMENT_KEY_RESULT_ERRORCODE, WeiboErrorCode.SUCCESS);
map.put(ARGUMENT_KEY_RESULT_USERID, oauth2AccessToken.getUid());
map.put(ARGUMENT_KEY_RESULT_ACCESSTOKEN, oauth2AccessToken.getToken());
map.put(ARGUMENT_KEY_RESULT_REFRESHTOKEN, oauth2AccessToken.getRefreshToken());
map.put(ARGUMENT_KEY_RESULT_EXPIRATIONDATE, oauth2AccessToken.getExpiresTime());
long expiresIn = (long) Math.ceil((oauth2AccessToken.getExpiresTime() - System.currentTimeMillis()) / 1000.0);
map.put(ARGUMENT_KEY_RESULT_EXPIRESIN, expiresIn);//
} else {
map.put(ARGUMENT_KEY_RESULT_ERRORCODE, FakeWeiboErrorCode.UNKNOWN);
map.put(ARGUMENT_KEY_RESULT_ERRORCODE, WeiboErrorCode.UNKNOWN);
}
channel.invokeMethod(METHOD_ONAUTHRESP, map);
}
@ -139,7 +140,7 @@ public class FakeWeiboPlugin implements MethodCallHandler, PluginRegistry.Activi
@Override
public void cancel() {
Map<String, Object> map = new HashMap<>();
map.put(ARGUMENT_KEY_RESULT_ERRORCODE, FakeWeiboErrorCode.USERCANCEL);
map.put(ARGUMENT_KEY_RESULT_ERRORCODE, WeiboErrorCode.USERCANCEL);
channel.invokeMethod(METHOD_ONAUTHRESP, map);
}
@ -148,12 +149,12 @@ public class FakeWeiboPlugin implements MethodCallHandler, PluginRegistry.Activi
// WbConnectErrorMessage对象两个属性设置错误
if (TextUtils.equals(wbConnectErrorMessage.getErrorMessage(), "21338")) {
Map<String, Object> map = new HashMap<>();
map.put(ARGUMENT_KEY_RESULT_ERRORCODE, FakeWeiboErrorCode.SSO_PKG_SIGN_ERROR);
map.put(ARGUMENT_KEY_RESULT_ERRORCODE, WeiboErrorCode.SSO_PKG_SIGN_ERROR);
map.put(ARGUMENT_KEY_RESULT_ERRORMESSAGE, wbConnectErrorMessage.getErrorCode());
channel.invokeMethod(METHOD_ONAUTHRESP, map);
} else {
Map<String, Object> map = new HashMap<>();
map.put(ARGUMENT_KEY_RESULT_ERRORCODE, FakeWeiboErrorCode.UNKNOWN);
map.put(ARGUMENT_KEY_RESULT_ERRORCODE, WeiboErrorCode.UNKNOWN);
channel.invokeMethod(METHOD_ONAUTHRESP, map);
}
}
@ -218,21 +219,21 @@ public class FakeWeiboPlugin implements MethodCallHandler, PluginRegistry.Activi
@Override
public void onWbShareSuccess() {
Map<String, Object> map = new HashMap<>();
map.put(ARGUMENT_KEY_RESULT_ERRORCODE, FakeWeiboErrorCode.SUCCESS);
map.put(ARGUMENT_KEY_RESULT_ERRORCODE, WeiboErrorCode.SUCCESS);
channel.invokeMethod(METHOD_ONSHAREMSGRESP, map);
}
@Override
public void onWbShareCancel() {
Map<String, Object> map = new HashMap<>();
map.put(ARGUMENT_KEY_RESULT_ERRORCODE, FakeWeiboErrorCode.USERCANCEL);
map.put(ARGUMENT_KEY_RESULT_ERRORCODE, WeiboErrorCode.USERCANCEL);
channel.invokeMethod(METHOD_ONSHAREMSGRESP, map);
}
@Override
public void onWbShareFail() {
Map<String, Object> map = new HashMap<>();
map.put(ARGUMENT_KEY_RESULT_ERRORCODE, FakeWeiboErrorCode.SHARE_IN_SDK_FAILED);
map.put(ARGUMENT_KEY_RESULT_ERRORCODE, WeiboErrorCode.SHARE_IN_SDK_FAILED);
channel.invokeMethod(METHOD_ONSHAREMSGRESP, map);
}
});

View File

@ -8,7 +8,6 @@
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 2D5378251FAA1A9400D5DBA9 /* flutter_assets */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@ -41,7 +40,6 @@
/* 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>"; };
2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = Flutter/flutter_assets; sourceTree = SOURCE_ROOT; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
@ -83,7 +81,6 @@
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
2D5378251FAA1A9400D5DBA9 /* flutter_assets */,
3B80C3931E831B6300D905FE /* App.framework */,
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEBA1CF902C7004384FC /* Flutter.framework */,
@ -213,7 +210,6 @@
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@ -6,6 +6,9 @@ import 'package:flutter/services.dart';
import 'package:fake_weibo/fake_weibo.dart';
const String _WEIBO_APP_KEY = '3393861383';
const List<String> _WEIBO_SCOPE = <String>[
WeiboScope.ALL,
];
void main() {
runZoned(() {
@ -25,14 +28,12 @@ void main() {
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
FakeWeibo weibo = FakeWeibo(
appKey: _WEIBO_APP_KEY,
scope: [
FakeWeiboScope.ALL,
],
);
weibo.registerApp();
return FakeWeiboProvider(
Weibo weibo = Weibo()
..registerApp(
appKey: _WEIBO_APP_KEY,
scope: _WEIBO_SCOPE,
);
return WeiboProvider(
weibo: weibo,
child: MaterialApp(
home: Home(
@ -46,7 +47,7 @@ class MyApp extends StatelessWidget {
class Home extends StatefulWidget {
Home({Key key, @required this.weibo}) : super(key: key);
final FakeWeibo weibo;
final Weibo weibo;
@override
State<StatefulWidget> createState() {
@ -55,10 +56,10 @@ class Home extends StatefulWidget {
}
class _HomeState extends State<Home> {
StreamSubscription<FakeWeiboAuthResp> _auth;
StreamSubscription<FakeWeiboShareMsgResp> _share;
StreamSubscription<WeiboAuthResp> _auth;
StreamSubscription<WeiboSdkResp> _share;
FakeWeiboAuthResp _authResp;
WeiboAuthResp _authResp;
@override
void initState() {
@ -67,13 +68,13 @@ class _HomeState extends State<Home> {
_share = widget.weibo.shareMsgResp().listen(_listenShareMsg);
}
void _listenAuth(FakeWeiboAuthResp resp) {
void _listenAuth(WeiboAuthResp resp) {
_authResp = resp;
String content = 'auth: ${resp.errorCode}';
_showTips('登录', content);
}
void _listenShareMsg(FakeWeiboShareMsgResp resp) {
void _listenShareMsg(WeiboSdkResp resp) {
String content = 'share: ${resp.errorCode}';
_showTips('分享', content);
}
@ -108,26 +109,29 @@ class _HomeState extends State<Home> {
ListTile(
title: Text('登录'),
onTap: () {
widget.weibo.auth();
widget.weibo.auth(
appKey: _WEIBO_APP_KEY,
scope: _WEIBO_SCOPE,
);
},
),
ListTile(
title: Text('用户信息'),
onTap: () async {
if (_authResp != null &&
_authResp.errorCode == FakeWeiboErrorCode.SUCCESS) {
FakeWeiboApiUserResp userResp = await widget.weibo.getUserInfo(
appkey: _WEIBO_APP_KEY,
userId: _authResp.userId,
accessToken: _authResp.accessToken);
if (userResp != null &&
userResp.errorCode ==
FakeWeiboApiBaseResp.ERROR_CODE_SUCCESS) {
_authResp.errorCode == WeiboSdkResp.SUCCESS) {
WeiboUserInfoResp userInfoResp = await widget.weibo.getUserInfo(
appkey: _WEIBO_APP_KEY,
userId: _authResp.userId,
accessToken: _authResp.accessToken,
);
if (userInfoResp != null &&
userInfoResp.errorCode == WeiboApiResp.ERROR_CODE_SUCCESS) {
_showTips('用户信息',
'${userResp.screenName}\n${userResp.description}\n${userResp.location}\n${userResp.profileImageUrl}');
'${userInfoResp.screenName}\n${userInfoResp.description}\n${userInfoResp.location}\n${userInfoResp.profileImageUrl}');
} else {
_showTips('用户信息',
'获取用户信息失败\n${userResp.errorCode}:${userResp.error}');
'获取用户信息失败\n${userInfoResp.errorCode}:${userInfoResp.error}');
}
}
},

View File

@ -43,7 +43,7 @@ static NSString * const ARGUMENT_KEY_RESULT_ERRORMESSAGE = @"errorMessage";
static NSString * const ARGUMENT_KEY_RESULT_USERID = @"userId";
static NSString * const ARGUMENT_KEY_RESULT_ACCESSTOKEN = @"accessToken";
static NSString * const ARGUMENT_KEY_RESULT_REFRESHTOKEN = @"refreshToken";
static NSString * const ARGUMENT_KEY_RESULT_EXPIRATIONDATE = @"expirationDate";
static NSString * const ARGUMENT_KEY_RESULT_EXPIRESIN = @"expiresIn";
-(instancetype)initWithChannel:(FlutterMethodChannel *)channel {
self = [super init];
@ -145,11 +145,11 @@ static NSString * const ARGUMENT_KEY_RESULT_EXPIRATIONDATE = @"expirationDate";
NSString * userId = authorizeResponse.userID;
NSString * accessToken = authorizeResponse.accessToken;
NSString * refreshToken = authorizeResponse.refreshToken;
long long expirationDate = authorizeResponse.expirationDate.timeIntervalSince1970 * 1000;
long long expiresIn = ceil(authorizeResponse.expirationDate.timeIntervalSinceNow);//
[dictionary setValue:userId forKey:ARGUMENT_KEY_RESULT_USERID];
[dictionary setValue:accessToken forKey:ARGUMENT_KEY_RESULT_ACCESSTOKEN];
[dictionary setValue:refreshToken forKey:ARGUMENT_KEY_RESULT_REFRESHTOKEN];
[dictionary setValue:[NSNumber numberWithLongLong:expirationDate] forKey:ARGUMENT_KEY_RESULT_EXPIRATIONDATE];
[dictionary setValue:[NSNumber numberWithLongLong:expiresIn] forKey:ARGUMENT_KEY_RESULT_EXPIRESIN];
}
[_channel invokeMethod:METHOD_ONAUTHRESP arguments:dictionary];
} else if ([response isKindOfClass:[WBSendMessageToWeiboResponse class]]) {

View File

@ -1,529 +1,9 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart';
class FakeWeiboScope {
FakeWeiboScope._();
static const String EMAIL = 'email';
static const String DIRECT_MESSAGES_READ = 'direct_messages_read';
static const String DIRECT_MESSAGES_WRITE = 'direct_messages_write';
static const String FRIENDSHIPS_GROUPS_READ = 'friendships_groups_read';
static const String FRIENDSHIPS_GROUPS_WRITE = 'friendships_groups_write';
static const String STATUSES_TO_ME_READ = 'statuses_to_me_read';
static const String FOLLOW_APP_OFFICIAL_MICROBLOG =
'follow_app_official_microblog';
static const String INVITATION_WRITE = 'invitation_write';
static const String ALL = 'all';
}
class FakeWeiboErrorCode {
FakeWeiboErrorCode._();
///
static const int SUCCESS = 0;
///
static const int USERCANCEL = -1;
///
static const int SENT_FAIL = -2;
///
static const int AUTH_DENY = -3;
///
static const int USERCANCEL_INSTALL = -4;
///
static const int PAY_FAIL = -5;
/// response UserInfo
static const int SHARE_IN_SDK_FAILED = -8;
///
static const int UNSUPPORT = -99;
///
static const int UNKNOWN = -100;
/// sso package or sign error
static const int SSO_PKG_SIGN_ERROR = 21338;
}
abstract class FakeWeiboBaseResp {
FakeWeiboBaseResp({
@required this.errorCode,
this.errorMessage,
});
final int errorCode;
final String errorMessage;
}
class FakeWeiboAuthResp extends FakeWeiboBaseResp {
FakeWeiboAuthResp._({
@required int errorCode,
String errorMessage,
this.userId,
this.accessToken,
this.refreshToken,
this.expirationDate,
}) : super(
errorCode: errorCode,
errorMessage: errorMessage,
);
final String userId;
final String accessToken;
final String refreshToken;
final int expirationDate;
}
class FakeWeiboShareMsgResp extends FakeWeiboBaseResp {
FakeWeiboShareMsgResp._({
@required int errorCode,
String errorMessage,
}) : super(
errorCode: errorCode,
errorMessage: errorMessage,
);
}
abstract class FakeWeiboApiBaseResp {
FakeWeiboApiBaseResp({
@required this.errorCode,
this.error,
this.request,
});
static const String KEY_ERROR_CODE = 'error_code';
static const String KEY_ERROR = 'error';
static const String KEY_REQUEST = 'request';
static const int ERROR_CODE_SUCCESS = 0;
/// https://open.weibo.com/wiki/Help/error
final int errorCode;
final String error;
final String request;
}
class FakeWeiboApiUserResp extends FakeWeiboApiBaseResp {
FakeWeiboApiUserResp._({
int errorCode,
String error,
String request,
this.id,
this.idstr,
this.screenName,
this.name,
this.province,
this.city,
this.location,
this.description,
this.url,
this.profileImageUrl,
this.profileUrl,
this.domain,
this.weihao,
this.gender,
this.verified,
this.avatarLarge,
this.avatarHD,
}) : super(errorCode: errorCode, error: error, request: request);
static const String KEY_ID = 'id';
static const String KEY_ID_STR = 'idstr';
static const String KEY_SCREEN_NAME = 'screen_name';
static const String KEY_NAME = 'name';
static const String KEY_PROVINCE = 'province';
static const String KEY_CITY = 'city';
static const String KEY_LOCATION = 'location';
static const String KEY_DESCRIPTION = 'description';
static const String KEY_URL = 'url';
static const String KEY_PROFILE_IMAGE_URL = 'profile_image_url';
static const String KEY_PROFILE_URL = 'profile_url';
static const String KEY_DOMAIN = 'domain';
static const String KEY_WEIHAO = 'weihao';
static const String KEY_GENDER = 'gender';
static const String KEY_VERIFIED = 'verified';
static const String KEY_AVATAR_LARGE = 'avatar_large';
static const String KEY_AVATAR_HD = 'avatar_hd';
/// UIDint64
final int id;
/// UID
final String idstr;
///
final String screenName;
///
final String name;
/// ID
final String province;
/// ID
final String city;
///
final String location;
///
final String description;
///
final String url;
/// 50×50
final String profileImageUrl;
/// URL地址
final String profileUrl;
///
final String domain;
///
final String weihao;
/// mfn
final String gender;
// ///
// final int followers_count;
// ///
// final int friends_count;
// ///
// final int statuses_count;
// ///
// final int favourites_count;
// ///
// final String created_at;
// ///
// final bool following;
// /// truefalse
// final bool allow_all_act_msg;
// /// truefalse
// final bool geo_enabled;
/// V用户truefalse
final bool verified;
// ///
// final int verified_type;
// ///
// final String remark;
// ///
// final dynamic status;
// /// truefalse
// final bool allow_all_comment;
///
final String avatarLarge;
///
final String avatarHD;
// ///
// final String verified_reason;
// /// truefalse
// final bool follow_me;
// /// 线0线1线
// final int online_status;
// ///
// final int bi_followers_count;
// /// zh-cnzh-twen
// final String lang;
// /// OpenAPI
// final String star;
// final String mbtype;
// final String mbrank;
// final String block_word;
}
class FakeWeibo {
FakeWeibo({
@required String appKey,
@required List<String> scope,
String redirectUrl = _DEFAULT_REDIRECTURL,
}) : assert(appKey != null && appKey.isNotEmpty),
assert(scope != null && scope.isNotEmpty),
_appKey = appKey,
_scope = scope,
_redirectUrl = redirectUrl;
static const String _METHOD_REGISTERAPP = 'registerApp';
static const String _METHOD_ISWEIBOINSTALLED = 'isWeiboInstalled';
static const String _METHOD_AUTH = 'auth';
static const String _METHOD_SHARETEXT = 'shareText';
static const String _METHOD_SHAREIMAGE = 'shareImage';
static const String _METHOD_SHAREWEBPAGE = 'shareWebpage';
static const String _METHOD_ONAUTHRESP = 'onAuthResp';
static const String _METHOD_ONSHAREMSGRESP = 'onShareMsgResp';
static const String _ARGUMENT_KEY_APPKEY = 'appKey';
static const String _ARGUMENT_KEY_SCOPE = 'scope';
static const String _ARGUMENT_KEY_REDIRECTURL = 'redirectUrl';
static const String _ARGUMENT_KEY_TEXT = 'text';
static const String _ARGUMENT_KEY_TITLE = 'title';
static const String _ARGUMENT_KEY_DESCRIPTION = 'description';
static const String _ARGUMENT_KEY_THUMBDATA = 'thumbData';
static const String _ARGUMENT_KEY_IMAGEDATA = 'imageData';
static const String _ARGUMENT_KEY_WEBPAGEURL = 'webpageUrl';
static const String _ARGUMENT_KEY_RESULT_ERRORCODE = "errorCode";
static const String _ARGUMENT_KEY_RESULT_ERRORMESSAGE = "errorMessage";
static const String _ARGUMENT_KEY_RESULT_USERID = 'userId';
static const String _ARGUMENT_KEY_RESULT_ACCESSTOKEN = 'accessToken';
static const String _ARGUMENT_KEY_RESULT_REFRESHTOKEN = 'refreshToken';
static const String _ARGUMENT_KEY_RESULT_EXPIRATIONDATE = 'expirationDate';
static const String _DEFAULT_REDIRECTURL =
'https://api.weibo.com/oauth2/default.html';
static const MethodChannel _channel =
MethodChannel('v7lin.github.io/fake_weibo');
final String _appKey;
final List<String> _scope;
/// -> -> -> -> OAuth2.0
final String _redirectUrl;
final StreamController<FakeWeiboAuthResp> _authRespStreamController =
StreamController<FakeWeiboAuthResp>.broadcast();
final StreamController<FakeWeiboShareMsgResp> _shareMsgRespStreamController =
StreamController<FakeWeiboShareMsgResp>.broadcast();
Future<void> registerApp() {
_channel.setMethodCallHandler(_handleMethod);
return _channel.invokeMethod(
_METHOD_REGISTERAPP,
<String, dynamic>{
_ARGUMENT_KEY_APPKEY: _appKey,
_ARGUMENT_KEY_SCOPE: _scope.join(','),
_ARGUMENT_KEY_REDIRECTURL: _redirectUrl,
},
);
}
Future<dynamic> _handleMethod(MethodCall call) async {
switch (call.method) {
case _METHOD_ONAUTHRESP:
_authRespStreamController.add(FakeWeiboAuthResp._(
errorCode: call.arguments[_ARGUMENT_KEY_RESULT_ERRORCODE] as int,
errorMessage: call.arguments[_ARGUMENT_KEY_RESULT_ERRORMESSAGE] as String,
userId: call.arguments[_ARGUMENT_KEY_RESULT_USERID] as String,
accessToken:
call.arguments[_ARGUMENT_KEY_RESULT_ACCESSTOKEN] as String,
refreshToken:
call.arguments[_ARGUMENT_KEY_RESULT_REFRESHTOKEN] as String,
expirationDate:
call.arguments[_ARGUMENT_KEY_RESULT_EXPIRATIONDATE] as int,
));
break;
case _METHOD_ONSHAREMSGRESP:
_shareMsgRespStreamController.add(FakeWeiboShareMsgResp._(
errorCode: call.arguments[_ARGUMENT_KEY_RESULT_ERRORCODE] as int,
errorMessage: call.arguments[_ARGUMENT_KEY_RESULT_ERRORMESSAGE] as String,
));
break;
}
}
Stream<FakeWeiboAuthResp> authResp() {
return _authRespStreamController.stream;
}
Stream<FakeWeiboShareMsgResp> shareMsgResp() {
return _shareMsgRespStreamController.stream;
}
Future<bool> isWeiboInstalled() async {
return (await _channel.invokeMethod(_METHOD_ISWEIBOINSTALLED)) as bool;
}
Future<void> auth() {
return _channel.invokeMethod(
_METHOD_AUTH,
<String, dynamic>{
_ARGUMENT_KEY_APPKEY: _appKey,
_ARGUMENT_KEY_SCOPE: _scope.join(','),
_ARGUMENT_KEY_REDIRECTURL: _redirectUrl,
},
);
}
Future<FakeWeiboApiUserResp> getUserInfo({
@required String appkey,
@required String userId,
@required String accessToken,
}) {
assert(userId != null && userId.isNotEmpty);
assert(accessToken != null && accessToken.isNotEmpty);
Map<String, String> params = <String, String>{
'uid': userId,
};
return HttpClient()
.getUrl(_encodeUrl('https://api.weibo.com/2/users/show.json', appkey,
accessToken, params))
.then((HttpClientRequest request) {
return request.close();
}).then((HttpClientResponse response) async {
if (response.statusCode == HttpStatus.ok) {
String content = await utf8.decodeStream(response);
Map<dynamic, dynamic> map = json.decode(content) as Map<dynamic, dynamic>;
int errorCode = map.containsKey(FakeWeiboApiBaseResp.KEY_ERROR_CODE)
? map[FakeWeiboApiBaseResp.KEY_ERROR_CODE] as int
: FakeWeiboApiBaseResp.ERROR_CODE_SUCCESS;
if (errorCode == FakeWeiboApiBaseResp.ERROR_CODE_SUCCESS) {
return FakeWeiboApiUserResp._(
errorCode: errorCode,
id: map[FakeWeiboApiUserResp.KEY_ID] as int,
idstr: map[FakeWeiboApiUserResp.KEY_ID_STR] as String,
screenName: map[FakeWeiboApiUserResp.KEY_SCREEN_NAME] as String,
name: map[FakeWeiboApiUserResp.KEY_NAME] as String,
province: map[FakeWeiboApiUserResp.KEY_PROVINCE] as String,
city: map[FakeWeiboApiUserResp.KEY_CITY] as String,
location: map[FakeWeiboApiUserResp.KEY_LOCATION] as String,
description: map[FakeWeiboApiUserResp.KEY_DESCRIPTION] as String,
url: map[FakeWeiboApiUserResp.KEY_URL] as String,
profileImageUrl:
map[FakeWeiboApiUserResp.KEY_PROFILE_IMAGE_URL] as String,
profileUrl: map[FakeWeiboApiUserResp.KEY_PROFILE_URL] as String,
domain: map[FakeWeiboApiUserResp.KEY_DOMAIN] as String,
weihao: map[FakeWeiboApiUserResp.KEY_WEIHAO] as String,
gender: map[FakeWeiboApiUserResp.KEY_GENDER] as String,
verified: map[FakeWeiboApiUserResp.KEY_VERIFIED] as bool,
avatarLarge: map[FakeWeiboApiUserResp.KEY_AVATAR_LARGE] as String,
avatarHD: map[FakeWeiboApiUserResp.KEY_AVATAR_HD] as String,
);
} else {
return FakeWeiboApiUserResp._(
errorCode: errorCode,
error: map[FakeWeiboApiBaseResp.KEY_ERROR] as String,
request: map[FakeWeiboApiBaseResp.KEY_REQUEST] as String,
);
}
}
throw HttpException(
'HttpResponse statusCode: ${response.statusCode}, reasonPhrase: ${response.reasonPhrase}.');
});
}
Uri _encodeUrl(
String baseUrl,
String appkey,
String accessToken,
Map<String, String> params,
) {
params.putIfAbsent('source', () => appkey);
params.putIfAbsent('access_token', () => accessToken);
Uri baseUri = Uri.parse(baseUrl);
Map<String, List<String>> queryParametersAll =
Map<String, List<String>>.of(baseUri.queryParametersAll);
params.forEach((String key, String value) {
queryParametersAll.remove(key);
queryParametersAll.putIfAbsent(key, () => <String>[value]);
});
return baseUri.replace(queryParameters: queryParametersAll);
}
Future<void> shareText({
@required String text,
}) {
assert(text != null && text.isNotEmpty && text.length <= 1024);
return _channel.invokeMethod(
_METHOD_SHARETEXT,
<String, dynamic>{
_ARGUMENT_KEY_TEXT: text,
},
);
}
Future<void> shareImage({
@required Uint8List imageData,
}) {
assert(imageData != null && imageData.lengthInBytes <= 2 * 1024 * 1024);
return _channel.invokeMethod(
_METHOD_SHAREIMAGE,
<String, dynamic>{
_ARGUMENT_KEY_IMAGEDATA: imageData,
},
);
}
Future<void> shareWebpage({
@required String title,
@required String description,
@required Uint8List thumbData,
@required String webpageUrl,
}) {
assert(title != null && title.isNotEmpty && title.length <= 512);
assert(description != null &&
description.isNotEmpty &&
description.length <= 1024);
assert(thumbData != null && thumbData.lengthInBytes <= 32 * 1024);
assert(webpageUrl != null &&
webpageUrl.isNotEmpty &&
webpageUrl.length <= 255);
return _channel.invokeMethod(
_METHOD_SHAREWEBPAGE,
<String, dynamic>{
_ARGUMENT_KEY_TITLE: title,
_ARGUMENT_KEY_DESCRIPTION: description,
_ARGUMENT_KEY_THUMBDATA: thumbData,
_ARGUMENT_KEY_WEBPAGEURL: webpageUrl,
},
);
}
}
class FakeWeiboProvider extends InheritedWidget {
FakeWeiboProvider({
Key key,
@required this.weibo,
@required Widget child,
}) : super(key: key, child: child);
final FakeWeibo weibo;
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
FakeWeiboProvider oldProvider = oldWidget as FakeWeiboProvider;
return weibo != oldProvider.weibo;
}
static FakeWeiboProvider of(BuildContext context) {
return context.inheritFromWidgetOfExactType(FakeWeiboProvider)
as FakeWeiboProvider;
}
}
library fake_weibo;
export 'src/domain/api/weibo_api_resp.dart';
export 'src/domain/api/weibo_user_info_resp.dart'
hide WeiboUserInfoRespSerializer;
export 'src/domain/sdk/weibo_auth_resp.dart' hide WeiboAuthRespSerializer;
export 'src/domain/sdk/weibo_sdk_resp.dart' hide WeiboSdkRespSerializer;
export 'src/weibo.dart';
export 'src/weibo_scope.dart';

View File

@ -0,0 +1,14 @@
abstract class WeiboApiResp {
WeiboApiResp({
int errorCode,
this.error,
this.request,
}) : errorCode = errorCode ?? ERROR_CODE_SUCCESS;
static const int ERROR_CODE_SUCCESS = 0;
/// https://open.weibo.com/wiki/Help/error
final int errorCode;
final String error;
final String request;
}

View File

@ -0,0 +1,64 @@
import 'package:jaguar_serializer/jaguar_serializer.dart';
import 'package:fake_weibo/src/domain/api/weibo_api_resp.dart';
part 'weibo_user_info_resp.jser.dart';
@GenSerializer(nameFormatter: toSnakeCase)
class WeiboUserInfoRespSerializer extends Serializer<WeiboUserInfoResp>
with _$WeiboUserInfoRespSerializer {}
class WeiboUserInfoResp extends WeiboApiResp {
WeiboUserInfoResp({
int errorCode,
String error,
String request,
this.id,
this.idstr,
this.screenName,
this.name,
this.location,
this.description,
this.profileImageUrl,
this.gender,
this.avatarLarge,
this.avatarHd,
}) : super(errorCode: errorCode, error: error, request: request);
/// UIDint64
final int id;
/// UID
final String idstr;
///
final String screenName;
///
final String name;
///
final String location;
///
final String description;
/// 50×50
final String profileImageUrl;
/// mfn
final String gender;
///
final String avatarLarge;
///
final String avatarHd;
bool isMale() {
return gender == 'm';
}
bool isFemale() {
return gender == 'f';
}
}

View File

@ -0,0 +1,54 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'weibo_user_info_resp.dart';
// **************************************************************************
// JaguarSerializerGenerator
// **************************************************************************
abstract class _$WeiboUserInfoRespSerializer
implements Serializer<WeiboUserInfoResp> {
@override
Map<String, dynamic> toMap(WeiboUserInfoResp model) {
if (model == null) return null;
Map<String, dynamic> ret = <String, dynamic>{};
setMapValue(ret, 'id', model.id);
setMapValue(ret, 'idstr', model.idstr);
setMapValue(ret, 'screen_name', model.screenName);
setMapValue(ret, 'name', model.name);
setMapValue(ret, 'location', model.location);
setMapValue(ret, 'description', model.description);
setMapValue(ret, 'profile_image_url', model.profileImageUrl);
setMapValue(ret, 'gender', model.gender);
setMapValue(ret, 'avatar_large', model.avatarLarge);
setMapValue(ret, 'avatar_hd', model.avatarHd);
setMapValue(ret, 'error_code', model.errorCode);
setMapValue(ret, 'error', model.error);
setMapValue(ret, 'request', model.request);
return ret;
}
@override
WeiboUserInfoResp fromMap(Map map) {
if (map == null) return null;
final obj = new WeiboUserInfoResp(
errorCode: map['error_code'] as int ?? getJserDefault('errorCode'),
error: map['error'] as String ?? getJserDefault('error'),
request: map['request'] as String ?? getJserDefault('request'),
id: map['id'] as int ?? getJserDefault('id'),
idstr: map['idstr'] as String ?? getJserDefault('idstr'),
screenName:
map['screen_name'] as String ?? getJserDefault('screenName'),
name: map['name'] as String ?? getJserDefault('name'),
location: map['location'] as String ?? getJserDefault('location'),
description:
map['description'] as String ?? getJserDefault('description'),
profileImageUrl: map['profile_image_url'] as String ??
getJserDefault('profileImageUrl'),
gender: map['gender'] as String ?? getJserDefault('gender'),
avatarLarge:
map['avatar_large'] as String ?? getJserDefault('avatarLarge'),
avatarHd: map['avatar_hd'] as String ?? getJserDefault('avatarHd'));
return obj;
}
}

View File

@ -0,0 +1,24 @@
import 'package:jaguar_serializer/jaguar_serializer.dart';
import 'package:fake_weibo/src/domain/sdk/weibo_sdk_resp.dart';
part 'weibo_auth_resp.jser.dart';
@GenSerializer()
class WeiboAuthRespSerializer extends Serializer<WeiboAuthResp>
with _$WeiboAuthRespSerializer {}
class WeiboAuthResp extends WeiboSdkResp {
WeiboAuthResp({
int errorCode,
String errorMessage,
this.userId,
this.accessToken,
this.refreshToken,
this.expiresIn,
}) : super(errorCode: errorCode, errorMessage: errorMessage);
final String userId;
final String accessToken;
final String refreshToken;
final int expiresIn;
}

View File

@ -0,0 +1,38 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'weibo_auth_resp.dart';
// **************************************************************************
// JaguarSerializerGenerator
// **************************************************************************
abstract class _$WeiboAuthRespSerializer implements Serializer<WeiboAuthResp> {
@override
Map<String, dynamic> toMap(WeiboAuthResp model) {
if (model == null) return null;
Map<String, dynamic> ret = <String, dynamic>{};
setMapValue(ret, 'userId', model.userId);
setMapValue(ret, 'accessToken', model.accessToken);
setMapValue(ret, 'refreshToken', model.refreshToken);
setMapValue(ret, 'expiresIn', model.expiresIn);
setMapValue(ret, 'errorCode', model.errorCode);
setMapValue(ret, 'errorMessage', model.errorMessage);
return ret;
}
@override
WeiboAuthResp fromMap(Map map) {
if (map == null) return null;
final obj = new WeiboAuthResp(
errorCode: map['errorCode'] as int ?? getJserDefault('errorCode'),
errorMessage:
map['errorMessage'] as String ?? getJserDefault('errorMessage'),
userId: map['userId'] as String ?? getJserDefault('userId'),
accessToken:
map['accessToken'] as String ?? getJserDefault('accessToken'),
refreshToken:
map['refreshToken'] as String ?? getJserDefault('refreshToken'),
expiresIn: map['expiresIn'] as int ?? getJserDefault('expiresIn'));
return obj;
}
}

View File

@ -0,0 +1,47 @@
import 'package:jaguar_serializer/jaguar_serializer.dart';
part 'weibo_sdk_resp.jser.dart';
@GenSerializer()
class WeiboSdkRespSerializer extends Serializer<WeiboSdkResp>
with _$WeiboSdkRespSerializer {}
class WeiboSdkResp {
WeiboSdkResp({
int errorCode,
this.errorMessage,
}) : errorCode = errorCode ?? SUCCESS;
///
static const int SUCCESS = 0;
///
static const int USERCANCEL = -1;
///
static const int SENT_FAIL = -2;
///
static const int AUTH_DENY = -3;
///
static const int USERCANCEL_INSTALL = -4;
///
static const int PAY_FAIL = -5;
/// response UserInfo
static const int SHARE_IN_SDK_FAILED = -8;
///
static const int UNSUPPORT = -99;
///
static const int UNKNOWN = -100;
/// sso package or sign error
static const int SSO_PKG_SIGN_ERROR = 21338;
final int errorCode;
final String errorMessage;
}

View File

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

221
lib/src/weibo.dart Normal file
View File

@ -0,0 +1,221 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:fake_weibo/src/domain/api/weibo_user_info_resp.dart';
import 'package:fake_weibo/src/domain/sdk/weibo_auth_resp.dart';
import 'package:fake_weibo/src/domain/sdk/weibo_sdk_resp.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart';
class Weibo {
static const String _METHOD_REGISTERAPP = 'registerApp';
static const String _METHOD_ISWEIBOINSTALLED = 'isWeiboInstalled';
static const String _METHOD_AUTH = 'auth';
static const String _METHOD_SHARETEXT = 'shareText';
static const String _METHOD_SHAREIMAGE = 'shareImage';
static const String _METHOD_SHAREWEBPAGE = 'shareWebpage';
static const String _METHOD_ONAUTHRESP = 'onAuthResp';
static const String _METHOD_ONSHAREMSGRESP = 'onShareMsgResp';
static const String _ARGUMENT_KEY_APPKEY = 'appKey';
static const String _ARGUMENT_KEY_SCOPE = 'scope';
static const String _ARGUMENT_KEY_REDIRECTURL = 'redirectUrl';
static const String _ARGUMENT_KEY_TEXT = 'text';
static const String _ARGUMENT_KEY_TITLE = 'title';
static const String _ARGUMENT_KEY_DESCRIPTION = 'description';
static const String _ARGUMENT_KEY_THUMBDATA = 'thumbData';
static const String _ARGUMENT_KEY_IMAGEDATA = 'imageData';
static const String _ARGUMENT_KEY_WEBPAGEURL = 'webpageUrl';
static const String _DEFAULT_REDIRECTURL =
'https://api.weibo.com/oauth2/default.html';
static const MethodChannel _channel =
MethodChannel('v7lin.github.io/fake_weibo');
final StreamController<WeiboAuthResp> _authRespStreamController =
StreamController<WeiboAuthResp>.broadcast();
final StreamController<WeiboSdkResp> _shareMsgRespStreamController =
StreamController<WeiboSdkResp>.broadcast();
Future<void> registerApp({
@required String appKey,
@required List<String> scope,
String redirectUrl =
_DEFAULT_REDIRECTURL, // -> -> -> -> OAuth2.0
}) {
assert(appKey != null && appKey.isNotEmpty);
assert(scope != null && scope.isNotEmpty);
_channel.setMethodCallHandler(_handleMethod);
return _channel.invokeMethod(
_METHOD_REGISTERAPP,
<String, dynamic>{
_ARGUMENT_KEY_APPKEY: appKey,
_ARGUMENT_KEY_SCOPE: scope.join(','),
_ARGUMENT_KEY_REDIRECTURL: redirectUrl,
},
);
}
Future<dynamic> _handleMethod(MethodCall call) async {
switch (call.method) {
case _METHOD_ONAUTHRESP:
_authRespStreamController.add(WeiboAuthRespSerializer()
.fromMap(call.arguments as Map<dynamic, dynamic>));
break;
case _METHOD_ONSHAREMSGRESP:
_shareMsgRespStreamController.add(WeiboSdkRespSerializer()
.fromMap(call.arguments as Map<dynamic, dynamic>));
break;
}
}
Stream<WeiboAuthResp> authResp() {
return _authRespStreamController.stream;
}
Stream<WeiboSdkResp> shareMsgResp() {
return _shareMsgRespStreamController.stream;
}
Future<bool> isWeiboInstalled() async {
return (await _channel.invokeMethod(_METHOD_ISWEIBOINSTALLED)) as bool;
}
Future<void> auth({
@required String appKey,
@required List<String> scope,
String redirectUrl = _DEFAULT_REDIRECTURL,
}) {
assert(appKey != null && appKey.isNotEmpty);
assert(scope != null && scope.isNotEmpty);
return _channel.invokeMethod(
_METHOD_AUTH,
<String, dynamic>{
_ARGUMENT_KEY_APPKEY: appKey,
_ARGUMENT_KEY_SCOPE: scope.join(','),
_ARGUMENT_KEY_REDIRECTURL: redirectUrl,
},
);
}
Future<WeiboUserInfoResp> getUserInfo({
@required String appkey,
@required String userId,
@required String accessToken,
}) {
assert(userId != null && userId.isNotEmpty);
assert(accessToken != null && accessToken.isNotEmpty);
Map<String, String> params = <String, String>{
'uid': userId,
};
return HttpClient()
.getUrl(_encodeUrl('https://api.weibo.com/2/users/show.json', appkey,
accessToken, params))
.then((HttpClientRequest request) {
return request.close();
}).then((HttpClientResponse response) async {
if (response.statusCode == HttpStatus.ok) {
String content = await utf8.decodeStream(response);
return WeiboUserInfoRespSerializer()
.fromMap(json.decode(content) as Map<dynamic, dynamic>);
}
throw HttpException(
'HttpResponse statusCode: ${response.statusCode}, reasonPhrase: ${response.reasonPhrase}.');
});
}
Uri _encodeUrl(
String baseUrl,
String appkey,
String accessToken,
Map<String, String> params,
) {
params.putIfAbsent('source', () => appkey);
params.putIfAbsent('access_token', () => accessToken);
Uri baseUri = Uri.parse(baseUrl);
Map<String, List<String>> queryParametersAll =
Map<String, List<String>>.of(baseUri.queryParametersAll);
params.forEach((String key, String value) {
queryParametersAll.remove(key);
queryParametersAll.putIfAbsent(key, () => <String>[value]);
});
return baseUri.replace(queryParameters: queryParametersAll);
}
Future<void> shareText({
@required String text,
}) {
assert(text != null && text.isNotEmpty && text.length <= 1024);
return _channel.invokeMethod(
_METHOD_SHARETEXT,
<String, dynamic>{
_ARGUMENT_KEY_TEXT: text,
},
);
}
Future<void> shareImage({
@required Uint8List imageData,
}) {
assert(imageData != null && imageData.lengthInBytes <= 2 * 1024 * 1024);
return _channel.invokeMethod(
_METHOD_SHAREIMAGE,
<String, dynamic>{
_ARGUMENT_KEY_IMAGEDATA: imageData,
},
);
}
Future<void> shareWebpage({
@required String title,
@required String description,
@required Uint8List thumbData,
@required String webpageUrl,
}) {
assert(title != null && title.isNotEmpty && title.length <= 512);
assert(description != null &&
description.isNotEmpty &&
description.length <= 1024);
assert(thumbData != null && thumbData.lengthInBytes <= 32 * 1024);
assert(webpageUrl != null &&
webpageUrl.isNotEmpty &&
webpageUrl.length <= 255);
return _channel.invokeMethod(
_METHOD_SHAREWEBPAGE,
<String, dynamic>{
_ARGUMENT_KEY_TITLE: title,
_ARGUMENT_KEY_DESCRIPTION: description,
_ARGUMENT_KEY_THUMBDATA: thumbData,
_ARGUMENT_KEY_WEBPAGEURL: webpageUrl,
},
);
}
}
class WeiboProvider extends InheritedWidget {
WeiboProvider({
Key key,
@required this.weibo,
@required Widget child,
}) : super(key: key, child: child);
final Weibo weibo;
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
WeiboProvider oldProvider = oldWidget as WeiboProvider;
return weibo != oldProvider.weibo;
}
static WeiboProvider of(BuildContext context) {
return context.inheritFromWidgetOfExactType(WeiboProvider) as WeiboProvider;
}
}

14
lib/src/weibo_scope.dart Normal file
View File

@ -0,0 +1,14 @@
class WeiboScope {
WeiboScope._();
static const String EMAIL = 'email';
static const String DIRECT_MESSAGES_READ = 'direct_messages_read';
static const String DIRECT_MESSAGES_WRITE = 'direct_messages_write';
static const String FRIENDSHIPS_GROUPS_READ = 'friendships_groups_read';
static const String FRIENDSHIPS_GROUPS_WRITE = 'friendships_groups_write';
static const String STATUSES_TO_ME_READ = 'statuses_to_me_read';
static const String FOLLOW_APP_OFFICIAL_MICROBLOG =
'follow_app_official_microblog';
static const String INVITATION_WRITE = 'invitation_write';
static const String ALL = 'all';
}

View File

@ -1,6 +1,6 @@
name: fake_weibo
description: A powerful weibo plugin for Flutter.
version: 0.0.1+1
version: 0.1.0
author: v7lin <v7lin@qq.com>
homepage: https://github.com/v7lin/fake_weibo

View File

@ -1,3 +1,7 @@
import 'dart:convert';
import 'package:fake_weibo/src/domain/api/weibo_user_info_resp.dart';
import 'package:fake_weibo/src/domain/sdk/weibo_auth_resp.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:jaguar_serializer/jaguar_serializer.dart';
@ -21,6 +25,19 @@ void main() {
});
test('smoke test - jaguar_serializer', () {
WeiboAuthResp authResp = WeiboAuthRespSerializer().fromMap(json.decode(
'{"expiresIn":157679999,"errorCode":0,"accessToken":"2.00s67kHGj4SghD8e46233eea9O1IDB","userId":"5611295980","refreshToken":"2.00s67kHGj4SghD6ee52b48200iqHz3"}')
as Map<dynamic, dynamic>);
expect(authResp.errorCode, equals(0));
expect(authResp.expiresIn, equals(157679999));
expect(authResp.accessToken, equals('2.00s67kHGj4SghD8e46233eea9O1IDB'));
WeiboUserInfoResp userInfoResp = WeiboUserInfoRespSerializer().fromMap(json.decode(
'{"id":5611295980,"idstr":"5611295980","class":1,"screen_name":"v7lin","name":"v7lin","province":"35","city":"1","location":"福建 福州","description":"","url":"","profile_image_url":"http://tvax4.sinaimg.cn/crop.0.0.640.640.50/0067KqpSly8fujqh4oj39j30hs0hsjs6.jpg","cover_image_phone":"http://ww1.sinaimg.cn/crop.0.0.640.640.640/549d0121tw1egm1kjly3jj20hs0hsq4f.jpg","profile_url":"u/5611295980","domain":"","weihao":"","gender":"m","followers_count":1,"friends_count":1,"pagefriends_count":0,"statuses_count":0,"video_status_count":0,"favourites_count":0,"created_at":"Mon May 18 19:32:52 +0800 2015","following":false,"allow_all_act_msg":false,"geo_enabled":true,"verified":false,"verified_type":-1,"remark":"","insecurity":{"sexual_content":false},"ptype":0,"allow_all_comment":true,"avatar_large":"http://tvax4.sinaimg.cn/crop.0.0.640.640.180/0067KqpSly8fujqh4oj39j30hs0hsjs6.jpg","avatar_hd":"http://tvax4.sinaimg.cn/crop.0.0.640.640.1024/0067KqpSly8fujqh4oj39j30hs0hsjs6.jpg","verified_reason":"","verified_trade":"","verified_reason_url":"","verified_source":"","verified_source_url":"","follow_me":false,"like":false,"like_me":false,"online_status":0,"bi_followers_count":0,"lang":"zh-cn","star":0,"mbtype":0,"mbrank":0,"block_word":0,"block_app":0,"credit_score":80,"user_ability":0,"urank":4,"story_read_state":-1,"vclub_member":0,"is_teenager":0,"is_guardian":0,"is_teenager_list":0}')
as Map<dynamic, dynamic>);
expect(userInfoResp.errorCode, equals(0));
expect(userInfoResp.id, equals(5611295980));
expect(userInfoResp.screenName, equals('v7lin'));
expect(userInfoResp.avatarHd, equals('http://tvax4.sinaimg.cn/crop.0.0.640.640.1024/0067KqpSly8fujqh4oj39j30hs0hsjs6.jpg'));
});
}