隨著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斷開兩個模擬器的連線,再重新連線來模擬進接裝態。  |