Location code worked in 10.4 crashes in 11.1

I have sunk many, many hours into trying to see where this prangs, with no error message in Delphi, and an android message that says the program keeps stopping. On the odd occasions when the breakpoints work, it shows the program going off with the fairies into the land of machine code.

It worked fine on Delphi 10.4 on a Huawei phone. It dies on Alexandria 11.1 on a Samsung.

I have not put the whole programme anywhere, but could send it if that proved useful.

Chester Wilson.

 GPS := tGPScgw.Create(SpeedoMainForm);
 GPS.Start(SpeedoMainForm, DataRate);

type
tGPScgw = class(tComponent)
constructor Create(AParent : tComponent); override;
destructor Destroy; override;
procedure Start(Form : tForm; RefreshTime : integer);
procedure Stop;

  private
	  LocationManagerService: JObject;
	  FLocationManager : JLocationManager;
	  LocationListener : TLocationListener;
	  function GetLat : double;
	  function GetLong : double;
	  function GetAlt : double;
	  function GetSpeed : single;

  public
	  property Latitude : double read GetLat;
	  property Longitude : double read GetLong;
	  property Altitude : double read GetAlt;
	  property Speed : single read GetSpeed;

end;

type
TLocationListener = class(TJavaLocal, JLocationListener)
private
[weak]
FParent : TForm;
public
constructor Create(AParent : TForm);
procedure onLocationChanged(location: JLocation); cdecl;
procedure onProviderDisabled(provider: JString); cdecl;
procedure onProviderEnabled(provider: JString); cdecl;
procedure onStatusChanged(provider: JString; status: Integer; extras: JBundle); cdecl;
private
fLatitude, fLongitude, fAltitude : double;
fSpeed : single;
end;

constructor tGPScgw.Create(AParent : tComponent);
begin
inherited Create(AParent);
FLocationManager := nil;
LocationListener := tLocationListener.Create(GPScgwForm);
end;

procedure tGPScgw.Start(Form : tForm; RefreshTime : integer);
begin
LocationManagerService := tAndroidHelper.Context.getSystemService(
TJContext.JavaClass.LOCATION_SERVICE);
FLocationManager := TJLocationManager.Wrap(
(LocationManagerService as ILocalObject).GetObjectID);

// IF I LEAVE THIS OUT, NO PRANGS (BUT NO UPDATES EITHER)
FLocationManager.requestLocationUpdates(
TJLocationManager.JavaClass.GPS_PROVIDER, RefreshTime, 0, LocationListener,
TJLooper.JavaClass.getMainLooper);
end;

Seems to be missing a bunch of code.

Your TLocationListener is also missing an overload for onLocationChanged(locations: JList). I know it’s not listed in JLocationListener in Androidapi.JNI.Location, but it needs to be in TLocationListener in order to work in Android 12 (see lines 113 and 1006 of System.Android.Sensors), which might be the problem if your device has Android 12.

Also, if the project was created in Delphi 10.4, please check that you have the correct libraries for Android - see: HowTo/Solutions/AndroidLibraries at main · DelphiWorlds/HowTo · GitHub

If you’re still having trouble after those changes, perhaps include more complete code

Thanks, Dave.
The phone is Android 12.
It took a LOT of searching to find that a right-click on “Libraries” gives the option of resetting to the current defaults, but that has been done.
Added the overload.

It still crashes. Oh what a nuisance (or words to that effect). Here is the whole lot.

The main form (SpeedoMainForm) has an image (InmageSpeed, taking up most of the screen), a Memo (Memo1, for debugging),
a LocationSensor (LocationSensor1) and a Timer (Timer1 - imaginative, I know!).
The GPScgw Form has nothing.
The PowerManager is a formless unit.

Here is the source code for the 3 sections of the programme: Main Unit, GPScgw, and PowerManager:

MAIN UNIT -------------------------------------------------------------------------

unit SpeedoMainUnit;

interface

uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
FMX.Platform.Android, FMX.Objects, FMX.StdCtrls, System.IOUtils,
System.Sensors, System.Sensors.Components, System.Math, PowerManager,
FMX.Controls.Presentation, FMX.Edit, FMX.Layouts, FMX.Memo, FMX.ScrollBox,
FMX.Memo.Types, System.Permissions, FMX.DialogService, gpsCGW;

