MAUI開發Android程式使PDA掃碼廣播訊息轉發至Web頁面

2023-05-12 12:00:29

前言

公司系統的手持終端(PDA)是用的Vue寫的前端程式碼
在PDA上用瀏覽器直接開啟Web頁面
PDA掃碼的時候,輸出模式直接用模擬鍵盤按鍵的方式輸出
這樣在Web頁面上,如果一個輸入框在當前有焦點的情況下
PDA掃碼的內容會直接填充至對應的輸入框
正常的話這樣沒有問題

但是最近有一個專案,PDA不是我們提供。
而是使用現有PDA,要把我們的系統在現有PDA上使用
但是現有PDA使用的掃碼輸出方式是用的Andorid廣播

因為現有PDA不只有我們一家系統,所以不能修改掃碼輸出方式
這樣就使得我們不得不對系統進行改造


思考方法

因為系統已經使用Vue框架已經開發好的。不可能為了這麼點事情
把PDA上的系統全用Android重新來開發一次,那樣成本太大

所以想的辦法也很簡單。
就是做一個Andorid的程式套殼,然後在程式裡使用WebView載入現有系統
這樣在Andorid程式裡接收PDA掃碼的廣播訊號
然後收到訊號後,把掃碼到的內容使用WebView的JavaScript呼叫方式
傳輸到Web頁面接收。
這樣就實現了在Web頁面上接收Andorid的廣播訊息功能

實現過程

因為我們沒有Andorid的開發人員,
只有前端的NodeJs和後端的.Net開發人員
所以開發Android程式框架也很自然
只能是.Net開發人員使用 Xamarin.Android 或者 MAUI 這兩種方式

因為只是一個Android的程式套殼,介面也不是很難
所以就當一次小試驗,自然也就想嘗試一下微軟最新的MAUI了

安裝MAUI

因為我們原來開發沒有使用過MAUI,雖然機器上有VS2022
但是也要新增MAUI的開發功能
安裝MAUI參考連結

建立MAUI應用

參照微軟的檔案一步步操作建立MAUI應用
建立MAUI應用參考連結

在主介面新增WebView

WebView參考檔案

我們設定WebView的瀏覽地址為我們系統的Web地址
此處設定為:http://10.76.99.70:8081/

把MainPage.xaml檔案修改如下

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="WMS.RFWrap.MainPage">

    <ScrollView>
        <StackLayout>
            <WebView x:Name="mainWeb" Source="http://10.76.99.70:8081/" VerticalOptions="FillAndExpand"></WebView>
        </StackLayout>
    </ScrollView>

</ContentPage>

接收Android廣播訊息

在Platforms.Andorid目錄下建立廣播訊息接收程式碼
其中,IntentFilter設定的值要與PDA上設定的廣播訊息程式碼一至

namespace WMS.RFWrap.Platforms.Android;

[BroadcastReceiver(Enabled = true, Exported = true)]
[IntentFilter(new[] { "android.intent.ACTION_DECODE_DATA" })]
public class ScanBroadcastReceiver : BroadcastReceiver
{
    private Action<string> ScanDataAccepted;
    public ScanBroadcastReceiver()
    {

    }
    public ScanBroadcastReceiver(Action<string> action)
    {
        this.ScanDataAccepted = action;
    }
    public override void OnReceive(Context context, Intent intent)
    {
        var value = intent.GetStringExtra("barcode_string");
        ScanDataAccepted?.Invoke(value);
    }
}

在Platforms.Andorid.MainActivity.cs檔案註冊廣播接收

public class MainActivity : MauiAppCompatActivity
{
    public ScanBroadcastReceiver scanReceiver { get; set; }
    /// <summary>
    /// 委託事件
    /// </summary>
    public static Action<string> ScanDeviceRecevied;
    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        scanReceiver = new ScanBroadcastReceiver((barcode) =>
        {
            ScanDeviceRecevied?.Invoke(barcode);
        });
    }
    protected override void OnResume()
    {
        base.OnResume();
        RegisterReceiver(scanReceiver, new Android.Content.IntentFilter("android.intent.ACTION_DECODE_DATA"));
    }
    protected override void OnPause()
    {
        UnregisterReceiver(scanReceiver);
        base.OnPause();
    }
}

在MainPage.xaml.cs檔案裡編寫接收程式碼
接收到廣播掃碼內容後,通過WebView的Eval方法來執行Web頁面方法
我們這裡使用的是window.postMessage協定通訊
window.postMessage參考檔案
我們呼叫postMessage方法,把內容傳輸到Web頁面

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
#if ANDROID
        //var mainActivity = Platform.CurrentActivity as MainActivity;
        MainActivity.ScanDeviceRecevied = (barcode) => {
            Console.WriteLine(barcode);
            OnScanBarcode(barcode);
        };
#endif
    }
    public void OnScanBarcode(string barcode)
    {
        if (mainWeb != null)
        {
            var data = new { type = "barcode", data = barcode };
            var json = JsonSerializer.Serialize(data);
            var script = $"postMessage({json},'*')";
            mainWeb.Eval(script);
        }
    }
}

在前端Vue頁面接收WebView傳過來的postMessage訊號
因為我們使用的是postMessage訊號,所以在前端只要監聽message事件就可以了
message事件與Vue無關,所有Web頁都可以使用
然後在onScanMsg回撥方法裡面處理對應的資料,就能正常接收資訊了
window.addEventListener('message', onScanMsg)

<template>
  <router-view />
</template>

<script>
import { provide, ref, reactive, toRaw, onMounted, onUnmounted } from 'vue'
import { useStore } from 'vuex'
export default {
  setup() {
    const store = useStore() //使用store
    onMounted(() => {
      window.addEventListener('message', onScanMsg)
    })
    const onScanMsg = (e) => {
      console.log('onScanMsg', e)
      if (typeof e.data === 'object' && e.data.type === 'barcode')
        store.commit('SET_SCANCODE', e.data.data)
    }
    return {}
  }
}
</script>

<style>
</style>

本範例是接收到資訊後,通過vuex設定全域性Store值
這樣在要獲取掃碼的頁面只用監聽Store值變化,就可以接收到最新的掃碼結果

<template>
  <van-nav-bar title="掃碼" left-text="資料" left-arrow fixed placeholder @click-left="$router.replace({ path: '/Home/DataIndex' })" />
  <van-field v-model="scancode" label="資料" placeholder="請掃碼" />
  <van-list>
    <van-cell-group>
      <van-cell v-for="item in list" :key="item" :title="item" />
    </van-cell-group>
  </van-list>
</template>

<script>
import { defineComponent, reactive, ref, toRefs, toRaw, onMounted, watch, computed, getCurrentInstance } from 'vue'
import { useStore } from 'vuex'
export default {
  setup() {
    const store = useStore()//使用store
    const scancode = computed(() => store.getters.scancode)
    const list = ref([])
    watch(() => store.getters.scancode, (newValue) => {
      list.value.push(newValue)
    })
    return {
      scancode,
      list
    }
  }
}
</script>

<style>
</style>

最終實現結果

強烈建議PDA裝置廠商把 PDA廣播訊號 整合至 WebView