ionic是比较流行的跨平台移动app的开发工具,但是在开发一个信息聚合平台的时候发现一个问题,就是做QQ和微博分享的时候,ionic的js就不能处理了,所以需要写cordova插件.然后我用的是友盟的社会化分享组件,可以节省很多的代码,前提是你必须要有友盟的AppKey,腾讯和新浪的AppId并且完成了账号的绑定,具体流程见
http://dev.umeng.com/social/android/operation
写一个cordova插件
cordova插件的目录结构如下:
- cordova-plugin-ThirdParty
- plugin.xml
- src
- android
- 各类友盟的jar包,类似SocialSDK_Sina.jar,SocialSDK_QQZone_1.jar
- ThirdParty.java
- ios
- ThirdParty.m
- ThirdParty.h
- www
- ThirdParty.js
- plugin.xml是用来配置插件的
- src中是各平台的原生代码
- www是javascript调用原生代码的方法
ThirdParty.js
里面主要是调用原生代码的方法,这里以微博,QQ登录为例
var exec = require('cordova/exec'),
cordova = require('cordova');
module.exports = {
QQLogin:function(successCallback, errorCallback){
exec(successCallback, errorCallback, "ThirdParty", "QQLogin", []);
},
WBLogin:function(successCallback, errorCallback){
exec(successCallback, errorCallback, "ThirdParty", "WBLogin", []);
},
};
这个js文件包含两个方法,QQLogin和WBLogin,传递的参数分别是成功的回调函数和失败的回调函数.
plugin.xml
这是插件的配置文件,主要代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
xmlns:android="http://schemas.android.com/apk/res/android"
id="cordova-plugin-thirdparty"
version="0.3.6">
<!--这是插件的名字-->
<name>ThirdParty</name>
<!--插件的描述,随你喜欢-->
<description>Cordova ThirdParty Plugin</description>
<!--require cordova version -->
<engines>
<engine name="cordova" version=">=3.5.0" />
<engine name="cordova-android" version=">=4.0.0-dev" />
</engines>
<!--这里用于生成一个全局的js对象ThirdParty,这个对象可以调用ThirdParty.js中的方法-->
<js-module src="www/ThirdParty.js" name="ThirdParty">
<clobbers target="ThirdParty" />
</js-module>
<!-- 针对ios平台 -->
<platform name="ios">
<config-file target="config.xml" parent="/*">
<feature name="ThirdParty">
<param name="ios-package" value="ThirdParty"/>
</feature>
<!--whitelist for QQ SDK-->
<access origin = "https://openmobile.qq.com/*"/>
<access origin = "http://qzs.qq.com/open/mobile/login/*"/>
<access origin = "http://qzonestyle.gtimg.cn/*"/>
<access origin = "http://pub.idqqimg.com/*"/>
<access origin = "http://appsupport.qq.com/*"/>
<access origin = "http://support.qq.com/*"/>
<access origin = "http://qzs.qq.com/*"/>
<access origin = "http://m.qzone.com/*"/>
</config-file>
<!--set ios URLTypes for QQ SDK -->
<config-file target="*-Info.plist" parent="CFBundleURLTypes">
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>tencent这里应替换成你的QQ_APP_ID</string>
</array>
</dict>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>sina.这里应替换成你的友盟AppKey</string>
</array>
</dict>
</array>
</config-file>
<header-file src="src/ios/ThirdParty.h"/>
<source-file src="src/ios/ThirdParty.m"/>
<!--required frameworks for qq sdk-->
<framework src="CoreGraphics.framework" />
<framework src="CoreTelephony.framework" />
<framework src="SystemConfiguration.framework" />
<framework src="Security.framework" />
<framework src="libiconv.dylib" />
<framework src="libsqlite3.dylib" />
<framework src="libstdc++.dylib" />
<framework src="libz.dylib" />
<!--QQ SDK version 2.9.0-->
<info>
提示你安装ios已完成
</info>
</platform>
<!-- 针对android -->
<platform name="android">
<config-file target="res/xml/config.xml" parent="/*">
<feature name="ThirdParty" >
<!--这会在你的安卓项目中生成一个包,里面装着ThirdParty.java-->
<param name="android-package" value="com.shenaolin.cordova.ThirdParty"/>
</feature>
<!--whitelist for QQ SDK-->
<access origin = "https://openmobile.qq.com/*"/>
<access origin = "http://qzs.qq.com/open/mobile/login/*"/>
<access origin = "http://qzonestyle.gtimg.cn/*"/>
<access origin = "http://pub.idqqimg.com/*"/>
<access origin = "http://appsupport.qq.com/*"/>
<access origin = "http://support.qq.com/*"/>
<access origin = "http://qzs.qq.com/*"/>
<access origin = "http://m.qzone.com/*"/>
</config-file>
<!--添加权限 -->
<config-file target="AndroidManifest.xml" parent="/manifest">
<!-- 检测网络状态 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- 获取mac地址作为用户的备用唯一标识 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- 获取用户手机的IMEI,用来唯一的标识用户 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- 缓存资源优先存入SDcard -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- 允许应用程序联网,以便向我们的服务器端发送数据 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- QQ、QQ空间所需权限 -->
<uses-permission android:name="android.permission.GET_TASKS" />
</config-file>
<!--QQ授权Activity -->
<config-file target="AndroidManifest.xml" parent="/manifest/application">
<activity
android:name="com.tencent.tauth.AuthActivity"
android:noHistory="true"
android:launchMode="singleTask" >
<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这里是QQ_APP_ID"/>
</intent-filter>
</activity>
<activity
android:name="com.tencent.connect.common.AssistActivity"
android:configChanges="orientation|keyboardHidden"
android:screenOrientation="portrait"
android:theme="@android:style/Theme.Translucent.NoTitleBar" >
</activity>
<meta-data
android:name="UMENG_APPKEY"
android:value="这里是你的友盟APPKEY" >
</meta-data>
</config-file>
<source-file src="src/android/ThirdParty.java" target-dir="src/com/shenaolin/cordova" />
<!--以下是我从友盟中下的库,你可以根据需要替换,这些都将出现在安卓项目的jinLib文件夹下-->
<source-file src="src/android/httpmime-4.1.3.jar" target-dir="libs/" />
<source-file src="src/android/SocialSDK_QQZone_1.jar" target-dir="libs/" />
<source-file src="src/android/SocialSDK_QQZone_2.jar" target-dir="libs/" />
<source-file src="src/android/SocialSDK_QQZone_3.jar" target-dir="libs/" />
<source-file src="src/android/SocialSDK_Sina.jar" target-dir="libs/" />
<source-file src="src/android/umeng_social_sdk.jar" target-dir="libs/" />
<source-file src="src/android/android-support-v4.jar" target-dir="libs/" />
</platform>
</plugin>
以上的配置根据你的需求进行替换
ThirdParty.java
现在要对ThirdParty.js中的请求方法进行处理,也就是在android原生环境中设置QQ登陆,微博登陆的处理方法,如下:
package com.shenaolin.cordova;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.os.Bundle;
import android.os.AsyncTask;
import android.text.TextUtils;
import android.widget.Toast;
import com.umeng.socialize.bean.SHARE_MEDIA;
import com.umeng.socialize.bean.SocializeEntity;
import com.umeng.socialize.controller.UMServiceFactory;
import com.umeng.socialize.controller.UMSocialService;
import com.umeng.socialize.controller.listener.SocializeListeners;
import com.umeng.socialize.exception.SocializeException;
import com.umeng.socialize.sso.SinaSsoHandler;
import com.umeng.socialize.sso.UMQQSsoHandler;
import com.umeng.socialize.utils.Log;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.json.JSONArray;
import org.json.JSONException;
import java.util.Map;
import java.util.Set;
public class ThirdParty extends CordovaPlugin {
private UMSocialService mController;
private Context context;
//初始化插件,经过试验,这个方法每次exec请求后都会执行
@Override
protected void pluginInitialize() {
super.pluginInitialize();
mController = UMServiceFactory.getUMSocialService("com.umeng.login");
mController.getConfig().setSsoHandler(new SinaSsoHandler());
context=this.cordova.getActivity();
final Activity activity = this.cordova.getActivity();
}
@Override
public boolean execute(String action, final JSONArray args,
final CallbackContext callbackContext) throws JSONException {
Log.e("SA", "+++");
if (action.equals("QQLogin")) {
return ssoLogin(callbackContext);
}
if(action.equals("WBLogin")){
return WBLogin(callbackContext);
}
return super.execute(action, args, callbackContext);
}
private boolean WBLogin(final CallbackContext callbackContext) {
mController.doOauthVerify(context, SHARE_MEDIA.SINA, new SocializeListeners.UMAuthListener() {
@Override
public void onError(SocializeException e, SHARE_MEDIA platform) {
callbackContext.error(0);
}
@Override
public void onComplete(Bundle value, SHARE_MEDIA platform) {
if (value != null && !TextUtils.isEmpty(value.getString("uid"))) {
Toast.makeText(context, "授权成功.", Toast.LENGTH_SHORT).show();
mController.getPlatformInfo(context, SHARE_MEDIA.SINA, new SocializeListeners.UMDataListener() {
@Override
public void onStart() {
Toast.makeText(context, "获取平台数据开始...", Toast.LENGTH_SHORT).show();
}
@Override
public void onComplete(int status, Map<String, Object> info) {
if (status == 200 && info != null) {
StringBuilder sb = new StringBuilder();
sb.append("{");
Set<String> keys = info.keySet();
for (String key : keys) {
sb.append("\"" + key + "\"" + ":" + "\"" + info.get(key).toString() + "\",");
}
sb.delete(sb.length() - 1, sb.length());
sb.append("}");
Log.d("TestData", sb.toString());
callbackContext.success(sb.toString());
} else {
Log.d("TestData", "发生错误:" + status);
callbackContext.error(1);
}
}
});
} else {
Toast.makeText(context, "授权失败", Toast.LENGTH_SHORT).show();
callbackContext.error(2);
}
}
@Override
public void onCancel(SHARE_MEDIA platform) {
}
@Override
public void onStart(SHARE_MEDIA platform) {
}
});
return true;
}
private boolean QQLogin(final CallbackContext callbackContext){
mController.doOauthVerify(context, SHARE_MEDIA.QQ, new SocializeListeners.UMAuthListener() {
@Override
public void onStart(SHARE_MEDIA platform) {
Toast.makeText(context, "授权开始", Toast.LENGTH_SHORT).show();
}
@Override
public void onError(SocializeException e, SHARE_MEDIA platform) {
Toast.makeText(context, "授权错误", Toast.LENGTH_SHORT).show();
callbackContext.error(0);
}
@Override
public void onComplete(Bundle value, SHARE_MEDIA platform) {
Toast.makeText(context, "授权完成", Toast.LENGTH_SHORT).show();
//获取相关授权信息
mController.getPlatformInfo(context, SHARE_MEDIA.QQ, new SocializeListeners.UMDataListener() {
@Override
public void onStart() {
Toast.makeText(context, "获取平台数据开始...", Toast.LENGTH_SHORT).show();
}
@Override
public void onComplete(int status, Map<String, Object> info) {
if(status == 200 && info != null){
StringBuilder sb = new StringBuilder();
Set<String> keys = info.keySet();
sb.append("{");
for(String key : keys){
sb.append("\""+key+"\""+":"+"\""+info.get(key).toString()+"\",");
}
sb.delete(sb.length()-1,sb.length());
sb.append("}");
callbackContext.success(sb.toString());
Log.d("TestData",sb.toString());
}else{
Log.d("TestData","发生错误:"+status);
callbackContext.error(1);
}
}
});
}
@Override
public void onCancel(SHARE_MEDIA platform) {
Toast.makeText(context, "授权取消", Toast.LENGTH_SHORT).show();
callbackContext.error(2);
}
} );
return true;
}
}
其中的QQLogin和WBLogin分别完成了QQ登录和微博登录,并且通过callbackcontext返回了用户信息的json,其中代码都是从友盟官网copy的,详情参考文档
http://dev.umeng.com/social/android/detail-share#4
当然,在安卓项目的MainActivity中还需要做一下配置:
public class MainActivity extends CordovaActivity
{
UMSocialService mController;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
mController = UMServiceFactory.getUMSocialService("com.umeng.login");
mController.getConfig().setSsoHandler(new SinaSsoHandler());
// Set by <content src="index.html" /> in config.xml
final Activity activity = this;
new AsyncTask<Object,Void,Void>(){
@Override
protected Void doInBackground(Object... params) {
UMQQSsoHandler qqSsoHandler = new UMQQSsoHandler(activity, "腾讯的AppId",
"腾讯的AppKey");
qqSsoHandler.addToSocialSDK();
return null;
}
}.execute();
loadUrl(launchUrl);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
/**使用SSO授权必须添加如下代码 */
UMSsoHandler ssoHandler = mController.getConfig().getSsoHandler(requestCode);
if(ssoHandler != null){
ssoHandler.authorizeCallBack(requestCode, resultCode, data);
}
}
}
这里也是根据友盟的官方文档.
现在,这个安卓项目已经可以返回json数据了,我们可以在ionic项目的任何地方调用QQ和微博登陆
ThirdParty.QQLogin(function(json){
//这是成功的回调
alert("QQ登陆成功,用户信息:"+json);
},function(failReason){
//这是失败的回调
alert("QQ登陆失败,失败类型:"+failReason);
});
ThirdParty.m
仿照ThirdParty.java,ThirdParty.m的写法如下:
#import "ThirdParty.h"
#import <TencentOpenAPI/QQApiInterface.h>
#import "UMSocial.h"
NSString *QQ_NOT_INSTALLED = @"QQ Client is not installed";
NSString *QQ_PARAM_NOT_FOUND = @"param is not found";
NSString *QQ_LOGIN_ERROR = @"QQ login error";
NSString *QQ_LOGIN_CANCEL = @"QQ login cancelled";
NSString *QQ_LOGIN_NETWORK_ERROR = @"QQ login network error";
@implementation ThirdParty
/**
* QQ单点登录
*
* @param command CDVInvokedUrlCommand
*/
- (void)QQLogin:(CDVInvokedUrlCommand *)command {
printf("登陆!");
UMSocialSnsPlatform *snsPlatform = [UMSocialSnsPlatformManager getSocialPlatformWithName:UMShareToQQ];
snsPlatform.loginClickHandler(self.viewController,[UMSocialControllerService defaultControllerService],YES,^(UMSocialResponseEntity *response){
// 获取微博用户名、uid、token等
if (response.responseCode == UMSResponseCodeSuccess) {
[[UMSocialDataService defaultDataService] requestSnsInformation:UMShareToQQ completion:^(UMSocialResponseEntity *response){
// NSString *string = [NSString stringWithFormat:@"%@",response.data];
NSString *sb = @"{";
NSArray *keys = response.data.keyEnumerator.allObjects;
NSArray *values = response.data.objectEnumerator.allObjects;
for (int i=0; i<response.data.count; i++) {
NSString *key_value = [NSString stringWithFormat:@"\"%@\":\"%@\",",keys[i],values[i]];
sb = [sb stringByAppendingString:key_value];
}
sb = [sb substringToIndex:sb.length-1];
sb = [sb stringByAppendingString:@"}"];
NSLog(@"%@",sb);
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:sb];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
}else{
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:1];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
});
}
//微博登陆
- (void)WBLogin:(CDVInvokedUrlCommand *)command {
printf("登陆!");
UMSocialSnsPlatform *snsPlatform = [UMSocialSnsPlatformManager getSocialPlatformWithName:UMShareToSina];
snsPlatform.loginClickHandler(self.viewController,[UMSocialControllerService defaultControllerService],YES,^(UMSocialResponseEntity *response){
// 获取微博用户名、uid、token等
if (response.responseCode == UMSResponseCodeSuccess) {
[[UMSocialDataService defaultDataService] requestSnsInformation:UMShareToSina completion:^(UMSocialResponseEntity *response){
NSLog(@"SnsInformation is %@",response.data);
NSString *string = [NSString stringWithFormat:@"%@",response.data];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:string];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
}else{
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:1];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
});
}
AppDelegate相应的配置:
objective-c
[UMSocialData setAppKey:@"55d1e85c67e58e48a8000a30"];
[UMSocialQQHandler setQQWithAppId:@"QQ_APP_ID" appKey:@"QQ_APP_KEY" url:@"http://www.umeng.com/social"];
[UMSocialSinaHandler openSSOWithRedirectURL:@"http://sns.whalecloud.com/sina2/callback"];
这里微博的openSSOWithRedirectURL要与微博App设置的回调一致,具体的要求见友盟文档:
http://dev.umeng.com/social/ios/detail-share
最后将友盟sdk中ios的部分拖入xcode项目中得resource,如果报exit1 -lSocialSina之类的错误,是searchPath的问题,把友盟的包加入搜索路径即可.
之后在js中调用的方法与android一样.