type
TSpeedoMainForm = class(TForm)
ImageSpeed: TImage;
Timer1: TTimer;
LocationSensor1: TLocationSensor;
Memo1: TMemo;
function SpeedFmt : string;

 procedure FormCreate(Sender: TObject);
 procedure FormResize(Sender: TObject);
 procedure FormActivate(Sender: TObject);
 procedure FormClose(Sender: TObject; var Action: TCloseAction);
 procedure Timer1Timer(Sender: TObject);

private
{ Private declarations }
public
{ Public declarations }
end;

var
SpeedoMainForm: TSpeedoMainForm;
count : integer;

gSpeed: double;

HeightOfSpeedText, WidthOfSpeedText : integer;

Initialized : boolean;

tempint1, tempint2, tempint3, tempint4 : integer;
tempstr1, tempstr2, tempstr3, tempstr4 : string;

tempBool : boolean;

GPS : tGPScgw;

const DataRate = 500;

implementation

{$R *.fmx}

procedure TSpeedoMainForm.FormCreate(Sender: TObject);
begin
count := 0;

Canvas.Font.Family := ‘Tahoma’;
Canvas.Font.Size := 24; //96;
Canvas.Font.Style := [tFontStyle.fsBold];

Initialized := false;
end;

procedure TSpeedoMainForm.FormActivate(Sender: TObject);
var
PermissionAccessFineLocation : string;
begin
if Initialized then exit;
Initialized := true;

AcquireWakeLock; //Keep the screen awake.

ImageSpeed.Bitmap := tBitmap.Create(round(ImageSpeed.Width), round(ImageSpeed.Height));

Memo1.Lines.Clear;

PermissionAccessFineLocation := ‘android.permission.ACCESS_FINE_LOCATION’;

PermissionsService.RequestPermissions([PermissionAccessFineLocation],
procedure(const APermissions: TClassicStringDynArray;
const AGrantResults: TClassicPermissionStatusDynArray)
begin
if (Length(AGrantResults) = 1) and (AGrantResults[0] = TPermissionStatus.Granted) then
{ activate or deactivate the location sensor }
LocationSensor1.Active := true
else
begin
TDialogService.ShowMessage(‘Location permission not granted’);
end;
end);

LocationSensor1.Active := true;
LocationSensor1.Sensor.Start;

 GPS := tGPScgw.Create(SpeedoMainForm);
 GPS.Start(SpeedoMainForm, DataRate);

Timer1.Enabled := true;
end;

procedure TSpeedoMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
ReleaseWakeLock;
end;

procedure TSpeedoMainForm.Timer1Timer(Sender: TObject);
var
s : string;
begin
while Memo1.Lines.Count > 5 do Memo1.Lines.Delete(0);

gSpeed := GPS.Speed;
inc(count);
Memo1.Lines.Add(‘Speed: T (’ + IntToStr(count) + ‘):’ + FloatToStr(gSpeed));
Memo1.Repaint;

if gSpeed < 0 then exit;

 gSpeed := gSpeed * 3.6;   // to km/h

ImageSpeed.Bitmap.Canvas.Font.Family := ‘Tahoma’;
ImageSpeed.Bitmap.Canvas.Font.Style := [tFontStyle.fsBold];

ImageSpeed.Position.X := 0;
ImageSpeed.Position.Y := 0;

s := SpeedFmt;
ImageSpeed.Bitmap.Canvas.Font.Size := 300;
while ImageSpeed.Bitmap.Canvas.TextWidth(s) > ImageSpeed.Width do
ImageSpeed.Bitmap.Canvas.Font.Size := ImageSpeed.Bitmap.Canvas.Font.Size - 20;

ImageSpeed.Bitmap.Canvas.BeginScene;

ImageSpeed.Bitmap.Canvas.Fill.Color := (tAlphaColorRec.Yellow or $c0);
ImageSpeed.Bitmap.Canvas.Clear(TAlphaColorRec.Black);

ImageSpeed.Bitmap.Canvas.FillText(
tRectF.Create(0, 0, ImageSpeed.Width, ImageSpeed.Height),
s, false, 100, [TFillTextFlag.RightToLeft], TTextAlign.Center, TTextAlign.Center);

