如何在 Delphi 10.4.1 应用程序中从 Zebra TC21 扫描仪接收 Android Intent

How to receive Android Intent from Zebra TC21 Scanner in Delphi 10.4.1 application

我正在使用 Delphi 10.4.1 开发 Android 应用程序以部署在 Zebra TC21 上。我正在尝试从 DataWedge Intent 接收条形码,但我的应用程序中的 HandleIntentAction 未触发。 Zebra TC21 扫描器配置:Android10,DataWedge 版本 8.2.60。我在 Delphi、Embarcadero 示例、DataWedge 配置演示中阅读了一些关于接收意图的文章,但我仍然无法解决问题。有人可以帮我吗?

“MyTest”应用程序(pl.dplodz.MyTest):

procedure TMainForm.FormCreate(Sender: TObject);
var AppEventService: IFMXApplicationEventService;
begin
  if TPlatformServices.Current.SupportsPlatformService(IFMXApplicationEventService, AppEventService) then
    AppEventService.SetApplicationEventHandler(HandleAppEvent);
  // Register the type of intent action that we want to be able to receive.
  // Note: A corresponding <action> tag must also exist in the <intent-filter> section of AndroidManifest.template.xml.
  MainActivity.RegisterIntentAction(StringToJString('pl.dplodz.ACTION'));
  TMessageManager.DefaultManager.SubscribeToMessage(TMessageReceivedNotification, HandleActivityMessage);
  Memo1.Lines.Clear;
  Memo1.Lines.Add(TimeToStr(Now) + ': AppStarted');
end;

procedure TMainForm.HandleActivityMessage(const Sender: TObject; const M: TMessage);
begin
  Memo1.Lines.Add(TimeToStr(Now) + ': HandleActivityMessage Fired');
  if M is TMessageReceivedNotification then
    HandleIntentAction(TMessageReceivedNotification(M).Value);
end;

function TMainForm.HandleAppEvent(AAppEvent: TApplicationEvent; AContext: TObject): Boolean;
var StartupIntent: JIntent;
begin
  Memo1.Lines.Add(TimeToStr(Now) + ': HandleAppEvent Fired');
  Result := False;
  if AAppEvent = TApplicationEvent.BecameActive then
  begin
    StartupIntent := MainActivity.getIntent;
    if StartupIntent <> nil then
      HandleIntentAction(StartupIntent);
  end;
end;

function TMainForm.HandleIntentAction(const Data: JIntent): Boolean;
var Extras : JBundle;
    JStr   : JString;
begin
  Memo1.Lines.Add(TimeToStr(Now) + ': HandleIntentAction Fired');
  Result := False;
  if Data <> nil then
  begin
    Extras := Data.getExtras;
    if Extras <> nil then
      Memo1.Lines.Add(JStringToString(Extras.getString(TJIntent.JavaClass.EXTRA_TEXT)))
    else
      Memo1.Lines.Add(TimeToStr(Now) + ': Extras = nil');
  end
  else
    Memo1.Lines.Add(TimeToStr(Now) + ': Data = nil');
  Invalidate;
end;

procedure TMainForm.CloseBtnClick(Sender: TObject);
begin
  Close;
end;

procedure TMainForm.ClearMemoBtnClick(Sender: TObject);
begin
  Memo1.Lines.Clear;
end;

AndroidManifest.Template.XML:

