Search code examples
androiddelphiandroid-intentfiremonkeydelphi-10-seattle

How to read intent in Delphi multi-device app broadcasted from android app


I recently got my hands on a Android barcode scanner device. I want to write a small inventory multi-device app in Delphi 10 Seattle. Scanner has two mode settings to return scanned barcode, first mode just passes barcode to an focused edit control, second one is broadcast mode. Because Delphi still has some quirks with edit controls which are not working properly especially in tandem with virtual keyboard in android enviroment, I have to use broadcast mode. On the web I found manual with little to none helpful info, API to control device, and android demo application written in eclipse.

I was able to get API wrapper for Delphi and successfully control device, change parameters, turn on off scanner and etc.

Yet doe to poor understanding how android intent and broadcasting works,I am not able to successfully implement it.

I did study intent sample for Delphi http://docwiki.embarcadero.com/CodeExamples/Seattle/en/FMX.Android_Intents_Sample. Did run Android intent sample app and it did work.

Eclipse android demo project is working fine too, did compile and run it without a problem. Dilemma now is how to receive broadcasted intent in Delphi. I have manifest.xml and mainactivity code from working eclipse demo here...

Manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.testscan"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="15"
        android:targetSdkVersion="19" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.testscan.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>
 

Mainactivity:

package com.example.testscan;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.device.ScanDevice;
import android.os.Bundle;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;

public class MainActivity extends Activity {
    ScanDevice sm;
     private final static String SCAN_ACTION = "scan.rcv.message";
     private String barcodeStr;
     private EditText showScanResult;

     
        private BroadcastReceiver mScanReceiver = new BroadcastReceiver() {

            @Override
            public void onReceive(Context context, Intent intent) {
                // TODO Auto-generated method stub

                byte[] barocode = intent.getByteArrayExtra("barocode");
                int barocodelen = intent.getIntExtra("length", 0);
                byte temp = intent.getByteExtra("barcodeType", (byte) 0);
                android.util.Log.i("debug", "----codetype--" + temp);
                barcodeStr = new String(barocode, 0, barocodelen);
                showScanResult.append("tere");
                showScanResult.append(barcodeStr);
                showScanResult.append("\n");
         //       showScanResult.setText(barcodeStr);
                sm.stopScan();
            }

        };

     
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        sm = new ScanDevice();
        CheckBox ch = (CheckBox) findViewById(R.id.checkBox1);
        if(sm.getOutScanMode()==1){
            ch.setChecked(true);
        }else{
            ch.setChecked(false);
        }
        ch.setOnCheckedChangeListener(new OnCheckedChangeListener(){

            @Override
            public void onCheckedChanged(CompoundButton arg0, boolean arg1) {
                // TODO Auto-generated method stub
                if(arg1){
                    sm.setOutScanMode(1);
                }else{
                    sm.setOutScanMode(0);
                }
            }});
        
        showScanResult=(EditText) findViewById(R.id.editText1);
    }
    
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.openScanner:
            System.out.println("openScanner = "+sm.getOutScanMode());
            sm.openScan();
            break;
        case R.id.closeScanner:
            sm.closeScan();
            break;
        case R.id.startDecode:
            sm.startScan();
            break;
        case R.id.stopDecode:
            sm.stopScan();
            break;
        case R.id.start_continue:
            sm.setScanLaserMode(4);
            break;
        case R.id.stop_continue:
            sm.setScanLaserMode(8);
            break;
        case R.id.close:
            finish();
            break;
        default:
            break;
        }
    }
    @Override
    protected void onPause() {
        // TODO Auto-generated method stub
        super.onPause();
        if(sm != null) {
            sm.stopScan();
        }
        unregisterReceiver(mScanReceiver);
    }
    @Override
    protected void onResume() {
        // TODO Auto-generated method stub
        super.onResume();
        IntentFilter filter = new IntentFilter();
        filter.addAction(SCAN_ACTION);
        registerReceiver(mScanReceiver, filter);
    }

    
}

