This commit is contained in:
2021-02-20 14:03:11 +08:00
16 changed files with 642 additions and 77 deletions

View File

@ -9,6 +9,8 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<application
android:allowBackup="true"

View File

@ -20,9 +20,9 @@ import androidx.appcompat.app.AppCompatActivity;
import chaoran.business.BrandEnum;
import chaoran.business.R;
import chaoran.business.adapter.*;
import chaoran.business.engine.NetworkSettingEngine;
import chaoran.business.engine.impl.NetworkSettingEngine;
import chaoran.business.engine.SettingEngine;
import chaoran.business.engine.TekVoiceEngine;
import chaoran.business.engine.impl.TekVoiceEngine;
import chaoran.business.engine.VoiceEngine;
/**
@ -161,7 +161,7 @@ public class MainActivity extends AppCompatActivity implements ResultListener {
protected void onResume() {
//再次唤醒该页面时,重新加载页面和语音配置
webView.loadUrl(url());
voiceEngine.loadParam();
voiceEngine.reload();
super.onResume();
}

View File

@ -13,7 +13,8 @@ import android.os.Bundle;
import android.widget.*;
import androidx.appcompat.app.AppCompatActivity;
import chaoran.business.R;
import chaoran.business.engine.TekVoiceEngine;
import chaoran.business.engine.OfflineVoiceEngine;
import chaoran.business.engine.impl.TekVoiceEngine;
import chaoran.business.engine.VoiceEngine;
/**
@ -28,7 +29,7 @@ public class VoiceSettingActivity extends AppCompatActivity {
private SharedPreferences sharedPreferences;
private RadioGroup voiceMemberGroup;
private VoiceEngine voiceEngine;
private OfflineVoiceEngine voiceEngine;
@Override
protected void onCreate(Bundle savedInstanceState) {

View File

@ -0,0 +1,135 @@
package chaoran.business.engine;
/*
**********************************************
* DATE PERSON REASON
* 2021-02-19 FXY Created
**********************************************
*/
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.SharedPreferences;
import android.widget.Toast;
import chaoran.business.engine.entity.BlueToothSetting;
import chaoran.business.engine.entity.Setting;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.UUID;
public abstract class BlueToothWrapperEngine implements PrintEngine {
//打印驱动标志
protected static final int PRINT_TYPE = 1664;
//蓝牙标识
private UUID uuid;
//上下文
private Context context;
//蓝牙适配器
protected BluetoothAdapter bluetoothAdapter;
//蓝牙socket对象
private BluetoothSocket bluetoothSocket;
//开启蓝牙且连接设备
public abstract void startThenConnect();
//关闭蓝牙
public abstract void close();
//获取绑定的设备
public abstract BluetoothDevice getBoundDevice();
public BlueToothWrapperEngine(Context context) {
this.context = context;
this.bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
this.uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
}
//蓝牙打印机打印操作
@Override
public void print(File file) {
BluetoothDevice boundDevice = getBoundDevice();
//先获取连接上的设备继续操作
if (null == boundDevice) {
Toast.makeText(context, "蓝牙未连接设备或者连接的设备不是打印机", Toast.LENGTH_LONG).show();
return;
}
new PrintThread(boundDevice, file).start();
}
@Override
public Setting getSetting() {
SharedPreferences sharedPreferences = context.getSharedPreferences("BlueToothEngine", Context.MODE_PRIVATE);
String name = sharedPreferences.getString("name", "");
String address = sharedPreferences.getString("address", "");
return new BlueToothSetting(name, address);
}
@Override
public boolean saveSetting(Setting s) {
BlueToothSetting blueToothSetting = (BlueToothSetting) s;
SharedPreferences sharedPreferences = context.getSharedPreferences("BlueToothEngine", Context.MODE_PRIVATE);
SharedPreferences.Editor edit = sharedPreferences.edit();
edit.putString("name", blueToothSetting.getName());
edit.putString("address", blueToothSetting.getAddress());
edit.commit();
return true;
}
/**
* 连接为客户端
*/
private class PrintThread extends Thread {
private File file;
public PrintThread(BluetoothDevice device, File file) {
this.file = file;
try {
bluetoothSocket = device.createRfcommSocketToServiceRecord(uuid);
} catch (IOException e) {
e.printStackTrace();
}
}
public void run() {
//取消的发现,因为它将减缓连接
bluetoothAdapter.cancelDiscovery();
OutputStream outputStream = null;
FileInputStream fileInputStream = null;
try {
//连接socket
bluetoothSocket.connect();
//连接成功获取输出流
outputStream = bluetoothSocket.getOutputStream();
fileInputStream = new FileInputStream(file);
byte[] data = new byte[1024];
int number;
while ((number = fileInputStream.read(data)) != -1) {
outputStream.write(data, 0, number);
outputStream.flush();
}
fileInputStream.close();
outputStream.close();
} catch (Exception connectException) {
try {
fileInputStream.close();
outputStream.close();
bluetoothSocket.close();
} catch (Exception closeException) {
closeException.printStackTrace();
}
}
}
}
}

