最近申请了支付宝的二维码收钱码,其中支付宝有这么一个功能,就是,别人扫描你的二维码给你转账之后,收到钱会有一条语音推送,”支付宝到账 1000万“之类的推送消息,不管你的支付宝app有没有被杀死。
只要你的远程推送开着,并且支付宝的'二维码收钱到账语音提醒',都打开着,就可以收到。
打开方式:支付宝点击右上角设置-通用-新消息通知,打开到账提醒即可。
并且别人给你转多少钱就会播报到账多少钱。
探索实现一下。
当前有两种方案实现了上面描述的场景。
必备条件
上面的描述场景只有在iOS10以上版本才可以,因为必须要基于Notification Servivice Extension
如果对Notification Servivice Extension不是很熟悉的,建议先了解一下
iOS10 推送extension之 Service Extension你玩过了吗?
实现方式
1、ServiceExtension中收到推送之后,用AVSpeechSynthesisVoice相关类,直接把推送过来需要播报相关的文字转化成语音播报
2、ServiceExtension中收到推送之后,将要播报的数字,找到对应的单个音频,排序,用拼接音频的方式<通过推送过来的文字去查找相关的音频,然后拼接成一个音频>,然后使用AudioServicesCreateSystemSoundID播放
在介绍相关方式之前,先介绍一个测试工具
SmartPush
使用方式也很简单,应该一看就懂
正式进入主题
方式一
@implementation NotificationService- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { self.contentHandler = contentHandler; self.bestAttemptContent = [request.content mutableCopy]; // Modify the notification content here... self.bestAttemptContent.title = [NSString stringWithFormat:@'%@ [modified]', self.bestAttemptContent.title]; self.contentHandler(self.bestAttemptContent); [self playVoiceWithAVSpeechSynthesisVoiceWithContent:self.bestAttemptContent.body];}- (void)playVoiceWithAVSpeechSynthesisVoiceWithContent:(NSString *)content{ if (content.length == 0) { return; } // 创建嗓音,指定嗓音不存在则返回nil AVSpeechSynthesisVoice *voice = [AVSpeechSynthesisVoice voiceWithLanguage:@'zh-CN']; // 创建语音合成器 AVSpeechSynthesizer *synthesizer = [[AVSpeechSynthesizer alloc] init]; // 实例化发声的对象 AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:content]; utterance.voice = voice; utterance.rate = 0.5; // 语速 // 朗读的内容 [synthesizer speakUtterance:utterance];}
推送的内容是:
{ 'aps':{ 'alert':{ 'title':'iOS 10 title', 'subtitle':'iOS 10 subtitle', 'body':'世上只有妈妈好,有妈的孩子像块宝。投进妈妈的怀抱,幸福哪里找。没妈的孩子像根草' }, 'my-attachment':'http://img01.taopic.com/160317/240440-16031FU23937.jpg', 'mutable-content':1, 'category':'myNotificationCategory1', 'badge':3 }}
坑点
说明:当前实现的是将push内容中的body
播放出来
1、如果你收到推送了但是添加了系统的铃声,也就是你在push的json中添加了'sound':'default'
那么就可能会影响推送声音的播放
2、收到推送了,但是没有播报语音,检查一下这里
'body':'世上只有妈妈好,有妈的孩子像块宝。投进妈妈的怀抱,幸福哪里找。没妈的孩子像根草'
,最多也就是播放到世上只有妈妈好,有妈的孩子像块宝。投进妈妈的怀抱
方式二
经过对比,支付宝播放的声音明显比系统方法文字转语音播放的好听,一听就是小姑娘录得。要么就是自己集成了一套文字转语音的东西。
首先尝试使用科大讯飞来实现,结果失败了。
然后尝试的是语音合成的方式来播放
比如提前先录好 以下可能播报的内容
支付宝到账、 0、 1、 2、 3、 4、 5、 6、 7、 8、 9、 十、 百、 千、 万、 十万、 百万、 千万、 亿、 元 等等
这样的几种录音,然后用相关的名字命名好<相关的规则自己命名就好>。
比如push过来的是内容是 10010
,那么转化成的录音文件名称的数组就是
@[@'支付宝到账',@'1',@'万',@'0',@'1',@'十',@'元']
然后找到这几个文件,然后按照顺序拼接成一个语音文件进行播放
代码演示:
@implementation NotificationServicestatic int lianxunPlay = 1;- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { self.contentHandler = contentHandler; self.bestAttemptContent = [request.content mutableCopy]; // Modify the notification content here... self.bestAttemptContent.title = [NSString stringWithFormat:@'%@ [modified]', self.bestAttemptContent.title]; self.contentHandler(self.bestAttemptContent); // 方式4,语音合成,使用AudioServicesPlayAlertSoundWithCompletion播放,成功,但是时间最多5秒 [self hechengVoice];}
- (void)hechengVoice{ /************************合成音频并播放*****************************/ NSMutableArray *audioAssetArray = [[NSMutableArray alloc] init]; NSMutableArray *durationArray = [[NSMutableArray alloc] init]; [durationArray addObject:@(0)]; AVMutableComposition *composition = [AVMutableComposition composition]; NSArray *fileNameArray = @[@'daozhang',@'1',@'2',@'3',@'4',@'5',@'6']; CMTime allTime = kCMTimeZero; for (NSInteger i = 0; i < fileNameArray.count; i ) { NSString *auidoPath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@'%@',fileNameArray[i]] ofType:@'m4a']; AVURLAsset *audioAsset = [AVURLAsset assetWithURL:[NSURL fileURLWithPath:auidoPath]]; [audioAssetArray addObject:audioAsset]; // 音频轨道 AVMutableCompositionTrack *audioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:0]; // 音频素材轨道 AVAssetTrack *audioAssetTrack = [[audioAsset tracksWithMediaType:AVMediaTypeAudio] firstObject]; // 音频合并 - 插入音轨文件 [audioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioAsset.duration) ofTrack:audioAssetTrack atTime:allTime error:nil]; // 更新当前的位置 allTime = CMTimeAdd(allTime, audioAsset.duration); } // 合并后的文件导出 - `presetName`要和之后的`session.outputFileType`相对应。 AVAssetExportSession *session = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetAppleM4A]; NSString *outPutFilePath = [[self.filePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:@'xindong.m4a']; if ([[NSFileManager defaultManager] fileExistsAtPath:outPutFilePath]) { [[NSFileManager defaultManager] removeItemAtPath:outPutFilePath error:nil]; } // 查看当前session支持的fileType类型 NSLog(@'---%@',[session supportedFileTypes]); session.outputURL = [NSURL fileURLWithPath:outPutFilePath]; session.outputFileType = AVFileTypeAppleM4A; //与上述的`present`相对应 session.shouldOptimizeForNetworkUse = YES; //优化网络 [session exportAsynchronouslyWithCompletionHandler:^{ if (session.status == AVAssetExportSessionStatusCompleted) { NSLog(@'合并成功----%@', outPutFilePath); NSURL *url = [NSURL fileURLWithPath:outPutFilePath]; static SystemSoundID soundID = 0; AudioServicesCreateSystemSoundID((__bridge CFURLRef _Nonnull)(url), &soundID); AudioServicesPlayAlertSoundWithCompletion(soundID, ^{ NSLog(@'播放完成'); }); } else { // 其他情况, 具体请看这里`AVAssetExportSessionStatus`. } }]; /************************合成音频并播放*****************************/}
说明:
上面并没有实现 数字转对应音频文件名称数组的过程,直接实现的是合成音频的方法。
坑点:
1、播放的时长仍然受到限制,大概5秒钟。不过如果说播放一个钱数,足够了。
2、合成之后的音频文件用AVAudioPlayer
播放是没有声音的
分析:
以上的功能只是针对iOS10以上的系统版本可以,那么iOS10以下的怎么办?可以这么办,不用播报到账多少钱,可以通过定制远程推送的语音,来播报”支付宝,您有一笔到账,请及时查看“之类的,支付宝好像也是这个套路。
每次push之前,先去后台查看当前需要推送的设备的系统版本是啥<这个不难实现>,然后定制推送不同的内容。iOS10以上的就推送钱数,并且不推送sound。
iOS10以下的就推送 'sound'='定制的声音文件名称'。
综上所述,支付宝实现的方式应该是方式二,本地合成音频文件播放的。
还有一个注意点就是,因为打开了下面的这个开关
最后献上相关的Demo地址,如果你有更好的建议欢迎留言,如有不正,欢迎来喷。
可以直接用我的Demo进行调试,调试的时候注意修改下bundleId,然后用自己的开发者账号配置一下相关的push证书就可以了
联系客服