jianshu_0035.png
動(dòng)機(jī)
支付寶商家收款時(shí)吃衅,語(yǔ)音提示:支付寶收款xxx元,當(dāng)時(shí)覺(jué)得這東西還挺有趣的徘层,第一時(shí)間通知給商家,減少不必要的糾紛惑灵,節(jié)約時(shí)間成本,對(duì)商家對(duì)用戶都挺好的英支。
我們產(chǎn)品先做了<我的錢(qián)包>,現(xiàn)在也希望在商家版有這樣收款播報(bào)的功能干花,我覺(jué)得挺好的。
效果圖
jianshu_0038.png
使用
- gradle引入
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
dependencies {
implementation 'com.github.YzyCoding:PushVoiceBroadcast:1.0.2'
}
- 一行代碼
VoicePlay.with(MainActivity.this).play(amount);
需求
- 固定播報(bào)文字池凄,除了金額動(dòng)態(tài)
- 收到多條推送,順序播報(bào)
- 來(lái)電時(shí)鬼廓,暫停播報(bào),掛斷后繼續(xù)播報(bào)
- 正在播放音樂(lè),暫停音樂(lè)馏锡,播放完成繼續(xù)播放音樂(lè)
- 如果音量過(guò)小,調(diào)節(jié)音量
分析
當(dāng)然是google一把伟端,尋找新世界
- 系統(tǒng)類TextToSpeech,文字轉(zhuǎn)語(yǔ)音责蝠,對(duì)中文支持很不給力,可以安裝 “訊飛語(yǔ)記” TTS來(lái)滿足
- 提前錄制好"收款成功"霜医,“0”,“1”支子,“2”...簡(jiǎn)小音頻拼成一句話播放
- 訊飛SDK在線文字轉(zhuǎn)語(yǔ)音播放?
隨后呢值朋,我又下載了支付寶APK叹侄,反編譯出來(lái)看看昨登,下圖得知,支付寶的做法就是提前錄制好丰辣,然后根據(jù)金額拼接成一句話,可不是笙什,畢竟播報(bào)的是固定的那么幾個(gè)字,在線文字轉(zhuǎn)音頻琐凭,還是TTS肯定麻煩了,所以還是選擇和支付寶一樣的做法统屈。
jianshu_0036.png
jianshu_0037.png
思路
- 金額轉(zhuǎn)大寫(xiě)
- 文字轉(zhuǎn)音頻
- 順序播放
實(shí)踐
- 關(guān)于金額的工具類
public class MoneyUtils {
private static final char[] NUM = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
private static final char[] CHINESE_UNIT = {'元', '拾', '佰', '仟', '萬(wàn)', '拾', '佰', '仟', '億', '拾', '佰', '仟'};
/**
* 返回關(guān)于錢(qián)的中文式大寫(xiě)數(shù)字,支僅持到億
*/
public static String readInt(int moneyNum) {
String res = "";
int i = 0;
if (moneyNum == 0) {
return "0";
}
if (moneyNum == 10) {
return "拾";
}
if (moneyNum > 10 && moneyNum < 20) {
return "拾" + moneyNum % 10;
}
while (moneyNum > 0) {
res = CHINESE_UNIT[i++] + res;
res = NUM[moneyNum % 10] + res;
moneyNum /= 10;
}
return res.replaceAll("0[拾佰仟]", "0")
.replaceAll("0+億", "億")
.replaceAll("0+萬(wàn)", "萬(wàn)")
.replaceAll("0+元", "元")
.replaceAll("0+", "0")
.replace("元", "");
}
}
- 文字轉(zhuǎn)本地音頻
private static List<String> genReadableMoney(String numString) {
List<String> result = new ArrayList<>();
if (!TextUtils.isEmpty(numString)) {
if (numString.contains(VoiceConstants.DOT_POINT)) {
String integerPart = numString.split("\\.")[0];
String decimalPart = numString.split("\\.")[1];
List<String> intList = readIntPart(integerPart);
List<String> decimalList = readDecimalPart(decimalPart);
result.addAll(intList);
if (!decimalList.isEmpty()) {
result.add(VoiceConstants.DOT);
result.addAll(decimalList);
}
} else {
result.addAll(readIntPart(numString));
}
}
return result;
}
private static List<String> readDecimalPart(String decimalPart) {
List<String> result = new ArrayList<>();
if (!"00".equals(decimalPart)) {
char[] chars = decimalPart.toCharArray();
for (char ch : chars) {
result.add(String.valueOf(ch));
}
}
return result;
}
private static List<String> readIntPart(String integerPart) {
List<String> result = new ArrayList<>();
String intString = MoneyUtils.readInt(Integer.parseInt(integerPart));
int len = intString.length();
for (int i = 0; i < len; i++) {
char current = intString.charAt(i);
if (current == '拾') {
result.add(VoiceConstants.TEN);
} else if (current == '佰') {
result.add(VoiceConstants.HUNDRED);
} else if (current == '仟') {
result.add(VoiceConstants.THOUSAND);
} else if (current == '萬(wàn)') {
result.add(VoiceConstants.TEN_THOUSAND);
} else if (current == '億') {
result.add(VoiceConstants.TEN_MILLION);
} else {
result.add(String.valueOf(current));
}
}
return result;
}
- 順序播放
private void start(final List<String> voicePlay) {
synchronized (VoicePlay.this) {
MediaPlayer mMediaPlayer = new MediaPlayer();
final CountDownLatch mCountDownLatch = new CountDownLatch(1);
AssetFileDescriptor assetFileDescription = null;
try {
final int[] counter = {0};
assetFileDescription = FileUtils.getAssetFileDescription(mContext,
String.format(VoiceConstants.FILE_PATH, voicePlay.get(counter[0])));
mMediaPlayer.setDataSource(
assetFileDescription.getFileDescriptor(),
assetFileDescription.getStartOffset(),
assetFileDescription.getLength());
mMediaPlayer.prepareAsync();
mMediaPlayer.setOnPreparedListener(mediaPlayer -> mMediaPlayer.start());
mMediaPlayer.setOnCompletionListener(mediaPlayer -> {
mediaPlayer.reset();
counter[0]++;
if (counter[0] < voicePlay.size()) {
try {
AssetFileDescriptor fileDescription2 = FileUtils.getAssetFileDescription(mContext,
String.format(VoiceConstants.FILE_PATH, voicePlay.get(counter[0])));
mediaPlayer.setDataSource(
fileDescription2.getFileDescriptor(),
fileDescription2.getStartOffset(),
fileDescription2.getLength());
mediaPlayer.prepare();
} catch (IOException e) {
e.printStackTrace();
mCountDownLatch.countDown();
}
} else {
mediaPlayer.release();
mCountDownLatch.countDown();
}
});
} catch (Exception e) {
e.printStackTrace();
mCountDownLatch.countDown();
} finally {
if (assetFileDescription != null) {
try {
assetFileDescription.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
try {
mCountDownLatch.await();
notifyAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
容錯(cuò)處理
/**
* 提取字符串中的 數(shù)字 帶小數(shù)點(diǎn) 腕扶,沒(méi)有就返回""
*
* @param money
* @return
*/
public static String getMoney(String money) {
Pattern pattern = Pattern.compile("(\\d+\\.\\d+)");
Matcher m = pattern.matcher(money);
if (m.find()) {
money = m.group(1) == null ? "" : m.group(1);
} else {
pattern = Pattern.compile("(\\d+)");
m = pattern.matcher(money);
if (m.find()) {
money = m.group(1) == null ? "" : m.group(1);
} else {
money = "";
}
}
return money;
}
@Test
public void testMoney() {
String money = StringUtils.getMoney("");
System.out.println("money == " + money);
String money1 = StringUtils.getMoney("收到影秀卡付款0.01元");
System.out.println("money1 == " + money1);
String money2 = StringUtils.getMoney("收到測(cè)試影秀卡付款1000.00元");
System.out.println("money1 == " + money2);
String money3 = StringUtils.getMoney("收到測(cè)試影秀卡付款1000元");
System.out.println("money2 == " + money3);
String money4 = StringUtils.getMoney("收到測(cè)試影秀卡付款999.99元");
System.out.println("money3 == " + money4);
String money5 = StringUtils.getMoney("999.99");
System.out.println("money4 == " + money5);
String money6 = StringUtils.getMoney("1");
System.out.println("money5 == " + money6);
}
Log:
money ==
money1 == 0.01
money1 == 1000.00
money2 == 1000
money3 == 999.99
money4 == 999.99
money5 == 1
開(kāi)發(fā)中
來(lái)電未測(cè)試
總結(jié)
代碼分為兩部分
音頻組合 VoiceTextTemplate
音頻播放 VoicePlay
VoiceBuilder 建造者模式
同步采用 synchronized + notifyAll()
更多優(yōu)化請(qǐng)聯(lián)系@我
項(xiàng)目Demo
項(xiàng)目地址