Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Write locations to file #81

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ BackgroundGeolocation.addWatcher(

// The minimum number of metres between subsequent locations. Defaults
// to 0.
distanceFilter: 50
distanceFilter: 50,

// A file URL to use to log the locations received, can be good for app crashes and debug
file: "file://path/to/file.txt"
},
function callback(location, error) {
if (error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import android.os.IBinder;
import android.provider.Settings;

import com.getcapacitor.JSObject;
import com.getcapacitor.Logger;
import com.getcapacitor.NativePlugin;
import com.getcapacitor.Plugin;
Expand Down Expand Up @@ -57,7 +56,7 @@ private void fetchLastLocation(PluginCall call) {
@Override
public void onSuccess(Location location) {
if (location != null) {
call.resolve(formatLocation(location));
call.resolve(BackgroundGeolocationService.formatLocation(location));
}
}
}
Expand Down Expand Up @@ -141,7 +140,8 @@ public void addWatcher(final PluginCall call) {
service.addWatcher(
call.getCallbackId(),
backgroundNotification,
call.getFloat("distanceFilter", 0f)
call.getFloat("distanceFilter", 0f),
call.getString("file", null)
);
}

Expand Down Expand Up @@ -212,28 +212,7 @@ private static Boolean isLocationEnabled(Context context)
}
}

private static JSObject formatLocation(Location location) {
JSObject obj = new JSObject();
obj.put("latitude", location.getLatitude());
obj.put("longitude", location.getLongitude());
// The docs state that all Location objects have an accuracy, but then why is there a
// hasAccuracy method? Better safe than sorry.
obj.put("accuracy", location.hasAccuracy() ? location.getAccuracy() : JSONObject.NULL);
obj.put("altitude", location.hasAltitude() ? location.getAltitude() : JSONObject.NULL);
if (Build.VERSION.SDK_INT >= 26 && location.hasVerticalAccuracy()) {
obj.put("altitudeAccuracy", location.getVerticalAccuracyMeters());
} else {
obj.put("altitudeAccuracy", JSONObject.NULL);
}
// In addition to mocking locations in development, Android allows the
// installation of apps which have the power to simulate location
// readings in other apps.
obj.put("simulated", location.isFromMockProvider());
obj.put("speed", location.hasSpeed() ? location.getSpeed() : JSONObject.NULL);
obj.put("bearing", location.hasBearing() ? location.getBearing() : JSONObject.NULL);
obj.put("time", location.getTime());
return obj;
}


// Sends messages to the service.
private BackgroundGeolocationService.LocalBinder service = null;
Expand All @@ -254,7 +233,7 @@ public void onReceive(Context context, Intent intent) {
}
return;
}
call.success(formatLocation(location));
call.success(BackgroundGeolocationService.formatLocation(location));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
import android.content.Intent;
import android.location.Location;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;

import com.getcapacitor.JSObject;
import com.getcapacitor.Logger;
import com.getcapacitor.android.BuildConfig;
import com.google.android.gms.location.FusedLocationProviderClient;
Expand All @@ -16,10 +18,17 @@
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.HashSet;

import androidx.localbroadcastmanager.content.LocalBroadcastManager;

import org.json.JSONObject;

