最近開發(fā)記錄
程序要求:工廠內(nèi)通訊使用,及時(shí)聯(lián)絡(luò)處理問題,收到消息及時(shí)提醒用戶處理。目前已測試android4以上
1.保持程序后臺運(yùn)行
2.維持長連接,接收即時(shí)消息,后臺播放無聲音樂,每十秒執(zhí)行一次,無聲音樂長度為1秒
3.接收到信息,顯示通知,提醒,語音播報(bào)
4.消息存儲,根據(jù)不同的消息跳轉(zhuǎn)不同的頁面
需要用到插件
1.cordova-plugin-background-mode 后臺運(yùn)行
2.cordova-plugin-appminimize 程序最小化
3. 即時(shí)消息采用第三方融云
4. cordova-plugin-nativeaudio 播放本地音頻
5.cordova-plugin-local-notification 本地消息通知
6.語音播報(bào)采用百度語音合成
7.cordova-sqlite-storage 存儲消息
1.后臺運(yùn)行插件(ionic3版本,如果是ionic4,按照4的文檔添加)
1.1 添加插件
$ ionic cordova plugin add cordova-sqlite-storage
$ npm install --save @ionic-native/sqlite@4
$ ionic cordova plugin add cordova-plugin-appminimize
$ npm install --save @ionic-native/app-minimize@4
1.2 插件添加到應(yīng)用程序的模塊中(此處不講)
1.3 在app.component中啟用(有刪減)
import { Component, ViewChild } from '@angular/core';
import { Nav, Platform, App, Keyboard, IonicApp, Events } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { AppMinimize } from '@ionic-native/app-minimize';
import { BackgroundMode } from '@ionic-native/background-mode';
import { ToastServiceProvider } from '../providers/toast-service/toast-service';
@Component({
templateUrl: 'app.html'
})
export class MyApp {
@ViewChild(Nav) nav: Nav;
rootPage: any = LoginPage;
backButtonPressed: boolean = false; //用于判斷返回鍵是否觸發(fā)
constructor(public platform: Platform,
public statusBar: StatusBar,
public splashScreen: SplashScreen,
private toast: ToastServiceProvider,
private appMinimize: AppMinimize,
private keyboard: Keyboard,
private ionicApp: IonicApp,
private app: App,
private events: Events,
private backgroundMode: BackgroundMode,
private helper: HelperProvider) {
this.initializeApp();
}
initializeApp() {
this.platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
// this.statusBar.styleDefault();
// this.splashScreen.hide();
//判斷是否真機(jī)運(yùn)行
if (this.helper.isMobile()) {
this.registerBackButtonAction();//注冊返回按鍵事件
this.setBackground();//設(shè)置后臺運(yùn)行
}
this.statusBar.styleDefault();
this.splashScreen.hide();
this.nav.setRoot(LoginPage);
});
}
/**
* 設(shè)置后臺運(yùn)行
*/
setBackground() {
this.backgroundMode.enable();
//延遲啟用
setTimeout(() => {
this.backgroundMode.setDefaults({
title: ‘正在后臺運(yùn)行,請勿關(guān)閉應(yīng)用’,
text: ‘點(diǎn)擊即可進(jìn)入應(yīng)用’,
icon: 'assets/imgs/logo.png'
})
}, 2000);
}
/**
* android 物理返回鍵處理
*/
registerBackButtonAction() {
this.platform.registerBackButtonAction(() => {
if (this.keyboard.isOpen()) {//如果鍵盤開啟則隱藏鍵盤
this.keyboard.close();
return;
}
//如果想點(diǎn)擊返回按鈕隱藏toast或loading或Overlay就把下面加上
// this.ionicApp._toastPortal.getActive() || this.ionicApp._loadingPortal.getActive() || this.ionicApp._overlayPortal.getActive()
let activePortal = this.ionicApp._modalPortal.getActive();
if (activePortal) {
activePortal.dismiss().catch(() => { });
activePortal.onDidDismiss(() => { });
return;
}
let loadingPortal = this.ionicApp._loadingPortal.getActive();
if (loadingPortal) {
loadingPortal.dismiss().catch(() => { });
return;
}
if (this.app.getActiveNav().canGoBack() || this.nav.canGoBack()) {
this.app.navPop();
} else {
this.showExit();
}
return;
// return this.app.getActiveNav().canGoBack() ? this.app.navPop() : this.showExit();
}, 100);
}
//雙擊退出提示框
showExit() {
if (this.backButtonPressed) { //當(dāng)觸發(fā)標(biāo)志為true時(shí),即2秒內(nèi)雙擊返回按鍵則退出APP
// this.platform.exitApp();
this.appMinimize.minimize();
} else {
this.toast.showToast('再次返回將自動(dòng)切換到后臺運(yùn)行');
this.backButtonPressed = true;
setTimeout(() => this.backButtonPressed = false, 2000);//2秒內(nèi)沒有再次點(diǎn)擊返回則將觸發(fā)標(biāo)志標(biāo)記為false
}
}
}
關(guān)聯(lián)文件ToastServiceProvider
import { Injectable } from '@angular/core';
import { ToastController } from 'ionic-angular';
/*
Generated class for the ToastServiceProvider provider.
See https://angular.io/guide/dependency-injection for more info on providers
and Angular DI.
*/
@Injectable()
export class ToastServiceProvider {
toast: any;//Toast
constructor(private toastCtrl: ToastController) {
}
/**
* 吐司提示(默認(rèn)中間)
*/
showToast(mes: string, position?: string) {
if (this.toast) {
this.toast.dismiss();
}
this.toast = this.toastCtrl.create({
message: mes,
duration: 2000,
position: position ? position : 'middle'
});
this.toast.present();
}
}
HelperProvider
import { Injectable } from '@angular/core';
import { Platform,} from 'ionic-angular';
import { Network } from '@ionic-native/network';;
/*
Generated class for the HelperProvider provider.
See https://angular.io/guide/dependency-injection for more info on providers
and Angular DI.
*/
@Injectable()
export class HelperProvider {
constructor(private platform: Platform,
private network: Network) {
}
/**
* 是否真機(jī)環(huán)境
* @return {boolean}
*/
isMobile(): boolean {
return this.platform.is('mobile') && !this.platform.is('mobileweb');
}
/**
* 是否android真機(jī)環(huán)境
* @return {boolean}
*/
isAndroid(): boolean {
return this.isMobile() && this.platform.is('android');
}
/**
* 是否android6以下
*/
isAndroidLow():boolean{
return this.isAndroid() && this.platform.version().major < 6;
}
/**
* 是否ios真機(jī)環(huán)境
* @return {boolean}
*/
isIos(): boolean {
return this.isMobile() && (this.platform.is('ios') || this.platform.is('ipad') || this.platform.is('iphone'));
}
/**
* 獲取網(wǎng)絡(luò)類型 如`unknown`, `ethernet`, `wifi`, `2g`, `3g`, `4g`, `cellular`, `none`
*/
getNetworkType(): string {
if (!this.isMobile()) {
return 'wifi';
}
return this.network.type;
}
/**
* 判斷是否有網(wǎng)絡(luò)
* @returns {boolean}
*/
isConnecting(): boolean {
if (this.getNetworkType() == 'none') {
//沒有網(wǎng)絡(luò)
}
return this.getNetworkType() != 'none';
}
/**
* 獲取隨機(jī)數(shù)
*/
generateMixed(n: number) {
let res = "";
var chars = ['2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];//隨機(jī)數(shù)
for (var i = 0; i < n; i++) {
var id = Math.ceil(Math.random() * 31);
res += chars[id];
}
return res;
}
}
2 集成融云
由于測試期間使用融云web SDK在android6以下出現(xiàn)連接融云服務(wù)器受限,出現(xiàn)403狀態(tài)碼,最后為了解決該問題,暫時(shí)在android6以下版本采用融云cordova插件代替
2.1 添加融云cordova插件
$ ionic cordova plugin add cordova-plugin-rongcloud-im
2.2 添加web版融云

image.png

image.png
2.3 在app.component中
/// <reference path="../assets/js/RongIMLib-2.3.2.d.ts" />
如圖:

image.png
2.4 創(chuàng)建RongcloudServieProvider
$ ionic g provider RongcloudServie
2.5 修改RongcloudServieProvider
StorageServiceProvider參考
SqliteServiceProvider 參考
import { HelperProvider } from './../helper/helper';
import { Injectable } from '@angular/core';
import { Events } from 'ionic-angular';
import { StorageServiceProvider } from '../../providers/storage-service/storage-service';
import { SqliteServiceProvider } from '../../providers/sqlite-service/sqlite-service';
declare let RongCloudLibPlugin;
/*
Generated class for the RongcloudServieProvider provider.
See https://angular.io/guide/dependency-injection for more info on providers
and Angular DI.
*/
@Injectable()
export class RongcloudServieProvider {
IM_KEY = 'xxxxxxxxxxxxxxxxx';//融云官網(wǎng)申請的應(yīng)用key
constructor(public events: Events,
private sqlite: SqliteServiceProvider,
private storageService: StorageServiceProvider,
private helper: HelperProvider) {
}
/**
* 注冊實(shí)例化RongYunIm服務(wù)
* Ming 2018-10-29
* @memberof RongcloudServieProvider
*/
init() {
if (this.helper.isAndroidLow()) {
RongCloudLibPlugin.init({ appKey: IM_KEY },(ret, err)=>{
// alert(ret.status);
});
} else {
RongIMLib.RongIMClient.init(IM_KEY);
}
}
/**
* 鏈接融云服務(wù)器狀態(tài)回掉事件
* Ming 2018-10-29
* @param
* @memberof RongcloudServieProvider
*/
connectionStatusListener() {
if (this.helper.isAndroidLow()) {
RongCloudLibPlugin.setConnectionStatusListener((ret, err)=>{
this.onListenerStatus(ret.result.connectionStatus);
});
} else {
RongIMLib.RongIMClient.setConnectionStatusListener({
onChanged: (status) => {
this.onListenerStatus(status);
}
});
}
}
onListenerStatus(status) {
switch (status) {
case RongIMLib.ConnectionStatus.CONNECTED:
// this.coms.closeLoading(loading);
console.log('聊天服務(wù)鏈接成功');
break;
case RongIMLib.ConnectionStatus.CONNECTING:
// this.coms.toastInfo("正在鏈接聊天服務(wù)", 1000, 'top');
break;
// case RongIMLib.ConnectionStatus.DISCONNECTED:
case RongIMLib.ConnectionStatus.NETWORK_UNAVAILABLE:
// this.coms.toastError('服務(wù)已經(jīng)斷開 正為您重新連接');
this.reconnect();
// alert(moment());
break;
case RongIMLib.ConnectionStatus.KICKED_OFFLINE_BY_OTHER_CLIENT:
// this.logoutAndClear();
// this.coms.toastInfo("其他設(shè)備登錄成功,您已經(jīng)被迫下線!", 2000, 'top');
// this.alertService.showAlert('你已在其他設(shè)備登錄成功,可能導(dǎo)致此設(shè)備無法正常接收信息');
// this.reconnect();
break;
case RongIMLib.ConnectionStatus.DOMAIN_INCORRECT:
this.alertService.showAlert('域名解析失敗');
this.reconnect();
// this.coms.toastError("域名解析失敗", 2000, 'top');
break;
case RongIMLib.ConnectionStatus.DISCONNECTED:
// this.alertService.showAlert('已斷開連接');
this.reconnect();
// this.coms.toastError("域名解析失敗", 2000, 'top');
break;
case RongIMLib.ConnectionStatus.CONNECTION_CLOSED:
// this.alertService.showAlert('連接已關(guān)閉');
// this.reconnect();
this.connect(this.globalData.rongcloudtoken);
// this.coms.toastError("域名解析失敗", 2000, 'top');
break;
}
}
/**
* 收到消息的監(jiān)聽回掉事件
* Ming 2018-10-29
* @param
* @memberof RongcloudServieProvider
*/
async receiveMessageListener() {
if (this.helper.isAndroidLow()) {
RongCloudLibPlugin.setOnReceiveMessageListener((ret, err) => {
this.onReceived(ret.result.message);
})
} else {
RongIMLib.RongIMClient.setOnReceiveMessageListener({
// 接收到的消息
onReceived: (message) => {
this.onReceived(message)
}
})
}
}
/**
* 處理消息 (根據(jù)自己實(shí)際需求處理)
* @param message
*/
async onReceived(message) {
if (message && message.content.extra) {
let msg = JSON.parse(message.content.extra);
if (msg) {
//保存數(shù)據(jù)
this.sqlite.executeSql('INSERT INTO message(userId,title, state, content, sendId, sendTime, msgType, isWarning, referenceId, receivers,isValid,isDeleted,createTime,referenceType,referenceCode) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
[this.globalData.userId.toString(), msg.title.toString(), '0', msg.content ? msg.content.toString() : '', msg.sendId.toString(), msg.sendTime.toString(), msg.msgType.toString(), msg.isWarning, msg.referenceId ? msg.referenceId.toString() : '', JSON.stringify(msg.receivers), msg.isValid, msg.isDeleted, new Date().getTime().toString(), msg.referenceType ? msg.referenceType.toString() : '',msg.referenceCode?msg.referenceCode.toString():""]).then(() => {
this.events.publish('message:new', msg);//廣播通知有信息
});
}
}
/**
* 獲取未讀消息
* 此接口必須在init()方法之后,連接融云服務(wù)器 connect 之前調(diào)用。
* Ming 2018-10-29
* @param {*} token 傳入鏈接的Token
* @param {(hasMessage) => {}} successCallback 執(zhí)行成功后的回掉事件
* @param {(err) => {}} errorCallback 執(zhí)行錯(cuò)誤后的回掉事件
* @memberof RongcloudServieProvider
*/
hasRemoteUnreadMessages(token): Promise<any> {
return new Promise((resolve, reject) => {
RongIMLib.RongIMClient.getInstance().hasRemoteUnreadMessages(token, {
onSuccess: (hasMessage) => {
resolve(hasMessage)
console.log('未讀消息' + hasMessage)
},
onError: (err) => {
reject(err)
}
});
})
}
// 獲取未讀消息總數(shù)
getUnreadMessageNumber(): Promise<any> {
return new Promise((resolve, reject) => {
RongIMLib.RongIMClient.getInstance().getTotalUnreadCount({
onSuccess: (count) => {
resolve(count);
},
onError: (error) => {
reject(error)
}
});
})
}
/**
* 發(fā)送信息
*/
sendMessage(conversationtype: RongIMLib.ConversationType, targetId: string, msg: any): Promise<any> {
return new Promise((resolve, reject) => {
if(this.helper.isAndroidLow()){
RongCloudLibPlugin.sendTextMessage({
conversationType: conversationtype,
targetId: targetId,
text: msg
},(ret, err)=>{
resolve(ret);
})
} else {
RongIMLib.RongIMClient.getInstance().sendMessage(
conversationtype,
targetId,
msg,
{
onSuccess: (message) => {
resolve(message);
},
onError: (errorCode) => {
let info = '';
switch (errorCode) {
case RongIMLib.ErrorCode.TIMEOUT:
info = '超時(shí)';
break;
case RongIMLib.ErrorCode.REJECTED_BY_BLACKLIST:
info = '在黑名單中,無法向?qū)Ψ桨l(fā)送消息';
break;
case RongIMLib.ErrorCode.NOT_IN_DISCUSSION:
info = '不在討論組中';
break;
case RongIMLib.ErrorCode.NOT_IN_GROUP:
info = '不在群組中';
break;
case RongIMLib.ErrorCode.NOT_IN_CHATROOM:
info = '不在聊天室中';
break;
default:
info = 'x';
break;
}
reject(info)
},
onBefore: (messageId) => {
resolve(messageId)
}
}
);
}
})
}
/**
* 通過融云返回的TokenId鏈接融云服務(wù)器
* 傳入鏈接的Token
* @param {*} token
* @param {(userId) => {}} successCallback 執(zhí)行成功后的回掉事件并得到用戶UserId
* @param {() => {}} tokenIncorrectCallback Token失效事件
* @param {(errorCode) => {}} errorCallback 執(zhí)行錯(cuò)誤后的回掉事件并放回錯(cuò)誤編碼
* @memberof RongcloudServieProvider
*/
connect(token): Promise<any> {
return new Promise((resolve, reject) => {
if (this.helper.isAndroidLow()) {
RongCloudLibPlugin.connect({
token: token
},(ret, err)=>{
if (ret.status == 'success') {
// alert(ret.result.userId);
resolve(ret.result.userId);
} else {
resolve(ret);
}
})
} else {
RongIMLib.RongIMClient.connect(token, {
onSuccess: (userId) => {
resolve(userId);
// this.setHeartbeat();//開啟心跳
},
onTokenIncorrect: () => {
reject('token無效');
},
onError: (errorCode) => {
let info = '';
switch (errorCode) {
case RongIMLib.ErrorCode.TIMEOUT:
info = '超時(shí)';
break;
case RongIMLib.ConnectionState.IDENTIFIER_REJECTED:
info = 'appkey不正確';
break;
case RongIMLib.ConnectionState.SERVER_UNAVAILABLE:
info = '服務(wù)器不可用';
break;
}
reject(info)
}
})
}
})
}
getCurrentConnectionStatus() {
if (this.helper.isAndroidLow()) {
return RongCloudLibPlugin.getConnectionStatus();
} else {
return RongIMLib.RongIMClient.getInstance().getCurrentConnectionStatus();
}
}
/**
* 融云重新連接
*/
reconnect(): Promise<any> {
if (this.storageService.sessionRead('rongcloud') == 'yes') {
// this.loadingService.showLoading('正在重新連接聊天服務(wù)器');
return new Promise((resolve, reject) => {
if (this.helper.isAndroidLow()) {
this.connect(this.globalData.rongcloudtoken);
} else {
RongIMLib.RongIMClient.reconnect({
onSuccess: (userId) => {
// this.loadingService.hideLoading();
// console.log("融云重連成功." + userId);
resolve(userId);
},
onTokenIncorrect: () => {
// console.log('token無效');
// this.loadingService.hideLoading();
reject('token無效');
this.alertService.showAlert('融云重連失敗token無效');
},
onError: (errorCode) => {
// console.log("融云重連失敗" + errorCode);
// this.loadingService.hideLoading();
reject(errorCode);
// this.alertService.showAlert('融云重連失敗' + errorCode);
}
}, {
auto: true,
url: 'cdn.ronghub.com/RongIMLib-2.3.2.min.js',
rate: [100, 1000, 3000, 6000, 10000]
});
}
})
}
}
/**
* 斷開融云連接
*/
disconnectRy() {
if (this.helper.isAndroidLow()) {
// 描述:斷開后是否接收 Push
RongCloudLibPlugin.disconnect({ isReceivePush: false },(ret, err)=>{
// alert(ret.status);
});
} else {
RongIMLib.RongIMClient.getInstance().disconnect();
}
}
/**
* 登出融云
*/
logoutRy() {
try {
if (this.helper.isAndroidLow()) {
RongCloudLibPlugin.logout((ret, err)=>{
// alert(ret.status);
});
} else {
RongIMLib.RongIMClient.getInstance().logout();
}
} catch (e) {
console.log(e)
}
}
}
2.6 在app.component中調(diào)用及監(jiān)聽,修改app.component為
import { Component, ViewChild } from '@angular/core';
import { Nav, Platform, App, Keyboard, IonicApp, Events } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { AppMinimize } from '@ionic-native/app-minimize';
import { BackgroundMode } from '@ionic-native/background-mode';
import { ToastServiceProvider } from '../providers/toast-service/toast-service';
import { RongcloudServieProvider } from '../providers/rongcloud-servie/rongcloud-servie';
import { SqliteServiceProvider } from '../providers/sqlite-service/sqlite-service';
@Component({
templateUrl: 'app.html'
})
export class MyApp {
@ViewChild(Nav) nav: Nav;
rootPage: any = LoginPage;
backButtonPressed: boolean = false; //用于判斷返回鍵是否觸發(fā)
constructor(public platform: Platform,
public statusBar: StatusBar,
public splashScreen: SplashScreen,
private toast: ToastServiceProvider,
private appMinimize: AppMinimize,
private keyboard: Keyboard,
private ionicApp: IonicApp,
private app: App,
private events: Events,
private backgroundMode: BackgroundMode,
private rongService: RongcloudServieProvider,
private sqlite: SqliteServiceProvider,
private helper: HelperProvider) {
this.initializeApp();
}
initializeApp() {
//監(jiān)聽登錄成功 登錄成功或失敗都需要觸發(fā)一個(gè)event事件
this.events.subscribe('login:success', (res) => {
//登錄成功拿到后臺返回的用戶對應(yīng)的token
this.connectRongCloud(res.token);
});
//監(jiān)聽用戶退出登錄
this.events.subscribe('login:out', () => {
this.onExit();
})
this.platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
// this.statusBar.styleDefault();
// this.splashScreen.hide();
//初始化融云
this.rongService.init();
//判斷是否真機(jī)運(yùn)行
if (this.helper.isMobile()) {
this.registerBackButtonAction();//注冊返回按鍵事件
this.setBackground();//設(shè)置后臺運(yùn)行
}
//確保異步執(zhí)行完后才隱藏啟動(dòng)動(dòng)畫
this.events.subscribe('db:create', () => {
//創(chuàng)建數(shù)據(jù)庫與表成功后才關(guān)閉動(dòng)畫跳轉(zhuǎn)頁面
this.statusBar.styleDefault();
this.splashScreen.hide();
this.nav.setRoot(LoginPage);
})
//初始化創(chuàng)建數(shù)據(jù)庫
this.sqlite.createDb();
});
}
/**
* 設(shè)置后臺運(yùn)行
*/
setBackground() {
this.backgroundMode.enable();
//延遲啟用
setTimeout(() => {
this.backgroundMode.setDefaults({
title: ‘正在后臺運(yùn)行,請勿關(guān)閉應(yīng)用’,
text: ‘點(diǎn)擊即可進(jìn)入應(yīng)用’,
icon: 'assets/imgs/logo.png'
})
}, 2000);
}
/**
* android 物理返回鍵處理
*/
registerBackButtonAction() {
this.platform.registerBackButtonAction(() => {
if (this.keyboard.isOpen()) {//如果鍵盤開啟則隱藏鍵盤
this.keyboard.close();
return;
}
//如果想點(diǎn)擊返回按鈕隱藏toast或loading或Overlay就把下面加上
// this.ionicApp._toastPortal.getActive() || this.ionicApp._loadingPortal.getActive() || this.ionicApp._overlayPortal.getActive()
let activePortal = this.ionicApp._modalPortal.getActive();
if (activePortal) {
activePortal.dismiss().catch(() => { });
activePortal.onDidDismiss(() => { });
return;
}
let loadingPortal = this.ionicApp._loadingPortal.getActive();
if (loadingPortal) {
loadingPortal.dismiss().catch(() => { });
return;
}
if (this.app.getActiveNav().canGoBack() || this.nav.canGoBack()) {
this.app.navPop();
} else {
this.showExit();
}
return;
// return this.app.getActiveNav().canGoBack() ? this.app.navPop() : this.showExit();
}, 100);
}
//雙擊退出提示框
showExit() {
if (this.backButtonPressed) { //當(dāng)觸發(fā)標(biāo)志為true時(shí),即2秒內(nèi)雙擊返回按鍵則退出APP
// this.platform.exitApp();
this.appMinimize.minimize();
} else {
this.toast.showToast('再次返回將自動(dòng)切換到后臺運(yùn)行');
this.backButtonPressed = true;
setTimeout(() => this.backButtonPressed = false, 2000);//2秒內(nèi)沒有再次點(diǎn)擊返回則將觸發(fā)標(biāo)志標(biāo)記為false
}
}
/**
* 連接融云
*/
connectRongCloud(token) {
this.events.subscribe('message:new', message => {
if (this.helper.isMobile()) {
// 如果后臺運(yùn)行,則把程序從后臺切換到前臺運(yùn)行
this.backgroundMode.moveToForeground();
}
});
this.rongService.connectionStatusListener();
// 監(jiān)聽融云的服務(wù)...
this.rongService.receiveMessageListener();
this.rongService.connect(token).then((userId) => {
// 成功連接融云服務(wù)
console.log('登錄成功:' + userId);
}).catch((error) => {
// 登錄失敗處理
this.toast.showToast('登錄聊天服務(wù)器失敗' + error);
})
}
}
/**
* 退出登錄,清除用戶登錄信息
*/
onExit() {
this.rongService.logoutRy();
this.nav.setRoot(LoginPage);
}
3 添加播放音頻與本地通知
3.1 添加插件
$ ionic cordova plugin add cordova-plugin-nativeaudio
$ npm install --save @ionic-native/native-audio@4
$ ionic cordova plugin add cordova-plugin-local-notification
$ npm install --save @ionic-native/local-notifications@4
3.2 插件添加到應(yīng)用程序的模塊中、
3.3 創(chuàng)建AudioServiceProvider
$ ionic g provider AudioService
3.4 修改AudioServiceProvider
import { StorageServiceProvider } from './../storage-service/storage-service';
import { HelperProvider } from './../helper/helper';
import { Injectable } from '@angular/core';
import { NativeAudio } from '@ionic-native/native-audio';
/*
Generated class for the AudioServiceProvider provider.
See https://angular.io/guide/dependency-injection for more info on providers
and Angular DI.
*/
@Injectable()
export class AudioServiceProvider {
time:any;
audio: HTMLAudioElement;
constructor(private nativeAudio: NativeAudio,
private helper: HelperProvider,
private storageService: StorageServiceProvider) {
// this.nativeAudio.preloadComplex('bell', 'assets/bell.mp3', 1, 1, 0).catch(e => console.log(e));
//緩存無聲音頻
this.nativeAudio.preloadComplex('no', 'assets/no.mp3', 1, 1, 0).catch(e => console.log(e));
// this.nativeAudio.preloadComplex('prompt', 'assets/prompt.mp3', 1, 1, 0).catch(e => console.log(e));
//添加收到消息提示聲音,為了兼容低版本vivo和web端
this.audio = document.createElement('audio');
this.audio.src = 'http://xxx/assets/file/prompt.mp3';
this.audio.autoplay = false;
}
/**
* 播放無聲音音樂
*/
backgroundPlay() {
if(this.time){
clearInterval(this.time);
}
this.time = setInterval(()=>{
setTimeout(() => {
try{
this.nativeAudio.play('no');
}catch(err){
console.log(err)
}
}, 9000);
},10000)
}
/**
* 停止播放背景音樂
*/
stopBackground(){
clearInterval(this.time);
this.time = null;
this.nativeAudio.stop('no');
}
/**
* 播放消息提示聲音
*/
playMsg(){
// this.nativeAudio.play('prompt');
this.audio.play();
}
}
3.5 修改app.component,追加代碼
import { AudioServiceProvider } from '../providers/audio-service/audio-service';
import { LocalNotifications } from '@ionic-native/local-notifications';
constructor(private audioService: AudioServiceProvider,
private localNotifications: LocalNotifications) {}
/**
* 設(shè)置后臺運(yùn)行
*/
setBackground() {
this.audioService.backgroundPlay();
}
/**
* 連接融云
*/
connectRongCloud() {
this.events.subscribe('message:new', message => {
// 播放消息聲音
this.audioService.playMsg();
if (this.helper.isMobile()) {
//顯示通知
this.localNotifications.schedule({
id: message.id,
icon: 'res://ic_chat',
smallIcon: 'res://ic_chat',
sound: null,
silent: true,
title:message.title,
text: message.content,
data: message
});
//監(jiān)聽點(diǎn)擊事件
this.localNotifications.on('click').subscribe(res => {
//清除所有消息
this.localNotifications.clearAll();
});
}
});
}
4 集成百度語音合成
一開始采用cordova-plugin-tts進(jìn)行播報(bào),設(shè)置語言為中文,但在vivo手機(jī)出現(xiàn)只讀英文,中文被過濾無法播報(bào),好像是編碼問題,后來選用百度語音合成,因?yàn)槊赓M(fèi)
4.1 創(chuàng)建應(yīng)用

