创意实现 Android 支付宝商家收款语音播报
本文作者
作者:鱼__鱼
链接:
https://www.jianshu.com/p/62e6382c610b
本文由作者授权发布。
支付宝商家收款时,语音提示:支付宝收款xxx元,当时觉得这东西还挺有趣的,第一时间通知给商家,减少不必要的纠纷,节约时间成本,对商家对用户都挺好的。
我们产品先做了<我的钱包>,现在也希望在商家版有这样收款播报的功能,我觉得挺好的。
效果图
使用
gradle引入
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
dependencies {
implementation 'com.github.YzyCoding:PushVoiceBroadcast:1.0.2'
}
一行代码
VoicePlay
.with(MainActivity.this)
.play(amount);
需求
固定播报文字,除了金额动态
收到多条推送,顺序播报
来电时,暂停播报,挂断后继续播报
正在播放音乐,暂停音乐,播放完成继续播放音乐
如果音量过小,调节音量
当然是google一把,寻找新世界
系统类TextToSpeech,文字转语音,对中文支持很不给力,可以安装 “讯飞语记” TTS来满足
提前录制好"收款成功",“0”,“1”,“2”...简小音频拼成一句话播放
讯飞SDK在线文字转语音播放?
随后呢,我又下载了支付宝APK,反编译出来看看,下图得知,支付宝的做法就是提前录制好,然后根据金额拼接成一句话,可不是,毕竟播报的是固定的那么几个字,在线文字转音频,还是TTS肯定麻烦了,所以还是选择和支付宝一样的做法。
思路
金额转大写
文字转音频
顺序播放
1. 关于金额的工具类
public class MoneyUtils {
private static final char[] NUM = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
private static final char[] CHINESE_UNIT = {'元', '拾', '佰', '仟', '万', '拾', '佰', '仟', '亿', '拾', '佰', '仟'};
/**
* 返回关于钱的中文式大写数字,支仅持到亿
*/
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+万", "万")
.replaceAll("0+元", "元")
.replaceAll("0+", "0")
.replace("元", "");
}
}
2. 文字转本地音频
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 == '万') {
result.add(VoiceConstants.TEN_THOUSAND);
} else if (current == '亿') {
result.add(VoiceConstants.TEN_MILLION);
} else {
result.add(String.valueOf(current));
}
}
return result;
}
3. 顺序播放
private void start(final List<String> voicePlay) {
synchronized (VoicePlay.this) {
MediaPlayer mMediaPlayer = new MediaPlayer();
final CountDownLatch mCountDownLatch = new CountDownLatch(1);
AssetFileDescriptor assetFileDescription = null;
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();
}
});
mCountDownLatch.await();
notifyAll();
}
}
来电未测试
总结
代码分为两部分
音频组合 VoiceTextTemplate
音频播放 VoicePlay
VoiceBuilder 建造者模式
同步采用 synchronized + notifyAll()
更多优化请联系@我
项目Demo
https://github.com/YzyCoding/PushVoiceBroadcast
最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!
推荐阅读:
扫一扫 关注我的公众号
如果你想要跟大家分享你的文章,欢迎投稿~
┏(^0^)┛明天见!