NFC Emulator for Windows Phone 8

隨著NFC的發展,相關的應用也日趨廣泛,讓原本用”應用不廣泛,不普及”為由不添加NFC晶片至手機中的諸家廠商也開始轉向,不管是Android還是Windows Phone,中高階手機都已經內建了NFC晶片

/黃忠成

 

 

NFC(Near Field Communication)

 

      隨著NFC的發展,相關的應用也日趨廣泛,讓原本用”應用不廣泛,不普及”為由不添加NFC晶片至手機中的諸家廠商也開始轉向,不管是Android還是Windows Phone,中高階手機都已經內建了NFC晶片,可以預想的未來的iOS裝置也會跟上。

  在這領域中,最積極的莫過於Sony所推出的Andorid手機及Nokia所推出的Windows Phone手機,這兩家廠商只要不是低階的手機,NFC都一定是標準配備。

 

Proximity Tapper

 

     在Windows Phone 8上市初期,NFC雖然出現在Nokia全系列的Windows 8手機中,但是開發者卻沒有一個方便的模擬工具可以進行NFC相關應用的測試,筆者為了測試NFC於Windows Phone 8的應用,先後購買了HTC的8X及Nokia 620來進行測試,

  對開發者而言,這是一個非常傷本的投資。

    很幸運的,Windows 8 NFC Emulator出現後不久,一套免費的NFC Emulator for Windows Phone 8 工具推出了,這就是本文介紹的: Proximity Tapper。

  透過這個工具,開發者不需要再耗費金錢去購買兩支Windows Phone 8手機,就可以在一個以上的Windows Phone 8模擬器上測試NFC功能,最棒的是她是完全免費且Open Source的。

 

 

安裝及使用

 

   Proximity Tapper的安裝非常簡單,只要到以下網址下載程式:

 

http://proximitytapper.codeplex.com/

 

切到Download頁面即可下載並安裝。

 

圖1

 

特別注意一點,在安裝過程中會出現Windows 防火牆的提示,請一律選擇”允許”,如果不小心沒選到,事後可以到控制台中的Windows防火牆去打開。

圖2

 

確定一切正常後,便可開啟ProximityTapper.exe,通常安裝後會直接執行,未來可透過桌面捷徑來啟動。

圖3

啟動後會看到以下畫面。

圖4

預設是會自動尋找已啟動的Windows Phone 8 Emulator,接著啟動一個Windows Phone 8 Emulator來試試。

圖5

 

如果你啟動了Windows Phone 8 Emulator,而ProximityTapper沒找到,那麼很有可能是防火牆的設定未打開,請先確定這點。

當找到後,可以選取該Emulator,然後點選Tap Selected Device and Remain Connected按鈕,然後就可以點選下方的Send Url來模擬以NFC傳送URL至模擬器。

圖6

 

圖7

 

 

Windows Phone 8 SDK中內建的NFC APIs

 

  Windows Phone 8 SDK中提供了基礎的NFC APIs,開發者可以利用這些APIs來撰寫NFC相關的應用程式,例如在兩個Windwos Phone 8 裝置中交換訊息,或是先進行配對然後以藍芽了傳送較大的檔案,

本文先以一個簡單的交換訊息應用程式來展示ProximityTapper的模擬功能。

  注意,要使用NFC功能的Windows Phone Project必須啟用ID_CAP_PROXIMITY權限。

圖8

 

接著請在畫面上放置一個Button,程式如下:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Net;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Navigation;

using Microsoft.Phone.Controls;

using Microsoft.Phone.Shell;

using PhoneApp15.Resources;

using Windows.Networking.Proximity;


namespace PhoneApp15

{

    public partial class MainPage : PhoneApplicationPage

    {

        // Constructor

        public MainPage()

        {

            InitializeComponent();


            // Sample code to localize the ApplicationBar

            //BuildLocalizedApplicationBar();

        }


        private void Button_Click(object sender, RoutedEventArgs e)

        {

            ProximityDevice device = ProximityDevice.GetDefault();

            if (device == null)

                MessageBox.Show("nfc is not found.");

            device.PublishUriMessage(new Uri("http://www.microsoft.com"));

        }

………………………

完成後執行此程式,接著啟動另一個模擬器(Windows Phoen 8可以同時開啟不同解析度的模擬器,你可以開啟另一個Visual Studio 2012來啟動另一個模擬器,或是透過後面提供的連結來手動啟動),

然後在ProximityTapper以Ctrl+Mouse Click選取兩個模擬器。

圖9

 

圖10

 

完成配對後,就可以點選Button來送出訊息。

圖11

 

 

手動啟動Windows Phone 8 Emulator

http://blogs.msdn.com/b/notime/archive/2013/04/21/shortcut-to-start-windows-phone-8-emulator.aspx

 

 

 

 

PeerFinder

 