View File

@ -0,0 +1,82 @@
package chaoran.business.engine;
/*
**********************************************
* DATE PERSON REASON
* 2021-02-19 FXY Created
**********************************************
*/
import android.content.Context;
import android.content.SharedPreferences;
import chaoran.business.engine.entity.Setting;
import chaoran.business.engine.entity.VoiceSetting;
/**
* 离线语音引擎
*/
public abstract class OfflineVoiceEngine implements VoiceEngine {
protected Context context;
public OfflineVoiceEngine(Context context) {
this.context = context;
}
/**
* 持久化配置到文件
*
* @param setting
* @return
*/
public boolean saveSetting(Setting setting) {
VoiceSetting voiceSetting = (VoiceSetting) setting;
SharedPreferences.Editor editor = context.getSharedPreferences("voiceEngine", Context.MODE_PRIVATE).edit();
editor.putString("voiceSize", voiceSetting.getVoiceSize());
editor.putString("voiceIndicate", voiceSetting.getVoiceIndicate());
editor.putString("voiceSpeed", voiceSetting.getVoiceSpeed());
editor.putString("voiceMember", voiceSetting.getVoiceMember());
editor.commit();
return true;
}
/**
* 设置参数,用于动态改变语音设置
*
* @param voiceMember
* @param voiceSize
* @param voiceIndicate
* @param voiceSpeed
*/
public abstract void setParam(String voiceMember, String voiceSize, String voiceIndicate, String voiceSpeed);
/**
* 获取语音配置参数
*
* @return
*/
public Setting getSetting() {
SharedPreferences sharedPreferences = context.getSharedPreferences("voiceEngine", Context.MODE_PRIVATE);
//设置发音人资源路径
String voiceMember = sharedPreferences.getString("voiceMember", "xiaofeng");
//设置合成语速
String voiceSpeed = sharedPreferences.getString("voiceSpeed", "80");
//设置合成音调
String voiceIndicate = sharedPreferences.getString("voiceIndicate", "50");
//设置合成音量
String voiceSize = sharedPreferences.getString("voiceSize", "100");
VoiceSetting voiceSetting = new VoiceSetting();
voiceSetting.setVoiceMember(voiceMember);
voiceSetting.setVoiceSize(voiceSize);
voiceSetting.setVoiceIndicate(voiceIndicate);
voiceSetting.setVoiceSpeed(voiceSpeed);
return voiceSetting;
}
}

View File

@ -0,0 +1,23 @@
package chaoran.business.engine;
/*
**********************************************
* DATE PERSON REASON
* 2021-02-19 FXY Created
**********************************************
*/
import java.io.File;
/**
* 打印引擎
*/
public interface PrintEngine extends SettingEngine {
/**
* 打印文件,目前仅处理打印文件操作
*
* @param file
*/
public void print(File file);
}

View File

@ -8,15 +8,17 @@ package chaoran.business.engine;
*/
import chaoran.business.engine.entity.Setting;
/**
* 设置引擎
*/
public interface SettingEngine {
//显示设置
public String display();
public Setting getSetting();
//保存设置
public boolean save(String s);
public boolean saveSetting(Setting s);
}

View File

