顯示具有 firebase 標籤的文章。 顯示所有文章
顯示具有 firebase 標籤的文章。 顯示所有文章

2018年2月6日 星期二

Ionic3 + FCM (推播)

Firebase 推出FCM 目的將用來取代GCM,雖然GCM 仍然不滅,但我們先來嘗鮮看看,連推播的介面都已經設計好了,只要簡單幾行Code就可以完成推播的功能,廢話不說,就先看示範吧。

一、   下戴必要的套件


二、   設定Config.XML

修改widget id的名稱,因為要對應到FCM準備要推播的應用程式名稱,所以如果不一致就無法接收推播了喔!!!  很重要

三、注入model

import { FCM } from '@ionic-native/fcm';
@NgModule({
  declarations: [
    MyApp,
    AboutPage,
    ContactPage,
    HomePage,
    TabsPage
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp)
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
    AboutPage,
    ContactPage,
    HomePage,
    TabsPage
  ],
  providers: [
    StatusBar,
    SplashScreen,
    {provide: ErrorHandler, useClass: IonicErrorHandler},
    FCM
  ]
})
export class AppModule {}
FCM 推播的物件是要被注入Provider ,如果沒注入 就會失敗囉~

四、呼叫FCM 的實作演練
import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { FCM } from '@ionic-native/fcm';   //注入FCM Constructor
@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

  constructor(public navCtrl: NavController, private fcm: FCM) {
  
  fcm.subscribeToTopic('marketing');
 
//取得FCM token
  fcm.getToken().then(token => {
    alert("getToken")
  })

 //取得FCM 的推播訊息
   fcm.onNotification().subscribe(data => {
    if (data.wasTapped) { //取得背景監聽
      alert("message in background!");
    } else {
      alert("message in front! "); //前景
      alert(JSON.stringify(data)); //取得推播資訊
    };
  })

  //取消推播
  fcm.unsubscribeFromTopic('marketing');

  }

}

根據Ionic的官方文件 需要訂閱名稱,位置放在constructor fcm.subscribeToTopic('marketing');

在結束時就要將訂閱名稱取消
fcm.unsubscribeFromTopic('marketing');

五、設定推播內容

Project OverView à 新增其他應用程式 (config.xml的 widget id)



















點選新增訊息



選擇剛剛新增 應用程式











設定前景資料(當應用程式是開啟)

接收的推播


2017年10月11日 星期三

如何實作 Ionic 3 的 WebRTC

 不少人都在詢問如何實作cordovaWebRTC,目前作者使用Peer.JS 就能完成一連串的影像互傳的功能,此篇文章在『Angular2 簡易版的webRTC』已簡單介紹過了,目前要更進一步實作出Cordova 在手機上與電腦也能互傳影像。

一、介紹FireBase
支援real-time的一種雲端服務,更重要的一點是完全免費的喔!!! 簡單的key-valueJSON格式,支援資料庫的匯入和匯出,順帶一提免費版的有些容量是有限制的。

為何需要雲端資料庫FireBase儲存資料,因為Peer.js 會產生一組perid讓對方可以透過perid 互傳影像,所以儲存登入者名稱和peerid…

二、登入FireBase 開啟功能


Google 登入à go to console à新增專案




















點選overview旁邊的設定à專案設定



選擇 firebase 加入網路應用程式

複製以下的設定檔
















一、建立ionic 專案

ionic start myApp tabs


$ ionic cordova plugin add cordova-plugin-firebase
$ npm install --save @ionic-native/firebase 
$ npm install angularfire2 firebase --save

Providers/databaseservice/user.ts (實作資料層)

import { Injectable } from '@angular/core';
import {AngularFireDatabase,FirebaseListObservable } from 'angularfire2/database';

@Injectable()
export class UserService {
   constructor(private _af: AngularFireDatabase) {
   }

   public listUsers(): FirebaseListObservable<any[]>{
      return this._af.list('users');
   }

   public addUser(userName,peerID){
        this.listUsers().push({ userName: userName, peerID: peerID});
   }
  
   public removeUser(userName){
       let removeItem = this._af.list('users', {
           query: {
               orderByChild: "userName",
               equalTo: userName
           }
       });

    // Query the list for the purposes of this example:
       removeItem.subscribe((items) => {
           // Remove the matching item:
           if (items.length) {
               removeItem.remove(items[0].$key)
                   .then(() => console.log('removed ' + items[0].$key))
                   .catch((error) => console.log(error));
           }
       });
   }
}

修改app.module.ts
import { AngularFireModule } from 'angularfire2';
import {AngularFireDatabaseModule} from 'angularfire2/database';
import { UserService } from "../providers/databaseservice/users";

// AF2 Settings
export const firebaseConfig = {
  apiKey: "xxxxxxxxxx",
  authDomain: "xxxxxxxxx.firebaseapp.com",
  databaseURL: "https:// xxxxxxxxx.firebaseio.com",
  storageBucket: "xxxxxxxxx.appspot.com",
  messagingSenderId: "xxxxxxxxx"
};


注入module
imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp),
    AngularFireModule.initializeApp(firebaseConfig),
    AngularFireDatabaseModule
  ],