EDIT: 2017-04-24

I found a Japanese blogger who wrote an article about creating simple broadcaster receiver using Delphi 10 Seattle and newer versions http://www.gesource.jp/weblog/?p=7269 . Which, I later discovered, was mentioned in one of @alitrun mentioned sources comments. So, to work with scanner in broadcast mode you need U8000S_ScanSDK.pas jar wrapper to access and control device. it's needed to set scanner app in broadcastmode. Then you need to create BroadcastReceiverListener class ...

Simple port of android app mentioned above is here:

unit Unit1;

interface

uses
   System.SysUtils, System.Types, System.UITypes, System.Classes,
   System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics,
   FMX.Dialogs,
   FMX.Controls.Presentation, FMX.Edit,

   Androidapi.JNIBridge, Androidapi.JNI.Embarcadero,
   Androidapi.JNI.GraphicsContentViewText,
   U8000S_ScanSDK; { U8000S_ScanSDK.jar  wrapper }

type
   TMyReceiver = class(TJavaLocal, JFMXBroadcastReceiverListener)
      public
         constructor Create;
         procedure onReceive(context: JContext; intent: JIntent); cdecl;
   end;

   TForm1 = class(TForm)
      Edit1: TEdit;
      procedure FormCreate(Sender: TObject);
      procedure FormDestroy(Sender: TObject);
      private
         { Private declarations }
         FMyListener: TMyReceiver;
         FBroadcastReceiver: JFMXBroadcastReceiver;

         ScanDevice: JScanDevice; { U8000S_ScanSDK.jar api device interface }
         procedure managebarcodescan(barcode: string);
      public
         { Public declarations }
   end;

var
   Form1: TForm1;

implementation

uses
   Androidapi.Helpers,
   Androidapi.JNI.JavaTypes;
{$R *.fmx}

function HexToString(H: String): String;
var
   I: Integer;
begin
Result := '';
for I := 1 to length(H) div 2 do
   Result := Result + Char(StrToInt('$' + Copy(H, (I - 1) * 2 + 1, 2)));
end;

constructor TMyReceiver.Create;
begin
inherited;
end;

procedure TMyReceiver.onReceive(context: JContext; intent: JIntent);
var
   barocode: tjavaarray<System.byte>;
   len: Integer;
   buffer: string;
   I: Integer;
begin
if JStringToString(intent.getAction) = 'scan.rcv.message' then
   begin
   len := intent.getIntExtra(StringToJString('length'), 0);
   barocode := intent.getByteArrayExtra(StringToJString('barocode'));
   for I := 0 to len - 1 do
      begin
      buffer := buffer + inttohex(barocode[I], 2);
      end;
   buffer := HexToString(buffer);
   barocode.Free;
   // do something with result
   Form1.managebarcodescan(buffer);
   end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
   Filter: JIntentFilter;
begin
FMyListener := TMyReceiver.Create;
FBroadcastReceiver := TJFMXBroadcastReceiver.JavaClass.init(FMyListener);
Filter := TJIntentFilter.JavaClass.init;
Filter.addAction(StringToJString('scan.rcv.message'));
TAndroidHelper.context.getApplicationContext.registerReceiver(FBroadcastReceiver, Filter);

ScanDevice := TJScanDevice.Create;   {creating scanner interface}
ScanDevice.setScanCodeEnterKey;
ScanDevice.setOutScanMode(0);     {setting scanner to broadcast mode }
ScanDevice.openScan;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
TAndroidHelper.context.getApplicationContext.unregisterReceiver(FBroadcastReceiver);

ScanDevice.stopScan;
ScanDevice._Release;
end;

procedure TForm1.managebarcodescan(barcode: string);
begin
Edit1.Text := barcode;
end;

end.

Solution

  • Here I posted a class to receive a simple broadcast from android send SMS func.

    If you need that another intent starts your intent - you need to attach a special JAR file to your project. Here is the manual how to do it (use google translate)
    Also check this(Launching activities and handling results in Delphi Android apps)