ImageSpeed.Bitmap.Canvas.EndScene;
end;

function tSpeedoMainForm.SpeedFmt : string;
begin
Result := ‘-’;
if isNan(gSpeed) then exit;

if gSpeed < 0.05 then Result := ‘0’
else
begin
if trunc(gSpeed) < 10 then
Result := Format(’%5.1f’, [gSpeed])
else
Result := Format(’%3.0f’, [gSpeed]);

  Result := trim(Result);
 end;

end;

procedure TSpeedoMainForm.FormResize(Sender: TObject);
var
s : string;
begin
ImageSpeed.Width := (ClientWidth);
ImageSpeed.Height := ClientHeight - Memo1.Height; // (but not always!)
ImageSpeed.Height := (ClientHeight);

 ImageSpeed.Bitmap.SetSize(round(ClientWidth), round(ClientHeight - Memo1.Height));
 ImageSpeed.Bitmap.SetSize(round(ClientWidth), round(ClientHeight));

 Memo1.Position.Y := ClientHeight - Memo1.Height;

if (ImageSpeed.Bitmap.HandleAllocated and Initialized) then //At the start there ain’t no handle, causing things to crash.
begin
gSpeed := GPS.Speed;
inc(count);
Memo1.Lines.Add(‘Speed: R (’ + IntToStr(count) + ‘):’ + FloatToStr(gSpeed));
gSpeed := gSpeed * 3.6; // to km/h

	s := SpeedFmt;

	ImageSpeed.Bitmap.Canvas.Font.Family := 'Tahoma';
	ImageSpeed.Bitmap.Canvas.Font.Style := [tFontStyle.fsBold];

	ImageSpeed.Bitmap.Canvas.Font.Size := 300;
	while ImageSpeed.Bitmap.Canvas.TextWidth(s) > ImageSpeed.Width do
     	ImageSpeed.Bitmap.Canvas.Font.Size := ImageSpeed.Bitmap.Canvas.Font.Size - 20;


	ImageSpeed.Bitmap.Canvas.BeginScene;

	ImageSpeed.Bitmap.Canvas.Fill.Color :=  (tAlphaColorRec.Yellow or $c0);
	ImageSpeed.Bitmap.Canvas.Clear(TAlphaColorRec.Black);

	ImageSpeed.Bitmap.Canvas.FillText(tRectF.Create(0, 0, ImageSpeed.Width, ImageSpeed.Height),
  		s, false, 100, [TFillTextFlag.RightToLeft], TTextAlign.Center, TTextAlign.Center);

	ImageSpeed.Bitmap.Canvas.EndScene;
end;

end;

end.

GPSCGW --------------------------------------------------------------------------------

unit GPScgw;

interface

uses
System.SysUtils, System.Types, System.UITypes, System.Classes,
{System.Variants, FMX.Types, FMX.Controls,}
FMX.Forms,
{FMX.Graphics, FMX.Dialogs,}
Androidapi.JNI.Location, Androidapi.JNIBridge, Androidapi.JNI.JavaTypes,
Androidapi.JNI.Os, {FMX.Layouts, FMX.ListBox, FMX.StdCtrls,}
FMX.Helpers.Android, AndroidAPI.Looper,
Androidapi.Helpers, Androidapi.JNI.GraphicsContentViewText,
System.Math;

type
TGPScgwForm = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;

type
TLocationListener = class(TJavaLocal, JLocationListener)
private
[weak]
FParent : TForm;
public
constructor Create(AParent : TForm);
procedure onLocationChanged(location: JLocation); overload; cdecl;
procedure onProviderDisabled(provider: JString); cdecl;
procedure onProviderEnabled(provider: JString); cdecl;
procedure onStatusChanged(provider: JString; status: Integer; extras: JBundle); cdecl;
private
fLatitude, fLongitude, fAltitude : double;
fSpeed : single;
end;

tGPScgw = class(tComponent)
constructor Create(AParent : tComponent); override;
destructor Destroy; override;
procedure Start(Form : tForm; RefreshTime : integer);
procedure Stop;

  private
	  LocationManagerService: JObject;
	  FLocationManager : JLocationManager;
	  LocationListener : TLocationListener;
	  function GetLat : double;
	  function GetLong : double;
	  function GetAlt : double;
	  function GetSpeed : single;

  public
	  property Latitude : double read GetLat;
	  property Longitude : double read GetLong;
	  property Altitude : double read GetAlt;
	  property Speed : single read GetSpeed;

