I have a TIdUDPServer on a form setup with a default port and broadcast mode enabled and cannot get it to receive anything under Android.
The same code is working fine under iOS but if I retarget Android I do not receive anything.
Is there a trick I am missing. I have checked the "User Permissions" and turn on everything I think might influence this but have had no luck with getting it working.
Thanks in advance, Martin
Is your device connected to WiFi or a mobile network? UDP does not work over mobile unless you initiate an outgoing UDP connection first to open up the mobile's carrier's firewall.
Are the UDP packets being sent directly to the device's IP or to a broadcast IP? By default, Android discards UDP packets that are not addressed directly to the device's IP. For multicast packets, WifiManager.createMulticastLock() must been called beforehand to allow such packets, and you need to include the CHANGE_WIFI_MULTICAST_STATE permission in the app's manifest. That might apply to UDP broadcasts as well, I am not sure.
Thank you Remy for putting me on the right track.
Here is the complete code to receive broadcast UDP packets...
unit Androidapi.JNI.WiFiManager;
interface
uses
Androidapi.JNIBridge, Androidapi.Jni, androidapi.JNI.JavaTypes, androidapi.JNI.Net,
androidapi.JNI.Os, FMX.Helpers.Android, Androidapi.JNI.GraphicsContentViewText, SysUtils;
Type
JWiFiManager = interface; // android/net/wifi/WifiManager
JMulticastLock = interface; // android/net/wifi/WifiManager$MulticastLock
JWiFiManagerClass = interface(JObjectClass)
['{F69F53AE-BC63-436A-8F69-57389B30CAA8}']
function getSystemService(Contex: JString): JWiFiManager; cdecl;
end;
[JavaSignature('android/net/wifi/WifiManager')]
JWiFiManager = interface(JObject)
['{382E85F2-6BF8-4255-BA3C-03C696AA6450}']
function createMulticastLock(tag: JString): JMulticastLock;
end;
TJWiFiManager = class(TJavaGenericImport<JWiFiManagerClass, JWiFiManager>) end;
JMulticastLockClass = interface(JObjectClass)
['{C0546633-3DF2-46B0-8E2C-C14411674A6F}']
end;
[JavaSignature('android/net/wifi/WifiManager$MulticastLock')]
JMulticastLock = interface(JObject)
['{CFA00D0C-097C-45E3-8B33-0E5A6C9FB9F1}']
procedure acquire();
function isHeld(): Boolean;
procedure release();
procedure setReferenceCounted(refCounted: boolean);
end;
TJMulticastLock = class(TJavaGenericImport<JMulticastLockClass, JMulticastLock>) end;
function GetWiFiManager: JWiFiManager;
implementation
function GetWiFiManager: JWiFiManager;
var
Obj: JObject;
begin
Obj := SharedActivityContext.getSystemService(TJContext.JavaClass.WIFI_SERVICE);
if not Assigned(Obj) then
raise Exception.Create('Could not locate Wifi Service');
Result := TJWiFiManager.Wrap((Obj as ILocalObject).GetObjectID);
if not Assigned(Result) then
raise Exception.Create('Could not access Wifi Manager');
end;
And then to acquire the necessary lock...
wifi_manager := GetWiFiManager;
multiCastLock := wifi_manager.createMulticastLock(StringToJString('LightFactory Remote'));
multiCastLock.setReferenceCounted(true);
multiCastLock.acquire;
Finally dont forget the permissions mentioned in Remy's post.
Related
I am dealing with the following issue with Firemonkey (Delphi 10.4): When the Android OS will shut down and my app is still running, it does not trigger the OnCloseQuery, OnClose, and nor the OnDestroy events. Is there a way to detect or intercept the OS shutdown event? The same issue is presented when I kill the app with the square button (that is when I show the recently opened apps with the square button and I close the app that way).
Thank you in advance.
I finally found a solution from a TMS customer (Ken Randall "Randall_Ken" Active Customer.)
uses FMX.Platform;
procedure TMyForm.FormCreate(Sender: TObject);
var
AppEventSvc: IFMXApplicationEventService;
begin
if TPlatformServices.Current.SupportsPlatformService
(IFMXApplicationEventService, IInterface(AppEventSvc)) then
begin
AppEventSvc.SetApplicationEventHandler(AppEvent);
end;
end;
function TMyForm.AppEvent(AAppEvent: TApplicationEvent;
AContext: TObject): Boolean;
begin
if AAppEvent = TApplicationEvent.WillTerminate then
begin
// Do soomething
end;
Result := true;
end;
I need to get information about outgoing and incoming calls on an Android phone using Delphi 10.3 Rio.
There's many info I want to get about the calls (it's duration, the number of the other phone, if the call was made from this phone or received, etc.), but for now I'm starting with the (I think) most basic information I can get and check for the call state (dialing, connected, disconnected and so on).
I know there's a function for that in Delphi but I haven't been able to make it work.
I've been following Delphi's tutorial on how to use the IFMXPhoneDialerService to do so, but it doesn't seem to work.
I reassigned IFMXPhoneDialerService's OnCallStateChanged event to a custom one but the custom one is never reached.
Here's how I've been doing it
interface
uses
FMX.Controls,
FMX.Controls.Presentation,
FMX.Dialogs,
FMX.Edit,
FMX.Forms,
FMX.PhoneDialer,
FMX.Platform,
FMX.StdCtrls,
FMX.Types,
System.Classes;
type
TQReportsDialerForm = class(TForm)
private
PhoneDialerService: IFMXPhoneDialerService;
procedure MyOnCallStateChanged(const ACallID: String;
const ACallState: TCallState);
public
constructor Create(AOwner: TComponent); override;
end;
var
QReportsDialerForm: TQReportsDialerForm;
implementation
{$R *.fmx}
{$R *.XLgXhdpiTb.fmx ANDROID}
{$R *.LgXhdpiPh.fmx ANDROID}
procedure TQReportsDialerForm.MyOnCallStateChanged(const ACallID: string;
const ACallState: TCallState);
var
outText: string;
begin
Log.d('Entered custom call state handler');
case ACallState of
TCallState.Connected:
outText := 'Connected';
TCallState.Dialing:
outText := 'Dialing';
TCallState.Disconnected:
outText := 'Disconnected';
TCallState.Incoming:
outText := 'Incoming';
TCallState.None:
outText := 'No calls';
end;
lblCallState.Text := outText;
end;
constructor TQReportsDialerForm.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
TPlatformServices.Current.SupportsPlatformService(IFMXPhoneDialerService,
IInterface(PhoneDialerService));
if Assigned(PhoneDialerService) then
begin
PhoneDialerService.OnCallStateChanged := MyOnCallStateChanged;
Log.d('PhoneDialerService correctly assigned.');
end
else
Log.d('Couldn''t assign PhoneDialerService.');
end;
end.
I know the dialer service is assigned because the code logs the PhoneDialerService correctly assigned. message, but when a call state changes it doesn't log the Entered custom call state handler message on MyOnCallStateChanged procedure.
As an additional info, something I found rather strange is that when I'm debugging the app, if I look at the type of OnCallStateChanged after assigning it, it shows as an "Erroneous type", so I think I may be doing something wrong there, but I haven't been able to see where the error is.
I'm using Delphi 10.3 Community Edition and want to use the WRITE_SETTINGS in my application to set the brightness.
I could get it managed to implement this procedure to call the settings dialog:
procedure RequestWriteSettings;
var
Intent: JIntent;
begin
Intent := TJIntent.JavaClass.init(TJSettings.JavaClass.ACTION_MANAGE_WRITE_SETTINGS);
TAndroidHelper.Activity.startActivity(Intent);
end;
I can call this procedure in my application, the dialog appears and I can set the necessary permissions.
But I don't want to call this procedure permanently, because that's not user friendly.
I need to check if the WRITE_SETTINGS permission is already set, but I don't know how to implement this in Delphi/Firemonkey.
What I could find is that one has to call the "Settings.System.canWrite(context)" function, but I only can find samples for java.
Calling these kind of java routines in Delphi isn't that easy. I'm searching around already for some weeks and tried "things on my own", but still without success.
Can someone provide the code line how this routine has to be called in Delphi?
Thanks so much in advance!
MPage
Example code for checking WRITE_SETTINGS:
uses
Androidapi.JNI.GraphicsContentViewText, Androidapi.JNI.Provider, Androidapi.JNI.Net, Androidapi.Helpers;
procedure TForm1.RequestWriteSettingsButtonClick(Sender: TObject);
begin
if not TJSettings_System.JavaClass.canWrite(TAndroidHelper.Context) then
StartWritePermissionsActivity
else
ShowMessage('System says app can write settings');
end;
procedure TForm1.StartWritePermissionsActivity;
var
LIntent: JIntent;
begin
LIntent := TJIntent.JavaClass.init(TJSettings.JavaClass.ACTION_MANAGE_WRITE_SETTINGS);
LIntent.setData(TJnet_Uri.JavaClass.parse(StringToJString('package:').concat(TAndroidHelper.Context.getPackageName)));
TAndroidHelper.Context.startActivity(LIntent);
end;
In the meanwhile I found a solution for myself, but I think Dave's is better. ;-)
That's what I found with the "trial and error" method:
function HasWriteSettings: Boolean;
begin
// Call canWrite to check for permission WRITE_SETTINGS
Result := TJSettings_System.JavaClass.canWrite(TAndroidHelper.Context.getApplicationContext);
end;
Does anyone know how to get IDs of devices connected to DataSnap Server?
I made an application which uses DataSnap Server and I want to limit the connection and identify the connected devices.
This might help
procedure TServerContainer1.DSServer1Connect(
DSConnectEventObject: TDSConnectEventObject);
var
UserName: String;
ClientInfo: TDBXClientInfo;
logmsg: String;
i: Integer;
begin
// Note: this event handler gets called directly AFTER authentication
UserName := TDSSessionManager.GetThreadSession.GetData('UserName');
ClientInfo := DSConnectEventObject.ChannelInfo.ClientInfo;
logmsg := Format('User %s connected via %s from IP address %s',
[UserName, ClientInfo.Protocol, ClientInfo.IpAddress]);
// ...
end;
On the TDSServer component, you can specify an OnConnect event. There is a record type called ‘TDBXClientInfo’ which you can get from the ‘TDBXChannelInfo’ stored in the TDSConnectEventObject of the OnConnect event. This record contains the IP Address.
Mat DeLong has lot of useful information about DataSnap:
https://mathewdelong.wordpress.com/2011/09/15/delphilive-2011-recap/
I need get telephone number of device on which my application runing. If has device two SIM cards ideal to get both numbers or if SIM card is not inserted (tablet device) can detect this.
I found some JAVA code but I have no idea how translate it to Delphi
TelephonyManager phneMgr = (TelephonyManager)mAppContext.getSystemService(Context.TELEPHONY_SERVICE);
String phneNmbr = phneMgr.getLine1Number();
I try write something but it not working ....
USES Androidapi.Helpers, Androidapi.JNI.JavaTypes, Androidapi.JNI.Telephony;
procedure TForm1.Button1Click(Sender: TObject);
var
num: JString;
tman: Androidapi.JNI.Telephony.JTelephonyManager;
begin
tman:=TJtelephonyManager.Create;
num := tman.getLine1Number;
edit1.Text:=Jstringtostring(num);
end;
Something like this should do it, based on experience with other system services. This translates what you have suggested is viable Java code.
I'll edit this to make it compile correctly (if there are any issues with it) when I have a copy of Delphi to hand later, but this is roughly what is required.
Note that quick look at the telephony manager documentation doesn't readily say how one would get the phone number for a second SIM, but it does translate what you were trying to translate.
uses
System.SysUtils,
Androidapi.Helpers,
Androidapi.JNI.GraphicsContentViewText,
Androidapi.JNI.JavaTypes,
Androidapi.JNI.Telephony;
function DeviceTelephoneNumber: string;
var
TelephonyManagerObj: JObject;
TelephonyManager: JTelephonyManager;
begin
TelephonyManagerObj:= TAndroidHelper.Context.getSystemService(
TJContext.JavaClass.TELEPHONY_SERVICE);
if TelephonyManagerObj <> nil then
begin
TelephonyManager := TJTelephonyManager.Wrap(TelephonyManagerObj);
if TelephonyManager <> nil then
Result := JStringToString(TelephonyManager.getLine1Number);
end;
end;
This code is also a possibility, which works in Android 5.1 and later.
function DeviceTelephoneNumbers: TArray<string>;
var
SubscriptionManager: JSubscriptionManager;
I, SubscriptionInfoCount: Integer;
SubscriptionInfoList: JList;
SubscriptionInfo: JSubscriptionInfo;
begin
// Subscription manager is only available in Android 5.1 and later
if TOSVersion.Check(5, 1) then
begin
SubscriptionManager := TJSubscriptionManager.JavaClass.from(
TAndroidHelper.Context);
SubscriptionInfoCount := SubscriptionManager.getActiveSubscriptionInfoCount;
SubscriptionInfoList := SubscriptionManager.getActiveSubscriptionInfoList;
SetLength(Result, SubscriptionInfoCount);
for I := 0 to Pred(SubscriptionInfoCount) do
begin
SubscriptionInfo := TJSubscriptionInfo.Wrap(SubscriptionInfoList.get(I));
if SubscriptionInfo <> nil then
Result[I] := JStringToString(SubscriptionInfo.getNumber);
end;
end
else
begin
// If running on older OS, use older API
SetLength(Result, SubscriptionInfoCount);
Result[0] := DeviceTelephoneNumber
end;
end;