Android支付寶商家收款語(yǔ)音播報(bào)(無(wú)SDK)

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

使用

  1. gradle引入
    allprojects {
        repositories {
        ...
            maven { url 'https://jitpack.io' }
        }
    }

    dependencies {
        implementation 'com.github.YzyCoding:PushVoiceBroadcast:1.0.2'
    }
  1. 一行代碼
   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一把伟端,尋找新世界

  1. 系統(tǒng)類TextToSpeech,文字轉(zhuǎn)語(yǔ)音责蝠,對(duì)中文支持很不給力,可以安裝 “訊飛語(yǔ)記” TTS來(lái)滿足
  2. 提前錄制好"收款成功"霜医,“0”,“1”支子,“2”...簡(jiǎn)小音頻拼成一句話播放
  3. 訊飛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í)踐

  1. 關(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("元", "");
    }
}
  1. 文字轉(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;
    }
  1. 順序播放
 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)目地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末吨掌,一起剝皮案震驚了整個(gè)濱河市脓恕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌进肯,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件江掩,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡乘瓤,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)衙傀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人统抬,你說(shuō)我怎么就攤上這事〈辖ǎ” “怎么了钙畔?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵金麸,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我挥下,道長(zhǎng)揍魂,這世上最難降的妖魔是什么棚瘟? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮偎蘸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘禀苦。我一直安慰自己蔓肯,他們只是感情好振乏,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布秉扑。 她就那樣靜靜地躺著调限,像睡著了一般。 火紅的嫁衣襯著肌膚如雪误澳。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,457評(píng)論 1 311
  • 那天忆谓,我揣著相機(jī)與錄音,去河邊找鬼倡缠。 笑死哨免,一個(gè)胖子當(dāng)著我的面吹牛昙沦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播盾饮,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼丘损!你這毒婦竟也來(lái)了普办?” 一聲冷哼從身側(cè)響起号俐,我...
    開(kāi)封第一講書(shū)人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吏饿,沒(méi)想到半個(gè)月后踪危,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體猪落,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年笨忌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片官疲。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖途凫,靈堂內(nèi)的尸體忽然破棺而出垢夹,到底是詐尸還是另有隱情维费,我是刑警寧澤促王,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站蝇狼,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏倡怎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一监署、第九天 我趴在偏房一處隱蔽的房頂上張望豹障。 院中可真熱鬧焦匈,春花似錦、人聲如沸缓熟。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)垦写。三九已至彰触,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間况毅,已是汗流浹背分蓖。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工尔许, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人味廊。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓蒸甜,卻偏偏與公主長(zhǎng)得像余佛,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子辉巡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

推薦閱讀更多精彩內(nèi)容