注入prvider
  providers: [
    StatusBar,
    SplashScreen,
    UserService,
    {provide: ErrorHandler, useClass: IonicErrorHandler},
  ]

ionic g page login  建立登入頁

pages/login/login.html
<ion-header>
  <ion-navbar color="primary">
    <ion-title  align-title="center">登錄</ion-title>
  </ion-navbar>
</ion-header>
<ion-content padding>
  <ion-item>
    <ion-label fixed>名稱</ion-label>
    <ion-input type="text" [(ngModel)]="name"></ion-input>
  </ion-item>

  <ion-item>
    <ion-label fixed>密碼</ion-label>
    <ion-input type="text" [(ngModel)]="password"></ion-input>
  </ion-item>
  <p>
   <button ion-button full round (click)="login(name)">登錄</button>
</ion-content>

pages/login/login.ts
@Component({
  selector: 'page-login',
  templateUrl: 'login.html',
})
export class LoginPage {

  constructor(public navCtrl: NavController, public navParams: NavParams) {
  }

  login(name) {
     登入頁面並傳入參數
     this.navCtrl.push(TabsPage, {
      userName: name
    });
  }
}

pages/tabs/tabs.ts
import { Component } from '@angular/core';
import { HomePage } from '../home/home';
import { NavController,NavParams } from 'ionic-angular';

@Component({
  templateUrl: 'tabs.html'
})
export class TabsPage {
  userName; 宣告參數
  tab1Root = HomePage;

  constructor(params: NavParams) {
     this.userName = params.get("userName"); 取得參數
  }
}  

pages/home/home.html
<ion-header>
  <ion-navbar>
    <ion-title>{{viewUserName}} 歡迎</ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>
    <h1>{{mypeerid}}</h1>
      <ion-card>
        <ion-card-content>
            <video #myvideo></video>
        </ion-card-content>
    </ion-card>
    <ion-item *ngFor="let user of users | async"
        item="item" (click)="startCall(user.userName,user.peerID)">
        {{user.userName}} - {{user.$key}}</ion-item>

     <button ion-button full (click)="videoconnect()">開始通話</button>
     <button ion-button full (click)="stopCall(viewUserName)">結束通話</button>
</ion-content>

pages/home/home.ts
import { Component, ViewChild, OnInit } from '@angular/core';
import { NavController, NavParams } from 'ionic-angular';
import { FirebaseListObservable } from 'angularfire2/database'; 注入fireBase的相關模組
import { UserService } from "../../providers/databaseservice/users";

declare var Peer: any;   宣告peer的物件
@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})

export class HomePage implements OnInit {
  title = 'app';
  mypeerid: any;
  peer;
  anotherid;
  key;
  @ViewChild('myvideo') myVideo: any取得myVideo的元素
  users: FirebaseListObservable<any[]> fireBase的陣列集合
  viewUserName:any;
  _streamCopy;
  constructor(public navCtrl: NavController, public navParams: NavParams, public userService: UserService) {
    this.users = userService.listUsers(); 取得資料users的集合
    this.viewUserName=navParams.data;
  }

  ngOnInit() {
    let video = this.myVideo.nativeElement;
    this.peer = new Peer({ host: 'peerjs-server.herokuapp.com', secure: true, port: 443 })
    this.mypeerid = this.peer.id;
    setTimeout(() => {
      this.mypeerid = this.peer.id;
       this.userService.addUser(this.viewUserName,this.peer.id );
    }, 1000);
     
    this.peer.on('connection', function (conn) {
      conn.on('data', function (data) {
        console.log(data);
      });
    });

    var n = <any>navigator;
    n.getUserMedia = (n.getUserMedia || n.webkitGetUserMedia || n.mozGetUserMedia || n.msGetUserMedia);
    this.peer.on('call', function (call) {
      n.getUserMedia({ video: true, audio: false }, function (stream) {
        call.answer(stream);
        call.on('stream', function (remotestream) {
          video.src = URL.createObjectURL(remotestream);
          video.play();
        })
      }, function (err) {
        console.log('Failed to get stream', err);
      })
    })
  }

  startCall(userName,anotherID) {
    this.anotherid = anotherID;
    var conn = this.peer.connect(anotherID);
    conn.on('open', function () {
      conn.send('Message from that id');
    });
  }

   this.peer.on('disconnected', function () {
console.log("disconnected");
});

  videoconnect(){
    let video = this.myVideo.nativeElement;
    var localvar = this.peer;
    var fname = this.anotherid;
    var n = <any>navigator;
    console.log("another peerID:"+this.anotherid);
    n.getUserMedia = ( n.getUserMedia || n.webkitGetUserMedia || n.mozGetUserMedia  || n.msGetUserMedia );
   
    n.getUserMedia({
      video:  { width: 700, height: 720 } , audio:false
    }, function(stream) {
      var call = localvar.call(fname, stream);
      call.on('stream', function(remotestream) {
        video.src = URL.createObjectURL(remotestream);
        video.play();
      })
    }, function(err){
      alert(err.name);
      console.log('Failed to get stream', err);
    })
  }

  stopCall(userName) {
    this.userService.removeUser(userName);
    this.peer.destroy();
  }
}

Ionic cordova build android

設定Android的權限

Platforms/android/ AndroidManifest.xml
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />


圖片示範