image.png
成功后會(huì)得到AppID,API Key,Secret Key
4.2 Java后臺設(shè)置
后臺寫一個(gè)接口代碼,播放參數(shù)前端傳給后臺
/**
* 測試單聊
*
* @param tex
* @return
* @throws Exception
*/
@RequestMapping("getAudio")
public void getAudio(HttpServletRequest request, Model model,
@RequestParam("lan") String lan,
@RequestParam("ctp") Integer ctp,
@RequestParam("tex") String tex,
@RequestParam("cuid") String cuid,
@RequestParam("spd") String spd,
@RequestParam("pit") String pit,
@RequestParam("vol") String vol,
@RequestParam("per") String per,
HttpServletResponse response
) throws Exception {
HashMap<String, Object> option = new HashMap<>();
option.put("cuid", cuid);
option.put("spd", spd);
option.put("pit", pit);
option.put("vol", vol);
option.put("per", per);
byte[] buff = ttsUtils.textToSpeech(tex, lan, ctp, option);
if (buff != null) {
response.setContentType("audio/mp3");
OutputStream outputStream = response.getOutputStream();
outputStream.write(buff);
outputStream.close();
}
}
4.3 創(chuàng)建TtsServiceProvider
$ ionic g provider TtsService
填寫代碼
import { StorageServiceProvider } from './../storage-service/storage-service';
import { Injectable } from '@angular/core';
import { ConfigProvider } from '../config/config';
/*
Generated class for the TtsServiceProvider provider.
See https://angular.io/guide/dependency-injection for more info on providers
and Angular DI.
*/
@Injectable()
export class TtsServiceProvider {
audio: any;
options = {
lan: "zh",//語言
ctp: '1',
cuid: '',//唯一標(biāo)識
tex: '',//播放內(nèi)容
spd: 5,//語速,取值0-15,默認(rèn)為5中語速
pit: 5,//音調(diào),取值0-15,默認(rèn)為5中語調(diào)
vol: 15,//音量,取值0-15,默認(rèn)為5中音量
per: 0//發(fā)音人選擇, 0為普通女聲,1為普通男生,3為情感合成-度逍遙,4為情感合成-度丫丫,默認(rèn)為普通女聲
}
constructor( private storageService: StorageServiceProvider) {
}
/**
* 初始化百度tts
*/
init() {
this.audio = document.createElement('audio');
this.audio.autoplay = false;
//如果第一次啟動(dòng),參數(shù)不存在,則存儲默認(rèn)參數(shù)
if (!this.storageService.read("baiduTTS")) {
this.storageService.write("baiduTTS", this.options);
}
}
/**
* 播放內(nèi)容
* @param text
*/
speak(text: string) {
//讀取最新存儲語音設(shè)置
this.options = this.storageService.read("baiduTTS");
this.options.cuid = 'xxx';//此處應(yīng)為用戶唯一標(biāo)識
//url編碼
this.options.tex = encodeURIComponent(text);
//設(shè)置地址為后臺播放地址
this.audio.src = 'http://xxx/message/getAudio' + '?' + this.toBodyString(this.options);
this.audio.play();
}
}
/**
* 停止播放
*/
stop() {
this.audio.pause();
}
/**
*
* @param obj
* @return {string}
* 聲明: var obj= {};
* 調(diào)用: toQueryString(obj);
* 返回: "name=%E5%B0%8F%E5%86%9B&age=23"
*/
toBodyString(obj) {
let ret = [];
for (let key in obj) {
if (key != 'constructor') {
key = encodeURIComponent(key);
let values = obj[key];
if (values && values.constructor == Array) {//數(shù)組
let queryValues = [];
for (let i = 0, len = values.length, value; i < len; i++) {
value = values[i];
queryValues.push(this.toQueryPair(key, value));
}
ret = ret.concat(queryValues);
} else { //字符串
ret.push(this.toQueryPair(key, values));
}
}
}
return ret.join('&');
}
private toQueryPair(key, value) {
if (typeof value == 'undefined') {
return key;
}
return key + '=' + encodeURIComponent(value === null ? '' : String(value));
}
}
4.4 修改app.component,追加代碼
import { TtsServiceProvider } from '../providers/tts-service/tts-service';
constructor(private ttsService: TtsServiceProvider) {}
initializeApp() {
this.platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
// 初始化百度tts
this.ttsService.init();
});
}
/**
* 連接融云
*/
connectRongCloud() {
this.events.subscribe('message:new', message => {
// 播放文本
this.ttsService.speak( message.content);
if (this.helper.isMobile()) {
this.localNotifications.on('click').subscribe(res => {
//清除該條
this.audioService.stopPlay();
//停止播放
this.ttsService.stop();
this.localNotifications.clearAll();
});
}
});
}