diff --git a/example/index.html b/example/index.html index 266c234..115c2c5 100755 --- a/example/index.html +++ b/example/index.html @@ -216,6 +216,19 @@ }, (error) => { alert("Send message fail: " + error.description); }); + //这里的路径以android为例 + // var videoFilePath = "sdcard/DCIM/1.mp4"; + // var videoFileName = "xxxxxx"; + // var videoImagePath = "sdcard/DCIM/1.png"; + // var videoImageFormat = "png"; + // var videoDuration = 10; + // window.JMessage.sendVideoMessage({'type': 'single','username': username,'appKey': appKey, + // "videoFilePath":videoFilePath,"videoFileName":videoFileName,"videoImagePath":videoImagePath,"videoImageFormat":videoImageFormat,"videoDuration":videoDuration}, + // (msg) => { + // console.log("sendVideo success"); + // },(error) => { + // console.log("sendVideo error:"+error.description); + // }); } function addConversation() { diff --git a/package.json b/package.json index b8c55c2..527183a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jmessage-phonegap-plugin", - "version": "4.1.1", + "version": "4.1.2", "description": "JMessage Cordova Plugin.", "cordova": { "id": "jmessage-phonegap-plugin", diff --git a/src/android/JMessagePlugin.java b/src/android/JMessagePlugin.java index 6c3a0be..ebc357c 100644 --- a/src/android/JMessagePlugin.java +++ b/src/android/JMessagePlugin.java @@ -4,8 +4,10 @@ import android.app.Activity; import android.content.Intent; import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.media.MediaPlayer; import android.net.Uri; +import android.os.Environment; import android.text.TextUtils; import android.util.Log; @@ -21,6 +23,7 @@ import java.io.File; import java.io.FileNotFoundException; +import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; @@ -46,6 +49,7 @@ import cn.jpush.im.android.api.content.ImageContent; import cn.jpush.im.android.api.content.LocationContent; import cn.jpush.im.android.api.content.TextContent; +import cn.jpush.im.android.api.content.VideoContent; import cn.jpush.im.android.api.content.VoiceContent; import cn.jpush.im.android.api.enums.ContentType; import cn.jpush.im.android.api.event.ChatRoomMessageEvent; @@ -628,6 +632,57 @@ void sendVoiceMessage(JSONArray data, CallbackContext callback) { } } + void sendVideoMessage(JSONArray data, CallbackContext callback) { + boolean hasPermission = PermissionHelper.hasPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE); + if (!hasPermission) { + handleResult(ERR_CODE_PERMISSION, ERR_MSG_PERMISSION_WRITE_EXTERNAL_STORAGE, callback); + return; + } + String videoFilePath; + String videoFileName; + String videoImagePath; + String videoImageFormat; + int videoDuration; + Map extras = null; + MessageSendingOptions messageSendingOptions = null; + Conversation conversation; + try { + JSONObject params = data.getJSONObject(0); + conversation = JMessageUtils.createConversation(params); + if (conversation == null) { + handleResult(ERR_CODE_CONVERSATION, ERR_MSG_CONVERSATION, callback); + return; + } + videoFilePath = params.getString("videoFilePath"); + videoFileName = params.getString("videoFileName"); + videoImagePath = params.getString("videoImagePath"); + videoImageFormat = params.getString("videoImageFormat"); + videoDuration = params.getInt("videoDuration"); + if (params.has("extras")) { + extras = fromJson(params.getJSONObject("extras")); + } + if (params.has("messageSendingOptions")) { + messageSendingOptions = toMessageSendingOptions(params.getJSONObject("messageSendingOptions")); + } + } catch (JSONException e) { + e.printStackTrace(); + handleResult(ERR_CODE_PARAMETER, ERR_MSG_PARAMETER, callback); + return; + } + try { + Bitmap thumbImage = BitmapFactory.decodeFile(videoImagePath); + File videoFile = new File(videoFilePath); + VideoContent videoContent = new VideoContent(thumbImage, videoImageFormat, videoFile, videoFileName, videoDuration); + if (extras != null) { + videoContent.setExtras(extras); + } + sendMessage(conversation, videoContent, messageSendingOptions, callback); + } catch (IOException e) { + e.printStackTrace(); + handleResult(ERR_CODE_FILE, ERR_MSG_FILE, callback); + } + } + void sendCustomMessage(JSONArray data, CallbackContext callback) { try { JSONObject params = data.getJSONObject(0); @@ -1061,6 +1116,48 @@ public void onComplete(int status, String desc, File file) { }); } + void downloadVideoFile(JSONArray data, final CallbackContext callback) { + final Message msg; + + try { + JSONObject params = data.getJSONObject(0); + msg = JMessageUtils.getMessage(params); + if (msg == null) { + handleResult(ERR_CODE_MESSAGE, ERR_MSG_MESSAGE, callback); + return; + } + } catch (JSONException e) { + e.printStackTrace(); + handleResult(ERR_CODE_PARAMETER, ERR_MSG_PARAMETER, callback); + return; + } + + if (msg.getContentType() != ContentType.file) { + handleResult(ERR_CODE_MESSAGE, "Message type isn't video", callback); + return; + } + + VideoContent content = (VideoContent) msg.getContent(); + content.downloadVideoFile(msg, new DownloadCompletionCallback() { + @Override + public void onComplete(int status, String desc, File file) { + if (status == 0) { + JSONObject result = new JSONObject(); + try { + result.put("messageId", msg.getId()); + result.put("filePath", file.getAbsolutePath()); + } catch (JSONException e) { + e.printStackTrace(); + } + handleResult(result, status, desc, callback); + + } else { + handleResult(status, desc, callback); + } + } + }); + } + void downloadFile(JSONArray data, final CallbackContext callback) { final Message msg; @@ -2630,46 +2727,67 @@ public void onEvent(OfflineMessageEvent event) throws JSONException { final int fI = i; switch (msg.getContentType()) { - case image: - ((ImageContent) msg.getContent()).downloadThumbnailImage(msg, new DownloadCompletionCallback() { - @Override - public void onComplete(int status, String desc, File file) { - if (fI == fLatestMediaMessageIndex) { - for (Message msg : offlineMsgList) { - msgJsonArr.put(toJson(msg)); - } - try { - json.put("messageArray", msgJsonArr); - } catch (JSONException e) { - e.printStackTrace(); - } + case image: + ((ImageContent) msg.getContent()).downloadThumbnailImage(msg, new DownloadCompletionCallback() { + @Override + public void onComplete(int status, String desc, File file) { + if (fI == fLatestMediaMessageIndex) { + for (Message msg : offlineMsgList) { + msgJsonArr.put(toJson(msg)); + } + try { + json.put("messageArray", msgJsonArr); + } catch (JSONException e) { + e.printStackTrace(); + } - JSONObject eventJson = toJson("syncOfflineMessage", json); - eventSuccess(eventJson); - } - } - }); - break; - case voice: - ((VoiceContent) msg.getContent()).downloadVoiceFile(msg, new DownloadCompletionCallback() { - @Override - public void onComplete(int status, String desc, File file) { - if (fI == fLatestMediaMessageIndex) { - for (Message msg : offlineMsgList) { - msgJsonArr.put(toJson(msg)); + JSONObject eventJson = toJson("syncOfflineMessage", json); + eventSuccess(eventJson); } - try { - json.put("messageArray", msgJsonArr); - } catch (JSONException e) { - e.printStackTrace(); + } + }); + break; + case voice: + ((VoiceContent) msg.getContent()).downloadVoiceFile(msg, new DownloadCompletionCallback() { + @Override + public void onComplete(int status, String desc, File file) { + if (fI == fLatestMediaMessageIndex) { + for (Message msg : offlineMsgList) { + msgJsonArr.put(toJson(msg)); + } + try { + json.put("messageArray", msgJsonArr); + } catch (JSONException e) { + e.printStackTrace(); + } + + JSONObject eventJson = toJson("syncOfflineMessage", json); + eventSuccess(eventJson); } + } + }); + break; + case video: + ((VideoContent) msg.getContent()).downloadVideoFile(msg, new DownloadCompletionCallback() { + @Override + public void onComplete(int status, String desc, File file) { + if (fI == fLatestMediaMessageIndex) { + for (Message msg : offlineMsgList) { + msgJsonArr.put(toJson(msg)); + } + try { + json.put("messageArray", msgJsonArr); + } catch (JSONException e) { + e.printStackTrace(); + } - JSONObject eventJson = toJson("syncOfflineMessage", json); - eventSuccess(eventJson); + JSONObject eventJson = toJson("syncOfflineMessage", json); + eventSuccess(eventJson); + } } - } - }); - default: + }); + break; + default: } } } diff --git a/src/android/JMessageUtils.java b/src/android/JMessageUtils.java index a38a906..21e1858 100644 --- a/src/android/JMessageUtils.java +++ b/src/android/JMessageUtils.java @@ -2,6 +2,7 @@ import android.graphics.Bitmap; import android.os.Environment; +import android.util.Log; import org.apache.cordova.CallbackContext; import org.json.JSONArray; @@ -39,6 +40,7 @@ static void handleResult(int status, String desc, CallbackContext callback) { callback.success(); } else { try { + JSONObject errorObject = getErrorObject(status, desc); callback.error(getErrorObject(status, desc)); } catch (JSONException e) { e.printStackTrace(); diff --git a/src/ios/Plugins/JMessageHelper.m b/src/ios/Plugins/JMessageHelper.m index 1c73b25..c01f712 100644 --- a/src/ios/Plugins/JMessageHelper.m +++ b/src/ios/Plugins/JMessageHelper.m @@ -117,7 +117,7 @@ - (void)onReceiveUserLoginStatusChangeEvent:(JMSGUserLoginStatusChangeEvent *)ev switch (event.eventType) { case kJMSGEventNotificationLoginKicked: [[NSNotificationCenter defaultCenter] postNotificationName:kJJMessageLoginStateChanged - object:@{@"type":@"user_kicked"}]; + object:@{@"type":@"user_logout"}]; break; case kJMSGEventNotificationServerAlterPassword: [[NSNotificationCenter defaultCenter] postNotificationName:kJJMessageLoginStateChanged @@ -125,7 +125,11 @@ - (void)onReceiveUserLoginStatusChangeEvent:(JMSGUserLoginStatusChangeEvent *)ev break; case kJMSGEventNotificationUserLoginStatusUnexpected: [[NSNotificationCenter defaultCenter] postNotificationName:kJJMessageLoginStateChanged - object:@{@"type":@"user_login_state_unexpected"}]; + object:@{@"type":@"user_login_status_unexpected"}]; + break; + case kJMSGEventNotificationCurrentUserDeleted: + [[NSNotificationCenter defaultCenter] postNotificationName:kJJMessageLoginStateChanged + object:@{@"type":@"user_deleted"}]; break; } } @@ -502,6 +506,16 @@ - (NSMutableDictionary *)messageToDictionary { dict[@"duration"] = [voiceContent duration]; break; } + case kJMSGContentTypeVideo: { + dict[@"type"] = @"video"; + dict[@"mediaFilePath"] = [self getOriginMediaFilePath]; + JMSGVideoContent *videoContent = (JMSGVideoContent *) self.content; + //dict[@"mediaFilePath"] = [videoContent mediaFilePath]; + dict[@"mediaFileName"] = [videoContent fileName]; + dict[@"videoDuration"] = [videoContent videoThumbImageLocalPath]; + dict[@"videoDuration"] = [videoContent duration]; + break; + } case kJMSGContentTypeCustom: { dict[@"type"] = @"custom"; JMSGCustomContent *customContent = (JMSGCustomContent *) self.content; diff --git a/src/ios/Plugins/JMessagePlugin.h b/src/ios/Plugins/JMessagePlugin.h index 2af77c0..3637c60 100644 --- a/src/ios/Plugins/JMessagePlugin.h +++ b/src/ios/Plugins/JMessagePlugin.h @@ -55,6 +55,7 @@ - (void)downloadThumbImage:(CDVInvokedUrlCommand *)command; - (void)downloadOriginalImage:(CDVInvokedUrlCommand *)command; - (void)downloadVoiceFile:(CDVInvokedUrlCommand *)command; +- (void)downloadVideoFile:(CDVInvokedUrlCommand *)command; - (void)downloadFile:(CDVInvokedUrlCommand *)command; - (void)createConversation:(CDVInvokedUrlCommand *)command; - (void)deleteConversation:(CDVInvokedUrlCommand *)command; diff --git a/src/ios/Plugins/JMessagePlugin.m b/src/ios/Plugins/JMessagePlugin.m index a368b42..83ca83f 100644 --- a/src/ios/Plugins/JMessagePlugin.m +++ b/src/ios/Plugins/JMessagePlugin.m @@ -295,6 +295,28 @@ - (JMSGMessage *)createMessageWithDictionary:(NSDictionary *)param type:(JMSGCon content = [[JMSGVoiceContent alloc] initWithVoiceData:[NSData dataWithContentsOfFile: mediaPath] voiceDuration:@(duration)]; break; } + case kJMSGContentTypeVideo:{ + NSString *videoFilePath = nil; + NSString *videoFileName = nil; + NSString *videoImagePath = nil; + NSNumber *number = nil; + if(param[@"videoFilePath"]){ + videoFilePath = param[@"videoFilePath"]; + } + if(param[@"videoFileName"]){ + videoFileName = param[@"videoFileName"]; + } + if(param[@"videoImagePath"]){ + videoImagePath = param[@"videoImagePath"]; + } + if(param[@"videoDuration"]){ + number = param[@"videoDuration"]; + } + double duration = [number integerValue]; + content = [[JMSGVideoContent alloc] initWithVideoData:[NSData dataWithContentsOfFile:videoFilePath] thumbData:[NSData dataWithContentsOfFile:videoImagePath] duration:@(duration)]; + [(JMSGVideoContent *)content setFileName:videoFileName]; + break; + } case kJMSGContentTypeLocation:{ content = [[JMSGLocationContent alloc] initWithLatitude:param[@"latitude"] longitude:param[@"longitude"] scale:param[@"scale"] address: param[@"address"]]; break; @@ -369,6 +391,10 @@ - (JMSGContentType)convertStringToContentType:(NSString *)str { if ([str isEqualToString:@"voice"]) { return kJMSGContentTypeVoice; } + + if ([str isEqualToString:@"video"]) { + return kJMSGContentTypeVideo; + } if ([str isEqualToString:@"location"]) { return kJMSGContentTypeLocation; @@ -939,6 +965,35 @@ - (void)sendVoiceMessage:(CDVInvokedUrlCommand *)command { }]; } +- (void)sendVideoMessage:(CDVInvokedUrlCommand *)command { + NSDictionary * param = [command argumentAtIndex:0]; + + JMSGOptionalContent *messageSendingOptions = nil; + if (param[@"messageSendingOptions"] && [param[@"messageSendingOptions"] isKindOfClass: [NSDictionary class]]) { + messageSendingOptions = [self convertDicToJMSGOptionalContent:param[@"messageSendingOptions"]]; + } + + JMSGMessage *message = [self createMessageWithDictionary:param type:kJMSGContentTypeVideo]; + if (!message) { + [self returnErrorWithLog:@"cannot create message, check your params and make sure the media resource is valid" command:command]; + return; + } + + [self getConversationWithDictionary:param callback:^(JMSGConversation *conversation, NSError *error) { + if (error) { + [self handleResultWithDictionary: nil command:command error: error]; + return; + } + + self.SendMsgCallbackDic[message.msgId] = command.callbackId; + if (messageSendingOptions) { + [conversation sendMessage:message optionalContent:messageSendingOptions]; + } else { + [conversation sendMessage:message]; + } + }]; +} + - (void)sendCustomMessage:(CDVInvokedUrlCommand *)command { NSDictionary * param = [command argumentAtIndex:0]; @@ -1839,7 +1894,7 @@ - (void)downloadThumbImage:(CDVInvokedUrlCommand *)command { } if (message.contentType != kJMSGContentTypeImage) { - [self returnErrorWithLog:@"It is not voice message" command:command]; + [self returnErrorWithLog:@"It is not image message" command:command]; return; } else { JMSGImageContent *content = (JMSGImageContent *) message.content; @@ -1874,7 +1929,7 @@ - (void)downloadOriginalImage:(CDVInvokedUrlCommand *)command { } if (message.contentType != kJMSGContentTypeImage) { - [self returnErrorWithLog:@"It is not voice message" command:command]; + [self returnErrorWithLog:@"It is not image message" command:command]; return; } else { JMSGImageContent *content = (JMSGImageContent *) message.content; @@ -1912,7 +1967,7 @@ - (void)downloadVoiceFile:(CDVInvokedUrlCommand *)command { } if (message.contentType != kJMSGContentTypeVoice) { - [self returnErrorWithLog:@"It is not image message" command:command]; + [self returnErrorWithLog:@"It is not voice message" command:command]; return; } else { JMSGVoiceContent *content = (JMSGVoiceContent *) message.content; @@ -1931,6 +1986,36 @@ - (void)downloadVoiceFile:(CDVInvokedUrlCommand *)command { }]; } +- (void)downloadVideoFile:(CDVInvokedUrlCommand *)command { + NSDictionary * param = [command argumentAtIndex:0]; + + [self getConversationWithDictionary:param callback:^(JMSGConversation *conversation, NSError *error) { + JMSGMessage *message = [conversation messageWithMessageId:param[@"messageId"]]; + + if (message == nil) { + [self returnErrorWithLog:@"cann't find this message" command: command]; + return; + } + + if (message.contentType != kJMSGContentTypeVideo) { + [self returnErrorWithLog:@"It is not video message" command:command]; + return; + } else { + JMSGVideoContent *content = (JMSGVideoContent *) message.content; + [content videoDataWithProgress:nil completionHandler:^(NSData *data, NSString *objectId, NSError *error) { + if (error) { + [self handleResultWithDictionary: nil command: command error: error]; + return; + } + JMSGFileContent *fileContent = (JMSGFileContent *) message.content; + [self handleResultWithDictionary:@{@"messageId": message.msgId, + @"filePath":[fileContent originMediaLocalPath] ? : @""} + command:command error:error]; + }]; + } + }]; +} + - (void)downloadFile:(CDVInvokedUrlCommand *)command { NSDictionary * param = [command argumentAtIndex:0]; diff --git a/www/JMessagePlugin.js b/www/JMessagePlugin.js index df126c2..8db3854 100644 --- a/www/JMessagePlugin.js +++ b/www/JMessagePlugin.js @@ -283,6 +283,27 @@ var JMessagePlugin = { sendVoiceMessage: function(params, success, error) { exec(success, error, PLUGIN_NAME, "sendVoiceMessage", [params]); }, + /** + * @param {object} params = { + * 'type': String, // 'single' / 'group' + * 'groupId': String, // 当 type 为 'group' 时,groupId 不能为空 + * 'username': String, // 当 type 为 'single' 时,username 不能为空 + * 'appKey': String, // 当 type 为 'single' 时,用于指定对象所属应用的 appKey。如果为空,默认为当前应用。 + * 'roomId': String, // 当 type 为 'chatRoom' 时,roomId 不能为空 + * 'videoFilePath': String, // 本地视频文件路径 + * 'videoFileName': String, // 本地视频文件名 + * 'videoImagePath': String, // 本地视频缩略图路径 + * 'videoImageFormat': String, // 本地视频缩略图格式(ios可不传) + * 'videoDuration': int, // 本地视频播放时长,单位秒 + * 'extras': object, // Optional. 自定义键值对 = {'key1': 'value1'} + * 'messageSendingOptions': MessageSendingOptions // Optional. MessageSendingOptions 对象。 + * } + * @param {function} success = function (msg) {} // 以参数形式返回消息对象。 + * @param {function} error = function ({'code': '错误码', 'description': '错误信息'}) {} + */ + sendVideoMessage: function(params,success,error){ + exec(success, error, PLUGIN_NAME, "sendVideoMessage", [params]); + }, /** * @param {object} params = { * 'type': String, // 'single' / 'group' / 'chatRoom' @@ -705,6 +726,38 @@ var JMessagePlugin = { downloadVoiceFile: function(params, success, error) { exec(success, error, PLUGIN_NAME, "downloadVoiceFile", [params]); }, + /** + * 下载视频消息文件,如果已经下载,会直接返回本地文件路径,不会重复下载。 + * + * @param {object} params = { + * 'type': String, // 'single' / 'group' + * 'groupId': String, // 目标群组 id。 + * 'username': String, // 目标用户名。 + * 'appKey': String, // 目标用户所属 AppKey。 + * 'messageId': string // 指定消息 id。 + * } + * @param {function} success = function ({'messageId': String, 'filePath': string}) {} + * @param {function} error = function ({'code': '错误码', 'description': '错误信息'}) {} + */ + downloadVideoFile: function(params, success, error) { + exec(success, error, PLUGIN_NAME, "downloadVideoFile", [params]); + }, + /** + * 下载视频消息文件,如果已经下载,会直接返回本地文件路径,不会重复下载。 + * + * @param {object} params = { + * 'type': String, // 'single' / 'group' + * 'groupId': String, // 目标群组 id。 + * 'username': String, // 目标用户名。 + * 'appKey': String, // 目标用户所属 AppKey。 + * 'messageId': string // 指定消息 id。 + * } + * @param {function} success = function ({'messageId': String, 'filePath': string}) {} + * @param {function} error = function ({'code': '错误码', 'description': '错误信息'}) {} + */ + downloadVideoFile: function(params, success, error) { + exec(success, error, PLUGIN_NAME, "downloadVideoFile", [params]); + }, /** * 下载文件消息文件,如果已经下载,会直接返回本地文件路径,不会重复下载。 *