import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:weibo_kit/src/model/api/weibo_user_info_resp.dart'; import 'package:weibo_kit/src/model/sdk/weibo_auth_resp.dart'; import 'package:weibo_kit/src/model/sdk/weibo_sdk_resp.dart'; class Weibo { Weibo() { _channel.setMethodCallHandler(_handleMethod); } static const String _METHOD_REGISTERAPP = 'registerApp'; static const String _METHOD_ISINSTALLED = 'isInstalled'; 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_IMAGEURI = 'imageUri'; static const String _ARGUMENT_KEY_WEBPAGEURL = 'webpageUrl'; static const String _SCHEME_FILE = 'file'; static const String _DEFAULT_REDIRECTURL = 'https://api.weibo.com/oauth2/default.html'; final MethodChannel _channel = const MethodChannel('v7lin.github.io/weibo_kit'); final StreamController _authRespStreamController = StreamController.broadcast(); final StreamController _shareMsgRespStreamController = StreamController.broadcast(); Future registerApp({ @required String appKey, @required List scope, String redirectUrl = _DEFAULT_REDIRECTURL, // 新浪微博开放平台 -> 我的应用 -> 应用信息 -> 高级信息 -> OAuth2.0授权设置 }) { assert(appKey != null && appKey.isNotEmpty); assert(scope != null && scope.isNotEmpty); return _channel.invokeMethod( _METHOD_REGISTERAPP, { _ARGUMENT_KEY_APPKEY: appKey, _ARGUMENT_KEY_SCOPE: scope.join(','), _ARGUMENT_KEY_REDIRECTURL: redirectUrl, }, ); } Future _handleMethod(MethodCall call) async { switch (call.method) { case _METHOD_ONAUTHRESP: _authRespStreamController.add( WeiboAuthResp.fromJson(call.arguments as Map)); break; case _METHOD_ONSHAREMSGRESP: _shareMsgRespStreamController.add( WeiboSdkResp.fromJson(call.arguments as Map)); break; } } /// 登录 Stream authResp() { return _authRespStreamController.stream; } /// 分享 Stream shareMsgResp() { return _shareMsgRespStreamController.stream; } Future isInstalled() { return _channel.invokeMethod(_METHOD_ISINSTALLED); } /// 登录 Future auth({ @required String appKey, @required List scope, String redirectUrl = _DEFAULT_REDIRECTURL, }) { assert(appKey != null && appKey.isNotEmpty); assert(scope != null && scope.isNotEmpty); return _channel.invokeMethod( _METHOD_AUTH, { _ARGUMENT_KEY_APPKEY: appKey, _ARGUMENT_KEY_SCOPE: scope.join(','), _ARGUMENT_KEY_REDIRECTURL: redirectUrl, }, ); } /// 用户信息 Future getUserInfo({ @required String appkey, @required String userId, @required String accessToken, }) { assert(userId != null && userId.isNotEmpty); assert(accessToken != null && accessToken.isNotEmpty); Map params = { '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 WeiboUserInfoResp.fromJson( json.decode(content) as Map); } throw HttpException( 'HttpResponse statusCode: ${response.statusCode}, reasonPhrase: ${response.reasonPhrase}.'); }); } Uri _encodeUrl( String baseUrl, String appkey, String accessToken, Map params, ) { params['source'] = appkey; params['access_token'] = accessToken; Uri baseUri = Uri.parse(baseUrl); Map> queryParametersAll = Map>.of(baseUri.queryParametersAll); for (MapEntry entry in params.entries) { queryParametersAll.remove(entry.key); queryParametersAll.putIfAbsent(entry.key, () => [entry.value]); } return baseUri.replace(queryParameters: queryParametersAll); } /// 分享 - 文本 Future shareText({ @required String text, }) { assert(text != null && text.length <= 1024); return _channel.invokeMethod( _METHOD_SHARETEXT, { _ARGUMENT_KEY_TEXT: text, }, ); } /// 分享 - 图片 Future shareImage({ String text, Uint8List imageData, Uri imageUri, }) { assert(text == null || text.length <= 1024); assert((imageData != null && imageData.lengthInBytes <= 2 * 1024 * 1024) || (imageUri != null && imageUri.isScheme(_SCHEME_FILE) && imageUri.toFilePath().length <= 512 && File.fromUri(imageUri).lengthSync() <= 10 * 1024 * 1024)); return _channel.invokeMethod( _METHOD_SHAREIMAGE, { if (text != null && text.isNotEmpty) _ARGUMENT_KEY_TEXT: text, if (imageData != null) _ARGUMENT_KEY_IMAGEDATA: imageData, if (imageUri != null) _ARGUMENT_KEY_IMAGEURI: imageUri.toString(), }, ); } /// 分享 - 网页 Future 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, { _ARGUMENT_KEY_TITLE: title, _ARGUMENT_KEY_DESCRIPTION: description, _ARGUMENT_KEY_THUMBDATA: thumbData, _ARGUMENT_KEY_WEBPAGEURL: webpageUrl, }, ); } }