  透過PeerFinder,可以讓兩個裝置以NFC配對然後開啟藍芽或直接透過NFC傳送自訂義的資訊,下面是一個簡單的程式。

using System;

using System.Collections.Generic;

using System.Linq;

using System.Net;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Navigation;

using Microsoft.Phone.Controls;

using Microsoft.Phone.Shell;

using PhoneApp15.Resources;

using Windows.Networking.Proximity;

using System.Runtime.InteropServices.WindowsRuntime;

using Windows.Storage.Streams;

using Windows.Networking.Sockets;

using System.Threading.Tasks;


namespace PhoneApp15

{

    public partial class MainPage : PhoneApplicationPage

    {

        // Constructor

        public MainPage()

        {

            InitializeComponent();


            PeerFinder.TriggeredConnectionStateChanged += PeerFinder_TriggeredConnectionStateChanged;

            PeerFinder.Start();


            Task.Factory.StartNew(async () =>

                {

                    while (true)

                    {

                        if (_streamSocket != null)

                        {

                            DataReader dr = new DataReader(_streamSocket.InputStream);

                            try

                            {

                                try

                                {

                                    await dr.LoadAsync(sizeof(uint));

                                }

                                catch (Exception)

                                {

                                    continue;

                                }

                                uint strlen = dr.ReadUInt32();

                                await dr.LoadAsync(strlen);

                                string data = dr.ReadString(strlen);

                                Dispatcher.BeginInvoke(() =>

                                {

                                    MessageBox.Show(data);

                                });

                            }

                            finally

                            {

                                dr.DetachStream();

                                dr.Dispose();

                                dr = null;

                            }

                        }

                        await Task.Delay(1000);

                    }

                });

            // Sample code to localize the ApplicationBar

            //BuildLocalizedApplicationBar();

        }



        private async void Button_Click(object sender, RoutedEventArgs e)

        {

            if (_streamSocket != null)

            {

                DataWriter dw = new DataWriter(_streamSocket.OutputStream);

                string message = "i am code6421";

                try

                {

                    try

                    {

                        dw.WriteUInt32((uint)message.Length);

                        await dw.StoreAsync();

                    }

                    catch (Exception)

                    {

                        _streamSocket.Dispose();

                        return;

                    }

                    dw.WriteString(message);

                    await dw.StoreAsync();

                }

                finally

                {

                    dw.DetachStream();

                    dw.Dispose();

                    dw = null;

                }

            }

        }


        private StreamSocket _streamSocket = null;


        async void PeerFinder_TriggeredConnectionStateChanged(object sender, TriggeredConnectionStateChangedEventArgs args)

        {

            switch (args.State)

            {

                case TriggeredConnectState.Listening:

                    // Connecting as host

                    break;

                case TriggeredConnectState.PeerFound:

                    // Proximity gesture is complete and user can pull their devices away. Remaining work is to

                    // establish the connection using a different transport, like TCP/IP or Bluetooth

                    break;

                case TriggeredConnectState.Connecting:

                    // Connecting as a client

                    break;

                case TriggeredConnectState.Completed:

                    // Connection completed, retrieve the socket over which to communicate

                    _streamSocket = args.Socket;                  

                    break;

                case TriggeredConnectState.Canceled:

                    break;

                case TriggeredConnectState.Failed:

                    // Connection was unsuccessful

                    break;

            }

        }


        // Sample code for building a localized ApplicationBar

        //private void BuildLocalizedApplicationBar()

        //{

        //    // Set the page's ApplicationBar to a new instance of ApplicationBar.

        //    ApplicationBar = new ApplicationBar();


        //    // Create a new button and set the text value to the localized string from AppResources.

        //    ApplicationBarIconButton appBarButton = new ApplicationBarIconButton(new Uri("/Assets/AppBar/appbar.add.rest.png", UriKind.Relative));

        //    appBarButton.Text = AppResources.AppBarButtonText;

        //    ApplicationBar.Buttons.Add(appBarButton);


        //    // Create a new menu item with the localized string from AppResources.

        //    ApplicationBarMenuItem appBarMenuItem = new ApplicationBarMenuItem(AppResources.AppBarMenuItemText);

        //    ApplicationBar.MenuItems.Add(appBarMenuItem);

        //}

    }

}

 

編譯後透過Visual Studio 2012分別部署到兩個模擬器,分別執行後測試。

圖12

圖13

 

PS: 在其中一個模擬器執行App後,你必須先透過ProximityTapper斷開兩個模擬器的連線,再重新連線來模擬進接裝態。