package xu.li.cordova.wechat; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Environment; import android.util.Log; import android.webkit.URLUtil; import com.tencent.mm.sdk.modelmsg.SendAuth; import com.tencent.mm.sdk.modelmsg.SendMessageToWX; import com.tencent.mm.sdk.modelmsg.WXImageObject; import com.tencent.mm.sdk.modelmsg.WXMediaMessage; import com.tencent.mm.sdk.modelmsg.WXTextObject; import com.tencent.mm.sdk.modelmsg.WXWebpageObject; import com.tencent.mm.sdk.modelpay.PayReq; import com.tencent.mm.sdk.openapi.IWXAPI; import com.tencent.mm.sdk.openapi.WXAPIFactory; import org.apache.cordova.CallbackContext; import org.apache.cordova.CordovaPlugin; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.FileInputStream; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; public class Wechat extends CordovaPlugin { public static final String TAG = "Cordova.Plugin.Wechat"; public static final String WXAPPID_PROPERTY_KEY = "wechatappid"; public static final String ERROR_WECHAT_NOT_INSTALLED = "未安装微信"; public static final String ERROR_INVALID_PARAMETERS = "参数错误"; public static final String ERROR_USER_CANCEL = "用户点击取消并返回"; public static final String ERROR_AUTH_DENIED = "授权失败"; public static final String ERROR_SENT_FAILED = "发送失败"; public static final String ERROR_UNSUPPORT = "微信不支持"; public static final String ERROR_COMMON = "普通错误类型"; public static final String ERROR_UNKNOWN = "未知错误"; public static final String EXTERNAL_STORAGE_IMAGE_PREFIX = "external://"; public static final String KEY_ARG_MESSAGE = "message"; public static final String KEY_ARG_SCENE = "scene"; public static final String KEY_ARG_TEXT = "text"; public static final String KEY_ARG_MESSAGE_TITLE = "title"; public static final String KEY_ARG_MESSAGE_DESCRIPTION = "description"; public static final String KEY_ARG_MESSAGE_THUMB = "thumb"; public static final String KEY_ARG_MESSAGE_MEDIA = "media"; public static final String KEY_ARG_MESSAGE_MEDIA_TYPE = "type"; public static final String KEY_ARG_MESSAGE_MEDIA_WEBPAGEURL = "webpageUrl"; public static final String KEY_ARG_MESSAGE_MEDIA_IMAGE = "image"; public static final String KEY_ARG_MESSAGE_MEDIA_TEXT = "text"; public static final int TYPE_WX_SHARING_APP = 1; public static final int TYPE_WX_SHARING_EMOTION = 2; public static final int TYPE_WX_SHARING_FILE = 3; public static final int TYPE_WX_SHARING_IMAGE = 4; public static final int TYPE_WX_SHARING_MUSIC = 5; public static final int TYPE_WX_SHARING_VIDEO = 6; public static final int TYPE_WX_SHARING_WEBPAGE = 7; public static final int TYPE_WX_SHARING_TEXT = 8; public static final int SCENE_SESSION = 0; public static final int SCENE_TIMELINE = 1; public static final int SCENE_FAVORITE = 2; public static IWXAPI wxAPI; public static CallbackContext currentCallbackContext; protected String appId; @Override protected void pluginInitialize() { super.pluginInitialize(); if (wxAPI == null) { wxAPI = WXAPIFactory.createWXAPI(webView.getContext(), getAppId(), true); } wxAPI.registerApp(webView.getPreferences().getString(WXAPPID_PROPERTY_KEY, "")); } @Override public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { Log.d(TAG, action + " is called."); if (action.equals("share")) { return share(args, callbackContext); } else if (action.equals("sendAuthRequest")) { return sendAuthRequest(args, callbackContext); } else if (action.equals("sendPaymentRequest")) { return sendPaymentRequest(args, callbackContext); } else if (action.equals("isWXAppInstalled")) { return isInstalled(callbackContext); } return super.execute(action, args, callbackContext); } protected boolean share(JSONArray args, CallbackContext callbackContext) throws JSONException { final IWXAPI api = getWXAPI(true); // check if installed if (!api.isWXAppInstalled()) { callbackContext.error(ERROR_WECHAT_NOT_INSTALLED); return true; } // check if # of arguments is correct if (args.length() != 1) { callbackContext.error(ERROR_INVALID_PARAMETERS); return true; } final JSONObject params = args.getJSONObject(0); final SendMessageToWX.Req req = new SendMessageToWX.Req(); req.transaction = buildTransaction(); if (params.has(KEY_ARG_SCENE)) { int scene = params.getInt(KEY_ARG_SCENE); switch (scene) { case SCENE_FAVORITE: req.scene = SendMessageToWX.Req.WXSceneFavorite; break; case SCENE_TIMELINE: req.scene = SendMessageToWX.Req.WXSceneTimeline; break; case SCENE_SESSION: req.scene = SendMessageToWX.Req.WXSceneSession; break; } } else { req.scene = SendMessageToWX.Req.WXSceneTimeline; } // run in background cordova.getThreadPool().execute(new Runnable() { @Override public void run() { try { req.message = buildSharingMessage(params); } catch (JSONException e) { Log.e(TAG, "Failed to build message." + e); } api.sendReq(req); Log.d(TAG, "Message sent."); } }); // save the current callback context currentCallbackContext = callbackContext; return true; } protected boolean sendAuthRequest(JSONArray args, CallbackContext callbackContext) { final IWXAPI api = getWXAPI(true); int length = args.length(); final SendAuth.Req req = new SendAuth.Req(); try { if (length == 1) { req.scope = args.getString(0); } else if (length == 2) { req.scope = args.getString(0); req.state = args.getString(1); } else { req.scope = "snsapi_userinfo"; req.state = "wechat"; } } catch (Exception e) { req.scope = "snsapi_userinfo"; req.state = "wechat"; Log.e(TAG, e.getMessage()); } api.sendReq(req); currentCallbackContext = callbackContext; return true; } protected boolean sendPaymentRequest(JSONArray args, CallbackContext callbackContext) { final IWXAPI api = getWXAPI(true); // check if # of arguments is correct if (args.length() != 1) { callbackContext.error(ERROR_INVALID_PARAMETERS); return true; } PayReq req = new PayReq(); try { final JSONObject params = args.getJSONObject(0); req.openId = getAppId(); req.partnerId = params.getString("mch_id"); req.prepayId = params.getString("prepay_id"); req.nonceStr = params.getString("nonce"); req.timeStamp = params.getString("timestamp"); req.sign = params.getString("sign"); req.packageValue = "Sign=WXPay"; } catch (Exception e) { Log.e(TAG, e.getMessage()); callbackContext.error(ERROR_INVALID_PARAMETERS); return true; } api.sendReq(req); currentCallbackContext = callbackContext; return true; } protected boolean isInstalled(CallbackContext callbackContext) { final IWXAPI api = getWXAPI(true); if (!api.isWXAppInstalled()) { callbackContext.success(0); } else { callbackContext.success(1); } return true; } protected WXMediaMessage buildSharingMessage(JSONObject params) throws JSONException { Log.d(TAG, "Start building message."); // media parameters WXMediaMessage.IMediaObject mediaObject = null; WXMediaMessage wxMediaMessage = new WXMediaMessage(); if (params.has(KEY_ARG_TEXT)) { WXTextObject textObject = new WXTextObject(); textObject.text = params.getString(KEY_ARG_TEXT); mediaObject = textObject; wxMediaMessage.description = textObject.text; } else { JSONObject message = params.getJSONObject(KEY_ARG_MESSAGE); JSONObject media = message.getJSONObject(KEY_ARG_MESSAGE_MEDIA); wxMediaMessage.title = message.getString(KEY_ARG_MESSAGE_TITLE); wxMediaMessage.description = message .getString(KEY_ARG_MESSAGE_DESCRIPTION); // thumbnail Bitmap thumbnail = getBitmap(message, KEY_ARG_MESSAGE_THUMB); if (thumbnail != null) { wxMediaMessage.setThumbImage(thumbnail); } // check types int type = media.has(KEY_ARG_MESSAGE_MEDIA_TYPE) ? media .getInt(KEY_ARG_MESSAGE_MEDIA_TYPE) : TYPE_WX_SHARING_WEBPAGE; switch (type) { case TYPE_WX_SHARING_APP: break; case TYPE_WX_SHARING_EMOTION: break; case TYPE_WX_SHARING_FILE: break; case TYPE_WX_SHARING_IMAGE: mediaObject = new WXImageObject(getBitmap(message.getJSONObject(KEY_ARG_MESSAGE_MEDIA), KEY_ARG_MESSAGE_MEDIA_IMAGE)); case TYPE_WX_SHARING_MUSIC: break; case TYPE_WX_SHARING_VIDEO: break; case TYPE_WX_SHARING_WEBPAGE: default: mediaObject = new WXWebpageObject(media .getString(KEY_ARG_MESSAGE_MEDIA_WEBPAGEURL)); } } wxMediaMessage.mediaObject = mediaObject; return wxMediaMessage; } protected IWXAPI getWXAPI() { return getWXAPI(true); } protected IWXAPI getWXAPI(boolean register) { String appId = getAppId(); if (wxAPI == null) { wxAPI = WXAPIFactory.createWXAPI(webView.getContext(), appId, true); } if (register) { wxAPI.registerApp(appId); } return wxAPI; } private String buildTransaction() { return String.valueOf(System.currentTimeMillis()); } private String buildTransaction(final String type) { return type + System.currentTimeMillis(); } protected Bitmap getBitmap(JSONObject message, String key) { HttpURLConnection conn = null; InputStream is = null; Bitmap bmp = null; String url = null; try { url = message.getString(key); if (URLUtil.isHttpUrl(url) || URLUtil.isHttpsUrl(url)) { conn = (HttpURLConnection)new URL(url).openConnection(); is = conn.getInputStream(); } else { if (url.startsWith(EXTERNAL_STORAGE_IMAGE_PREFIX)) { // external path url = Environment.getExternalStorageDirectory().getAbsolutePath() + url.substring(EXTERNAL_STORAGE_IMAGE_PREFIX.length()); is = new FileInputStream(url); } else if (!url.startsWith("/")) { // relative path is = cordova.getActivity().getApplicationContext().getAssets().open(url); } else { is = new FileInputStream(url); } } bmp = BitmapFactory.decodeStream(is); is.close(); } catch (Exception e) { Log.e(TAG, "Failed to decode image at " + url, e); bmp = null; } finally { if (conn != null) { conn.disconnect(); } } return bmp; } protected String getAppId() { if (this.appId == null) { this.appId = preferences.getString(WXAPPID_PROPERTY_KEY, ""); } return this.appId; } }