ionic3 即時(shí)通訊(待機(jī)/后臺運(yùn)行可用)

最近開發(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插件

融云cordova地址

$ ionic cordova plugin add cordova-plugin-rongcloud-im
2.2 添加web版融云

融云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)用

點(diǎn)擊注冊并創(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();
          });
        } 
      });

  }



代碼有刪減,可能存在缺失代碼問題,可惜的是本地通知在待機(jī)狀態(tài)下并不能出現(xiàn)

更多詳情可以簡書交流或 深圳網(wǎng)站建設(shè) https://www.qijianwang.net

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容