Hi all,
I have created a new repository on GitHub to keep all interesting Delphi demo that I have figured out. Here the link to repository. This is also a way to share the code with you, so it can be improved. The first project pushed is LocationSensorPatch, relative to previous post. I will put all my demo ASAP.
Sunday, January 10, 2016
Saturday, January 2, 2016
Using LocationSensor on Android Service in Delphi Seattle
Hi all,
this is a very great news!
Me and my CEO (Daniele Teti, bit Time Professionals) have figured out how to implement and use the LocationSensor on Android Service.
Some months ago, I found out that there is a malfunction in Delphi: If you put LocationSensor component on Android Service DM, the service crashes. So I opened an issue on the quality portal, which was recognized and classified with majority prior.
However we need to retrieve the Device position from a background Service not by a foreground Activity.
We have investigated into the code and found this mistake in System.Android.Sensors unit:
The code above works fine if you are in Android Activity context, like our case. If you are in a service context though, this doesn't work (the DelphiActivity pointer is null, rightly). If there is not necessity to have a reference to the Activty, but only the context of Application, this code is to be preferred:
Below you can find the System.Sensors and System.Android.Sensors units (opportunely renamed System.SensorsDD and System.Android.SensorsDD), with the fixes explained earlier (the code contains comments for any changes):
System.SensorsDD:
System.Android.SensorsDD:
Now you are able to using LocationSensor in this way:
You can find a complete demo here. Hopefully this solution will be helpful to you the way it was for us.
P.S.
Stay tuned as because of this fix a very interesting application/project is about to see the light of day :)
this is a very great news!
Me and my CEO (Daniele Teti, bit Time Professionals) have figured out how to implement and use the LocationSensor on Android Service.
Some months ago, I found out that there is a malfunction in Delphi: If you put LocationSensor component on Android Service DM, the service crashes. So I opened an issue on the quality portal, which was recognized and classified with majority prior.
However we need to retrieve the Device position from a background Service not by a foreground Activity.
We have investigated into the code and found this mistake in System.Android.Sensors unit:
- For the majority of the code, it is assumed that the context of Application is an Activity
Here is the code:
FActivity := TJNativeActivity.Wrap(System.DelphiActivity)^.clazz);
The code above works fine if you are in Android Activity context, like our case. If you are in a service context though, this doesn't work (the DelphiActivity pointer is null, rightly). If there is not necessity to have a reference to the Activty, but only the context of Application, this code is to be preferred:
LContext := TJContextWrapper.Wrap(System.JavaContext);
Below you can find the System.Sensors and System.Android.Sensors units (opportunely renamed System.SensorsDD and System.Android.SensorsDD), with the fixes explained earlier (the code contains comments for any changes):
System.SensorsDD:
// about line 748
implementation
uses
System.Variants, System.Math, System.Character,
{$IFDEF ANDROID}
// -- patch dd
// include the modified System.Android.SensorsDD
System.Android.SensorsDD;
{$ENDIF ANDROID}
System.Android.SensorsDD:
...
//about line 12
uses
// -- patch dd
// use the modified System.SensorsDD
System.SensorsDD;
...
// about line 70
class constructor TPermission.Create;
var
PackageInfo: JPackageInfo;
PackageManager: JPackageManager;
Activity: JActivity;
LContext: JContext;
begin
// -- patch dd
// Activity := TJNativeActivity.Wrap
// (PANativeActivity(System.DelphiActivity)^.clazz)
LContext := TJContextWrapper.Wrap(System.JavaContext);
PackageManager := LContext.getPackageManager();
PackageInfo := PackageManager.getPackageInfo
(LContext.getApplicationContext.getPackageName,
TJPackageManager.JavaClass.GET_PERMISSIONS);
FPermissions := PackageInfo.requestedPermissions;
end;
...
// about line 100
type
TAndroidGeocoder = class(TGeocoder)
private type
TGeocoderRunnable = class(TJavaLocal, JRunnable)
private
FCoord: TLocationCoord2D;
FLGeocoder: JGeocoder;
public
constructor Create(ACoord: TLocationCoord2D; AGeocoder: JGeocoder);
procedure run; cdecl;
end;
private
class var
FGeocoder: JGeocoder;
// FActivity: JActivity; // -- patch dd
FActivity: JContextWrapper; // -- patch dd
...
// about line 130
TUIAndroidLocationSensor = class(TCustomLocationSensor)
private
FPermitted: Boolean;
// FActivity: JNativeActivity; // -- patch dd
FActivity: JContext; // -- patch dd
FLastValue: JLocation;
FLocationManager: JLocationManager;
FAccuracy: TLocationAccuracy;
FDistance: TLocationDistance;
...
// about line 1600
constructor TUIAndroidLocationSensor.Create(AManager: TSensorManager);
var
LocationService: JObject;
begin
inherited;
// FActivity := TJNativeActivity.Wrap
// (PANativeActivity(System.DelphiActivity)^.clazz); // -- patch dd
FActivity := TJContext.Wrap(System.JavaContext); // -- patch dd
LocationService := FActivity.getSystemService
(TJContext.JavaClass.LOCATION_SERVICE);
if Assigned(LocationService) then
FLocationManager := TJLocationManager.Wrap((LocationService as ILocalObject)
.GetObjectID);
end;
...
// about line 1630
function RunIfPossible(var ARunnable: TLocationRunnable;
var AListener: TLocationListener; AProviderName: JString): Boolean;
var
Provider: JLocationProvider;
LHandler: JHandler;
begin
Result := False;
if FLocationManager.isProviderEnabled(AProviderName) then
begin
if AListener = nil then
AListener := TLocationListener.Create(Self);
Provider := FLocationManager.getProvider(AProviderName);
if Provider <> nil then
begin
ARunnable := TLocationRunnable.Create(FLocationManager, AListener,
AProviderName);
// FActivity.runOnUiThread(ARunnable); // --patch dd
// -- patch dd
// You can use post method of Handler instead runOnUiThread in this case.
// more info here: http://developer.android.com/guide/topics/fundamentals/processes-and-threads.html
LHandler := TJHandler.JavaClass.init;
LHandler.post(ARunnable);
Result := True;
end;
end;
end;
...
// about line 1830
class constructor TAndroidGeocoder.Create;
begin
// -- patch dd
// FActivity := TJNativeActivity.Wrap
// (PANativeActivity(System.DelphiActivity)^.clazz);
FActivity := TJContextWrapper.Wrap(System.JavaContext);
FGeocoder := TJGeocoder.JavaClass.init(FActivity);
end;
Now you are able to using LocationSensor in this way:
uses
System.SensorsDD, System.Android.SensorsDD
...
...
var
FSensors: TSensorArray;
Sensor: TCustomSensor;
begin
TSensorManager.Current.Active := true;
FSensors := TSensorManager.Current.GetSensorsByCategory(TSensorCategory.Location);
FSensor := nil;
for Sensor in FSensors do
begin
if TCustomLocationSensor(Sensor).SensorType = TLocationSensorType.GPS then
begin
FSensor := TCustomLocationSensor(Sensor);
Break;
end;
end;
if not Assigned(FSensor) then
Exit; { no location sensor is available }
{ start the sensor if it is not started }
if not FSensor.Started then
FSensor.Start;
You can find a complete demo here. Hopefully this solution will be helpful to you the way it was for us.
P.S.
Stay tuned as because of this fix a very interesting application/project is about to see the light of day :)
Labels:
Android
,
delphi
,
delphi10
,
delphi10seattle
,
emvpfeed
,
How-to
,
Location
,
radstudio10seattle
,
Service
,
Tips
,
Tutorial
Subscribe to:
Posts
(
Atom
)