diff --git a/BCL.csproj b/BCL.csproj index a57dcae94..b14ea4517 100644 --- a/BCL.csproj +++ b/BCL.csproj @@ -205,6 +205,9 @@ local_settings.d.ts + + location.d.ts + diff --git a/Tests/location_tests.ts b/Tests/location_tests.ts index 619414fac..0b8c6cf7b 100644 --- a/Tests/location_tests.ts +++ b/Tests/location_tests.ts @@ -1,5 +1,6 @@ import TKUnit = require("Tests/TKUnit"); import locationModule = require("location/location"); +import types = require("location/location_types"); var LocationManager = locationModule.LocationManager; var Location = locationModule.Location; @@ -16,17 +17,19 @@ export var testLocation = function () { locationManager.startLocationMonitoring(function(location) { locationReceived = true; }, function(error) { - console.log('Location error received: ' + error); + //console.log('Location error received: ' + error); locationReceived = error; } - ); + ); var isReady = function () { return locationReceived; } - TKUnit.waitUntilReady(isReady, 3); + TKUnit.waitUntilReady(isReady, 10); + locationManager.stopLocationMonitoring(); + TKUnit.assert(true === locationReceived, locationReceived); }; @@ -46,4 +49,44 @@ export var testLastKnownLocation = function () { var lastKnownLocation = locationManager.lastKnownLocation; TKUnit.assert((lastKnownLocation != null), "There is no last known location"); }; - \ No newline at end of file + +function doOnce(options: locationModule.Options) { + var locationReceived; + locationModule.getLocation(options).then(function (location) { + locationReceived = true; + }).fail(function (error) { + //console.log('Location error received: ' + error); + locationReceived = error; + }); + + var isReady = function () { + return locationReceived; + } + + TKUnit.waitUntilReady(isReady, 10); + + TKUnit.assert(true === locationReceived, locationReceived); +} + +export var testLocationOnce = function () { + doOnce(undefined); +}; + +export var testLocationOnceTimeout0 = function () { + doOnce({timeout: 0}); +}; + +export var testLocationOnceMaximumAge = function () { + TKUnit.waitUntilReady(function () { return false; }, 2); + doOnce({ maximumAge: 3000, timeout: 0 }); // this should pass + try { + doOnce({ maximumAge: 1000, timeout: 0 }); + TKUnit.assert(false, "maximumAge check failed"); + } + catch (e) { + } +}; + +export var testLocationOnceTimeout1000 = function () { + doOnce({ timeout: 1000 }); +}; diff --git a/location/location.android.ts b/location/location.android.ts index f33682448..8ef51dcb5 100644 --- a/location/location.android.ts +++ b/location/location.android.ts @@ -1,9 +1,12 @@ import types = require("location/location_types"); import appModule = require("application/application"); +import common = require("location/location_common"); +import merger = require("utils/module_merge"); // merge the exports of the types module with the exports of this file declare var exports; -require("utils/module_merge").merge(types, exports); +merger.merge(types, exports); +merger.merge(common, exports); export class LocationManager { // in meters diff --git a/location/location.d.ts b/location/location.d.ts index 6b8d80eae..efdecd83f 100644 --- a/location/location.d.ts +++ b/location/location.d.ts @@ -1,4 +1,7 @@ -export declare enum Accuracy { + +import promises = require("promises/promises"); + +export declare enum Accuracy { // in meters ANY, HIGH, @@ -31,21 +34,31 @@ export declare class Location { public ios: any; // iOS CLLocation } -export declare class Options { +export interface Options { /** * Specifies desired accuracy in meters. Defaults to DesiredAccuracy.HIGH */ - desiredAccuracy: number; + 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; + updateDistance?: number; /** - * Minimum time interval between location updates, in milliseconds (android only) + * Minimum time interval between location updates, in milliseconds (ignored on iOS) */ - minimumUpdateTime: number; + minimumUpdateTime?: number; + + /** + * how old locations to receive in ms. + */ + maximumAge?: number; + + /** + * how long to wait for a location in ms. + */ + timeout?: number; } export declare class LocationManager { @@ -98,3 +111,5 @@ export declare class LocationManager { */ lastKnownLocation: Location; } + +export declare var getLocation: (options?: Options) => promises.Promise; diff --git a/location/location.ios.ts b/location/location.ios.ts index 5a25000ae..e233da525 100644 --- a/location/location.ios.ts +++ b/location/location.ios.ts @@ -1,8 +1,11 @@ import types = require("location/location_types"); +import common = require("location/location_common"); +import merger = require("utils/module_merge"); // merge the exports of the types module with the exports of this file declare var exports; -require("utils/module_merge").merge(types, exports); +merger.merge(types, exports); +merger.merge(common, exports); export class LocationManager { diff --git a/location/location_common.ts b/location/location_common.ts new file mode 100644 index 000000000..e7c560eb2 --- /dev/null +++ b/location/location_common.ts @@ -0,0 +1,70 @@ + +import types = require("location/location_types"); +import promises = require("promises/promises"); +import locationModule = require("location/location"); +import timer = require("timer/timer"); + +export var getLocation = function (options?: types.Options) : promises.Promise { + var d = promises.defer(); + + var timerId; + var locationManager = new locationModule.LocationManager(); + + if (options && (0 === options.timeout)) { + var location = locationManager.lastKnownLocation; + if (location) { + if (options && ("number" === typeof options.maximumAge)) { + if (location.timestamp.valueOf() + options.maximumAge > new Date().valueOf()) { + d.resolve(location); + } + else { + d.reject(new Error("timeout is 0 and last known location is older than maximumAge")); + } + } + else { + d.resolve(location); + } + } + else { + d.reject(new Error("timeout is 0 and no known location found")); + } + return d.promise(); + } + + locationManager.startLocationMonitoring(function (location: types.Location) { + if (options && ("number" === typeof options.maximumAge)) { + if (location.timestamp.valueOf() + options.maximumAge > new Date().valueOf()) { + locationManager.stopLocationMonitoring(); + if ("undefined" !== typeof timerId) { + timer.clearTimeout(timerId); + } + d.resolve(location); + } + } + else { + locationManager.stopLocationMonitoring(); + if ("undefined" !== typeof timerId) { + timer.clearTimeout(timerId); + } + d.resolve(location); + } + }, function (error: Error) { + console.error('Location error received: ' + error); + locationManager.stopLocationMonitoring(); + if ("undefined" !== typeof timerId) { + timer.clearTimeout(timerId); + } + d.reject(error); + }, + options + ); + + if (options && ("number" === typeof options.timeout)) { + timerId = timer.setTimeout(function () { + locationManager.stopLocationMonitoring(); + d.reject(new Error("timeout searching for location")); + }, options.timeout); + } + + return d.promise(); +} diff --git a/location/location_types.ts b/location/location_types.ts index 875ffc579..88f99585d 100644 --- a/location/location_types.ts +++ b/location/location_types.ts @@ -23,21 +23,31 @@ export class Location { public ios: any; // iOS native location } -export class Options { +export interface Options { /** * Specifies desired accuracy in meters. Defaults to DesiredAccuracy.HIGH */ - public desiredAccuracy: number; + 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; + updateDistance?: number; /** * Minimum time interval between location updates, in milliseconds (ignored on iOS) */ - public minimumUpdateTime: number; + minimumUpdateTime?: number; + + /** + * how old locations to receive in ms. + */ + maximumAge?: number; + + /** + * how long to wait for a location in ms. + */ + timeout?: number; } export class LocationRegion {