end;

var
GPScgwForm : tGPScgwForm;

 tempint1, tempint2, tempint3, tempint4 : integer;
 tempbool : boolean;

implementation

{$R *.fmx}

{ tGPScgw }

constructor tGPScgw.Create(AParent : tComponent);
begin
inherited Create(AParent);
FLocationManager := nil;
LocationListener := tLocationListener.Create(GPScgwForm);
end;

destructor tGPScgw.Destroy;
begin
Stop;
inherited Destroy;
end;

procedure tGPScgw.Start(Form : tForm; RefreshTime : integer);
begin
LocationManagerService := tAndroidHelper.Context.getSystemService(
TJContext.JavaClass.LOCATION_SERVICE);
FLocationManager := TJLocationManager.Wrap(
(LocationManagerService as ILocalObject).GetObjectID);

 FLocationManager.requestLocationUpdates(
		TJLocationManager.JavaClass.GPS_PROVIDER, RefreshTime, 0, LocationListener,
		TJLooper.JavaClass.getMainLooper);

end;

procedure tGPScgw.Stop;
begin
if Assigned(fLocationManager) then
FLocationManager.removeUpdates(LocationListener);
end;

function tGPScgw.GetLat : double;
begin
Result := LocationListener.fLatitude;
end;

function tGPScgw.GetLong : double;
begin
Result := LocationListener.fLongitude;
end;

function tGPScgw.GetAlt : double;
begin
Result := LocationListener.fAltitude;
end;

function tGPScgw.GetSpeed : single;
begin
// Result := SpeedoMainForm.LocationSensor1.Sensor.Speed; FORGET IT! LocationSensors are hopeless.

Result := LocationListener.fSpeed;
if IsNan(Result) then Result := -1;
end;

{ TLocationListener }

constructor TLocationListener.Create(AParent: TForm);
begin
inherited Create;
FParent := AParent;
end;

procedure TLocationListener.onLocationChanged(location: JLocation);
begin
fLatitude := Location.getLatitude;
fLongitude := Location.getLongitude;
fAltitude := Location.getAltitude;
fSpeed := Location.getSpeed;
end;

procedure TLocationListener.onProviderDisabled(provider: JString);
begin

end;

procedure TLocationListener.onProviderEnabled(provider: JString);
begin

end;

procedure TLocationListener.onStatusChanged(provider: JString; status: Integer;
extras: JBundle);
begin

end;

end.

POWERMANAGER ------------------------------------------------------------------

unit PowerManager;

interface

function AcquireWakeLock : Boolean;
procedure ReleaseWakeLock;

implementation

uses
System.SysUtils,
Androidapi.JNI,
Androidapi.JNIBridge,
Androidapi.JNI.GraphicsContentViewText,
Androidapi.JNI.JavaTypes,
FMX.Helpers.Android,
Androidapi.Helpers;

type
JPowerManager = interface;
JWakeLock = interface;

JWakeLockClass = interface(JObjectClass)
[’{918E171F-CDB8-4464-9507-F49272CE7636}’]
end;

[JavaSignature(‘android/os/PowerManager$WakeLock’)]
JWakeLock = interface(JObject)
[’{D17B1136-FA15-4AEB-85B1-2D490F0FD320}’]
{Methods}
procedure acquire; cdecl;
procedure release; cdecl;
function isHeld: Boolean; cdecl;
end;
TJWakeLock = class(TJavaGenericImport<JWakeLockClass, JWakeLock>) end;