// A bound and started service that is promoted to a foreground service when
// location updates have been requested and the main activity is stopped.
//
Expand Down Expand Up @@ -79,7 +88,8 @@ public class LocalBinder extends Binder {
void addWatcher(
final String id,
Notification backgroundNotification,
float distanceFilter
float distanceFilter,
String filePath
) {
FusedLocationProviderClient client = LocationServices.getFusedLocationProviderClient(
BackgroundGeolocationService.this
Expand All @@ -94,6 +104,7 @@ void addWatcher(
@Override
public void onLocationResult(LocationResult locationResult) {
Location location = locationResult.getLastLocation();
logLocationToFile(filePath, location);
Intent intent = new Intent(ACTION_BROADCAST);
intent.putExtra("location", location);
intent.putExtra("id", id);
Expand Down Expand Up @@ -170,4 +181,47 @@ void stopService() {
BackgroundGeolocationService.this.stopSelf();
}
}

void logLocationToFile(String filePath, Location location) {
if (filePath == null || filePath.length() == 0) {
return;
}
try {
File file = new File(filePath.replace("file://", ""));
if (!file.exists()) file.createNewFile();
FileOutputStream fileStream = new FileOutputStream(file, true);
OutputStreamWriter osw = new OutputStreamWriter(fileStream);
osw.write(formatLocation(location) + "\n");
osw.flush();
osw.close();
fileStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

public static JSObject formatLocation(Location location) {
JSObject obj = new JSObject();
obj.put("latitude", location.getLatitude());
obj.put("longitude", location.getLongitude());
// The docs state that all Location objects have an accuracy, but then why is there a
// hasAccuracy method? Better safe than sorry.
obj.put("accuracy", location.hasAccuracy() ? location.getAccuracy() : JSONObject.NULL);
obj.put("altitude", location.hasAltitude() ? location.getAltitude() : JSONObject.NULL);
if (Build.VERSION.SDK_INT >= 26 && location.hasVerticalAccuracy()) {
obj.put("altitudeAccuracy", location.getVerticalAccuracyMeters());
} else {
obj.put("altitudeAccuracy", JSONObject.NULL);
}
// In addition to mocking locations in development, Android allows the
// installation of apps which have the power to simulate location
// readings in other apps.
obj.put("simulated", location.isFromMockProvider());
obj.put("speed", location.hasSpeed() ? location.getSpeed() : JSONObject.NULL);
obj.put("bearing", location.hasBearing() ? location.getBearing() : JSONObject.NULL);
obj.put("time", location.getTime());
return obj;
}
}
70 changes: 70 additions & 0 deletions definitions.d.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,107 @@
/**
* The add watcher options
*/
export interface WatcherOptions {
/**
* The message to be displayed in the notification bar
*/
backgroundMessage?: string;
/**
* The title of the notification bar
*/
backgroundTitle?: string;
/**
* Whether or not to request permission if there is no permission
*/
requestPermissions?: boolean;
/**
* if "true", stale locations may be delivered while the device obtains a GPS fix.
* You are responsible for checking the "time"
*/
stale?: boolean;
/**
* The minimum number of metres between subsequent locations
*/
distanceFilter?: number;
/**
* A file URL to use to log the locations received, can be good for app crashes and debug
* @example file://path/to/file.txt
*/
file?: string;
}

/**
* The location object
*/
export interface Location {
/**
* Latitude in degrees.
*/
latitude: number;
/**
* Longitude in degrees.
*/
longitude: number;
/**
* Radius of horizontal uncertainty in metres, with 68% confidence.
*/
accuracy: number;
/**
* Metres above sea level (or null).
*/
altitude: number | null;
/**
* Vertical uncertainty in metres, with 68% confidence (or null).
*/
altitudeAccuracy: number | null;
/**
* True if the location was simulated by software, rather than GPS.
*/
simulated: boolean;
/**
* Deviation from true north in degrees (or null).
*/
bearing: number | null;
/**
* Speed in metres per second (or null).
*/
speed: number | null;
/**
* Time the location was produced, in milliseconds since the unix epoch.
*/
time: number | null;
}

export interface CallbackError extends Error {
code?: string;
}

/**
* The plugin's main interface
*/
export interface BackgroundGeolocationPlugin {
/**
* Add a watcher to the GPS according to the given options
* @param options the add watcher options
* @param callback the locaiton callback when a new location is received
* @returns a promise with the watcher ID in order to allow removing it
*/
addWatcher(
options: WatcherOptions,
callback: (
position?: Location,
error?: CallbackError
) => void
): Promise<string>;
/**
* Removes a watcher by ID
* @param options the remove watcher options
*/
removeWatcher(options: {
id: string
}): Promise<void>;
/**
* Opens the OS app settings screen to allow changing permissions
*/
openSettings(): Promise<void>;
}