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.