JPowerManagerClass = interface(JObjectClass)
[’{7D0696A2-ADEA-4158-AE1F-5E720DEDBCF9}’]
{Property methods}
function _GetFULL_WAKE_LOCK: Integer; cdecl;
function _GetSCREEN_BRIGHT_WAKE_LOCK: Integer; cdecl;
function _GetSCREEN_DIM_WAKE_LOCK: Integer; cdecl;
function _GetPARTIAL_WAKE_LOCK: Integer; cdecl;
{Properties}
//Keep screen on bright & keyboard on
//Deprecated in API level 17 - Jelly Bean MR1
property FULL_WAKE_LOCK: Integer read _GetFULL_WAKE_LOCK;
//Keep screen on bright
//Deprecated in API level 13 - Honeycomb MR2
property SCREEN_BRIGHT_WAKE_LOCK: Integer read _GetSCREEN_BRIGHT_WAKE_LOCK;
//Keep screen on dim
//Deprecated in API level 17 - Jelly Bean MR1
property SCREEN_DIM_WAKE_LOCK: Integer read _GetSCREEN_DIM_WAKE_LOCK;
//Keep CPU running, screen & keyboard can go off
property PARTIAL_WAKE_LOCK: Integer read _GetPARTIAL_WAKE_LOCK;
end;

[JavaSignature(‘android/os/PowerManager’)]
JPowerManager = interface(JObject)
[’{DEAED658-4353-4D17-B0A3-8179E48BE87F}’]
{Methods}
function newWakeLock(levelAndFlags: Integer; tag: JString): JWakeLock; cdecl;
end;
TJPowerManager = class(TJavaGenericImport<JPowerManagerClass, JPowerManager>) end;

function GetPowerManager: JPowerManager;
var
PowerServiceNative: JObject;
begin
// PowerServiceNative := SharedActivityContext.getSystemService( deprecated
PowerServiceNative := tAndroidHelper.Context.getSystemService(
TJContext.JavaClass.POWER_SERVICE);
if not Assigned(PowerServiceNative) then
raise Exception.Create(‘Could not locate Power Service’);
Result := TJPowerManager.Wrap(
(PowerServiceNative as ILocalObject).GetObjectID);
if not Assigned(Result) then
raise Exception.Create(‘Could not access Power Manager’);
end;

var
WakeLock: JWakeLock = nil;

function AcquireWakeLock: Boolean;
var
PowerManager: JPowerManager;
begin
Result := Assigned(WakeLock);
if not Result then
begin
PowerManager := GetPowerManager;
WakeLock := PowerManager.newWakeLock(
TJPowerManager.JavaClass.SCREEN_BRIGHT_WAKE_LOCK,
StringToJString(‘Delphi’));
Result := Assigned(WakeLock);
end;
if Result then
begin
if not WakeLock.IsHeld then
begin
WakeLock.acquire;
Result := WakeLock.isHeld
end;
end;
end;

procedure ReleaseWakeLock;
begin
if Assigned(WakeLock) then
begin
WakeLock.release;
WakeLock := nil
end;
end;

end.

Are you unable to zip a test project and attach it here?

1 Like

Thank you, Dave - I had to get home to be able to do it.
Hope I have put all the necessary files into it.
Chester.

Speedo.zip (14.6 KB)

Thanks, Chester.

There are 2 main problems here, one of which I was not expecting, and that is it appears that calling PermissionsService.RequestPermissions during the FormActivate method causes the handler never to be invoked. It works if you call it in FormCreate.

The second problem is that you’ve still not added the onLocationChanged(locations: JList) overload mentioned earlier. I’m attaching the updated GPScgw unit with comments (prefixed with DPN) added for the parts I changed.

GPScgw.pas (4.3 KB)

For the first problem, I’ll leave that as an exercise for you, however you should move all the code after the call to PermissionsService.RequestPermissions to inside the permissions handler, since it should be executed only if permissions were granted

Dave, that worked a treat, in fact better than a treat, as I will explain.
Sorry - I did not understand that it was not just the “overload” directive required for onLocationChanged, it was a complete declaration with a different argument.

You will see the changes to the permission code. Now it works on FormActivate as well (why, I know not, but I can put breakpoints in and use them too!)

The interesting thing is that now, if I do these things in order, I can get All The Time access to Location, and the programme only asks me once.

  1. Instal the programme
  2. Go via Settings to give it Location access (When In Use, at this stage)
  3. Run the programme. It will take you back to the Location settings but will give you the 4 options, with the top one being the All The Time one.
  4. Get out of that, and you will have background location access whenever you run the programme. Without being asked!

Yippee!!
Speedo.zip (15.4 KB)

Good to hear it’s all working!