Skip to content

Commit

Permalink
Voximplant Flutter SDK 2.4.0
Browse files Browse the repository at this point in the history
  • Loading branch information
VladimirBrejcha committed Oct 14, 2020
1 parent 1212895 commit 57c3199
Show file tree
Hide file tree
Showing 26 changed files with 842 additions and 77 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## 2.4.0
* Update Android and iOS platform code to use Voximplant Android SDK 2.20.4
and Voximplant iOS SDK 2.34.3
* Introduce VIAudioFile API

## 2.3.0
* Update Android and iOS platform code to use Voximplant Android SDK 2.19.0
and Voximplant iOS SDK 2.33.0
Expand Down
4 changes: 2 additions & 2 deletions android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
group 'com.voximplant.flutter_voximplant'
version '2.3.0'
version '2.4.0'

buildscript {
repositories {
Expand Down Expand Up @@ -38,6 +38,6 @@ android {
}

dependencies {
api 'com.voximplant:voximplant-sdk:2.19.0'
api 'com.voximplant:voximplant-sdk:2.20.4'
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package com.voximplant.flutter_voximplant;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;

import com.voximplant.sdk.Voximplant;
import com.voximplant.sdk.hardware.IAudioFile;
import com.voximplant.sdk.hardware.IAudioFileListener;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;

public class AudioFileManager {
private final Map<String, AudioFileModule> mAudioFileModules;
private Handler mHandler = new Handler(Looper.getMainLooper());
private final BinaryMessenger mMessenger;
private final Context mAppContext;

AudioFileManager(BinaryMessenger messenger, Context context) {
this.mMessenger = messenger;
this.mAudioFileModules = new HashMap<>();
this.mAppContext = context;
}

void handleMethodCall(MethodCall call, MethodChannel.Result result) {
if (call.arguments == null) {
mHandler.post(() -> result.error(VoximplantErrors.ERROR_INVALID_ARGUMENTS, call.method + ": Invalid arguments", null));
return;
}
switch (call.method) {
case "initWithFile":
initWithFile(call, result);
break;
case "loadFile":
loadFile(call, result);
break;
case "releaseResources":
releaseResources(call, result);
break;
default:
handleInModule(call, result);
break;
}
}

void initWithFile(MethodCall call, MethodChannel.Result result) {
String fileName = call.argument("name");
if (fileName == null) {
mHandler.post(() -> result.error(VoximplantErrors.ERROR_INVALID_ARGUMENTS, call.method + ": name is null", null));
return;
}
String fileUsage = call.argument("usage");
if (fileUsage == null) {
mHandler.post(() -> result.error(VoximplantErrors.ERROR_INVALID_ARGUMENTS, call.method + ": usage is null", null));
return;
}
int rawId = mAppContext.getResources().getIdentifier(fileName, "raw", mAppContext.getPackageName());
IAudioFile audioFile = Voximplant.createAudioFile(mAppContext, rawId, Utils.convertStringToAudioFileUsage(fileUsage));
if (audioFile == null) {
mHandler.post(() -> result.error(VoximplantErrors.ERROR_INVALID_ARGUMENTS, call.method + ": failed to locate audio file", null));
return;
}
String fileId = UUID.randomUUID().toString();
AudioFileModule module = new AudioFileModule(mMessenger, audioFile, fileId, null);
mAudioFileModules.put(fileId, module);
mHandler.post(() -> result.success(fileId));
}

void loadFile(MethodCall call, MethodChannel.Result result) {
String fileUrl = call.argument("url");
if (fileUrl == null) {
mHandler.post(() -> result.error(VoximplantErrors.ERROR_INVALID_ARGUMENTS, call.method + ": url is null", null));
return;
}
String fileUsage = call.argument("usage");
if (fileUsage == null) {
mHandler.post(() -> result.error(VoximplantErrors.ERROR_INVALID_ARGUMENTS, call.method + ": usage is null", null));
return;
}
IAudioFile audioFile = Voximplant.createAudioFile(fileUrl, Utils.convertStringToAudioFileUsage(fileUsage));
if (audioFile == null) {
mHandler.post(() -> result.error(VoximplantErrors.ERROR_INVALID_ARGUMENTS, call.method + ": failed to load audio file", null));
return;
}
String fileId = UUID.randomUUID().toString();
AudioFileModule module = new AudioFileModule(mMessenger, audioFile, fileId, result);
mAudioFileModules.put(fileId, module);
}

void releaseResources(MethodCall call, MethodChannel.Result result) {
String fileId = call.argument("fileId");
if (fileId == null) {
mHandler.post(() -> result.error(VoximplantErrors.ERROR_INVALID_ARGUMENTS, call.method + ": fileId is null", null));
return;
}
if (!mAudioFileModules.containsKey(fileId)) {
mHandler.post(() -> result.error(VoximplantErrors.ERROR_INVALID_ARGUMENTS, call.method + ": could'nt find audioFile", null));
return;
}
AudioFileModule module = mAudioFileModules.get(fileId);
if (module == null) {
mHandler.post(() -> result.error(VoximplantErrors.ERROR_INVALID_ARGUMENTS, call.method + ": could'nt find audioFile", null));
return;
}
if (module.mAudioFile != null) {
module.mAudioFile.setAudioFileListener(null);
module.mAudioFile.release();
}
mAudioFileModules.remove(fileId);
mHandler.post(() -> result.success(null));
}

void handleInModule(MethodCall call, MethodChannel.Result result) {
String fileId = call.argument("fileId");
if (fileId == null) {
mHandler.post(() -> result.error(VoximplantErrors.ERROR_INVALID_ARGUMENTS, call.method + ": fileId is null", null));
return;
}
if (!mAudioFileModules.containsKey(fileId)) {
mHandler.post(() -> result.error(VoximplantErrors.ERROR_INVALID_ARGUMENTS, call.method + ": could'nt find audioFile", null));
return;
}
AudioFileModule module = mAudioFileModules.get(fileId);
if (module == null) {
mHandler.post(() -> result.error(VoximplantErrors.ERROR_INVALID_ARGUMENTS, call.method + ": could'nt find audioFile", null));
return;
}
module.handleMethodCall(call, result);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package com.voximplant.flutter_voximplant;

import android.os.Handler;
import android.os.Looper;

import com.voximplant.sdk.hardware.IAudioFile;
import com.voximplant.sdk.hardware.IAudioFileListener;

import java.util.HashMap;
import java.util.Map;

import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;

public class AudioFileModule implements EventChannel.StreamHandler, IAudioFileListener {
final IAudioFile mAudioFile;
private final String mFileId;
private EventChannel mEventChannel;
private EventChannel.EventSink mEventSink;
private Handler mHandler = new Handler(Looper.getMainLooper());
private MethodChannel.Result mLoadFileCompletion;
private MethodChannel.Result mPlayCompletion;
private MethodChannel.Result mStopCompletion;

AudioFileModule(BinaryMessenger messenger, IAudioFile file, String fileId, MethodChannel.Result loadFileCompletion) {
mLoadFileCompletion = loadFileCompletion;
mEventChannel = new EventChannel(messenger, "plugins.voximplant.com/audio_file_events_" + fileId);
mEventChannel.setStreamHandler(this);
mFileId = fileId;
mAudioFile = file;
mAudioFile.setAudioFileListener(this);
}

void handleMethodCall(MethodCall call, MethodChannel.Result result) {
switch (call.method) {
case "play":
play(call, result);
break;
case "stop":
stop(result);
break;
default:
result.notImplemented();
break;
}
}

void play(MethodCall call, MethodChannel.Result result) {
Object looped = call.argument("looped");
if (looped instanceof Boolean) {
if (mAudioFile != null) {
mPlayCompletion = result;
mAudioFile.play((Boolean)looped);
}
} else {
mHandler.post(() -> result.error(VoximplantErrors.ERROR_INVALID_ARGUMENTS, call.method + ": looped is null", null));
}
}

void stop(MethodChannel.Result result) {
if (mAudioFile != null) {
mStopCompletion = result;
mAudioFile.stop(false);
}
}

@Override
public void onStart(IAudioFile audioFile) {
if (mPlayCompletion != null) {
mHandler.post(() -> {
mPlayCompletion.success(null);
mPlayCompletion = null;
});
}
}

@Override
public void onStop(IAudioFile audioFile) {
if (mStopCompletion != null) {
mHandler.post(() -> {
mStopCompletion.success(null);
mStopCompletion = null;
});
} else if (mFileId != null) {
Map<String, Object> params = new HashMap<>();
params.put("name", "didStopPlaying");
sendEvent(params);
}
}

@Override
public void onPrepared(IAudioFile audioFile) {
if (mLoadFileCompletion != null) {
mHandler.post(() -> {
mLoadFileCompletion.success(mFileId);
mLoadFileCompletion = null;
});
}
}

@Override
public void onListen(Object arguments, EventChannel.EventSink events) {
mEventSink = events;
}

@Override
public void onCancel(Object arguments) {
mEventSink = null;
}

private void sendEvent(Map<String, Object> event) {
if (mEventSink != null) {
mHandler.post(() -> mEventSink.success(event));
}
}
}
22 changes: 22 additions & 0 deletions android/src/main/java/com/voximplant/flutter_voximplant/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.voximplant.sdk.call.VideoCodec;
import com.voximplant.sdk.call.VideoStreamType;
import com.voximplant.sdk.client.LoginError;
import com.voximplant.sdk.hardware.AudioFileUsage;
import com.voximplant.sdk.messaging.IErrorEvent;

import static com.voximplant.flutter_voximplant.VoximplantErrors.ERROR_INTERNAL;
Expand Down Expand Up @@ -65,6 +66,8 @@ static String convertLoginErrorToString(LoginError error) {
return VoximplantErrors.ERROR_NETWORK_ISSUES;
case TOKEN_EXPIRED:
return VoximplantErrors.ERROR_TOKEN_EXPIRED;
case MAU_ACCESS_DENIED:
return VoximplantErrors.ERROR_MAU_ACCESS_DENIED;
case INTERNAL_ERROR:
default:
return ERROR_INTERNAL;
Expand All @@ -87,6 +90,8 @@ static String getErrorDescriptionForLoginError(LoginError error) {
return "Connection to the Voximplant Cloud is closed due to network issues.";
case TOKEN_EXPIRED:
return "Token expired.";
case MAU_ACCESS_DENIED:
return "Monthly Active Users (MAU) limit is reached. Payment is required.";
case INTERNAL_ERROR:
default:
return "Internal error.";
Expand Down Expand Up @@ -238,4 +243,21 @@ static int convertVideoStreamTypeToInt(VideoStreamType type) {
return 0;
}
}

static AudioFileUsage convertStringToAudioFileUsage(String usage) {
if (usage == null) {
return AudioFileUsage.UNKNOWN;
}
switch (usage) {
case "incall":
return AudioFileUsage.IN_CALL;
case "notification":
return AudioFileUsage.NOTIFICATION;
case "ringtone":
return AudioFileUsage.RINGTONE;
case "unknown":
default:
return AudioFileUsage.UNKNOWN;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class VoximplantErrors {
static final String ERROR_MEDIA_IS_ON_HOLD = "ERROR_MEDIA_IS_ON_HOLD";

static final String ERROR_ACCOUNT_FROZEN = "ERROR_ACCOUNT_FROZEN";
static final String ERROR_MAU_ACCESS_DENIED = "ERROR_MAU_ACCESS_DENIED";
static final String ERROR_INVALID_PASSWORD = "ERROR_INVALID_PASSWORD";
static final String ERROR_INVALID_STATE = "ERROR_INVALID_STATE";
static final String ERROR_INVALID_USERNAME = "ERROR_INVALID_USERNAME";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ public class VoximplantPlugin implements MethodCallHandler, FlutterPlugin {
private CallManager mCallManager;
private CameraModule mCameraModule;
private MessagingModule mMessagingModule;
private AudioFileManager mAudioFileManager;

public VoximplantPlugin() {
Voximplant.subVersion = "flutter-2.3.0";
Voximplant.subVersion = "flutter-2.4.0";
}

private void configure(Context context, TextureRegistry textures, BinaryMessenger messenger) {
Expand All @@ -40,6 +41,7 @@ private void configure(Context context, TextureRegistry textures, BinaryMessenge
mCameraModule = new CameraModule(context);
mChannel.setMethodCallHandler(this);
mMessagingModule = new MessagingModule(messenger);
mAudioFileManager = new AudioFileManager(messenger, context);
}

public static void registerWith(Registrar registrar) {
Expand Down Expand Up @@ -71,6 +73,8 @@ public void onMethodCall(MethodCall call, Result result) {
String AUDIO_DEVICE = "AudioDevice";
String VIDEO_STREAM = "VideoStream";
String CAMERA = "Camera";
String AUDIO_FILE = "AudioFile";

if (isMethodCallOfType(MESSAGING, call)) {
mMessagingModule.handleMethodCall(excludeMethodType(call), result);

Expand All @@ -97,6 +101,9 @@ public void onMethodCall(MethodCall call, Result result) {
} else if (isMethodCallOfType(CAMERA, call)) {
mCameraModule.handleMethodCall(excludeMethodType(call), result);

} else if (isMethodCallOfType(AUDIO_FILE, call)) {
mAudioFileManager.handleMethodCall(excludeMethodType(call), result);

} else {
result.notImplemented();
}
Expand Down
1 change: 1 addition & 0 deletions example/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
.pub/
/build/
.flutter-plugins-dependencies
.last_build_id

# Android related
**/android/**/gradle-wrapper.jar
Expand Down
Loading

0 comments on commit 57c3199

Please sign in to comment.