<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="%package%"
        android:versionCode="%versionCode%"
        android:versionName="%versionName%"
        android:installLocation="%installLocation%">

    <uses-sdk android:minSdkVersion="%minSdkVersion%" android:targetSdkVersion="%targetSdkVersion%" />
    <%uses-permission%>
    <uses-feature android:glEsVersion="0x00020000" android:required="True"/>
    <application android:persistent="%persistent%" 
        android:restoreAnyVersion="%restoreAnyVersion%" 
        android:label="%label%" 
        android:debuggable="%debuggable%" 
        android:largeHeap="%largeHeap%"
        android:icon="%icon%"
        android:theme="%theme%"
        android:hardwareAccelerated="%hardwareAccelerated%"
        android:resizeableActivity="false"
        android:requestLegacyExternalStorage="true">

        <%provider%>
        <%application-meta-data%>
        <%uses-libraries%>
        <%services%>
        <!-- Our activity is a subclass of the built-in NativeActivity framework class.
             This will take care of integrating with our NDK code. -->
        <activity android:name="com.embarcadero.firemonkey.FMXNativeActivity"
                android:label="%activityLabel%"
                android:configChanges="orientation|keyboard|keyboardHidden|screenSize"
                android:launchMode="singleTask">
            <!-- Tell NativeActivity the name of our .so -->
            <meta-data android:name="android.app.lib_name"
                android:value="%libNameValue%" />
            <intent-filter>  
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter> 
            <intent-filter>  
                <action android:name="pl.dplodz.ACTION" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        <%activity%>
        <%receivers%>
    </application>
</manifest>
<!-- END_INCLUDE(manifest) -->

我的 DataWedge 配置文件配置:

New profile: "MyTest", key configuration:
Profile Enabled
Associated Apps: "com.embarcadero.firemonkey.FMXNativeActivity | pl.dplodz.MyTest"
Barcode Input: Enabled
Keystroke Output: Disabled
Intent Output section:
  Intent action: pl.dplodz.ACTION
  Intent category: android.intent.category.DEFAULT  (btw. I tested with empty value - no changes)
  Intent delivery: Broadcast intent
  Component Information: [empty]

我在扫描代码时听到哔声,但应用程序没有任何反应。问题出在哪里?在 DataWedge 配置中?在申请中?

根据我的评论,如果您通过 RegisterIntentAction 处理意图,则需要将 Intent Delivery 设置为 Start Activity。

但是,您可以通过在代码中创建接收器来接收广播意图。在这种情况下,您的应用程序需要已经 运行,但这里有一个可能的实现:

uses
  Androidapi.JNIBridge, Androidapi.JNI.Embarcadero, Androidapi.JNI.GraphicsContentViewText;

type
  TDataWedgeDataEvent  = procedure(Sender: TObject; const Data: string) of object;

  TDataWedgeBroadcastListener = class(TJavaLocal, JFMXBroadcastReceiverListener)
  private
    FReceiver: JBroadcastReceiver;
    FOnData: TDataWedgeDataEvent;
  public
    { JFMXBroadcastReceiverListener }
    procedure onReceive(context: JContext; intent: JIntent); cdecl;
  public
    constructor Create;
    destructor Destroy; override;
    property OnData: TDataWedgeDataEvent read FOnData write FOnData;
  end;

implementation

uses
  Androidapi.Helpers;

{ TDataWedgeBroadcastListener }

constructor TDataWedgeBroadcastListener.Create;
var
  LIntentFilter: JIntentFilter;
begin
  inherited;
  FReceiver := TJFMXBroadcastReceiver.JavaClass.init(Self);
  LIntentFilter := TJIntentFilter.JavaClass.init;
  LIntentFilter.addAction(StringToJString('pl.dplodz.ACTION')); // or whatever value the profile is configured for
  LIntentFilter.addCategory(StringToJString('android.intent.category.DEFAULT'));
  TAndroidHelper.Context.registerReceiver(FReceiver, LIntentFilter);
end;

destructor TDataWedgeBroadcastListener.Destroy;
begin
  TAndroidHelper.Context.unregisterReceiver(FReceiver);
  inherited;
end;

procedure TDataWedgeBroadcastListener.onReceive(context: JContext; intent: JIntent);
begin
  if (intent <> nil) and Assigned(FOnData) then
    FOnData(Self, JStringToString(intent.getStringExtra(StringToJString('com.symbol.datawedge.data_string'))));
end;

创建 TDataWedgeBroadcastListener 实例并连接到 OnData 事件。使用此方法,您无需在清单中设置 Intent 过滤器