提交 e0706898 作者: edy

feat: 安卓增加相关方法

上级 7bedddd4
...@@ -74,5 +74,5 @@ android { ...@@ -74,5 +74,5 @@ android {
} }
dependencies { dependencies {
implementation 'androidx.lifecycle:lifecycle-process:2.8.5' // implementation 'androidx.lifecycle:lifecycle-process:2.8.5'
} }
package com.example.aivoice_plugin package com.example.aivoice_plugin
import android.app.Application
import android.content.Context import android.content.Context
import androidx.annotation.NonNull import androidx.annotation.NonNull
import com.bytedance.speech.speechengine.SpeechEngineGenerator
import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.EventChannel.EventSink
import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.MethodCallHandler
...@@ -10,7 +14,7 @@ import io.flutter.plugin.common.MethodChannel.Result ...@@ -10,7 +14,7 @@ import io.flutter.plugin.common.MethodChannel.Result
/** AivoicePlugin */ /** AivoicePlugin */
class AivoicePlugin: FlutterPlugin, MethodCallHandler { class AivoicePlugin: FlutterPlugin, MethodCallHandler, EventChannel.StreamHandler {
/// The MethodChannel that will the communication between Flutter and native Android /// The MethodChannel that will the communication between Flutter and native Android
...@@ -20,28 +24,51 @@ class AivoicePlugin: FlutterPlugin, MethodCallHandler { ...@@ -20,28 +24,51 @@ class AivoicePlugin: FlutterPlugin, MethodCallHandler {
private lateinit var channel : MethodChannel private lateinit var channel : MethodChannel
private var context: Context? = null private var context: Context? = null
private var asrConfig : Map<String, String>? = null
private val eventChannel: EventChannel? = null
private var events: EventSink? = null
private val asrCenter: AsrCenter by lazy {
AsrCenter(context, AsrCenter.AsrCenterCallback {
sendMessageToFlutter(it)
})
}
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
context = flutterPluginBinding.getApplicationContext(); context = flutterPluginBinding.applicationContext;
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "aivoice_plugin") channel = MethodChannel(flutterPluginBinding.binaryMessenger, "aivoice_plugin")
channel.setMethodCallHandler(this) channel.setMethodCallHandler(this)
val eventChannel = EventChannel(flutterPluginBinding.binaryMessenger, "aivoice_plugin/events")
eventChannel.setStreamHandler(this)
} }
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
when (call.method) { when (call.method) {
"initEngine" -> { "initEngine" -> {
println("来自安卓222")
asrConfig = call.arguments as Map<String, String>?
// 现有的实现 // 现有的实现
asrCenter.initEngineBtnClicked()
} }
"stopEngine" -> { "stopEngine" -> {
// 现有的实现 // 现有的实现
asrCenter.stopEngineBtnClicked()
} }
"uninitEngine" -> { "uninitEngine" -> {
// 现有的实现 // 现有的实现
asrCenter.uninitEngineBtnClicked()
} }
"startOrStopEngine" -> { "startOrStopEngine" -> {
// 现有的实现 // 现有的实现
asrCenter.startEngineBtnClicked()
} }
"prepareEnvironment" -> { "prepareEnvironment" -> {
// 现有的实现 val re = SpeechEngineGenerator.PrepareEnvironment(context, context as Application?)
println("prepare:===>$re")
} }
"ttsStartEngineBtnClick" -> { "ttsStartEngineBtnClick" -> {
println("来自安卓") println("来自安卓")
...@@ -87,7 +114,23 @@ class AivoicePlugin: FlutterPlugin, MethodCallHandler { ...@@ -87,7 +114,23 @@ class AivoicePlugin: FlutterPlugin, MethodCallHandler {
} }
} }
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null) channel.setMethodCallHandler(null)
} }
override fun onListen(arguments: Any?, events: EventSink) {
this.events = events
// 你可以在这里发送初始事件
// sendMessageToFlutter("Initial message from Android")
}
override fun onCancel(arguments: Any?) {
this.events = null
}
fun sendMessageToFlutter(message: Map<*, *>?) {
if (events != null) {
events?.success(message)
}
}
} }
...@@ -6,19 +6,17 @@ package com.example.aivoice_plugin; ...@@ -6,19 +6,17 @@ package com.example.aivoice_plugin;
import android.Manifest; import android.Manifest;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment; import android.os.Environment;
import android.os.Handler; import android.os.Handler;
import android.text.method.ScrollingMovementMethod;
import android.util.Log; import android.util.Log;
import android.view.MotionEvent;
import android.widget.Button;
import android.widget.TextView;
import android.content.ContextWrapper;
//import androidx.lifecycle.LifecycleObserver; //import androidx.lifecycle.LifecycleObserver;
import androidx.core.content.ContextCompat; //import androidx.lifecycle.ProcessLifecycleOwner;
import androidx.lifecycle.ProcessLifecycleOwner;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import java.io.File; import java.io.File;
...@@ -30,14 +28,21 @@ import com.bytedance.speech.speechengine.SpeechEngineGenerator; ...@@ -30,14 +28,21 @@ import com.bytedance.speech.speechengine.SpeechEngineGenerator;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import java.io.File; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
public class AsrCenter implements SpeechEngine.SpeechListener {
public class AsrActivity implements SpeechEngine.SpeechListener { // 定义回调接口
public interface AsrCenterCallback {
void onRevive(Map<String, Object> message);
}
private Context _context; private Context _context = null;
// Record // Record
private Handler recordHandler = null; private Handler recordHandler = null;
...@@ -54,21 +59,24 @@ public class AsrActivity implements SpeechEngine.SpeechListener { ...@@ -54,21 +59,24 @@ public class AsrActivity implements SpeechEngine.SpeechListener {
private SpeechEngine mSpeechEngine = null; private SpeechEngine mSpeechEngine = null;
private boolean mEngineStarted = false; private boolean mEngineStarted = false;
public AsrCenterCallback _callback;
// Permissions // Permissions
private static final List<String> ASR_PERMISSIONS = Collections.singletonList( private static final List<String> ASR_PERMISSIONS = Collections.singletonList(
Manifest.permission.RECORD_AUDIO Manifest.permission.RECORD_AUDIO
); );
// StreamRecorder // StreamRecorder
private SpeechStreamRecorder mStreamRecorder; private final SpeechStreamRecorder mStreamRecorder;
// Statistics // Statistics
private long mFinishTalkingTimestamp = -1; private long mFinishTalkingTimestamp = -1;
@SuppressLint({"ClickableViewAccessibility", "InflateParams", "HardwareIds", "UseCompatLoadingForDrawables"}) @SuppressLint({"ClickableViewAccessibility", "InflateParams", "HardwareIds", "UseCompatLoadingForDrawables"})
public AsrActivity(Context context) { public AsrCenter(Context context,AsrCenterCallback callback ) {
_context = context; _context = context;
_callback = callback;
Log.i(SpeechDemoDefines.TAG, "Asr onCreate"); Log.i(SpeechDemoDefines.TAG, "Asr onCreate");
...@@ -300,21 +308,21 @@ public class AsrActivity implements SpeechEngine.SpeechListener { ...@@ -300,21 +308,21 @@ public class AsrActivity implements SpeechEngine.SpeechListener {
} }
} }
private void initEngineBtnClicked() { public void initEngineBtnClicked() {
if (mEngineStarted) { if (mEngineStarted) {
return; return;
} }
initEngine(); initEngine();
} }
private void uninitEngineBtnClicked() { public void uninitEngineBtnClicked() {
if (mEngineStarted) { if (mEngineStarted) {
return; return;
} }
uninitEngine(); uninitEngine();
} }
private void startEngineBtnClicked() { public void startEngineBtnClicked() {
Log.i(SpeechDemoDefines.TAG, "配置启动参数."); Log.i(SpeechDemoDefines.TAG, "配置启动参数.");
configStartAsrParams(); configStartAsrParams();
//【可选配置】是否启用云端自动判停,仅一句话识别场景生效 //【可选配置】是否启用云端自动判停,仅一句话识别场景生效
...@@ -342,7 +350,7 @@ public class AsrActivity implements SpeechEngine.SpeechListener { ...@@ -342,7 +350,7 @@ public class AsrActivity implements SpeechEngine.SpeechListener {
clearResultText(); clearResultText();
} }
private void stopEngineBtnClicked() { public void stopEngineBtnClicked() {
Log.i(SpeechDemoDefines.TAG, "关闭引擎(异步)"); Log.i(SpeechDemoDefines.TAG, "关闭引擎(异步)");
Log.i(SpeechDemoDefines.TAG, "Directive: DIRECTIVE_STOP_ENGINE"); Log.i(SpeechDemoDefines.TAG, "Directive: DIRECTIVE_STOP_ENGINE");
mSpeechEngine.sendDirective(SpeechEngineDefines.DIRECTIVE_STOP_ENGINE, ""); mSpeechEngine.sendDirective(SpeechEngineDefines.DIRECTIVE_STOP_ENGINE, "");
...@@ -495,6 +503,10 @@ public class AsrActivity implements SpeechEngine.SpeechListener { ...@@ -495,6 +503,10 @@ public class AsrActivity implements SpeechEngine.SpeechListener {
text += "\nreqid: " + reader.getString("reqid"); text += "\nreqid: " + reader.getString("reqid");
text += "\nresponse_delay: " + response_delay; text += "\nresponse_delay: " + response_delay;
} }
Map<String, Object> map = new HashMap<>();
map.put("text",text);
map.put("isFinal", isFinal);
_callback.onRevive(map);
setResultText(text); setResultText(text);
} catch (JSONException e) { } catch (JSONException e) {
e.printStackTrace(); e.printStackTrace();
...@@ -521,4 +533,28 @@ public class AsrActivity implements SpeechEngine.SpeechListener { ...@@ -521,4 +533,28 @@ public class AsrActivity implements SpeechEngine.SpeechListener {
public void clearResultText() { public void clearResultText() {
} }
// /**
// * check and request multiple permissions
// * @param permissions: permission list
// * @return if all permissions already granted.
// */
// public boolean requestPermission(List<String> permissions) {
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// ArrayList<String> unAuthedPermission = new ArrayList<>();
// for (String permission : permissions) {
// if (ContextCompat.checkSelfPermission(_context, permission)
// != PackageManager.PERMISSION_GRANTED) {
// unAuthedPermission.add(permission);
// }
// }
// if (unAuthedPermission.isEmpty()) {
// return true;
// }
// ActivityCompat.requestPermissions(_context.getac, unAuthedPermission.toArray(new String[0]), 999);
// return false;
// } else {
// return true;
// }
// }
} }
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<application <application
android:label="aivoice_plugin_example" android:label="aivoice_plugin_example"
android:name="${applicationName}" android:name="${applicationName}"
...@@ -43,8 +44,11 @@ ...@@ -43,8 +44,11 @@
</queries> </queries>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.RECORD_AUDIO"
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> tools:ignore="ManifestOrder" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
......
package com.example.aivoice_plugin_example package com.example.aivoice_plugin_example
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity()
\ No newline at end of file class MainActivity: FlutterActivity()
...@@ -53,6 +53,88 @@ class _MyAppState extends State<MyApp> { ...@@ -53,6 +53,88 @@ class _MyAppState extends State<MyApp> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var ttsView = Row(
children: [
TextButton(
onPressed: () {
_aivoicePlugin.ttsInitEngine(configMap);
},
child: const Text('init')),
TextButton(
onPressed: () {
_aivoicePlugin.ttsStartEngineBtnClick(
{"text": "引擎启动成功,收到该回调后,在单次合成场景下收到该回调时语音合成已经开始,同时数据字段为该次请求的请求 ID; 连续合成场景下还需要再发送合成指令,才真正的开始合成。"});
},
child: const Text('start')),
TextButton(
onPressed: () {
_aivoicePlugin.ttsStopEngineBtnClicked();
},
child: const Text('stop')),
TextButton(
onPressed: () {
_aivoicePlugin.ttsSynthesis({});
},
child: const Text('合成')),
TextButton(
onPressed: () {
_aivoicePlugin.ttsStartEngineBtnClick(
{"text": "引擎启动成功,收到该回调后,在单次合成场景下收到该回调时语音合成已经开始,同时数据字段为该次请求的请求 ID; 连续合成场景下还需要再发送合成指令,才真正的开始合成。"});
_aivoicePlugin.ttsSynthesis({});
},
child: const Text('一件开始'))
// TextButton(
// onPressed: () {
// _aivoicePlugin.initEngine(configMap);
// },
// child: const Text('init')),
// TextButton(
// onPressed: () {
// _aivoicePlugin.startOrStopEngine(true);
// },
// child: const Text('start')),
// TextButton(
// onPressed: () {
// _aivoicePlugin.stopEngine();
// },
// child: const Text('stop')),
],
);
var asrView = Row(
children: [
TextButton(
onPressed: () {
_aivoicePlugin.initEngine(configMap);
},
child: const Text('init2')),
TextButton(
onPressed: () {
_aivoicePlugin.startOrStopEngine(true);
},
child: const Text('start')),
TextButton(
onPressed: () {
_aivoicePlugin.stopEngine();
},
child: const Text('stop')),
// TextButton(
// onPressed: () {
// _aivoicePlugin.initEngine(configMap);
// },
// child: const Text('init')),
// TextButton(
// onPressed: () {
// _aivoicePlugin.startOrStopEngine(true);
// },
// child: const Text('start')),
// TextButton(
// onPressed: () {
// _aivoicePlugin.stopEngine();
// },
// child: const Text('stop')),
],
);
return MaterialApp( return MaterialApp(
home: Scaffold( home: Scaffold(
appBar: AppBar( appBar: AppBar(
...@@ -65,53 +147,7 @@ class _MyAppState extends State<MyApp> { ...@@ -65,53 +147,7 @@ class _MyAppState extends State<MyApp> {
], ],
), ),
), ),
floatingActionButton: Row( floatingActionButton: asrView,
children: [
TextButton(
onPressed: () {
_aivoicePlugin.ttsInitEngine(configMap);
},
child: const Text('init')),
TextButton(
onPressed: () {
_aivoicePlugin.ttsStartEngineBtnClick(
{"text": "引擎启动成功,收到该回调后,在单次合成场景下收到该回调时语音合成已经开始,同时数据字段为该次请求的请求 ID; 连续合成场景下还需要再发送合成指令,才真正的开始合成。"});
},
child: const Text('start')),
TextButton(
onPressed: () {
_aivoicePlugin.ttsStopEngineBtnClicked();
},
child: const Text('stop')),
TextButton(
onPressed: () {
_aivoicePlugin.ttsSynthesis({});
},
child: const Text('合成')),
TextButton(
onPressed: () {
_aivoicePlugin.ttsStartEngineBtnClick(
{"text": "引擎启动成功,收到该回调后,在单次合成场景下收到该回调时语音合成已经开始,同时数据字段为该次请求的请求 ID; 连续合成场景下还需要再发送合成指令,才真正的开始合成。"});
_aivoicePlugin.ttsSynthesis({});
},
child: const Text('一件开始'))
// TextButton(
// onPressed: () {
// _aivoicePlugin.initEngine(configMap);
// },
// child: const Text('init')),
// TextButton(
// onPressed: () {
// _aivoicePlugin.startOrStopEngine(true);
// },
// child: const Text('start')),
// TextButton(
// onPressed: () {
// _aivoicePlugin.stopEngine();
// },
// child: const Text('stop')),
],
),
), ),
); );
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论