BCL: location updates according to API feedback

This commit is contained in:
Stanimir Karoserov
2014-04-28 18:59:50 +03:00
parent aa77546c80
commit bf9b564bf5
4 changed files with 124 additions and 67 deletions

View File

@ -1,9 +1,13 @@
import types = require("Location/location_types"); import types = require("Location/location_types");
import app_module = require("Application/application"); import app_module = require("Application/application");
// merge types
declare var exports;
exports.Location = types.Location;
exports.Accuracy = types.Accuracy;
export class LocationManager { export class LocationManager {
// in meters // in meters
// we might need some predefined values here like 'any' and 'high'
public desiredAccuracy: number; public desiredAccuracy: number;
// The minimum distance (measured in meters) a device must move horizontally before an update event is generated. // The minimum distance (measured in meters) a device must move horizontally before an update event is generated.
@ -15,7 +19,7 @@ export class LocationManager {
public isStarted: boolean; public isStarted: boolean;
private androidLocationManager: any; private androidLocationManager: any;
private _locationListener: any; private locationListener: any;
private static locationFromAndroidLocation(androidLocation: android.location.Location): types.Location { private static locationFromAndroidLocation(androidLocation: android.location.Location): types.Location {
var location = new types.Location(); var location = new types.Location();
@ -27,7 +31,7 @@ export class LocationManager {
location.speed = androidLocation.getSpeed(); location.speed = androidLocation.getSpeed();
location.direction = androidLocation.getBearing(); location.direction = androidLocation.getBearing();
location.timestamp = new Date(androidLocation.getTime()); location.timestamp = new Date(androidLocation.getTime());
location.androidNative = androidLocation; location.android = androidLocation;
//console.dump(location); //console.dump(location);
return location; return location;
} }
@ -53,42 +57,42 @@ export class LocationManager {
return androidLocation; return androidLocation;
} }
public static isLocationEnabled(): boolean { public static isEnabled(): boolean {
var criteria = new android.location.Criteria(); var criteria = new android.location.Criteria();
criteria.setAccuracy(1); // low ? fine ? who knows what 1 means (bug in android docs?) criteria.setAccuracy(1); // low ? fine ? who knows what 1 means (bug in android docs?)
var lm = app_module.Application.current.android.context.getSystemService('location'); var lm = app_module.Application.current.android.context.getSystemService(android.content.Context.LOCATION_SERVICE);
return (lm.getBestProvider(criteria, true) != null) ? true : false; return (lm.getBestProvider(criteria, true) != null) ? true : false;
} }
public static distanceInMeters(loc1: types.Location, loc2: types.Location): number { public static distance(loc1: types.Location, loc2: types.Location): number {
if (!loc1.androidNative) { if (!loc1.android) {
loc1.androidNative = LocationManager.androidLocationFromLocation(loc1); loc1.android = LocationManager.androidLocationFromLocation(loc1);
} }
if (!loc2.androidNative) { if (!loc2.android) {
loc2.androidNative = LocationManager.androidLocationFromLocation(loc2); loc2.android = LocationManager.androidLocationFromLocation(loc2);
} }
return loc1.androidNative.distanceTo(loc2.androidNative); return loc1.android.distanceTo(loc2.android);
} }
constructor() { constructor() {
// put some defaults // put some defaults
this.desiredAccuracy = types.DesiredAccuracy.HIGH; this.desiredAccuracy = types.Accuracy.HIGH;
this.updateDistance = 0; this.updateDistance = 0;
this.minimumUpdateTime = 200; this.minimumUpdateTime = 200;
this.isStarted = false; this.isStarted = false;
this.androidLocationManager = app_module.Application.current.android.context.getSystemService('location'); this.androidLocationManager = app_module.Application.current.android.context.getSystemService(android.content.Context.LOCATION_SERVICE);
} }
//////////////////////// ////////////////////////
// monitoring // monitoring
//////////////////////// ////////////////////////
public startLocationMonitoring(onLocation: (location: types.Location) => any, onError?: (error: Error) => any) { public startLocationMonitoring(onLocation: (location: types.Location) => any, onError?: (error: Error) => any, options?: types.Options) {
if (!this.isStarted) { if (!this.isStarted) {
var criteria = new android.location.Criteria(); var criteria = new android.location.Criteria();
criteria.setAccuracy((this.desiredAccuracy === types.DesiredAccuracy.HIGH) ? 1 : 2); criteria.setAccuracy((this.desiredAccuracy === types.Accuracy.HIGH) ? 1 : 2);
this._locationListener = <any>new android.location.LocationListener({ this.locationListener = <any>new android.location.LocationListener({
onLocationChanged: function (location: android.location.Location) { onLocationChanged: function (location: android.location.Location) {
if (this._onLocation) { if (this._onLocation) {
this._onLocation(LocationManager.locationFromAndroidLocation(location)); this._onLocation(LocationManager.locationFromAndroidLocation(location));
@ -104,10 +108,20 @@ export class LocationManager {
onStatusChanged: function (arg1: string, arg2: number, arg3: android.os.Bundle): void { onStatusChanged: function (arg1: string, arg2: number, arg3: android.os.Bundle): void {
} }
}); });
this._locationListener._onLocation = onLocation;
this._locationListener._onError = onError; if (options) {
if (options.desiredAccuracy)
this.desiredAccuracy = options.desiredAccuracy;
if (options.updateDistance)
this.updateDistance = options.updateDistance;
if (options.minimumUpdateTime)
this.minimumUpdateTime = options.minimumUpdateTime;
}
this.locationListener._onLocation = onLocation;
this.locationListener._onError = onError;
try { try {
this.androidLocationManager.requestLocationUpdates(long(this.minimumUpdateTime), float(this.updateDistance), criteria, this._locationListener, null); this.androidLocationManager.requestLocationUpdates(long(this.minimumUpdateTime), float(this.updateDistance), criteria, this.locationListener, null);
this.isStarted = true; this.isStarted = true;
} }
catch (e) { catch (e) {
@ -124,7 +138,7 @@ export class LocationManager {
public stopLocationMonitoring() { public stopLocationMonitoring() {
if (this.isStarted) { if (this.isStarted) {
this.androidLocationManager.removeUpdates(this._locationListener); this.androidLocationManager.removeUpdates(this.locationListener);
this.isStarted = false; this.isStarted = false;
} }
} }
@ -133,9 +147,9 @@ export class LocationManager {
// other // other
//////////////////////// ////////////////////////
public getLastKnownLocation(): types.Location { get lastKnownLocation(): types.Location {
var criteria = new android.location.Criteria(); var criteria = new android.location.Criteria();
criteria.setAccuracy((this.desiredAccuracy === types.DesiredAccuracy.HIGH) ? 1 : 2); criteria.setAccuracy((this.desiredAccuracy === types.Accuracy.HIGH) ? 1 : 2);
try { try {
var providers = this.androidLocationManager.getProviders(criteria, false); var providers = this.androidLocationManager.getProviders(criteria, false);
var it = providers.iterator(); var it = providers.iterator();

View File

@ -1,4 +1,4 @@
export declare enum DesiredAccuracy { export declare enum Accuracy {
// in meters // in meters
ANY, ANY,
HIGH, HIGH,
@ -26,26 +26,11 @@ export declare class Location {
timestamp: Date; timestamp: Date;
public androidNative: any; // android Location public android: any; // android Location
public iosNative: any; // iOS CLLocation public ios: any; // iOS CLLocation
} }
export declare class RegionChangeListener { export declare class Options {
onRegionEnter(region: LocationRegion);
onRegionExit(region: LocationRegion);
}
export declare class LocationManager {
/**
* Report are location services switched ON for this device (on Android) or application (iOS)
*/
static isLocationEnabled(): boolean;
/**
* Measure distance in meters between two locations
*/
static distanceInMeters(loc1: Location, loc2: Location): number;
/** /**
* Specifies desired accuracy in meters. Defaults to DesiredAccuracy.HIGH * Specifies desired accuracy in meters. Defaults to DesiredAccuracy.HIGH
*/ */
@ -60,6 +45,33 @@ export declare class LocationManager {
* Minimum time interval between location updates, in milliseconds (android only) * Minimum time interval between location updates, in milliseconds (android only)
*/ */
minimumUpdateTime: number; minimumUpdateTime: number;
}
export declare class LocationManager {
/**
* Report are location services switched ON for this device (on Android) or application (iOS)
*/
static isEnabled(): boolean;
/**
* Measure distance in meters between two locations
*/
static distance(loc1: Location, loc2: Location): number;
/**
* Specifies desired accuracy in meters. Defaults to DesiredAccuracy.HIGH
*/
desiredAccuracy: number;
/**
* Update distance filter in meters. Specifies how often to update. Default on iOS is no filter, on Android it is 0 meters
*/
updateDistance: number;
/**
* Minimum time interval between location updates, in milliseconds (ignored on iOS)
*/
minimumUpdateTime: number;
/** /**
* True if location listener is already started. In this case all other start requests will be ignored * True if location listener is already started. In this case all other start requests will be ignored
@ -71,7 +83,7 @@ export declare class LocationManager {
/** /**
* Starts location monitoring. * Starts location monitoring.
*/ */
startLocationMonitoring(onLocation: (location: Location) => any, onError?: (error: Error) => any); startLocationMonitoring(onLocation: (location: Location) => any, onError?: (error: Error) => any, options?: Options);
/** /**
* Stops location monitoring * Stops location monitoring
@ -83,5 +95,5 @@ export declare class LocationManager {
/** /**
* Returns last known location from device's location services or null of no known last location * Returns last known location from device's location services or null of no known last location
*/ */
getLastKnownLocation(): Location; lastKnownLocation: Location;
} }

View File

@ -1,4 +1,11 @@
import types = require("Location/location_types"); 
import types = require("Location/location_types");
// merge types
declare var exports;
exports.Location = types.Location;
exports.Accuracy = types.Accuracy;
exports.Options = types.Options;
export class LocationManager { export class LocationManager {
@ -24,7 +31,7 @@ export class LocationManager {
location.speed = clLocation.speed; location.speed = clLocation.speed;
location.direction = clLocation.course; location.direction = clLocation.course;
location.timestamp = new Date(clLocation.timestamp.timeIntervalSince1970() * 1000); location.timestamp = new Date(clLocation.timestamp.timeIntervalSince1970() * 1000);
location.iosNative = clLocation; location.ios = clLocation;
//console.dump(location); //console.dump(location);
return location; return location;
} }
@ -40,29 +47,34 @@ export class LocationManager {
return iosLocation; return iosLocation;
} }
public static isLocationEnabled(): boolean { public static isEnabled(): boolean {
return CoreLocation.CLLocationManager.locationServicesEnabled(); if (CoreLocation.CLLocationManager.locationServicesEnabled()) {
//return CoreLocation.CLLocationManager.authorizationStatus() === CoreLocation.CLAuthorizationStatus.kCLAuthorizationStatusAuthorized;
// FIXME: issue reported https://github.com/telerik/Kimera/issues/122
return true;
}
return false;
} }
public static distanceInMeters(loc1: types.Location, loc2: types.Location): number { public static distance(loc1: types.Location, loc2: types.Location): number {
if (!loc1.iosNative) { if (!loc1.ios) {
loc1.iosNative = LocationManager.iosLocationFromLocation(loc1); loc1.ios = LocationManager.iosLocationFromLocation(loc1);
} }
if (!loc2.iosNative) { if (!loc2.ios) {
loc2.iosNative = LocationManager.iosLocationFromLocation(loc2); loc2.ios = LocationManager.iosLocationFromLocation(loc2);
} }
return loc1.iosNative.distanceFromLocation(loc2.iosNative); return loc1.ios.distanceFromLocation(loc2.ios);
} }
constructor() { constructor() {
this.isStarted = false; this.isStarted = false;
this.desiredAccuracy = types.DesiredAccuracy.HIGH; this.desiredAccuracy = types.Accuracy.HIGH;
this.updateDistance = -1; // kCLDistanceFilterNone this.updateDistance = -1; // kCLDistanceFilterNone
this.iosLocationManager = new CoreLocation.CLLocationManager(); this.iosLocationManager = new CoreLocation.CLLocationManager();
} }
// monitoring // monitoring
public startLocationMonitoring(onLocation: (location: types.Location) => any, onError?: (error: Error) => any) { public startLocationMonitoring(onLocation: (location: types.Location) => any, onError?: (error: Error) => any, options?: types.Options) {
if (!this.isStarted) { if (!this.isStarted) {
var LocationListener = Foundation.NSObject.extends({ var LocationListener = Foundation.NSObject.extends({
setupWithFunctions: function (onLocation, onError) { setupWithFunctions: function (onLocation, onError) {
@ -91,12 +103,20 @@ export class LocationManager {
} }
}); });
if (options) {
if (options.desiredAccuracy)
this.desiredAccuracy = options.desiredAccuracy;
if (options.updateDistance)
this.updateDistance = options.updateDistance;
}
this.listener = new LocationListener(); this.listener = new LocationListener();
this.listener.setupWithFunctions(onLocation, onError); this.listener.setupWithFunctions(onLocation, onError);
this.iosLocationManager.delegate = this.listener; this.iosLocationManager.delegate = this.listener;
this.iosLocationManager.desiredAccuracy = this.desiredAccuracy; this.iosLocationManager.desiredAccuracy = this.desiredAccuracy;
this.iosLocationManager.distanceFilter = this.updateDistance; this.iosLocationManager.distanceFilter = this.updateDistance;
this.iosLocationManager.startUpdatingLocation(); this.iosLocationManager.startUpdatingLocation();
this.isStarted = true;
} }
else if (onError) { else if (onError) {
onError(new Error('location monitoring already started')); onError(new Error('location monitoring already started'));
@ -106,13 +126,15 @@ export class LocationManager {
public stopLocationMonitoring() { public stopLocationMonitoring() {
if (this.isStarted) { if (this.isStarted) {
this.iosLocationManager.stopUpdatingLocation(); this.iosLocationManager.stopUpdatingLocation();
this.iosLocationManager.delegate = null;
this.listener = null;
this.isStarted = false; this.isStarted = false;
} }
} }
// other // other
public getLastKnownLocation(): types.Location { get lastKnownLocation(): types.Location {
var clLocation = this.iosLocationManager.location; var clLocation = this.iosLocationManager.location;
if (null != clLocation) { if (null != clLocation) {
return LocationManager.locationFromCLLocation(clLocation); return LocationManager.locationFromCLLocation(clLocation);

View File

@ -1,4 +1,4 @@
export enum DesiredAccuracy { export enum Accuracy {
// in meters // in meters
ANY = 300, ANY = 300,
HIGH = 3, HIGH = 3,
@ -19,8 +19,25 @@ export class Location {
public timestamp: Date; public timestamp: Date;
public androidNative: any; // android Location public android: any; // android Location
public iosNative: any; // iOS native location public ios: any; // iOS native location
}
export declare class Options {
/**
* Specifies desired accuracy in meters. Defaults to DesiredAccuracy.HIGH
*/
public desiredAccuracy: number;
/**
* Update distance filter in meters. Specifies how often to update. Default on iOS is no filter, on Android it is 0 meters
*/
public updateDistance: number;
/**
* Minimum time interval between location updates, in milliseconds (ignored on iOS)
*/
public minimumUpdateTime: number;
} }
export class LocationRegion { export class LocationRegion {
@ -29,11 +46,3 @@ export class LocationRegion {
public raduis: number; // radius in meters public raduis: number; // radius in meters
} }
// TODO: This might be implemented with two callbacks, no need of special type.
export class RegionChangeListener {
onRegionEnter(region: LocationRegion) {
}
onRegionExit(region: LocationRegion) {
}
}