@ -7,24 +7,30 @@ package chaoran.business.engine;
**********************************************
*/
/**
* 语音引擎
*/
public interface VoiceEngine extends SettingEngine {
public interface VoiceEngine {
//开始说话
/**
* 开始说话
*
* @param text
*/
public void startSpeaking(String text);
//停止说话
/**
* 停止说话
*/
public void stopSpeaking();
//销毁资源
/**
* 重启
*/
public void reload();
/**
* 销毁资源
*/
public void destroy();
//装载参数,从配置文件中装载参数
public String loadParam();
//保存参数,保存参数到配置文件中
public boolean saveParam(String voiceMember, String voiceSize, String voiceIndicate, String voiceSpeed);
//设置参数
public void setParam(String voiceMember, String voiceSize, String voiceIndicate, String voiceSpeed);
}

View File

@ -0,0 +1,54 @@
package chaoran.business.engine.entity;
/*
**********************************************
* DATE PERSON REASON
* 2021-02-20 FXY Created
**********************************************
*/
import androidx.annotation.Nullable;
public class BlueToothSetting extends Setting {
//蓝牙-名称
private String name;
//蓝牙-地址
private String address;
public BlueToothSetting(String name, String address) {
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public int hashCode() {
return address.hashCode();
}
@Override
public boolean equals(@Nullable Object obj) {
if (obj instanceof BlueToothSetting) {
BlueToothSetting blueToothSetting = (BlueToothSetting) obj;
return blueToothSetting.getAddress().equals(this.getAddress());
}
return false;
}
}

View File

@ -7,7 +7,7 @@ package chaoran.business.engine.entity;
**********************************************
*/
public class Network {
public class NetworkSetting extends Setting {
private String address;
private Integer port;
private String path;

View File

@ -0,0 +1,12 @@
package chaoran.business.engine.entity;
/*
**********************************************
* DATE PERSON REASON
* 2021-02-19 FXY Created
**********************************************
*/
public abstract class Setting {
}

View File

@ -7,7 +7,10 @@ package chaoran.business.engine.entity;
**********************************************
*/
public class Voice {
/**
* 语音设置基本数据
*/
public class VoiceSetting extends Setting{
private String voiceMember;
private String voiceSize;
private String voiceIndicate;

View File

@ -0,0 +1,110 @@
package chaoran.business.engine.impl;
/*
**********************************************
* DATE PERSON REASON
* 2021-02-19 FXY Created
**********************************************
*/
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.webkit.JavascriptInterface;
import android.widget.Toast;
import chaoran.business.engine.BlueToothWrapperEngine;
import chaoran.business.engine.entity.BlueToothSetting;
import com.google.gson.Gson;
import java.lang.reflect.Method;
/**
* 蓝牙打印机
*/
public class BlueToothPrintEngine extends BlueToothWrapperEngine {
private Context context;
//已匹配设备
private BluetoothDevice match = null;
private Gson gson;
public BlueToothPrintEngine(Context context) {
super(context);
//初始化
gson = new Gson();
this.context = context;
//广播注册
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
context.registerReceiver(mReceiver, filter);
}
@Override
public void startThenConnect() {
// 设置蓝牙可见性最多300秒
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 20);
context.startActivity(intent);
//连接设备
BlueToothSetting setting = (BlueToothSetting) getSetting();
String address = setting.getAddress();
if (null != address && !address.trim().equals("")) {
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
boolean bond = device.createBond();
if (!bond)
Toast.makeText(context, "连接设备失败,设备名:" + setting.getName() + " mac" + setting.getAddress(), Toast.LENGTH_SHORT).show();
}
}
@Override
public void close() {
bluetoothAdapter.disable();
context.unregisterReceiver(mReceiver);
}
@Override
public BluetoothDevice getBoundDevice() {
return match;
}
/**
* 通过广播搜索蓝牙设备
*/
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// 把搜索的设置添加到集合中
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//已经匹配的设备
if (device.getBondState() == BluetoothDevice.BOND_BONDED) { //bond_bonded已经连接成功长连接保持活跃的状态
//保存已匹配且是打印机设备的Mac地址
if (device.getBluetoothClass().getDeviceClass() == PRINT_TYPE) {
saveSetting(new BlueToothSetting(device.getName(), device.getAddress()));
match = device;
}
}
}
}
};
@JavascriptInterface
public String getStringSetting() {
return gson.toJson(getSetting());
}
@JavascriptInterface
public void saveSetting(String s) {
saveSetting(gson.fromJson(s, BlueToothSetting.class));
}
}

View File

@ -1,4 +1,4 @@
package chaoran.business.engine;
package chaoran.business.engine.impl;
/*
**********************************************
@ -11,7 +11,9 @@ package chaoran.business.engine;
import android.content.Context;
import android.content.SharedPreferences;
import android.webkit.JavascriptInterface;
import chaoran.business.engine.entity.Network;
import chaoran.business.engine.SettingEngine;
import chaoran.business.engine.entity.NetworkSetting;
import chaoran.business.engine.entity.Setting;
import com.google.gson.Gson;
/**
@ -20,37 +22,57 @@ import com.google.gson.Gson;
public class NetworkSettingEngine implements SettingEngine {
private Context context;
private Gson gson;
public NetworkSettingEngine(Context context) {
gson = new Gson();
this.context = context;
gson = new Gson();
}
//展示网络设置
@Override
@JavascriptInterface
public String display() {
public Setting getSetting() {
SharedPreferences spf = context.getSharedPreferences("crtech", Context.MODE_PRIVATE);
Integer port = spf.getInt("port", -1);
String address = spf.getString("address", "").replaceAll(" ", "");
String path = spf.getString("path", "").replaceAll(" ", "");
Network network = new Network();
network.setAddress(address);
network.setPort(port);
network.setPath(path);
return gson.toJson(network);
NetworkSetting networkSetting = new NetworkSetting();
networkSetting.setAddress(address);
networkSetting.setPort(port);
networkSetting.setPath(path);
return networkSetting;
}
@Override
@JavascriptInterface
public boolean save(String s) {
Network network = gson.fromJson(s, Network.class);
public boolean saveSetting(Setting setting) {
NetworkSetting networkSetting = (NetworkSetting) setting;
SharedPreferences.Editor editor = context.getSharedPreferences("crtech", Context.MODE_PRIVATE).edit();
editor.putString("address", network.getAddress());
editor.putString("path", network.getPath());
editor.putInt("port", network.getPort());
editor.putString("address", networkSetting.getAddress());
editor.putString("path", networkSetting.getPath());
editor.putInt("port", networkSetting.getPort());
editor.commit();
return true;
}
/**
* 页面显示设置
*
* @return
*/
@JavascriptInterface
public String getStringSetting() {
return gson.toJson(getSetting());
}
/**
* 页面保存设置
*
* @param s
* @return
*/
@JavascriptInterface
public boolean saveSetting(String s) {
return saveSetting(gson.fromJson(s, NetworkSetting.class));
}
}

View File

@ -0,0 +1,116 @@
package chaoran.business.engine.impl;
/*
**********************************************
* DATE PERSON REASON
* 2021-02-19 FXY Created
**********************************************
*/
import android.content.Context;
import android.widget.Toast;
import chaoran.business.engine.PrintEngine;
import chaoran.business.engine.entity.Setting;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
/**
* 简单串口打印机
*/
public class SimplePrintEngine implements PrintEngine {
//ip地址
private String address;
//端口
private Integer port;
//上下文
private Context context;
private SimplePrintEngine() {
}
public SimplePrintEngine(Context context, String address, Integer port) {
this.context = context;
this.address = address;
this.port = port;
}
@Override
public void print(File file) {
print(file, address, port);
}
//--------------------------此处如果需要持久化,参考蓝牙打印机引擎类------------------------------//
@Override
public Setting getSetting() {
return null;
}
@Override
public boolean saveSetting(Setting s) {
return false;
}
//------------------------持久化需要,参考蓝牙打印引擎类-----------------------------------------//
private void print(File file, String address, Integer port) {
//打印机端口默认9100
if (port == -1)
port = 9100;
Socket socket = null;
FileInputStream fis = null;
try {
socket = new Socket(address, port);
OutputStream out = socket.getOutputStream();
fis = new FileInputStream(file);
byte[] buf = new byte[1024];
int len = 0;
//判断是否读到文件末尾
while ((len = fis.read(buf)) != -1) {
out.write(buf, 0, len);
}
//告诉服务端,文件已传输完毕
socket.shutdownOutput();
socket.close();
fis.close();
} catch (IOException e) {
Toast.makeText(context, "打印文件出错!", Toast.LENGTH_SHORT).show();
try {
socket.shutdownOutput();
socket.close();
fis.close();
} catch (IOException ioException) {
Toast.makeText(context, "断开与打印机的连接时出错!", Toast.LENGTH_SHORT).show();
ioException.printStackTrace();
}
}
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
public Context getContext() {
return context;
}
public void setContext(Context context) {
this.context = context;
}
}

View File

@ -1,14 +1,14 @@
package chaoran.business.engine;
package chaoran.business.engine.impl;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.webkit.JavascriptInterface;
import android.widget.Toast;
import chaoran.business.R;
import chaoran.business.engine.entity.Voice;
import chaoran.business.engine.OfflineVoiceEngine;
import chaoran.business.engine.entity.VoiceSetting;
import com.google.gson.Gson;
import com.iflytek.cloud.*;
import com.iflytek.cloud.util.ResourceUtil;
@ -21,11 +21,9 @@ import com.iflytek.cloud.util.ResourceUtil.RESOURCE_TYPE;
**********************************************
*/
public class TekVoiceEngine implements VoiceEngine {
public class TekVoiceEngine extends OfflineVoiceEngine {
private static String TAG = "TekVoiceEngine";
private Gson gson;
// 语音合成对象
private SpeechSynthesizer mTts;
@ -39,13 +37,16 @@ public class TekVoiceEngine implements VoiceEngine {
private Context context;
private Gson gson;
public TekVoiceEngine(Context context) {
super(context);
gson = new Gson();
this.context = context;
// 初始化合成对象
mTts = SpeechSynthesizer.createSynthesizer(context, mTtsInitListener);
//设置合成引擎的参数
loadParam();
reload();
//初始化对话框
mToast = Toast.makeText(context, "", Toast.LENGTH_SHORT);
}
@ -141,36 +142,30 @@ public class TekVoiceEngine implements VoiceEngine {
};
@Override
@JavascriptInterface
public String loadParam() {
public void reload() {
// 装载参数之前先清空参数
mTts.setParameter(SpeechConstant.PARAMS, null);
SharedPreferences sharedPreferences = context.getSharedPreferences("voiceEngine", Context.MODE_PRIVATE);
VoiceSetting voiceSetting = (VoiceSetting) getSetting();
//设置使用本地引擎
mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL);
//设置发音人资源路径
String voicerLocal = sharedPreferences.getString("voiceMember", "xiaofeng");
mTts.setParameter(ResourceUtil.TTS_RES_PATH, getResourcePath(voicerLocal));
mTts.setParameter(ResourceUtil.TTS_RES_PATH, getResourcePath(voiceSetting.getVoiceMember()));
//设置发音人
mTts.setParameter(SpeechConstant.VOICE_NAME, voicerLocal);
mTts.setParameter(SpeechConstant.VOICE_NAME, voiceSetting.getVoiceMember());
//设置合成语速
String voiceSpeed = sharedPreferences.getString("voiceSpeed", "80");
mTts.setParameter(SpeechConstant.SPEED, voiceSpeed);
mTts.setParameter(SpeechConstant.SPEED, voiceSetting.getVoiceSpeed());
//设置合成音调
String voiceIndicate = sharedPreferences.getString("voiceIndicate", "50");
mTts.setParameter(SpeechConstant.PITCH, voiceIndicate);
mTts.setParameter(SpeechConstant.PITCH, voiceSetting.getVoiceIndicate());
//设置合成音量
String voiceSize = sharedPreferences.getString("voiceSize", "100");
mTts.setParameter(SpeechConstant.VOLUME, voiceSize);
mTts.setParameter(SpeechConstant.VOLUME, voiceSetting.getVoiceSize());
//设置播放器音频流类型
mTts.setParameter(SpeechConstant.STREAM_TYPE, "3");
@ -182,26 +177,8 @@ public class TekVoiceEngine implements VoiceEngine {
mTts.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");
mTts.setParameter(SpeechConstant.TTS_AUDIO_PATH, Environment.getExternalStorageDirectory() + "/msc/tts.wav");
Voice voice = new Voice();
voice.setVoiceMember(voicerLocal);
voice.setVoiceSize(voiceSize);
voice.setVoiceIndicate(voiceIndicate);
voice.setVoiceSpeed(voiceSpeed);
return gson.toJson(voice);
}
@Override
@JavascriptInterface
public boolean saveParam(String voiceMember, String voiceSize, String voiceIndicate, String voiceSpeed) {
SharedPreferences.Editor editor = context.getSharedPreferences("voiceEngine", Context.MODE_PRIVATE).edit();
editor.putString("voiceSize", voiceSize);
editor.putString("voiceIndicate", voiceIndicate);
editor.putString("voiceSpeed", voiceSpeed);
editor.putString("voiceMember", voiceMember);
editor.commit();
return true;
}
@Override
@JavascriptInterface
@ -250,4 +227,24 @@ public class TekVoiceEngine implements VoiceEngine {
mTts.destroy();
}
}
/**
* 页面展示语音配置数据
*/
@JavascriptInterface
public String getStringSetting() {
return gson.toJson(getSetting());
}
/**
* 页面保存语音配置数据
*
* @param s
* @return
*/
@JavascriptInterface
public boolean saveSetting(String s) {
return saveSetting(gson.fromJson(s, VoiceSetting.class));
}
}