From 91521fa106453a5095e2e7470056a3107fcf148e Mon Sep 17 00:00:00 2001 From: Fabian Guerra Date: Thu, 2 Nov 2017 19:22:14 -0400 Subject: [PATCH 001/264] [ios] Fix image property of MGLAnnotationImage does not update. --- platform/ios/src/MGLMapView.mm | 5 ----- 1 file changed, 5 deletions(-) diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 31eac056e..1cbbc2287 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -4548,11 +4548,6 @@ public: NSString *fallbackReuseIdentifier = MGLDefaultStyleMarkerSymbolName; NSString *fallbackIconIdentifier = [MGLAnnotationSpritePrefix stringByAppendingString:fallbackReuseIdentifier]; - // Remove the old icon from the style. - if ( ! [iconIdentifier isEqualToString:fallbackIconIdentifier]) { - _mbglMap->removeAnnotationImage(iconIdentifier.UTF8String); - } - if (annotationImage.image) { // Add the new icon to the style. From b38618b450a8153d0b93f9a44b78039181f6c3f9 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 26 Jan 2018 18:15:27 +0100 Subject: [PATCH 002/264] [windows] - run AppVeyor for all branches --- appveyor.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 8013c669f..d31160261 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,10 +7,6 @@ environment: shallow_clone: true -branches: - only: - - master - cache: - '%APPVEYOR_BUILD_FOLDER%\mason_packages' - '%APPVEYOR_BUILD_FOLDER%\LLVM-5.0.1-win64.exe' From 65bab30384577c9aac88fb7b34f8877a95d86704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Wed, 24 Jan 2018 00:28:06 -0800 Subject: [PATCH 003/264] [ios, macos] Documented ranges of style properties --- .../darwin/scripts/generate-style-code.js | 26 +++++++-- platform/darwin/src/MGLBackgroundStyleLayer.h | 4 +- platform/darwin/src/MGLCircleStyleLayer.h | 18 +++--- .../darwin/src/MGLFillExtrusionStyleLayer.h | 12 ++-- platform/darwin/src/MGLFillStyleLayer.h | 4 +- platform/darwin/src/MGLHillshadeStyleLayer.h | 8 +-- platform/darwin/src/MGLLight.h | 4 +- platform/darwin/src/MGLLineStyleLayer.h | 24 ++++---- platform/darwin/src/MGLRasterStyleLayer.h | 26 ++++----- platform/darwin/src/MGLSymbolStyleLayer.h | 58 +++++++++---------- 10 files changed, 99 insertions(+), 85 deletions(-) diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.js index a7804ac94..f919a649c 100755 --- a/platform/darwin/scripts/generate-style-code.js +++ b/platform/darwin/scripts/generate-style-code.js @@ -300,7 +300,17 @@ global.propertyDoc = function (propertyName, property, layerType, kind) { doc += `\n\nThis attribute corresponds to the ${property.original} layout property in the Mapbox Style Specification.`; } doc += '\n\nYou can set this property to an expression containing any of the following:\n\n'; - doc += `* Constant ${describeType(property)} values\n`; + doc += `* Constant ${describeType(property)} values`; + if ('minimum' in property) { + if ('maximum' in property) { + doc += ` between ${formatNumber(property.minimum)} and ${formatNumber(property.maximum)} inclusive`; + } else { + doc += ` no less than ${formatNumber(property.minimum)}`; + } + } else if ('maximum' in property) { + doc += ` no greater than ${formatNumber(property.maximum)}`; + } + doc += '\n'; if (property.type === 'enum') { doc += '* Any of the following constant string values:\n'; doc += Object.keys(property.values).map(value => ' * `' + value + '`: ' + property.values[value].doc).join('\n') + '\n'; @@ -392,7 +402,7 @@ global.describeValue = function (value, property, layerType) { case 'boolean': return value ? '`YES`' : '`NO`'; case 'number': - return 'the float `' + value + '`'; + return 'the float ' + formatNumber(value); case 'string': if (value === '') { return 'the empty string'; @@ -427,7 +437,7 @@ global.describeValue = function (value, property, layerType) { if (color.r === 1 && color.g === 1 && color.b === 1 && color.a === 1) { return '`UIColor.whiteColor`'; } - return 'a `UIColor`' + ` object whose RGB value is ${color.r}, ${color.g}, ${color.b} and whose alpha value is ${color.a}`; + return 'a `UIColor`' + ` object whose RGB value is ${formatNumber(color.r)}, ${formatNumber(color.g)}, ${formatNumber(color.b)} and whose alpha value is ${formatNumber(color.a)}`; case 'array': let units = property.units || ''; if (units) { @@ -438,12 +448,12 @@ global.describeValue = function (value, property, layerType) { if (value[0] === 0 && value[1] === 0 && value[2] === 0 && value[3] === 0) { return 'an `NSValue` object containing `UIEdgeInsetsZero`'; } - return 'an `NSValue` object containing a `UIEdgeInsets` struct set to' + ` ${value[0]}${units} on the top, ${value[3]}${units} on the left, ${value[2]}${units} on the bottom, and ${value[1]}${units} on the right`; + return 'an `NSValue` object containing a `UIEdgeInsets` struct set to' + ` ${formatNumber(value[0])}${units} on the top, ${formatNumber(value[3])}${units} on the left, ${formatNumber(value[2])}${units} on the bottom, and ${formatNumber(value[1])}${units} on the right`; case 'offset': case 'translate': - return 'an `NSValue` object containing a `CGVector` struct set to' + ` ${value[0]}${units} rightward and ${value[1]}${units} downward`; + return 'an `NSValue` object containing a `CGVector` struct set to' + ` ${formatNumber(value[0])}${units} rightward and ${formatNumber(value[1])}${units} downward`; case 'position': - return 'an `MGLSphericalPosition` struct set to' + ` ${value[0]} radial, ${value[1]} azimuthal and ${value[2]} polar`; + return 'an `MGLSphericalPosition` struct set to' + ` ${formatNumber(value[0])} radial, ${formatNumber(value[1])} azimuthal and ${formatNumber(value[2])} polar`; default: return 'the array `' + value.join('`, `') + '`'; } @@ -452,6 +462,10 @@ global.describeValue = function (value, property, layerType) { } }; +global.formatNumber = function (num) { + return num.toLocaleString().replace('-', '\u2212'); +} + global.propertyDefault = function (property, layerType) { if (property.name === 'heatmap-color') { return 'an expression that evaluates to a rainbow color scale from blue to red'; diff --git a/platform/darwin/src/MGLBackgroundStyleLayer.h b/platform/darwin/src/MGLBackgroundStyleLayer.h index 9d2673c85..4bb8a36e0 100644 --- a/platform/darwin/src/MGLBackgroundStyleLayer.h +++ b/platform/darwin/src/MGLBackgroundStyleLayer.h @@ -71,11 +71,11 @@ which it is added. The opacity at which the background will be drawn. The default value of this property is an expression that evaluates to the float - `1`. Set this property to `nil` to reset it to the default value. + 1. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values between 0 and 1 inclusive * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables diff --git a/platform/darwin/src/MGLCircleStyleLayer.h b/platform/darwin/src/MGLCircleStyleLayer.h index caa6d2f6c..252363717 100644 --- a/platform/darwin/src/MGLCircleStyleLayer.h +++ b/platform/darwin/src/MGLCircleStyleLayer.h @@ -116,7 +116,7 @@ MGL_EXPORT full opacity. The default value of this property is an expression that evaluates to the float - `0`. Set this property to `nil` to reset it to the default value. + 0. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -165,11 +165,11 @@ MGL_EXPORT The opacity at which the circle will be drawn. The default value of this property is an expression that evaluates to the float - `1`. Set this property to `nil` to reset it to the default value. + 1. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values between 0 and 1 inclusive * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -214,11 +214,11 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - `5`. Set this property to `nil` to reset it to the default value. + 5. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values no less than 0 * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -293,11 +293,11 @@ MGL_EXPORT The opacity of the circle's stroke. The default value of this property is an expression that evaluates to the float - `1`. Set this property to `nil` to reset it to the default value. + 1. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values between 0 and 1 inclusive * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -320,11 +320,11 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - `0`. Set this property to `nil` to reset it to the default value. + 0. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values no less than 0 * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables diff --git a/platform/darwin/src/MGLFillExtrusionStyleLayer.h b/platform/darwin/src/MGLFillExtrusionStyleLayer.h index d1d5af6ba..2eb317467 100644 --- a/platform/darwin/src/MGLFillExtrusionStyleLayer.h +++ b/platform/darwin/src/MGLFillExtrusionStyleLayer.h @@ -77,14 +77,14 @@ MGL_EXPORT This property is measured in meters. The default value of this property is an expression that evaluates to the float - `0`. Set this property to `nil` to reset it to the default value. + 0. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `fillExtrusionHeight` is non-`nil`. Otherwise, it is ignored. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values no less than 0 * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -137,11 +137,11 @@ MGL_EXPORT This property is measured in meters. The default value of this property is an expression that evaluates to the float - `0`. Set this property to `nil` to reset it to the default value. + 0. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values no less than 0 * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -162,11 +162,11 @@ MGL_EXPORT per-layer, not per-feature, basis, and data-driven styling is not available. The default value of this property is an expression that evaluates to the float - `1`. Set this property to `nil` to reset it to the default value. + 1. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values between 0 and 1 inclusive * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables diff --git a/platform/darwin/src/MGLFillStyleLayer.h b/platform/darwin/src/MGLFillStyleLayer.h index 5caab91b4..421813bbc 100644 --- a/platform/darwin/src/MGLFillStyleLayer.h +++ b/platform/darwin/src/MGLFillStyleLayer.h @@ -127,11 +127,11 @@ MGL_EXPORT value will also affect the 1pt stroke around the fill, if the stroke is used. The default value of this property is an expression that evaluates to the float - `1`. Set this property to `nil` to reset it to the default value. + 1. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values between 0 and 1 inclusive * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables diff --git a/platform/darwin/src/MGLHillshadeStyleLayer.h b/platform/darwin/src/MGLHillshadeStyleLayer.h index b2eeb59ab..bc831c7f0 100644 --- a/platform/darwin/src/MGLHillshadeStyleLayer.h +++ b/platform/darwin/src/MGLHillshadeStyleLayer.h @@ -107,11 +107,11 @@ MGL_EXPORT Intensity of the hillshade The default value of this property is an expression that evaluates to the float - `0.5`. Set this property to `nil` to reset it to the default value. + 0.5. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values between 0 and 1 inclusive * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -187,11 +187,11 @@ MGL_EXPORT `hillshadeIlluminationAnchor` is set to `MGLHillshadeIlluminationAnchorMap`. The default value of this property is an expression that evaluates to the float - `335`. Set this property to `nil` to reset it to the default value. + 335. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values between 0 and 359 inclusive * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables diff --git a/platform/darwin/src/MGLLight.h b/platform/darwin/src/MGLLight.h index d7e64cbfa..2bdb874a3 100644 --- a/platform/darwin/src/MGLLight.h +++ b/platform/darwin/src/MGLLight.h @@ -163,11 +163,11 @@ MGL_EXPORT more extreme contrast. The default value of this property is an expression that evaluates to the float - `0.5`. + 0.5. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values between 0 and 1 inclusive * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables diff --git a/platform/darwin/src/MGLLineStyleLayer.h b/platform/darwin/src/MGLLineStyleLayer.h index 003f48ae4..bdb5464eb 100644 --- a/platform/darwin/src/MGLLineStyleLayer.h +++ b/platform/darwin/src/MGLLineStyleLayer.h @@ -179,7 +179,7 @@ MGL_EXPORT Used to automatically convert miter joins to bevel joins for sharp angles. The default value of this property is an expression that evaluates to the float - `2`. Set this property to `nil` to reset it to the default value. + 2. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `lineJoin` is set to an expression that evaluates to `miter`. Otherwise, it is ignored. @@ -201,7 +201,7 @@ MGL_EXPORT Used to automatically convert round joins to miter joins for shallow angles. The default value of this property is an expression that evaluates to the float - `1.05`. Set this property to `nil` to reset it to the default value. + 1.05. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `lineJoin` is set to an expression that evaluates to `round`. Otherwise, it is ignored. @@ -227,11 +227,11 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - `0`. Set this property to `nil` to reset it to the default value. + 0. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values no less than 0 * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -291,7 +291,7 @@ MGL_EXPORT You can set this property to an expression containing any of the following: - * Constant array values + * Constant array values no less than 0 * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -319,11 +319,11 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - `0`. Set this property to `nil` to reset it to the default value. + 0. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values no less than 0 * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -348,7 +348,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - `0`. Set this property to `nil` to reset it to the default value. + 0. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -372,11 +372,11 @@ MGL_EXPORT The opacity at which the line will be drawn. The default value of this property is an expression that evaluates to the float - `1`. Set this property to `nil` to reset it to the default value. + 1. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values between 0 and 1 inclusive * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -518,11 +518,11 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - `1`. Set this property to `nil` to reset it to the default value. + 1. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values no less than 0 * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables diff --git a/platform/darwin/src/MGLRasterStyleLayer.h b/platform/darwin/src/MGLRasterStyleLayer.h index 7773246ca..fd41f271e 100644 --- a/platform/darwin/src/MGLRasterStyleLayer.h +++ b/platform/darwin/src/MGLRasterStyleLayer.h @@ -64,7 +64,7 @@ MGL_EXPORT brightness. The default value of this property is an expression that evaluates to the float - `1`. Set this property to `nil` to reset it to the default value. + 1. Set this property to `nil` to reset it to the default value. This attribute corresponds to the raster-brightness-max @@ -72,7 +72,7 @@ MGL_EXPORT You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values between 0 and 1 inclusive * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -97,7 +97,7 @@ MGL_EXPORT brightness. The default value of this property is an expression that evaluates to the float - `0`. Set this property to `nil` to reset it to the default value. + 0. Set this property to `nil` to reset it to the default value. This attribute corresponds to the raster-brightness-min @@ -105,7 +105,7 @@ MGL_EXPORT You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values between 0 and 1 inclusive * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -129,11 +129,11 @@ MGL_EXPORT Increase or reduce the contrast of the image. The default value of this property is an expression that evaluates to the float - `0`. Set this property to `nil` to reset it to the default value. + 0. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values between −1 and 1 inclusive * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -157,11 +157,11 @@ MGL_EXPORT This property is measured in milliseconds. The default value of this property is an expression that evaluates to the float - `300`. Set this property to `nil` to reset it to the default value. + 300. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values no less than 0 * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -178,7 +178,7 @@ MGL_EXPORT This property is measured in degrees. The default value of this property is an expression that evaluates to the float - `0`. Set this property to `nil` to reset it to the default value. + 0. Set this property to `nil` to reset it to the default value. This attribute corresponds to the raster-hue-rotate @@ -210,11 +210,11 @@ MGL_EXPORT The opacity at which the image will be drawn. The default value of this property is an expression that evaluates to the float - `1`. Set this property to `nil` to reset it to the default value. + 1. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values between 0 and 1 inclusive * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -236,11 +236,11 @@ MGL_EXPORT Increase or reduce the saturation of the image. The default value of this property is an expression that evaluates to the float - `0`. Set this property to `nil` to reset it to the default value. + 0. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values between −1 and 1 inclusive * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables diff --git a/platform/darwin/src/MGLSymbolStyleLayer.h b/platform/darwin/src/MGLSymbolStyleLayer.h index 1017db544..2fd5ef247 100644 --- a/platform/darwin/src/MGLSymbolStyleLayer.h +++ b/platform/darwin/src/MGLSymbolStyleLayer.h @@ -559,14 +559,14 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - `2`. Set this property to `nil` to reset it to the default value. + 2. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values no less than 0 * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -610,7 +610,7 @@ MGL_EXPORT This property is measured in degrees. The default value of this property is an expression that evaluates to the float - `0`. Set this property to `nil` to reset it to the default value. + 0. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. @@ -673,7 +673,7 @@ MGL_EXPORT This property is measured in factor of the original icon sizes. The default value of this property is an expression that evaluates to the float - `1`. Set this property to `nil` to reset it to the default value. + 1. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. @@ -684,7 +684,7 @@ MGL_EXPORT You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values no less than 0 * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -853,7 +853,7 @@ MGL_EXPORT This property is measured in degrees. The default value of this property is an expression that evaluates to the float - `45`. Set this property to `nil` to reset it to the default value. + 45. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`, and `symbolPlacement` is set to an expression that evaluates to `line`. Otherwise, @@ -885,7 +885,7 @@ MGL_EXPORT This property is measured in ems. The default value of this property is an expression that evaluates to the float - `10`. Set this property to `nil` to reset it to the default value. + 10. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -896,7 +896,7 @@ MGL_EXPORT You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values no less than 0 * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -968,14 +968,14 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - `250`. Set this property to `nil` to reset it to the default value. + 250. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `symbolPlacement` is set to an expression that evaluates to `line`. Otherwise, it is ignored. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values no less than 1 * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -1122,7 +1122,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - `16`. Set this property to `nil` to reset it to the default value. + 16. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -1133,7 +1133,7 @@ MGL_EXPORT You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values no less than 0 * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -1212,7 +1212,7 @@ MGL_EXPORT This property is measured in ems. The default value of this property is an expression that evaluates to the float - `0`. Set this property to `nil` to reset it to the default value. + 0. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -1234,7 +1234,7 @@ MGL_EXPORT This property is measured in ems. The default value of this property is an expression that evaluates to the float - `1.2`. Set this property to `nil` to reset it to the default value. + 1.2. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -1331,14 +1331,14 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - `2`. Set this property to `nil` to reset it to the default value. + 2. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values no less than 0 * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -1382,7 +1382,7 @@ MGL_EXPORT This property is measured in degrees. The default value of this property is an expression that evaluates to the float - `0`. Set this property to `nil` to reset it to the default value. + 0. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -1498,14 +1498,14 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - `0`. Set this property to `nil` to reset it to the default value. + 0. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values no less than 0 * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -1556,14 +1556,14 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - `0`. Set this property to `nil` to reset it to the default value. + 0. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values no less than 0 * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -1583,14 +1583,14 @@ MGL_EXPORT The opacity at which the icon will be drawn. The default value of this property is an expression that evaluates to the float - `1`. Set this property to `nil` to reset it to the default value. + 1. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values between 0 and 1 inclusive * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -1741,14 +1741,14 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - `0`. Set this property to `nil` to reset it to the default value. + 0. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values no less than 0 * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -1799,14 +1799,14 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - `0`. Set this property to `nil` to reset it to the default value. + 0. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values no less than 0 * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -1826,14 +1826,14 @@ MGL_EXPORT The opacity at which the text will be drawn. The default value of this property is an expression that evaluates to the float - `1`. Set this property to `nil` to reset it to the default value. + 1. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values between 0 and 1 inclusive * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables From 045392938b95843808cb36999407d10f75917ea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Thu, 25 Jan 2018 23:02:07 -0800 Subject: [PATCH 004/264] [ios] Fixed syntax error in style author guide --- platform/darwin/docs/guides/For Style Authors.md.ejs | 2 +- platform/ios/docs/guides/For Style Authors.md | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/platform/darwin/docs/guides/For Style Authors.md.ejs b/platform/darwin/docs/guides/For Style Authors.md.ejs index 3f6c3fc2a..fc259e4da 100644 --- a/platform/darwin/docs/guides/For Style Authors.md.ejs +++ b/platform/darwin/docs/guides/For Style Authors.md.ejs @@ -359,7 +359,7 @@ In style specification | Method, function, or predicate type | Format string syn <% if (macOS) { -%> `rgb` | `+[NSColor colorWithCalibratedRed:green:blue:alpha:]` | `rgba` | `+[NSColor colorWithCalibratedRed:green:blue:alpha:]` | -<% } else { %> +<% } else { -%> `rgb` | `+[UIColor colorWithRed:green:blue:alpha:]` | `rgba` | `+[UIColor colorWithRed:green:blue:alpha:]` | <% } -%> diff --git a/platform/ios/docs/guides/For Style Authors.md b/platform/ios/docs/guides/For Style Authors.md index 51cd87a76..8db1bd331 100644 --- a/platform/ios/docs/guides/For Style Authors.md +++ b/platform/ios/docs/guides/For Style Authors.md @@ -345,7 +345,6 @@ In style specification | Method, function, or predicate type | Format string syn `concat` | `stringByAppendingString:` | `downcase` | `lowercase:` | `lowercase('DOWNTOWN')` `upcase` | `uppercase:` | `uppercase('Elysian Fields')` - `rgb` | `+[UIColor colorWithRed:green:blue:alpha:]` | `rgba` | `+[UIColor colorWithRed:green:blue:alpha:]` | `to-rgba` | | From 306898d77bae63220b3a69191aaadef6da7e2fc5 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Fri, 26 Jan 2018 15:56:03 -0500 Subject: [PATCH 005/264] [ios] Update changelog and bump pod spec to 4.0.0-beta.1 (#11069) * [ios] Update changelog and bump pod spec to 4.0.0-beta.1 * [ios, macos] Reorganized changelog entries * [ios] Add missing changelog entries. * [ios, macos] Rolled up core changes into changelogs --- platform/ios/CHANGELOG.md | 27 +++++++++++++------ .../Mapbox-iOS-SDK-nightly-dynamic.podspec | 2 +- platform/ios/Mapbox-iOS-SDK-symbols.podspec | 2 +- platform/ios/Mapbox-iOS-SDK.podspec | 2 +- platform/macos/CHANGELOG.md | 20 +++++++++----- 5 files changed, 36 insertions(+), 17 deletions(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 75a77720a..272270321 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -2,37 +2,48 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started. -## master +## 4.0.0 ### Packaging * Removed support for 32-bit simulators. ([#10962](https://github.com/mapbox/mapbox-gl-native/pull/10962)) +* Added a Hebrew localization. ([#10967](https://github.com/mapbox/mapbox-gl-native/pull/10967)) ### Styles and rendering * The layout and paint properties on subclasses of `MGLStyleLayer` are now of type `NSExpression` instead of `MGLStyleValue`. A new “Predicates and Expressions” guide provides an overview of the supported operators. ([#10726](https://github.com/mapbox/mapbox-gl-native/pull/10726)) * Added an `MGLComputedShapeSource` class that allows applications to supply vector data to a style layer on a per-tile basis. ([#9983](https://github.com/mapbox/mapbox-gl-native/pull/9983)) * A style can now display smooth hillshading and customize its appearance at runtime using the `MGLHillshadeStyleLayer` class. Hillshading is based on a rasterized digital elevation model supplied by the `MGLRasterDEMSource` class. ([#10642](https://github.com/mapbox/mapbox-gl-native/pull/10642)) -* Properties such as `MGLSymbolStyleLayer.iconAllowsOverlap` and `MGLSymbolStyleLayer.iconIgnoresPlacement` now account for symbols in other sources. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436)) +* The `MGLSymbolStyleLayer.textFontNames` property can now depend on a feature’s attributes. ([#10850](https://github.com/mapbox/mapbox-gl-native/pull/10850)) + +### Map rendering + * Improved the reliability of collision detection between symbols near the edges of tiles, as well as between symbols when the map is tilted. It is no longer necessary to enable `MGLSymbolStyleLayer.symbolAvoidsEdges` to prevent symbols in adjacent tiles from overlapping with each other. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436)) * Symbols can fade in and out as the map pans, rotates, or tilts. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436)) +* Properties such as `MGLSymbolStyleLayer.iconAllowsOverlap` and `MGLSymbolStyleLayer.iconIgnoresPlacement` now account for symbols in other sources. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436)) * Fixed an issue preventing a dynamically-added `MGLRasterStyleLayer` from drawing until the map pans. ([#10270](https://github.com/mapbox/mapbox-gl-native/pull/10270)) * Fixed an issue preventing `MGLImageSource`s from drawing on the map when the map is zoomed in and tilted. ([#10677](https://github.com/mapbox/mapbox-gl-native/pull/10677)) - -### Snapshots - -* Fixed a memory leak that occurred when creating a map snapshot. ([#10585](https://github.com/mapbox/mapbox-gl-native/pull/10585)) +* Improved the sharpness of raster tiles on Retina displays. ([#10984](https://github.com/mapbox/mapbox-gl-native/pull/10984)) +* Fixed a crash parsing a malformed style. ([#11001](https://github.com/mapbox/mapbox-gl-native/pull/11001)) ### Annotations +* Fixed an issue preventing `MGLAnnotationImage.image` from being updated. ([#10372](https://github.com/mapbox/mapbox-gl-native/pull/10372)) * Improved performance of `MGLAnnotationView`-backed annotations that have `scalesWithViewingDistance` enabled. ([#10951](https://github.com/mapbox/mapbox-gl-native/pull/10951)) +### Map snapshots + +* Fixed a memory leak that occurred when creating a map snapshot. ([#10585](https://github.com/mapbox/mapbox-gl-native/pull/10585)) +* Fixed an issue that caused `MGLMapSnapshotter.pointForCoordinate` to return an incorrect value. ([#11035](https://github.com/mapbox/mapbox-gl-native/pull/11035)) + ### Other changes -* Added a Hebrew localization. ([#10967](https://github.com/mapbox/mapbox-gl-native/pull/10967)) +* Feature querying results now account for the `MGLSymbolStyleLayer.circleStrokeWidth` property. ([#10897](https://github.com/mapbox/mapbox-gl-native/pull/10897)) +* Fixed an issue preventing labels from being transliterated when VoiceOver was enabled on iOS 10._x_ and below. ([#10881](https://github.com/mapbox/mapbox-gl-native/pull/10881)) +* Labels are now transliterated from more languages when VoiceOver is enabled. ([#10881](https://github.com/mapbox/mapbox-gl-native/pull/10881)) * Long-pressing the attribution button causes the SDK’s version number to be displayed in the action sheet that appears. ([#10650](https://github.com/mapbox/mapbox-gl-native/pull/10650)) -## 3.7.3 +## 3.7.3 - January 10, 2018 * Fixed a crash while zooming while annotations are present on the map. ([#10791](https://github.com/mapbox/mapbox-gl-native/pull/10791)) * CJK characters can be displayed in a locally installed font or a custom font bundled with the application, reducing map download times. Specify the font name using the `MGLIdeographicFontFamilyName` key in the application’s Info.plist file. ([#10522](https://github.com/mapbox/mapbox-gl-native/pull/10522)) diff --git a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec index fb9511e70..56714a521 100644 --- a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec +++ b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '3.7.3' + version = '4.0.0-beta.1' m.name = 'Mapbox-iOS-SDK-nightly-dynamic' m.version = "#{version}-nightly" diff --git a/platform/ios/Mapbox-iOS-SDK-symbols.podspec b/platform/ios/Mapbox-iOS-SDK-symbols.podspec index ea8ae667f..fb7e55bbd 100644 --- a/platform/ios/Mapbox-iOS-SDK-symbols.podspec +++ b/platform/ios/Mapbox-iOS-SDK-symbols.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '3.7.3' + version = '4.0.0-beta.1' m.name = 'Mapbox-iOS-SDK-symbols' m.version = "#{version}-symbols" diff --git a/platform/ios/Mapbox-iOS-SDK.podspec b/platform/ios/Mapbox-iOS-SDK.podspec index 1e81272d6..ee3510370 100644 --- a/platform/ios/Mapbox-iOS-SDK.podspec +++ b/platform/ios/Mapbox-iOS-SDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '3.7.3' + version = '4.0.0-beta.1' m.name = 'Mapbox-iOS-SDK' m.version = version diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index c8373b2bf..bcdbb9824 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -1,25 +1,33 @@ # Changelog for Mapbox Maps SDK for macOS -## master +## 0.7.0 ### Styles and rendering * The layout and paint properties on subclasses of `MGLStyleLayer` are now of type `NSExpression` instead of `MGLStyleValue`. A new “Predicates and Expressions” guide provides an overview of the supported operators. ([#10726](https://github.com/mapbox/mapbox-gl-native/pull/10726)) * Added an `MGLComputedShapeSource` class that allows applications to supply vector data to a style layer on a per-tile basis. ([#9983](https://github.com/mapbox/mapbox-gl-native/pull/9983)) * A style can now display smooth hillshading and customize its appearance at runtime using the `MGLHillshadeStyleLayer` class. Hillshading is based on a rasterized digital elevation model supplied by the `MGLRasterDEMSource` class. ([#10642](https://github.com/mapbox/mapbox-gl-native/pull/10642)) -* Properties such as `MGLSymbolStyleLayer.iconAllowsOverlap` and `MGLSymbolStyleLayer.iconIgnoresPlacement` now account for symbols in other sources. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436)) +* The `MGLSymbolStyleLayer.textFontNames` property can now depend on a feature’s attributes. ([#10850](https://github.com/mapbox/mapbox-gl-native/pull/10850)) + +### Map rendering + * Improved the reliability of collision detection between symbols near the edges of tiles, as well as between symbols when the map is tilted. It is no longer necessary to enable `MGLSymbolStyleLayer.symbolAvoidsEdges` to prevent symbols in adjacent tiles from overlapping with each other. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436)) * Symbols can fade in and out as the map pans, rotates, or tilts. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436)) +* Properties such as `MGLSymbolStyleLayer.iconAllowsOverlap` and `MGLSymbolStyleLayer.iconIgnoresPlacement` now account for symbols in other sources. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436)) * Fixed an issue preventing a dynamically-added `MGLRasterStyleLayer` from drawing until the map pans. ([#10270](https://github.com/mapbox/mapbox-gl-native/pull/10270)) * Fixed an issue preventing `MGLImageSource`s from drawing on the map when the map is zoomed in and tilted. ([#10677](https://github.com/mapbox/mapbox-gl-native/pull/10677)) +* Improved the sharpness of raster tiles on Retina displays. ([#10984](https://github.com/mapbox/mapbox-gl-native/pull/10984)) +* Fixed a crash parsing a malformed style. ([#11001](https://github.com/mapbox/mapbox-gl-native/pull/11001)) + +### Map snapshots + +* Fixed a memory leak that occurred when creating a map snapshot. ([#10585](https://github.com/mapbox/mapbox-gl-native/pull/10585)) +* Fixed an issue that caused `MGLMapSnapshotter.pointForCoordinate` to return an incorrect value. ([#11035](https://github.com/mapbox/mapbox-gl-native/pull/11035)) ### Other changes * Added a Hebrew localization. ([#10967](https://github.com/mapbox/mapbox-gl-native/pull/10967)) - -### Snapshots - -* Fixed a memory leak that occurred when creating a map snapshot. ([#10585](https://github.com/mapbox/mapbox-gl-native/pull/10585)) +* Feature querying results now account for the `MGLSymbolStyleLayer.circleStrokeWidth` property. ([#10897](https://github.com/mapbox/mapbox-gl-native/pull/10897)) ## v0.6.1 - January 16, 2018 From ce429dcc1fce2a827e5c9462e3226c3bb04c6a39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Thu, 25 Jan 2018 15:24:29 -0800 Subject: [PATCH 006/264] [core] don't force downloading of Open Sans fonts When a SymbolLayer doesn't have a text-font defined, we automatically add Open Sans/Arial Unicode MS. However, when the SymbolLayer is only used for rendering icons, it doesn't have text-field defined either. In those cases, we still force downloading Open Sans/Arial Unicode MS during offline pack creation. If the user doesn't use this font, this change should save ~15MB and a few seconds in download time. --- src/mbgl/style/parser.cpp | 2 +- test/fixtures/style_parser/font_stacks.json | 3 +++ test/style/style_parser.test.cpp | 21 +++++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/mbgl/style/parser.cpp b/src/mbgl/style/parser.cpp index b177f2159..8d14d7972 100644 --- a/src/mbgl/style/parser.cpp +++ b/src/mbgl/style/parser.cpp @@ -277,7 +277,7 @@ std::vector Parser::fontStacks() const { std::set result; for (const auto& layer : layers) { - if (layer->is()) { + if (layer->is() && !layer->as()->getTextField().isUndefined()) { layer->as()->getTextFont().match( [&] (Undefined) { result.insert({"Open Sans Regular", "Arial Unicode MS Regular"}); diff --git a/test/fixtures/style_parser/font_stacks.json b/test/fixtures/style_parser/font_stacks.json index 07fe223d4..0ff3e936a 100644 --- a/test/fixtures/style_parser/font_stacks.json +++ b/test/fixtures/style_parser/font_stacks.json @@ -12,6 +12,7 @@ "source": "source", "source-layer": "source-layer", "layout": { + "text-field": "a", "text-font": ["a"] } }, { @@ -20,6 +21,7 @@ "source": "source", "source-layer": "source-layer", "layout": { + "text-field": "a", "text-font": { "stops": [[0, ["a", "b"]]] } @@ -30,6 +32,7 @@ "source": "source", "source-layer": "source-layer", "layout": { + "text-field": "a", "text-font": { "stops": [[0, ["a", "b"]], [1, ["a", "b", "c"]]] } diff --git a/test/style/style_parser.test.cpp b/test/style/style_parser.test.cpp index 7f7c06f4c..43b982c3b 100644 --- a/test/style/style_parser.test.cpp +++ b/test/style/style_parser.test.cpp @@ -103,6 +103,23 @@ TEST(StyleParser, FontStacks) { ASSERT_EQ(FontStack({"a", "b", "c"}), result[2]); } +TEST(StyleParser, FontStacksNoTextField) { + style::Parser parser; + parser.parse(R"({ + "version": 8, + "layers": [{ + "id": "symbol", + "type": "symbol", + "source": "vector", + "layout": { + "text-font": ["a"] + } + }] + })"); + auto result = parser.fontStacks(); + ASSERT_EQ(0u, result.size()); +} + TEST(StyleParser, FontStacksCaseExpression) { style::Parser parser; parser.parse(R"({ @@ -112,6 +129,7 @@ TEST(StyleParser, FontStacksCaseExpression) { "type": "symbol", "source": "vector", "layout": { + "text-field": "a", "text-font": ["case", ["==", "a", ["string", ["get", "text-font"]]], ["literal", ["Arial"]], ["literal", ["Helvetica"]]] } }] @@ -131,6 +149,7 @@ TEST(StyleParser, FontStacksMatchExpression) { "type": "symbol", "source": "vector", "layout": { + "text-field": "a", "text-font": ["match", ["get", "text-font"], "a", ["literal", ["Arial"]], ["literal", ["Helvetica"]]] } }] @@ -150,6 +169,7 @@ TEST(StyleParser, FontStacksStepExpression) { "type": "symbol", "source": "vector", "layout": { + "text-field": "a", "text-font": ["array", "string", ["step", ["get", "text-font"], ["literal", ["Arial"]], 0, ["literal", ["Helvetica"]]]] } }] @@ -170,6 +190,7 @@ TEST(StyleParser, FontStacksGetExpression) { "type": "symbol", "source": "vector", "layout": { + "text-field": "a", "text-font": ["array", "string", ["get", "text-font"]] } }] From ff891054a2a2eb85e6d1e9908ad929caa0229033 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 26 Jan 2018 11:43:03 +0100 Subject: [PATCH 007/264] [android] - update changelog for v6.0.0-beta.1 --- platform/android/CHANGELOG.md | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 95cf3a21f..fa212b86a 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,9 +2,28 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. -## master +## 6.0.0-beta.1 - January 26, 2018 + - Binding integration for expressions [#10654](https://github.com/mapbox/mapbox-gl-native/pull/10654) + - CustomGeometrySource [#9983](https://github.com/mapbox/mapbox-gl-native/pull/9983) + - HillshadeLayer and RasterDemSource [#11031](https://github.com/mapbox/mapbox-gl-native/pull/11031) + - Revisit marker placement for snapshot [#11029](https://github.com/mapbox/mapbox-gl-native/pull/11029) + - SafeVarargs annotation for expressions [#11027](https://github.com/mapbox/mapbox-gl-native/pull/11027) + - Expression#toString [#11024](https://github.com/mapbox/mapbox-gl-native/pull/11024) + - Rename initRenderSurface to onSurfaceCreated [#11023](https://github.com/mapbox/mapbox-gl-native/pull/11023) + - Expose attribution manager as public API [#10942](https://github.com/mapbox/mapbox-gl-native/pull/10942) + - Replace Mapzen vector source example with Mapillary [#10931](https://github.com/mapbox/mapbox-gl-native/pull/10931) - Add Hebrew localization [#10967](https://github.com/mapbox/mapbox-gl-native/pull/10967) - + - Cleanup gradle configuration files [#10903](https://github.com/mapbox/mapbox-gl-native/pull/10903) + - Send double tap event only once [#10855](https://github.com/mapbox/mapbox-gl-native/pull/10855) + - Parameter validation for LatLngBounds#from [#10831](https://github.com/mapbox/mapbox-gl-native/pull/10831) + - Replace JSON parsing [#10815](https://github.com/mapbox/mapbox-gl-native/pull/10815) + - Orientation change regression test [#10814](https://github.com/mapbox/mapbox-gl-native/pull/10814) + - Max & min LatLng constants [#10780](https://github.com/mapbox/mapbox-gl-native/pull/10780) + - LatLng#wrap return new instance of LatLng [#10769](https://github.com/mapbox/mapbox-gl-native/pull/10769) + - Custom library loader [#10733](https://github.com/mapbox/mapbox-gl-native/pull/10733) + - Inconsistent parameters for LatLngBounds.union [#10728](https://github.com/mapbox/mapbox-gl-native/pull/10728) + - Gradle 4.1 / AS 3.0 [#10549](https://github.com/mapbox/mapbox-gl-native/pull/10549) + ## 5.3.2 - January 22, 2018 - Validate surface creation before destroying [#10890](https://github.com/mapbox/mapbox-gl-native/pull/10890) - Add filesource activation ot OfflineRegion [#10904](https://github.com/mapbox/mapbox-gl-native/pull/10904) From 828847101b2e2ca3351cc403588b3738794d923c Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 26 Jan 2018 11:49:26 +0100 Subject: [PATCH 008/264] [android] - release android v6.0.0-beta.1 --- circle.yml | 2 +- platform/android/MapboxGLAndroidSDKTestApp/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/circle.yml b/circle.yml index c7e1e519c..1374a541e 100644 --- a/circle.yml +++ b/circle.yml @@ -414,7 +414,7 @@ jobs: - deploy: name: Publish to Maven command: | - if [ "${CIRCLE_BRANCH}" == "master" ]; then make run-android-upload-archives ; fi + if [ "${CIRCLE_BRANCH}" == "release-boba" ]; then make run-android-upload-archives ; fi # ------------------------------------------------------------------------------ diff --git a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle index 3723ae2ac..3740341e9 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle +++ b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle @@ -8,7 +8,7 @@ android { applicationId "com.mapbox.mapboxsdk.testapp" minSdkVersion androidVersions.minSdkVersion targetSdkVersion androidVersions.targetSdkVersion - versionCode 12 + versionCode 13 versionName "6.0.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } From 02e6265c836358f71e7297ff6588d7c00e49b608 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Fri, 26 Jan 2018 17:24:59 -0800 Subject: [PATCH 009/264] [ios] Made NSExpression+MGLAdditions.h public --- platform/ios/ios.xcodeproj/project.pbxproj | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index d44fee343..10928e8e8 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -55,7 +55,7 @@ 3510FFEC1D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3510FFE91D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm */; }; 3510FFED1D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3510FFE91D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm */; }; 3510FFF01D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3510FFEE1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3510FFF11D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3510FFEE1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h */; }; + 3510FFF11D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3510FFEE1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3510FFF21D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3510FFEF1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm */; }; 3510FFF31D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3510FFEF1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm */; }; 3510FFF91D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3510FFF71D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.h */; }; @@ -444,6 +444,8 @@ DA8963381CC549A100684375 /* sprites in Resources */ = {isa = PBXBuildFile; fileRef = DA8963341CC549A100684375 /* sprites */; }; DA8963391CC549A100684375 /* styles in Resources */ = {isa = PBXBuildFile; fileRef = DA8963351CC549A100684375 /* styles */; }; DA89633A1CC549A100684375 /* tiles in Resources */ = {isa = PBXBuildFile; fileRef = DA8963361CC549A100684375 /* tiles */; }; + DA9EA82B201C0C0C00F9874D /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA9EA82A201C0C0B00F9874D /* NSExpression+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DA9EA82C201C0C0C00F9874D /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA9EA82A201C0C0B00F9874D /* NSExpression+MGLAdditions.h */; }; DAA32CC31E4C6B65006F8D24 /* MGLDistanceFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 3557F7AF1E1D27D300CCA5E6 /* MGLDistanceFormatter.m */; }; DAA4E4081CBB6C9500178DFB /* Mapbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA8847D21CBAF91600AB86E3 /* Mapbox.framework */; }; DAA4E4091CBB6C9500178DFB /* Mapbox.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DA8847D21CBAF91600AB86E3 /* Mapbox.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -1020,6 +1022,7 @@ DA9C012C1E4C7ADB00C4742A /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "pt-BR"; path = "pt-BR.lproj/Foundation.stringsdict"; sourceTree = ""; }; DA9C012D1E4C7B1F00C4742A /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = ""; }; DA9C012E1E4C7B6100C4742A /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Root.strings"; sourceTree = ""; }; + DA9EA82A201C0C0B00F9874D /* NSExpression+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSExpression+MGLAdditions.h"; sourceTree = ""; }; DAA32CA11E4C44DB006F8D24 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = de; path = de.lproj/Foundation.stringsdict; sourceTree = ""; }; DAA32CA21E4C44DD006F8D24 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = de; path = de.lproj/Localizable.stringsdict; sourceTree = ""; }; DAA32CA31E4C44F1006F8D24 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Foundation.strings; sourceTree = ""; }; @@ -1789,6 +1792,7 @@ 408AA8561DAEDA0800022900 /* NSDictionary+MGLAdditions.mm */, DA8848141CBAFA6200AB86E3 /* NSException+MGLAdditions.h */, 3510FFEE1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h */, + DA9EA82A201C0C0B00F9874D /* NSExpression+MGLAdditions.h */, DAC25FCB200FD83E009BE98E /* NSExpression+MGLPrivateAdditions.h */, 3510FFEF1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm */, 35B82BF61D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h */, @@ -1982,6 +1986,7 @@ DA8848841CBB033F00AB86E3 /* FABAttributes.h in Headers */, DA8847FD1CBAFA5100AB86E3 /* MGLTilePyramidOfflineRegion.h in Headers */, DA88482F1CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.h in Headers */, + DA9EA82B201C0C0C00F9874D /* NSExpression+MGLAdditions.h in Headers */, DA8848601CBAFC2E00AB86E3 /* Mapbox.h in Headers */, DAF0D8101DFE0EA000B28378 /* MGLRasterSource_Private.h in Headers */, 350098BB1D480108004B2AF0 /* MGLVectorSource.h in Headers */, @@ -2012,6 +2017,7 @@ DABFB8641CBE99E500D62B32 /* MGLOfflineStorage.h in Headers */, 96E516E32000552A00A02306 /* MGLAccountManager_Private.h in Headers */, 96E5170420005A6B00A02306 /* SMCalloutView.h in Headers */, + DA9EA82C201C0C0C00F9874D /* NSExpression+MGLAdditions.h in Headers */, 96036A02200565C700510F3D /* NSOrthography+MGLAdditions.h in Headers */, DAD165791CF4CDFF001FF4B9 /* MGLShapeCollection.h in Headers */, 4049C29E1DB6CD6C00B3F799 /* MGLPointCollection.h in Headers */, From 2d6fcd3178503df318d52f1fdf698648f968d1b1 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Tue, 30 Jan 2018 15:35:23 +0100 Subject: [PATCH 010/264] [android] - update javadoc with links to style specification --- .../style/expressions/Expression.java | 139 +++++++++++++++++- 1 file changed, 138 insertions(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java index 7b841a258..0f76f590c 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java @@ -246,7 +246,7 @@ public class Expression { } /** - * Create a literal object expression + * Create a literal object expression. * * @param object the object * @return the expression @@ -280,6 +280,7 @@ public class Expression { * @param green green color expression * @param blue blue color expression * @return expression + * @see Style specification */ public static Expression rgb(@NonNull Expression red, @NonNull Expression green, @NonNull Expression blue) { @@ -297,6 +298,7 @@ public class Expression { * @param green green color value * @param blue blue color value * @return expression + * @see Style specification */ public static Expression rgb(@NonNull Number red, @NonNull Number green, @NonNull Number blue) { return rgb(literal(red), literal(green), literal(blue)); @@ -314,6 +316,7 @@ public class Expression { * @param blue blue color value * @param alpha alpha color value * @return expression + * @see Style specification */ public static Expression rgba(@NonNull Expression red, @NonNull Expression green, @NonNull Expression blue, @NonNull Expression alpha) { @@ -332,6 +335,7 @@ public class Expression { * @param blue blue color value * @param alpha alpha color value * @return expression + * @see Style specification */ public static Expression rgba(@NonNull Number red, @NonNull Number green, @NonNull Number blue, @NonNull Number alpha) { return rgba(literal(red), literal(green), literal(blue), literal(alpha)); @@ -342,6 +346,7 @@ public class Expression { * * @param expression an expression to convert to a color * @return expression + * @see Style specification */ public static Expression toRgba(@NonNull Expression expression) { return new Expression<>("to-rgba", expression); @@ -358,6 +363,7 @@ public class Expression { * @param compareOne the first expression * @param compareTwo the second expression * @return expression + * @see Style specification */ public static Expression eq(@NonNull Expression compareOne, @NonNull Expression compareTwo) { return new Expression<>("==", compareOne, compareTwo); @@ -369,6 +375,7 @@ public class Expression { * @param compareOne the first boolean * @param compareTwo the second boolean * @return expression + * @see Style specification */ public static Expression eq(boolean compareOne, boolean compareTwo) { return eq(literal(compareOne), literal(compareTwo)); @@ -380,6 +387,7 @@ public class Expression { * @param compareOne the first number * @param compareTwo the second number * @return expression + * @see Style specification */ public static Expression eq(@NonNull String compareOne, @NonNull String compareTwo) { return eq(literal(compareOne), literal(compareTwo)); @@ -391,6 +399,7 @@ public class Expression { * @param compareOne the first number * @param compareTwo the second number * @return expression + * @see Style specification */ public static Expression eq(@NonNull Number compareOne, @NonNull Number compareTwo) { return eq(literal(compareOne), literal(compareTwo)); @@ -403,6 +412,7 @@ public class Expression { * @param compareOne the first expression * @param compareTwo the second expression * @return expression + * @see Style specification */ public static Expression neq(@NonNull Expression compareOne, @NonNull Expression compareTwo) { return new Expression<>("!=", compareOne, compareTwo); @@ -414,6 +424,7 @@ public class Expression { * @param compareOne the first boolean * @param compareTwo the second boolean * @return expression + * @see Style specification */ public static Expression neq(boolean compareOne, boolean compareTwo) { return new Expression<>("!=", literal(compareOne), literal(compareTwo)); @@ -425,6 +436,7 @@ public class Expression { * @param compareOne the first string * @param compareTwo the second string * @return expression + * @see Style specification */ public static Expression neq(@NonNull String compareOne, @NonNull String compareTwo) { return new Expression<>("!=", literal(compareOne), literal(compareTwo)); @@ -436,6 +448,7 @@ public class Expression { * @param compareOne the first number * @param compareTwo the second number * @return expression + * @see Style specification */ public static Expression neq(@NonNull Number compareOne, @NonNull Number compareTwo) { return new Expression<>("!=", literal(compareOne), literal(compareTwo)); @@ -448,6 +461,7 @@ public class Expression { * @param compareOne the first expression * @param compareTwo the second expression * @return expression + * @see Style specification */ public static Expression gt(@NonNull Expression compareOne, @NonNull Expression compareTwo) { return new Expression<>(">", compareOne, compareTwo); @@ -459,6 +473,7 @@ public class Expression { * @param compareOne the first number * @param compareTwo the second number * @return expression + * @see Style specification */ public static Expression gt(@NonNull Number compareOne, @NonNull Number compareTwo) { return new Expression<>(">", literal(compareOne), literal(compareTwo)); @@ -470,6 +485,7 @@ public class Expression { * @param compareOne the first string * @param compareTwo the second string * @return expression + * @see Style specification */ public static Expression gt(@NonNull String compareOne, @NonNull String compareTwo) { return new Expression<>(">", literal(compareOne), literal(compareTwo)); @@ -482,6 +498,7 @@ public class Expression { * @param compareOne the first number * @param compareTwo the second number * @return expression + * @see Style specification */ public static Expression lt(@NonNull Expression compareOne, @NonNull Expression compareTwo) { return new Expression<>("<", compareOne, compareTwo); @@ -493,6 +510,7 @@ public class Expression { * @param compareOne the first number * @param compareTwo the second number * @return expression + * @see Style specification */ public static Expression lt(@NonNull Number compareOne, @NonNull Number compareTwo) { return new Expression<>("<", literal(compareOne), literal(compareTwo)); @@ -504,6 +522,7 @@ public class Expression { * @param compareOne the first string * @param compareTwo the second string * @return expression + * @see Style specification */ public static Expression lt(@NonNull String compareOne, @NonNull String compareTwo) { return new Expression<>("<", literal(compareOne), literal(compareTwo)); @@ -516,6 +535,7 @@ public class Expression { * @param compareOne the first expression * @param compareTwo the second expression * @return expression + * @see Style specification */ public static Expression gte(@NonNull Expression compareOne, @NonNull Expression compareTwo) { return new Expression<>(">=", compareOne, compareTwo); @@ -527,6 +547,7 @@ public class Expression { * @param compareOne the first number * @param compareTwo the second number * @return expression + * @see Style specification */ public static Expression gte(@NonNull Number compareOne, @NonNull Number compareTwo) { return new Expression<>(">=", literal(compareOne), literal(compareTwo)); @@ -538,6 +559,7 @@ public class Expression { * @param compareOne the first string * @param compareTwo the second string * @return expression + * @see Style specification */ public static Expression gte(@NonNull String compareOne, @NonNull String compareTwo) { return new Expression<>(">=", literal(compareOne), literal(compareTwo)); @@ -550,6 +572,7 @@ public class Expression { * @param compareOne the first expression * @param compareTwo the second expression * @return expression + * @see Style specification */ public static Expression lte(@NonNull Expression compareOne, @NonNull Expression compareTwo) { return new Expression<>("<=", compareOne, compareTwo); @@ -561,6 +584,7 @@ public class Expression { * @param compareOne the first number * @param compareTwo the second number * @return expression + * @see Style specification */ public static Expression lte(@NonNull Number compareOne, @NonNull Number compareTwo) { return new Expression<>("<=", literal(compareOne), literal(compareTwo)); @@ -572,6 +596,7 @@ public class Expression { * @param compareOne the first string * @param compareTwo the second string * @return expression + * @see Style specification */ public static Expression lte(@NonNull String compareOne, @NonNull String compareTwo) { return new Expression<>("<=", literal(compareOne), literal(compareTwo)); @@ -587,6 +612,7 @@ public class Expression { * * @param input expression input * @return expression + * @see Style specification */ @SafeVarargs public static Expression all(@NonNull Expression... input) { @@ -603,6 +629,7 @@ public class Expression { * * @param input expression input * @return expression + * @see Style specification */ @SafeVarargs public static Expression any(@NonNull Expression... input) { @@ -614,6 +641,7 @@ public class Expression { * * @param input expression input * @return expression + * @see Style specification */ public static Expression not(@NonNull Expression input) { return new Expression<>("!", input); @@ -624,6 +652,7 @@ public class Expression { * * @param input boolean input * @return expression + * @see Style specification */ public static Expression not(boolean input) { return not(literal(input)); @@ -631,9 +660,14 @@ public class Expression { /** * Selects the first output whose corresponding test condition evaluates to true. + *

+ * For each case a condition and an output should be provided. + * The last parameter should provide the default output. + *

* * @param input expression input * @return expression + * @see Style specification */ @SafeVarargs public static Expression switchCase(@NonNull @Size(min = 1) Expression... input) { @@ -647,6 +681,7 @@ public class Expression { * * @param input expression input * @return expression + * @see Style specification */ public static Expression match(@NonNull @Size(min = 2) Expression... input) { return new Expression("match", input); @@ -659,6 +694,7 @@ public class Expression { * * @param input expression input * @return expression + * @see Style specification */ public static Expression match(@NonNull Expression input, @NonNull Stop... stops) { Expression[] expressions = new Expression[stops.length * 2]; @@ -674,6 +710,7 @@ public class Expression { * * @param input expression input * @return expression + * @see Style specification */ public static Expression coalesce(@NonNull Expression... input) { return new Expression("coalesce", input); @@ -690,6 +727,7 @@ public class Expression { *

* * @return expression + * @see Style specification */ public static Expression properties() { return new Expression<>("properties"); @@ -699,6 +737,7 @@ public class Expression { * Gets the feature's geometry type: Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon. * * @return expression + * @see Style specification */ public static Expression geometryType() { return new Expression<>("geometry-type"); @@ -708,6 +747,7 @@ public class Expression { * Gets the feature's id, if it has one. * * @return expression + * @see Style specification */ public static Expression id() { return new Expression<>("id"); @@ -723,6 +763,7 @@ public class Expression { * Can only be used in the `heatmap-color` property. * * @return expression + * @see Style specification */ public static Expression heatmapDensity() { return new Expression<>("heatmap-density"); @@ -738,6 +779,7 @@ public class Expression { * @param number the index expression * @param expression the array expression * @return expression + * @see Style specification */ public static Expression at(@NonNull Expression number, @NonNull Expression expression) { return new Expression<>("at", number, expression); @@ -749,6 +791,7 @@ public class Expression { * @param number the index expression * @param expression the array expression * @return expression + * @see Style specification */ public static Expression at(@NonNull Number number, @NonNull Expression expression) { return at(literal(number), expression); @@ -761,6 +804,7 @@ public class Expression { * * @param input expression input * @return expression + * @see Style specification */ public static Expression get(@NonNull Expression input) { return new Expression<>("get", input); @@ -773,6 +817,7 @@ public class Expression { * * @param input string input * @return expression + * @see Style specification */ public static Expression get(@NonNull String input) { return get(literal(input)); @@ -785,6 +830,7 @@ public class Expression { * @param key a property value key * @param object an expression object * @return expression + * @see Style specification */ public static Expression get(@NonNull Expression key, @NonNull Expression object) { return new Expression<>("get", key, object); @@ -797,6 +843,7 @@ public class Expression { * @param key a property value key * @param object an expression object * @return expression + * @see Style specification */ public static Expression get(@NonNull String key, @NonNull Expression object) { return get(literal(key), object); @@ -807,6 +854,7 @@ public class Expression { * * @param key the expression property value key * @return expression + * @see Style specification */ public static Expression has(@NonNull Expression key) { return new Expression<>("has", key); @@ -817,6 +865,7 @@ public class Expression { * * @param key the property value key * @return expression + * @see Style specification */ public static Expression has(@NonNull String key) { return has(literal(key)); @@ -828,6 +877,7 @@ public class Expression { * @param key the expression property value key * @param object an expression object * @return expression + * @see Style specification */ public static Expression has(@NonNull Expression key, @NonNull Expression object) { return new Expression<>("has", key, object); @@ -839,6 +889,7 @@ public class Expression { * @param key the property value key * @param object an expression object * @return expression + * @see Style specification */ public static Expression has(@NonNull String key, @NonNull Expression object) { return has(literal(key), object); @@ -849,6 +900,7 @@ public class Expression { * * @param expression an expression object or expression string * @return expression + * @see Style specification */ public static Expression length(@NonNull Expression expression) { return new Expression<>("length", expression); @@ -859,6 +911,7 @@ public class Expression { * * @param input a string * @return expression + * @see Style specification */ public static Expression length(@NonNull String input) { return length(literal(input)); @@ -872,6 +925,7 @@ public class Expression { * Returns mathematical constant ln(2). * * @return expression + * @see Style specification */ public static Expression ln2() { return new Expression<>("ln2"); @@ -881,6 +935,7 @@ public class Expression { * Returns the mathematical constant pi. * * @return expression + * @see Style specification */ public static Expression pi() { return new Expression<>("pi"); @@ -890,6 +945,7 @@ public class Expression { * Returns the mathematical constant e. * * @return expression + * @see Style specification */ public static Expression e() { return new Expression<>("e"); @@ -900,6 +956,7 @@ public class Expression { * * @param numbers the numbers to calculate the sum for * @return expression + * @see Style specification */ @SafeVarargs public static Expression sum(@Size(min = 2) Expression... numbers) { @@ -911,6 +968,7 @@ public class Expression { * * @param numbers the numbers to calculate the sum for * @return expression + * @see Style specification */ @SuppressWarnings("unchecked") public static Expression sum(@Size(min = 2) Number... numbers) { @@ -926,6 +984,7 @@ public class Expression { * * @param numbers the numbers to calculate the product for * @return expression + * @see Style specification */ @SafeVarargs public static Expression product(@Size(min = 2) Expression... numbers) { @@ -937,6 +996,7 @@ public class Expression { * * @param numbers the numbers to calculate the product for * @return expression + * @see Style specification */ @SuppressWarnings("unchecked") public static Expression product(@Size(min = 2) Number... numbers) { @@ -952,6 +1012,7 @@ public class Expression { * * @param number the number subtract from 0 * @return expression + * @see Style specification */ public static Expression subtract(@NonNull Expression number) { return new Expression<>("-", number); @@ -962,6 +1023,7 @@ public class Expression { * * @param number the number subtract from 0 * @return expression + * @see Style specification */ public static Expression subtract(@NonNull Number number) { return subtract(literal(number)); @@ -973,6 +1035,7 @@ public class Expression { * @param first the first number * @param second the second number * @return expression + * @see Style specification */ public static Expression subtract(@NonNull Expression first, @NonNull Expression second) { return new Expression<>("-", first, second); @@ -984,6 +1047,7 @@ public class Expression { * @param first the first number * @param second the second number * @return expression + * @see Style specification */ public static Expression subtract(@NonNull Number first, @NonNull Number second) { return subtract(literal(first), literal(second)); @@ -995,6 +1059,7 @@ public class Expression { * @param first the first number * @param second the second number * @return expression + * @see Style specification */ @SuppressWarnings("unchecked") public static Expression division(@NonNull Expression first, @NonNull Expression second) { @@ -1007,6 +1072,7 @@ public class Expression { * @param first the first number * @param second the second number * @return expression + * @see Style specification */ @SuppressWarnings("unchecked") public static Expression division(@NonNull Number first, @NonNull Number second) { @@ -1019,6 +1085,7 @@ public class Expression { * @param first the first number * @param second the second number * @return expression + * @see Style specification */ public static Expression mod(@NonNull Expression first, @NonNull Expression second) { return new Expression<>("%", first, second); @@ -1030,6 +1097,7 @@ public class Expression { * @param first the first number * @param second the second number * @return expression + * @see Style specification */ public static Expression mod(@NonNull Number first, @NonNull Number second) { return mod(literal(first), literal(second)); @@ -1041,6 +1109,7 @@ public class Expression { * @param first the first number * @param second the second number * @return expression + * @see Style specification */ public static Expression pow(@NonNull Expression first, @NonNull Expression second) { return new Expression<>("^", first, second); @@ -1052,6 +1121,7 @@ public class Expression { * @param first the first number * @param second the second number * @return expression + * @see Style specification */ public static Expression pow(@NonNull Number first, @NonNull Number second) { return pow(literal(first), literal(second)); @@ -1062,6 +1132,7 @@ public class Expression { * * @param number the number to take the square root from * @return expression + * @see Style specification */ public static Expression sqrt(@NonNull Expression number) { return new Expression<>("sqrt", number); @@ -1072,6 +1143,7 @@ public class Expression { * * @param number the number to take the square root from * @return expression + * @see Style specification */ public static Expression sqrt(@NonNull Number number) { return sqrt(literal(number)); @@ -1082,6 +1154,7 @@ public class Expression { * * @param number the number to take base-ten logarithm from * @return expression + * @see Style specification */ public static Expression log10(@NonNull Expression number) { return new Expression<>("log10", number); @@ -1092,6 +1165,7 @@ public class Expression { * * @param number the number to take base-ten logarithm from * @return expression + * @see Style specification */ public static Expression log10(@NonNull Number number) { return log10(literal(number)); @@ -1102,6 +1176,7 @@ public class Expression { * * @param number the number to take natural logarithm from * @return expression + * @see Style specification */ public static Expression ln(Expression number) { return new Expression<>("ln", number); @@ -1112,6 +1187,7 @@ public class Expression { * * @param number the number to take natural logarithm from * @return expression + * @see Style specification */ public static Expression ln(Number number) { return ln(literal(number)); @@ -1122,6 +1198,7 @@ public class Expression { * * @param number the number to take base-two logarithm from * @return expression + * @see Style specification */ public static Expression log2(@NonNull Expression number) { return new Expression<>("log2", number); @@ -1132,6 +1209,7 @@ public class Expression { * * @param number the number to take base-two logarithm from * @return expression + * @see Style specification */ public static Expression log2(@NonNull Number number) { return log2(literal(number)); @@ -1142,6 +1220,7 @@ public class Expression { * * @param number the number to calculate the sine for * @return expression + * @see Style specification */ public static Expression sin(@NonNull Expression number) { return new Expression<>("sin", number); @@ -1152,6 +1231,7 @@ public class Expression { * * @param number the number to calculate the sine for * @return expression + * @see Style specification */ public static Expression sin(@NonNull Number number) { return sin(literal(number)); @@ -1162,6 +1242,7 @@ public class Expression { * * @param number the number to calculate the cosine for * @return expression + * @see Style specification */ public static Expression cos(@NonNull Expression number) { return new Expression<>("cos", number); @@ -1172,6 +1253,7 @@ public class Expression { * * @param number the number to calculate the cosine for * @return expression + * @see Style specification */ public static Expression cos(@NonNull Number number) { return new Expression<>("cos", literal(number)); @@ -1182,6 +1264,7 @@ public class Expression { * * @param number the number to calculate the tangent for * @return expression + * @see Style specification */ public static Expression tan(@NonNull Expression number) { return new Expression<>("tan", number); @@ -1192,6 +1275,7 @@ public class Expression { * * @param number the number to calculate the tangent for * @return expression + * @see Style specification */ public static Expression tan(@NonNull Number number) { return new Expression<>("tan", literal(number)); @@ -1202,6 +1286,7 @@ public class Expression { * * @param number the number to calculate the arcsine for * @return expression + * @see Style specification */ public static Expression asin(@NonNull Expression number) { return new Expression<>("asin", number); @@ -1212,6 +1297,7 @@ public class Expression { * * @param number the number to calculate the arcsine for * @return expression + * @see Style specification */ public static Expression asin(@NonNull Number number) { return asin(literal(number)); @@ -1222,6 +1308,7 @@ public class Expression { * * @param number the number to calculate the arccosine for * @return expression + * @see Style specification */ public static Expression acos(@NonNull Expression number) { return new Expression<>("acos", number); @@ -1232,6 +1319,7 @@ public class Expression { * * @param number the number to calculate the arccosine for * @return expression + * @see Style specification */ public static Expression acos(@NonNull Number number) { return acos(literal(number)); @@ -1242,6 +1330,7 @@ public class Expression { * * @param number the number to calculate the arctangent for * @return expression + * @see Style specification */ public static Expression atan(@NonNull Expression number) { return new Expression("atan", number); @@ -1252,6 +1341,7 @@ public class Expression { * * @param number the number to calculate the arctangent for * @return expression + * @see Style specification */ public static Expression atan(@NonNull Number number) { return atan(literal(number)); @@ -1262,6 +1352,7 @@ public class Expression { * * @param numbers varargs of numbers to get the minimum from * @return expression + * @see Style specification */ @SafeVarargs public static Expression min(@Size(min = 1) Expression... numbers) { @@ -1273,6 +1364,7 @@ public class Expression { * * @param numbers varargs of numbers to get the minimum from * @return expression + * @see Style specification */ @SuppressWarnings("unchecked") public static Expression min(@Size(min = 1) Number... numbers) { @@ -1288,6 +1380,7 @@ public class Expression { * * @param numbers varargs of numbers to get the maximum from * @return expression + * @see Style specification */ @SafeVarargs public static Expression max(@Size(min = 1) Expression... numbers) { @@ -1299,6 +1392,7 @@ public class Expression { * * @param numbers varargs of numbers to get the maximum from * @return expression + * @see Style specification */ @SuppressWarnings("unchecked") public static Expression max(@Size(min = 1) Number... numbers) { @@ -1322,6 +1416,7 @@ public class Expression { * * @param string the string to upcase * @return expression + * @see Style specification */ public static Expression upcase(@NonNull Expression string) { return new Expression<>("upcase", string); @@ -1336,6 +1431,7 @@ public class Expression { * * @param string string to upcase * @return expression + * @see Style specification */ public static Expression upcase(@NonNull String string) { return upcase(literal(string)); @@ -1350,6 +1446,7 @@ public class Expression { * * @param input expression input * @return expression + * @see Style specification */ public static Expression downcase(@NonNull Expression input) { return new Expression<>("downcase", input); @@ -1364,6 +1461,7 @@ public class Expression { * * @param input string to downcase * @return expression + * @see Style specification */ public static Expression downcase(@NonNull String input) { return downcase(literal(input)); @@ -1374,6 +1472,7 @@ public class Expression { * * @param input expression input * @return expression + * @see Style specification */ @SafeVarargs public static Expression concat(@NonNull Expression... input) { @@ -1385,6 +1484,7 @@ public class Expression { * * @param input expression input * @return expression + * @see Style specification */ @SuppressWarnings("unchecked") public static Expression concat(@NonNull String... input) { @@ -1406,6 +1506,7 @@ public class Expression { * * @param input expression input * @return expression + * @see Style specification */ public static Expression array(@NonNull Expression input) { return new Expression<>("array", input); @@ -1416,6 +1517,7 @@ public class Expression { * * @param input expression input * @return expression + * @see Style specification */ public static Expression typeOf(@NonNull Expression input) { return new Expression<>("typeof", input); @@ -1428,6 +1530,7 @@ public class Expression { * * @param input expression input * @return expression + * @see Style specification */ public static Expression string(@NonNull Expression input) { return new Expression<>("string", input); @@ -1440,6 +1543,7 @@ public class Expression { * * @param input expression input * @return expression + * @see Style specification */ public static Expression number(@NonNull Expression input) { return new Expression<>("number", input); @@ -1452,6 +1556,7 @@ public class Expression { * * @param input expression input * @return expression + * @see Style specification */ public static Expression bool(@NonNull Expression input) { return new Expression<>("boolean", input); @@ -1462,6 +1567,7 @@ public class Expression { * * @param input expression input * @return expression + * @see Style specification */ public static Expression object(@NonNull Expression input) { return new Expression<>("object", input); @@ -1479,6 +1585,7 @@ public class Expression { * * @param input expression input * @return expression + * @see Style specification */ public static Expression toString(@NonNull Expression input) { return new Expression<>("to-string", input); @@ -1494,6 +1601,7 @@ public class Expression { * * @param input expression input * @return expression + * @see Style specification */ public static Expression toNumber(@NonNull Expression input) { return new Expression<>("to-number", input); @@ -1505,6 +1613,7 @@ public class Expression { * * @param input expression input * @return expression + * @see Style specification */ public static Expression toBool(@NonNull Expression input) { return new Expression<>("to-boolean", input); @@ -1517,6 +1626,7 @@ public class Expression { * * @param input expression input * @return expression + * @see Style specification */ public static Expression toColor(@NonNull Expression input) { return new Expression<>("to-color", input); @@ -1532,6 +1642,7 @@ public class Expression { * * @param input expression input * @return expression + * @see Style specification */ @SafeVarargs public static Expression let(@Size(min = 1) Expression... input) { @@ -1543,6 +1654,7 @@ public class Expression { * * @param expression the variable naming expression that was bound with using let * @return expression + * @see Style specification */ public static Expression var(@NonNull Expression expression) { return new Expression<>("var", expression); @@ -1553,6 +1665,7 @@ public class Expression { * * @param variableName the variable naming that was bound with using let * @return expression + * @see Style specification */ public static Expression var(@NonNull String variableName) { return var(literal(variableName)); @@ -1570,6 +1683,7 @@ public class Expression { *

* * @return expression + * @see Style specification */ public static Expression zoom() { return new Expression<>("zoom"); @@ -1579,6 +1693,13 @@ public class Expression { // Ramps, scales, curves // + /** + * Produces a stop value to be used as part of the step expression. + * + * @param stop the stop input + * @param value the stop output + * @return the stop + */ public static Stop stop(@NonNull Object stop, @NonNull Object value) { return new Stop(stop, value); } @@ -1593,6 +1714,7 @@ public class Expression { * @param input the input value * @param stops pair of input and output values * @return expression + * @see Style specification */ @SafeVarargs public static Expression step(@NonNull Number input, @NonNull Expression expression, Expression... stops) { @@ -1609,6 +1731,7 @@ public class Expression { * @param expression the input expression * @param stops pair of input and output values * @return expression + * @see Style specification */ @SafeVarargs public static Expression step(@NonNull Expression input, @NonNull Expression expression, Expression... stops) { @@ -1625,6 +1748,7 @@ public class Expression { * @param input the input value * @param stops pair of input and output values * @return expression + * @see Style specification */ @SafeVarargs public static Expression step(@NonNull Number input, @NonNull Expression expression, Stop... stops) { @@ -1646,6 +1770,7 @@ public class Expression { * @param input the input value * @param stops pair of input and output values * @return expression + * @see Style specification */ @SafeVarargs public static Expression step(@NonNull Expression input, @NonNull Expression expression, Stop... stops) { @@ -1667,6 +1792,7 @@ public class Expression { * @param number the input expression * @param stops pair of input and output values * @return expression + * @see Style specification */ @SafeVarargs public static Expression interpolate(@NonNull Expression interpolation, @@ -1684,6 +1810,7 @@ public class Expression { * @param number the input expression * @param stops pair of input and output values * @return expression + * @see Style specification */ @SafeVarargs public static Expression interpolate(@NonNull Expression interpolation, @@ -1700,6 +1827,7 @@ public class Expression { * interpolates linearly between the pair of stops just less than and just greater than the input. * * @return expression + * @see Style specification */ public static Expression linear() { return new Expression<>("linear"); @@ -1713,6 +1841,7 @@ public class Expression { * * @param base value controlling the route at which the output increases * @return expression + * @see Style specification */ public static Expression exponential(@NonNull Number base) { return exponential(literal(base)); @@ -1726,6 +1855,7 @@ public class Expression { * * @param expression base number expression * @return expression + * @see Style specification */ public static Expression exponential(@NonNull Expression expression) { return new Expression<>("exponential", expression); @@ -1739,6 +1869,7 @@ public class Expression { * @param x2 x value of the second point of a cubic bezier, ranges from 0 to 1 * @param y2 y value fo the second point of a cubic bezier, ranges from 0 to 1 * @return expression + * @see Style specification */ public static Expression cubicBezier(@NonNull Expression x1, @NonNull Expression y1, @NonNull Expression x2, @NonNull Expression y2) { @@ -1753,6 +1884,7 @@ public class Expression { * @param x2 x value of the second point of a cubic bezier, ranges from 0 to 1 * @param y2 y value fo the second point of a cubic bezier, ranges from 0 to 1 * @return expression + * @see Style specification */ public static Expression cubicBezier(@NonNull Number x1, @NonNull Number y1, @NonNull Number x2, @NonNull Number y2) { @@ -1777,6 +1909,11 @@ public class Expression { } + /** + * Returns a string representation of the object that matches the definition set in the style specification. + * + * @return a string representation of the object. + */ @Override public String toString() { StringBuilder builder = new StringBuilder(); From 954c20b8c1832e0d94c4495a152b795df56ae967 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Thu, 25 Jan 2018 14:04:13 +0100 Subject: [PATCH 011/264] [android] - blacklist adreno 2xx for VAO support --- src/mbgl/gl/context.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp index 7444ac112..b6244d58c 100644 --- a/src/mbgl/gl/context.cpp +++ b/src/mbgl/gl/context.cpp @@ -258,9 +258,10 @@ UniqueTexture Context::createTexture() { bool Context::supportsVertexArrays() const { static bool blacklisted = []() { - // Blacklist Adreno 3xx as it crashes on glBuffer(Sub)Data + // Blacklist Adreno 2xx, 3xx as it crashes on glBuffer(Sub)Data const std::string renderer = reinterpret_cast(glGetString(GL_RENDERER)); - return renderer.find("Adreno (TM) 3") != std::string::npos; + return renderer.find("Adreno (TM) 2") != std::string::npos + || renderer.find("Adreno (TM) 3") != std::string::npos; }(); return !blacklisted && From 3714aa94e779b4a5a3d6d531b6a4273ae2275ba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Mon, 29 Jan 2018 15:36:38 -0500 Subject: [PATCH 012/264] [android] - log lack of permissions only if enabling location --- .../main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java index 6eacbbaea..81fd091c9 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java @@ -374,7 +374,7 @@ public final class TrackingSettings { } private void setMyLocationEnabled(boolean locationEnabled, boolean isCustomLocationSource) { - if (!PermissionsManager.areLocationPermissionsGranted(myLocationView.getContext())) { + if (locationEnabled && !PermissionsManager.areLocationPermissionsGranted(myLocationView.getContext())) { Timber.e("Could not activate user location tracking: " + "user did not accept the permission or permissions were not requested."); return; From cdf65020c18812d492d94a64b9fc42853442de22 Mon Sep 17 00:00:00 2001 From: Martin Hellwig Date: Thu, 25 Jan 2018 17:04:53 +0100 Subject: [PATCH 013/264] Implement new bearingTrackingMode GPS_NORTH_FACING --- .../constants/MyBearingTracking.java | 7 +++++- .../maps/widgets/MyLocationView.java | 23 +++++++++++++++---- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java index ceac862f3..c042b0057 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java @@ -22,7 +22,7 @@ import java.lang.annotation.RetentionPolicy; */ public class MyBearingTracking { - @IntDef( {NONE, COMPASS, GPS}) + @IntDef( {NONE, COMPASS, GPS, GPS_NORTH_FACING}) @Retention(RetentionPolicy.SOURCE) public @interface Mode { } @@ -42,4 +42,9 @@ public class MyBearingTracking { */ public static final int GPS = 0x00000008; + /** + * Tracking the bearing of the user based on GPS data, but camera always faces north direction + */ + public static final int GPS_NORTH_FACING = 0x0000000B; + } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java index 4f27e0ada..1cdd91028 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java @@ -356,7 +356,9 @@ public class MyLocationView extends View { foregroundDrawable.draw(canvas); } } else if (foregroundBearingDrawable != null && foregroundBounds != null) { - if (myBearingTrackingMode == MyBearingTracking.GPS || compassListener.isSensorAvailable()) { + if (myBearingTrackingMode == MyBearingTracking.GPS + || myBearingTrackingMode == MyBearingTracking.GPS_NORTH_FACING + || compassListener.isSensorAvailable()) { foregroundBearingDrawable.draw(canvas); } else { // We are tracking MyBearingTracking.COMPASS, but sensor is not available. @@ -383,7 +385,8 @@ public class MyLocationView extends View { public void setBearing(double bearing) { this.bearing = bearing; if (myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { - if (myBearingTrackingMode == MyBearingTracking.GPS) { + if (myBearingTrackingMode == MyBearingTracking.GPS + || myBearingTrackingMode == MyBearingTracking.GPS_NORTH_FACING) { if (location != null) { setCompass(location.getBearing() - bearing); } @@ -519,7 +522,8 @@ public class MyLocationView extends View { } private void toggleGps(boolean enableGps) { - toggleGps(enableGps, mapboxMap != null && mapboxMap.getTrackingSettings().isCustomLocationSource()); + toggleGps(enableGps, mapboxMap != null + && mapboxMap.getTrackingSettings().isCustomLocationSource()); } /** @@ -580,7 +584,8 @@ public class MyLocationView extends View { this.location = location; myLocationBehavior.updateLatLng(location); - if (mapboxMap != null && myBearingTrackingMode == MyBearingTracking.GPS + if (mapboxMap != null && (myBearingTrackingMode == MyBearingTracking.GPS + || myBearingTrackingMode == MyBearingTracking.GPS_NORTH_FACING) && myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { setBearing(mapboxMap.getCameraPosition().bearing); } @@ -616,7 +621,8 @@ public class MyLocationView extends View { compassListener.onResume(); } else { compassListener.onPause(); - if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) { + if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW + && myBearingTrackingMode == MyBearingTracking.GPS) { // always face north setCompass(0); } else { @@ -1017,6 +1023,13 @@ public class MyLocationView extends View { setCompass(0, COMPASS_UPDATE_RATE_MS); } + if (myBearingTrackingMode == MyBearingTracking.GPS_NORTH_FACING) { + builder.bearing(0); + if (location.hasBearing()) { + setCompass(location.getBearing(), COMPASS_UPDATE_RATE_MS); + } + } + // accuracy updateAccuracy(location); From c9ef84316ac3336f86de1473ed0b17fb33fc46d6 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Wed, 31 Jan 2018 13:18:49 +0100 Subject: [PATCH 014/264] [android] - update changelog for 5.4.0 --- platform/android/CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index fa212b86a..88ce0cf69 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,6 +2,12 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. +## 5.4.0 - January 30, 2018 + - Blacklist Adreno 2xx GPU for VAO support [#11047](https://github.com/mapbox/mapbox-gl-native/pull/11047) + - Bearing tracking mode GPS_NORTH_FACING [#11095](https://github.com/mapbox/mapbox-gl-native/pull/11095) + - Disable logging missing location permissons [#11084](https://github.com/mapbox/mapbox-gl-native/pull/11084) + - Create offline handler using the main thread looper [#11021](https://github.com/mapbox/mapbox-gl-native/pull/11021) + ## 6.0.0-beta.1 - January 26, 2018 - Binding integration for expressions [#10654](https://github.com/mapbox/mapbox-gl-native/pull/10654) - CustomGeometrySource [#9983](https://github.com/mapbox/mapbox-gl-native/pull/9983) From afdb2916537f648f17122fa2c96f8b88759d28cc Mon Sep 17 00:00:00 2001 From: Tobrun Date: Wed, 31 Jan 2018 14:30:25 +0100 Subject: [PATCH 015/264] [android] - blacklist flaky instrumentation tests --- circle.yml | 15 ++------------- .../android/scripts/exclude-activity-gen.json | 7 ++++++- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/circle.yml b/circle.yml index 1374a541e..c40b6c9c1 100644 --- a/circle.yml +++ b/circle.yml @@ -339,21 +339,10 @@ jobs: shell: /bin/bash -euo pipefail command: | gcloud firebase test android models list - (gcloud firebase test android run --type instrumentation \ + gcloud firebase test android run --type instrumentation \ --app platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/debug/MapboxGLAndroidSDKTestApp-debug.apk \ --test platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/androidTest/debug/MapboxGLAndroidSDKTestApp-debug-androidTest.apk \ - --device-ids sailfish --os-version-ids 26 --locales en --orientations portrait --timeout 20m \ - 2>&1 | tee firebase.log) || EXIT_CODE=$? - - FIREBASE_TEST_BUCKET=$(sed -n 's|^.*\[https://console.developers.google.com/storage/browser/\([^]]*\).*|gs://\1|p' firebase.log) - echo "Downloading from: ${FIREBASE_TEST_BUCKET}" - gsutil -m cp -n -R -Z "$FIREBASE_TEST_BUCKET*" platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/debug - - echo "Try running ndk-stack on downloaded logcat to symbolicate the stacktraces:" - find platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/debug -type f -name "logcat" -print0 | \ - xargs -0 -I '{}' ${ANDROID_NDK_HOME}/ndk-stack -sym build/android-arm-v7/Debug -dump {} - - exit ${EXIT_CODE:-0} + --device-ids sailfish --os-version-ids 26 --locales en --orientations portrait --timeout 20m - store_artifacts: path: platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/debug destination: . diff --git a/platform/android/scripts/exclude-activity-gen.json b/platform/android/scripts/exclude-activity-gen.json index f05001c6a..c1a6b5bb4 100644 --- a/platform/android/scripts/exclude-activity-gen.json +++ b/platform/android/scripts/exclude-activity-gen.json @@ -27,5 +27,10 @@ "QueryRenderedFeaturesBoxHighlightActivity", "MultiMapActivity", "MapInDialogActivity", - "SimpleMapActivity" + "SimpleMapActivity", + "ManualZoomActivity", + "MaxMinZoomActivity", + "ScrollByActivity", + "ZoomFunctionSymbolLayerActivity", + "SymbolGeneratorActivity" ] \ No newline at end of file From 1d48a7d2ff33cfe239ffa6822062085b70397963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Tue, 6 Feb 2018 22:03:26 -0800 Subject: [PATCH 016/264] [ios] Removed byte order mark --- platform/ios/framework/Settings.bundle/ca.lproj/Root.strings | 2 +- platform/ios/framework/Settings.bundle/de.lproj/Root.strings | 2 +- platform/ios/framework/Settings.bundle/es.lproj/Root.strings | 2 +- platform/ios/framework/Settings.bundle/fi.lproj/Root.strings | 2 +- platform/ios/framework/Settings.bundle/fr.lproj/Root.strings | 2 +- platform/ios/framework/Settings.bundle/lt.lproj/Root.strings | 2 +- platform/ios/framework/Settings.bundle/nl.lproj/Root.strings | 2 +- platform/ios/framework/Settings.bundle/pl.lproj/Root.strings | 2 +- platform/ios/framework/Settings.bundle/pt-BR.lproj/Root.strings | 2 +- platform/ios/framework/Settings.bundle/sv.lproj/Root.strings | 2 +- platform/ios/framework/Settings.bundle/uk.lproj/Root.strings | 2 +- platform/ios/framework/Settings.bundle/vi.lproj/Root.strings | 2 +- .../ios/framework/Settings.bundle/zh-Hans.lproj/Root.strings | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/platform/ios/framework/Settings.bundle/ca.lproj/Root.strings b/platform/ios/framework/Settings.bundle/ca.lproj/Root.strings index 94e833558..3f6104a07 100644 --- a/platform/ios/framework/Settings.bundle/ca.lproj/Root.strings +++ b/platform/ios/framework/Settings.bundle/ca.lproj/Root.strings @@ -1,3 +1,3 @@ -"TELEMETRY_GROUP_TITLE" = "Configuració de privacitat"; +"TELEMETRY_GROUP_TITLE" = "Configuració de privacitat"; "TELEMETRY_SWITCH_TITLE" = "Telemetria Mapbox"; "TELEMETRY_GROUP_FOOTER" = "Aquest ajust permet que l’aplicació comparteixi dades anònimes de localització i ús amb Mapbox."; diff --git a/platform/ios/framework/Settings.bundle/de.lproj/Root.strings b/platform/ios/framework/Settings.bundle/de.lproj/Root.strings index c57189d4a..30bd2252c 100644 --- a/platform/ios/framework/Settings.bundle/de.lproj/Root.strings +++ b/platform/ios/framework/Settings.bundle/de.lproj/Root.strings @@ -1,3 +1,3 @@ -"TELEMETRY_GROUP_TITLE" = "Privatsphäre-Einstellungen"; +"TELEMETRY_GROUP_TITLE" = "Privatsphäre-Einstellungen"; "TELEMETRY_SWITCH_TITLE" = "Mapbox-Telemetrie"; "TELEMETRY_GROUP_FOOTER" = "Diese Einstellung erlaubt der Applikation, anonymisierte Orts- und Nutzungsdaten an Mapbox zu senden."; diff --git a/platform/ios/framework/Settings.bundle/es.lproj/Root.strings b/platform/ios/framework/Settings.bundle/es.lproj/Root.strings index bb0c52dcd..48da35818 100644 --- a/platform/ios/framework/Settings.bundle/es.lproj/Root.strings +++ b/platform/ios/framework/Settings.bundle/es.lproj/Root.strings @@ -1,3 +1,3 @@ -"TELEMETRY_GROUP_TITLE" = "Ajustes de privacidad"; +"TELEMETRY_GROUP_TITLE" = "Ajustes de privacidad"; "TELEMETRY_SWITCH_TITLE" = "Telemetría Mapbox"; "TELEMETRY_GROUP_FOOTER" = "Esta configuración permite que la aplicación comparta datos anónimos de ubicación y uso con Mapbox."; diff --git a/platform/ios/framework/Settings.bundle/fi.lproj/Root.strings b/platform/ios/framework/Settings.bundle/fi.lproj/Root.strings index e0c2e50d0..b12a017e8 100644 --- a/platform/ios/framework/Settings.bundle/fi.lproj/Root.strings +++ b/platform/ios/framework/Settings.bundle/fi.lproj/Root.strings @@ -1,3 +1,3 @@ -"TELEMETRY_GROUP_TITLE" = "Yksityisyysasetukset"; +"TELEMETRY_GROUP_TITLE" = "Yksityisyysasetukset"; "TELEMETRY_SWITCH_TITLE" = "Mapbox-telemetria"; "TELEMETRY_GROUP_FOOTER" = "Tämä asetus antaa sovellukselle luvan jakaa anonymisoituja sijainti- ja käyttötietoja Mapboxille."; diff --git a/platform/ios/framework/Settings.bundle/fr.lproj/Root.strings b/platform/ios/framework/Settings.bundle/fr.lproj/Root.strings index c386d9846..f00e8e2fe 100644 --- a/platform/ios/framework/Settings.bundle/fr.lproj/Root.strings +++ b/platform/ios/framework/Settings.bundle/fr.lproj/Root.strings @@ -1,3 +1,3 @@ -"TELEMETRY_GROUP_TITLE" = "Paramètres de confidentialité"; +"TELEMETRY_GROUP_TITLE" = "Paramètres de confidentialité"; "TELEMETRY_SWITCH_TITLE" = "Télémétrie Mapbox"; "TELEMETRY_GROUP_FOOTER" = "Cette option permet à l’application de partager des données de localisation et d’utilisation anonymes avec Mapbox."; diff --git a/platform/ios/framework/Settings.bundle/lt.lproj/Root.strings b/platform/ios/framework/Settings.bundle/lt.lproj/Root.strings index 832b16608..5ae715439 100644 --- a/platform/ios/framework/Settings.bundle/lt.lproj/Root.strings +++ b/platform/ios/framework/Settings.bundle/lt.lproj/Root.strings @@ -1,3 +1,3 @@ -"TELEMETRY_GROUP_TITLE" = "Privatumo nustatymai"; +"TELEMETRY_GROUP_TITLE" = "Privatumo nustatymai"; "TELEMETRY_SWITCH_TITLE" = "Mapbox Telemetrija"; "TELEMETRY_GROUP_FOOTER" = "Šis nustatymas leidžia programėlei dalintis su Mapbox anonimizuota lokacija bei naudojimosi duomenimis."; diff --git a/platform/ios/framework/Settings.bundle/nl.lproj/Root.strings b/platform/ios/framework/Settings.bundle/nl.lproj/Root.strings index ea73b0ba3..9f0900234 100644 --- a/platform/ios/framework/Settings.bundle/nl.lproj/Root.strings +++ b/platform/ios/framework/Settings.bundle/nl.lproj/Root.strings @@ -1,3 +1,3 @@ -"TELEMETRY_GROUP_TITLE" = "Privacy Instellingen"; +"TELEMETRY_GROUP_TITLE" = "Privacy Instellingen"; "TELEMETRY_SWITCH_TITLE" = "Mapbox Telemetrie"; "TELEMETRY_GROUP_FOOTER" = "Deze instelling laat toe om anonieme locatie en gebruiksgegevens te delen met Mapbox."; diff --git a/platform/ios/framework/Settings.bundle/pl.lproj/Root.strings b/platform/ios/framework/Settings.bundle/pl.lproj/Root.strings index 4cf2c7138..f273a1529 100644 --- a/platform/ios/framework/Settings.bundle/pl.lproj/Root.strings +++ b/platform/ios/framework/Settings.bundle/pl.lproj/Root.strings @@ -1,3 +1,3 @@ -"TELEMETRY_GROUP_TITLE" = "Ustawienia prywatności"; +"TELEMETRY_GROUP_TITLE" = "Ustawienia prywatności"; "TELEMETRY_SWITCH_TITLE" = "Mapbox Telemetria"; "TELEMETRY_GROUP_FOOTER" = "Ta opcja pozwala aplikacji na anonimowe wysyłanie lokalizacji i danych do Mapbox."; diff --git a/platform/ios/framework/Settings.bundle/pt-BR.lproj/Root.strings b/platform/ios/framework/Settings.bundle/pt-BR.lproj/Root.strings index 5b81fb66e..f96fadd20 100644 --- a/platform/ios/framework/Settings.bundle/pt-BR.lproj/Root.strings +++ b/platform/ios/framework/Settings.bundle/pt-BR.lproj/Root.strings @@ -1,3 +1,3 @@ -"TELEMETRY_GROUP_TITLE" = "Configurações de privacidade"; +"TELEMETRY_GROUP_TITLE" = "Configurações de privacidade"; "TELEMETRY_SWITCH_TITLE" = "Telemetria do Mapbox"; "TELEMETRY_GROUP_FOOTER" = "Essa configuração permite que o aplicativo compartilhe dados de localização e uso anônimos com o Mapbox."; diff --git a/platform/ios/framework/Settings.bundle/sv.lproj/Root.strings b/platform/ios/framework/Settings.bundle/sv.lproj/Root.strings index f8324c14c..e771af505 100644 --- a/platform/ios/framework/Settings.bundle/sv.lproj/Root.strings +++ b/platform/ios/framework/Settings.bundle/sv.lproj/Root.strings @@ -1,3 +1,3 @@ -"TELEMETRY_GROUP_TITLE" = "Sekretessinställningar"; +"TELEMETRY_GROUP_TITLE" = "Sekretessinställningar"; "TELEMETRY_SWITCH_TITLE" = "Mapbox Telemetri"; "TELEMETRY_GROUP_FOOTER" = "Denna inställning tillåter applikationen att dela anonymiserad plats och användningsdata med Mapbox."; diff --git a/platform/ios/framework/Settings.bundle/uk.lproj/Root.strings b/platform/ios/framework/Settings.bundle/uk.lproj/Root.strings index 5d2bfd648..b1615849f 100644 --- a/platform/ios/framework/Settings.bundle/uk.lproj/Root.strings +++ b/platform/ios/framework/Settings.bundle/uk.lproj/Root.strings @@ -1,3 +1,3 @@ -"TELEMETRY_GROUP_TITLE" = "Налаштування конфіденційності"; +"TELEMETRY_GROUP_TITLE" = "Налаштування конфіденційності"; "TELEMETRY_SWITCH_TITLE" = "Телеметрія Mapbox"; "TELEMETRY_GROUP_FOOTER" = "Ці налаштування дозволяють застосунку надсилати анонімізовані дані про місце знаходження та використання даних до Mapbox."; diff --git a/platform/ios/framework/Settings.bundle/vi.lproj/Root.strings b/platform/ios/framework/Settings.bundle/vi.lproj/Root.strings index e156d9937..29060d00a 100644 --- a/platform/ios/framework/Settings.bundle/vi.lproj/Root.strings +++ b/platform/ios/framework/Settings.bundle/vi.lproj/Root.strings @@ -1,3 +1,3 @@ -"TELEMETRY_GROUP_TITLE" = "Thiết lập Quyền riêng tư"; +"TELEMETRY_GROUP_TITLE" = "Thiết lập Quyền riêng tư"; "TELEMETRY_SWITCH_TITLE" = "Trình viễn trắc Mapbox"; "TELEMETRY_GROUP_FOOTER" = "Tùy chọn này cho phép ứng dụng gửi cho Mapbox các vị trí và dữ liệu sử dụng được vô danh hóa."; diff --git a/platform/ios/framework/Settings.bundle/zh-Hans.lproj/Root.strings b/platform/ios/framework/Settings.bundle/zh-Hans.lproj/Root.strings index 3cfb1a4ba..b9e1a9832 100644 --- a/platform/ios/framework/Settings.bundle/zh-Hans.lproj/Root.strings +++ b/platform/ios/framework/Settings.bundle/zh-Hans.lproj/Root.strings @@ -1,3 +1,3 @@ -"TELEMETRY_GROUP_TITLE" = "隐私设置"; +"TELEMETRY_GROUP_TITLE" = "隐私设置"; "TELEMETRY_SWITCH_TITLE" = "Mapbox传感数据"; "TELEMETRY_GROUP_FOOTER" = "此设置允许应用将用户位置和数据以匿名的方式分享给Mapbox。"; From 6757456e1604c69035246756a4ac28b5c8b0460f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Tue, 6 Feb 2018 22:00:17 -0800 Subject: [PATCH 017/264] [android, ios, macos] Updated Russian translations --- .../src/main/res/values-ru/strings.xml | 17 +++--- .../Settings.bundle/ru.lproj/Root.strings | 6 +- .../resources/ru.lproj/Localizable.strings | 58 ++++++++++++------ .../ru.lproj/Localizable.stringsdict | 60 +++++++++++++------ .../macos/sdk/ru.lproj/Localizable.strings | 6 +- 5 files changed, 98 insertions(+), 49 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values-ru/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-ru/strings.xml index 39880d56b..a27412525 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/values-ru/strings.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-ru/strings.xml @@ -1,15 +1,16 @@ - Компас. Активируйте, чтобы повернуть карту на Север. - Иконка атрибутов. Активируйте, чтобы показать диалог. - Местоположение. Отображает ваше местоположение на карте. - Отображает карту, созданную при помощи Mapbox. Протисните при помощи двух пальцев. Приблизьте, соединением пальцев. - Mapbox Android SDK + Компас. Активируйте, чтобы развернуть карту на север. + Значок атрибутов. Активируйте, чтобы показать диалог. + Местоположение. Отображает вашу позицию на карте. + Отображает карту, созданную при помощи Mapbox. Пролистывайте двумя пальцами. Меняйте масштаб сведением пальцев. + Mapbox Maps SDK для Android Сделать карты Mapbox лучше - Вы помогаете сделать карты OpenStreetMap и Mapbox лучше путем предоставления анонимных данных об использовании. + Вы помогаете улучшать карты OpenStreetMap и Mapbox, предоставляя обезличенные данные об использовании. Согласен Не согласен Дополнительная информация - Запрошенный OfflineRegionDefinition не входит в допустимые границы: %s - + На устройстве нет веб-браузера, нельзя показать веб-страницу. + Запрошенный OfflineRegionDefinition не входит в допустимые границы: %s + Настройки телеметрии diff --git a/platform/ios/framework/Settings.bundle/ru.lproj/Root.strings b/platform/ios/framework/Settings.bundle/ru.lproj/Root.strings index 2883ec4b8..3e37d6412 100644 --- a/platform/ios/framework/Settings.bundle/ru.lproj/Root.strings +++ b/platform/ios/framework/Settings.bundle/ru.lproj/Root.strings @@ -1,3 +1,3 @@ -"TELEMETRY_GROUP_TITLE" = "Настройки приватности"; -"TELEMETRY_SWITCH_TITLE" = "Mapbox Телеметрия"; -"TELEMETRY_GROUP_FOOTER" = "Эта настройка разрешает приложению отправлять анонимную позицию и данные об использовании в Mapbox"; +"TELEMETRY_GROUP_TITLE" = "Настройки приватности"; +"TELEMETRY_SWITCH_TITLE" = "Телеметрия Mapbox"; +"TELEMETRY_GROUP_FOOTER" = "Эта настройка разрешает приложению отправлять обезличенные данные об использовании и местоположении в Mapbox."; diff --git a/platform/ios/resources/ru.lproj/Localizable.strings b/platform/ios/resources/ru.lproj/Localizable.strings index 6d5ea9025..b6ceffd52 100644 --- a/platform/ios/resources/ru.lproj/Localizable.strings +++ b/platform/ios/resources/ru.lproj/Localizable.strings @@ -1,44 +1,47 @@ /* Accessibility hint */ -"ANNOTATION_A11Y_HINT" = "Показать больше информации"; +"ANNOTATION_A11Y_HINT" = "Дополнительная информация"; /* No comment provided by engineer. */ -"API_CLIENT_400_DESC" = "The session data task failed. Original request was:%@"; +"API_CLIENT_400_DESC" = "Во время обмена данными произошла ошибка. Оригинал запроса: %@"; /* No comment provided by engineer. */ -"API_CLIENT_400_REASON" = "The status code was %ld"; +"API_CLIENT_400_REASON" = "Код ответа %ld"; /* No comment provided by engineer. */ "CANCEL" = "Отмена"; /* Accessibility hint for closing the selected annotation’s callout view and returning to the map */ -"CLOSE_CALLOUT_A11Y_HINT" = "Вернуться на карту"; +"CLOSE_CALLOUT_A11Y_HINT" = "Вернуться к карте"; /* Accessibility hint */ -"COMPASS_A11Y_HINT" = "Повернуть карту на север"; +"COMPASS_A11Y_HINT" = "Развернуть карту на север"; /* Accessibility label */ "COMPASS_A11Y_LABEL" = "Компас"; /* Compass abbreviation for north */ -"COMPASS_NORTH" = "N"; +"COMPASS_NORTH" = "С"; /* Instructions in Interface Builder designable; {key}, {plist file name} */ -"DESIGNABLE" = "Для отображения здесь карт Mapbox задайте %1$@ к вашему токену доступа в %2$@\n\nПодробные инструкции см.:"; +"DESIGNABLE" = "Для отображения здесь карт Mapbox, задайте %1$@ для вашего токена доступа в %2$@\n\nПодробные инструкции:"; /* Setup documentation URL display string; keep as short as possible */ "FIRST_STEPS_URL" = "mapbox.com/help/first-steps-ios-sdk"; /* Accessibility hint */ -"INFO_A11Y_HINT" = "Показать авторство, форму обратной связи и многое другое"; +"INFO_A11Y_HINT" = "Показать благодарности, форму отправки отзыва и другое"; /* Accessibility label */ "INFO_A11Y_LABEL" = "Об этой карте"; +/* List separator */ +"LIST_SEPARATOR" = ", "; + /* User-friendly error description */ "LOAD_MAP_FAILED_DESC" = "Не удалось загрузить карту из-за неизвестной ошибки."; /* User-friendly error description */ -"LOAD_STYLE_FAILED_DESC" = "Не удалось загрузить карту так как невозможно загрузить стиль."; +"LOAD_STYLE_FAILED_DESC" = "Не удалось загрузить карту из-за ошибки загрузки стиля."; /* Accessibility label */ "LOGO_A11Y_LABEL" = "Mapbox"; @@ -46,23 +49,44 @@ /* Accessibility label */ "MAP_A11Y_LABEL" = "Карта"; -/* Map accessibility value */ -"MAP_A11Y_VALUE" = "Масштаб %1$dx\n%2$ld аннотации(й) видны"; +/* Map accessibility value; {number of visible annotations} */ +"MAP_A11Y_VALUE_ANNOTATIONS" = "Показано %ld аннотаций."; + +/* Map accessibility value; {list of visible places} */ +"MAP_A11Y_VALUE_PLACES" = "Показано мест: %@."; + +/* Map accessibility value; {number of visible roads} */ +"MAP_A11Y_VALUE_ROADS" = "Показано %ld дорог."; + +/* Map accessibility value; {zoom level} */ +"MAP_A11Y_VALUE_ZOOM" = "Масштаб %dx."; /* User-friendly error description */ "PARSE_STYLE_FAILED_DESC" = "Не удалось загрузить карту из-за ошибки в стиле."; +/* String format for accessibility value for road feature; {starting compass direction}, {ending compass direction} */ +"ROAD_DIRECTION_A11Y_FMT" = "%1$@ на %2$@"; + +/* Accessibility value indicating that a road is a divided road (dual carriageway) */ +"ROAD_DIVIDED_A11Y_VALUE" = "Двусторонняя дорога"; + +/* Accessibility value indicating that a road is a one-way road */ +"ROAD_ONEWAY_A11Y_VALUE" = "Односторонняя дорога"; + +/* String format for accessibility value for road feature; {route number} */ +"ROAD_REF_A11Y_FMT" = "Трасса %@"; + /* Action sheet title */ -"SDK_NAME" = "Mapbox Maps SDK for iOS"; +"SDK_NAME" = "Mapbox Maps SDK для iOS"; /* Developer-only SDK update notification; {latest version, in format x.x.x} */ -"SDK_UPDATE_AVAILABLE" = "Mapbox Maps SDK for iOS версии %@теперь доступен."; +"SDK_UPDATE_AVAILABLE" = "Доступна версия Mapbox Maps SDK %@ для iOS:"; /* User-friendly error description */ -"STYLE_NOT_FOUND_DESC" = "Не удалось загрузить карту так как стиль не найден или несовместим."; +"STYLE_NOT_FOUND_DESC" = "Не удалось загрузить карту, так как стиль не найден или несовместим."; /* Telemetry prompt message */ -"TELEMETRY_DISABLED_MSG" = "Вы можете помочь сделать карты OpenStreetMap и Mapbox лучше путем предоставления анонимных данных об использовании."; +"TELEMETRY_DISABLED_MSG" = "Вы поможете улучшить карты OpenStreetMap и Mapbox, предоставляя обезличенные данные об использовании."; /* Telemetry prompt button */ "TELEMETRY_DISABLED_OFF" = "Не участвовать"; @@ -71,7 +95,7 @@ "TELEMETRY_DISABLED_ON" = "Участвовать"; /* Telemetry prompt message */ -"TELEMETRY_ENABLED_MSG" = "Вы помогаете сделать карты OpenStreetMap и Mapbox лучше путем предоставления анонимных данных об использовании."; +"TELEMETRY_ENABLED_MSG" = "Вы помогаете улучшать карты OpenStreetMap и Mapbox, предоставляя обезличенные данные об использовании."; /* Telemetry prompt button */ "TELEMETRY_ENABLED_OFF" = "Прекратить участие"; @@ -83,7 +107,7 @@ "TELEMETRY_MORE" = "Узнать больше"; /* Action in attribution sheet */ -"TELEMETRY_NAME" = "Mapbox телеметрия"; +"TELEMETRY_NAME" = "Телеметрия Mapbox"; /* Telemetry prompt title */ "TELEMETRY_TITLE" = "Сделать карты Mapbox лучше"; diff --git a/platform/ios/resources/ru.lproj/Localizable.stringsdict b/platform/ios/resources/ru.lproj/Localizable.stringsdict index 81877703b..fabd55778 100644 --- a/platform/ios/resources/ru.lproj/Localizable.stringsdict +++ b/platform/ios/resources/ru.lproj/Localizable.stringsdict @@ -2,11 +2,50 @@ - MAP_A11Y_VALUE + MAP_A11Y_VALUE_ANNOTATIONS NSStringLocalizedFormatKey - %#@level@ -%#@count@ + %#@count@ + count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + Показана %d аннотация + few + Показано %d аннотации + many + Показано %d аннотаций + other + Показано %d аннотаций + + + MAP_A11Y_VALUE_ROADS + + NSStringLocalizedFormatKey + %#@count@ + count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + Показана %d дорога + few + Показано %d дороги + many + Показано %d дорог + other + Показано %d дорог + + + MAP_A11Y_VALUE_ZOOM + + NSStringLocalizedFormatKey + %#@level@ level NSStringFormatSpecTypeKey @@ -22,21 +61,6 @@ other Масштаб %dx - count - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - ld - one - %d аннотация видны - few - %d аннотации видны - many - %d аннотаций видны - other - %d аннотации видны - diff --git a/platform/macos/sdk/ru.lproj/Localizable.strings b/platform/macos/sdk/ru.lproj/Localizable.strings index 067e05b14..8554c9ebd 100644 --- a/platform/macos/sdk/ru.lproj/Localizable.strings +++ b/platform/macos/sdk/ru.lproj/Localizable.strings @@ -1,8 +1,8 @@ -/* User-friendly error description */ -"LOAD_MAP_FAILED_DESC" = "Не удалось загрузит карту из-за неизвестной ошибки."; +/* User-friendly error description */ +"LOAD_MAP_FAILED_DESC" = "Не удалось загрузить карту из-за неизвестной ошибки."; /* User-friendly error description */ -"LOAD_STYLE_FAILED_DESC" = "Не удалось загрузить карту так как невозможно загрузить стиль."; +"LOAD_STYLE_FAILED_DESC" = "Не удалось загрузить карту из-за ошибки загрузки стиля."; /* Accessibility title */ "MAP_A11Y_TITLE" = "Mapbox"; From bffc834ff17748ebc6e7ad298a94a520bbd887cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Tue, 6 Feb 2018 22:03:58 -0800 Subject: [PATCH 018/264] [ios] Updated Swedish translations --- .../sv.lproj/Localizable.stringsdict | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/platform/ios/resources/sv.lproj/Localizable.stringsdict b/platform/ios/resources/sv.lproj/Localizable.stringsdict index 90115bd80..5f44d19b3 100644 --- a/platform/ios/resources/sv.lproj/Localizable.stringsdict +++ b/platform/ios/resources/sv.lproj/Localizable.stringsdict @@ -2,22 +2,10 @@ - MAP_A11Y_VALUE + MAP_A11Y_VALUE_ANNOTATIONS NSStringLocalizedFormatKey - %#@level@ -%#@count@ - level - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - d - one - Zoom %dx - other - Zoom %dx - + %#@count@ count NSStringFormatSpecTypeKey @@ -30,5 +18,37 @@ %d annoteringar synliga + MAP_A11Y_VALUE_ROADS + + NSStringLocalizedFormatKey + %#@count@ + count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + %d väg synlig + other + %d vägar synliga + + + MAP_A11Y_VALUE_ZOOM + + NSStringLocalizedFormatKey + %#@level@ + level + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + Zooma %dx + other + Zooma %dx + + From 6e7b839fe057da3a8478f9ff033ebc49c75a146e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Tue, 6 Feb 2018 22:04:30 -0800 Subject: [PATCH 019/264] [macos] Updated Hungarian translations --- platform/macos/sdk/hu.lproj/Localizable.strings | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/macos/sdk/hu.lproj/Localizable.strings b/platform/macos/sdk/hu.lproj/Localizable.strings index b3724190c..062b1f5ce 100644 --- a/platform/macos/sdk/hu.lproj/Localizable.strings +++ b/platform/macos/sdk/hu.lproj/Localizable.strings @@ -1,8 +1,8 @@ -/* User-friendly error description */ +/* User-friendly error description */ "LOAD_MAP_FAILED_DESC" = "Egy ismeretlen hiba miatt nem sikerült betölteni a térképet."; /* User-friendly error description */ -"LOAD_STYLE_FAILED_DESC" = "Nem sikerült betölteni a térképet, mert a stílus nem tölthető be."; +"LOAD_STYLE_FAILED_DESC" = "Nem sikerült betölteni a térképet, mert a stílust nem lehetett betölteni."; /* Accessibility title */ "MAP_A11Y_TITLE" = "Mapbox"; From e6d6a231e861f3e1e1f8c193bb17196dec9d05eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Tue, 6 Feb 2018 22:07:12 -0800 Subject: [PATCH 020/264] [ios, macos] Added Danish localization --- .../resources/da.lproj/Foundation.strings | 297 ++++++++++++++++++ .../resources/da.lproj/Foundation.stringsdict | 54 ++++ platform/ios/CHANGELOG.md | 2 +- platform/ios/app/da.lproj/Localizable.strings | 0 .../Settings.bundle/da.lproj/Root.strings | 3 + platform/ios/ios.xcodeproj/project.pbxproj | 13 + .../resources/da.lproj/Localizable.strings | 117 +++++++ .../da.lproj/Localizable.stringsdict | 54 ++++ platform/macos/CHANGELOG.md | 2 +- .../macos/app/da.lproj/Localizable.strings | 0 .../macos/macos.xcodeproj/project.pbxproj | 9 + .../macos/sdk/da.lproj/Localizable.strings | 27 ++ 12 files changed, 576 insertions(+), 2 deletions(-) create mode 100644 platform/darwin/resources/da.lproj/Foundation.strings create mode 100644 platform/darwin/resources/da.lproj/Foundation.stringsdict create mode 100644 platform/ios/app/da.lproj/Localizable.strings create mode 100644 platform/ios/framework/Settings.bundle/da.lproj/Root.strings create mode 100644 platform/ios/resources/da.lproj/Localizable.strings create mode 100644 platform/ios/resources/da.lproj/Localizable.stringsdict create mode 100644 platform/macos/app/da.lproj/Localizable.strings create mode 100644 platform/macos/sdk/da.lproj/Localizable.strings diff --git a/platform/darwin/resources/da.lproj/Foundation.strings b/platform/darwin/resources/da.lproj/Foundation.strings new file mode 100644 index 000000000..d8d18f453 --- /dev/null +++ b/platform/darwin/resources/da.lproj/Foundation.strings @@ -0,0 +1,297 @@ +/* Clock position format, long: {hours} o’clock */ +"CLOCK_FMT_LONG" = "%@ klokken"; + +/* Clock position format, medium: {hours} o’clock */ +"CLOCK_FMT_MEDIUM" = "%@ klokken"; + +/* Clock position format, short: {hours}:00 */ +"CLOCK_FMT_SHORT" = "%@:00"; + +/* East, long */ +"COMPASS_E_LONG" = "øst"; + +/* East, short */ +"COMPASS_E_SHORT" = "Ø"; + +/* East by north, long */ +"COMPASS_EbN_LONG" = "øst via nord"; + +/* East by north, short */ +"COMPASS_EbN_SHORT" = "ØvN"; + +/* East by south, long */ +"COMPASS_EbS_LONG" = "øst via syd"; + +/* East by south, short */ +"COMPASS_EbS_SHORT" = "ØvS"; + +/* East-northeast, long */ +"COMPASS_ENE_LONG" = "øst-nordøst"; + +/* East-northeast, short */ +"COMPASS_ENE_SHORT" = "ØNØ"; + +/* East-southeast, long */ +"COMPASS_ESE_LONG" = "øst-sydøst"; + +/* East-southeast, short */ +"COMPASS_ESE_SHORT" = "ØSØ"; + +/* North, long */ +"COMPASS_N_LONG" = "nord"; + +/* North, short */ +"COMPASS_N_SHORT" = "N"; + +/* North by east, long */ +"COMPASS_NbE_LONG" = "nord via øst"; + +/* North by east, short */ +"COMPASS_NbE_SHORT" = "NvØ"; + +/* North by west, long */ +"COMPASS_NbW_LONG" = "nord via vest"; + +/* North by west, short */ +"COMPASS_NbW_SHORT" = "NvV"; + +/* Northeast, long */ +"COMPASS_NE_LONG" = "nordøst"; + +/* Northeast, short */ +"COMPASS_NE_SHORT" = "NØ"; + +/* Northeast by east, long */ +"COMPASS_NEbE_LONG" = "nordøst via øst"; + +/* Northeast by east, short */ +"COMPASS_NEbE_SHORT" = "NØvØ"; + +/* Northeast by north, long */ +"COMPASS_NEbN_LONG" = "nordøst via nord"; + +/* Northeast by north, short */ +"COMPASS_NEbN_SHORT" = "NØvN"; + +/* North-northeast, long */ +"COMPASS_NNE_LONG" = "nord-nordøst"; + +/* North-northeast, short */ +"COMPASS_NNE_SHORT" = "NNØ"; + +/* North-northwest, long */ +"COMPASS_NNW_LONG" = "nord-nordvest"; + +/* North-northwest, short */ +"COMPASS_NNW_SHORT" = "NNV"; + +/* Northwest, long */ +"COMPASS_NW_LONG" = "nordvest"; + +/* Northwest, short */ +"COMPASS_NW_SHORT" = "NV"; + +/* Northwest by north, long */ +"COMPASS_NWbN_LONG" = "nordvest via nord"; + +/* Northwest by north, short */ +"COMPASS_NWbN_SHORT" = "NVvN"; + +/* Northwest by west, long */ +"COMPASS_NWbW_LONG" = "nordvest via vest"; + +/* Northwest by west, short */ +"COMPASS_NWbW_SHORT" = "NVvW"; + +/* South, long */ +"COMPASS_S_LONG" = "syd"; + +/* South, short */ +"COMPASS_S_SHORT" = "S"; + +/* South by east, long */ +"COMPASS_SbE_LONG" = "syd via øst"; + +/* South by east, short */ +"COMPASS_SbE_SHORT" = "SvØ"; + +/* South by west, long */ +"COMPASS_SbW_LONG" = "syd via vest"; + +/* South by west, short */ +"COMPASS_SbW_SHORT" = "SvV"; + +/* Southeast, long */ +"COMPASS_SE_LONG" = "sydøst"; + +/* Southeast, short */ +"COMPASS_SE_SHORT" = "SØ"; + +/* Southeast by east, long */ +"COMPASS_SEbE_LONG" = "sydøst via øst"; + +/* Southeast by east, short */ +"COMPASS_SEbE_SHORT" = "SØvØ"; + +/* Southeast by south, long */ +"COMPASS_SEbS_LONG" = "sydøst via syd"; + +/* Southeast by south, short */ +"COMPASS_SEbS_SHORT" = "SØvS"; + +/* South-southeast, long */ +"COMPASS_SSE_LONG" = "syd-sydøst"; + +/* South-southeast, short */ +"COMPASS_SSE_SHORT" = "SSØ"; + +/* South-southwest, long */ +"COMPASS_SSW_LONG" = "syd-sydvest"; + +/* South-southwest, short */ +"COMPASS_SSW_SHORT" = "SSV"; + +/* Southwest, long */ +"COMPASS_SW_LONG" = "sydvest"; + +/* Southwest, short */ +"COMPASS_SW_SHORT" = "SV"; + +/* Southwest by south, long */ +"COMPASS_SWbS_LONG" = "sydvest via syd"; + +/* Southwest by south, short */ +"COMPASS_SWbS_SHORT" = "SVvS"; + +/* Southwest by west, long */ +"COMPASS_SWbW_LONG" = "sydvest via vest"; + +/* Southwest by west, short */ +"COMPASS_SWbW_SHORT" = "SVvV"; + +/* West, long */ +"COMPASS_W_LONG" = "vest"; + +/* West, short */ +"COMPASS_W_SHORT" = "V"; + +/* West by north, long */ +"COMPASS_WbN_LONG" = "vest via nord"; + +/* West by north, short */ +"COMPASS_WbN_SHORT" = "VvN"; + +/* West by south, long */ +"COMPASS_WbS_LONG" = "vest via syd"; + +/* West by south, short */ +"COMPASS_WbS_SHORT" = "VvS"; + +/* West-northwest, long */ +"COMPASS_WNW_LONG" = "vest-nordvest"; + +/* West-northwest, short */ +"COMPASS_WNW_SHORT" = "VNV"; + +/* West-southwest, long */ +"COMPASS_WSW_LONG" = "vest-sydvest"; + +/* West-southwest, short */ +"COMPASS_WSW_SHORT" = "VSV"; + +/* Degrees format, long */ +"COORD_DEG_LONG" = "%d grad(er)"; + +/* Degrees format, medium: {degrees} */ +"COORD_DEG_MEDIUM" = "%d°"; + +/* Degrees format, short: {degrees} */ +"COORD_DEG_SHORT" = "%d°"; + +/* Coordinate format, long: {degrees}{minutes} */ +"COORD_DM_LONG" = "%1$@ og %2$@"; + +/* Coordinate format, medium: {degrees}{minutes} */ +"COORD_DM_MEDIUM" = "%1$@%2$@"; + +/* Coordinate format, short: {degrees}{minutes} */ +"COORD_DM_SHORT" = "%1$@%2$@"; + +/* Coordinate format, long: {degrees}{minutes}{seconds} */ +"COORD_DMS_LONG" = "%1$@, %2$@, og %3$@"; + +/* Coordinate format, medium: {degrees}{minutes}{seconds} */ +"COORD_DMS_MEDIUM" = "%1$@%2$@%3$@"; + +/* Coordinate format, short: {degrees}{minutes}{seconds} */ +"COORD_DMS_SHORT" = "%1$@%2$@%3$@"; + +/* East longitude format, long: {longitude} */ +"COORD_E_LONG" = "%@ øst"; + +/* East longitude format, medium: {longitude} */ +"COORD_E_MEDIUM" = "%@ øst"; + +/* East longitude format, short: {longitude} */ +"COORD_E_SHORT" = "%@Ø"; + +/* Coordinate pair format, long: {latitude}, {longitude} */ +"COORD_FMT_LONG" = "%1$@ via %2$@"; + +/* Coordinate pair format, medium: {latitude}, {longitude} */ +"COORD_FMT_MEDIUM" = "%1$@, %2$@"; + +/* Coordinate pair format, short: {latitude}, {longitude} */ +"COORD_FMT_SHORT" = "%1$@, %2$@"; + +/* Minutes format, long */ +"COORD_MIN_LONG" = "%d minut(ter)"; + +/* Minutes format, medium: {minutes} */ +"COORD_MIN_MEDIUM" = "%d′"; + +/* Minutes format, short: {minutes} */ +"COORD_MIN_SHORT" = "%d′"; + +/* North latitude format, long: {latitude} */ +"COORD_N_LONG" = "%@ nord"; + +/* North latitude format, medium: {latitude} */ +"COORD_N_MEDIUM" = "%@ nord"; + +/* North latitude format, short: {latitude} */ +"COORD_N_SHORT" = "%@N"; + +/* South latitude format, long: {latitude} */ +"COORD_S_LONG" = "%@ syd"; + +/* South latitude format, medium: {latitude} */ +"COORD_S_MEDIUM" = "%@ syd"; + +/* South latitude format, short: {latitude} */ +"COORD_S_SHORT" = "%@S"; + +/* Seconds format, long */ +"COORD_SEC_LONG" = "%d sekunde(r)"; + +/* Seconds format, medium: {seconds} */ +"COORD_SEC_MEDIUM" = "%d″"; + +/* Seconds format, short: {seconds} */ +"COORD_SEC_SHORT" = "%d″"; + +/* West longitude format, long: {longitude} */ +"COORD_W_LONG" = "%@ vest"; + +/* West longitude format, medium: {longitude} */ +"COORD_W_MEDIUM" = "%@ vest"; + +/* West longitude format, short: {longitude} */ +"COORD_W_SHORT" = "%@V"; + +/* OpenStreetMap full name attribution */ +"OSM_FULL_NAME" = "OpenStreetMap"; + +/* OpenStreetMap short name attribution */ +"OSM_SHORT_NAME" = "OSM"; + diff --git a/platform/darwin/resources/da.lproj/Foundation.stringsdict b/platform/darwin/resources/da.lproj/Foundation.stringsdict new file mode 100644 index 000000000..17983b8aa --- /dev/null +++ b/platform/darwin/resources/da.lproj/Foundation.stringsdict @@ -0,0 +1,54 @@ + + + + + COORD_DEG_LONG + + NSStringLocalizedFormatKey + %#@degrees@ + degrees + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d grad + other + %d grader + + + COORD_MIN_LONG + + NSStringLocalizedFormatKey + %#@minutes@ + minutes + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d minut + other + %d minutter + + + COORD_SEC_LONG + + NSStringLocalizedFormatKey + %#@seconds@ + seconds + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d sekund + other + %d sekunder + + + + diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 272270321..0cb39024a 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -7,7 +7,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT ### Packaging * Removed support for 32-bit simulators. ([#10962](https://github.com/mapbox/mapbox-gl-native/pull/10962)) -* Added a Hebrew localization. ([#10967](https://github.com/mapbox/mapbox-gl-native/pull/10967)) +* Added Danish and Hebrew localizations. ([#10967](https://github.com/mapbox/mapbox-gl-native/pull/10967), [#11136](https://github.com/mapbox/mapbox-gl-native/pull/11134)) ### Styles and rendering diff --git a/platform/ios/app/da.lproj/Localizable.strings b/platform/ios/app/da.lproj/Localizable.strings new file mode 100644 index 000000000..e69de29bb diff --git a/platform/ios/framework/Settings.bundle/da.lproj/Root.strings b/platform/ios/framework/Settings.bundle/da.lproj/Root.strings new file mode 100644 index 000000000..b1129a865 --- /dev/null +++ b/platform/ios/framework/Settings.bundle/da.lproj/Root.strings @@ -0,0 +1,3 @@ +"TELEMETRY_GROUP_TITLE" = "Privatlivs indstillinger"; +"TELEMETRY_SWITCH_TITLE" = "Mapbox Telemetry"; +"TELEMETRY_GROUP_FOOTER" = "Denne indstilling giver app'en tilladelse til at dele anonyme bruger data og position med Mapbox."; diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index 10928e8e8..68071123a 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -1071,6 +1071,12 @@ DAD1656B1CF41981001FF4B9 /* MGLFeature.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFeature.mm; sourceTree = ""; }; DAD165761CF4CDFF001FF4B9 /* MGLShapeCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLShapeCollection.h; sourceTree = ""; }; DAD165771CF4CDFF001FF4B9 /* MGLShapeCollection.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLShapeCollection.mm; sourceTree = ""; }; + DAD88E07202ACFE800AAA536 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = ""; }; + DAD88E08202AD01300AAA536 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Foundation.strings; sourceTree = ""; }; + DAD88E09202AD01F00AAA536 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = da; path = da.lproj/Foundation.stringsdict; sourceTree = ""; }; + DAD88E0A202AD03C00AAA536 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = ""; }; + DAD88E0B202AD04D00AAA536 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = da; path = da.lproj/Localizable.stringsdict; sourceTree = ""; }; + DAD88E0C202AD06500AAA536 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Root.strings; sourceTree = ""; }; DAE7DEC11E245455007505A6 /* MGLNSStringAdditionsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLNSStringAdditionsTests.m; path = ../../darwin/test/MGLNSStringAdditionsTests.m; sourceTree = ""; }; DAE8CCAD1E6E8C70009B5CB0 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = ""; }; DAE8CCAE1E6E8C76009B5CB0 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Root.strings; sourceTree = ""; }; @@ -2369,6 +2375,7 @@ bg, ar, he, + da, ); mainGroup = DA1DC9411CB6C1C2006E619F; productRefGroup = DA1DC94B1CB6C1C2006E619F /* Products */; @@ -2843,6 +2850,7 @@ DA3389651FA3EE1B001EA329 /* bg */, DA80E9601FE84AD90065FC9B /* ar */, DACBC60B20118ABE00C4D7E2 /* he */, + DAD88E07202ACFE800AAA536 /* da */, ); name = Localizable.strings; sourceTree = ""; @@ -2869,6 +2877,7 @@ DACCD9C81F1F473700BB09A1 /* hu */, DA33896A1FA3EE58001EA329 /* bg */, DACBC60E20118AFE00C4D7E2 /* he */, + DAD88E0C202AD06500AAA536 /* da */, ); name = Root.strings; sourceTree = ""; @@ -2893,6 +2902,7 @@ DA5C09BB1EFC486C0056B178 /* hu */, DA3389681FA3EE48001EA329 /* bg */, DACBC60D20118ADE00C4D7E2 /* he */, + DAD88E0A202AD03C00AAA536 /* da */, ); name = Localizable.strings; sourceTree = ""; @@ -2915,6 +2925,7 @@ DA33895F1FA3EAB7001EA329 /* pt-BR */, DA3389661FA3EE28001EA329 /* bg */, DACBC60C20118AD000C4D7E2 /* he */, + DAD88E08202AD01300AAA536 /* da */, ); name = Foundation.strings; sourceTree = ""; @@ -2937,6 +2948,7 @@ DA3389671FA3EE2F001EA329 /* bg */, DA33896B1FA3EF4A001EA329 /* hu */, DA80E9611FE84AEF0065FC9B /* ar */, + DAD88E09202AD01F00AAA536 /* da */, ); name = Foundation.stringsdict; sourceTree = ""; @@ -2965,6 +2977,7 @@ DA704CBC1F637405004B3F28 /* uk */, DA704CBD1F63746E004B3F28 /* zh-Hant */, DA3389691FA3EE50001EA329 /* bg */, + DAD88E0B202AD04D00AAA536 /* da */, ); name = Localizable.stringsdict; sourceTree = ""; diff --git a/platform/ios/resources/da.lproj/Localizable.strings b/platform/ios/resources/da.lproj/Localizable.strings new file mode 100644 index 000000000..dd384b21f --- /dev/null +++ b/platform/ios/resources/da.lproj/Localizable.strings @@ -0,0 +1,117 @@ +/* Accessibility hint */ +"ANNOTATION_A11Y_HINT" = "Vis mere info"; + +/* No comment provided by engineer. */ +"API_CLIENT_400_DESC" = "Denne session kunne ikke gennemføres pga. data fejl. Den oprindelige forespørgsel var: %@"; + +/* No comment provided by engineer. */ +"API_CLIENT_400_REASON" = "Status koden var %ld"; + +/* No comment provided by engineer. */ +"CANCEL" = "Fortryd"; + +/* Accessibility hint for closing the selected annotation’s callout view and returning to the map */ +"CLOSE_CALLOUT_A11Y_HINT" = "Retur til kortet"; + +/* Accessibility hint */ +"COMPASS_A11Y_HINT" = "Fast nord"; + +/* Accessibility label */ +"COMPASS_A11Y_LABEL" = "Kompas"; + +/* Compass abbreviation for north */ +"COMPASS_NORTH" = "N"; + +/* Instructions in Interface Builder designable; {key}, {plist file name} */ +"DESIGNABLE" = "For at vise et Mapbox-hosted kort her, angiv %1$@ til din access token i %2$@\n\nFor yderligere instruktion se:"; + +/* Setup documentation URL display string; keep as short as possible */ +"FIRST_STEPS_URL" = "mapbox.com/help/first-steps-ios-sdk"; + +/* Accessibility hint */ +"INFO_A11Y_HINT" = "Vis credits, feedback formular med mere"; + +/* Accessibility label */ +"INFO_A11Y_LABEL" = "Om kortet"; + +/* List separator */ +"LIST_SEPARATOR" = ", "; + +/* User-friendly error description */ +"LOAD_MAP_FAILED_DESC" = "Kortet kunne ikke hentes på grund af en ukendt fejl"; + +/* User-friendly error description */ +"LOAD_STYLE_FAILED_DESC" = "Kortet kunne ikke hentes på grund af en fejl i kort formatteringen"; + +/* Accessibility label */ +"LOGO_A11Y_LABEL" = "Mapbox"; + +/* Accessibility label */ +"MAP_A11Y_LABEL" = "Kort"; + +/* Map accessibility value; {number of visible annotations} */ +"MAP_A11Y_VALUE_ANNOTATIONS" = "%ld synlige kommentarer."; + +/* Map accessibility value; {list of visible places} */ +"MAP_A11Y_VALUE_PLACES" = "Synlige steder: %@."; + +/* Map accessibility value; {number of visible roads} */ +"MAP_A11Y_VALUE_ROADS" = "%ld synlige veje."; + +/* Map accessibility value; {zoom level} */ +"MAP_A11Y_VALUE_ZOOM" = "Zoom %dx."; + +/* User-friendly error description */ +"PARSE_STYLE_FAILED_DESC" = "Kortet kunne ikke hentes på grund af en fejl"; + +/* String format for accessibility value for road feature; {starting compass direction}, {ending compass direction} */ +"ROAD_DIRECTION_A11Y_FMT" = "%1$@ til %2$@"; + +/* Accessibility value indicating that a road is a divided road (dual carriageway) */ +"ROAD_DIVIDED_A11Y_VALUE" = "Delt vej"; + +/* Accessibility value indicating that a road is a one-way road */ +"ROAD_ONEWAY_A11Y_VALUE" = "Ensrettet"; + +/* String format for accessibility value for road feature; {route number} */ +"ROAD_REF_A11Y_FMT" = "Rute %@"; + +/* Action sheet title */ +"SDK_NAME" = "Mapbox Maps SDK for iOS"; + +/* Developer-only SDK update notification; {latest version, in format x.x.x} */ +"SDK_UPDATE_AVAILABLE" = "Mapbox Maps SDK for iOS version %@ er nu tilgængelig:"; + +/* User-friendly error description */ +"STYLE_NOT_FOUND_DESC" = "Kortet kunne ikke hentes fordi det enten ikke findes, eller ikke er kompatibelt."; + +/* Telemetry prompt message */ +"TELEMETRY_DISABLED_MSG" = "Du kan hjælpe med at gøre OpenStreetMap og Mapbox kort bedre ved at bidrage med annonyme bruger data."; + +/* Telemetry prompt button */ +"TELEMETRY_DISABLED_OFF" = "Deltag ikke"; + +/* Telemetry prompt button */ +"TELEMETRY_DISABLED_ON" = "Deltag"; + +/* Telemetry prompt message */ +"TELEMETRY_ENABLED_MSG" = "Du hjælper med at gøre OpenStreetMap og Mapbox kort bedre ved at bidrage med annonyme bruger data."; + +/* Telemetry prompt button */ +"TELEMETRY_ENABLED_OFF" = "Stop deltagelse"; + +/* Telemetry prompt button */ +"TELEMETRY_ENABLED_ON" = "Fortsæt deltagelse"; + +/* Telemetry prompt button */ +"TELEMETRY_MORE" = "Fortæl mig mere"; + +/* Action in attribution sheet */ +"TELEMETRY_NAME" = "Mapbox Telemetry"; + +/* Telemetry prompt title */ +"TELEMETRY_TITLE" = "Gør Mapbox kort bedre"; + +/* Default user location annotation title */ +"USER_DOT_TITLE" = "Du er her"; + diff --git a/platform/ios/resources/da.lproj/Localizable.stringsdict b/platform/ios/resources/da.lproj/Localizable.stringsdict new file mode 100644 index 000000000..296b8c88d --- /dev/null +++ b/platform/ios/resources/da.lproj/Localizable.stringsdict @@ -0,0 +1,54 @@ + + + + + MAP_A11Y_VALUE_ANNOTATIONS + + NSStringLocalizedFormatKey + %#@count@ + count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + %ld synlig kommentar + other + %ld synlige kommentarer + + + MAP_A11Y_VALUE_ROADS + + NSStringLocalizedFormatKey + %#@count@ + count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + %ld synlig vej + other + %ld synlige veje + + + MAP_A11Y_VALUE_ZOOM + + NSStringLocalizedFormatKey + %#@level@ + level + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + Zoom %dx + other + Zoom %dx. + + + + diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index bcdbb9824..d35f396a2 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -26,7 +26,7 @@ ### Other changes -* Added a Hebrew localization. ([#10967](https://github.com/mapbox/mapbox-gl-native/pull/10967)) +* Added Danish and Hebrew localizations. ([#10967](https://github.com/mapbox/mapbox-gl-native/pull/10967), [#11136](https://github.com/mapbox/mapbox-gl-native/pull/11134)) * Feature querying results now account for the `MGLSymbolStyleLayer.circleStrokeWidth` property. ([#10897](https://github.com/mapbox/mapbox-gl-native/pull/10897)) ## v0.6.1 - January 16, 2018 diff --git a/platform/macos/app/da.lproj/Localizable.strings b/platform/macos/app/da.lproj/Localizable.strings new file mode 100644 index 000000000..e69de29bb diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj index 090902fca..e42885f87 100644 --- a/platform/macos/macos.xcodeproj/project.pbxproj +++ b/platform/macos/macos.xcodeproj/project.pbxproj @@ -526,6 +526,10 @@ DACFE7971F66EA0C00630DA8 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = vi; path = vi.lproj/Foundation.stringsdict; sourceTree = ""; }; DAD165721CF4CD7A001FF4B9 /* MGLShapeCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLShapeCollection.h; sourceTree = ""; }; DAD165731CF4CD7A001FF4B9 /* MGLShapeCollection.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLShapeCollection.mm; sourceTree = ""; }; + DAD88E03202ACF5900AAA536 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = ""; }; + DAD88E04202ACF7C00AAA536 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Foundation.strings; sourceTree = ""; }; + DAD88E05202ACF8200AAA536 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = da; path = da.lproj/Foundation.stringsdict; sourceTree = ""; }; + DAD88E06202ACF9200AAA536 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = ""; }; DAE6C2E11CC304F900DB3429 /* Credits.rtf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = ""; }; DAE6C2E31CC3050F00DB3429 /* DroppedPinAnnotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DroppedPinAnnotation.h; sourceTree = ""; }; DAE6C2E41CC3050F00DB3429 /* DroppedPinAnnotation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DroppedPinAnnotation.m; sourceTree = ""; }; @@ -1399,6 +1403,7 @@ bg, ar, he, + da, ); mainGroup = DA839E891CC2E3400062CAFB; productRefGroup = DA839E931CC2E3400062CAFB /* Products */; @@ -1633,6 +1638,7 @@ DA3389611FA3EDCE001EA329 /* bg */, DA80E95D1FE84A300065FC9B /* ar */, DACBC6082011885800C4D7E2 /* he */, + DAD88E03202ACF5900AAA536 /* da */, ); name = Localizable.strings; sourceTree = ""; @@ -1684,6 +1690,7 @@ DA704CBF1F637548004B3F28 /* hu */, DA3389641FA3EE00001EA329 /* bg */, DACBC60A2011889E00C4D7E2 /* he */, + DAD88E06202ACF9200AAA536 /* da */, ); name = Localizable.strings; sourceTree = ""; @@ -1707,6 +1714,7 @@ DA3389601FA3EAC4001EA329 /* pt-BR */, DA3389621FA3EDEF001EA329 /* bg */, DACBC6092011888C00C4D7E2 /* he */, + DAD88E04202ACF7C00AAA536 /* da */, ); name = Foundation.strings; sourceTree = ""; @@ -1729,6 +1737,7 @@ DA3389631FA3EDF5001EA329 /* bg */, DA33896C1FA3EF51001EA329 /* hu */, DA80E95F1FE84A540065FC9B /* ar */, + DAD88E05202ACF8200AAA536 /* da */, ); name = Foundation.stringsdict; sourceTree = ""; diff --git a/platform/macos/sdk/da.lproj/Localizable.strings b/platform/macos/sdk/da.lproj/Localizable.strings new file mode 100644 index 000000000..1c9dcaa68 --- /dev/null +++ b/platform/macos/sdk/da.lproj/Localizable.strings @@ -0,0 +1,27 @@ +/* User-friendly error description */ +"LOAD_MAP_FAILED_DESC" = "Kortet kunne ikke indlæses, fordi der opstod en ukendt fejl."; + +/* User-friendly error description */ +"LOAD_STYLE_FAILED_DESC" = "Kortet kunne ikke indlæses, fordi benævnelsen ikke kan indlæses."; + +/* Accessibility title */ +"MAP_A11Y_TITLE" = "Mapbox"; + +/* User-friendly error description */ +"PARSE_STYLE_FAILED_DESC" = "Kortet kunne ikke indlæses, fordi benævnelsen er beskadiget."; + +/* User-friendly error description */ +"STYLE_NOT_FOUND_DESC" = "Kortet kunne ikke indlæses, fordi benævnelsen ikke kan findes eller er uforenelig."; + +/* Label of Zoom In button */ +"ZOOM_IN_LABEL" = "+"; + +/* Tooltip of Zoom In button */ +"ZOOM_IN_TOOLTIP" = "Zoom Ind"; + +/* Label of Zoom Out button; U+2212 MINUS SIGN */ +"ZOOM_OUT_LABEL" = "−"; + +/* Tooltip of Zoom Out button */ +"ZOOM_OUT_TOOLTIP" = "Zoom Ud"; + From 97005ccdfa6641a3d33fbe046b5c7dc5fdb72999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Tue, 10 Oct 2017 15:35:00 +0200 Subject: [PATCH 021/264] [core] use the RunLoop's schedule call directly RunLoop already has a queue, and has the ability to schedule weak mailboxes, so we can remove the duplicate code. --- include/mbgl/util/thread.hpp | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/include/mbgl/util/thread.hpp b/include/mbgl/util/thread.hpp index 672eebf6d..e3bd18143 100644 --- a/include/mbgl/util/thread.hpp +++ b/include/mbgl/util/thread.hpp @@ -128,26 +128,9 @@ private: MBGL_STORE_THREAD(tid); void schedule(std::weak_ptr mailbox) override { - { - std::lock_guard lock(mutex); - queue.push(mailbox); - } - - loop->invoke([this] { receive(); }); + loop->schedule(mailbox); } - void receive() { - std::unique_lock lock(mutex); - - auto mailbox = queue.front(); - queue.pop(); - lock.unlock(); - - Mailbox::maybeReceive(mailbox); - } - - std::mutex mutex; - std::queue> queue; std::thread thread; std::unique_ptr> object; From 9b961fb6f9d1bdc9a5a8afe01162858d3ce2c39c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Tue, 23 Jan 2018 14:30:01 -0800 Subject: [PATCH 022/264] [core] factor out RunLoop::wake() --- include/mbgl/util/run_loop.hpp | 12 ++++++++---- platform/android/src/run_loop.cpp | 7 ++----- platform/darwin/src/run_loop.cpp | 7 ++----- platform/default/run_loop.cpp | 7 ++----- platform/qt/src/run_loop.cpp | 7 ++----- 5 files changed, 16 insertions(+), 24 deletions(-) diff --git a/include/mbgl/util/run_loop.hpp b/include/mbgl/util/run_loop.hpp index acbea8027..ecb18c857 100644 --- a/include/mbgl/util/run_loop.hpp +++ b/include/mbgl/util/run_loop.hpp @@ -76,16 +76,20 @@ private: using Queue = std::queue>; - void push(std::shared_ptr); + // Wakes up the RunLoop so that it starts processing items in the queue. + void wake(); - void withMutex(std::function&& fn) { + // Adds a WorkTask to the queue, and wakes it up. + void push(std::shared_ptr task) { std::lock_guard lock(mutex); - fn(); + queue.push(std::move(task)); + wake(); } void process() { + std::unique_lock lock(mutex); Queue queue_; - withMutex([&] { queue_.swap(queue); }); + lock.unlock(); while (!queue_.empty()) { (*(queue_.front()))(); diff --git a/platform/android/src/run_loop.cpp b/platform/android/src/run_loop.cpp index 1d284a9e7..34366d836 100644 --- a/platform/android/src/run_loop.cpp +++ b/platform/android/src/run_loop.cpp @@ -216,11 +216,8 @@ LOOP_HANDLE RunLoop::getLoopHandle() { return Get()->impl.get(); } -void RunLoop::push(std::shared_ptr task) { - withMutex([&] { - queue.push(std::move(task)); - impl->wake(); - }); +void RunLoop::wake() { + impl->wake(); } void RunLoop::run() { diff --git a/platform/darwin/src/run_loop.cpp b/platform/darwin/src/run_loop.cpp index d60a88cf5..0778b004e 100644 --- a/platform/darwin/src/run_loop.cpp +++ b/platform/darwin/src/run_loop.cpp @@ -29,11 +29,8 @@ RunLoop::~RunLoop() { Scheduler::SetCurrent(nullptr); } -void RunLoop::push(std::shared_ptr task) { - withMutex([&] { - queue.push(std::move(task)); - impl->async->send(); - }); +void RunLoop::wake() { + impl->async->send(); } void RunLoop::run() { diff --git a/platform/default/run_loop.cpp b/platform/default/run_loop.cpp index 5bccd21d5..868ee7211 100644 --- a/platform/default/run_loop.cpp +++ b/platform/default/run_loop.cpp @@ -129,11 +129,8 @@ LOOP_HANDLE RunLoop::getLoopHandle() { return Get()->impl->loop; } -void RunLoop::push(std::shared_ptr task) { - withMutex([&] { - queue.push(std::move(task)); - impl->async->send(); - }); +void RunLoop::wake() { + impl->async->send(); } void RunLoop::run() { diff --git a/platform/qt/src/run_loop.cpp b/platform/qt/src/run_loop.cpp index af0c50ebb..c25243c8e 100644 --- a/platform/qt/src/run_loop.cpp +++ b/platform/qt/src/run_loop.cpp @@ -52,11 +52,8 @@ LOOP_HANDLE RunLoop::getLoopHandle() { return nullptr; } -void RunLoop::push(std::shared_ptr task) { - withMutex([&] { - queue.push(std::move(task)); - impl->async->send(); - }); +void RunLoop::wake() { + impl->async->send(); } void RunLoop::run() { From 1eb628327d30031b3a2c6b9b8f9d14b6f5197b2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Tue, 23 Jan 2018 14:43:37 -0800 Subject: [PATCH 023/264] [core] change RunLoop processing to check queue on every iteration --- include/mbgl/util/run_loop.hpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/include/mbgl/util/run_loop.hpp b/include/mbgl/util/run_loop.hpp index ecb18c857..716765268 100644 --- a/include/mbgl/util/run_loop.hpp +++ b/include/mbgl/util/run_loop.hpp @@ -88,12 +88,13 @@ private: void process() { std::unique_lock lock(mutex); - Queue queue_; - lock.unlock(); - - while (!queue_.empty()) { - (*(queue_.front()))(); - queue_.pop(); + std::shared_ptr task; + while (!queue.empty()) { + task = std::move(queue.front()); + queue.pop(); + lock.unlock(); + (*task)(); + lock.lock(); } } From 9cb4ffd24833550b8cdf8c2256e29fd92cd5d5f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Tue, 23 Jan 2018 16:59:59 -0800 Subject: [PATCH 024/264] [core] prioritize Thread::pause() calls --- include/mbgl/util/run_loop.hpp | 43 ++++++++++++++++++++++++++-------- include/mbgl/util/thread.hpp | 2 +- test/util/run_loop.test.cpp | 14 +++++++++++ 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/include/mbgl/util/run_loop.hpp b/include/mbgl/util/run_loop.hpp index 716765268..381e3ae21 100644 --- a/include/mbgl/util/run_loop.hpp +++ b/include/mbgl/util/run_loop.hpp @@ -26,6 +26,11 @@ public: New, }; + enum class Priority : bool { + Default = false, + High = true, + }; + enum class Event : uint8_t { None = 0, Read = 1, @@ -47,11 +52,16 @@ public: void addWatch(int fd, Event, std::function&& callback); void removeWatch(int fd); + // Invoke fn(args...) on this RunLoop. + template + void invoke(Priority priority, Fn&& fn, Args&&... args) { + push(priority, WorkTask::make(std::forward(fn), std::forward(args)...)); + } + // Invoke fn(args...) on this RunLoop. template void invoke(Fn&& fn, Args&&... args) { - std::shared_ptr task = WorkTask::make(std::forward(fn), std::forward(args)...); - push(task); + invoke(Priority::Default, std::forward(fn), std::forward(args)...); } // Post the cancellable work fn(args...) to this RunLoop. @@ -59,7 +69,7 @@ public: std::unique_ptr invokeCancellable(Fn&& fn, Args&&... args) { std::shared_ptr task = WorkTask::make(std::forward(fn), std::forward(args)...); - push(task); + push(Priority::Default, task); return std::make_unique(task); } @@ -80,25 +90,38 @@ private: void wake(); // Adds a WorkTask to the queue, and wakes it up. - void push(std::shared_ptr task) { + void push(Priority priority, std::shared_ptr task) { std::lock_guard lock(mutex); - queue.push(std::move(task)); + if (priority == Priority::High) { + highPriorityQueue.emplace(std::move(task)); + } else { + defaultQueue.emplace(std::move(task)); + } wake(); } void process() { - std::unique_lock lock(mutex); std::shared_ptr task; - while (!queue.empty()) { - task = std::move(queue.front()); - queue.pop(); + std::unique_lock lock(mutex); + while (true) { + if (!highPriorityQueue.empty()) { + task = std::move(highPriorityQueue.front()); + highPriorityQueue.pop(); + } else if (!defaultQueue.empty()) { + task = std::move(defaultQueue.front()); + defaultQueue.pop(); + } else { + break; + } lock.unlock(); (*task)(); + task.reset(); lock.lock(); } } - Queue queue; + Queue defaultQueue; + Queue highPriorityQueue; std::mutex mutex; std::unique_ptr impl; diff --git a/include/mbgl/util/thread.hpp b/include/mbgl/util/thread.hpp index e3bd18143..74e722b02 100644 --- a/include/mbgl/util/thread.hpp +++ b/include/mbgl/util/thread.hpp @@ -103,7 +103,7 @@ public: auto pausing = paused->get_future(); - loop->invoke([this] { + loop->invoke(RunLoop::Priority::High, [this] { auto resuming = resumed->get_future(); paused->set_value(); resuming.get(); diff --git a/test/util/run_loop.test.cpp b/test/util/run_loop.test.cpp index 57bc613f9..4d2c70442 100644 --- a/test/util/run_loop.test.cpp +++ b/test/util/run_loop.test.cpp @@ -50,3 +50,17 @@ TEST(RunLoop, MultipleRun) { EXPECT_TRUE(secondTimeout); } + +TEST(RunLoop, Priorities) { + std::vector order; + + RunLoop loop(RunLoop::Type::New); + loop.invoke([&] { order.push_back(1); }); + loop.invoke(RunLoop::Priority::High, [&] { order.push_back(2); }); + loop.invoke([&] { order.push_back(3); }); + loop.invoke(RunLoop::Priority::High, [&] { order.push_back(4); }); + loop.invoke(RunLoop::Priority::Default, [&] { loop.stop(); }); + loop.run(); + + EXPECT_EQ((std::vector{ 2, 4, 1, 3 }), order); +} From 62ae0738e8a3d1b3e4b661fa7e8d8bb1d6339c51 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Wed, 31 Jan 2018 13:09:00 +0000 Subject: [PATCH 025/264] [core] Don't crash on line labels with 0 glyphs. Fixes issue #10956. --- src/mbgl/layout/symbol_projection.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mbgl/layout/symbol_projection.cpp b/src/mbgl/layout/symbol_projection.cpp index ee6385c93..9e077e253 100644 --- a/src/mbgl/layout/symbol_projection.cpp +++ b/src/mbgl/layout/symbol_projection.cpp @@ -318,7 +318,7 @@ namespace mbgl { placedGlyphs.push_back(*placedGlyph); } placedGlyphs.push_back(firstAndLastGlyph->second); - } else { + } else if (symbol.glyphOffsets.size() == 1) { // Only a single glyph to place // So, determine whether to flip based on projected angle of the line segment it's on if (keepUpright && !flip) { @@ -337,7 +337,6 @@ namespace mbgl { return *orientationChange; } } - assert(symbol.glyphOffsets.size() == 1); // We are relying on SymbolInstance.hasText filtering out symbols without any glyphs at all const float glyphOffsetX = symbol.glyphOffsets.front(); optional singleGlyph = placeGlyphAlongLine(fontScale * glyphOffsetX, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, symbol.tileDistances, labelPlaneMatrix, false); @@ -347,6 +346,8 @@ namespace mbgl { placedGlyphs.push_back(*singleGlyph); } + // The number of placedGlyphs must equal the number of glyphOffsets, which must correspond to the number of glyph vertices + // There may be 0 glyphs here, if a label consists entirely of glyphs that have 0x0 dimensions for (auto& placedGlyph : placedGlyphs) { addDynamicAttributes(placedGlyph.point, placedGlyph.angle, dynamicVertexArray); } From 7a0a4c44f469e180606afa250a47414441e8e675 Mon Sep 17 00:00:00 2001 From: Osana Babayan <32496536+osana@users.noreply.github.com> Date: Thu, 8 Feb 2018 10:33:12 -0500 Subject: [PATCH 026/264] migrated to use mapbox-java3.0 migrated to use mapbox-java3.0 old 2.2.9 telementry is still used though --- .../com/mapbox/mapboxsdk/maps/MapboxMap.java | 4 +- .../mapbox/mapboxsdk/maps/NativeMapView.java | 4 +- .../style/sources/CustomGeometrySource.java | 4 +- .../style/sources/GeoJsonSource.java | 6 +- .../style/sources/GeometryTileProvider.java | 2 +- .../mapboxsdk/style/sources/VectorSource.java | 2 +- .../MapboxGLAndroidSDKTestApp/build.gradle | 2 + .../testapp/style/GeoJsonSourceTests.java | 8 +- .../annotation/AnimatedMarkerActivity.java | 8 +- ...QueryRenderedFeaturesBoxCountActivity.java | 12 +- ...yRenderedFeaturesBoxHighlightActivity.java | 5 +- ...enderedFeaturesBoxSymbolCountActivity.java | 2 +- ...eryRenderedFeaturesPropertiesActivity.java | 15 +- .../feature/QuerySourceFeaturesActivity.java | 9 +- .../activity/style/CustomSpriteActivity.java | 18 ++- .../activity/style/FillExtrusionActivity.java | 4 +- .../activity/style/GridSourceActivity.java | 21 +-- .../activity/style/RuntimeStyleActivity.java | 9 +- .../style/SymbolGeneratorActivity.java | 20 +-- .../activity/style/SymbolLayerActivity.java | 11 +- .../ZoomFunctionSymbolLayerActivity.java | 18 ++- .../mapboxsdk/testapp/utils/GeoParseUtil.java | 16 +-- platform/android/config.cmake | 2 - platform/android/gradle/dependencies.gradle | 12 +- .../src/geojson/conversion/geometry.hpp | 130 +++++++++--------- platform/android/src/geojson/feature.cpp | 18 +-- platform/android/src/geojson/feature.hpp | 8 +- .../src/geojson/feature_collection.cpp | 6 +- .../src/geojson/feature_collection.hpp | 4 +- platform/android/src/geojson/geometry.cpp | 2 +- platform/android/src/geojson/geometry.hpp | 2 +- platform/android/src/geojson/line_string.cpp | 28 ++-- platform/android/src/geojson/line_string.hpp | 6 +- .../android/src/geojson/multi_line_string.cpp | 22 +-- .../android/src/geojson/multi_line_string.hpp | 6 +- platform/android/src/geojson/multi_point.cpp | 10 +- platform/android/src/geojson/multi_point.hpp | 4 +- .../android/src/geojson/multi_polygon.cpp | 16 +-- .../android/src/geojson/multi_polygon.hpp | 4 +- platform/android/src/geojson/point.cpp | 34 ++++- platform/android/src/geojson/point.hpp | 10 +- platform/android/src/geojson/polygon.cpp | 12 +- platform/android/src/geojson/polygon.hpp | 6 +- platform/android/src/geojson/position.cpp | 27 ---- platform/android/src/geojson/position.hpp | 27 ---- platform/android/src/jni.cpp | 2 - 46 files changed, 284 insertions(+), 314 deletions(-) delete mode 100644 platform/android/src/geojson/position.cpp delete mode 100644 platform/android/src/geojson/position.hpp diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index 509e784e5..834317dd2 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java @@ -16,6 +16,8 @@ import android.text.TextUtils; import android.view.View; import android.view.ViewGroup; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.Geometry; import com.mapbox.mapboxsdk.annotations.Annotation; import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions; @@ -42,8 +44,6 @@ import com.mapbox.mapboxsdk.style.layers.Layer; import com.mapbox.mapboxsdk.style.light.Light; import com.mapbox.mapboxsdk.style.sources.Source; import com.mapbox.services.android.telemetry.location.LocationEngine; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.Geometry; import java.lang.reflect.ParameterizedType; import java.util.HashMap; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java index f1635c898..785b04577 100755 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java @@ -11,6 +11,8 @@ import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.DisplayMetrics; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.Geometry; import com.mapbox.mapboxsdk.LibraryLoader; import com.mapbox.mapboxsdk.annotations.Icon; import com.mapbox.mapboxsdk.annotations.Marker; @@ -29,8 +31,6 @@ import com.mapbox.mapboxsdk.style.light.Light; import com.mapbox.mapboxsdk.style.sources.CannotAddSourceException; import com.mapbox.mapboxsdk.style.sources.Source; import com.mapbox.mapboxsdk.utils.BitmapUtils; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.Geometry; import java.nio.ByteBuffer; import java.util.ArrayList; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java index 62f1719dd..3467ed7a7 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java @@ -5,10 +5,10 @@ import android.support.annotation.Nullable; import android.support.annotation.UiThread; import android.support.annotation.WorkerThread; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; import com.mapbox.mapboxsdk.geometry.LatLngBounds; import com.mapbox.mapboxsdk.style.layers.Filter; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; import java.lang.ref.WeakReference; import java.util.ArrayList; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java index 10ecb945a..5c740554c 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java @@ -4,10 +4,10 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.UiThread; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Geometry; import com.mapbox.mapboxsdk.style.layers.Filter; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; -import com.mapbox.services.commons.geojson.Geometry; import java.net.URL; import java.util.ArrayList; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeometryTileProvider.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeometryTileProvider.java index 3f1eb315d..17e7f0f5e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeometryTileProvider.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeometryTileProvider.java @@ -2,8 +2,8 @@ package com.mapbox.mapboxsdk.style.sources; import android.support.annotation.WorkerThread; +import com.mapbox.geojson.FeatureCollection; import com.mapbox.mapboxsdk.geometry.LatLngBounds; -import com.mapbox.services.commons.geojson.FeatureCollection; /** * Interface that defines methods for working with {@link CustomGeometrySource}. diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java index 9b59cf896..62b08a90e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java @@ -5,8 +5,8 @@ import android.support.annotation.Nullable; import android.support.annotation.Size; import android.support.annotation.UiThread; +import com.mapbox.geojson.Feature; import com.mapbox.mapboxsdk.style.layers.Filter; -import com.mapbox.services.commons.geojson.Feature; import java.net.URL; import java.util.ArrayList; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle index 3740341e9..de4c0cc3b 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle +++ b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle @@ -57,6 +57,8 @@ dependencies { transitive = true } + api dependenciesList.mapboxJavaTurf + implementation dependenciesList.supportAppcompatV7 implementation dependenciesList.supportRecyclerView implementation dependenciesList.supportDesign diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java index 5d10cfa38..2156c9697 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java @@ -13,9 +13,9 @@ import com.mapbox.mapboxsdk.testapp.R; import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest; import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity; import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; -import com.mapbox.services.commons.geojson.Point; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Point; import org.hamcrest.Matcher; import org.junit.Test; @@ -67,7 +67,7 @@ public class GeoJsonSourceTests extends BaseActivityTest { @Override public void perform(UiController uiController, View view) { - GeoJsonSource source = new GeoJsonSource("source", Point.fromCoordinates(new double[] {0d, 0d})); + GeoJsonSource source = new GeoJsonSource("source", Point.fromLngLat(0d, 0d)); mapboxMap.addSource(source); mapboxMap.addLayer(new CircleLayer("layer", source.getId())); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java index a557bb4ed..e6db07114 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java @@ -12,6 +12,7 @@ import android.support.v7.app.AppCompatActivity; import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; +import com.mapbox.geojson.Point; import com.mapbox.mapboxsdk.annotations.Icon; import com.mapbox.mapboxsdk.annotations.IconFactory; import com.mapbox.mapboxsdk.annotations.Marker; @@ -25,8 +26,7 @@ import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.testapp.R; import com.mapbox.mapboxsdk.testapp.utils.IconUtils; -import com.mapbox.services.api.utils.turf.TurfMeasurement; -import com.mapbox.services.commons.models.Position; +import com.mapbox.turf.TurfMeasurement; import java.util.ArrayList; import java.util.List; @@ -272,8 +272,8 @@ public class AnimatedMarkerActivity extends AppCompatActivity { private double getBearing(LatLng from, LatLng to) { return TurfMeasurement.bearing( - Position.fromCoordinates(from.getLongitude(), from.getLatitude()), - Position.fromCoordinates(to.getLongitude(), to.getLatitude()) + Point.fromLngLat(from.getLongitude(), from.getLatitude()), + Point.fromLngLat(to.getLongitude(), to.getLatitude()) ); } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java index 70d5b5342..c4ea81263 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java @@ -7,10 +7,10 @@ import android.view.View; import android.widget.Toast; import com.google.gson.JsonElement; +import com.mapbox.geojson.Feature; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.services.commons.geojson.Feature; import java.util.List; import java.util.Map; @@ -62,12 +62,12 @@ public class QueryRenderedFeaturesBoxCountActivity extends AppCompatActivity { for (Feature feature : features) { if (feature != null) { Timber.i("Got feature %s with %s properties and Geometry %s", - feature.getId(), - feature.getProperties() != null ? feature.getProperties().entrySet().size() : "", - feature.getGeometry() != null ? feature.getGeometry().getClass().getSimpleName() : "" + feature.id(), + feature.properties() != null ? feature.properties().entrySet().size() : "", + feature.geometry() != null ? feature.geometry().getClass().getSimpleName() : "" ); - if (feature.getProperties() != null) { - for (Map.Entry entry : feature.getProperties().entrySet()) { + if (feature.properties() != null) { + for (Map.Entry entry : feature.properties().entrySet()) { Timber.i("Prop %s - %s", entry.getKey(), entry.getValue()); } } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java index 8c9d05676..df608360a 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java @@ -7,14 +7,15 @@ import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Toast; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.style.layers.FillLayer; import com.mapbox.mapboxsdk.style.layers.Filter; import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; + import java.util.List; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java index 9bad5f3e6..46409d189 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java @@ -7,13 +7,13 @@ import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Toast; +import com.mapbox.geojson.Feature; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.style.layers.SymbolLayer; import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; import com.mapbox.mapboxsdk.testapp.R; import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils; -import com.mapbox.services.commons.geojson.Feature; import java.io.IOException; import java.util.List; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java index 150b081f7..be32718dc 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java @@ -12,12 +12,13 @@ import android.widget.LinearLayout; import android.widget.TextView; import com.google.gson.JsonElement; +import com.mapbox.geojson.Feature; import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; import com.mapbox.mapboxsdk.annotations.Marker; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.services.commons.geojson.Feature; + import java.util.List; import java.util.Map; @@ -80,12 +81,12 @@ public class QueryRenderedFeaturesPropertiesActivity extends AppCompatActivity { for (Feature feature : features) { if (feature != null) { Timber.i("Got feature %s with %s properties and Geometry %s", - feature.getId(), - feature.getProperties() != null ? feature.getProperties().entrySet().size() : "", - feature.getGeometry() != null ? feature.getGeometry().getClass().getSimpleName() : "" + feature.id(), + feature.properties() != null ? feature.properties().entrySet().size() : "", + feature.geometry() != null ? feature.geometry().getClass().getSimpleName() : "" ); - if (feature.getProperties() != null) { - for (Map.Entry entry : feature.getProperties().entrySet()) { + if (feature.properties() != null) { + for (Map.Entry entry : feature.properties().entrySet()) { Timber.i("Prop %s - %s", entry.getKey(), entry.getValue()); } } @@ -114,7 +115,7 @@ public class QueryRenderedFeaturesPropertiesActivity extends AppCompatActivity { if (customMarker.features.size() > 0) { view.addView(row(String.format("Found %s features", customMarker.features.size()))); Feature feature = customMarker.features.get(0); - for (Map.Entry prop : feature.getProperties().entrySet()) { + for (Map.Entry prop : feature.properties().entrySet()) { view.addView(row(String.format("%s: %s", prop.getKey(), prop.getValue()))); } } else { diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QuerySourceFeaturesActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QuerySourceFeaturesActivity.java index c8bef2685..14de81ab3 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QuerySourceFeaturesActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QuerySourceFeaturesActivity.java @@ -5,15 +5,16 @@ import android.support.v7.app.AppCompatActivity; import android.widget.Toast; import com.google.gson.JsonObject; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Point; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.style.layers.CircleLayer; import com.mapbox.mapboxsdk.style.layers.Filter; import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; -import com.mapbox.services.commons.geojson.Point; + import java.util.List; @@ -42,7 +43,7 @@ public class QuerySourceFeaturesActivity extends AppCompatActivity { properties.addProperty("key1", "value1"); final GeoJsonSource source = new GeoJsonSource("test-source", FeatureCollection.fromFeatures(new Feature[] { - Feature.fromGeometry(Point.fromCoordinates(new double[] {0, 0}), properties) + Feature.fromGeometry(Point.fromLngLat(0, 0), properties) })); mapboxMap.addSource(source); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CustomSpriteActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CustomSpriteActivity.java index 8d35e659d..30cb0a866 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CustomSpriteActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CustomSpriteActivity.java @@ -7,6 +7,9 @@ import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.view.View; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Point; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.maps.MapView; @@ -15,10 +18,7 @@ import com.mapbox.mapboxsdk.style.layers.Layer; import com.mapbox.mapboxsdk.style.layers.SymbolLayer; import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; -import com.mapbox.services.commons.geojson.Point; -import com.mapbox.services.commons.models.Position; + import timber.log.Timber; @@ -58,7 +58,7 @@ public class CustomSpriteActivity extends AppCompatActivity { mapboxMap.addImage(CUSTOM_ICON, BitmapFactory.decodeResource(getResources(), R.drawable.ic_car_top)); // Add a source with a geojson point - point = Point.fromCoordinates(Position.fromCoordinates(13.400972d, 52.519003d)); + point = Point.fromLngLat(13.400972d, 52.519003d); source = new GeoJsonSource( "point", FeatureCollection.fromFeatures(new Feature[] {Feature.fromGeometry(point)}) @@ -78,15 +78,13 @@ public class CustomSpriteActivity extends AppCompatActivity { fab.setImageResource(R.drawable.ic_directions_car_black); } else { // Update point - point = Point.fromCoordinates( - Position.fromCoordinates(point.getCoordinates().getLongitude() + 0.001, - point.getCoordinates().getLatitude() + 0.001) - ); + point = Point.fromLngLat(point.longitude() + 0.001, + point.latitude() + 0.001); source.setGeoJson(FeatureCollection.fromFeatures(new Feature[] {Feature.fromGeometry(point)})); // Move the camera as well mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(new LatLng( - point.getCoordinates().getLatitude(), point.getCoordinates().getLongitude()))); + point.latitude(), point.longitude()))); } } }); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java index a88a489cb..15d7024ab 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java @@ -12,7 +12,7 @@ import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.style.layers.FillExtrusionLayer; import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.services.commons.geojson.Polygon; +import com.mapbox.geojson.Polygon; import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionColor; import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionHeight; @@ -35,7 +35,7 @@ public class FillExtrusionActivity extends AppCompatActivity { mapView.onCreate(savedInstanceState); mapView.getMapAsync(map -> { mapboxMap = map; - Polygon domTower = Polygon.fromCoordinates(new double[][][] { + Polygon domTower = Polygon.fromLngLats(new double[][][] { new double[][] { new double[] { 5.12112557888031, diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GridSourceActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GridSourceActivity.java index 9dda0f8fa..fdc3826fb 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GridSourceActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GridSourceActivity.java @@ -5,6 +5,10 @@ import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v7.app.AppCompatActivity; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.MultiLineString; +import com.mapbox.geojson.Point; import com.mapbox.mapboxsdk.geometry.LatLngBounds; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; @@ -13,10 +17,7 @@ import com.mapbox.mapboxsdk.style.layers.LineLayer; import com.mapbox.mapboxsdk.style.sources.CustomGeometrySource; import com.mapbox.mapboxsdk.style.sources.GeometryTileProvider; import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; -import com.mapbox.services.commons.geojson.MultiLineString; -import com.mapbox.services.commons.models.Position; + import java.util.ArrayList; import java.util.Arrays; @@ -68,18 +69,18 @@ public class GridSourceActivity extends AppCompatActivity implements OnMapReadyC List gridLines = new ArrayList(); for (double y = Math.ceil(bounds.getLatNorth() / gridSpacing) * gridSpacing; y >= Math.floor(bounds.getLatSouth() / gridSpacing) * gridSpacing; y -= gridSpacing) { - gridLines.add(Arrays.asList(Position.fromCoordinates(bounds.getLonWest(), y), - Position.fromCoordinates(bounds.getLonEast(), y))); + gridLines.add(Arrays.asList(Point.fromLngLat(bounds.getLonWest(), y), + Point.fromLngLat(bounds.getLonEast(), y))); } - features.add(Feature.fromGeometry(MultiLineString.fromCoordinates(gridLines))); + features.add(Feature.fromGeometry(MultiLineString.fromLngLats(gridLines))); gridLines = new ArrayList(); for (double x = Math.floor(bounds.getLonWest() / gridSpacing) * gridSpacing; x <= Math.ceil(bounds.getLonEast() / gridSpacing) * gridSpacing; x += gridSpacing) { - gridLines.add(Arrays.asList(Position.fromCoordinates(x, bounds.getLatSouth()), - Position.fromCoordinates(x, bounds.getLatNorth()))); + gridLines.add(Arrays.asList(Point.fromLngLat(x, bounds.getLatSouth()), + Point.fromLngLat(x, bounds.getLatNorth()))); } - features.add(Feature.fromGeometry(MultiLineString.fromCoordinates(gridLines))); + features.add(Feature.fromGeometry(MultiLineString.fromLngLats(gridLines))); return FeatureCollection.fromFeatures(features); } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java index 942ce9aa3..6eb4772b1 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java @@ -8,6 +8,8 @@ import android.view.Menu; import android.view.MenuItem; import android.widget.Toast; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.maps.MapView; @@ -28,8 +30,7 @@ import com.mapbox.mapboxsdk.style.sources.TileSet; import com.mapbox.mapboxsdk.style.sources.VectorSource; import com.mapbox.mapboxsdk.testapp.R; import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; + import java.io.IOException; import java.util.ArrayList; @@ -364,7 +365,7 @@ public class RuntimeStyleActivity extends AppCompatActivity { Timber.d("Updating parks source"); // change the source - int park = counter < parks.getFeatures().size() - 1 ? counter : 0; + int park = counter < parks.features().size() - 1 ? counter : 0; GeoJsonSource source = mapboxMap.getSourceAs("dynamic-park-source"); @@ -375,7 +376,7 @@ public class RuntimeStyleActivity extends AppCompatActivity { } List features = new ArrayList<>(); - features.add(parks.getFeatures().get(park)); + features.add(parks.features().get(park)); source.setGeoJson(FeatureCollection.fromFeatures(features)); // Re-post diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java index 1ef59db9b..ca4176be6 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java @@ -15,7 +15,8 @@ import android.view.View; import android.widget.TextView; import android.widget.Toast; -import com.google.gson.GsonBuilder; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; @@ -25,12 +26,7 @@ import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; import com.mapbox.mapboxsdk.style.sources.Source; import com.mapbox.mapboxsdk.testapp.R; import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; -import com.mapbox.services.commons.geojson.Geometry; -import com.mapbox.services.commons.geojson.custom.GeometryDeserializer; -import com.mapbox.services.commons.geojson.custom.PositionDeserializer; -import com.mapbox.services.commons.models.Position; + import java.io.IOException; import java.util.HashMap; @@ -211,14 +207,8 @@ public class SymbolGeneratorActivity extends AppCompatActivity implements OnMapR try { // read local geojson from raw folder String tinyCountriesJson = ResourceUtils.readRawResource(activity, R.raw.tiny_countries); + return FeatureCollection.fromJson(tinyCountriesJson); - // convert geojson to a model - FeatureCollection featureCollection = new GsonBuilder() - .registerTypeAdapter(Geometry.class, new GeometryDeserializer()) - .registerTypeAdapter(Position.class, new PositionDeserializer()) - .create().fromJson(tinyCountriesJson, FeatureCollection.class); - - return featureCollection; } catch (IOException exception) { return null; } @@ -288,7 +278,7 @@ public class SymbolGeneratorActivity extends AppCompatActivity implements OnMapR FeatureCollection featureCollection = params[0]; HashMap imagesMap = new HashMap<>(); - for (Feature feature : featureCollection.getFeatures()) { + for (Feature feature : featureCollection.features()) { String countryName = feature.getStringProperty(FEATURE_ID); TextView textView = new TextView(context); textView.setBackgroundColor(context.getResources().getColor(R.color.blueAccent)); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolLayerActivity.java index d89d71e60..e3a4f4be9 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolLayerActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolLayerActivity.java @@ -11,6 +11,9 @@ import android.view.MenuItem; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Point; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.maps.MapView; @@ -18,9 +21,7 @@ import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.style.layers.SymbolLayer; import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; -import com.mapbox.services.commons.geojson.Point; + import java.util.Arrays; import java.util.List; @@ -62,8 +63,8 @@ public class SymbolLayerActivity extends AppCompatActivity implements MapboxMap. // Add a source FeatureCollection markers = FeatureCollection.fromFeatures(new Feature[] { - Feature.fromGeometry(Point.fromCoordinates(new double[] {4.91638, 52.35673}), featureProperties("Marker 1")), - Feature.fromGeometry(Point.fromCoordinates(new double[] {4.91638, 52.34673}), featureProperties("Marker 2")) + Feature.fromGeometry(Point.fromLngLat(4.91638, 52.35673), featureProperties("Marker 1")), + Feature.fromGeometry(Point.fromLngLat(4.91638, 52.34673), featureProperties("Marker 2")) }); mapboxMap.addSource(new GeoJsonSource(MARKER_SOURCE, markers)); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java index 4a6e62ef7..180e2e726 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java @@ -7,16 +7,16 @@ import android.view.Menu; import android.view.MenuItem; import com.google.gson.JsonObject; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Point; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.style.layers.Property; import com.mapbox.mapboxsdk.style.layers.SymbolLayer; import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; -import com.mapbox.services.commons.geojson.Point; -import com.mapbox.services.commons.models.Position; + import java.util.List; @@ -87,15 +87,13 @@ public class ZoomFunctionSymbolLayerActivity extends AppCompatActivity { } private FeatureCollection createFeatureCollection() { - Position position = isInitialPosition - ? Position.fromCoordinates(-74.01618140, 40.701745) - : Position.fromCoordinates(-73.988097, 40.749864); + Point point = isInitialPosition + ? Point.fromLngLat(-74.01618140, 40.701745) + : Point.fromLngLat(-73.988097, 40.749864); - Point point = Point.fromCoordinates(position); - Feature feature = Feature.fromGeometry(point); JsonObject properties = new JsonObject(); properties.addProperty(KEY_PROPERTY_SELECTED, isSelected); - feature.setProperties(properties); + Feature feature = Feature.fromGeometry(point, properties); return FeatureCollection.fromFeatures(new Feature[] {feature}); } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/GeoParseUtil.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/GeoParseUtil.java index 97f70e33b..c21e47965 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/GeoParseUtil.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/GeoParseUtil.java @@ -2,11 +2,11 @@ package com.mapbox.mapboxsdk.testapp.utils; import android.content.Context; import android.text.TextUtils; + +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Point; import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; -import com.mapbox.services.commons.geojson.Point; -import com.mapbox.services.commons.models.Position; import java.io.BufferedReader; import java.io.IOException; @@ -31,10 +31,10 @@ public class GeoParseUtil { public static List parseGeoJsonCoordinates(String geojsonStr) { List latLngs = new ArrayList<>(); FeatureCollection featureCollection = FeatureCollection.fromJson(geojsonStr); - for (Feature feature : featureCollection.getFeatures()) { - if (feature.getGeometry() instanceof Point) { - Position point = ((Point) feature.getGeometry()).getCoordinates(); - latLngs.add(new LatLng(point.getLatitude(), point.getLongitude())); + for (Feature feature : featureCollection.features()) { + if (feature.geometry() instanceof Point) { + Point point = (Point) feature.geometry(); + latLngs.add(new LatLng(point.latitude(), point.longitude())); } } return latLngs; diff --git a/platform/android/config.cmake b/platform/android/config.cmake index 4832f8b25..e1c36789f 100644 --- a/platform/android/config.cmake +++ b/platform/android/config.cmake @@ -248,8 +248,6 @@ add_library(mbgl-android STATIC platform/android/src/geojson/point.hpp platform/android/src/geojson/polygon.cpp platform/android/src/geojson/polygon.hpp - platform/android/src/geojson/position.cpp - platform/android/src/geojson/position.hpp # Geometry platform/android/src/geometry/lat_lng.cpp diff --git a/platform/android/gradle/dependencies.gradle b/platform/android/gradle/dependencies.gradle index 0c72368d3..7ce0cd619 100644 --- a/platform/android/gradle/dependencies.gradle +++ b/platform/android/gradle/dependencies.gradle @@ -8,7 +8,8 @@ ext { ] versions = [ - mapboxServices: '2.2.9', + mapboxServices: '3.0.0-beta.2', + mapboxTelemetry: '2.2.9', supportLib : '25.4.0', espresso : '3.0.1', testRunner : '1.0.1', @@ -22,9 +23,12 @@ ext { ] dependenciesList = [ - mapboxJavaServices : "com.mapbox.mapboxsdk:mapbox-java-services:${versions.mapboxServices}@jar", - mapboxJavaGeoJSON : "com.mapbox.mapboxsdk:mapbox-java-geojson:${versions.mapboxServices}@jar", - mapboxAndroidTelemetry: "com.mapbox.mapboxsdk:mapbox-android-telemetry:${versions.mapboxServices}@aar", + mapboxJavaServices : "com.mapbox.mapboxsdk:mapbox-sdk-services:${versions.mapboxServices}@jar", + mapboxJavaGeoJSON : "com.mapbox.mapboxsdk:mapbox-sdk-geojson:${versions.mapboxServices}@jar", + mapboxAndroidTelemetry: "com.mapbox.mapboxsdk:mapbox-android-telemetry:${versions.mapboxTelemetry}@aar", + + // for testApp + mapboxJavaTurf : "com.mapbox.mapboxsdk:mapbox-sdk-turf:${versions.mapboxServices}@jar", junit : "junit:junit:${versions.junit}", mockito : "org.mockito:mockito-core:${versions.mockito}", diff --git a/platform/android/src/geojson/conversion/geometry.hpp b/platform/android/src/geojson/conversion/geometry.hpp index 2ca63e2c1..5d2aab4c2 100644 --- a/platform/android/src/geojson/conversion/geometry.hpp +++ b/platform/android/src/geojson/conversion/geometry.hpp @@ -21,88 +21,91 @@ public: jni::JNIEnv& env; /** - * Point (double[]) + * static Point fromLngLat(double longitude,double latitude) */ jni::jobject* operator()(const mapbox::geometry::point &geometry) const { - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/Point")).release(); - static jni::jmethodID* fromCoordinates = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([D)Lcom/mapbox/services/commons/geojson/Point;"); + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/Point")).release(); + static jni::jmethodID* fromLngLat = &jni::GetStaticMethodID(env, *javaClass, "fromLngLat", "(DD)Lcom/mapbox/geojson/Point;"); - // Create Point - jni::LocalObject> position = jni::NewLocalObject(env, toGeoJsonPosition(env, geometry.x, geometry.y)); - return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromCoordinates, position.get())); + return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromLngLat, geometry.x, geometry.y)); } /** - * LineString (double[][]) + * static LineString fromLngLats(List points) */ jni::jobject* operator()(const mapbox::geometry::line_string &geometry) const { - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/LineString")).release(); - static jni::jmethodID* fromCoordinates = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([[D)Lcom/mapbox/services/commons/geojson/LineString;"); + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/LineString")).release(); + static jni::jmethodID* fromLngLats = &jni::GetStaticMethodID(env, *javaClass, "fromLngLats", "(Ljava/util/List;)Lcom/mapbox/geojson/LineString;"); // Create - jni::LocalObject> coordinates = jni::NewLocalObject(env, toGeoJsonCoordinates(env, geometry)); - return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromCoordinates, coordinates.get())); + jni::LocalObject listOfPoints = jni::NewLocalObject(env, toGeoJsonListOfPoints(env, geometry)); + return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromLngLats, listOfPoints.get())); } /** - * MultiPoint (double[][]) + * static MultiPoint fromLngLats(List points) */ jni::jobject* operator()(const mapbox::geometry::multi_point &geometry) const { - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/MultiPoint")).release(); - static jni::jmethodID* fromCoordinates = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([[D)Lcom/mapbox/services/commons/geojson/MultiPoint;"); + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/MultiPoint")).release(); + static jni::jmethodID* fromLngLats = &jni::GetStaticMethodID(env, *javaClass, "fromLngLats", "(Ljava/util/List;)Lcom/mapbox/geojson/MultiPoint;"); // Create - jni::LocalObject> coordinates = jni::NewLocalObject(env, toGeoJsonCoordinates(env, geometry)); - return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromCoordinates, coordinates.get())); + jni::LocalObject coordinates = jni::NewLocalObject(env, toGeoJsonListOfPoints(env, geometry)); + return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromLngLats, coordinates.get())); } /** - * Polygon (double[][][]) + * static Polygon fromLngLats(List> coordinates) */ jni::jobject* operator()(const mapbox::geometry::polygon &geometry) const { - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/Polygon")).release(); - static jni::jmethodID* fromCoordinates = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([[[D)Lcom/mapbox/services/commons/geojson/Polygon;"); + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/Polygon")).release(); + static jni::jmethodID* fromLngLats = &jni::GetStaticMethodID(env, *javaClass, "fromLngLats", "(Ljava/util/List;)Lcom/mapbox/geojson/Polygon;"); // Create - jni::LocalObject> shape = jni::NewLocalObject(env, toShape<>(env, geometry)); - return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromCoordinates, shape.get())); + jni::LocalObject shape = jni::NewLocalObject(env, toShape<>(env, geometry)); + return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromLngLats, shape.get())); } /** - * MultiLineString (double[][][]) + * static MultiLineString fromLngLats(List> points) */ jni::jobject* operator()(const mapbox::geometry::multi_line_string &geometry) const { - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/MultiLineString")).release(); - static jni::jmethodID* fromCoordinates = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([[[D)Lcom/mapbox/services/commons/geojson/MultiLineString;"); + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/MultiLineString")).release(); + static jni::jmethodID* fromLngLats = &jni::GetStaticMethodID(env, *javaClass, "fromLngLats", "(Ljava/util/List;)Lcom/mapbox/geojson/MultiLineString;"); // Create - jni::LocalObject> shape = jni::NewLocalObject(env, toShape<>(env, geometry)); - return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromCoordinates, shape.get())); + jni::LocalObject shape = jni::NewLocalObject(env, toShape<>(env, geometry)); + return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromLngLats, shape.get())); } /** * MultiPolygon (double[][][][]) -> [[[D + Object array == [[[[D + * + * static MultiPolygon fromLngLats(List>> points) */ jni::jobject* operator()(const mapbox::geometry::multi_polygon &geometry) const { - static jni::jclass* listClass = jni::NewGlobalRef(env, &jni::FindClass(env, "[[[D")).release(); - jni::LocalObject> jarray = jni::NewLocalObject(env, &jni::NewObjectArray(env, geometry.size(), *listClass)); + // ArrayList + static jni::jclass* arrayListClass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/util/ArrayList")).release(); + static jni::jmethodID* constructor = &jni::GetMethodID(env, *arrayListClass, "", "(I)V"); + static jni::jmethodID* add = &jni::GetMethodID(env, *arrayListClass, "add", "(ILjava/lang/Object;)V"); + jni::jobject* arrayList = &jni::NewObject(env, *arrayListClass, *constructor, geometry.size()); for(size_t i = 0; i < geometry.size(); i = i + 1) { - jni::LocalObject> shape = jni::NewLocalObject(env, toShape<>(env, geometry.at(i))); - jni::SetObjectArrayElement(env, *jarray, i, shape.get()); + jni::LocalObject shape = jni::NewLocalObject(env, toShape<>(env, geometry.at(i))); + jni::CallMethod(env, arrayList, *add, i, shape.get()); } // Create the MultiPolygon - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/MultiPolygon")).release(); - static jni::jmethodID* fromGeometries = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([[[[D)Lcom/mapbox/services/commons/geojson/MultiPolygon;"); - return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromGeometries, jarray.get())); + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/MultiPolygon")).release(); + static jni::jmethodID* fromGeometries = &jni::GetStaticMethodID(env, *javaClass, "fromLngLats", "(Ljava/util/List;)Lcom/mapbox/geojson/MultiPolygon;"); + return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromGeometries, arrayList)); } /** * GeometryCollection */ jni::jobject* operator()(const mapbox::geometry::geometry_collection &collection) const { - static jni::jclass* geometryClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/Geometry")).release(); + static jni::jclass* geometryClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/Geometry")).release(); jni::LocalObject> jarray = jni::NewLocalObject(env, &jni::NewObjectArray(env, collection.size(), *geometryClass)); for(size_t i = 0; i < collection.size(); i = i + 1) { @@ -112,8 +115,8 @@ public: } // Turn into array list and create the GeometryCollection - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/GeometryCollection")).release(); - static jni::jmethodID* fromGeometries = &jni::GetStaticMethodID(env, *javaClass, "fromGeometries", "(Ljava/util/List;)Lcom/mapbox/services/commons/geojson/GeometryCollection;"); + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/GeometryCollection")).release(); + static jni::jmethodID* fromGeometries = &jni::GetStaticMethodID(env, *javaClass, "fromGeometries", "(Ljava/util/List;)Lcom/mapbox/geojson/GeometryCollection;"); jni::LocalObject list = jni::NewLocalObject(env, toArrayList<>(env, *jarray)); return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromGeometries, list.get())); @@ -122,47 +125,50 @@ public: private: /** - * x, y -> jarray ([x,y]) - */ - static jni::jarray* toGeoJsonPosition(JNIEnv& env, double x, double y) { - jni::jarray& jarray = jni::NewArray(env, 2); - jni::jdouble array[] = {x, y}; - jni::SetArrayRegion(env, jarray, 0, 2, array); - return &jarray; - } + * vector> -> List + */ + static jni::jobject* toGeoJsonListOfPoints(JNIEnv& env, std::vector> points) { - /** - * vector> -> jarray (double[][]) -> [D + Object array == [[D - */ - static jni::jarray* toGeoJsonCoordinates(JNIEnv& env, std::vector> points) { - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "[D")).release(); - jni::jarray& jarray = jni::NewObjectArray(env, points.size(), *javaClass); + // ArrayList + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/util/ArrayList")).release(); + static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "", "(I)V"); + static jni::jmethodID* add = &jni::GetMethodID(env, *javaClass, "add", "(ILjava/lang/Object;)V"); + jni::jobject* arrayList = &jni::NewObject(env, *javaClass, *constructor, points.size()); + + + // Point + static jni::jclass* pointJavaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/geojson/Point")).release(); + static jni::jmethodID* fromLngLat = &jni::GetStaticMethodID(env, *pointJavaClass, "fromLngLat", "(DD)Lcom/mapbox/geojson/Point;"); for(size_t i = 0; i < points.size(); i = i + 1) { mapbox::geometry::point point = points.at(i); - jni::LocalObject> position = jni::NewLocalObject(env, toGeoJsonPosition(env, point.x, point.y)); - jni::SetObjectArrayElement(env, jarray, i, position.get()); + jni::LocalObject pointObject = + jni::NewLocalObject(env, jni::CallStaticMethod(env, *pointJavaClass, *fromLngLat, point.x, point.y)); + jni::CallMethod(env, arrayList, *add, i, pointObject.get()); } - return &jarray; + return arrayList; } /** - * polygon - * multi_line_string - * -> jarray (double[][][]) -> [[D + Object array == [[[D + * geometry -> List> */ template - static jni::jarray* toShape(JNIEnv& env, SHAPE value) { - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "[[D")).release(); - jni::jarray& jarray = jni::NewObjectArray(env, value.size(), *javaClass); + static jni::jobject* toShape(JNIEnv& env, SHAPE value) { + + // ArrayList + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/util/ArrayList")).release(); + static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "", "(I)V"); + static jni::jmethodID* add = &jni::GetMethodID(env, *javaClass, "add", "(ILjava/lang/Object;)V"); + jni::jobject* arrayList = &jni::NewObject(env, *javaClass, *constructor, value.size()); + for(size_t i = 0; i < value.size(); i = i + 1) { - jni::LocalObject> coordinates = jni::NewLocalObject(env, toGeoJsonCoordinates(env, value.at(i))); - jni::SetObjectArrayElement(env, jarray, i, coordinates.get()); + jni::LocalObject listOfPoints = jni::NewLocalObject(env, toGeoJsonListOfPoints(env, value.at(i))); + jni::CallMethod(env, arrayList, *add, i, listOfPoints.get()); } - return &jarray; + return arrayList; } }; diff --git a/platform/android/src/geojson/feature.cpp b/platform/android/src/geojson/feature.cpp index a6b387cd1..d8a4e829e 100644 --- a/platform/android/src/geojson/feature.cpp +++ b/platform/android/src/geojson/feature.cpp @@ -8,11 +8,11 @@ namespace geojson { mbgl::Feature Feature::convert(jni::JNIEnv& env, jni::Object jFeature) { // Convert - auto jGeometry = getGeometry(env, jFeature); - auto jProperties = Feature::getProperties(env, jFeature); + auto jGeometry = geometry(env, jFeature); + auto jProperties = Feature::properties(env, jFeature); std::experimental::optional id; - auto jId = Feature::getId(env, jFeature); + auto jId = Feature::id(env, jFeature); if (jId) { id = { jni::Make(env, jId) }; } @@ -31,18 +31,18 @@ mbgl::Feature Feature::convert(jni::JNIEnv& env, jni::Object jFeature) return feature; } -jni::Object Feature::getGeometry(jni::JNIEnv& env, jni::Object jFeature) { - static auto method = Feature::javaClass.GetMethod ()>(env, "getGeometry"); +jni::Object Feature::geometry(jni::JNIEnv& env, jni::Object jFeature) { + static auto method = Feature::javaClass.GetMethod ()>(env, "geometry"); return jFeature.Call(env, method); } -jni::Object Feature::getProperties(jni::JNIEnv& env, jni::Object jFeature) { - static auto method = Feature::javaClass.GetMethod ()>(env, "getProperties"); +jni::Object Feature::properties(jni::JNIEnv& env, jni::Object jFeature) { + static auto method = Feature::javaClass.GetMethod ()>(env, "properties"); return jFeature.Call(env, method); } -jni::String Feature::getId(jni::JNIEnv& env, jni::Object jFeature) { - static auto method = Feature::javaClass.GetMethod(env, "getId"); +jni::String Feature::id(jni::JNIEnv& env, jni::Object jFeature) { + static auto method = Feature::javaClass.GetMethod(env, "id"); return jFeature.Call(env, method); } diff --git a/platform/android/src/geojson/feature.hpp b/platform/android/src/geojson/feature.hpp index b5d856cc4..ab59d783e 100644 --- a/platform/android/src/geojson/feature.hpp +++ b/platform/android/src/geojson/feature.hpp @@ -16,17 +16,17 @@ namespace geojson { class Feature : private mbgl::util::noncopyable { public: - static constexpr auto Name() { return "com/mapbox/services/commons/geojson/Feature"; }; + static constexpr auto Name() { return "com/mapbox/geojson/Feature"; }; static jni::Object fromGeometry(jni::JNIEnv&, jni::Object, jni::Object, jni::String); static mbgl::Feature convert(jni::JNIEnv&, jni::Object); - static jni::Object getGeometry(jni::JNIEnv&, jni::Object); + static jni::Object geometry(jni::JNIEnv&, jni::Object); - static jni::String getId(jni::JNIEnv&, jni::Object); + static jni::String id(jni::JNIEnv&, jni::Object); - static jni::Object getProperties(jni::JNIEnv&, jni::Object); + static jni::Object properties(jni::JNIEnv&, jni::Object); static jni::Class javaClass; diff --git a/platform/android/src/geojson/feature_collection.cpp b/platform/android/src/geojson/feature_collection.cpp index 2f156532a..59f1e317e 100644 --- a/platform/android/src/geojson/feature_collection.cpp +++ b/platform/android/src/geojson/feature_collection.cpp @@ -7,7 +7,7 @@ namespace android { namespace geojson { mbgl::FeatureCollection FeatureCollection::convert(jni::JNIEnv& env, jni::Object jCollection) { - auto jFeatureList = FeatureCollection::getFeatures(env, jCollection); + auto jFeatureList = FeatureCollection::features(env, jCollection); auto jFeatures = java::util::List::toArray(env, jFeatureList); auto size = size_t(jFeatures.Length(env)); @@ -23,8 +23,8 @@ mbgl::FeatureCollection FeatureCollection::convert(jni::JNIEnv& env, jni::Object return collection; } -jni::Object FeatureCollection::getFeatures(jni::JNIEnv& env, jni::Object jCollection) { - static auto method = FeatureCollection::javaClass.GetMethod ()>(env, "getFeatures"); +jni::Object FeatureCollection::features(jni::JNIEnv& env, jni::Object jCollection) { + static auto method = FeatureCollection::javaClass.GetMethod ()>(env, "features"); return jCollection.Call(env, method); } diff --git a/platform/android/src/geojson/feature_collection.hpp b/platform/android/src/geojson/feature_collection.hpp index 8e9717e82..259ffab37 100644 --- a/platform/android/src/geojson/feature_collection.hpp +++ b/platform/android/src/geojson/feature_collection.hpp @@ -13,11 +13,11 @@ namespace geojson { class FeatureCollection : private mbgl::util::noncopyable { public: - static constexpr auto Name() { return "com/mapbox/services/commons/geojson/FeatureCollection"; }; + static constexpr auto Name() { return "com/mapbox/geojson/FeatureCollection"; }; static mbgl::FeatureCollection convert(jni::JNIEnv&, jni::Object); - static jni::Object getFeatures(jni::JNIEnv&, jni::Object); + static jni::Object features(jni::JNIEnv&, jni::Object); static jni::Class javaClass; diff --git a/platform/android/src/geojson/geometry.cpp b/platform/android/src/geojson/geometry.cpp index 33bb4ee3d..ca19d8fb0 100644 --- a/platform/android/src/geojson/geometry.cpp +++ b/platform/android/src/geojson/geometry.cpp @@ -33,7 +33,7 @@ mapbox::geojson::geometry Geometry::convert(jni::JNIEnv &env, jni::Object jGeometry) { - static auto method = Geometry::javaClass.GetMethod(env, "getType"); + static auto method = Geometry::javaClass.GetMethod(env, "type"); auto jType = jGeometry.Call(env, method); auto type = jni::Make(env, jType); jni::DeleteLocalRef(env, jType); diff --git a/platform/android/src/geojson/geometry.hpp b/platform/android/src/geojson/geometry.hpp index bdcff6bb3..b7f8909f6 100644 --- a/platform/android/src/geojson/geometry.hpp +++ b/platform/android/src/geojson/geometry.hpp @@ -11,7 +11,7 @@ namespace geojson { class Geometry : private mbgl::util::noncopyable { public: - static constexpr auto Name() { return "com/mapbox/services/commons/geojson/Geometry"; }; + static constexpr auto Name() { return "com/mapbox/geojson/Geometry"; }; static mapbox::geojson::geometry convert(jni::JNIEnv&, jni::Object); diff --git a/platform/android/src/geojson/line_string.cpp b/platform/android/src/geojson/line_string.cpp index d0719f253..9e99c72c4 100644 --- a/platform/android/src/geojson/line_string.cpp +++ b/platform/android/src/geojson/line_string.cpp @@ -1,6 +1,6 @@ #include "line_string.hpp" -#include "position.hpp" +#include "point.hpp" namespace mbgl { namespace android { @@ -10,35 +10,35 @@ mapbox::geojson::line_string LineString::convert(jni::JNIEnv &env, jni::Object*/> jPositionList) { +mapbox::geojson::line_string LineString::convert(jni::JNIEnv &env, jni::Object*/> jPointList) { mapbox::geojson::line_string lineString; - if (jPositionList) { - auto jPositionArray = java::util::List::toArray(env, jPositionList); + if (jPointList) { + auto jPointArray = java::util::List::toArray(env, jPointList); - auto size = jPositionArray.Length(env); + auto size = jPointArray.Length(env); for (std::size_t i = 0; i < size; i++) { - auto jPosition = jPositionArray.Get(env, i); - lineString.push_back(Position::convert(env, jPosition)); - jni::DeleteLocalRef(env, jPosition); + auto jPoint = jPointArray.Get(env, i); + lineString.push_back(Point::convert(env, jPoint)); + jni::DeleteLocalRef(env, jPoint); } - jni::DeleteLocalRef(env, jPositionArray); + jni::DeleteLocalRef(env, jPointArray); } return lineString; } -jni::Object LineString::getCoordinates(jni::JNIEnv &env, jni::Object jLineString) { - static auto method = LineString::javaClass.GetMethod ()>(env, "getCoordinates"); +jni::Object LineString::coordinates(jni::JNIEnv &env, jni::Object jLineString) { + static auto method = LineString::javaClass.GetMethod ()>(env, "coordinates"); return jLineString.Call(env, method); } diff --git a/platform/android/src/geojson/line_string.hpp b/platform/android/src/geojson/line_string.hpp index d3be68d0a..86033c2e6 100644 --- a/platform/android/src/geojson/line_string.hpp +++ b/platform/android/src/geojson/line_string.hpp @@ -14,15 +14,15 @@ namespace geojson { class LineString : private mbgl::util::noncopyable { public: - static constexpr auto Name() { return "com/mapbox/services/commons/geojson/LineString"; }; + static constexpr auto Name() { return "com/mapbox/geojson/LineString"; }; static constexpr auto Type() { return "LineString"; }; static mapbox::geojson::line_string convert(jni::JNIEnv&, jni::Object); - static mapbox::geojson::line_string convert(jni::JNIEnv&, jni::Object*/>); + static mapbox::geojson::line_string convert(jni::JNIEnv&, jni::Object*/>); - static jni::Object getCoordinates(jni::JNIEnv&, jni::Object); + static jni::Object coordinates(jni::JNIEnv&, jni::Object); static jni::Class javaClass; diff --git a/platform/android/src/geojson/multi_line_string.cpp b/platform/android/src/geojson/multi_line_string.cpp index b676144bf..c748d4786 100644 --- a/platform/android/src/geojson/multi_line_string.cpp +++ b/platform/android/src/geojson/multi_line_string.cpp @@ -10,27 +10,27 @@ mapbox::geojson::multi_line_string MultiLineString::convert(jni::JNIEnv &env, jn mapbox::geojson::multi_line_string multiLineString; if (jMultiLineString) { - auto jPositionListsList = MultiLineString::getCoordinates(env, jMultiLineString); - multiLineString = MultiLineString::convert(env, jPositionListsList); - jni::DeleteLocalRef(env, jPositionListsList); + auto jPointListsList = MultiLineString::coordinates(env, jMultiLineString); + multiLineString = MultiLineString::convert(env, jPointListsList); + jni::DeleteLocalRef(env, jPointListsList); } return multiLineString; } -mapbox::geojson::multi_line_string MultiLineString::convert(jni::JNIEnv &env, jni::Object>*/> jPositionListsList) { +mapbox::geojson::multi_line_string MultiLineString::convert(jni::JNIEnv &env, jni::Object>*/> jPointListsList) { mapbox::geojson::multi_line_string multiLineString; - if (jPositionListsList) { - auto jPositionListsArray = java::util::List::toArray(env, jPositionListsList); + if (jPointListsList) { + auto jPositionListsArray = java::util::List::toArray(env, jPointListsList); auto size = jPositionListsArray.Length(env); multiLineString.reserve(size); for (std::size_t i = 0; i < size; i++) { - auto jPositionList = jPositionListsArray.Get(env, i); - multiLineString.push_back(LineString::convert(env, jPositionList)); - jni::DeleteLocalRef(env, jPositionList); + auto jPointsList = jPositionListsArray.Get(env, i); + multiLineString.push_back(LineString::convert(env, jPointsList)); + jni::DeleteLocalRef(env, jPointsList); } jni::DeleteLocalRef(env, jPositionListsArray); @@ -39,8 +39,8 @@ mapbox::geojson::multi_line_string MultiLineString::convert(jni::JNIEnv &env, jn return multiLineString; } -jni::Object MultiLineString::getCoordinates(jni::JNIEnv &env, jni::Object jLineString) { - static auto method = MultiLineString::javaClass.GetMethod ()>(env, "getCoordinates"); +jni::Object MultiLineString::coordinates(jni::JNIEnv &env, jni::Object jLineString) { + static auto method = MultiLineString::javaClass.GetMethod ()>(env, "coordinates"); return jLineString.Call(env, method); } diff --git a/platform/android/src/geojson/multi_line_string.hpp b/platform/android/src/geojson/multi_line_string.hpp index af33fe72d..358a6b5dd 100644 --- a/platform/android/src/geojson/multi_line_string.hpp +++ b/platform/android/src/geojson/multi_line_string.hpp @@ -13,15 +13,15 @@ namespace geojson { class MultiLineString : private mbgl::util::noncopyable { public: - static constexpr auto Name() { return "com/mapbox/services/commons/geojson/MultiLineString"; }; + static constexpr auto Name() { return "com/mapbox/geojson/MultiLineString"; }; static constexpr auto Type() { return "MultiLineString"; }; static mapbox::geojson::multi_line_string convert(jni::JNIEnv&, jni::Object); - static mapbox::geojson::multi_line_string convert(jni::JNIEnv&, jni::Object>*/>); + static mapbox::geojson::multi_line_string convert(jni::JNIEnv&, jni::Object>*/>); - static jni::Object getCoordinates(jni::JNIEnv&, jni::Object); + static jni::Object coordinates(jni::JNIEnv&, jni::Object); static jni::Class javaClass; diff --git a/platform/android/src/geojson/multi_point.cpp b/platform/android/src/geojson/multi_point.cpp index f3acdb1ea..4f9ff596b 100644 --- a/platform/android/src/geojson/multi_point.cpp +++ b/platform/android/src/geojson/multi_point.cpp @@ -12,16 +12,16 @@ mapbox::geojson::multi_point MultiPoint::convert(jni::JNIEnv &env, jni::Object(LineString::convert(env, jPositionListsList)); - jni::DeleteLocalRef(env, jPositionListsList); + auto jPointListsList = MultiPoint::coordinates(env, jMultiPoint); + multiPoint = convertExplicit(LineString::convert(env, jPointListsList)); + jni::DeleteLocalRef(env, jPointListsList); } return multiPoint; } -jni::Object MultiPoint::getCoordinates(jni::JNIEnv &env, jni::Object jMultiPoint) { - static auto method = MultiPoint::javaClass.GetMethod ()>(env, "getCoordinates"); +jni::Object MultiPoint::coordinates(jni::JNIEnv &env, jni::Object jMultiPoint) { + static auto method = MultiPoint::javaClass.GetMethod ()>(env, "coordinates"); return jMultiPoint.Call(env, method); } diff --git a/platform/android/src/geojson/multi_point.hpp b/platform/android/src/geojson/multi_point.hpp index 7a698287e..e893e879a 100644 --- a/platform/android/src/geojson/multi_point.hpp +++ b/platform/android/src/geojson/multi_point.hpp @@ -13,13 +13,13 @@ namespace geojson { class MultiPoint : private mbgl::util::noncopyable { public: - static constexpr auto Name() { return "com/mapbox/services/commons/geojson/MultiPoint"; }; + static constexpr auto Name() { return "com/mapbox/geojson/MultiPoint"; }; static constexpr auto Type() { return "MultiPoint"; }; static mapbox::geojson::multi_point convert(jni::JNIEnv&, jni::Object); - static jni::Object getCoordinates(jni::JNIEnv&, jni::Object); + static jni::Object coordinates(jni::JNIEnv&, jni::Object); static jni::Class javaClass; diff --git a/platform/android/src/geojson/multi_polygon.cpp b/platform/android/src/geojson/multi_polygon.cpp index a55884a11..f4eb0f6b2 100644 --- a/platform/android/src/geojson/multi_polygon.cpp +++ b/platform/android/src/geojson/multi_polygon.cpp @@ -10,27 +10,27 @@ mapbox::geojson::multi_polygon MultiPolygon::convert(jni::JNIEnv &env, jni::Obje mapbox::geojson::multi_polygon multiPolygon; if (jMultiPolygon) { - auto jPositionListsListList = MultiPolygon::getCoordinates(env, jMultiPolygon); - auto jPositionListsListArray = java::util::List::toArray(env, jPositionListsListList); + auto jPointListsListList = MultiPolygon::coordinates(env, jMultiPolygon); + auto jPointListsListArray = java::util::List::toArray(env, jPointListsListList); - auto size = jPositionListsListArray.Length(env); + auto size = jPointListsListArray.Length(env); multiPolygon.reserve(size); for (size_t i = 0; i < size; i++) { - auto jPositionListsList = jPositionListsListArray.Get(env, i); + auto jPositionListsList = jPointListsListArray.Get(env, i); multiPolygon.push_back(Polygon::convert(env, jPositionListsList)); jni::DeleteLocalRef(env, jPositionListsList); } - jni::DeleteLocalRef(env, jPositionListsListList); - jni::DeleteLocalRef(env, jPositionListsListArray); + jni::DeleteLocalRef(env, jPointListsListList); + jni::DeleteLocalRef(env, jPointListsListArray); } return multiPolygon; } -jni::Object MultiPolygon::getCoordinates(jni::JNIEnv &env, jni::Object jPolygon) { - static auto method = MultiPolygon::javaClass.GetMethod ()>(env, "getCoordinates"); +jni::Object MultiPolygon::coordinates(jni::JNIEnv &env, jni::Object jPolygon) { + static auto method = MultiPolygon::javaClass.GetMethod ()>(env, "coordinates"); return jPolygon.Call(env, method); } diff --git a/platform/android/src/geojson/multi_polygon.hpp b/platform/android/src/geojson/multi_polygon.hpp index 1f144cffd..6e1dfacfc 100644 --- a/platform/android/src/geojson/multi_polygon.hpp +++ b/platform/android/src/geojson/multi_polygon.hpp @@ -13,13 +13,13 @@ namespace geojson { class MultiPolygon : private mbgl::util::noncopyable { public: - static constexpr auto Name() { return "com/mapbox/services/commons/geojson/MultiPolygon"; }; + static constexpr auto Name() { return "com/mapbox/geojson/MultiPolygon"; }; static constexpr auto Type() { return "MultiPolygon"; }; static mapbox::geojson::multi_polygon convert(jni::JNIEnv&, jni::Object); - static jni::Object getCoordinates(jni::JNIEnv&, jni::Object); + static jni::Object coordinates(jni::JNIEnv&, jni::Object); static jni::Class javaClass; diff --git a/platform/android/src/geojson/point.cpp b/platform/android/src/geojson/point.cpp index 3d19a119d..5feb1b852 100644 --- a/platform/android/src/geojson/point.cpp +++ b/platform/android/src/geojson/point.cpp @@ -1,19 +1,41 @@ #include "point.hpp" +#include "../java/util.hpp" +#include "../java_types.hpp" namespace mbgl { namespace android { namespace geojson { mapbox::geojson::point Point::convert(jni::JNIEnv &env, jni::Object jPoint) { - auto jPosition = Point::getPosition(env, jPoint); - auto point = Position::convert(env, jPosition); - jni::DeleteLocalRef(env, jPosition); + mapbox::geojson::point point; + + if (jPoint) { + auto jDoubleList = Point::coordinates(env, jPoint); + point = Point::convert(env, jDoubleList); + jni::DeleteLocalRef(env, jDoubleList); + } + return point; } -jni::Object Point::getPosition(JNIEnv& env, jni::Object jPoint) { - static auto method = Point::javaClass.GetMethod ()>(env, "getCoordinates"); - return jPoint.Call(env, method); +mapbox::geojson::point Point::convert(jni::JNIEnv &env, jni::Object*/> jDoubleList) { + auto jDoubleArray = java::util::List::toArray(env, jDoubleList); + + jni::jdouble lon = jni::CallMethod(env, + jDoubleArray.Get(env, 0), + *java::Number::doubleValueMethodId); + jni::jdouble lat = jni::CallMethod(env, + jDoubleArray.Get(env, 1), + *java::Number::doubleValueMethodId); + mapbox::geojson::point point(lon, lat); + jni::DeleteLocalRef(env, jDoubleArray); + + return point; +} + +jni::Object Point::coordinates(jni::JNIEnv &env, jni::Object jPoint) { + static auto method = Point::javaClass.GetMethod ()>(env, "coordinates"); + return jPoint.Call(env, method); } void Point::registerNative(jni::JNIEnv &env) { diff --git a/platform/android/src/geojson/point.hpp b/platform/android/src/geojson/point.hpp index 64ac0af9c..c6412299b 100644 --- a/platform/android/src/geojson/point.hpp +++ b/platform/android/src/geojson/point.hpp @@ -3,23 +3,25 @@ #include #include -#include "position.hpp" - #include +#include "../java/util.hpp" + namespace mbgl { namespace android { namespace geojson { class Point : private mbgl::util::noncopyable { public: - static constexpr auto Name() { return "com/mapbox/services/commons/geojson/Point"; }; + static constexpr auto Name() { return "com/mapbox/geojson/Point"; }; static constexpr auto Type() { return "Point"; }; static mapbox::geojson::point convert(jni::JNIEnv&, jni::Object); - static jni::Object getPosition(JNIEnv&, jni::Object); + static mapbox::geojson::point convert(jni::JNIEnv&, jni::Object*/>); + + static jni::Object coordinates(JNIEnv&, jni::Object); static jni::Class javaClass; diff --git a/platform/android/src/geojson/polygon.cpp b/platform/android/src/geojson/polygon.cpp index ef00f9df3..30ba99664 100644 --- a/platform/android/src/geojson/polygon.cpp +++ b/platform/android/src/geojson/polygon.cpp @@ -12,7 +12,7 @@ mapbox::geojson::polygon Polygon::convert(jni::JNIEnv &env, jni::Object mapbox::geojson::polygon polygon; if (jPolygon) { - auto jPositionListsList = Polygon::getCoordinates(env, jPolygon); + auto jPositionListsList = Polygon::coordinates(env, jPolygon); polygon = Polygon::convert(env, jPositionListsList); jni::DeleteLocalRef(env, jPositionListsList); } @@ -20,11 +20,11 @@ mapbox::geojson::polygon Polygon::convert(jni::JNIEnv &env, jni::Object return polygon; } -mapbox::geojson::polygon Polygon::convert(jni::JNIEnv &env, jni::Object>*/> jPositionListsList) { +mapbox::geojson::polygon Polygon::convert(jni::JNIEnv &env, jni::Object>*/> jPointListsList) { mapbox::geojson::polygon polygon; - if (jPositionListsList) { - auto multiLine = MultiLineString::convert(env, jPositionListsList); + if (jPointListsList) { + auto multiLine = MultiLineString::convert(env, jPointListsList); polygon.reserve(multiLine.size()); for (auto&& line : multiLine) { polygon.emplace_back(convertExplicit(std::move(line))); @@ -35,8 +35,8 @@ mapbox::geojson::polygon Polygon::convert(jni::JNIEnv &env, jni::Object Polygon::getCoordinates(jni::JNIEnv &env, jni::Object jPolygon) { - static auto method = Polygon::javaClass.GetMethod ()>(env, "getCoordinates"); +jni::Object Polygon::coordinates(jni::JNIEnv &env, jni::Object jPolygon) { + static auto method = Polygon::javaClass.GetMethod ()>(env, "coordinates"); return jPolygon.Call(env, method); } diff --git a/platform/android/src/geojson/polygon.hpp b/platform/android/src/geojson/polygon.hpp index e5362cedf..a8b2b9382 100644 --- a/platform/android/src/geojson/polygon.hpp +++ b/platform/android/src/geojson/polygon.hpp @@ -13,15 +13,15 @@ namespace geojson { class Polygon : private mbgl::util::noncopyable { public: - static constexpr auto Name() { return "com/mapbox/services/commons/geojson/Polygon"; }; + static constexpr auto Name() { return "com/mapbox/geojson/Polygon"; }; static constexpr auto Type() { return "Polygon"; }; static mapbox::geojson::polygon convert(jni::JNIEnv &, jni::Object); - static mapbox::geojson::polygon convert(jni::JNIEnv&, jni::Object>*/>); + static mapbox::geojson::polygon convert(jni::JNIEnv&, jni::Object>*/>); - static jni::Object getCoordinates(jni::JNIEnv&, jni::Object); + static jni::Object coordinates(jni::JNIEnv&, jni::Object); static jni::Class javaClass; diff --git a/platform/android/src/geojson/position.cpp b/platform/android/src/geojson/position.cpp deleted file mode 100644 index c0e6da388..000000000 --- a/platform/android/src/geojson/position.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "position.hpp" - -namespace mbgl { -namespace android { -namespace geojson { - -mapbox::geojson::point Position::convert(jni::JNIEnv &env, jni::Object jPosition) { - static auto method = Position::javaClass.GetMethod ()>(env, "getCoordinates"); - // Array with 0: longitude, 1: latitude (and optionally 2: altitude) - auto coordinates = jPosition.Call(env, method); - jdouble lngLat[2]; - coordinates.GetRegion(env, 0, lngLat); - mapbox::geojson::point point(lngLat[0], lngLat[1]); - jni::DeleteLocalRef(env, coordinates); - return point; -} - -void Position::registerNative(jni::JNIEnv &env) { - // Lookup the class - javaClass = *jni::Class::Find(env).NewGlobalRef(env).release(); -} - -jni::Class Position::javaClass; - -} // namespace geojson -} // namespace android -} // namespace mbgl \ No newline at end of file diff --git a/platform/android/src/geojson/position.hpp b/platform/android/src/geojson/position.hpp deleted file mode 100644 index 7017a8172..000000000 --- a/platform/android/src/geojson/position.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include - -#include - -namespace mbgl { -namespace android { -namespace geojson { - -class Position : private mbgl::util::noncopyable { -public: - static constexpr auto Name() { return "com/mapbox/services/commons/models/Position"; }; - - static constexpr auto Type() { return "Position"; }; - - static mapbox::geojson::point convert(jni::JNIEnv&, jni::Object); - - static jni::Class javaClass; - - static void registerNative(jni::JNIEnv&); -}; - -} // namespace geojson -} // namespace android -} // namespace mbgl \ No newline at end of file diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp index 88ad0edb9..c2fd1c95a 100755 --- a/platform/android/src/jni.cpp +++ b/platform/android/src/jni.cpp @@ -20,7 +20,6 @@ #include "geojson/multi_polygon.hpp" #include "geojson/point.hpp" #include "geojson/polygon.hpp" -#include "geojson/position.hpp" #include "geometry/lat_lng.hpp" #include "geometry/lat_lng_bounds.hpp" #include "geometry/lat_lng_quad.hpp" @@ -128,7 +127,6 @@ void registerNatives(JavaVM *vm) { geojson::MultiPolygon::registerNative(env); geojson::Point::registerNative(env); geojson::Polygon::registerNative(env); - geojson::Position::registerNative(env); // Geometry LatLng::registerNative(env); From 6afff00ed41e0a143b5eacc2a71a9bd70878da5c Mon Sep 17 00:00:00 2001 From: Pablo Guardiola Date: Fri, 9 Feb 2018 16:46:41 +0100 Subject: [PATCH 027/264] [android] remove unnecessary @jar and @aar dependencies suffixes (#11161) * [android] remove unnecessary @jar and @aar dependencies suffixes * [android] remove lint baseline file from mapbox gl android sdk module and fix lint errors --- .../android/MapboxGLAndroidSDK/.gitignore | 2 - .../android/MapboxGLAndroidSDK/build.gradle | 2 - .../lint-baseline-local.xml | 37 ---------------- .../lint/lint-baseline-ci.xml | 44 ------------------- .../mapboxsdk/maps/widgets/CompassView.java | 4 +- .../res/{values-he => values-iw}/strings.xml | 0 platform/android/gradle/dependencies.gradle | 8 ++-- 7 files changed, 6 insertions(+), 91 deletions(-) delete mode 100644 platform/android/MapboxGLAndroidSDK/.gitignore delete mode 100644 platform/android/MapboxGLAndroidSDK/lint-baseline-local.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/lint/lint-baseline-ci.xml rename platform/android/MapboxGLAndroidSDK/src/main/res/{values-he => values-iw}/strings.xml (100%) diff --git a/platform/android/MapboxGLAndroidSDK/.gitignore b/platform/android/MapboxGLAndroidSDK/.gitignore deleted file mode 100644 index cec211fe8..000000000 --- a/platform/android/MapboxGLAndroidSDK/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -lint-baseline.xml -lint/lint-baseline-local.xml \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle index 0900f700c..12c7cc1e5 100644 --- a/platform/android/MapboxGLAndroidSDK/build.gradle +++ b/platform/android/MapboxGLAndroidSDK/build.gradle @@ -114,7 +114,6 @@ android { lintOptions { disable 'MissingTranslation', 'TypographyQuotes', 'ObsoleteLintCustomCheck', 'MissingPermission' - baseline file("lint-baseline-local.xml") checkAllWarnings true warningsAsErrors false } @@ -148,4 +147,3 @@ apply from: "${rootDir}/gradle/gradle-javadoc.gradle" apply from: "${rootDir}/gradle/gradle-publish.gradle" apply from: "${rootDir}/gradle/gradle-checkstyle.gradle" apply from: "${rootDir}/gradle/gradle-tests-staticblockremover.gradle" -apply from: "${rootDir}/gradle/gradle-lint.gradle" diff --git a/platform/android/MapboxGLAndroidSDK/lint-baseline-local.xml b/platform/android/MapboxGLAndroidSDK/lint-baseline-local.xml deleted file mode 100644 index 0a76f5350..000000000 --- a/platform/android/MapboxGLAndroidSDK/lint-baseline-local.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/platform/android/MapboxGLAndroidSDK/lint/lint-baseline-ci.xml b/platform/android/MapboxGLAndroidSDK/lint/lint-baseline-ci.xml deleted file mode 100644 index fd65c9f62..000000000 --- a/platform/android/MapboxGLAndroidSDK/lint/lint-baseline-ci.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java index 1e604c9be..45f72af1c 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java @@ -6,10 +6,10 @@ import android.support.annotation.NonNull; import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewPropertyAnimatorCompat; import android.support.v4.view.ViewPropertyAnimatorListenerAdapter; +import android.support.v7.widget.AppCompatImageView; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; -import android.widget.ImageView; import com.mapbox.mapboxsdk.maps.MapboxMap; @@ -22,7 +22,7 @@ import com.mapbox.mapboxsdk.maps.MapboxMap; * use {@link com.mapbox.mapboxsdk.maps.UiSettings}. *

*/ -public final class CompassView extends ImageView implements Runnable { +public final class CompassView extends AppCompatImageView implements Runnable { public static final long TIME_WAIT_IDLE = 500; public static final long TIME_MAP_NORTH_ANIMATION = 150; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values-he/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-iw/strings.xml similarity index 100% rename from platform/android/MapboxGLAndroidSDK/src/main/res/values-he/strings.xml rename to platform/android/MapboxGLAndroidSDK/src/main/res/values-iw/strings.xml diff --git a/platform/android/gradle/dependencies.gradle b/platform/android/gradle/dependencies.gradle index 7ce0cd619..99fa3f350 100644 --- a/platform/android/gradle/dependencies.gradle +++ b/platform/android/gradle/dependencies.gradle @@ -23,12 +23,12 @@ ext { ] dependenciesList = [ - mapboxJavaServices : "com.mapbox.mapboxsdk:mapbox-sdk-services:${versions.mapboxServices}@jar", - mapboxJavaGeoJSON : "com.mapbox.mapboxsdk:mapbox-sdk-geojson:${versions.mapboxServices}@jar", - mapboxAndroidTelemetry: "com.mapbox.mapboxsdk:mapbox-android-telemetry:${versions.mapboxTelemetry}@aar", + mapboxJavaServices : "com.mapbox.mapboxsdk:mapbox-sdk-services:${versions.mapboxServices}", + mapboxJavaGeoJSON : "com.mapbox.mapboxsdk:mapbox-sdk-geojson:${versions.mapboxServices}", + mapboxAndroidTelemetry: "com.mapbox.mapboxsdk:mapbox-android-telemetry:${versions.mapboxTelemetry}", // for testApp - mapboxJavaTurf : "com.mapbox.mapboxsdk:mapbox-sdk-turf:${versions.mapboxServices}@jar", + mapboxJavaTurf : "com.mapbox.mapboxsdk:mapbox-sdk-turf:${versions.mapboxServices}", junit : "junit:junit:${versions.junit}", mockito : "org.mockito:mockito-core:${versions.mockito}", From f581adde168ad20c124dcd6db8194dee9f0afff9 Mon Sep 17 00:00:00 2001 From: tobrun Date: Thu, 8 Feb 2018 13:37:37 +0100 Subject: [PATCH 028/264] [android] - don't recreate surface as part of view resize --- .../textureview/TextureViewRenderThread.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java index c34833e9c..1e76ffe3f 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java @@ -4,7 +4,6 @@ import android.graphics.SurfaceTexture; import android.support.annotation.NonNull; import android.support.annotation.UiThread; import android.view.TextureView; - import com.mapbox.mapboxsdk.maps.renderer.egl.EGLConfigChooser; import java.lang.ref.WeakReference; @@ -219,13 +218,6 @@ class TextureViewRenderThread extends Thread implements TextureView.SurfaceTextu break; } - // Check if the size has changed - if (sizeChanged) { - recreateSurface = true; - sizeChanged = false; - break; - } - // Reset the request render flag now, so we can catch new requests // while rendering requestRender = false; @@ -273,6 +265,12 @@ class TextureViewRenderThread extends Thread implements TextureView.SurfaceTextu continue; } + if (sizeChanged) { + mapRenderer.onSurfaceChanged(gl, w, h); + sizeChanged = false; + continue; + } + // Don't continue without a surface if (eglHolder.eglSurface == EGL10.EGL_NO_SURFACE) { continue; From ff8d1e4b50e59421bc187874cef213933ad5b311 Mon Sep 17 00:00:00 2001 From: tobrun Date: Tue, 6 Feb 2018 13:41:16 +0100 Subject: [PATCH 029/264] [android] - programmatically create GlSurfaceView --- .../src/main/java/com/mapbox/mapboxsdk/maps/MapView.java | 4 ++-- .../src/main/res/layout/mapbox_mapview_internal.xml | 7 ------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index 2e7d4c427..401d4716f 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -304,7 +304,7 @@ public class MapView extends FrameLayout { addView(textureView, 0); } else { - GLSurfaceView glSurfaceView = (GLSurfaceView) findViewById(R.id.surfaceView); + GLSurfaceView glSurfaceView = new GLSurfaceView(getContext()); glSurfaceView.setZOrderMediaOverlay(mapboxMapOptions.getRenderSurfaceOnTop()); mapRenderer = new GLSurfaceViewMapRenderer(getContext(), glSurfaceView, options.getLocalIdeographFontFamily()) { @Override @@ -314,7 +314,7 @@ public class MapView extends FrameLayout { } }; - glSurfaceView.setVisibility(View.VISIBLE); + addView(glSurfaceView, 0); } nativeMapView = new NativeMapView(this, mapRenderer); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml index df7ccaaca..29ff49f47 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml @@ -1,13 +1,6 @@ - - Date: Tue, 6 Feb 2018 14:42:25 +0100 Subject: [PATCH 030/264] [android] - add optional location provider lost to proguard configuration --- platform/android/MapboxGLAndroidSDK/proguard-rules.pro | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDK/proguard-rules.pro b/platform/android/MapboxGLAndroidSDK/proguard-rules.pro index b5a1d82c8..3b8adac5a 100644 --- a/platform/android/MapboxGLAndroidSDK/proguard-rules.pro +++ b/platform/android/MapboxGLAndroidSDK/proguard-rules.pro @@ -12,4 +12,7 @@ # config for okhttp 3.8.0, https://github.com/square/okhttp/pull/3354 -dontwarn okio.** -dontwarn javax.annotation.Nullable --dontwarn javax.annotation.ParametersAreNonnullByDefault \ No newline at end of file +-dontwarn javax.annotation.ParametersAreNonnullByDefault + +# config for optional location provider https://github.com/mapbox/mapbox-gl-native/issues/10960 +-dontwarn com.mapzen.android.lost.api** \ No newline at end of file From 0eb62576cecbb5ee6b22d64ae263ceb63dcfca1d Mon Sep 17 00:00:00 2001 From: tobrun Date: Tue, 6 Feb 2018 14:57:45 +0100 Subject: [PATCH 031/264] [android] - introduce mapview weak reference in global layout listener --- .../com/mapbox/mapboxsdk/maps/MapView.java | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index 401d4716f..ccc48ce78 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -22,7 +22,6 @@ import android.view.ViewTreeObserver; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ZoomButtonsController; - import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.annotations.Annotation; import com.mapbox.mapboxsdk.annotations.MarkerViewManager; @@ -37,19 +36,18 @@ import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings; import com.mapbox.mapboxsdk.net.ConnectivityReceiver; import com.mapbox.mapboxsdk.storage.FileSource; import com.mapbox.services.android.telemetry.MapboxTelemetry; +import timber.log.Timber; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.opengles.GL10; - -import timber.log.Timber; - import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_MAP_NORTH_ANIMATION; import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_WAIT_IDLE; @@ -133,17 +131,7 @@ public class MapView extends FrameLayout { setContentDescription(context.getString(R.string.mapbox_mapActionDescription)); setWillNotDraw(false); - getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - getViewTreeObserver().removeOnGlobalLayoutListener(this); - } else { - getViewTreeObserver().removeGlobalOnLayoutListener(this); - } - initialiseDrawingSurface(options); - } - }); + getViewTreeObserver().addOnGlobalLayoutListener(new MapViewLayoutListener(this, options)); } private void initialiseMap() { @@ -879,6 +867,30 @@ public class MapView extends FrameLayout { void onMapChanged(@MapChange int change); } + private static class MapViewLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener { + + private WeakReference mapViewWeakReference; + private MapboxMapOptions options; + + MapViewLayoutListener(MapView mapView, MapboxMapOptions options) { + this.mapViewWeakReference = new WeakReference<>(mapView); + this.options = options; + } + + @Override + public void onGlobalLayout() { + MapView mapView = mapViewWeakReference.get(); + if (mapView != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + mapView.getViewTreeObserver().removeOnGlobalLayoutListener(this); + } else { + mapView.getViewTreeObserver().removeGlobalOnLayoutListener(this); + } + mapView.initialiseDrawingSurface(options); + } + } + } + private class FocalPointInvalidator implements FocalPointChangeListener { private final List focalPointChangeListeners = new ArrayList<>(); From 22cbca41e58b135377734b4434efa5e6439a68fd Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 2 Feb 2018 10:30:05 +0100 Subject: [PATCH 032/264] [android] - don't invoke onLowMemory on map when the map isn't fully created yet --- .../src/main/java/com/mapbox/mapboxsdk/maps/MapView.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index ccc48ce78..9f591ddca 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -474,7 +474,9 @@ public class MapView extends FrameLayout { */ @UiThread public void onLowMemory() { - nativeMapView.onLowMemory(); + if (nativeMapView != null) { + nativeMapView.onLowMemory(); + } } /** From f3b32599061402316b0d1ab698301a8941c51ff5 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 9 Feb 2018 14:48:14 +0100 Subject: [PATCH 033/264] [android] - update changelog for 5.4.1 --- platform/android/CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 88ce0cf69..2721d0caf 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,6 +2,13 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. +## 5.4.1 - February 9, 2018 + - Don't recreate TextureView surface as part of view resizing, solves OOM crashes [#11148](https://github.com/mapbox/mapbox-gl-native/pull/11148) + - Don't invoke OnLowMemory before map is ready, solves startup crash on low memory devices [#11109](https://github.com/mapbox/mapbox-gl-native/pull/11109) + - Programmatically create GLSurfaceView, solves fragment bug [#11124](https://github.com/mapbox/mapbox-gl-native/pull/11124) + - Proguard config for optional location provider, solves obfuscation warnings [#11127](https://github.com/mapbox/mapbox-gl-native/pull/11127) + - MapView weak reference in global layout listener, solves memory leak [#11128](https://github.com/mapbox/mapbox-gl-native/pull/11128) + ## 5.4.0 - January 30, 2018 - Blacklist Adreno 2xx GPU for VAO support [#11047](https://github.com/mapbox/mapbox-gl-native/pull/11047) - Bearing tracking mode GPS_NORTH_FACING [#11095](https://github.com/mapbox/mapbox-gl-native/pull/11095) From 0b27909b8b9959a14f9075d3b61c33c93ddd8c31 Mon Sep 17 00:00:00 2001 From: Benjamin Jaeger Date: Tue, 23 Jan 2018 15:33:29 -0500 Subject: [PATCH 034/264] NativeMapView.addImage without copying into Java --- .../com/mapbox/mapboxsdk/maps/NativeMapView.java | 14 ++------------ platform/android/src/native_map_view.cpp | 13 +++---------- platform/android/src/native_map_view.hpp | 2 +- 3 files changed, 6 insertions(+), 23 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java index 785b04577..c1e9053df 100755 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java @@ -758,20 +758,11 @@ final class NativeMapView { return; } - // Check/correct config - if (image.getConfig() != Bitmap.Config.ARGB_8888) { - image = image.copy(Bitmap.Config.ARGB_8888, false); - } - - // Get pixels - ByteBuffer buffer = ByteBuffer.allocate(image.getByteCount()); - image.copyPixelsToBuffer(buffer); - // Determine pixel ratio float density = image.getDensity() == Bitmap.DENSITY_NONE ? Bitmap.DENSITY_NONE : image.getDensity(); float pixelRatio = density / DisplayMetrics.DENSITY_DEFAULT; - nativeAddImage(name, image.getWidth(), image.getHeight(), pixelRatio, buffer.array()); + nativeAddImage(name, image, pixelRatio); } public void addImages(@NonNull HashMap bitmapHashMap) { @@ -1032,8 +1023,7 @@ final class NativeMapView { private native void nativeRemoveSource(Source source, long sourcePtr); - private native void nativeAddImage(String name, int width, int height, float pixelRatio, - byte[] array); + private native void nativeAddImage(String name, Bitmap bitmap, float pixelRatio); private native void nativeAddImages(Image[] images); diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp index 36a73fee3..eefb578f3 100755 --- a/platform/android/src/native_map_view.cpp +++ b/platform/android/src/native_map_view.cpp @@ -893,16 +893,9 @@ void NativeMapView::removeSource(JNIEnv& env, jni::Object obj, jlong sou source->removeFromMap(env, obj, *map); } -void NativeMapView::addImage(JNIEnv& env, jni::String name, jni::jint w, jni::jint h, jni::jfloat scale, jni::Array pixels) { - jni::NullCheck(env, &pixels); - std::size_t size = pixels.Length(env); - - mbgl::PremultipliedImage premultipliedImage({ static_cast(w), static_cast(h) }); - if (premultipliedImage.bytes() != uint32_t(size)) { - throw mbgl::util::SpriteImageException("Sprite image pixel count mismatch"); - } - - jni::GetArrayRegion(env, *pixels, 0, size, reinterpret_cast(premultipliedImage.data.get())); +void NativeMapView::addImage(JNIEnv& env, jni::String name, jni::Object bitmap, jni::jfloat scale) { + jni::NullCheck(env, &bitmap); + mbgl::PremultipliedImage premultipliedImage = Bitmap::GetImage(env, bitmap); map->getStyle().addImage(std::make_unique( jni::Make(env, name), diff --git a/platform/android/src/native_map_view.hpp b/platform/android/src/native_map_view.hpp index 507d77ac5..9ed333d2d 100755 --- a/platform/android/src/native_map_view.hpp +++ b/platform/android/src/native_map_view.hpp @@ -236,7 +236,7 @@ public: void removeSource(JNIEnv&, jni::Object, jlong nativePtr); - void addImage(JNIEnv&, jni::String, jni::jint, jni::jint, jni::jfloat, jni::Array); + void addImage(JNIEnv&, jni::String, jni::Object bitmap, jni::jfloat); void addImages(JNIEnv&, jni::Array>); From ff747a8c53805a270451cd6052d02073e45546a7 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 2 Feb 2018 11:16:01 +0100 Subject: [PATCH 035/264] [android] - add conversion of non ARGB_8888 images to c++, expose Bitmap#copy on the c++ side --- .../java/com/mapbox/mapboxsdk/maps/NativeMapView.java | 5 +---- platform/android/src/bitmap.cpp | 10 ++++++++-- platform/android/src/bitmap.hpp | 1 + 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java index c1e9053df..06802180f 100755 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java @@ -759,10 +759,7 @@ final class NativeMapView { } // Determine pixel ratio - float density = image.getDensity() == Bitmap.DENSITY_NONE ? Bitmap.DENSITY_NONE : image.getDensity(); - float pixelRatio = density / DisplayMetrics.DENSITY_DEFAULT; - - nativeAddImage(name, image, pixelRatio); + nativeAddImage(name, image, image.getDensity() / DisplayMetrics.DENSITY_DEFAULT); } public void addImages(@NonNull HashMap bitmapHashMap) { diff --git a/platform/android/src/bitmap.cpp b/platform/android/src/bitmap.cpp index 50088116f..46e725305 100644 --- a/platform/android/src/bitmap.cpp +++ b/platform/android/src/bitmap.cpp @@ -110,8 +110,7 @@ PremultipliedImage Bitmap::GetImage(jni::JNIEnv& env, jni::Object bitmap } if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) { - // TODO: convert - throw std::runtime_error("bitmap decoding: bitmap format invalid"); + bitmap = Bitmap::Copy(env, bitmap); } const PixelGuard guard(env, bitmap); @@ -128,5 +127,12 @@ PremultipliedImage Bitmap::GetImage(jni::JNIEnv& env, jni::Object bitmap return { Size{ info.width, info.height }, std::move(pixels) }; } +jni::Object Bitmap::Copy(jni::JNIEnv& env, jni::Object bitmap) { + using Signature = jni::Object(jni::Object, jni::jboolean); + auto static method = _class.GetMethod(env, "copy"); + auto config = Bitmap::Config::Create(env, Bitmap::Config::Value::ARGB_8888); + return bitmap.Call(env, method, config, (jni::jboolean) false); +} + } // namespace android } // namespace mbgl diff --git a/platform/android/src/bitmap.hpp b/platform/android/src/bitmap.hpp index f64f42ae8..c4e41db1e 100644 --- a/platform/android/src/bitmap.hpp +++ b/platform/android/src/bitmap.hpp @@ -43,6 +43,7 @@ public: static PremultipliedImage GetImage(jni::JNIEnv&, jni::Object); static jni::Object CreateBitmap(jni::JNIEnv&, const PremultipliedImage&); + static jni::Object Copy(jni::JNIEnv&, jni::Object); private: static jni::Class _class; From b8240f725cbfdb4b5369b050c933acdd4e42b705 Mon Sep 17 00:00:00 2001 From: Pablo Guardiola Date: Tue, 13 Feb 2018 12:23:17 +0100 Subject: [PATCH 036/264] [android] Integration of the new events library (#10999) (#11183) * [android] integration of the new events library * JNI Bug - current build with JNI bug * fix #10999 comments * Clean-up - clean-up timbers and test code * [android] fix sdk identifier and sdk version * [android] merge from master (MAS 3.0) * [android] bump events lib version to 3.0.0-beta.1 and remove never used methods from math utils class --- .../android/MapboxGLAndroidSDK/build.gradle | 2 + .../mapbox/mapboxsdk/EmptyLocationSource.java | 107 ---------- .../java/com/mapbox/mapboxsdk/Mapbox.java | 31 +-- .../mapboxsdk/camera/CameraPosition.java | 2 +- .../mapboxsdk/location/LocationSource.java | 199 ------------------ .../mapboxsdk/location/package-info.java | 4 - .../maps/AttributionDialogManager.java | 5 +- .../com/mapbox/mapboxsdk/maps/Events.java | 36 ++++ .../mapboxsdk/maps/MapGestureDetector.java | 111 ++++++---- .../com/mapbox/mapboxsdk/maps/MapView.java | 22 +- .../mapboxsdk/maps/MapboxEventWrapper.java | 57 ----- .../com/mapbox/mapboxsdk/maps/MapboxMap.java | 2 +- .../mapboxsdk/maps/TrackingSettings.java | 6 +- .../maps/widgets/MyLocationView.java | 6 +- .../style/sources/GeoJsonOptions.java | 2 +- .../com/mapbox/mapboxsdk/utils/MathUtils.java | 49 +++++ .../java/com/mapbox/mapboxsdk/MapboxTest.java | 2 +- .../MapboxGLAndroidSDKTestApp/build.gradle | 11 +- .../src/main/AndroidManifest.xml | 2 +- .../activity/FeatureOverviewActivity.java | 4 +- .../userlocation/BaseLocationActivity.java | 4 +- .../userlocation/MockLocationEngine.java | 4 +- .../MyLocationDrawableActivity.java | 2 +- .../userlocation/MyLocationTintActivity.java | 2 +- .../MyLocationTrackingModeActivity.java | 2 +- platform/android/build.gradle | 1 + platform/android/gradle/dependencies.gradle | 3 +- 27 files changed, 209 insertions(+), 469 deletions(-) delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/EmptyLocationSource.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationSource.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/package-info.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Events.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxEventWrapper.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle index 12c7cc1e5..e2e088185 100644 --- a/platform/android/MapboxGLAndroidSDK/build.gradle +++ b/platform/android/MapboxGLAndroidSDK/build.gradle @@ -25,6 +25,8 @@ android { minSdkVersion androidVersions.minSdkVersion targetSdkVersion androidVersions.targetSdkVersion buildConfigField "String", "GIT_REVISION_SHORT", String.format("\"%s\"", getGitRevision()) + buildConfigField "String", "MAPBOX_SDK_IDENTIFIER", String.format("\"%s\"", "mapbox-maps-android") + buildConfigField "String", "MAPBOX_SDK_VERSION", String.format("\"%s\"", project.VERSION_NAME) buildConfigField "String", "MAPBOX_VERSION_STRING", String.format("\"Mapbox/%s\"", project.VERSION_NAME) buildConfigField "String", "MAPBOX_EVENTS_USER_AGENT", String.format("\"MapboxEventsAndroid/%s\"", project.VERSION_NAME) } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/EmptyLocationSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/EmptyLocationSource.java deleted file mode 100644 index 8ea7e61ee..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/EmptyLocationSource.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.mapbox.mapboxsdk; - - -import android.location.Location; -import android.support.annotation.Nullable; - -import com.mapbox.mapboxsdk.location.LocationSource; -import com.mapbox.services.android.telemetry.location.LocationEngine; -import com.mapbox.services.android.telemetry.location.LocationEngineListener; - -class EmptyLocationSource extends LocationSource { - - /** - * Activate the location engine which will connect whichever location provider you are using. You'll need to call - * this before requesting user location updates using {@link LocationEngine#requestLocationUpdates()}. - */ - @Override - public void activate() { - // Intentionally left empty - } - - /** - * Disconnect the location engine which is useful when you no longer need location updates or requesting the users - * {@link LocationEngine#getLastLocation()}. Before deactivating, you'll need to stop request user location updates - * using {@link LocationEngine#removeLocationUpdates()}. - */ - @Override - public void deactivate() { - // Intentionally left empty - } - - /** - * Check if your location provider has been activated/connected. This is mainly used internally but is also useful in - * the rare case when you'd like to know if your location engine is connected or not. - * - * @return boolean true if the location engine has been activated/connected, else false. - */ - @Override - public boolean isConnected() { - return false; - } - - /** - * Returns the Last known location is the location provider is connected and location permissions are granted. - * - * @return the last known location - */ - @Override - @Nullable - public Location getLastLocation() { - return null; - } - - /** - * Request location updates to the location provider. - */ - @Override - public void requestLocationUpdates() { - // Intentionally left empty - } - - /** - * Dismiss ongoing location update to the location provider. - */ - @Override - public void removeLocationUpdates() { - // Intentionally left empty - } - - /** - * Invoked when the Location has changed. - * - * @param location the new location - */ - @Override - public void onLocationChanged(Location location) { - // Intentionally left empty - } - - /** - * Useful when you'd like to add a location listener to handle location connections and update events. It is important - * to note, that the callback will continue getting called even when your application isn't in the foreground. - * Therefore, it is a good idea to use {@link LocationEngine#removeLocationEngineListener(LocationEngineListener)} - * inside your activities {@code onStop()} method. - * - * @param listener A {@link LocationEngineListener} which you'd like to add to your location engine. - * @since 2.0.0 - */ - @Override - public void addLocationEngineListener(LocationEngineListener listener) { - // Intentionally left empty - } - - /** - * If you no longer need your {@link LocationEngineListener} to be invoked with every location update, use this - * method to remove it. It's also important to remove your listeners before the activity is destroyed to prevent any - * potential memory leaks. - * - * @param listener the {@link LocationEngineListener} you'd like to remove from this {@link LocationEngine}. - * @return true. - * @since 2.0.0 - */ - @Override - public boolean removeLocationEngineListener(LocationEngineListener listener) { - return true; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java index b67b6e96f..853ea1c11 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java @@ -8,16 +8,12 @@ import android.support.annotation.NonNull; import android.support.annotation.UiThread; import android.text.TextUtils; +import com.mapbox.android.core.location.LocationEngine; +import com.mapbox.android.core.location.LocationEnginePriority; +import com.mapbox.android.core.location.LocationEngineProvider; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.exceptions.MapboxConfigurationException; -import com.mapbox.mapboxsdk.location.LocationSource; import com.mapbox.mapboxsdk.net.ConnectivityReceiver; -import com.mapbox.services.android.telemetry.MapboxTelemetry; -import com.mapbox.services.android.telemetry.location.LocationEngine; -import com.mapbox.services.android.telemetry.location.LocationEnginePriority; -import com.mapbox.services.android.telemetry.location.LocationEngineProvider; - -import timber.log.Timber; /** * The entry point to initialize the Mapbox Android SDK. @@ -56,15 +52,9 @@ public final class Mapbox { INSTANCE = new Mapbox(appContext, accessToken, locationEngine); locationEngine.setPriority(LocationEnginePriority.NO_POWER); - try { - MapboxTelemetry.getInstance().initialize( - appContext, accessToken, BuildConfig.MAPBOX_EVENTS_USER_AGENT, locationEngine); - } catch (Exception exception) { - Timber.e(exception, "Unable to instantiate Mapbox telemetry"); - } - ConnectivityReceiver.instance(appContext); } + return INSTANCE; } @@ -145,23 +135,12 @@ public final class Mapbox { return (activeNetwork != null && activeNetwork.isConnected()); } - /** - * Returns a location source instance with empty methods. - * - * @return an empty location source implementation - * @deprecated Replaced by {@link Mapbox#getLocationEngine()} - */ - @Deprecated - public static LocationSource getLocationSource() { - return new EmptyLocationSource(); - } - - /** * Returns the location engine used by the SDK. * * @return the location engine configured */ + // TODO Do we need to expose this? public static LocationEngine getLocationEngine() { return INSTANCE.locationEngine; } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java index c2f19072d..e732b2525 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java @@ -8,7 +8,7 @@ import android.support.annotation.FloatRange; import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.services.android.telemetry.utils.MathUtils; +import com.mapbox.mapboxsdk.utils.MathUtils; /** * Resembles the position, angle, zoom and tilt of the user's viewpoint. diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationSource.java deleted file mode 100644 index 131358715..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationSource.java +++ /dev/null @@ -1,199 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.content.Context; -import android.location.Location; -import android.support.annotation.Nullable; - -import com.mapbox.mapboxsdk.Mapbox; -import com.mapbox.services.android.telemetry.location.LocationEngine; -import com.mapbox.services.android.telemetry.location.LocationEngineListener; -import com.mapbox.services.android.telemetry.location.LocationEnginePriority; -import com.mapzen.android.lost.api.LocationListener; -import com.mapzen.android.lost.api.LocationRequest; -import com.mapzen.android.lost.api.LocationServices; -import com.mapzen.android.lost.api.LostApiClient; - -/** - * LocationEngine using the Open Source Lost library - * Manages locational updates. Contains methods to register and unregister location listeners. - *
    - *
  • You can register a LocationEngineListener with LocationSource#addLocationEngineListener(LocationEngineListener) - * to receive location updates.
  • - *
  • You can unregister a LocationEngineListener with - * LocationEngine#removeLocationEngineListener(LocationEngineListener)} to stop receiving location updates.
  • - *
- *

- * Note: If registering a listener in your Activity.onStart() implementation, you should unregister it in - * Activity.onStop(). (You won't receive location updates when paused, and this will cut down on unnecessary system - * overhead). Do not unregister in Activity.onSaveInstanceState(), because this won't be called if the user moves back - * in the history stack. - *

- * - * @deprecated Use a {@link Mapbox#getLocationEngine()} instead. - */ -@Deprecated -public class LocationSource extends LocationEngine implements LostApiClient.ConnectionCallbacks, LocationListener { - - private Context context; - private LostApiClient lostApiClient; - - /** - * Constructs a location source instance. - * - * @param context the context from which the Application context will be derived. - */ - public LocationSource(Context context) { - super(); - this.context = context.getApplicationContext(); - lostApiClient = new LostApiClient.Builder(this.context) - .addConnectionCallbacks(this) - .build(); - } - - /** - * Constructs a location source instance. - * Needed to create empty location source implementations. - */ - public LocationSource() { - } - - /** - * Activate the location engine which will connect whichever location provider you are using. You'll need to call - * this before requesting user location updates using {@link LocationEngine#requestLocationUpdates()}. - */ - @Override - public void activate() { - connect(); - } - - /** - * Disconnect the location engine which is useful when you no longer need location updates or requesting the users - * {@link LocationEngine#getLastLocation()}. Before deactivating, you'll need to stop request user location updates - * using {@link LocationEngine#removeLocationUpdates()}. - */ - @Override - public void deactivate() { - if (lostApiClient != null && lostApiClient.isConnected()) { - lostApiClient.disconnect(); - } - } - - /** - * Check if your location provider has been activated/connected. This is mainly used internally but is also useful in - * the rare case when you'd like to know if your location engine is connected or not. - * - * @return boolean true if the location engine has been activated/connected, else false. - */ - @Override - public boolean isConnected() { - return lostApiClient.isConnected(); - } - - /** - * Invoked when the location provider has connected. - */ - @Override - public void onConnected() { - for (LocationEngineListener listener : locationListeners) { - listener.onConnected(); - } - } - - /** - * Invoked when the location provider connection has been suspended. - */ - @Override - public void onConnectionSuspended() { - // Empty - } - - /** - * Returns the Last known location is the location provider is connected and location permissions are granted. - * - * @return the last known location - */ - @Override - @Nullable - public Location getLastLocation() { - if (lostApiClient.isConnected()) { - //noinspection MissingPermission - return LocationServices.FusedLocationApi.getLastLocation(lostApiClient); - } - return null; - } - - /** - * Request location updates to the location provider. - */ - @Override - public void requestLocationUpdates() { - LocationRequest request = LocationRequest.create(); - - if (interval != null) { - request.setInterval(interval); - } - if (fastestInterval != null) { - request.setFastestInterval(fastestInterval); - } - if (smallestDisplacement != null) { - request.setSmallestDisplacement(smallestDisplacement); - } - - if (priority == LocationEnginePriority.NO_POWER) { - request.setPriority(LocationRequest.PRIORITY_NO_POWER); - } else if (priority == LocationEnginePriority.LOW_POWER) { - request.setPriority(LocationRequest.PRIORITY_LOW_POWER); - } else if (priority == LocationEnginePriority.BALANCED_POWER_ACCURACY) { - request.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY); - } else if (priority == LocationEnginePriority.HIGH_ACCURACY) { - request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); - } - - if (lostApiClient.isConnected()) { - //noinspection MissingPermission - LocationServices.FusedLocationApi.requestLocationUpdates(lostApiClient, request, this); - } - } - - /** - * Dismiss ongoing location update to the location provider. - */ - @Override - public void removeLocationUpdates() { - if (lostApiClient.isConnected()) { - LocationServices.FusedLocationApi.removeLocationUpdates(lostApiClient, this); - } - } - - /** - * Returns the location engine type. - * - * @return Lost type - */ - @Override - public Type obtainType() { - return Type.LOST; - } - - /** - * Invoked when the Location has changed. - * - * @param location the new location - */ - @Override - public void onLocationChanged(Location location) { - for (LocationEngineListener listener : locationListeners) { - listener.onLocationChanged(location); - } - } - - private void connect() { - if (lostApiClient != null) { - if (lostApiClient.isConnected()) { - onConnected(); - } else { - lostApiClient.connect(); - } - } - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/package-info.java deleted file mode 100644 index b27559e95..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Contains the Mapbox Maps Android Location API classes. - */ -package com.mapbox.mapboxsdk.location; \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java index b12757c81..2bcbd5ce4 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java @@ -16,7 +16,6 @@ import com.mapbox.mapboxsdk.attribution.Attribution; import com.mapbox.mapboxsdk.attribution.AttributionParser; import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.style.sources.Source; -import com.mapbox.services.android.telemetry.MapboxTelemetry; import java.util.ArrayList; import java.util.List; @@ -88,7 +87,7 @@ public class AttributionDialogManager implements View.OnClickListener, DialogInt builder.setPositiveButton(R.string.mapbox_attributionTelemetryPositive, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - MapboxTelemetry.getInstance().setTelemetryEnabled(true); + Events.obtainTelemetry().enable(); dialog.cancel(); } }); @@ -102,7 +101,7 @@ public class AttributionDialogManager implements View.OnClickListener, DialogInt builder.setNegativeButton(R.string.mapbox_attributionTelemetryNegative, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - MapboxTelemetry.getInstance().setTelemetryEnabled(false); + Events.obtainTelemetry().disable(); dialog.cancel(); } }); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Events.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Events.java new file mode 100644 index 000000000..a68d4763a --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Events.java @@ -0,0 +1,36 @@ +package com.mapbox.mapboxsdk.maps; + + +import com.mapbox.android.telemetry.MapboxTelemetry; +import com.mapbox.android.telemetry.TelemetryEnabler; +import com.mapbox.mapboxsdk.BuildConfig; +import com.mapbox.mapboxsdk.Mapbox; + +class Events { + static final String TWO_FINGER_TAP = "TwoFingerTap"; + static final String DOUBLE_TAP = "DoubleTap"; + static final String SINGLE_TAP = "SingleTap"; + static final String PAN = "Pan"; + static final String PINCH = "Pinch"; + static final String ROTATION = "Rotation"; + static final String PITCH = "Pitch"; + private MapboxTelemetry telemetry; + + private Events() { + telemetry = new MapboxTelemetry(Mapbox.getApplicationContext(), Mapbox.getAccessToken(), + BuildConfig.MAPBOX_EVENTS_USER_AGENT); + TelemetryEnabler.State telemetryState = TelemetryEnabler.retrieveTelemetryStateFromPreferences(); + if (TelemetryEnabler.State.NOT_INITIALIZED.equals(telemetryState) + || TelemetryEnabler.State.ENABLED.equals(telemetryState)) { + telemetry.enable(); + } + } + + private static class EventsHolder { + private static final Events INSTANCE = new Events(); + } + + static MapboxTelemetry obtainTelemetry() { + return EventsHolder.INSTANCE.telemetry; + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java index 1788cb442..8047e1980 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java @@ -5,7 +5,6 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.PointF; -import android.location.Location; import android.support.annotation.Nullable; import android.support.v4.view.GestureDetectorCompat; import android.support.v4.view.ScaleGestureDetectorCompat; @@ -19,12 +18,12 @@ import android.view.ViewConfiguration; import com.almeros.android.multitouch.gesturedetectors.RotateGestureDetector; import com.almeros.android.multitouch.gesturedetectors.ShoveGestureDetector; import com.almeros.android.multitouch.gesturedetectors.TwoFingerGestureDetector; +import com.mapbox.android.telemetry.Event; +import com.mapbox.android.telemetry.MapEventFactory; +import com.mapbox.android.telemetry.MapState; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.services.android.telemetry.MapboxEvent; -import com.mapbox.services.android.telemetry.MapboxTelemetry; -import com.mapbox.services.android.telemetry.utils.MathUtils; -import com.mapbox.services.android.telemetry.utils.TelemetryUtils; +import com.mapbox.mapboxsdk.utils.MathUtils; import java.util.concurrent.CopyOnWriteArrayList; @@ -144,19 +143,6 @@ final class MapGestureDetector { return focalPoint; } - /** - * Given coordinates from a gesture, use the current projection to translate it into - * a Location object. - * - * @param x coordinate - * @param y coordinate - * @return location - */ - private Location getLocationFromGesture(float x, float y) { - LatLng latLng = projection.fromScreenLocation(new PointF(x, y)); - return TelemetryUtils.buildLocation(latLng.getLongitude(), latLng.getLatitude()); - } - /** * Called when user touches the screen, all positions are absolute. *

@@ -202,9 +188,13 @@ final class MapGestureDetector { && uiSettings.isZoomGesturesEnabled(); if (twoTap) { // Confirmed 2nd Finger Down - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( - getLocationFromGesture(event.getX(), event.getY()), - MapboxEvent.GESTURE_TWO_FINGER_SINGLETAP, transform)); + if (isZoomValid(transform)) { + MapEventFactory mapEventFactory = new MapEventFactory(); + LatLng latLng = projection.fromScreenLocation(new PointF(event.getX(), event.getY())); + MapState twoFingerTap = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); + twoFingerTap.setGesture(Events.TWO_FINGER_TAP); + Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, twoFingerTap)); + } } break; @@ -233,8 +223,12 @@ final class MapGestureDetector { // Scroll / Pan Has Stopped if (scrollGestureOccurred) { - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapDragEndEvent( - getLocationFromGesture(event.getX(), event.getY()), transform)); + if (isZoomValid(transform)) { + MapEventFactory mapEventFactory = new MapEventFactory(); + LatLng latLng = projection.fromScreenLocation(new PointF(event.getX(), event.getY())); + MapState dragend = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); + Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_DRAGEND, dragend)); + } scrollGestureOccurred = false; if (!scaleAnimating && !rotateAnimating) { @@ -350,9 +344,13 @@ final class MapGestureDetector { // Zoom in on gesture transform.zoom(true, new PointF(e.getX(), e.getY())); } - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( - getLocationFromGesture(e.getX(), e.getY()), - MapboxEvent.GESTURE_DOUBLETAP, transform)); + if (isZoomValid(transform)) { + MapEventFactory mapEventFactory = new MapEventFactory(); + LatLng latLng = projection.fromScreenLocation(new PointF(e.getX(), e.getY())); + MapState doubleTap = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); + doubleTap.setGesture(Events.DOUBLE_TAP); + Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, doubleTap)); + } break; } @@ -380,9 +378,13 @@ final class MapGestureDetector { notifyOnMapClickListeners(tapPoint); } - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( - getLocationFromGesture(motionEvent.getX(), motionEvent.getY()), - MapboxEvent.GESTURE_SINGLETAP, transform)); + if (isZoomValid(transform)) { + MapEventFactory mapEventFactory = new MapEventFactory(); + LatLng latLng = projection.fromScreenLocation(new PointF(motionEvent.getX(), motionEvent.getY())); + MapState singleTap = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); + singleTap.setGesture(Events.SINGLE_TAP); + Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, singleTap)); + } return true; } @@ -456,9 +458,13 @@ final class MapGestureDetector { cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); } - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( - getLocationFromGesture(e1.getX(), e1.getY()), - MapboxEvent.GESTURE_PAN_START, transform)); + if (isZoomValid(transform)) { + MapEventFactory mapEventFactory = new MapEventFactory(); + LatLng latLng = projection.fromScreenLocation(new PointF(e1.getX(), e1.getY())); + MapState pan = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); + pan.setGesture(Events.PAN); + Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, pan)); + } } // reset tracking if needed @@ -541,9 +547,13 @@ final class MapGestureDetector { recentScaleGestureOccurred = true; scalePointBegin = new PointF(detector.getFocusX(), detector.getFocusY()); scaleBeginTime = detector.getEventTime(); - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( - getLocationFromGesture(detector.getFocusX(), detector.getFocusY()), - MapboxEvent.GESTURE_PINCH_START, transform)); + if (isZoomValid(transform)) { + MapEventFactory mapEventFactory = new MapEventFactory(); + LatLng latLng = projection.fromScreenLocation(new PointF(detector.getFocusX(), detector.getFocusY())); + MapState pinch = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); + pinch.setGesture(Events.PINCH); + Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, pinch)); + } return true; } @@ -724,9 +734,13 @@ final class MapGestureDetector { // Also is zoom already started, don't rotate float angle = detector.getRotationDegreesDelta(); if (Math.abs(angle) >= ROTATE_INVOKE_ANGLE) { - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( - getLocationFromGesture(detector.getFocusX(), detector.getFocusY()), - MapboxEvent.GESTURE_ROTATION_START, transform)); + if (isZoomValid(transform)) { + MapEventFactory mapEventFactory = new MapEventFactory(); + LatLng latLng = projection.fromScreenLocation(new PointF(detector.getFocusX(), detector.getFocusY())); + MapState rotation = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); + rotation.setGesture(Events.ROTATION); + Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, rotation)); + } started = true; } @@ -895,9 +909,13 @@ final class MapGestureDetector { if (!tiltGestureOccurred && ((totalDelta > 10.0f) || (totalDelta < -10.0f))) { tiltGestureOccurred = true; beginTime = detector.getEventTime(); - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( - getLocationFromGesture(detector.getFocusX(), detector.getFocusY()), - MapboxEvent.GESTURE_PITCH_START, transform)); + if (isZoomValid(transform)) { + MapEventFactory mapEventFactory = new MapEventFactory(); + LatLng latLng = projection.fromScreenLocation(new PointF(detector.getFocusX(), detector.getFocusY())); + MapState pitch = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); + pitch.setGesture(Events.PITCH); + Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, pitch)); + } } if (!tiltGestureOccurred) { @@ -962,4 +980,15 @@ final class MapGestureDetector { void removeOnScrollListener(MapboxMap.OnScrollListener onScrollListener) { onScrollListenerList.remove(onScrollListener); } + + private boolean isZoomValid(Transform transform) { + if (transform == null) { + return false; + } + double mapZoom = transform.getZoom(); + if (mapZoom < MapboxConstants.MINIMUM_ZOOM || mapZoom > MapboxConstants.MAXIMUM_ZOOM) { + return false; + } + return true; + } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index 9f591ddca..49aaae052 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -22,6 +22,12 @@ import android.view.ViewTreeObserver; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ZoomButtonsController; + +import com.mapbox.android.telemetry.AppUserTurnstile; +import com.mapbox.android.telemetry.Event; +import com.mapbox.android.telemetry.MapEventFactory; +import com.mapbox.android.telemetry.MapboxTelemetry; +import com.mapbox.mapboxsdk.BuildConfig; import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.annotations.Annotation; import com.mapbox.mapboxsdk.annotations.MarkerViewManager; @@ -35,11 +41,7 @@ import com.mapbox.mapboxsdk.maps.widgets.MyLocationView; import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings; import com.mapbox.mapboxsdk.net.ConnectivityReceiver; import com.mapbox.mapboxsdk.storage.FileSource; -import com.mapbox.services.android.telemetry.MapboxTelemetry; -import timber.log.Timber; -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.opengles.GL10; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; @@ -48,6 +50,11 @@ import java.util.Iterator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import timber.log.Timber; + import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_MAP_NORTH_ANIMATION; import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_WAIT_IDLE; @@ -273,7 +280,12 @@ public class MapView extends FrameLayout { @UiThread public void onCreate(@Nullable Bundle savedInstanceState) { if (savedInstanceState == null) { - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapLoadEvent()); + MapboxTelemetry telemetry = Events.obtainTelemetry(); + AppUserTurnstile turnstileEvent = new AppUserTurnstile(BuildConfig.MAPBOX_SDK_IDENTIFIER, + BuildConfig.MAPBOX_SDK_VERSION); + telemetry.push(turnstileEvent); + MapEventFactory mapEventFactory = new MapEventFactory(); + telemetry.push(mapEventFactory.createMapLoadEvent(Event.Type.MAP_LOAD)); } else if (savedInstanceState.getBoolean(MapboxConstants.STATE_HAS_SAVED_STATE)) { this.savedInstanceState = savedInstanceState; } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxEventWrapper.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxEventWrapper.java deleted file mode 100644 index 6730278d7..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxEventWrapper.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.location.Location; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.mapbox.mapboxsdk.constants.MapboxConstants; -import com.mapbox.services.android.telemetry.MapboxEvent; - -import java.util.Hashtable; - -/** - * Wrapper class for MapboxEvent - *

- * Provides facility methods to use Transform and handle the case that the zoom, required for a telemetry event, - * isn't available yet. - *

- */ -class MapboxEventWrapper { - - @Nullable - static Hashtable buildMapClickEvent( - @NonNull Location location, @NonNull String gestureId, Transform transform) { - try { - double mapZoom = transform.getZoom(); - if (mapZoom >= MapboxConstants.MINIMUM_ZOOM && mapZoom <= MapboxConstants.MAXIMUM_ZOOM) { - // validate zoom #8057 - return MapboxEvent.buildMapClickEvent(location, gestureId, transform.getZoom()); - } - } catch (NullPointerException exception) { - // Map/Transform is not ready yet #8650 - // returning null is valid, event is ignored. - } - return null; - } - - @Nullable - static Hashtable buildMapDragEndEvent( - @NonNull Location location, Transform transform) { - try { - double mapZoom = transform.getZoom(); - if (mapZoom >= MapboxConstants.MINIMUM_ZOOM && mapZoom <= MapboxConstants.MAXIMUM_ZOOM) { - // validate zoom #8057 - return MapboxEvent.buildMapDragEndEvent(location, transform.getZoom()); - } - } catch (NullPointerException exception) { - // Map/Transform is not ready yet #8650 - // returning null is valid, event is ignored. - } - return null; - } - - @Nullable - static Hashtable buildMapLoadEvent() { - return MapboxEvent.buildMapLoadEvent(); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index 834317dd2..2fd9a9010 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java @@ -43,7 +43,7 @@ import com.mapbox.mapboxsdk.style.layers.Filter; import com.mapbox.mapboxsdk.style.layers.Layer; import com.mapbox.mapboxsdk.style.light.Light; import com.mapbox.mapboxsdk.style.sources.Source; -import com.mapbox.services.android.telemetry.location.LocationEngine; +import com.mapbox.android.core.location.LocationEngine; import java.lang.reflect.ParameterizedType; import java.util.HashMap; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java index 81fd091c9..374309682 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java @@ -12,9 +12,9 @@ import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.constants.MyBearingTracking; import com.mapbox.mapboxsdk.constants.MyLocationTracking; import com.mapbox.mapboxsdk.maps.widgets.MyLocationView; -import com.mapbox.services.android.telemetry.location.LocationEngine; -import com.mapbox.services.android.telemetry.location.LocationEngineListener; -import com.mapbox.services.android.telemetry.permissions.PermissionsManager; +import com.mapbox.android.core.location.LocationEngine; +import com.mapbox.android.core.location.LocationEngineListener; +import com.mapbox.android.core.permissions.PermissionsManager; import timber.log.Timber; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java index 1cdd91028..3f37da99d 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java @@ -35,9 +35,9 @@ import com.mapbox.mapboxsdk.constants.MyLocationTracking; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.Projection; -import com.mapbox.services.android.telemetry.location.LocationEngine; -import com.mapbox.services.android.telemetry.location.LocationEngineListener; -import com.mapbox.services.android.telemetry.location.LocationEnginePriority; +import com.mapbox.android.core.location.LocationEngine; +import com.mapbox.android.core.location.LocationEngineListener; +import com.mapbox.android.core.location.LocationEnginePriority; import java.lang.ref.WeakReference; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java index 81f7255b8..79cde7429 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java @@ -13,7 +13,7 @@ public class GeoJsonOptions extends HashMap { /** * Maximum zoom level at which to create vector tiles (higher means greater detail at high zoom levels). * - * @param maxZoom the maximum zoom - Defaults to 18. + * @param minZoom the maximum zoom - Defaults to 18. * @return the current instance for chaining */ public GeoJsonOptions withMinZoom(int minZoom) { diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java new file mode 100644 index 000000000..0c90e4b24 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java @@ -0,0 +1,49 @@ +package com.mapbox.mapboxsdk.utils; + +// TODO Remove this class if we finally include it within MAS 3.x (GeoJSON) +public class MathUtils { + + /** + * Test a value in specified range, returning minimum if it's below, and maximum if it's above + * + * @param value Value to test + * @param min Minimum value of range + * @param max Maximum value of range + * @return value if it's between min and max, min if it's below, max if it's above + */ + public static double clamp(double value, double min, double max) { + return Math.max(min, Math.min(max, value)); + } + + /** + * Test a value in specified range, returning minimum if it's below, and maximum if it's above + * + * @param value Value to test + * @param min Minimum value of range + * @param max Maximum value of range + * @return value if it's between min and max, min if it's below, max if it's above + */ + public static float clamp(float value, float min, float max) { + return Math.max(min, Math.min(max, value)); + } + + /** + * Constrains value to the given range (including min, excluding max) via modular arithmetic. + *

+ * Same formula as used in Core GL (wrap.hpp) + * std::fmod((std::fmod((value - min), d) + d), d) + min; + * + * @param value Value to wrap + * @param min Minimum value + * @param max Maximum value + * @return Wrapped value + */ + public static double wrap(double value, double min, double max) { + double delta = max - min; + + double firstMod = (value - min) % delta; + double secondMod = (firstMod + delta) % delta; + + return secondMod + min; + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java index 7a28d846e..d9e3ae427 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java @@ -5,7 +5,7 @@ import android.net.ConnectivityManager; import android.net.NetworkInfo; import com.mapbox.mapboxsdk.exceptions.MapboxConfigurationException; -import com.mapbox.services.android.telemetry.location.LocationEngine; +import com.mapbox.android.core.location.LocationEngine; import org.junit.Before; import org.junit.Test; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle index de4c0cc3b..135b46e0f 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle +++ b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle @@ -52,18 +52,17 @@ android { } dependencies { - implementation(project(':MapboxGLAndroidSDK')) - implementation(dependenciesList.mapboxJavaServices) { - transitive = true - } + api(project(':MapboxGLAndroidSDK')) + implementation dependenciesList.mapboxJavaServices - api dependenciesList.mapboxJavaTurf + implementation dependenciesList.mapboxJavaTurf implementation dependenciesList.supportAppcompatV7 implementation dependenciesList.supportRecyclerView implementation dependenciesList.supportDesign - implementation dependenciesList.lost + // implementation dependenciesList.lost + implementation dependenciesList.gmsLocation implementation dependenciesList.timber debugImplementation dependenciesList.leakCanaryDebug releaseImplementation dependenciesList.leakCanaryRelease diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml index 89f922fb9..2d6efc0d8 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml @@ -806,7 +806,7 @@ + android:value="api-events-staging.tilestream.net"/> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java index 95cc9687f..0ee1f78e0 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java @@ -22,8 +22,8 @@ import com.mapbox.mapboxsdk.testapp.adapter.FeatureAdapter; import com.mapbox.mapboxsdk.testapp.adapter.FeatureSectionAdapter; import com.mapbox.mapboxsdk.testapp.model.activity.Feature; import com.mapbox.mapboxsdk.testapp.utils.ItemClickSupport; -import com.mapbox.services.android.telemetry.permissions.PermissionsListener; -import com.mapbox.services.android.telemetry.permissions.PermissionsManager; +import com.mapbox.android.core.permissions.PermissionsListener; +import com.mapbox.android.core.permissions.PermissionsManager; import java.util.ArrayList; import java.util.Collections; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java index 71b8115d2..eec26cc9a 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java @@ -9,8 +9,8 @@ import android.support.design.widget.Snackbar; import android.support.v7.app.AppCompatActivity; import android.text.TextUtils; -import com.mapbox.services.android.telemetry.permissions.PermissionsListener; -import com.mapbox.services.android.telemetry.permissions.PermissionsManager; +import com.mapbox.android.core.permissions.PermissionsListener; +import com.mapbox.android.core.permissions.PermissionsManager; import java.util.List; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java index f4b54551b..f4fe710de 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java @@ -5,8 +5,8 @@ import android.animation.TypeEvaluator; import android.animation.ValueAnimator; import android.location.Location; -import com.mapbox.services.android.telemetry.location.LocationEngine; -import com.mapbox.services.android.telemetry.location.LocationEngineListener; +import com.mapbox.android.core.location.LocationEngine; +import com.mapbox.android.core.location.LocationEngineListener; import timber.log.Timber; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java index 000042306..f60305003 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java @@ -15,7 +15,7 @@ import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.MapboxMapOptions; import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.services.android.telemetry.location.LocationEngineListener; +import com.mapbox.android.core.location.LocationEngineListener; /** * Test activity showcasing how to change the MyLocationView drawable. diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTintActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTintActivity.java index 89774dc50..ff3c4dfbc 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTintActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTintActivity.java @@ -18,7 +18,7 @@ import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.TrackingSettings; import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings; import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.services.android.telemetry.location.LocationEngineListener; +import com.mapbox.android.core.location.LocationEngineListener; /** * Test activity showcasing how to tint the MyLocationView. diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java index 5ebe43e68..ffbb2c1a9 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java @@ -25,7 +25,7 @@ import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; import com.mapbox.mapboxsdk.maps.TrackingSettings; import com.mapbox.mapboxsdk.maps.UiSettings; import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.services.android.telemetry.location.LocationEngineListener; +import com.mapbox.android.core.location.LocationEngineListener; import timber.log.Timber; diff --git a/platform/android/build.gradle b/platform/android/build.gradle index 0368558e9..6cd950544 100644 --- a/platform/android/build.gradle +++ b/platform/android/build.gradle @@ -11,6 +11,7 @@ buildscript { allprojects { repositories { + mavenCentral() jcenter() google() maven { url "http://oss.sonatype.org/content/repositories/snapshots/" } diff --git a/platform/android/gradle/dependencies.gradle b/platform/android/gradle/dependencies.gradle index 99fa3f350..4ef4ae2f7 100644 --- a/platform/android/gradle/dependencies.gradle +++ b/platform/android/gradle/dependencies.gradle @@ -9,7 +9,7 @@ ext { versions = [ mapboxServices: '3.0.0-beta.2', - mapboxTelemetry: '2.2.9', + mapboxTelemetry: '3.0.0-beta.1', supportLib : '25.4.0', espresso : '3.0.1', testRunner : '1.0.1', @@ -46,6 +46,7 @@ ext { supportRecyclerView : "com.android.support:recyclerview-v7:${versions.supportLib}", lost : "com.mapzen.android:lost:${versions.lost}", + gmsLocation : 'com.google.android.gms:play-services-location:11.0.4', timber : "com.jakewharton.timber:timber:${versions.timber}", okhttp3 : "com.squareup.okhttp3:okhttp:${versions.okhttp}", leakCanaryDebug : "com.squareup.leakcanary:leakcanary-android:${versions.leakCanary}", From 734b420649392fd7e35e808e9a5053b9c38b5adc Mon Sep 17 00:00:00 2001 From: Tobrun Date: Tue, 13 Feb 2018 12:25:45 +0100 Subject: [PATCH 037/264] [android] - add constrained latlng documentation, add MapboxMap#LatLngZoom --- .../com/mapbox/mapboxsdk/maps/MapboxMap.java | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index 2fd9a9010..4f2c940ea 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java @@ -621,6 +621,9 @@ public final class MapboxMap { /** * Moves the center of the screen to a latitude and longitude specified by a LatLng object. This centers the * camera on the LatLng object. + *

+ * Note that at low zoom levels, setLatLng is constrained so that the entire viewport shows map data. + *

* * @param latLng Target location to change to */ @@ -640,6 +643,21 @@ public final class MapboxMap { nativeMapView.setZoom(zoom, focalPoint, 0); } + /** + * Moves the center and the zoom of the camera specified by a LatLng object and double zoom. + *

+ * Note that at low zoom levels, setLatLng is constrained so that the entire viewport shows map data. + *

+ * + * @param latLng Target location to change to + */ + public void setLatLngZoom(@NonNull LatLng latLng, + @FloatRange(from = MapboxConstants.MINIMUM_ZOOM, + to = MapboxConstants.MAXIMUM_ZOOM) double zoom) { + setZoom(zoom); + setLatLng(latLng); + } + /** * Moves the camera viewpoint angle to a particular angle in degrees. * @@ -1882,7 +1900,6 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the map is scrolled. * To unset the callback, use null. - * * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} instead. */ @Deprecated @@ -1895,7 +1912,6 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the map is scrolled. * To unset the callback, use null. - * */ public void addOnScrollListener(@Nullable OnScrollListener listener) { onRegisterTouchListener.onAddScrollListener(listener); @@ -1906,7 +1922,6 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the map is scrolled. * To unset the callback, use null. - * */ public void removeOnScrollListener(@Nullable OnScrollListener listener) { onRegisterTouchListener.onRemoveScrollListener(listener); @@ -1917,7 +1932,6 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the map is flinged. * To unset the callback, use null. - * * @deprecated Use {@link #addOnFlingListener(OnFlingListener)} instead. */ @Deprecated @@ -1950,7 +1964,6 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the user clicks on the map view. * To unset the callback, use null. - * * @deprecated Use {@link #addOnMapClickListener(OnMapClickListener)} instead. */ @Deprecated @@ -1983,7 +1996,6 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the user long clicks on the map view. * To unset the callback, use null. - * * @deprecated Use {@link #addOnMapLongClickListener(OnMapLongClickListener)} instead. */ @Deprecated From 143c346d9d809a93d802dc087cffe517659e1077 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 26 Jan 2018 15:32:16 +0100 Subject: [PATCH 038/264] [android] - translucent surface on TextureView --- .../com/mapbox/mapboxsdk/maps/MapView.java | 4 +- .../mapboxsdk/maps/MapboxMapOptions.java | 15 +++ .../maps/renderer/egl/EGLConfigChooser.java | 13 ++- .../textureview/TextureViewMapRenderer.java | 15 ++- .../textureview/TextureViewRenderThread.java | 9 +- .../src/main/res-public/values/public.xml | 1 + .../src/main/res/values/attrs.xml | 1 + .../src/main/AndroidManifest.xml | 11 ++ .../TextureViewResizeActivity.java | 2 +- ...tureViewTransparentBackgroundActivity.java | 94 ++++++++++++++++++ .../src/main/res/drawable-xxxhdpi/water.jpg | Bin 0 -> 165806 bytes .../layout/activity_textureview_resize.xml | 2 +- .../activity_textureview_transparent.xml | 28 ++++++ .../src/main/res/raw/no_bg_style.json | 43 ++++++++ .../src/main/res/values/descriptions.xml | 1 + .../src/main/res/values/titles.xml | 1 + .../android/scripts/exclude-activity-gen.json | 5 +- 17 files changed, 233 insertions(+), 12 deletions(-) create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewTransparentBackgroundActivity.java create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/water.jpg create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_transparent.xml create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/no_bg_style.json diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index 49aaae052..a921d1403 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -294,7 +294,9 @@ public class MapView extends FrameLayout { private void initialiseDrawingSurface(MapboxMapOptions options) { if (options.getTextureMode()) { TextureView textureView = new TextureView(getContext()); - mapRenderer = new TextureViewMapRenderer(getContext(), textureView, options.getLocalIdeographFontFamily()) { + String localFontFamily = options.getLocalIdeographFontFamily(); + boolean translucentSurface = options.getTranslucentTextureSurface(); + mapRenderer = new TextureViewMapRenderer(getContext(), textureView, localFontFamily, translucentSurface) { @Override protected void onSurfaceCreated(GL10 gl, EGLConfig config) { MapView.this.onSurfaceCreated(); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java index 46dba28b9..49188a5a9 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java @@ -87,6 +87,7 @@ public class MapboxMapOptions implements Parcelable { private String apiBaseUrl; private boolean textureMode; + private boolean translucentTextureSurface; private String style; @@ -156,6 +157,7 @@ public class MapboxMapOptions implements Parcelable { style = in.readString(); apiBaseUrl = in.readString(); textureMode = in.readByte() != 0; + translucentTextureSurface = in.readByte() != 0; prefetchesTiles = in.readByte() != 0; zMediaOverlay = in.readByte() != 0; localIdeographFontFamily = in.readString(); @@ -289,6 +291,8 @@ public class MapboxMapOptions implements Parcelable { typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_myLocationAccuracyThreshold, 0)); mapboxMapOptions.textureMode( typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_renderTextureMode, false)); + mapboxMapOptions.translucentTextureSurface( + typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_renderTextureTranslucentSurface, false)); mapboxMapOptions.setPrefetchesTiles( typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_enableTilePrefetch, true)); mapboxMapOptions.renderSurfaceOnTop( @@ -711,6 +715,11 @@ public class MapboxMapOptions implements Parcelable { return this; } + public MapboxMapOptions translucentTextureSurface(boolean translucentTextureSurface) { + this.translucentTextureSurface = translucentTextureSurface; + return this; + } + /** * Enable tile pre-fetching. Loads tiles at a lower zoom-level to pre-render * a low resolution preview while more detailed tiles are loaded. @@ -1085,6 +1094,10 @@ public class MapboxMapOptions implements Parcelable { return textureMode; } + public boolean getTranslucentTextureSurface() { + return translucentTextureSurface; + } + /** * Returns the font-family for locally overriding generation of glyphs in the * ‘CJK Unified Ideographs’ and ‘Hangul Syllables’ ranges. @@ -1159,6 +1172,7 @@ public class MapboxMapOptions implements Parcelable { dest.writeString(style); dest.writeString(apiBaseUrl); dest.writeByte((byte) (textureMode ? 1 : 0)); + dest.writeByte((byte) (translucentTextureSurface ? 1 : 0)); dest.writeByte((byte) (prefetchesTiles ? 1 : 0)); dest.writeByte((byte) (zMediaOverlay ? 1 : 0)); dest.writeString(localIdeographFontFamily); @@ -1340,6 +1354,7 @@ public class MapboxMapOptions implements Parcelable { ? Float.floatToIntBits(myLocationAccuracyThreshold) : 0); result = 31 * result + (apiBaseUrl != null ? apiBaseUrl.hashCode() : 0); result = 31 * result + (textureMode ? 1 : 0); + result = 31 * result + (translucentTextureSurface ? 1 : 0); result = 31 * result + (style != null ? style.hashCode() : 0); result = 31 * result + (prefetchesTiles ? 1 : 0); result = 31 * result + (zMediaOverlay ? 1 : 0); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigChooser.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigChooser.java index 247ffea90..46238ee78 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigChooser.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigChooser.java @@ -54,6 +54,17 @@ public class EGLConfigChooser implements GLSurfaceView.EGLConfigChooser { @SuppressWarnings("JavadocReference") private static final int EGL_OPENGL_ES2_BIT = 0x0004; + private boolean translucentSurface; + + public EGLConfigChooser() { + this(false); + } + + public EGLConfigChooser(boolean translucentSurface) { + super(); + this.translucentSurface = translucentSurface; + } + @Override public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { int[] configAttribs = getConfigAttributes(); @@ -274,7 +285,7 @@ public class EGLConfigChooser implements GLSurfaceView.EGLConfigChooser { EGL_RED_SIZE, 5, EGL_GREEN_SIZE, 6, EGL_BLUE_SIZE, 5, - EGL_ALPHA_SIZE, 0, + EGL_ALPHA_SIZE, translucentSurface ? 8 : 0, EGL_DEPTH_SIZE, 16, EGL_STENCIL_SIZE, 8, (emulator ? EGL_NONE : EGL_CONFORMANT), EGL_OPENGL_ES2_BIT, diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewMapRenderer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewMapRenderer.java index dcc95217f..ad25dea0d 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewMapRenderer.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewMapRenderer.java @@ -17,17 +17,22 @@ import javax.microedition.khronos.opengles.GL10; */ public class TextureViewMapRenderer extends MapRenderer { private TextureViewRenderThread renderThread; + private boolean translucentSurface; /** * Create a {@link MapRenderer} for the given {@link TextureView} * - * @param context the current Context - * @param textureView the TextureView + * @param context the current Context + * @param textureView the TextureView + * @param localIdeographFontFamily the local font family + * @param translucentSurface the translucency flag */ public TextureViewMapRenderer(@NonNull Context context, @NonNull TextureView textureView, - String localIdeographFontFamily) { + String localIdeographFontFamily, + boolean translucentSurface) { super(context, localIdeographFontFamily); + this.translucentSurface = translucentSurface; renderThread = new TextureViewRenderThread(textureView, this); renderThread.start(); } @@ -95,4 +100,8 @@ public class TextureViewMapRenderer extends MapRenderer { public void onDestroy() { renderThread.onDestroy(); } + + public boolean isTranslucentSurface() { + return translucentSurface; + } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java index 1e76ffe3f..4bba16099 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java @@ -53,9 +53,10 @@ class TextureViewRenderThread extends Thread implements TextureView.SurfaceTextu */ @UiThread TextureViewRenderThread(@NonNull TextureView textureView, @NonNull TextureViewMapRenderer mapRenderer) { + textureView.setOpaque(!mapRenderer.isTranslucentSurface()); textureView.setSurfaceTextureListener(this); this.mapRenderer = mapRenderer; - this.eglHolder = new EGLHolder(new WeakReference<>(textureView)); + this.eglHolder = new EGLHolder(new WeakReference<>(textureView), mapRenderer.isTranslucentSurface()); } // SurfaceTextureListener methods @@ -324,6 +325,7 @@ class TextureViewRenderThread extends Thread implements TextureView.SurfaceTextu private static class EGLHolder { private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; private final WeakReference textureViewWeakRef; + private boolean translucentSurface; private EGL10 egl; private EGLConfig eglConfig; @@ -331,8 +333,9 @@ class TextureViewRenderThread extends Thread implements TextureView.SurfaceTextu private EGLContext eglContext = EGL10.EGL_NO_CONTEXT; private EGLSurface eglSurface = EGL10.EGL_NO_SURFACE; - EGLHolder(WeakReference textureViewWeakRef) { + EGLHolder(WeakReference textureViewWeakRef, boolean translucentSurface) { this.textureViewWeakRef = textureViewWeakRef; + this.translucentSurface = translucentSurface; } void prepare() { @@ -357,7 +360,7 @@ class TextureViewRenderThread extends Thread implements TextureView.SurfaceTextu eglConfig = null; eglContext = EGL10.EGL_NO_CONTEXT; } else if (eglContext == EGL10.EGL_NO_CONTEXT) { - eglConfig = new EGLConfigChooser().chooseConfig(egl, eglDisplay); + eglConfig = new EGLConfigChooser(translucentSurface).chooseConfig(egl, eglDisplay); int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE}; eglContext = egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml b/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml index 412d8c5d9..fda37dc2d 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml @@ -75,6 +75,7 @@ + diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml index 97adce8a4..f0b80e46e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml @@ -117,6 +117,7 @@ + diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml index 2d6efc0d8..9e64e03e1 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml @@ -759,6 +759,17 @@ android:name="@string/category" android:value="@string/category_textureview"/> + + + + { + mapboxMap = map; + + try { + map.setStyleJson(ResourceUtils.readRawResource(getApplicationContext(), R.raw.no_bg_style)); + } catch (IOException exception) { + Timber.e(exception); + } + }); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + +} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/water.jpg b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/water.jpg new file mode 100644 index 0000000000000000000000000000000000000000..71b758b490ac29cc14da026533f068ea9d9ace2d GIT binary patch literal 165806 zcmb5VbyOTp&^NlcJ0w7G4I13t7nj9Da9P|fIDr5m3oK4>Szxi??gWC{LU2fcU|E7g z(983F-+j;h_tu%|uIaAsn(3a?T{ZQar=_PY0I|9XPz8YUOx!5k0Kn5vz_M~6*Z~00 z(gJV-003+NItmd0^_jzbK4nmd|A!}f=9Qvy02t3X%JXUP5ez{8zr6W#4p6jq_i+C| zDTA_(0fVqGpVEKj&%&5;0L=eS)3T_%|LvwGjvavZEQJcN0|1iI{zn!0^K~;6=l_-y z0{~Fm|CJy8-yQ(~!e<^=fRA5Lf=^U}Uxd7avb%OF= zOEh%s=Z3_1IC#&c|3B%e4?u#A0!NKNLtz4-lAxfGpgau$XrG@O?Z1imZ$STtFtN}v zu+dOaZ~)KMs?Q_;pJp^v3{-Rs6ihT66jU^H3=&LIMgSH+8IwG@9yYUpwHJlL3;oCx z7D1b$`j?6Z-ckLtl&n8bPKAVReW1}e&mFx&`9FI8Uo8Lt8afIp1||UOxl)k?fP#vS zhJpF41RVq8`Cd>^NuC85N%`e5^sK!i$(T}#>Q6Aq1^Q>1e=6ulol@9%L$O}42o@XI zHVn)uo;|Gq@X?-Ckf4zOWC2vi?vP`Vd}DHIEa2vzGM=8PN3&GgY@2?OZ4$tPUX0-T z&Sh5ZsB>8gYyowfb*Se@*=Hk`PxLFQG&fR?(>mq>zX<<^1chH_jMh12m=dlw7nNm5 z*fG0*E4P39X1clme6ONr<;~zmV?}*zaYPYlX2`-xDA{5hk-2zB+BG zk}dQaU*)cL>#Z|`xPV51i=?-JSM^R9d*fYSuL?I0*`l3!exxI=R$vv#vx#L8(FT_LNZmQXvT z6cfoK=K#c2lUfY5)_QxLuvAuA4BS7^L%Y?B*L;&C=0JI8MTW?u^_d+&(yZ(oQ>%E~ zL~5p_vP*4y|McX4dE@wLz8Pa_+C6l*M7PD$X_s2G~(NTMS>Egh%@m<7|3 z+sxALXy?qy0dj!mE7a3J4IG>Tlnv>BLY#3vyXr1lz*5x6^bL28X?D2b@RBD$*6HaF z(LbMmNwmfnLzi^Z*A2Y;W&QDR=>Ssaqo<28h4W^2I<1xF#>)7b-zKGOgWiR!w}&pB zLREB-Gd1&V7OQjDdV08q)7rKviy0YATWt@VODJDt>(B3efqr<|>ndcsRLz~3v3X3z zb**w^e+nf~iF96LHH5udY1pOp`~4#jA+(R4n4~tVhmICCr?!+mQ68&^ktD8lNTD5|@gc#<$&qiHhO|fWxmg)oOt)%j^QhA*}RYUSeR`^@;+(%Ox z#>x~DEb2Mh0~GU3E9g8>XX%y>HPY?p_11(91tuoBnbxJ{)PjDhD3Z240&Zwun7?l> z6%rN_>cDFIk#NiDx!`BB6$)gK5vg*;5PZ}ukix$K%=57R1!+`P|M(d34b)RgTICQr z6!o@(gy~^)N@iWiFTlh1+bD4CDXe&D+$L+3cOq& z2P@3JoRS4p#=b_@pi}6@S2+H(&8l+Kn=#Km~~M zquJE(3o&j~U~?;6M4WcJxoAwCbjIA~l7xIZHh;1FOWPD&XGT0tuSN*aNmxJ!RMWea znkY$<5a7Rq)L_I5IP!I?OUy>RFuoTCth}U6^6NE8eH3v(ls1qaEjs^$Z}hZlJN#fJ z-s6K&jw3nTjEdzQZXxA$_?!gsv36?D!FWWlvvZ|&ZZgt zZd~D2i1j$Uz{HT_Yw`9?_NsK>XMqii6{?5s8bpUb9DXG7xzq4Y$r`>Z1h?Ob(yVIq zx@)C8>_M(s4$&+Kv#al`w!TcxfSSbwEr(%Rd{F(D1V4PWbKnrrs{?m&&Pi7i;B_w4 z4jnyo-yq5->fhf~-|X}Wipu|Z>vjd6x6$CvXyQ-Y%V1oJJbknsmMGmxM7;SMLhlGd z&m}dEVLC2W9`*RiEMIxn#9PySaSt;6OTzQ5%ItF))`81eoUv40H40b3t+q+~>@G^LrZ4wxOH+^mn;rvCdw9|IC`@ka!1xa(!MfITHTH$71su{f(3!n z_J-sM;2f{BnP|FCKV;Ep3K|F6)LS0F^p1s*_$hmIFTv9f9ajD3wH{5d@Pk7pP7|C1 zBi4~57NY9|cD)IdSCzm!NHxVLECy`qIe!R-h&`p|l;M}z;R%r;<@G+olijS?_ z&~395-|KpRzXT4=^&Wq}HiA%#lZ{9Y}=6!4I*yrv^dkP9g{w-{lF-Rng<0ebtsa=}YOm z^P8d{4EJ!V0w1|iT%Kuhao6c{5#O?luf7J$Z%h3#deqU)Y`blE9s2strxfm8me#!D zz>W0;Sk;wz0!W*i3g?%${SoC|K8V|_A(vU!=bS*cg^5=oR`1GNhgT(Qbj zqiNCqO|XWiv^wolm)4X-m~-59HL?=$k{=NjI4l}tk9IY!{%O5!SSz~!1&uSV2);Idu&G}d#w#)j zBv#F8(w<#?B~v8k%z|^UppS?N{{^DhMWwDfDR+VNm=|{Ut91e|kciW`%%nFxs(>dz zD{@ZK{0jE1jFT0bWHxWX>W@?J+zk|^M_MH{p=?jz`qdy;j&$EfH(xjNgT&~+NW=fp zUCD|^R1(~~9YS{9k?)G10I~2{GsxI(JeXx3+tz_*H<7UP2~hlOyEuL%pgi$j7Vp%^~|!TPN>nL#|@@kRn@SD==S&Gn{n8HJ^SwY7#m87=tF#$kWT>L zgHBy=Rh`9b+q;rz1~Z;(@ouI5lgHnicRnLg{IkE??t#>pJQPRnCI@bBy?e?jqddH^hfjd-WgOVo1tBeknr6j z@C!Bs4W$M&R=3_$z4jH-mfC3KW*8|_Tj;`b$xD8qtI+k*%04D;4}JLGu;|#6a?Xx1wG4MnkN6a<;uDhgBHS^b^2xyRzQ0 zJh+W6IEX`&8O&Fo07{ju^#3y*IY^e&tH$LpDqfc8TX8ej#R07sM$_%Pa0yb`qD}r| zzmNgElP}&&7hg68>M^4fB=w={5Ogj;VLAQDM!K_x)ql zrN8dqw{;*jWuQx5=@EoRyp*K2^o}@Z307%sIUulbuhYoGrw~EJ9bd9lS6Aw)jgV*w z4yzU*BvM0rnU$`NYL3MnUz_4YNwuS~8qd8W!h!&ArcG8L$o0D~Bho+gX1J@puo5tf#QE!-h75wSDU3{C*WA^^G{f9(#=0YbG$B|vj z5)B?DXkVqh;u9c_-kqQG@+NsM-hh?pA~>N$3z?!J!ATrKJAoBhKwp1I^z73l4et7Z zu=EbN4fV%1VnTbIvz$3zQF1p9fB(KDdoMm5%+zx=sNxblu(EOhbVIz3q=rQ~n!;@O ziu#Q9D90jv&i4vCKmIIHo%S^M?MN{D^uc*{1l_3KY@Yp}rEeVbPA+CLJ=KXsL)n<_ zK*|$f9HFpZw*rl;$d|`l22Lk{_+<$*r?9Q&He(5|oD*CgFcaGkR=qF@8-rE{mG6=OPfE5#yLJqB< zC;0ve(C+_9-~aa`B8}g0g&uKPVgN!^j}hS)(aGnYpQ&z^tiJqZ+bQ84dC@#1f9EMHYjNpI!UT3P?J(m_GM(#QG zV~_CN_SL59pe4_H(}Vr|uV1scB}GKnqMw~TuNexN_&*mQ*m_{=sJGD61ap4jZsRtJ zr}2Zf1ah@tXtG4d!q&W*u%@PYTJwb3tm<;{QeG>AGF-jGIB*H2AAbjbds?&6=UzI1 z(2;*q%eifsYU%q~KfO5Pmk?|9kge8fm5lc98rnd&tu8J@jfSJll|ReSdA(%^E9|g<9P^7NE%x}26inG~} zTxbp>1D1YJPDJ?xSij9eV`i3>?VZ+%{lhc-;HzFEzIH?~KqZnH?ED?7_6zIcm&g-9 z2a@y~`L4P}PgT(_m|vR+!!CZx|8z9`eovgeB2SNzo3!xWH_G+lV>27HeU6+IO#)ZV z@Em3Grt>^;+5AQZGD<5sxBhxhuqZ<)Hv5Gx|J`v&cx^Cc$0L2UzHZY>m5G_F&rH0X z)6bZpOT34&E3(vNxq;~GBja!r#9|G?Q#iKaC!R_jex};;1LG5bA2KkO>$pvcR`D=g z`{yC=_5NK_wGgZze@}b5dws374OC73$D-B3ZhC_^%&liJjx zp5|~B&z=ysZ<@sA9#dAXzl3bM6wMhU5Y@KLQFA)@H7_(Z-8PyNG`I#ANF)X;KE%Dn_Rd))iSkD#`;$jzuT##cCUL&V zXhn$T=rugsw_R2Eq7gD4sOOME^RstPH$hO9ZN>3gfQ8zp1`ux zwo|87Jg+-@uY}W%!luBJe&ElTM|n}CP^Rt6Y$wH1$0xw?eEEr#Z%nr__!9o?tI7*$ z(BzklN=^M`pn1Ktlb8>DHp5>x9yurEWbq0$uJK%E3|Yd27I^R!qfm&)__omqnm>6G zzS=IE*Uq%#NJ?{^Sfwx_tvplM%^irqebwL<#%mrs+~RQw;mj-2Zvwt&k} zF34C8D3ku~(8@yo2>_>{`bTRtd-cie=%vd?aOZhx-By9_H=P;|@SZ}5wdul0>aNe} ztY2Mc-av=>W2(u_@+GU}SGB`8S1V=-QGDVQq#WK}CqA=NpT1rlA1GuZ^;V?m^$Z1N zVc@0*Qq3`xv(rK=oVP)ASpECsnMUye43OR?$GL?+xH*~jMUJwi-U`r^X&R zoNS;Sje168gg_3sw13~vEWnafh%FLQDYW?YZ<3qG+1s94#O3q)E_?Wg{PK-JEWV&< zc^4ffZo!4L3pE!geKE*aIck?J|EqeEG><+0KcRUNs@tGSHY~7*foEWLCSGc5Vl2;j zXRzLG!`z_5f?-Q;zoD`oact4NY{qy~m=!~i6hsiAf$GF)v#s&S(EQzk98s%2g<(#t z4zi5IMK`?&S$&Puc#UTkK=QM&v~m67A;jl-0ScIZX;c^tF6~dTgXIeID7nk=;8jqK zDttKKHd+Yi)zVkBsO!`!m;B`H)1pz6_`DeMqp>fPjt|YDr2bQrzq`PwmrZXK4HibUD)x_k z=Z4q#pd3tb9p_uHaQ8@2Qf&?}hs?E^u)lA+AMg*Hz1W*jE`q2a@Z8^xSxo2IvA_;q zwXSGAoO~6wxj#an6@Q4Tk&L)w6i->;B86_}CAs9)uXH@@ZYGfi|Fh z-po^nlhX-MMyLqRdi;_+84_l-mmY7T?U=hdlrYRbrT|e0yYM*fRvHHDQ&Vsn>G;Z} zwtKhHa`qC2#~p4CULK#@A;^ut>}G`Dm@>4!3*|ui;qFKwe)j8slQUA7jl~s=o>2zP zP;TaewQj;9G;#aZ6h&3h@s3e$zI{GFDccxBq4i_SdMww_BOTQe?kSe|02M$n(xge2 z-sXRN>0-?pwve!q>lXw-M_ zJ7H7Urj z;W625%k0G2l5baw77zHUV7Qoqdt>7C>>ZLmT zEJ+VXUZ(U1EKukf6bS3-X{*<5+VXi^pp|T8LNTt}WaCD^=)F}D|0L#zsy}C55Y$%x zQMf?)>W8wS1_u5a=z&3h;?i@->(MF#EqY3N0%e_WN$^ZTh(Q+GwdxUg*G>+biHYEwP&~p^hfe^bNto7_lsS1*KpH-=Z(he zp-Oxn+NM7`ogbF{rV{a09?g=+U&Zkg+bc~!AA#JHHWCV&hxJ&WnQO0J@*Ic1<#W<+ z96JtfTXHFD{T35M-p~>4s-%{dsch%{l;5UXyEI)gu;#{`{Bk$A%KP?P{;Z*MmR2n9 z{Md~St%Tf*B4oj@;!SDpfL(+!oJd7P-C06C8$2t=!U;u1<1ZJ)Bf$(*R^nhQ|-meKMOo9j-2Q-3zOPigYoDf~lOXGTq_~R-)zedb;H>=;o6thNS>d4hUXcmO zkaWG{H~j3wR$Meo{#dj;V2O`lz71uZ3RBr*u%r2R=i8^N*UugFXv38hS2$j>73FN(n=xWk%eB+xa+q*GGBf-PFMY zhqH_8Y);en3C;;w>8B_D-@C%=jPxmZcNHt>9byg+4~_s2R0-5nKIDo{YPz~Kjl`R> zAKzV;6~i_i>&F>Pq_GD_sDncyyfQ!zXoD{(_on5p!)6W%h>ui78fvQZh2I$KfNPD_ z<@l3#MR|iBUXRf1Rgv8M{Gq3kM(v|ve!$G^jD(TOo=tK*2V^Rz75vthj$?bgNrOtZ1lG7{1X9TgB2;lF6I*sTPL*T@~S{>Wo%u&c0oxg8BO_Lik^Y&4c7|q z$-2DWUfP=i82xJA?y*FO4!`xeVlLU2?1y1c&l|5cTUIesF$<0!c156?q$Ybxc}j)+ zI}p`V4rpJ)u$j}J2W24;ph2G)W_or8$JC_gT*BTOBjrWW3s`NJz7EAJ_Bun5zd@C^ z+{#Pe2!Ku$(Q1CSKK~1kCZ99MT5}FAD~w}`g7m4YU4rk;VJVAgXFvmjjfPZ~sHOp- z7>K~?j@!rUfB*LLQ(IQ>ELLF+=8OvzrwV<72H&T(m+m^dyiy7_vEb`(0+5+*q|{&1 zTO=1WznlA>bZZpp^UaigoR5*=1BryK5q59LQVOU0Z*}NsFV#S3N`22k@$B)Ko|6&G z3SQgve`g%B3x8%vKnDlKmY4UJ;W`tG++_EaiWT7|41~cRpN`)AgpL#={jTA!Y8F?e z^qQs#xbfZ&3sZ3&sIYQx+G(cF@WSUs$F*n|i~E{IIewQ}^XN5=dZh8rp=>fLEHThA z|5RhRIr>)B-Hqrm-+_0!aAp&z?##^1kJ&~BUH9xq#ac&6PoAGbihzxV)ES7@FIhR2 zk&)D0X>s|A#D&MYQhV$v_r7L92HzG7S3lSz4(Y2AOBJZzeB3S;y*FCZJUh7Po^(xlX;+}?EYQ*V z1i+R0K>x_VzgIM=FlLf$`ChbCx}l{R*N8%g!wI`n18>MO{E!dJ{N-m$>7!P}#gR;P zi|(L4ZoQDmU%BX{>(9HR-B!P4oo31<6H$>_asg#DQP`2tS zTa#KO9y_mwb6OH|77UDa#9{@Ha=@rKuoJ$vX;?W6I~|aHuZV6nT& zg%0KT*S(b*f}y-?P+RtOI{%$oO8zNN35H&iyP>G}%^h}Jq#qOVUzx#?*0BsN7D<#W zR%b+;#ek+aL{4i8?p%7g0e6jDOZSz`yZXQ%hRF3CN~94pMUUWM%VHw! zTIFVYV;c=|Sdc+}T!m!WikpUOMOl}5QR7C&i*T(C2??{0ZEE=@83@A>|aN3qSogc~dI#Gv318#Bci+mIL#6RW*EKxn4PO8mfFI^&CSPN#1f;OT zM(+ip2<7{aUM|;Cuf*pBRo1k0jA7z)7AKDlo!^!z02zGx4ZiOeC!NaIHcfczeYP|; z?Ya$%hX+MseeSfA+1#Pa5NgzyX{AxV4|iHAzJVuCFw7B$U;UN{N?Q_TRVmEuwkVz# zO4Sl3wb(%4u!lv7S+RttWmqTeT;O@%oBI#{HNCjH)5_0Rv3YqYs5=1|9VmRRBur_6 zoPl2-?pnB%_zAHU;XY_Jn6S78GSBC!M_)5zC7uN{eCd$=h>k}KT@5O!%8Se>z6Puy zhviJjuw+@jj^z3(%^NV9k_N3Jf>mK5$}NZX=`lq(GpwRVYgvyxBPJnEyq6-SNsBR) zwHt-4l^X8kz}Br}j;hUXv~?!3IK3`zI`Orso&pXGqbzE3#%+aTdx{G5r{Ku42#ijP(Si;_xk`Vv zJpnWt4ya9WF^Db@e+>kErsmKD_Kv;g2L=m`UIjri)h1P$VL$xg>c*+ZG$lY0Z2D5S z^X-zldKm3_m}k1wdf6%Z8VLF)&oedj%KtPG={oyTGEXT$%eEH7hZ#wp<`pWW*5moo z>8)2H%6zzv9(YB9>P3DO3*T2vEAJ0C{p|Ggf2Wn^!|3WVC8CYKuDa!yx7Gf0p!f9D z3x@<3s4i;(%Wc<%hx2QNsho;q?G&$RUOWMYCGf|Vaka^7PpjllYL_%O<{An*yCY~Z z;!XmwJC=sxdy;M=;-}DkYZ#R1p8%SetI0wCXbr;;e)!cAe_t{07H-+a4=^w?(oU(} zK+%ipoko)udDU@SrQUmS)8x89!bV{{1Lq7!3}f9wBntF4JC$A${8iD>!jlm7jjvCyncoB6>@K-CQvp| z@8ckzAJij5d+5xIn3gx1v|S$kTP0nmWW*|Xwm<>ZI*OZylHkY>dx~z4NRv<)e7!Cs z4z&D>4f}@;{jP5VkC*L+geIdZ_6%aBtQ(j>E*XYsDU&j%@$3{+OrnMRVUJDk1z`L*_6w zHHauFg36@edLIj1Z(s8L(N^EGpSHRh)NB>q4tD>DohWL!0_33jT^m>usYdv+SOWYy#b)o;FSX<1Cia@8^b!=b7Eg2)W9>_Dn*f7NLfmBlg#!z5-Zzg|FyGS3eV zn8q3#LtyW30Sk(xHtK{e<<2%yP8N6D1eil6_ejhN5KZ>+F%JAU6p4%p<9gEqgsq$; zH(fOIdiy6Y0V=m;1n0y0mvFx{og4d?bOdvOxV5ECII4!~@IR$Arvsf<8SYF_i4@K5 zPUhDPE8dqHy-!;>;ta+C!G(ozY7<`prb`yWS6sW_M32SXekZYV&1IKr-dSxV5ui z4RxKp;HJ`0Cv^dGbT)Ao~nh z+V~F9%%2caKGoZc;+CV@%lY*uK-s}v-bVXxk^yC&uKKsNJ9>v4ApF7>APuSx<$je~ z{uAJ6HL|kPvaV%Znwb*rm;ig7m_SIWQi00PVv9JvVlUI|8Rb4ed!Kbs*%Bp^FfWL| zP_dJoM)DhH&)OA9kacHT?BVyC^A3EtW{=Bui`zcob()CfDH?d^cpUuL9Y)tQFhZBz zX!>s6!rKFseQ3Fu&pK*PxQv_qY?ROjRy8}D9&FXGHis;l2Q!d0kxDhERy!Q&O#y!9ZO0oOG z3|^C#f8~{o7abKil3yjXeyyhq63TQ_c2C)Ha5~tXZ%VV_19H+92hon9qA(&z+xNuw z^$*L7rid*qLeD;Oh0!k?eiJx_n`i@HsO?$dZ46yF8JRadc9uW_*=Nk$Cv6is0&5WU z)H$Df(D=NygQm8(p%S|W8U{6BXCX-CywRj`S=pCRt>P9A>v-5|986^mUGNQ~U3|2y z)K-3hpZ+`o%Y37*Gy^=cI|O}>BXJBrhkvg|6#v>5b*$UK%6jHOQ%rv41Q4oh;`Ila@}{W~!0bixf|Qj2P-UIQWngbE-6YY;phF?XY2|zhmn&N=JfL_U2uy zfyL=f4U>R`e=!`wsL**5<%rUoGjud1m-7_r2{ zYHmvz7}y==2*sHD7GLCeoDkdcb|x+`K{IYdvdHhT|)G4&*J?#vW9)q|5w-S^W%!NN&l4 zd)6_g-ZH$Jg`?~K;PouzfEKCBT%iS6BFu7jOZ;eC?-gNx)sWQF9&Jvah76PY-X@lM z8HKrVM?$ED?Dr_wu%&6=@UV>Q=}0Dpk$jS|;R3N5jcLyOgB*C%4JGfkQUp=*Bf3*t z4{;+g2%;?JN8)O41G^U&^hORhU;Iv_mocZeo`XLDRwgxy#Y4r7W*DttxxsVe`FOmc zv$X)qxR-EEY`mn3n*YMtdI?PXVs?Tlo^ z2FH<2sma2xd`Qi)kO6a`HLdMLf=mGK<|?emS~(tYCYv=RNv97|D$`OApt1+LgTt{iF7F#(9lQiBS zqq*;;oqJ+Ro_Lu*y{XR<^Cc9S|(x1-iYJW zcDry%cc_^~s~XOciVk8#7}EBk+?uuVOXUZ6N<9Oarrr)kfA|+>%PJ|80SSgWFtVD8 zXJM}mqKO!-r$pCqY~Vq%+cZMpWze3SvZ2(~t&hINJgYiIk?DCa(b+%hg5F1=4s<)? zx&7Xy9Rz+B4el8C&`2=NhvT^H7JF+_c)6nNsH|+3l9m#0hBYd8njC&ed;%!WP``M* zao+|zL3V8pfCtzy`95GC$C!`&_5M;cfJ_fbYwo9S1J4=J_>v4 zM?wif!LH7CX@S(P%{bmM-FfAj!;EWfW>hGTvX)kGar%b@8rCT)^pdkxPd(D?{_h0|M}=@y*d%Ix-nwk%RN6uPz%uv)73H$dKfe0_r(uB;HtOTq3Wb@ z*I1+;|IVolx`0(T&5XBl2-oRP$~{L7%R=i5HrFHXF0`E5!bF@@=T`mPYpP<#MOG%| z6)iATpD!C`R?|zeHJhr^S<&0Euuyfs1kfd=SO0Jb%?_Qc*NCB@|MMQu z(iy#`9o<$VNNNu$arGt+aSN@9zO%)u5Yjh*CZ&-Y4@qiS6=b4LeY;=#dUZOtTJr>; zJOff8=s&`xH8HUeT4iA=rD!P$2~@=@kDlQ_D*bXkzSYJLt~+6`KH_WV*IldC!M;Np zXo)P)oVNWtM*x|RDS8&6;2N&V3eFj8`&8c)inDNeTu5rU zfnPiP;7Gz4Of*C*obsz{)o<5~a;fnxc#PX|5C3grl&lByh`M7@Z2~gkkeSiSc;6NR zw3v=;+o>0SsL$_S*3p`4SSuOsNC@J{Q->-KR;;%X6Mb%1SzdfWICAhUp?-V5v5R;d z-vvX-pz*iJP|UIA7+e;W02-G2&i0meu%CfK zmX^$9zwCqVNTsizUpIM_J0C}Bfo!SOm;zLGOhxLRF6rRC;8J&3CbpZG@nBd~I&Aea zQh{bm=o9rHG%0J~2o1nz;rHCO8moI?Dr(_P4EZ@CO$|rw2{5m&eVWbw=iOxBh#Of| z-1k9J0l`xQ+^q6|pM`>qVM}EAD&>l6>j_Y2+;mH1^!=A;rfW%K~~@({cpl<>_R{&?MR>)=WRs3(BK( z6=MqMV66ChxjdWAkjQpLzMJ^aUnl^11tLS^l zxNqZO!mrh-7~=~c)BcnCxUNx%3@dM$>}jdZ5a^<`@f9Er_s3A(+=J=I?<6X2rrB!N zEvu<_QWcWH80whFvE*h;IFY?EkC)Rpm0gzA&{(_<=te#7qqgRkuQJ}o4;|$Q+33eb zIsH(cwM9eQh*oSMVR`=sZJ%SvmpJG{J+cR&q`~af+(Oqj%>*025WyHdtJ$+zh<~&7QbBr&3G|hFPAdTUH3r&Zd$O` zmrPDvgt3_Z5*np)1*t9g8HCL!so+UwcEA!wcdyA5e$m?~@osT(JRP%sQJ9%XS{hZ2 z2|#0?BMt8zJ_~oRQ?>7{HO=_gDncu~3g@gusmuxHw=~u-z&@i%mkAMPW>1+nYS}Jv z^R9f{I8F_N+Kw?Z48KyYziIJ;eaTZ8* z_I!p4z%qz^{>^LnmC;m*LX5(EG>DM{{alaLv8XoT0-fB4~c<#@-q%y$AGngCj5 zdGDW&!)%3iB0sG-`$)*mp*y2k12@aiH4^*TTrx$TGS+(E%z`TTwV?jr0i!>{;`?z+ zT}8$q?JV566r3UE;F(;w<-VppBR0Cea{&zbGackIlYj#*l1V0GX+40O*}NpkN<-eA zsG^m7YSV*8q#_{Jh9DGq0jIAeVZt0=Lc1{jDSDqSZ_Ms%Nn@Yxdwq94^V6*oIt@9sEeUiR(>}8u`ScS} z>Jou#Ux&5F;zsb!e40W>RMj(CBoL1`*yDLR%;4AKu(UJ^yPb~#o#_DW>i(NAee{cO zksN*ruq+|3tT}8_eaanox>5%PlW2&@`OfJ~^D}5j(XS@x9i$c)Y;&(F2~Ml$>vb^c3(rZ>JmeFV8qD3M3AQ^;%&NfRJs$OC^P{>8KmMGc@BF9eyNRizFjkk8z{W0L3DkLs$xi?~ zuXLI-!y{n*^*YO^o8r4pD+8~$*Qhkv^M;uI7{z75qcm%;A%v@^G!(jsaZM6EV%BkR z<1r^{sbqdE>dcI}IgxD``H$VVNAMhiB}G_leg|ANS9Ud8E%2=oKxiAu7PZc0=EaHW zQki?o`t)sYR~5J9DGCJg-(hRl{UoS%jP-qv^XuY}8c=O=72gwp3;$-?vhXFX){XL9faA;G zvzM)KQ+}4~L;s4v9XiTd)=_$&R0P+@;AdQH&k^;cu?wPkOtqYQ#of0kA_QF_GWQ-I zY1vDQVlcGO>QJ##wxqt?w5w5-W+`HWdG~BGv3N)fb8BRDErUW3(;7m%#?IXRpkR4%$$`8&2cof z)JMxTQn*(^^y7o^DD2QWgNoo0vB&zu&vL$vAqahkP(=cN#t~moA3L_iUa32)XD-Sc z>S61J5FtaP&{yhO2=)5?Nw>kS;sCQNL&!912tag`bNnH$Z%L4W?Ali-0PIjXUylm?P(j+38_g_RlCYAt^kL`oB%07E(UmO`FliUh8fF}_ zkOzn*>&xXgvoI>k56c3@?HL?Bzfbu&ESMnbBgp6jOehoB&Dc5bv{W3phqWTHj_CdHKo3787@oQ2 zky-VeV!Na`9aiVtn|=ml-DPeaw%WaMI@Xd_n%RouMD{>|cl;2AFxl~imwFmWT`jR- zf5PJa$61=FpF`%T_Y2Z615=rOso+%hi0j>FPh4er;KBouCjJ z&xo*y!WlQlu|>pP+uv*H?rRu;@07kK2(zys#w9a$b{tn9AGGJ60Err(@j!yg{@o*t zEAg+RVyt8Y)6S&4T8r@NetKBU4jI+8n#D#G(%hbkdq4tG9`mq`uWWT!`l4#N)|tOD z>Nm{3rVF3QraVkQb;;#;Q}>rh?Te&C8MdG!Lpe(oS$GpFK2zzz8MEgLWV*J@(51byC(p5liPDt!dqsCu%Y^) zrMff)!@ayLUiBFlTOt$mv-f$)2~Z+8T-;bUpk8(S(SfkGOUTzcFo1Y5h+A~sLuz=H zik<-eX2B8Re4G!RmVBjph7&{KA2{TK%ern=V!8vudy28m8qF0M_)PV z27TW~`?pWsX|i$bH;eeOdE`vZt#$B2dulQ|s_1P&>KXquD75X~bl%DNz=-Fi#44N; z$irW09Ao!=oy8V+AN#B*?%#+R;p|Q0pV-FO*M5XhYrb)g)l0e*wAkwyzekn8yG!W~ zyMoy??5)AESwGhWB+&L=rP+I-JrGfFjamw+xsKtjKh~Pp*Xt*a+=|VL8I4l|MX3Y; zP{WpQMx<$%IJvLPDfr6f_<$e-gxRN4FvxCx3$qZvPsz!dp;E!5WY8F-xd@P)=2G~J?yXSXS zj#vBotsSBgK^YkZ`u?lh!)Ro0Zfp*Gl6gc$%k}J;=EodB5^IcR-|=+!M*;fvaZ*B@gGu{& z|8bez(t5Lt{nGl?U;O&|db1q{^K8J?7YkPrz<-aJ67kN*ZiTCiv9y-ua2F$%%inoM z5o0>pMgZ*v89u7|YiT{_8nV^eowpg6e$Nn-8dd7j09wY7>J|_K8g4j~6_kPojm6a~ zWgC*5_2TbxYmeTKInCtwK-NhziKNkRwW>v5|MoDL^85+tJC&`TBs&Z!yV)H$Qb6yK zpK)nKU`nwUn(!)=g5QcY>IYf%rGOg?XKOQvPF%<9?dLIp0mE>syWz~S?28}2>N-#6 zvh~%~U1EOM!sLmq-bqBIs1&Bt2=I?Ug52|Ooj0!Dt2J&mhfeLVd^WUi*RDRmP4bSt z7GLw8m;-XvOSPa@IBR+D#18%rWhSiqIr`qr`t7$gZ=6^E4*;Ypg@l@_hg=Fl`i5!d?RE@z4B+?KBvk`>>QZ>Sko7A1WH1_zY;kv1cjy7nh zX^|sphgJ4P7;+qMD5E?B@PCiLL-Olw zQFR`cmC)5vdblLZx-`vO&bNHes>C>bBq${rWJ4 zZlu0_=p@sw7#OMi|^usWFx;9I{kKBmoBvl>i@{73gD6cC)c}RIUF2)62u& zDv&fWLu%K;xgdyf=|>E64T__HRRJJ!bJ3)#Y_+jqM*jfA!aGS)>uH|l3dp3FBmF@( zJg*lTTe6qibwRJN!yV)6+`UcFid3acSjb2vR%IwoNg$GPy-wA7OIumgQw>XVwoNjH z6jZT#Z3=<68<)ud=iG6e@zTz>@SWNUYGZn>(mIw^kgwF+{{X~Ql=PFLkn9Qcy;H~p z1(V1JGLwOVzFTg9RzX; zm#}tv45GEmQnLEZvfZCDTP85=${{T5ZY;>-7D?IN6CZta8a$PblEF zG66hpUp+dR%TN8}+lTl2M>M^gMN^0THfSp(*>*P0*jV#$2b5>qzhw1cpqA%bY`D^` zG=Ue@qi|o0f~wx^c;_8NwcLA!XNASa$5V2pC(|WR@W0dUA(ce{Wi9 zPR+@uYO5aK-pZ?zv_%r0;3hy6dC~r`uSe|7x+LF;4 z#?aw*2y0e3V%nI;AqXyVNX8nvCYxV&lF4bY#o8Kc+z?X)Th@fu-o0DMSKTsER5^|2 z0l!QEFh&<|RQhkYkR`sV#j9+#dK;Z+juq4oZ2G!Ltg1IUsn)Dq70}4PCO~5nfpu zl63siG0VjxFF4@w)lyVEjNeydtFlii3iS0fl0cuU#^Sgmk@L}I&Y!p%9>%guZPk-g z07QvLSoKuu25?uF3Ne#_NBVV9VAeX0Xk2(nYp2xvYb-XnW2mO|jPktc8_dnPp;b(U zAaY5;@y8u6q|`cI;NYL>)nS%&^GW$sa%e0FE$v*R@p|Ch!b5?K?+SCuK8A z(7ZBljXy6EfEXQtg5dml>xK8U8anGhwD8-iCbu#&L}RK-nWJxhl<9m)<8VP90A%(Nlxnq}wz0Jx*5h`lxj|TxM)bA2j5DCZknrRU zzvXSbV~#R9H>8)cozk@$o5hCfXsxGj_|NHE9#sWENlr?$BCsIt--19JbI?w(?M-E? zQdiq;6)<{ny99fMScs55Nx{hj9(?3=U#GOK(hHO|bb7MWMPC$$(v74$ivWKugDQi+ z^x&KjaC%z`)T}cSZ*qvu@zEf*%(D#Q+ z#eK3>IiQN~LsLaiVq_}Yx5X_v46MrWmMTX)d_3f#)SAAE0o>l!Uo1MFl4V)1T2<&S zwtA3B+bM~v(pUxnE&90(LGS=pikn>Aq7^;8uk@NJ%u-v@wbi4SZcJ?pD=8jIkVwJj z2ac!f+aGZ+;c2yQjJQ2bFa|m8w(0($5Dp`4sA6R;=O7XB=cqJkOvjeC@PgC)Q95NP z6?CNX1-ZC(JeWf&=lFZ>bzE{+=??Qy)Yk+l=~J+THa|$-8HgXhJyctJW%$Uo*L_8} z+UJ~b!sBwc)hm(x!*DJK{{Z9Xtl@HpR@)|!R%r+*D$I-pP0HORBOq+!B<&;Qa&UR* zS)S`dBSN)WNKO>U%=ObCKd@zSxbyST3T8Fb4^P}d5no7^G9@VoaF{ygl6sQ-wcQYD z-^P7QB3(6Bhtped14gK1Kj^60^YPTx(p}E7OBA~E@kwK(ko&zD?X+-!fB2@6TgE@P zug|L5-A5}n+rF0$%at^>CPRbpF}UZC=dbDwC$FpLY3Zr1)2fZq$w_aM3a7~|>Nq_7 zV~&DQvROV!A9sxe?I|fqGhM&UHD0L+^^ekRJ7%?CWom}}K-Ov`hBLYvYWsX~5&lvl zEL-P+^r3~xQBhm&s*sW~?ri;! z-1F5JN~wX0k5AYL0zM#3G{DjTdzC1X0OqB;@^_Eadp-KitF2L0Tg5c*Bxq%XPaSf^ z%%HSJXp2V0PXwtdemWYxi*<+Uu7A-j^t&V2j~zP2&py*ADgj~uAXS)b`zF4dUr_MrE!IQ#sp%kkG>AL;FA^ zDGWXV>d2t%`^1*q*Y%G~__S0^`FSapCN-2#b@`N=@VoGiI@|FXp)54%S7o--UaO{y zF~_t-BhM#%j*QY%}3!Uy1IfbE2b(n2la}R zntxWuQvx~3^6MJ`@slRi{{XA2rhPg`>5@J5h@Z1|%~5L9C1W-UVab^;IlqOXs>L5^ zwb6!>9`o7k@Ui?s z00LJh{*l&u)G}xroaHqACEQ5-WK=`x<2e5Sma!-L`-%SmUW2Qx)D+VgCZYO?NLB^I zV4r|D1_b{A2dJ4ccvc{l7NB$qwcRB+B$ft@&SOEm#m7cj~tQEbOuJvm#9mQtj++>8P3y8QEv_ou9%hxqCx zs?(MlNh*OUE1Ec6$^QT-e3E~FdNr=9p|sf6X)7u0lxrk#M&kox$abRaj4|9#9x=xx zb(A!R;*YnKlp<@jiW;U97~zW93E0e_a!F!$WPkt!dB={gwXb=pZx&%mL0fCl^p#N; zmX?xcd!0hEj42zJ(^3uzb_e$GI$C86*^6LHZzcWsM*Sbf+dhVntw=~&1(1XpX4}aO zKioMyI3v;;LZ;?qziJEj)J;mYi8WmXZ9G%N&+G!Bq1enx;vjuO211ZC(Pb`y)7Dp4 zjypXEr09Z4XA(-R=L)BJ$YM@0jOVO9r)_$^s^=`4D_8d(j;bYUmWJ7Ts&ohbsMxRy zNcaH#x{s^>n28=aDQrlG{r@;UtG>YEy#!8*XJoPi^Dv@C^_ar7m=E0aCao=Y)IJAB(LUqw8uZsCyrFse&>CRXmVFBp(DQ zMj?D-ED!C{L%83G`Fn!0S}c}2-7jyeJ7-ws^<|NG${7ImuaZC=80y`9deAaUP~Ew; z)x{IEZ4F-85;RfdoaMgx00EwQi)8QDZgna2bw-=gwh5T1Nhz%@Bzs8y=TJyLzBD!o>|}n?YP3 zo{l5Xx`y3MwGF3gBc2sg^|sizsP_U&2UhqW+@ugl=;>Uf3+LhM7~8?u^K| z**hDUsA<{{TPf(EeQ$lu2TEhlWwNYW8fjN|I@42zcHu{{V>CXaj1W@HNVI_Xz0Xk$p8E<}wkVJmVb;+O?j>J7Z>g z=pWOX;Up8tL$(MDv4Z~o(0=^&S4G}UK@2ponCdRY3e(h0RZ|}1d>p=1A8+lFar2YW zv>LAcZm^}(QB6r@)0Lt{iO{uOOUcGsDAj_1Mn>lO@xba6DN;iej+XL+%-S+`jLy+B z<~unGWj2oH!uq)Limh*KyX%@pp@Qe6ZMBs#s#Q;MrD^LFk;p-YbW^vCjo+{xF6`Zh z)RcFIv(K(H)xqOWQEA{x*=6T~eIXYI80D}AM@RZ*w?^tpfv7Gzp1z3BA)O(5p&W&m zfc}(G?Tm#SU=ltLM>hWCXl*s8o~uh(?sC$4q)$}P&kR$}a#z-xps66>o^nqd4y>ij zm)(|=;cp*(B2h_6Qj)sq=?Od33drD)E#i8-E@u2PT&1Fpcq^_Hj;eOZZNU&9xp|~c zKY{V%t4$I3NYfP{UbXjirn%A#sin8bDdOBb2bm=DKFgAM@z*QAcixwMO={_CwvtqZ zkEbLGR4F$gbpG(gD!w0)FbZTUVy&4b@2T#$5adRS)4i4aGg;&`w7N!us-+MY^^HX2 zkGUJL%zo}mb;_ED@TFC7mOVeFG~?VUWihd9D@?*c$pA9D0>2|@Bkk4{iuc?e;aAjM zYhb+HW|l9^Xk_%`kB@Lw1yC>F?~HUT^mRhG(#9#R;x;PtLITKy46yd#yMDvubPtqK zxmvtfMx4Kh(3KfTrH~49?zwJYyYbgZ{T7o*X{()C{)Y5a1{YFdl9~i&+Cyj4aHjz6 zINQksBcS`I;reMSTIXxkQN<&($i_N2l0SGT!9*ohS8oME?Z+6$S>H=*>GahdJ4`S4 zN#cy|ny(bm5HMF_tFx+u{lu>C@{XC>AMrP+?-y8f?vd@Z+Op>RLknz`bq^8GKBmAF zD9%YI{rVz==>g@f`GNO^CDrVWva>F_n+X{d6%oyn0Udbj46QGw^^TT;Jvm3JZIe*p zh=OZynIaj^(0^L)Jb(xpB=h5|m7~7Nf0B7L&{`*C3K)cluG}4i8_38c0vE2Y-%Sx< znqU1&)S4RARSN7Blrl?lx<=Ud2@(S78P0H}v&XxhI*oPwSoY%OMksXG^~us*sve%! zi6Q6vL6dMfD}V-a4t(^Yrcao&hFhMVqd4g)VliRmUWSh>qgg`n{5gGX06_QbD1MgOJE?4xjgU=M?GEXe)`&Pex$(WG}28^dSs^@{@|ZTw9kmB>IMo*>?+Zd;OSaZf9f0`K#R}fUeS1o z{<70mIg57Iil2m2_h{LUJRBeN=ss@!_S)f6k?I`$!h% zpWhki`%xa->YIX8>%Pxh>Z=`b_0UwBX?BD9hSp}s&$tigp@*?{3i(*D{{V$=vEY>w zLtg>dquZRc8;L&yC#E|(P&?9_!2SNwjB57u)nb=im4T;uKm7>Ha;?TY(taSjYb^vK z-$*FqQW6m)bH@T8_*md<00+S1_~^pZ-(4jII9qaf{KQw&wE+4HeB2iu(TI2{RGKMuW~xKvAUrPex%Ia)$;k<%n>K=Zar zrD4b09aKuBIVF!T+8RdC_>xM9n6#-n97#Sbw6W&!^rY2Px}+JS)YP{mVgCRvC~4}? z@5bDZp@Uhj*)zOS+BMb`+XXowbqHBNR!?>oAroDFm z06<=12Vs(|RKvLW{UCvke1G4n33HNr>L;m#t7>d|gpR^WWsUey-Yg6Bhm8ksw8dPJ z1fjB9Lo$Ic{5(W2*pRu(2)mEvWjPbQYF~)I^w^C;OUH3NTi$_7D^mVeBV=+ais)!)`24D&P zKo(b2)RLO5(n?{2 zl~|xC&5VJCEd2etrm6gF_H$1`4Zb^N>YPCuq!e`2=>no-fU&81JOj^2dT#vE zOFcjMqCG1V`7#qvY?cC7IL3CIXC!1F?K~bj@pHadbsWzlO=!5mQcHa-bTG>jxyU>f z-U5P?8>Vl;} z89s1v(UY?&fVu~X`^HS)kG9cXF;ZKK5ZtUD+s8=T)@ZwT+&Va`D7$-4TTDl~StCe) z&@5$0ApBr;()FjibEfGH>h~Q6nWKm-Yg1XM*4+Z1g)bz_&&D!OSieS1+=`ooR=qRa z9Z4&)nHCk4M2R0HJftYvqa^MnbN;;%>pt;ny)|Vv%kOf(#xqEsp31rkN}GD+a8e;G zQg6sOEEPe{HxbgaXeNTvm(Y{;G1O4}V?eoLWlb}$EJ;dx$A%yYZO&U*(sB34vh=aZ zPhsy|Rc0nsk#zl4Rr#YQz%2wb$M^EX!SU5?+r0k()P*Ik$z85*(bWkIl?@%P(@#u_ zM>9!KnxFrwWvNslQ?E{|xa8E%SGI!KeJU=1e(|$js3NochUAGbk z5#1ztHyF}-L%lSTYl*bG=?YtY6fnXmYvQkx9$qh_*i!EIoit2s9HB50M;1EZrk+=5GSt>iyX;`C*^Q`MD z-C5RDh0RMBBZN}tYp^*7X)3q`7V*^je&i&zbJ5n>X<(#wM~X^mSUoD>B zHWZu?I*C_MmuH50YaJe;sf4tY?NJI(Fn&(-ie14t11txX2O~M?Y->$QkNpbTwN^?> zcs)2pwbq$w!8?dZ;|NE)o(aLp=g(S5MM@KaL>`UkBaEr2*i{pSWTY)B1;{GK4?IQB zLU|bVc<I&#su^0MlEJ5`?o&lPszIJwJ&-*_Ywl((I{}Tz$;sbg7Sbu}&Ks&B8W z3X4>)G)+;syKLNFc@2(EPT}PI^--j+m&-l=n^Nh^bnR0?0G3)vCZE)v1wQ!%zCd0* z+ze+N3>0TbK^PToE$R4enZ5x#8XlYK&%ap9nQ0r-N|I-;iYr8EWq z0JOReYm3KG4Tg(CANaP70!>(oCs;(y6kxg%;dYDwGQ&q)g$>Fo7#(5N2^y7 z822KRR5I8L>IYNqb#}(@VbXM$I#^n!D{WP}D%*S_u8Y&OR_$M@qezTV`bf{0Tqo0z z{t)}v@sg1Mj|*y78GyB{bD*E%vyjeMcf)`{YRsi~e>YUG#J zIl#(FTyu{k4Dra%RXRtynv&5`J%Xy&91+L*1v9`OA-Tt-Sr?3F3zLD*PCMlmo4i;? zxr%{l7QO*3RUK6tDFvGu1zGk;2*Jr^Bjkb`r0QtsYA$sZb{a}+WRjs^$ug|OAM6P0 zf_WZ4pB-+>n2E`CN6WUA4OYpTp(`RRK^{Oe1G$N@&p0Hx>#K~W8p_qK^z-?KXp(1K zbIFfjBkUPG{G8{n7X4SGulQ|69r{-cLP1KZOk;zU4xy0zkf)9gIs~}ZYMm=zQ3j=^ z`c4=bC8RYIg~mBp)TmSP4hQ~v3e}W4%9hagh%YVu5~Yp|Dk&Ab0zEwM@yE#O$_Pks zJRvDFswYem=1dnO>T(I)sBm;}eIcWMsO|kRl89X^ny#WV93fIUY6)g46Ve0Uoygp) z5<>!`A;-%5ZjsdXiaHHjqHAsUYYT&DxzSA}J;vU^hewK7U6~^e%)&gnDcnW~BdqL| zQ9`7|B;M>8u#}D8=sC~to{8t~W!hSpDj}qT2Zez_6G~VTFhJbI{r-M@^tm$T0wxEI zR7u)2D@y1{1Zoloh0TrJ7t@qK^!BRny{gAN)VKR@ajZf~8dxY9R({3&gOb(Mky2w80~+@pFZh30C@?K(tD0WnJ3u!&NaqCLjCzU{)c*NBFEt*e(UI0Xe?X$WB_XD_PEs&I0e}+{IKdbP z2R%aR`>e3sj8>E4A}0R;(kQ5n6o5bY`nZixuC(83^=+n0onW?Ko_JKq;Zg~vU~sIS zYQP(VCm2OVVXadn}y-i(?HOx3|XxUiI6(wedf)HV*-WlhSeMly1K=RFf>d*-lM zWnC$BfGtc*1I2Q%deGzGGZm2+l5or8KWq++Zm`noIKHD*{v8xbQ+M^fCp?PiasL22 z(rjdB1(yRKJa`fTr|!8THRsPx)@i{hO4FEPK|LJbi*>PzO)L1QhS_h5n_c#r*Zn%A zG*i@B8Y*b#jmFkVU~kKpE09#SNclZ4HKvg4zOJo(N!ecJT)T41ISFFY zM^#NM%?kdUBOyV9hEajnZByAh)%;IaM;w~z-7M1Q^CJX!0+zO% zgxxeHuINK!1Kc1d$H+g&Nvb=LmX@j=P3T=^e0nm4R%qB95++sTW8ma<*1<&`!sGO` z+H$)06oraW(vmixyArLy2m5jN1S8_Rmx3 zdmEr&)3kRhc8=9T^Ccq79W@oh>CCUsKCF@`7(Z{@@6lgx?S1LfQAoBv-&nNOJO}}+ zzuhj^YKcA%vL^JGoM3RH?s@9VqzM;Te|%z;{{Rq7scy!dMJJYAfF#>bI9tcxAH>y| zPhE8UDQVrlD<_jl)}nsxBP@-xIsHcip1SEy_v=dQ`khz!Lb#+5$h6Z`>BuVSD&d{5 z!*kUkj7PNO`_u&+i{q!Qw!65Os+xm-rqbqAMpUi0#ZeTf?1T*BL6y%1c{tCU4yy&b zx)*9F=?1W`v$CN`+JjC={i}7I@}6HtXQpasp^8Zm z93cS#Rw|^c#kGzOL*Sm5n&-AUI=VW##I=e_3)wPJ{{T~#D#(+{hm5A#R1&8w1K@%P z`Re_Qr4IU>@qnGFw585^88}h7AQPYPxF9xW_sDA>+N)Mtd%;UdZNJiK>-9Yd*+Fot zp%)6dLF6k->>&BixpGPN=bpJ*{{Rf#oYuC#T8Foq_fB2naPI=%B8mo#fJ3TI@)a59 zYO@iLGCEFOJ4pI&Docf``qtLDR0OAv6ncniSRM#bvP1h2ynfvhKEBqvc9Ke)#lnJX zl_asX6pJ%7k;KDmM<7O1kZ^tBvH-}*$Y4p7w0!_^cORI+sM=dnnxtZ=D+(-9QWCMH zfdwY)?UQkZHBDvK84@9DG_jscl`Fe<@5lqk?s|oJx>Sh&0HykpJXi#i`m>*$a0Y${ zOiDWIbxlc2eX5wk83}0)3mlB`gZDTY{{U{7dV4@=JEc5zH?2Qf?x3`WI&aD?pU|jt zfB^E@2lmHR?23hz2VGpcS)eHZk$p#7< z;OE?b)1m9Vvsr7MLp603isw&9J3A3v!*Gr9<~;sh>;e?v{r=q&T{03o;xg-Y?D^9| zX;cB+kmtmUo(UF!^V(Ylky?^1O%GO_)*$U@-TVf6K!{rvO^yup#p6O<$v}GTSsg*7fQ&L{_aT}ag)-zVVKaf|y#w+ct!)gTI>Y znyE&vwped;@&%4pyTsyS&n(X#PI&XQ{@irQqN@BD>R(Q6RbsnX>n8U~){Cil2N^6? zl~3RIJuUS%ozSZsml~O3sS*}stb|OCa6U)rCyZnLc<3^Q@ueXG1g1@Z0M9IBXPlgE z1%M}RPIG|3V|Q0nw(h9;!nF0Kck5HjE-Xe6LFB8Oa4C&rW9_%$I!{S7PjRTAuGsGr zT#&ntc9v#k!NYN#!vqk&C#FO;?&Vl1UL7^8Y?lfc6-p=5CURG)l$U7;QqsATd#rFh)XSo!K896=_S2=SfgUY_?Hq`6{Z3d8YJCOvIhV zn|z9K{!m5-L`;UC?@ZPof7 znz0K8rj_A;r%4q_54l*X^Zmqek<}i!q|*0^#ZA*qi2-bxDOrJ1Jme9AIN)-A_$Q$K zKd&x!nrgPHt*fd@(4x~w@id44EL6UIzB0rwf#<6|Bi`*9YO9z0V|JytFke`@%GHgq zl0B*vFC)OiH_782RZ^EM;{z?$hN(7%Hq}6;?G4UrN>oQ&A^Dd4(_HjTx=5s+)AjQJ zgt(OMU$8)m0T}y_9OoS_^$Kb|9bFu@%G>QcwByq;yblW%@<1ak`(PjR>uza1bK5<6 zX@=u%tgHHWKslWji8anae>F*uD1;nqcv_nq*G8MtBg$~8|DlKa5tO}QWk;XL#z=CsBQB);h{{RaM^0{xcV2xK#t2O1?DK0g)7^}zv3w3Qo@tvV~3a1RD z{`{YgI;5=98miSOw^vxLFy=7qYi=i8eYiVU zouWgR89%EIGy4vyPvGB6+2bj0yIHA9V{Ym+no!*Pw>Ev;pFg+Qbw58wv-SG^AW8T` z;tF|qM3IDK2_CqT{a;>Dl=Q^)QO~tGKc{{M!afNAu)zL$BDr@n zPTS`Y>g_RCd!mUJN_#C62eYIdpqLBB=!+XWj0CI0pqUt9gSHH80{jP-+V)(fzCK6^lkt%$}W1Wjzp@Xwm(o zdai+E2S2MDx!{g~?wVHNXAx1@bmiXhbeU8=LQ=?8$?=RXNXhWN-yHO7+YZflcSBrG zq`d2^E|{}9RH~MG)T%WY=V+(e&PWF%+6d1Cbi$?YziWFh9bHY5mgj!G*Egv(#;rtq zvbY;pmJ9aFbNA^`06}5zpR*fv3RKF}BM`xzfh6m6hCA82x{~m#e$L;tMWPLV-g_o3 zkcDKkwM#^^FyuupGRjza@_G2jMVF0L-mCo$7LtYtwB>@b1(J(PX!w4Ll2}=DnHn-A zsc(b5vBBW-dZO2#iwa)iTg8^|roNJ#GK8vGeQ6>9jm2Zm0b`O*Ti|1i^+Tq0?wpq4 z6(*zCJMFP1(9+aaL$OKlk-)pMe{vLjo_exVDQOtl#0sEo2}@9sh$UPy3@k9%UCHk` zweyd4nqyv5kLgf$_UA*DACnl32>|4bD{y{)f8U{7g>}bImp5HGsA#Gqe`_@>K(YS9 zMHyV+au3_5^@B(L9JRU%D=&AdDQjKM`g+!qV59g1oN>X)>Ahh5J!pzJX_lv2St_X2 zLv4gaY}rzPB%cHLZ#ZXXY_)^Czdh$Me-S$3gb4y0R%h#9CgG zQ@#BfNNbos{{Rw3R4xy|BcV#ImQql>L~s$Kw0;+^6B9m}GE%*9EM4>5I*|I<#%90w zidtL6L9x<3(kZj;b#f3AbCHeN$L;gfXuGL&KAcq=Sz4X2suj#&FFbAd=i`qbzfa4~ zl>9u!A*sJ>+PQ@~?UJ4fR)#eh=PfIGplANRg{ySuVd|ucuSeEUM;SXK^)5)uHb*7O z?<4tU;Gf&02gNYS*>Jrbh; z#0{Xo3&8kb51vO$4Qtzo=_ti^pOTukmS!MSQ^wwi0Ov22_L4L5eDHd@5hY6Qi-^yg zQ?+HvWvt*)Eckvl+HvH4zTA1~I@5nds+Io$rqE8U?hLW-bv%)P$H5yLC!c^iC`cAmL7h`D;*u?E*q&wFzqXApVvDwV{&7R7Yh+NenSagK`bSAaVTXtje0S$|nrgBSv`bwpPQLT$GxsMrS1T6DX>U)LY)1B_(#AxEC1Cs45l8y%;E;-LccC950Q5Lq= z#BK3<)0&!`K#HUQec-9d@x}?k==W7>UdCzgWV~wqHKl~L9)OOvRy0yu#yQVT%GwBBim$o94H%XG012*2 zvCF4P3C!ZK-E|dsH&K*kZDVnQ(NQh7#0Of5_edrmnAHgAn;U?tyLUu$ z&UnUo`*c57_;uA++F@|gI$!)Cqugnyx>QRhGI@OGX*u9=!q;xadHW_A={ar`Xhy22FeM#hgT|qt$7mpq~g>JQ3tH8W! zi5jL2%3IT4RG&Cl#~=ZLlkwnm+_U~2b+zb{gZvc(mYRC18fK`bbl)S>$+A|? z8zpm+Mpu!Hf({Q})}n@@qMACJon=PJF-s!2F&MUtCPo8pZU`KYmHFtCNn8^9bA~zf zbtMc*IfhhbbeVFeZ0i`$sTmjifr(UP;4jH1&j+54wC=K9MRtyQ+6wAgtAugWnPuA0LxF;J zs(a!X{Wt^;a&yxn<@jjS7kgzjPMX&iOQo&ds=a6_nomu)6RZFAgnErr zWvlvr>S?DHXVxUfh=NSy6>R?irg-s;k&)87y#D}$?Qf-RjayA_O-UxhB~5K83=k+7 zVX@0TSAq|Z1LLdf&Vj#ZGJc|^tw|i2aQE3?>rDj62Z##zFHN$Sl_ z+bu}97+SiPSl82-B#pR?0n0JNU;%|E7|M`G9c(=-tZB8zh_}^f`bt{UO|d4Yp`@jy zF>hiBSWo~5sO$ocGT*1&&TG_F66)HQeMFwEO+!4Zamn89!z4_EoDxS4nM2?oJylTA z>ps{>x`xe1Q!R?=8^u_XrfDi;!1WQbFvNqw&PLxLfO_#km&AI0(Mo!;Qnv)5N9q9C zLqZ+KEzf`9EZ~i6Z`Ic;6)QEu+ch;Rx1~`7#IihdGj3L81a4e!3=R$gkUaHOS$w9l z-6w}n>RD=OreZ||(9}TG*(3D07%Pvt2ge;jv1-c)a8lnruEfz?DWMd18f#5LPjaVi zr_!ieY2?{i0aJ!C%j>Hhz(@%T8&c^iy*G0`v~6ur%HNciL%AgT)5ysDI)+yIT-mIbJFsjw^nJ{+Dbdra7QD1gk`8_ za1X$UsLvP&B=ON^rC>S_ll~(jf~thfGOA=U(%C6VJA)Dpt&t)a7JtJ1!&X(zPov*T z-kyvptNxjP^~E^dw=zUd^cmxk`*qs4@ZE8+2dLCA+ib8vt?&AWtd)oy1xSG;{{SO_ z{B>2P`@ODhHSbmZTB_|%Fa$RXolA)E_J30&{{WjK_UX;0zYzD|pjzwf8b-xUG-cvh ztBq|-$DCwBxO|_D#2?%47nq&LqkYV51#J3|d-D=^6=KhWC)P92N(&8K$!ogQq|GUh z5g~{=0m*pXKqmu?;Qs&}d#0ZKb*kBJiiWFrF^4hD8%dn|PF#Y2aB<^3Y6$!B*)3qp zZ@phGaZV345LiB%ERl{S+|CCl1-A7o{{Tt*196Y)hqrBLwsp=}*@+7v@$2g{ckYWM7 zP8gk|2P4m3YP(b0`kEt9x2x=REg@B!u8+_GM}pBX;1i#d(=%P^tq&xNZnpN%QcYV7 zew-B+9MkRH!xJk=RIUe-rz7X2HmdC2sJ01=wyKHDzArkmwdQ-}pQbpUtdPj@dp&{Jm6T8f<`p2OXHSVCB$t;rxz2b`G#qMzG2{kmjn zi*~ucN%VAjhSPAXg~s-lx+Z1=2Xgu{Lhg9};=`Qz8S6Mcm-@RzlHc`AvQ15oPLc{K zY9wSNa}Xs^9~^P~^*Yvjvn99s9+lBG@>+Hi9ZhYbD4!k~nrRzo9QfRDKaQ$N0{;M* zVo;SaPLptNd;sxsk`IB6uS?qPU2u*esnV9J3~!pADCPATj!DCYDubUOw^P5siEE*r z)kE8M`T`8q32?dtOwHuGtlNVh9OFC=vmTGrnu5tjvg*B7vAl{AsjP9-86)6e84#o= zkGAfN?RR>57*dX!&v&avlreg#%N>|G8DuSj4tZiaRdQDU09V`Z8xnTiD-eoet}Yb) zOtO532w}frJ7KA+UaEfIX`5Ab6pCevx|*tjo^!}xh9prg2+mXyf)9c^kAKk?3OMcX z+vd|cblaG?Hk+ZL6X4_Z%T8Mxe5mu2o;s$dqr9z_RDUk0)VsI6S!5Jet z#?kP6^a)utve9^DxoKOgO2CO4Se4~|c^NLm42}rr{{S5xOtqy>7a5yFXw%I@O`&Jh zk>z3K;hY?5tNnFd#*ztYs;DD~DpH$Nw$zJhbHJ&VpDIt!KX3H2*4oz7X4C%w()9iM zrl#R2b$V*~{;KE+UKu3jG8E&0LX-3N1=HW+8|7P_J7BD-q#&bIOB%-t76cXC%oGum z`cIM59*492)~RuDrnuXc9Th*~ZZ6WG0Pz`%c~S|+H)9{aJy~6pvI@&Wa)#BfK}{r; zUt5+O6z#)M*0aWXw|9!N)m0@vp|@UVxi7YF>QoWDk~)$@03R{1Jc5C7$5%0TbGF^Y zN3?f0w=-TYa>%stUF&J8R#|xVsi>ivH%8#&*m4(;NY6&S=+Ro4Ry)R_)3zt5w?Y_H z+acXjkGuY3ij{Kz02yTm1El@t!+oowg1VEqwIO6GYN>&anm|0Vkb)=9(!g>1br@yO z0B7%e!(FD)*()Vyvwkkb>Jk#KHry?EEz~`j?TtM+uB7cfp1z7aO43ql>#Wg{oFcqt zrDaSK!~%eic?TIiOw%nzLqkz%vFZIMV@Vz~AWlVGO1xw0E;Gp@hG4k}RZC#`Jye}> zaM9GCOQ+RlAZ)2WLZAiM!~z3)gzaV-9P&SYkL}Z3HT?_KS@q?D;Ut^=OMOkEXeUyA zM$^QT{p`SZQETfz#9Y<#-SzrggkZN6_wyNaziw6lBC?xv0eZ+W(iC3*G_A6pZ zcGI@YrGL}Nl(R>Dim{V&a>*QQ+cF%3f(hD220CBUX@1e_C?3sqtGDU;xfVx@6wMUI zWyaKJRv_eVJdZdT>%OLtJFGj*GWvw?)A3plt2QkWt8lZ5=T2({tFT0^~HOX zH1?OdS>>beqtIlB?3EmTD>7kuK9JZQAG*rr_9g;Nge`QNn^n#3?S0Rb2F;Zz*@X zo8MBL%bjoI6#7>CL13pfGkB?^b5rMQo^s zhGqll=;52wm9u~j55N6%Sl}tiBmwXT zRW~m2YFn8x*6OiKO6*xF;*`f6Bc2sO;CqPYJzKPv8u4106*H$TK@1O{gXs=SA86_+ zGavZCYNFG2LCxL|px@g$-FK@I}6)hT16+Hv5s3tYB`)W4L;Hj*>}I^(^kOvLi(RP_kp%cR#%2?w}ux^dqSIuXMh| z^i(u|)9L2IPa;Z8vnU|)PXL?`9stMRrROaQ9rbh&_RW`5C13QiE^|`!-UEP7ATD#Y zi6nvx4~E++-4de}RBH z45eL$P~O!-yCXVqB}yYRYj4Y-b{a)RR;ty|H0dSD>0x2%ghbBYunfZ>orZExHmgBRDqgGJH?gC#Jfu|lBS%?0)h(yMi0kIEiKxLS<)FUx5{d|sla%s zDQ3#XP7Y*~J~rf?zA@3AzqGL1s^pHPD7L{W$xPy%rfi;0$zfDNi?!ll%2=B$bt*jkZVa^K?d3~}wx;a= z0GNVt;Et*4dw1RX>dI?%HInmr`f5@tWu%S@$lg{YmR>^rmkLNbbHF`Mo4dN2?F_Js zqtV9EdV-ZFja)>mH>}9+%I)APoSe5D0(zmK?^l1TmMe8V%T@mXOb*E%Dp8dPGs>fc zQU^Sd?dRvO6%m%OdSp^e#Aewj$XT3?F%o>pagekUX+F&NmcaB(*4Ub|vRNJFiPATs zi_!q$Kpwvd8dQOp&Il)JlIy$3&o7HMldaXjTeZC~4ck4jI?RNy^}l-=qwE z&8d17i%{aeqB7CUl1#-oJU;NnPaa2)w^ADEHCip+-DH+JLf@ADm#7d)2ENCShO zc=-A2t(J*>8I_AnPC%7G^qao6R9je)Z@dH7Dd`#-pxp~lG9!3qpMZ|o9a&J0mmhAg4n>p zKW?};Xg?47PF77r9bsas!aKFvGKERn7(k3k87K8G7$?V5wXMSGsA#FEZPmin3@R2` z7BVC!KGjptv;mR>XFOnZzn8X}UZQ6Dh_!9bNsj3r2qcJZJ~HMN2mL<$XP_Y~m>yz? z;rqs<>GbrgD5j@D%e+8iYxhQMb+;yt7tLAtX0|MhZc;0S z#30YkOF4GI2OwiV9(pg-0xJ|OMJmSyGQ}MkmYyH!>I;D6Mo~5>1h#o%af6NfndO02%~<2=WYH4?cQ-4jR) zpMnabXFtD3md#q#R6FCe+yyZn+Pj2pzNDPx)|m$UdwC#yjG6sXOtiw zcLSfW$3vS=oC6Va@88-5Pqr!%Jm5e=P1p?d$+UN~f2pCJ6xEYZAFHbMsAX0G=;@6- zwB!;=%Yym%0D;FnL%z!yvCkd$(=Ey<_OgnbnHdL#QVAQM{RurLb-#Qir@29+?f(GO z>Vj1SdX$M1);Zv3(~UOcA19uAzrSB8F1GmWlFLz85)iT^q8CS-!)-+%U~`P}JoV&K zslnr??9mlJ5=u;^1)!*5wk9CH;7YBz`EH5lvh>=jRj!*v*(oSxQp*)x-bHx4jv0fY z2ZM|fF^;NkKZT3-#;Ve-YN%+!m}%+iWtD>ug3B5vKF5xiSBkl8HBU`%rlqHnp~^`) zrHx)ejud2Na8H5%0G^lEJz=Rd(zFyeD}-q(uzi)1uJR8Bc9jZq_VLlynW(-o;Qpo* zp{&_ett`^2`=$Zl0zmZ~BS^vce(q%i5yZ4NdQ_3O%?$I>qil%BCm z9lzVTvfyJQ0F3j|?Jwhlx-|(;6=kzc)vRRg^(iHjamEO2$>$j%x(S_B4|JsXkIZb! z*xwCJPwOUf4+51ApHHUv9-r{X-W!x`V9~c4V>^eB`hi+>a5x7K&+&o(0NB#)l$tc z^(yluaz_lw%Kre%VGDb5GKxR>y!D>@f!b|X+DVw+Vz<>()l@X`D$N~2$_Kb^g@*l@ z#!GNN%nw#d(7Pmu+#wp8db(g&F)I#36+4^i`DbSvz{(>}-5qIOiEFRbHo;4K*D+5}moMq}bd8`C>(Xb12AN$0Ptb>eiOqO>~l4TYU72^N&SJG7~b4k{$;I zNf{pGD8mQFan!jvMqJt5l81LY`TNSRLQun z@s2s?rDjP)#|jPC+s93_w#8K(w9Kbin3Hx;1-TP&`+@PieyG;1Ow%+}_ehOrIZ5|# zR|n-+$38*-0B)8Vt3_&y#-pUT-8+SL`btwIl~(vFq>UzgdjjC$MtLWeAP%mR8gRLMeJC0LZ5!3qTPEa%6Z zB(-*y?p3qZxK&yjmX;;;D?X;7734Fn7B)E~l#LaiALexjk(vwj>rHB%H1#x~7#^~gqhsJ1Pt&L^*~r=^BsFji+-2gn4jT*VDVRMmT52WtENopJuS9P`(V+(p7k@cjLvpW#ZU z7-v)=DpRxKbI1gj{{V2~8T(GqcdP0nsM9tJ{b{FQ&m}bSv|xD~`Pv5=T5sGSF&8G$+HBR)9z=+@)-sP3KiIVm*dY?eB9z!Y|BS{igz;1Xq2 zEWmNebI0G$LA5>B?tImgQm(AjQI=xZTiHMf`OYvipBeuEZmfc}7g!!I_7O9UDkXMO zmZEhyKo7tbs9Sq-iaSP+t%Btx=Br9q-3j&~`dZA41C;@z_h-S$>TUBycUtpNEk&cX zcVwb>TsXDP&QArD6A&tM{@;A_*I9dot}1I_hFw)wVvRtNYIQ|yi6dZ5-9}E)oN0d0|)y7 zZ)PDsZ}ruczNb-XTFYw6!4^80hzC6T$s7~UC!gP_md!(Suu#iYPjjiZvlhzI%E+&r zg?){bV~-gaAdpWv9Iu_+*He>alU7|~qAE~FRdtP-$vm+>m{lG)^T#KlsV_BcD^=eN zH3Cd4PHKHngrEA10tScW&{$K9gJ9bHDgt;VX3spqGWeOl{u zU`!P9;#l8u%DKTf8^O0GU2bcgC3HszdO`!O9!iEB zDybfFIL{Cm;j}e$^>hN2q!Ic8V5EWx0c2oRTaVJ%$ocu|?$u({Hk+K6+eVbFskIkc znS}6erIkGGc7d>&C4nl#JBi@+4T{F%4w{oBs7fqNw7WMV6dc@dV`qnr4z%rdsT1TX0)4$IpH}(?j4aN;yH4L&wQkqEa$t;PxKGs5kit&$m zIU~vH9e3cfK~=p_wQBpVRY^FAyZ*4lmKYrUuugdKj=XA;JEVBLGM#~0F!s`ovlvPp z$52jDV|(uv%C@~~bk}(FO*KSrfwY#Op;ornZN}*7?gD&S+d0)(=a_koqlDsRG$w}eXZ(bs)I zaFp&Qrgsqmfs=uOH4t4Db+7kq3}sVWTHxJ1t*2%_31Fq%6#}6!lUr+>EdT z+87hTJoCnSre53jb3`Zgi@vd1wJaVmY_mxOM{&Zb*g*dPUU=*4>%YVep{2B5J2$8* zViD|1#R;coe36n-ROfSzrC|TDe z)2AN=3z)>yw=?s`VHT75~PVbc;y zhtO!~q>w%acJESf#1FXx=N)*m+-R0jQB?~BmC709mD)1HoM$Y#0QeZ`q|(crkmU6K z(au$TUA6XX#WaMDV4wcYZV{t(2YVx`riO-!`**R2X91~nbrhR}C<<|1Q79N7oyq{` z1Q0s)r~850i*33=u6397`iFh%J=;mrQy4}LRaS`toc+#xb)GA(byaodc<$>AF%7Vg14c1nIzM`&%ooJ$9L|%sI&@_zUECC(lv-9Ux>#Jm3T4?X14^ z#7kHCN=py?f$}k;uAR^PJy!4fe(2LutD`8Vj*3`f!8qEUzyaf&ALpUD`_J2*I~%R9 zx(*+DA5J*g4;jb@7$E1I`2G6Jw+%_5=w^AX`f_`m=3UDj!+J*u$GhDT2Pe)300ZZ% zn??HLs_EbSKia)FO0bf$*NTdh8CG23jIPH7e1He<@zBvBAmMU-@{M);L0GhQ;y_0q zr@_{I8u~mu?%#^NmYEjZDX%q3527mCoPqZB4&B~3!kqsA9VYc}c$c^fDr>7Iown}$ zxay&_OFYbXWk6<=85qWXI*K$#v8lJrl^UMcOFNK@nC~IgMSA`^+8aZDI8}!EFLUKsQ3*ddc^hHWcq~Zy*R*z~_?J3_bsmBq^3Ak_d z2J$@O^isyQgIlL>$w^B~F*O9ywN#FWjo9QW@;r_+$sKmGtxf8hDWoeybzjRD3xg#* ztg3SypaM$d5IFPtzkZIpE!uj0c@#H&d7$FcdUmCd{{T{>)l{}{A{qA>Uoy-|&xRQo z`*eM&bXVbzMZpEehRtu)LH5N}X{EeV*W4x>ixxE%R~Up5g&ne zp_QUF65zf$0DxqwImsl0(PbBI^~L6~=&jl&{{S^aV=^Q**&L`m4DrA` z`SXst)poB`*qWpKPhz>ywK3Yz&qV3&`xZF=0AZf9`z4(tqB}yCUDmMt4*06G*e%o1 z)7RYYEl!h5wCx_?xcy!^QIVW~r#%|Op)_Jnw)jQT?OJ_{^m3_2IpYTeo<7}ITcHqB zPL~=5mK8+w0_|KbPX$|$x$<%L&sUmrT;DX+&I_)aq@$*}qU?@ONmxV_d$JrD^UgDY z(-P8z{X#kF%9@$;4h4_~;sD>k*k^dVgR+;qMH^k~yLDJ0FO+KC+f{xSU?8{OsFgZ_ zt(J-DEmXAdU0k#QBBWTwG(=>w=bgueALZwaob<}kKZ$K+ZLgNQTUAzSD|;v}9CWZN zpw2eAPy&wwBaW;!1g=y%mJ23FwO`$$&>G&pW~?Y5=8Yf zQ@l(0%ld>*B#7Uz{r>*|_~`RncF+6`3ROO|)6>(DkEf%q`uT$Y0F;V#G2b3 zI!ONjGNNLjtYbfIY{L9CZ@XLYxag z>*u0$kts_BM?M3S+4=L~cl}ObD%3|Ai_}f)C#HRAK+hyF_AYVp$lx4v*R$Nawuz*v zsivixDyf|MG0d(bBmv1Kf#WBD58IBf>MdG+uEQ-9pTyg48!aHD?F zjOTVRf778VTV9-`G0G_|FaW^0k}{4mPEG*ceEy-HnjvZWOmor`PcrimOEreNi`e;c zXc3aOeQ_myO~l+TcQOZ~I8x#<#t?z+1K=nFqX;Y(+Z~?TmpcPdT?CO$6xDHTky{ui zd;IU)j0_LktC~BNOmuRsGSE}f#Gan$X~avG{^PYn$bW4A0HNw#)~Xtc6gO*x)fE93 z(xsIIf;jR#79(grK*0UaRZOv&H-l8y%$idr3BrJK1n$cq0NxtiZnnBxYIN<$y~P}= z$^QT`B?W^9&yYww^Nw-VtkCN1Ng1rNT(48I1NBoq##M+Y@PL*8llB{rJ_kmX^tM{t z43xQ*VNOD?)_AIpRw*EO)scV$(}{M5 zZWxjd4t#OXjIx~aF?821p&>ydv`wz;M=ali=Fm5!EO*P9dbFllX_lz9SE%J%X3`jd z{5B2_cIO~qA02j=L)<7T;fT*oQ!PSzqMjyqc%xoPk~JHKKEw?5=T*U`rmljfu9odW z(554qou!gseqKN}#&Ql36Zz=BL)vf2V3unwTH0+)<>jiWr;b{w(T};5NC5lcn>fb+ zbparpEfZB)I#j=!N`gX|F$^{SAXNM8n~d5ZMQyRgr*0KAR+{PRQT;T#R6>FnSAs-n z@)NjxfC~)uQK#-&fodx6kyq8+B%oDMQ1tRiE=D$|_hXEZzTX~49bVp!ou2U>28x#9 zcB_yutGLx5lh&1+`$+bn18B@>s(JJ0rbdSBmY&rgQFPUDQmhK48R4Ut#|{@P<-h^3 za=9nZMIzXRngcA5qa}Ufz3lop?-0YPMz~(k9jE)%c)Q%9rM~L@L1d?kacbJK@Y6yD zKqn~p91($%Nx)ie0*aTn7Ms?wt5Ou#R8&+ys>dWEZ$6piHUg@Q1N)wtlwGa1 zQ(39y(i)=F%N|feEEuO#w1h7ikO?qIJpTaxC*^y0r|ve1uBMW%?QXp<)GE@})H^jT zJh;GhjF5`Mal?>Qe0=o@SeRMvIz^{b&ZYfHl8O>Y{ve>+>@0aJJ3$pCZ+!!OZnC&l z+n@{UCBD&US^YOGKQGi&R8QRHv4Q8JXtkEMqK$v2Tql8M+9#HxAXW|DOtZ@v^zV@I)_WDb)_TK%Lj4V;*nCRq*_Y2<5sH4CNzl-M%4%0!v`ab^-EW*>gryy&mOp4 zO;qq1{)sKZcSQ-79;1*BK{){DAOa6mN?*D__tGyqy&##AX3Ynfw&o#3pF1=dL8dR7 z%9yQR;u4DA3ZZBEI{4r@Cm*d;5}De0D>6fW<~2u9p4GD-gcZjU>4q4e#7f+`(b zqx7XT$N@4)(xjm|#zSsWatG$pGMU`g(Vyy|SRSeNCd>PgeyTnN_nz^`&MR%7jEJzDXyHefm|N=h&sx z%W1Jz-Y=2~BnupI6(`zA0#9n9j(&1T`OZ3#Jk2uwmiGD$T+TK=8X;WHLl~X<-s<7{Oxy0Him@4~%rD?h4W^!b%Dm zR!?@|6+l%O`=9ma{{1^B{{Rj*CZ{i3sOoK!F)~Q>wJ#u-1nxL;0+0{8ByxHdx_%z) z_R4CPuI?SaIU)jIUGmaMxX22Jmiqvi@sok(s+g7cL565HG};-TGcm$+1&337zq})( zro`wfN7GZhijqzgNF!7I#3{)?I2`p`S8e5op9Ev2=8n%Z9Y)VuYW|eFW>|n7vwybu2>dzey(zA)hVQH@(P5*IM;#l3 zA*+;vSK6c6ql5FDf3I3z@vPH93XM0UbR|Vx;XO#!j@r>cMn?w;x!VNtFbDaLdJd)3 z8fu;vdJA<8DS$e>cKS7mqEX8%j1(wLj&Mn02kp@WH_2`-70QZpPHN6pF;XTmsV4!s zImp4kocq662qX67<@1sh5*P#CJ4todbu~|_Xl10PsKeDfl@C31%ouDG^l`}N z;QnPy?#Id7jlD5#H+@lfq@Fu1H1ofx#uumy9DHMPf=T_x00bYn@#9oz?MZ%?rc2lL zYT6kJqT8*ri0I;sST;8NCyNgN>BEd89gj4{h{vGtGOf;dT!wY z)Do{)Sj9^oea9@BVaW04RZTkW)X;} zI}@xZ>uT&8hKg2Xky>e~Bom3k;F0R7J~P6c5Od?lU3Cv>Zgd}62AYy(FpwZ7b+9vk-D}+t&o+9y~ZBn1#CYfT z80${oDYO++K|O-GsbPu<8EUI1-Bm2Ws8Wd;Cujhivi$Hk={2ium)dD#)bU>?{+~xw z3=>2SQdR)qC{wkF1Bb@#pTA8@64o7~3bticJnqOSC^5KlJe%F%7UxHNwGg+vQ&T%& z3^{ceBrw1XcLUBpf`0u~dZHQ5Dq|ZqxMwRL3!IVUf=L`7-=cbpz1Hn-0^;!1tTiPA zM++1V$J!b~aG|hBI63D#an*e_Dv1o$)k#Rv8hO>s0z?vJ@Sp;4%mU?DbA~E%2fnqQ+E}?|J5-IiP1#}y!2bZJqU!}j_c&75O;oP)FnV(` z#{(FbAYp>#v7eLYfsVRCQ`;M56crZwiDbBB2BnTqsaSB^oh6JY^=Z$QbYjXt4Ud-w zguM3qOH-xKRNBlwnwe>7W|^K!r!A5Y5wQn=7bAcP$?M8XCwx*#Lh1loumh^HVaOf_ zJ=(x_o$VH$?)xAln>ioS7RBvbyn>2ztR3Xr0p z%nNJE_Amae?G@3_I~bago}xt!Fbb-me)%9G^ZbsdeQnLDeWRgC^uXV=TOH`@osfsozpHo!NJCZ-U z9>KKa3=Dn7dbV1^a}evm*BmeF?(ZBbZ>erpTB?wWsOTe%;#OtI2MhsFPTx5GI{u}t zp{w6JyNJa;_i4RY{W(rJEHjb&fJZ+aMVie+B8gHPbK@daTGm;Q!OT*kfI4>x1Gl#TkJX+A zMi1?RdN;4_ZO+wFvEFrc4Rfm@mY$MDkzoXXd$%d``ndZ8!RnT6H#NefHJ%wBI2Q>u zVe}QwLBv7ZE&=0r3&wtWf`uORM7pYZL$OM%GdI@xmh$sxtd>`u5{jB(LTFURl7(U< z$=r7Ud1H~!kJ}wv(qDBY4T@_WE!M6mrg+uf8j%yYb@wAG;{$MPli(gf9dNT~U2AK+7LbJY-c{Zl$?ICaN(p%Bso3sbTP0hdh0{0J>X!7^Yh4IHE4E5_r$#0VH>eSz6j8e8J+NdOP$Ggr{kB=N5jy~N;IF1!V zGO7%@YM&=SMVt*XZY=M`q5ZGBv8Z27QEDsQ3sO_0O$9H~RyuB~cv)I@icruwj1~%a zWlI+FLFqTAwDpI$S87XDw(mu!s9~fK%ROx0P|n%Q$P}M^j!E|C9~tS#q%}^9waT__ zP3YdV#lc#VqORQ#Y3N%B46O(yrcfhL!9jHa$PA<&fNwp$x523vUeoG2TlKo4N0d^` zLRHh@w(j)YhL#h!6_PURf&n=P9txKD0BFBuRWh8^lZiu%vkgY($cN7DXRWwdHFlAx zo-H?Zde5f(nrRZMIO1qj40>yiXk(Ib&p7@1thjd%y4K2ltE#UwlKJ+Qjwk@fxJXF^ zbMkZc$3&Ghm6jN#p}tU5+vO3dQ!Oiq{akyF(m7oGWbk^Y(R$wh0Hkj8^pV)?wKH5~ z^dwr!Fyy}O6$_AoSQR{iFnYAW21Le11x&>#_JDVIGuibmX1^Tb;?wx0?!BTXU-6x7 zc>zRG)X5y`&Bk+;$>+cyev0g$i+=FeOerq1S*Ly5mN_95G8FjRwbh)A4t77zdLpOq z4{d3cnts4qgt>c^6}GC)v03zEnpA5a{wRsu=jYh)BVrxrlq&9_TAeki;9H6jg4^io)Cmqr#>pLyIRlP^?A9x_-sJ_d+c$Z1rCnUB6j5I^ zoasyMAZ+#NOt2^)Ei&x+XqXKv|<6irOXi4{v-5G zqn0f_sI;6__UX1rX>JsfmQYXY8y-c+JQ0F()O+5h{5IL*hD{&ar>3l@pVXD*vR2fl zI5|{W%6+Hz_k3~Cw!Qp5_m<)WhTCMWskN&SQB55+B$Gr*J7rK6TjvD(dGb0`%X8Ek zTD9!@j)Jn^S5C_vL^Yd4U@tfU3glpd4(xb7Ix-O`{UnY?Gb*e0oRt2gl&GA-&>q{I zJ9xA@XYN1Y#_=QFHO85}U1~waG*q_gnmFQKcW+s1WKvHU+84p|(N>$dei|y|4W_Kt zstM$cRo135-;_^>ELl{O_vC&0LkqRG{|f&#tFdvx<6(B`j3z1F;(#@m?C95QZ@ZI9Q&yU&NN1-&^@|% zWD)Ax4L8YXql)23kc{zyyTNt+N8t14q_1(bC7$O}YAl`7(l1^n-7Q5O#irs+81=ZWt2!2jR4hDWYnQFJ#>CtYr`r=zl z4+}*slTZ8xJ4fzR505!L8cU-owOzKD+q;WfaE_)RlNpOIs-%Oo#!lge`;*AddiQc_ zSPS!tuW22hG%9M&2LbRX1d*E*h9`5C4iK8Drl6{*RH3z7s-;y>O)Px3A1fdv9|zA? zboMHEsHC|tbb$J>O$9Y82WP;{E|>&+g6D&dFmuyO{{TkpHedMa+S`RfgO!S&5+|JF z8x$7fB#gE|1J7ORw3FKyPakY&K9}Z6^%n^ov!4h=YC-XngS36mR7{0k#7zGHifJaY zX;h*&k^w*NBg6|p?U%e-<4)KOh2^M%ifNUq?UivYMLk+G0s%szXrJ1?^?q;<2d5UD z{6uLh){28qY5NtPhtb{jw!KwbT{6^3jIpDtQBW7d5R(MKI8p{%qy_TvdYZXtuMt|P zDC687d8J=jlxNti#5){g2Lr|r+p621imbfRQ`PC(MV69QcPx$sN(Kh}9$1nO-1+EB z1!cy5akE2QDUcI6l9fJZ5(C|{7dxMnev>8lwmO>CO?TC>4J%Ps63UejK{T*W6BDvn zrvq*{8r?uPMQAz*)ozO0FE)7an{4sdg5zAqOQ_C%)ZK0dIp~6(mYQQIRZ9#_U>GecPel{ zW0+L>mquJF#5(gu+;vq|dwQ!Jl(EX2KqoOq@%u(OB^ZC8=&C}LoJtSxjAlxzl9eW1 zGORa%!rBz5lgOv6VtSZ$2d-nJq?KWfSJWz73>W<59Buva`}O8MMRJj9De9+L7)HNQ zBELSGzU*!s9!_v_Gy8$ouGM;L@bjyw6zTmtebVyI5=jjtkLZZN%O6jCivIv^6kz`V z9A;a_oQPcKqtda|+~~8mCX$oUmQn_PLXw<$^PiqN8I-h)LE-9p>S^mTrU2<{>EeFv zCfnbwRyEk*j4f`n-LypSMRHv+dUVAa2t#<0_ zqb+!+f>`2-WvFk=lX=ccpVfeWw*w>Rp_`RZ<#=Jmro) zSRCWe&su*?=^3i%8r81kv(_x>>B^MQOAD6smLZB~A1VhpIqC)X@K4)aLtNGN>%HQN zcLcBInWFyyQ7Cl%un$senIUJQo;sJ8Y*JN17BJc2Ra+n;jD7L^b*An= zfl9kwK&owf3~;Doj%?96Ky$`M2hW^+vB2t<4%v2>wRa;O{<8OXx=KVT9X+w(hwp;J zhR%HM^VG|zmpaKW$^%)ks^|%uQk)>=jYD=>yK+h0pn)#gcCMSa4$|N5)#7mBjYS>G zDIh9yh1>?5E99(^_Eq5$|P+CIq{A8;cUhoc#Dc zItOf|hQ?ozjj!;@Q^=aCWQjmz3g4(m0{ZH8h!)Ptc4EO&l{DHejtt`JzhcG?H6U)Ytj~aN{aSHkX9oNiMREzZ_zFWM&`yb z#(bYBO@8hATf~@Vom(*pDx8&}wZ`ZDp%&&y@X4DEw&``FNHvz4uw3cnDx$30$dT~B z^Eaw{rQ?vN2kbhssO^OkDPtuS2G7l=m0}9uc;hNrK;w)L`*gRHx_!>QTtrqH&+zRp zqSX~OExgr7Ob}*9m^Rf>oPe?9k}>c)_iyigO`hFPHQwQKyjBgYpi?wrXhRUPO(c<( zV#IUE@-j*3bx5ce7T`U!Zpqs_1%Ib8Dp3A)0ru~o4!OZyUV+!vqSX2x;}ug#!lpqi zk<;dH83ZFa3_OrmI3SN5NRLKq&63-qZfK=6(%a#mq9rhfNfWLANR#s2N zbPqz!Uua4=@wrwatvq9p%=GUvvnG5rVX_JKj~zg!{AuXMsA_FPa+L^NVy|Tq2W3`3+ptr5s=q%WlOXtG z=ccWXyPd1nTOy9WDmkYaSmv2yf4)Z2QdP;|#P9Ld?MH4jCGNH>+%>d~afLrBt$Ce4 zbtLW>`{3i_9aL1JS$T*lJHx{Z*mYHM@KM!Eo{dtV8Mpw~l6Bc0Ht! zN_iagcdow*6t~xw=cR8q3QGOO*qY%yRSliMykx5>C*)_Pl?Q1%$)hK%nwwBm&{nZ4 z9Sv11=>d#>rrjWT7?JXSY;^J}rOi)uK%WETeIt~gu&Z`-=ba@%BjDb!SVid)s98#cVZGM<9^N1(Hdknl&7UL_n;>@$FE4w%;uY z8Sd{35Ax~0E+}<=o2aBjZ8>7GM>$laf>)WnW!zK-4e6<2<+m~NG6s6Cs;j1kIg(o2 zD^E&uENW1b^35Dj$oO5k#{PPzs;{6M>g!{Uj?GRpH57%XM8hs7X@q-~4gt^ZNGIH# zCpD*jbQMgJuA88`O;XVPC0$KjBu;8jHb7T6*Z=`n1baXrbWtfXv$r_S{{R}R+HNGe zfc{jZc(Pax&c@sG&@o(6rqtINnNSeBRNRGbvQmr13&NAIj`CqI1kmaSduvRbZ9S#P%8sKl)s7-XIYk9W=(E>FnL zGtceUO+~9OUEaFGWTv)SQa5FtN<=_~V^TLrQGP-26lD17a7j?j;!$6=DJBIdU_)I- zKZx=W?Wz1w=o-uH6!i9MzN@O1XHsCNsF_s{`8OEka6e!_*QR|X%C1Xd-0Cg& ziv|}lQ_W3H6J>)1&J@dlNGEfA4t(?rp`f{T(%vqML2I{CmGseT5=f&s13r#+g=6O< z@6}GF*YoQeiYA}3-nA`dBAu&3(@z8imm9Gd^or!*x4FH%^;O8gaND2l6i39BRf8O& zu&vAk?j=AE4?Dve`&W3L(u~zLcRGmUR&!nS@jTfBa-d&%1D;7BWqKK5T9rsDG%bqb zSx~}CdRgkdOs;*h6~R%FljI!abtd209^lPwT~Tmbg`($Bp%#{&7LKZNbCh{ze@#;W zcue4*2cen!P25d8E8M5DTrMJDA2k`BRYvTn1@dv{_!vAB)euqig$UD9Hp=Dp(-5nS zh<{L9*^7NiN2K(PgZlpfaBeiTGS_}}H9dygoSs-j+ZTQ}4oJWR9&$QcH+K8CRPUyA zhLwVj(8twpucj2fyD$XFE0SHg@y>7u1FKt=bob*)Lw&LnMQo4hOHWnoM;YHBMP(a{ z5;3frH5BkDi)LyZ{w_fF3c+ z)om5cnxtVR!5_q@ehDGYd5{_tdZS8fikn78leR)ty(p%qufnpq4S;0Z$sp|njC=w- zfodHKeG;V|&b|{mDN_X?4wUD?UHjR!}G17MXB$72()UU2xRl+MDLY(BDUAzIw_M8*dmJ+8q!ka$2p(+y4 z0$ZuwsV(e0M$y`4o$g4GRIO6P6uMHZZ|SK}xMm{+@NzMpK790jQ*XCYQ%N=2b)%z! z)@dcBS(T+=s=JgGDy-Pr*x-?#d>Ss_+ciw}l;7dKDO+}mb|n&?j0lM%mfa=*10+uh8B1d!vNQ5JtJA%mpGdFxOH)!)>6kCg1a|wS z;zg)igs{~l?*m7X=`4Upg>Fj5_#}cl1F?5gMbF&hq-*b*gHLMD>P<%_HSWqS(8nuC zOSIEHccii7k-3N~jhl%8q^YtU%A4=jJ1Tk&sJd||$#-{rDJcgaQd=+`+V0@x6dl3* zI%=v(pHXRBbwz!;X-gQRnP-wV$HM|i8T;e*Jt?HM>dGl9Vj6vxdUo_`%|=z#v7M}q zWjX(SuYf}`#mey zMd%+vYib@eQa~guWlLj>miu&hMN<^yTX>ARxeK6131+cHSfD#nZBp8yZ%9WZqMjgqR3VW7LsR|q@WbPW{=IOHLJbj`p#4ZIF|nH{42 zNl!fmn@`=RYO+JLR`tknSW7)D-~N-oHWU6 zTpmE@Jz49%+*VYjJ;th>v%E^i23X-olbk^$q?J*RAbCH2nm^GitraU39VGt%^0Bru z#wdr#7*JgNoaa7x=dKres~mKcOL5dTdOF5Olu^w4ow?z{0zd#~m*YJyYKc(5E#n52 zumqWEU0RMIcP9uN+4DEl_#BZGz0=itUVrKO?%HiSa=ZxQq@)QXsDXgWl_ca~c>o-E z`RTc*AngRTGF@((wyIefhSOPI&^T5bfht=YxC0-~K}U7jbb-CvtqToir?gKfR(B5-W?A6D86ceHIUei-j;>Orq??F@R>`HD zfB={n3xYs849L{o%a^d%Ti@t^4E~IiH)?A^l2%QrPfF%uxy=>peK7dsK2J8|mON$aBxRkGSOhxaet?ln2C5IMG!_MRdb0 zRMO>^u}Eui4(F)+M~b;CEHzZFE)}%(tua4N7)K~TF@m5HSey(HFah#n4RLNZ|=1e!QxP~VmC+SHeyl@JnTC&6BG{{YR%>KhTla)uUDMJP=r)H2b|cGl3;(VfbgTRlC$x%S!{ zjk=DT(tx?D6Z#~Qs`Br4C3y+EJ3#kO-lx3|m zs?=1qvXydJSaZ*IXTmP8^|l&Yu}vv?uDDxfWJ3jf%G1-aCwc@@3xSRp`2=+`vgSEJh8lfqD7TW$T;@{hE2s zbuA_=k78- zdJeAB&>)d5@LedRkYz(w!&4h3GDbj0KgsF@7CK~=8s&JMBEVzmQu>TebHOrTecy}mEC<6nL=N%yHCbTV2t4y_WLK}$ZBPZ{ib>>>>E6PJ8 zq6pP+kcDgk$=a$s^i#vBC<$3)6t*2+mt+1ZOxW9|w-8FFGdK zr0s~;9YYiiJE&%lc0wPLyGc3l0B)_>mUb3}0L4H$zohL6Yh($E#8?0^dJA|0`3O9; zX+Fg23g)J>*sW%NO`X-MY73xWIF3c*jPM5-^UqZ`9>wX&43bh*dKk+pHLr7Ua1=0* z5s*(f9WT~BHWg=SZtlq3OX_+@Z~Y;Pk%6D*BcnC$Jr$~gCZeFa*8768)>Vc+m|SCa z;@$_q>QE)KDWa8AD-kdnbtsc-*}O|~u#Y!>+Upy615Z}p?o^P5j!J4vg>65jz`$ko z7y*J9;Ep;F)S3@pBR~8Os-mch5>Ksgo_OT@4qJ8zJRD=DW}f_5(^+ZFl1|^yMy?LY zp{kxKOujuw2RnuU=PG#fj-cE-vtYfM>nf%+6%u>Vt74LTl5v)0FVD{ecpn++jVWY< zVGS(S&?O5A#Iv20d|JnwLL_?j;!^8sTCo@roB=1Mxy~7XSC;w5@z=p6RH`a$wUseP z26smW=aMm;aHYI|1B~?etfSM{`047dRMZs!Vq~cZ@kxyG-a~={*QjqB`JmKx1w`5jK66U7@k5ul+>(FwCTvON2 zJLzo{Z#6ZQMiWy{LV-}iK1ZZb9JUX*kO2Gi(a|4<4&dq=bPcL6^!6kHwnY?6EZ+0y z0kBwS`?LPNLess%y;<#4^!i^%Su8Qi!(^e{vz6qXsGt%D1OiTZ`Rew;tMA&Hwdt=n z8`XUesNTFOG_>#_VamL~f)57@8BguwsHjxnJB*Ryd@}KY?38SYlM^a(RXs;R-A^*6 zDkEDD7_rq_OYqIwX_Y&NtIg8uOrDhVe#xh)axfN1(Ct(I0L8Ta{R&X)drq!vhMepd zXoZ+m#AB?uQ>=1S`Ue1}7uz0q&RZ?>$4%IDM{BLG18J=ZW|HMqIY{X19MOP`4(CZ8 zDP)<4j0_g|Adaq@=c}v{M=NPsYn3@3taKJSH-Hx_geH0B=Nx2+GmbbNAeyj}ETt>_ z%m;>E^N!V93=|V7a-_@DO9g6fY*hQ$bP%xZ$7D4{n%8%V=|M?!tg4Yht!+$47+ymK z1yE%82mb&aSW|uC1ERZy!?{auX)XG1)tG?Ay+}O@U~$gE z;iUQJBlPvMa<;pi2Hi@kJt}CQ8xC{& zhBAIjwtRK(lq9-|QS$MDE8=N%g9Nl9N;CwFXZk<@c$?Z9YpE%v#Ggq|>N9`Locg%O zk`x6&$J`#Qsr9WCbk3ERn&?s!wca7M5q|uiZyey9@;K`?Rd;f~Q<-gcYRU^`Jitb3 z8g>qgf(TNX+CJx#)MMM&=&C7dFPhb(iXz#DEXxor&Q9jR!ScY{4Mq((3^B;Xt$FF4gBU+Ee(+H2#f*xtmNb#QV#s+K#Bc+N68$5nS-aii}SI%b1h z%Wg5pv`|yg+owsJC)_@!nn??1j4LSPfu6GP(mE?hdh9i<4_tuD0I8f!4$`fFaez-N zf~SM$qzbC4Y9WnQ7~v)4+CQ~}Iov)z2lLm6KNS7# ztCwv)r&;4h+Ob7$M@Gp105f0$InT5ow^S60mRef;%szf2lU$rtw82fQ2IyS<`Nr9~ zXzJJ*)<2|B%aF!F0608uB!Ad>@%!|Y?iX{ro1m^zQd^|7(?DHaYU!jb?Vq`PFYqy+ z=b^nd-Rq^++9AC~AeEHOD@r|ZRAi8jyP0|5WH;*P0CdQpw&md5Tm^N-ulQZL=5vRv)4)>Bf}+$!aPJ!xubQeQxU zf(F(y0z8eQq6uis#RCxY9^LO}=QoOBB{t4~x08wrcO-(Z${$w%{#~CLCf4Jx!bm~}UskZ73H(yO9JAFh{rk$Nj z`~-H&WsXlAouGN+tAyb@<2t5#mXg4Oost8+^dOew;v!a&l9ryPO+RF#qN=KLfC{Pz zs$()1%9f~w*UyYPkiK~6vhUfNI<}Ic=iOZuZK9@eOwUIbp-U454(qrvdDJo6k~Xmo zgX5*dl&yJ*u2*}#4xhJ4BETt;qmH4Mo#$fXXvsM*o_yyWSKn?rZk1Zu+j~=7WT$ph zT{L1@LXdsQ$AiurXu#?gQU!#1M51TnMb=1i-O;_(VRzBaj&(D=-oL}8?HVh+R}D#B zJd&rOZlP#x-t^leZk&7Fln>Gfu5O4b?iB*qB}^J+Vd?{q?v0Ky_rrhR zr!|9L(%z`I!s zo+xV_nH9zz!m$Mz562+x81fH8RxxrR$rEWpLM2Y*0PkyZ;X_T{E+0S7qOJN)nXFVb zHk#>)JGjKyJ@KPWiH!gGN%~&1IeJZyYH^%Ot;6h9)F@Ze=CCxyBE{0CS#_l@~j;UdSn`Ar2EG@>h8S z9(d;;9~~Vb6A;2P0iQ`yR<{M-$L`AI(WF?tPVf;aVnPhc0J`m`?>t5{HD{85r?ad@)NLCq&R2Gqc z&PXTr9Dm!Y;jcT&uupKR(e%?Zu~m60WTt{KgNzf5jN|0|=cI(X>hYtft1p!|>7a^M zR*ottW~zmYZ&m@zfPrzt$;%MF?x^T{;izwPQdeq@;K6E@Rtck$9kNsu*xREK2^ek8 zOKy*G!r%eb%{mZSho9%~3xCYBr%N-tS)$3hnt2F&RHlx2=mJ5=w`g%$#QG=6)7*u5> zkMjU=j(S{PZ|37)6+~L{;HI7p>Z?p4XC43;APk%Vfs#*w$?0)&s1Lc6teicvkT~KelMs=(&~Z<5MUF^lsb`f!HC+2yS0@NuLKTmLoufEB4yiN_v9s8s zt&Xos)L$N`5W)$#g#zJ1s)MwQwp@~V;0~JBZBb~leFbtuMO{HC+fzlT*i*{`V5H|eh?q2^z2oa zNpAM-53eFz21=TZ(MT8dF(Jbk!Cc^E6X%}0X{}q`iYr8xeJgkF1vE~^B&DRIk&Ibk z&#MxUJb~@U*kqH_pF?Y|`1e)bwrifC(l87mk_|i_mSrkN{q>SzNI549f=B=ybkAKu zrSwfLZP#3P!D`^P$|9H5^rMcQ*bMKlnf*}+zCHu-=p|) zROXo`a?p`rql9$2!SEVxjBQ;9eVVjfYAv0@n9rC+MDSMnvf!}?$z#dLz~?-UIz8?G z0C%qz*Quet>ngA76tz;pJ*xFIwKAvxZ)qZP?0w0#cE}2x1CBarUHUXweIO2hv?p0AmYQZ7TD6&g{{RTrk9y1MZH>sQqJYT1Oeby<@EmT13GX9aM~2_Ub#9MIf5MWppj^6jH^J<`=~ywS9^6_)rG zx)k(8n;&Y-h$dBX0eG3mBRpfE#qH(eRLeSAY}R{S4LB6_k4Z;UOCPV6Xr-8;DttH;APj_Uf5mqhKH}x^t#^Xd?ZUzLv9ZZFRH;tVTGq=iVe(M8F0#SySDSyDio@I z@LO;$V^d?wr`jp(W>Y0ZpRFu{P4msm?ridfd>@0Yc|~=OtLkaxx5PnHZIq=_f-*|R zRdRFpai2Y3)czRiDKAM|T~%tDs%2L%sBaO%(G!4Q>5dDZ021V6^wx*D);$MrO07-X z{aa$EqN9)MeM-7I$tc{H%zG8k&4rAT@fX}q6;B7Q)*kL?ZtC4?ZJo2%I%4T{SedG* z)z+Hik>ym0Bbqgph$R64KRZTw9cTXlh|MPoNF2xS9VFj`_G*V~Ff6YuDN~lzTn`p_ zv7_zP%FS!1rjFNDKjo-Ms9jMe0Y|*;<+Jcq_{SrlW*z6PxywUAY)vl-MvdInD^;q1 zcrUxtRXi3f+yTaVC%SgJ*LbU)H6z|-kiPlRuJc0~E5;g~Tnusl08h?y)pgUgc8g6E zcS_AsRZR#cj+$GAV39W;(;N`QxH19AR7P#1Cm?65rfO2=Vu2@k{r=E}Ud^95Ar!J! zgr`Kz2`$9nalwmkg;uNW_4+$S&i??adyfTLh*_$jxL59`sbhcyuI>ze?jw;(uq5rr zNZnUxr0zzUqPBM%P|8zHQ@t&kvX*G1sHTD0oJ7>SjCGL`7u~)om3ak#2wpwx?L9>e zEx9ymMSp4uX0Eu=)yX&2&ngCu8EFECXw#MhmTzNl81$Z-v$~>q>E>E<43#AQRn1bG zLn4#8V~N>z>`}RiIFBQHZXsFV5k6az{!z)1S+S~c%a%}D_bYcUT($V&fRe0z;8&tBVT^#tTSO}<%cX1Cm-EX;jMdYVT@OgJagJb50` zj*wL}(4qis*Umc0J9?3FrOZO9eut?52*B0 zS5-HpS2;$3cL>iJ!8jc*HGkt1LFrYJg2QLLTq_|!kd}Z&3iF(gcH%L~Jadi@LK;)L zwOXH0-0k|FdsU*UoUs{VK@qfeYWuJ2UdN~O z#Wh;fBds(vlcXMlXWScjBo2M5+i~%nW2X@)DK4_@zbM3?v?d~N{W=m7ul+x)@wq-g zYq&iwwER(Pt!p%j9yFzt5%kE;I6PsB`O{oN;#5}qvE0f^W_Qu z0I#jLGFDpUrh=(k@}!p=cCf)Gk9j;~bDlHNEt^d0eOE@+a9C@q9wt|sdPP?%bDiFd zoTPvC0sucB9WrS}k+4Z$QS46HfH@BkRWA0W?9+A3%yXzR3A zp{J>qm8oQjsZ24{Z*>v^!<_9o2LO9|J%DgtFj{2Up#okWDg+-xXSmJxJSlD#Yb-XX zYVUTCwI*+fN%XT86GcFqy<8r5G2Q_982QhTI;7QlQt?m(ndMK|gWPg(qOET7fLOg6lidv$SSOX&y5N96U~= z%K0i!BdcmXE8mM0@zv7Z68&x%aF23jjzBPQ!;**gIP=kEUvM?0j+Q%xZiz_hn4*~K zDc-h_Mn6_vNMvEg2PE^=S2AF-2r$T{q*6lbrA(KG?ovp$;s7J5v=Em$V8 zm#VZQuc%W`>T8XqM~q|A{K4RRQNTRsIqI$(n39`o_piIO^K!C4y6RhmMaPlp*V4`i z{f|`br^~XX4WOzxYpTG+dItCOA$%xDk%*_O^!}B z6kL6|C;94h_FmHJyJyr}YpOK2(?{z;6?0-a*pCBwX*_>u$r&HVSM(SCHnK9+_XoOj z`YHK8i+(a!gf6g0V}FQ5Bh4?Rwc zv-*zmtqmV-ua)yT{WMmYqK!^`wikoWd}EG?>udB5w5(~VF5TF)qN)D?`Ch)Pib>lz z%e`5Yk)B35{kpEacDwNMVj}ge``Rj|Aw)^2DXW>XK3;f~9P{K14tj$=p`1(efsKW- zLc)G@j+kGma{h7mPJRxxja)xhbhTGR=nS*l*<*;(rcMl#Jb;1YoB_@=$5SgiciVk3 z9@GB-s@<)zRV#*A>-D7@PBFm^wnkt_^ZR(?p-VU6fAHa`qpiBr>Q3fdsUewCDj2D# zeQ9NpRsAJ(f=^aLpl;fmMmZ~vm>NIuz4&>flI2V_$8qe`Q4}-Lo3*+XnZjU3^HR|l zQl*NJIr+ipCaR@54@dQmhF-+j)0oYeRsN+An}0|g-Xq(zk8Y(tsP9gIxmFbPM;a(_`s?h4}r&#<4{F@oGeb+Jbes^w3Wb_#%1|VonYIU;z)9fej!5f5U0kK6 z>Kwnkb9Mg!4o{Uh{JINhQ*R`>8gJtZ{X^ON1**Qb+YYvbO-2w@Q$kF>l&pZ1W@b`Y zFgYrvM&1ePrsLU7SxZYxda@-%uyZMr7)+9*E*LNo^MVK790%w=9$fyH{&1IclSV za_>`et&L}JPS6rGc6{)2$HC9XydT?VyB|@0YAsFyXfqyAN{EX)Z_v_uqT<&$AmFl9biIpRE zshV^}2Mx8Qe4Wa3%8xt_ylT{Kls6;cYCx{goOd}{JwPY#rddX|zw50LL0IzD+gd3g zDjty#!m7&H4HUa#UF(MP30@(l&!ycA;sWmiBTTW?L`MBOkueI^XJc5v`;LMvM?+e_p@?bo_w+M|r~Cf^WuW@??mm_`*lH0%S6LyA) z_o3S%%a$wcIQhnP?W@-7%9(1YBbtJ(;`Hi@MH~qx6KhHxFy*kPK6A+;<$+IBuwj{t__VPeTb6rLO{9K6l7RY_7(l1heYQN|yO zjJH_$u!bzQKf&Mlf;iSKTW6YB1w<&uZ%P=*ZP;~pL{YR5qyz0zGC1qy_Mh;m9$2ci zCZP8(zzM<>P@%00+tF&(5rK%mHrW#u>NVC4^@57DSimsx~+`UI=i*DF)Wb{KyafE7> ze@~ovb~AmS*RZINv>tM(lOy=4D-7NVYOL2{1QIiPuoz{*n7>Z3>LdKh^DsL zYiZWDc<#vsB($-%?XJcX`I*~*82})Iw0P@e7{+>#q<})r{qAU7Cb+FCq>xaJTJClF)TYYCJ`s{EmwOEbT{2m;d= z96R7(csoHj8T<7LfNA<$)6&%2V6)S!G;yqyyUb%JXi^C{!k$Sv;PMAhEnUO8XfLR} zQq&Yb9SG&RtjpG`0k%i4KiT@DW^C&B&%d72ON>-jPx~V{6zQS zE0fD#q}EzctOnb7A$IeH3$$kda7h^F_UW;2?gpgP_L^&J*+u@Q?GUJ1il}~yh!$Pm zlPqCMl^6hS!ybQbl`5*2n-T9)-tvvnBjMVX67h+Rm}VzN2`jtsJWMJU_j|bB{{W%t zT}KnNctnD!<2&4rN2|0Bd=9R^#TKd>q_k9D?zZ?*QZM)fXE;0qjkr)Tj!7p69(twj zohn-H6mLyEBr+6LWuvE#W{jxI0Z`3@h9Gb zj-j`JG2}#C05EwN#t)8qpKjBZjdx8P`czX{;+b;FOmoz-g4i-9{08LVNj$M9q_(2g zma2Luy<1_q)!f#V3nuu?u6@{P3UGrgcpm`Zb)RlpqWxQUmhDj_&sgqdVH>z$#N-jR zfyg|Lc>8p>V5~0WA9`gS9WL1wa|#zJ3YIr_N&c&-d%SJym*GyssVY9c@2u;RWdbuK zbdW_L!12JrC*+>E)&3jwW}Bp?rnKGaOmxrdF{m&)w7KQvMT{%*afvc9);p-~e|0N> zj$KuIXqgpEDTG6qM3&&kg{bDvP^+WI){cdLckrkuX?r)suH!xN3$aknGSKREt+ zpp`h6GdzT88ts!zf|+z_bE#P;%zfdhujn*hn!3}rnZ420UZAM@iip;MWsYswBYBH4 z#;8C(m;*T>vN#IbN_#i;2`b-DP+TW)(=|`2FqP%OC9qf@X~EzD!2UX^o+^1MgsV*| z)Uz%cow6j{*bV;xP5?P0kDs2eEfsX}Q?f%$)fICSEY&gYcl5#&=>jwD2PBdH*y+TO z%zR^(t6E7^$th>E2LN>j{{Vf@m6n#bTVQ%yWo-?>tW}>%sRc_tBF`uUMigYb5t2aQ zbHL>E*{YjN=xVs__SzVuhN_j!tgMhKHvVOD53({wGTU>GGtzp=d-dzAv{TC(RDW4& zsgx@;LOB9*PDi&2R{-ShK6vQb^`?^K9@SN5k!a^bSyNG21I7fz5+vV{Hm`w!!hitt z)xjMj8lhdBz-9*~``YH<8(#kJ5cP+8Y2~PRX|*&phlPaNXP6~890X7f9|3u0$NOiX z`=5I1r-DjFzusA$fKN<9%Gq!3Dgz(bZt6(v^tCX}CAxH)jigysrQ10&ppbBsT?E?TfVmSoheZEgrB4R~=0yQb>D5om|R;4KD?Z)=E zu?80XU3aOq$3CgZ~hU38K9V{pOrgp9#KJSNpG=aJxcu1ICMiK#9Zd(f3~ z)5_-G0cCBZatx9JK^e-PIwWg2Ol}z=ARcPTZ6Prts3Fu4)1^1B;RO-D8vI z7L^xkkE$wl9jsg)89f#?A!ZcP7~g6kMj1vzu13|v4=0@Pn?~BDwXM~!w5@TE(-oGv zQfOLilgYsZ72Cv-?o*CP=!;MGgI(VuN||;fhB%`3qi1Z7^7!`HE!6L)q@#~|P^v=Sv#>bKPcc%Cz8Vi}hRI;q9Sw7k1`uKTU5#5YU8$93 zv$*^d?B+x+TF@`-*7WY(R-J~TaEqhqI-Kq}8+l`n82V4Ny`6$8 z3TM|>6L6_W%#uM`UZS88lA<($@pjP+lne-E}DMPr`2^;1^$Fw|vUo|wu^ zVDecDZ;*q-Mi&F_a(Z&vJAY}?^o5{;ikc>fNi@YC8m2h}oFK=*%KQP3j+UJ@BU1kO zM&%aDPyiVS2+X*)diSm^nSN#oEx(6;&|N|$&Z7BpV7w)L+O?GD86{(p5FS3mse`tf zBSzJfuy%6xp`CGE1h%?(`#l-1DN;a4c$okcn_ zlBX!!Ry>`g4W*MMfZ(5p%~ZGF$~ftN4An?OOGp5hQB%J?PbW5vp=_4(Z~}m9jPzxF?rrMDrm3!Wi7v9#)4+>(S!(1HqXx&R zFxq_}PYWdH!+um{y}`G5(>hY3ttxK%V&Yt+qK?;eR7#4I8NqsbM3J3;P{T`?!3QhA z3JdSy;i0W+-|I0^)k^`mMWz_LC{Wxiq%%8{&$vg7@%!{qH7fu@6DViOC$-FHg~v_z z%wKtSg+0T(UBH>vqx$`A84SW9M_DQwbYq7J%YLN+PW{6i;F3>8`YT$0irS8oQ|SKN zTqH`!>e9OXW{Mj50u%3!u9;zGWcXsh5(gPk(~imbX3=*$L^TzfUXIyvXv4~4UF@=x z$SnB-9F8%Lr`@|jMOO>M4xzczqEg393{?aujxa(hh1#*MPDvRg`9CBp?3P(&`a=?i z(WR6oQc6-@`L~mw0^fLU=`QYQ+PXMx(=5K7sx*yT)|ObJ7{CO`rO(eUN$_%d9M@W+ z>wDEycbzv=r)a7vsU)OYn#5@;MKck)G&_Pq8G@E8%Y_8wC;L#kHCyaq73%KO{u%nPVThLV9J6&a{p}5jqY35dBjv94_Av2c3mXbANfCf$u zcmQB>YA7Y4U7-AkEZ>Z)t7*+Dk9ojgaHx$Ok7HA$X?;Q5-En5UOJ}p`xT@-7D-?B< z;os_H+qu6f8J03st1<8a_Z8%IL#eM_qn7JQsr!w77Yp?eM3M_#YE%cC7ztHVCc)^T z;c4O`K1sW{*#vRkP1-R7Q-vbDaRQ+inGWf8by8bLYq-Y`%PV-hz8#sS2u1hC*2;SS0*Rm7rd z(}zrMJy;XOo9ZERRoV?fezHwY+MWfWnpi868p>9Rm`_rTCe@FHL6AT(mi<5uFgmc8 zv|77P-y*Y5chFVU6+_m7lBO|Fw~{uJ@B!{^*aYNw$mxTsyThQZI+OY(lD^WnX~R6tL3j7SS(ARfKd_rJ25Y6aCPc&@nA)he@ID5#{aNl7Erht=Ka zgOkV(FaRKP%nF5@fc!r@qs!Pd1vnO3?o?gBq>Rn3#=}#p(wDN`HnIG+Mh<+^ymj{ECIT)Ko`1 zU-i0 z)3%yQdc+&g*UKAIDxcN8m@m8M2pb;+^o_jtE4fhDG<8*UTjk;xj@)NtU^fE;R1Bsu z`j7+Bz;wL8BLaFCDx(b)YKm&2*6WoPp;^zT4$)Tfo!$v%ED6axc<2o;6_j;t9VNo;J#A~O zwK7}+s=4_jZemZIXD9yv9UoWQE9A7%S}r#@DJ4A3PwK3ZM8BvPKGlv+bA{!8aCqv; zU>PxwSSCuEmCjZqsOtCQhfD5mkxV;-t*vP!ml}(OvU+-IWDiXfBU04G?N=b7I~p;u zl>2xdc<9o>_^Z`PPaQt0vDe*YMOc~INGl*>r&TxC`)IB3{^9n^Nc2Aru<3;k3@0c_(Sqn1&FpY9m*(}T9! z?$@ej0+&(SB#DY-t$Y#Xe4VCALopw+pPsU9`|!7Yv_}-x_e)hfGh!N6yivuobkE)-gk?bL|-mML#lG~px* zow1ck8+Ysm2Sim}+|;_|?e+~tsivi%6$sKxHR-A*0fsUqboTI10e(3nsv?%Q%`J~r+C{ii-waw0!aZN{m2K$Un_fis^*9# z9*xxXL`X|!-B9X^fBvruegXYG9_w!1J%-^GHk8-PbE~OrwYH|S)o2dT6ne`bWjy?m z_vmivqI;E33c+;Nv{vXDRzQ&VEdaRK5)bMJs%Mw#?hF%AXY@gsl!|pw~#&$ z6!G@6xz)FtD&eB)ZIIC&-nG;I5daw~*^%Qd_x)Hr^Vhn~Fw|Ejp8Kw}#B)4s7Lp2w ztDAp2h+0kKCxOg^&Itf@BShN!ePX#*T=h1!)At(4eX~JJNi9fGl0yk)kM$pt4@GaR zdmDLXinvu>+U|iywg$BYFO-c z$fwyHA`hjZETjUnJ373$8Ivo(GG{-x9a!7# z{qfcsf*ZBxTI#BiQj7V}dX;M^;NYlZC$nV`72U-3W^nY>Sg5!3v*sK+(-lUf? zZjuV>l*jkUOD#MuMh;ojoc#IeJ*xiz2bb6@sV?1@($H7eQrrO-9YL#Xw+nSb#hkdM zk~!T$VgOf}$idu$j+B~rzS_!;ifaa*)S9l-PjF&oN~vV2rK#MzUN%WXMu@=vkh^{l z9v^+6p80o*u7;Dp6*?~APQcXJ?UqVGOCzYqsNYPUllx8&gYa?3NX({E`^X`<55?oA zm9=RQQ!0F=1)t_v(}_59u>8jhPjWvLnp*1|TC?y`qV+A-+=Y_iXR}hpUF>2*gG^*r z9Pm;#&jfSSDo@3)YN$08eNS?~4p-eJsHTyoud7QH1zlG88zjmqY3b)Xj!xz*N&ETf z<7n0?YaWfq@d2dok{LES)mmt%9yTW#1tnbLlY^Y&9aHy7`w`q~C?f9etkQRiIxw5XHwBsO=|BBik9Ic#{@GA z@1W5{$1JEMK%nGq^#KG7jPg28TzeJxYqHlTx@S|qN!EEtGq<55*(j+W@nLx2LNsvJY&O8w-*n?jkeQAd+v{O z?Hb1X*5B#XQY}Sn)OD2k1~!(OT$$&Lt09vC9~o%JUiC-f&qmm(?-q{T*{CWfl2)jv zuMacPM>{VVihucM3UZNQ0UifOLh2@1q&J7&GlFO+hAz~<1BmlI(!`xf0nU~YkhIpa zx7@E$PbY1pxCX>f!A*Fs1J)-2hrER&;4Uyv>^eEtdT#xGqoB83Ep}^7z?Gn&wY6+C zu~J6tz)3H-yPh41W58A9md8Rh+J>Y0T^-WmWk1wc(=xOIG{TZSh`*^+agl+ZKYqB- z*zLNe%}xC-zQboQR6Qru)iI8$r*ncHZb(E5cJOnPfCw4r`8rMCq;gdfo@y#U?7~Ys ztza{aR2t!n2Rg8LU)(K~G*$D^Q&go?Fv!uYu)nfp2OGwD+I)^Ybvj!W%AJH2aejU= z>AM9#?a^JcM(F!yt~UKut7(3Qo~2-xvVwJJn;0dP`P#dNGJkJ96lx6(r1e&!14mcf z>Mg8g5mZ#f+pw+4_MtdG8Nu_%K@wjq<7T67%ao*~z7}$aZ+TtoZdwjXrs`QZ9!XDcPVKc4{qbe zNgyZ~0G_1WEt=d~jnh$Do+}^{)0m}Yh$*KcP|~v=BnSfo&hejiQym>BXvsp9v4{g; z<(k(P61O{f*;WsW>}U|2p6_7l!^r0zzB6H(kXF1@CyhD)Ti zH8c^$B$$xyF0N8Mf_BChGQmzc!sU7@(|y#_cWXWUuHDDd^3dFOQd?*%?kP(mBF!J> zvoKS>KuctWRSK%3rI#%Z51+pu-to~$+V#noge0?A5T-3~7-aVr(#*pQ5Ngii>5COT zMwYVCU+vPYo8gkK=@fWEb0Z{*S&STjG5WELoMh%pw{_|?{-BnU%WtHpmT53*SRXj$ zLu7C~`#?ONdN$VDF3GRwYhISGsi~1_o*Qjt9TvzcAyP5*h`*aU)ewN4lT=jWp&6oyW>jQKS(s@K|8y`3Q;EC!wavWOQq!u&B@ne(6Uadz zDTzqHBR(9`$A%vZDgS{RU6p9S~aN#${z^XnPrQ*b$Mt9vO3MzHrIV`nr0msc9~^*eRrU z4Y?;`7CpGzl~T&sW62Gll6oPz%U5rh{U*M*#|vy1>wgz1tRssipKJr4_QRI=0f@ zsWGnNoDA~J4h9Z-@|m$WgGAHgTE19J2_oREsT}z9U|W1H`+;z;v|OH!v$zu6X{31F ziqt7(TzF*!g<-;xxZ}opiBWEO>P6;SdO4|@k_hRUB6ex9?k>wA0Jd^BFCK6YO-)H3 zY_)9^4L-THTccH4W?N-C%&#tY0U32Nj(@q>6UG4OD(9qst=GY)^iG)7m#<19UZ&<_ zO`%7j3~*EZ~&Z>gU&ihB_d08sGbuzr1jilX~Au$#zbQo{{XS;Lup>ZYD<+o zi*&u(Xgy_kBaT+6W{eU*&IUaA89y2ObgS2WiN0vM_~5U*QqCQj46un+5P(R|`=6C)g7`$8T)UlsH%IXpQ?p;}d$Laq7FFrqW z=N(kl-l37&vRTn;4qqEfGK>wR4tDU~N&EQf)(t0er-~Gco?6M7nn#hy2q2A^8E|+h zxky$%NMnpIG1E|5sS)IkDE|OAVj%-`K~MmS#PSdl8L%?mpZ#5XoKXpu6nijMdH(=* zd-!P)C@nXN+K`lV(#)An%PfF;8{y4=Iwi`uN z>1`9$S=N@M3Fn#OVH#6~&ez=71fK)|cXFz#M@>r=Rg&&ap5Lgh8)jcd6rrU0$?6U^ z`d|l4u>fEb&Uz`*KZG3{a;gtet*T~ep%Oz41kw6a0C0WoS)UvZap2^9bmY^WlCk#I z(rGls9VoAqAgXDkZN)(++(5x#f65m)BO{K0YbDEcz5VZvaqxv@X-!Ijz%zFANq5T6 zlrAluide03);(Rw8iyX*`9(I;2M!VhaHsyA#EcyDHLL#s4Arwj_fdSKKuMU8lZO4W zKxT2|wiIKhjA)dL2)I@(ERA)bGe8`j1dxyf9)9Za8Hh;Q)=xKaDqzv6!hM# zNF$P^+0<8)e!4+s|>~mrizfi)JN@T)^w=@dM7(%B2vBv(a=S{p8o(`yPs6rDS_mxNP)4B zyM509=g(hj>Zg{NQdQPM>$vXxm31wgV4gz`c)`v==bkJjB_Y2NQ?zQP&BcHR%#|y| z0^fLXXioiV+S62%o)8FBfLvE9&j`yJVXl6H(};M*b9faWr6%PxlP&Vl(HcRGi9; zBmOOi3Tfq%mK=tc06ylu@@EDUmyX`*x@pChwuYx{G|uuxUrjYMNs<{#$`q>&#~wH! zoQ{|sw7&P_OI{L#wt9~8BMj3`M@d^xaaffH_fv;$=LZ{GI3#j>bz!BoO}_70AyamS ziOHI!Q*Gd2IeF(Y{0Hh#Pb^sf<5=bQU zx@oDWcaWAGh>nhRe%RT=1j(4DUr<|^b|U-V{I6)ycF)EP=G3xN_P1DC?Q&9x;xx6k z`6}4%CRg83lC;A)3%CV1$?`A~KNb{rnd;+)YN|Dte9=iw+k0&Ffp-jgvo%nFxD2B! z2GfJjkP>lduKHbQqN{?Iqv)zns440p06Pq>RwEH+TydPY@7Jq;YP+3oHIx4UP89DI zKKJzOL6!Fp`C0a;^1Of8b>l%VB!hlA!e8?~)K7k*Y7QKNLANu6`fmY- zzNonBEk!`_?23Y0C63nW`2#++JYq-3KGCndTcC9WGlBw2^{@~} z$l7K?-hMy&^^vQ)f2bNItE#_Hz!>_0Ds8Pwo5{x2W;;$!K|BnMbT3h(Z#L?erqwXL zx;WLOQ7**wPV5tYswo!BCz&$SkOCM-W`-Z4jUjL#0|S55x_uX{M$+gxX9ISbBuxR8}t6XIc2ZAtQw znTnSwMOJ(OK5cLm&%eyWyHR)A%S~G)+eFaWExv>$>At#(No2+VEg4waDtVg+X=RZk zAElLvVvx9B>F?T|ua}FBT?}`3X(g?iS*JAnkGmkQ=_4LW0Hp9T2U6nDI+m$of{N91 z4vMknC}WS)XXRIV$akRtm2M6*=irKmvlmKAh$hr`nrMk-^pfAGX`!ou{!gV;fJ%G} zD|yJz$4w;+8KW^%J3)?Oo!-HKW4RzUg6vBTLq(;Q`P-9GK{VR(h6<_|3msG|QArn} zxE9?AZ%uxD0y!Y5Cp`|frqkL=`(1IdUMMT+>ZUThNkL9gl$?-B*dO?jB;$;Zn6}Q= zXsXqqO6k+$VwQONgY$J6~uxz){z_OK)zpB_uaJ9+U>zcB@)> zE7_u+qCKbdMD-cQ;pGZso<7}Jrx9k1$ug5hj@2_(Tx8*x-$3Jo0pS7kH+?1D3za06 z-pNs{Qpf2$b_<+V=XU=9-g-`2J;Uw}J`ceMQhppXE%L`xd(z{$RF^{<;?yCcmP(Y| zJwTE>BvKXR`=nAx$DWuoUbI-ZMd8deR$K`wj_pXnFU z(6qPqV^Umi5$UUS_gUMXdWEN0^S8;GKb+1K+h9}AE&Gt?qf z!DSKesEMnoqDxGZETwMR4sG36CnN*EC>f~i{{V5cMb@6_b8CE5&$azYrrZ#f+^Zo# zN*|t7`1tCNQTDdFu3x08TAz}HPlJ*LvuyRALKAFP;)dSr+_Opy>bdh&n) z8-V<>PB`e8Q-zlpMs-rEM9M-m83IqHPWcV)12{d~O4i#qrgl6SZ>MNml|Qin{{Z{? zkzz`=X#}?VT4quMzoWIlJ^@m3jy~NS&r0^qHT@>Gu76&px$0I4f9F9GC>d@}-RyjR z#QnOasM9wDc`kxE_BXr+MPY(QGRpZJk>qqP&5MY68&kKIQ8J25g zP~D|bM`^6PmMgSH6H7AVfT~dLQTs2Sw^y`%ztmJQOLDE(I!eNdPEu>q)K4Bq{?eWm zX=DEY<{2M6b?fUFkxr1DDp17N-Jr_m&N@M*v+(8L4M`j|Hm=`XB~MH=63Hi~$_M=< z;BnwKGu71(;T!QAPds&!TIlGYUQCU0h|3Tj0gZAq{{Vx~o{{bq-$hidME$_+z2rCn zBDl?Snl&K(pow$)ZX^$kblKCNjLXdpR4sqDcJ|_nDUK;DQCB3QFh5Wd?(>6!SPnVp z3p25Pn|tdU)Uq~uWq;LU4m7_q*e%KWYX+J(Lr0*fl3wNMT{)?1P9uu3=_B=GLfo!Q zdst@}$ItVgyy`C4{u{MCFI`(_pGj4mGfPJ*z{=Sp00Qd7pV)kKZKu2Up>^F1RT7l9 znUAt7govf^c`7~G{{YYadb>?Eps!S|^!j?uSgNK%V5e1O4W4&lNF@C6gV2iBlJini z%P+(_i83am1q8`+0RC{qbpR`V=Jd?hzQT5Pik>RkUiQ|8mA@!g(Zx1Y6OD>?mv0>W z^aH3ZL$=Eu%A)x-;8eJ38hUz3A#jW3+4509x-#l=?uPIPVGNQT6 zo0wyk+Mu5UJq+rK>#nq^lJ8^E^Vn?k>Q*>poPdZ}{;7R5Mt}NOIoe3b1oTT+ZSMW% z@D`%aUj)%6!4lA=I0KImj-th1cuGkrYK^YvByZ{cUG8U|l987nkq+~M23I9= zcmM&0#ufz@@hO3!zyXzZ707GxWO*2<8B zfx_gHGDnPo(3354&vp73eO1);a?0lvlfun^9c%o?J z5lFe(;G6}Gfyz3a%J?T6$=#jb9WS)K zXM8k?N{cPx?Q2#$Br(-Eh!K;LpqSWTl|q-s(I}kk$-w(BJF%xbXtLGT zj?3S))q7!OxLN6UQ8+)MNYyG?Q~bOFa87f_QiZNz31R`>(KBV!&>?eig{T_plD>InLcW#H^2=-+a1jW^Vd3PWq8wXMF14%`jZG0zi7atS+Gm2!6;IuyhtOfGE!qOG1O z(q%~{SrA+>1nGq28NlVCV1L7}U@7m^%Y4>)K6qoHoDG)5V zm>~C<+?}ouxj|w_lauGBy_(Yn=HG3(cH34xnvDUDwi}f<){0}hZW&fVP7Xo#g3s^( z=!*(MoT!b%^BJgJJxC~;gsg(Rp+QkJAXS-otT{V`&XS}7xm3P!hMM+CK z2+kFob0`Wku!g$(p#j4BDEEgZLF=3V+CU^h~p=a+ZT5@$j<@& z&hBvS#m=Ko&qbx{B$6PXS4VDpOp>yZyVRyh3!3{HQroT3TBU$a5~X0J3Jj~U)F$j9Xw)`U zfDGFtD)w450w_fW!jBfW!h-h|Rr+yPDB!qoIn$ zVUC3aN+Xh)nZYarF2-ggkhtMSK)~Ccrr_8J9NCCBnzfAFVo6Scn^Blz+qlM(yJ^~e zMW*eaN26?Y)YR-|G?nw01&cXz3LT3iDp~f2ISaT900|kFPT2IfrlU(}${J}LRntOr z+^N7k7^MK^dCuj1xbvR5X+G;ySKQ{dU8y6bxYkEK+JuOesn8cHH>&^;voSzVk@63? z-prMHk_WTY&r4Nhp>&Q2E$$E%{pThffM3|WqFIUYG3DLcj6 zN{Z%5wDdNbnz$X&#Mpq$RC=5CujP`NAmxrYTy({oR%HxC1dZ-r^mZ=`Pr4YpXOoC-_tCNw8kTdN7jxY~K zm#giv=91Mty5n6>6+fX#92GRtqW~CCM4oD%6N`sTY z#&M7iIv%vxZ#t%eG}E^4Miz3DS3GLatKc)FX9bvp{!#wj73j?$ru2>N>h%?UY_+ff zBy^KIdXkX260AUl7$jqPBcDAG3_!SeG^&}BNEpggG9a-V$yR`eu)9MSPu#UV^5+Z5 zBh(ojJYt~XSxWP^GB*_m=fLESjjOv9y1KC4E|eyD>f{Y9vQ|j~sM-h!fk-&P@(CpH zc+W{o&*C^~dz6vdEHzg5rbdohtDpv?6OxR}f)^}7InNvb++rNxjtx$4LYqwK$Eu_Z zi4sl!06JVL3mS#UQI%(?Ww4H~WXPjbvYPO@w|BF6l0BPR9)s}3rlN*8 z62%LdDVCaI_bRD>Em*Nl!GjP*AIN|peRI9B71z1_bS-J`Wq zvQb}a?ras@5kmf&zrZVjj&Yxphd(2w39CuMW2caxhZirlKqG;%-I83yn(o7P%8H(* z6{nt-S)q1~DOdpnnI7nwl&bS9uFwb|{qcYuZ)nl_a3$AETK{@h#Xr zSsb>Mv)``RU#dlp-m!*Oz!AvcmfE-*bDhKvbKugZ%|g52o}x4MjV{iplvPNiO15PR z^<(ABfm3L{>i1I7sjoD80@+JpH4)U^E7xkt4X$h_t5XgG7Y<3nkj1xgf-}?q08aiU zbY*sqq^#7F+9_`>=tt|++^SaC>$O9%ng}G4CduJsOatYy$xmu;#0TN%C1owIvv+Fj z2d1csTKMQ`rUgp5o6|!p9A!L`0c;*lO#4yaZpmwROC5i0wC#qvibRHbNfhN-KAG|W!#5+Vi^hzY$VIBU6B){|hy~-Ejii9T z$^QViL04|p_Tr|b)#bjX5McM0X7CE*}PfaU&&PwDTOn+$m{{Z8r zEnb|~ns#@kv1+|RTF(=EE3OjME=W6ucKTqG_BlN)Zx^q`1%lw>-B)@D>@aAl)xacw z?$>c14mSWv2adHRAxIxkF~!wYqN;cbl&M%wdL7}zKU?bp(e}?<(ZXb;?Zy1ct`$RC zB$$4{pPomae2zM4cH^*mgIA+}jdZS|u|gF}ys<+{&Q3k44$bH9mIJ}hob(}d@9*MA zM$|2#YZ}i{BxXi88!Ssy0T}-P+Ae?U{qxkz4xs#0cPcoaP3o5bX!D!jcy34yan^dHM z={*|FDx88pOhm67XZYv_^>6$ynB}IR?hd!C8z1u{wno87Qg{+9}pzxtH4^_M!p1Ts|IY9y(V)mY@DY(fE$NK!%q`P+~H5xr<0 z;6+OvSNIA28E2@DGaml{a8)uN)Y!;X zB)8b~OiKlTfv!}grxKRWyo>(c?FBI20XY@SCCjPFrZ|PdNA#uVr~`)ma|n? zM{2m#$N7+om08-Dk+6{XR>2(Y{ec}g>{iM2wH)$Q-mUbp7WE^ZT7@yXs_?1+-ph>p zBS{Y}?gyTsQ&N!mn9mzNbxhTQ=$3w^IC9rH{UfUpq)=Nsac%9r4XXRBYppX^R!;HR zX;IijqAa%gvF>hKH3*|~4DIm5Sm_VNr)xAsZKG=Ayj5OqRqWS0f<;P&CE5TC!O509 z3@n%&5C{XFl>Y#T&hd-&6%~t1>Uv1xM36jdMg?6yS;oYfS&j;^AY=vK&%2JY;iLYS zEOe_Xtvy2eNM=&IcD^!s$Safk5$EHq<;ycjGw=3}j*hZc&RLhTSt$XWhbk=Oy~ify zW-t0_RXT_tP{ZoXGqhE7N)-WS!WKv5h#_ff1@t++unlvalaRL7TnO2#Jl%*v~R!wgkhU1yu zk*=ZClvYNjSf2N9{`4>;3+>M*^5Tu1N5Bn%jQKqDuDoem9nPdv)x!-_ZuFK(a00F{ z6;6GDe)!1GJtK7#UB9{ql8?WX5k!&~j)K>sD(PX}k@_Ar$rv6_M;dO|ReN=}*sCs= zKUGvLm2-q}0*3V+iCuv6$!~+vq)K2&R$Lk~w(U{JEG|wWtbin+PB{-4GXDVZ^QSc( z7pX^GW+6*%Fx47~Vke!r3j7{3j1GK`hPB6QESe?{>9;$jJ<_ddntDoT<&W!P#t!6C zcSehnee0ZfUOKJU8eh9QYResFnY7$)vpAMEsI$~ZJalXr?rB{)-^!>82aZo2Akxw= z7y5fm+TD4y%OmZokgHErW%A{?2b2D9A1A9~Rx2sTFT5scBu%F#WjU0f7&!CMC2wmr zJ@UxZw%c`WO@@L=Y95Y;0IM^3V=RTsxfsB}7#JL6=2a|(=P%D7~ruv1-{wHUn8q3-9fFbR>~XB zjn{P(*ec>JEevJhk#J5{W{iP3;0^n-aseM5Mz~Yb_T=1b7Hh7mw_PgU86u84_ok*- zbyBi7v@4Ya58UIx=yGdrpwpU;OAX%FJ@KWII;x5hR%se$BX;F%ZDPYXZwKR^s$H2; zr6ty-c1H?akd-iTY+2YdhB$`kth-+Up4aE9AL{{WXp z%#q0&#`}Z@H2(mYa&ftiTh2V;g{5?@zWEoWM{~7l*Ag-CmQoMnlVYlqrS zLWKj*j)HEQ`nyC=6@3qHZBxNnF8J<^1w+#>ylnzW1159E262v0M>U%)VjM) zwRGe_C@U-HplKL@7t%E_My@vkLnAQ31ZR%8YF^CfZp&Nar|%8#PuZz%zO2#3c7%u} zaKJJo@Ic|E9`9*w=OKU{QB@KW8yNA1sdhHgn=`bVN<(e{20TFl#~|_7PWR5`A>FGx zX*Fp`*<-4vi_&Ba2r-c97;q7o1M&$RaC$sTg*K~_uKxgPqicutI*Uwy({D8K)5T9k z5GyLdEjcn4WpgO?Lxp6)0c0gs);Mf8)rosKdDHremX0Nl`gJ|P%UR<-qCrvV&cN_J zh;ffSc{fXjl&ylY;2HBWUdnnKQAk zm-=t2UGxRcekHfTW~OR=Nn3N%G&ef?705^=o|@@BEiGM4DJa<`E3-)jMI@zgOf9(? zLb9nK8k*f7CH@PY4PJw{Ee%AnNk)Mt_Rq9{8JTxSgS3(k4gljka=q(~L3*Ct_MLBX z(=`iFwG5PW(AUNj`~LtXKN@VldvehA7G&sTI>>%MT^%~{l4y4935B+30gq)nw} z+HwM`;#foEAJ$u(^NxU5I!I+D#{U5A4~lJ%wk8RZr{PomIbV3h#By@{!~p3Y*S@dR zi$|<)mg_xi4JeS;x%8?dMg$-T%Ywk;kGDJ?s=KqJMw7n59fFS8Sx&I9lA@HIs^i-i|-bo_=Bf-~wkRUCXi-a0+g_rBg5Xr-X+ zweT477uLAxn)r~PC0#DzNIaj>(0+J1#0VU|@PGM2!?K!#h3|;ubi-k6{{U>U1X?Rt zYI)>qW}U9ASaIsK6cS7cKX$-e4hVy#rlO3t`o6H8H}t&kRGh zQ}4(fc8l=8so`T?s&$>V+f194{*K*Wb&#;*+v&up!}l%+A3bZ?yX)F27+q)X<(5c- z<&{+sfsYHfb1^^1T&x}R(K>>uZMr7;OLD21zbXMy4KOTl6qzOfDLfAZ@OrveF@I7% z-l79GMJ+f56$u(>uK<&O7mP!B?S1B=RiN$mnb%j6+%-*ohv{ROuLYw>+EK9lZhZav zzSEzEPWo2FYVH~Wdt4Yf`r61TvPs7ZSdquc1O0ma2BW#^nW+{##1vGIHtoeOFER{x zD$5e1{{Xljx$3U-9l@bUU|NcTr)X%AnN;8(?HKTUe%(%Gu5a;9ZE|a$BC666=<)k{?2j+gBApKAIMH zX)rT|BLH$Sjt~2EysXm;r>)ghp0xFVG-y@fPykW_lprgB4m`2OK69R`E}F{irLB@w zTIwK0nOUl?B>J*3IV`ce<}|}_NGH#bF_9W%Ok*$1!-(5eTR0I~Kn&*!L+0DqzZ-8m z+BLQcOWUix4HQWD-wj1Pl3-_*Y`FvHgOGmRPi+CC>LCK>XSPoozQV6X62^|$DZv1H za7Y~T!1a~yUD3Q;J(oRKbCc65!_btsq1yrCMoB=>Kd1Z7}P-| z&`1&%7|R6Q6u&t4d=t-(iSjcD{{Xn&`a(+h&WMszL{?7wKR^^COy2ur*}({kEe~K> zWdcZjKpB7xaN9$222L~npyQ?1to%RrqTxqXEyBl7Pb$YPBnd%R8hT;C#&>5YaQRb= zgu;FEgzGlsqu!ZDnN6rHL;nD!574!05b!gQCz7b!Q+lcJ${8$YCSV8)A~J2QME^*B}OKK7Apb(OBN#t2t0F>jyg$q z5AkcF`+aqSk{va7yd$K|O)L=zB4VhxRWe{UODdNDfyu$=3QSp?AG2ua=#%~(GqU-R zx3CNe3>?^A!uy9%*zSX~{oc~`lvlX4o};qYM(r}pC@L0OR7VTF{V-WUIobeX!C2!M z&a3R_e;nA3jqe`Ws-fRR(=EQD7m2vcbH^lTvH`df$mDy080C5wyZ$-os%;akqd;D4 z^<;5VUs4)4-H$s~BpbMG-`mH4NgaC9ABry4I?r>~G*wrcg^SdXL11R|8U2cd@)5S8 zsP}*rleGMGX(1R6F9*Ldlvij@tuvF@q>}B63BmzCO1`HTY@1L)4l0Lqb!D=up4Vj3 zcbl9pMNGE}-(IFD=>a>XD-hnOM*y9u2mu+r$Ep*qd)?j0t0l18uN9Vh1`s7hToBXS z(Q??Ae5^M}K~O>Uh#ms)Ivdl~=_^-Ub%vsrDoRyJ;G|I$G;+(2Y_1d^S(nPImIIT= zQL4L%K}}Hv`cqNfwFP`|?3%YxnB}90hR!KzSOF#n{_#Y7k+J;sb)_KUC67mpG*76V zjzUsY01in}L%ZtFW^m(H2JL@U2XcFQ647)`D_eZ5jJ+ZCCb~~hu5gP@N}(y1Gy^79 z${(EI0wt;E?wmI|eKp)GomEV3&JQI0iKF!dt2#OwxsSYj`69hs!K`eJiw233cR~W z3Y_FFc8`)z9e4t~z5ZZErd

*aAzo5DJQbyIcf>yD?W50D&It+Oth)eLr%r=(WOTr76=S(n$~!Zu?YewVBygKe_L+m!RROh@IsG9J7{QrqY*5 z?D~@0Qtb-*m2?}-lN0`R-Ndo-4&)_qjs`K*rH44qoRFz&SjeBL`^53E1BLQnS<95I zRkEtI)5&QxjTDPKqtub*i*o{ZI62$L$v#GR05Q*1>U3*lG&)M&zD!=-wo*k#^)5ks315w=I<*2*3j#;&&6CFfc1yV7gsl zN#?4S7>lo@8AwfsCncNOJbV&K>&lp{gjR=UOHmUhOaO5Nw-!D;GCIQsrtd`!eE$H} zbyKZ1W7E|irGe<BahL;Z<^9E0Rbc^XI1SlwHw2g0CvgLjy^$XOB>N zqudTg_@v3_+l((rO+BNnm+db_sP4DeDQ!}imSG^miMJIZLGbz7R|FoI52tO~T8?{N zzLHY&(*r%l{nN=#%a&-dl8O%{`D_kIZ2T_oyJu+8nxC;fg6<98YPx!lLP(<}=BA!d+s6b*3}2@t0Na@7Iq{x@fBYTkXv~$A z8rrhDfT;yrjsPCZ2pzi1izHyP+MgI8Oxy0()^X-z|XigcBlda7um3W^CQY=Ev% zTROJZ7@MQl%(bf3>=L(Z*d9w{{X{|t-REt>FU~Rm!%3NC1f=ogmInRnG#%X+6M}o zLC-np%R~Mgbyl0Jn&)x2t-`VL&_!8Q9C4{r`kVnF-gqnS+PKC6=#sy@I)dCqrrl+U z-_&TKo}s-R#1IC?-ZrQkSTP4Xx$5H2-Kttjx{6D+`|75OVjbueh-olK8=?xagPwO2 z#&gxC8m4duo4~1&@ab1(xr@(SAt2_##w&9^Vb)#5{4MHDX(-C1)okn1i^VJuWBw8FwPwcbnZTf^TFZlAqIgWd6{*$SioyRB`7ctx0_D!L!vx zUrZLMc|MR)ER5T*cA!27l5@vF7ykgnRq9EkTKZ`%jexY(G?eR59PS~HxS{dmZU9e1D>8)Z!e-<=;Ml_6s%DIrr!^vEG!(eD`a9aNl@5a z)oZG?#PrJ?71UtMQB-P-ZVvJU{+h&0YB*LIZ1CA$dNP{Vr*A7&X|yG_35jVGS2Qsg zSg;$@e5TMQ})3k9>R@O9E3V5SLmI^5SNRm_RE$)f%?aox94+IwS{Fhw)tj) z*IQKEV|u#9rl*+lHlsR-*^kCuet6@dB{Sp#z$>5QDAJ!-vv$b}Qr98#!Yp|yBNH5n zJ>059uT)4kO%6u(ak5VFTr10oguLvR5XXyoopt&a?>7pY2-B3b zbk+%d7od&@Px)ter|E-~rj3|8X912&?`G*sO{wZ7zKiWOeO+zICEYtzPx-O~w=T>w zallZ#;3|XAm$TAT>QosY8BP-LNt2KT$;^?+u+l$Cba!$Fy|&L$+z#GV=-*j#DehCp zIVJ_v$H^Y34fCIz z-(l}=rLMdG07#&>(X|kd7Z~al0rXdvEZ_~JfH}$k0A8)Tg!e6Zr(^#B2~(xWOzKL< zQXnhPduyP zm;0S#JbtGc$BwLYe`oubZM)Rp?wTg&MNLl|?x_mfd|)o<$sRM2;~CFal&?LmMWnYZ zkiJ|9P=I?ibl7td_h??d!^bf~psitH&Afz$eB5>TROUVT}@sn@&t5oClevr7Rr_ z9E{1F4*=uO9C6iq5(6YRoD6L;728z>ES4k-4R@1n0A3aJdY4^F0Tq&6UZEl?wGj%3 zL!W71ZvcSYfA#2|)!eH+qPCiA)`6qBQ^6!N3Od&Mb|rDTJ*7_sgYNKi@#CV2^z5{e z#Yr{3x(0YAju&uY48R3#r4DczRDd`P4l$F^J+rqplgT_6n%O>}F7Sp{A)T9)EZGbQ zAE`+>J_pTk%|=EXMP|>QO6MRW3lP>P!K@f*uCEWP2jU0t;dHo0YrhY6`&6;)-|Nsk zt{;L*%S55P<7xDKdCBR^Z|?s9XZ1y$^yY-W>CGjo--AJWiMA)Wl=RxFBoCel%AKbe z1def?W{V7)A*r4kcmZ^!^&X-xAQD8NGGzY%l;ocn>e|gU%_-usO?94nMP`~98oj52 zHu3JKsg1Vro>9g#o`5Nph&qpnK1Maa<~d!GiV&A#K9G_J`jP=19`UcNdY&mV&*N30Nlx!C_W*R1Q;VE)Y+1u%;q^j(tt4iHra}903 z`kftZLn$0d0j2jCkKKxlah!}C;&leB(6PZ$ODkz>i4n^UTm`7AU06O#5(1QdS0ATx zr`tVWXlu=Hb|;#HyEPOQ*BflHttDkui|SyVcMKCW1~W}C_J-Q=2HJ8Pt4yXPPL&Wi zegsa>+mg(oDtw@|0^RTlBZ`#S9uIwERMB>;O;~F8>0K!$Bu$wXXuVe>BgSPU51ul5 z=Y7$)=_d`NyMb;O>T~O@zTxSjOOJ*zd+VbP)yVW!(?vr) z!rxCe(p4st({h4EKElIeGO!1Zf^PLchRd#&U2#FEbTzuwOBz!(%8570M6$LcSt5^o zuqh`QXm;*CS0I&6n(jujqqYl(?=9ZnR~Tqwv)o#Gp*jyRlp&y)Fd%Jk6!V;PA$6kd zRpPcftA}MYir(gSVJ)s(jS4Z`Np^}llM1o&RZ&+Q00%<(TA6D7GCe$?%lM0BRLj9s zi#DvHdZ#RlyKjaSZMU#GnzEJ~Z{p)bQ_m4HLb6RXM?MPgV5&a<039=I{{V;jElqA) zwwKn{&0|d(dO~|N@zg?~c}8S+$0VM4&&Nt@O_#KN#({LsyV2`pj3W9=rYq~&9R}Pf{Rv3YLnX~HB5ihDQeUYa*;9+{{YJ`A021@-^Je!+iE+M z_bqqbokOG0OB@yYYU^8HM^XnXQ8bj2&rwH$KS`&_-R=vFbjGuOJ-ZjDbtPTG>7_f3 z1yY#lrZ)XeS8%sZm;kfU+~$RnMH~#rpRvwQL785>O)&t*yp4rQB#)DxsbHHB-b7ir zIDs42Z@UeyD{3k49j3WktJ#FoPh+i)*Xc3NBv^JB_YQIbkN4_TtFYSg!4pN>Z^UN5 zvQSIzSZiIXsfiiEWmgKzfH?|Jo;d16-~Ry6KHp#G{{Zv8$6W%vge;U-I$2%D;zR6` zC1TrfcJ4nTKRp;(zYxEJ+WM*so9lYJW87l(r4@5a2%(pT*-->#ineoyj5-tHx|L0* zRmw1*7_5p)*?rMGOdLiZ5XJh!vi+t1023O1ma^khQ=&VKOHT12jw?MiMOjP|Tq&o5 zV4x3q55Xhvp0BLhw@B1UEe~_;6&5i-iCbSyOKQ5??od)NDkWQeLsSW)-N1B^E^~vP zI?{SKS$2oGNmjkO)V3W&C@UkQbcW)xN?FEY+NDIEn=xRmgO&^jJavChZLg!Nfm$(b ztSV^WU##?531WQ_kff8hAxE4O)db~#0?6iJg=~6x=k?{Kf71SK_l)gq?1ygqk*4I; z-Ky>+w+b1 z10bA{&t5feVJtev!zIRsma6L|PRfc)jV1aDs(O`Ul2M=X%Hx~{9(ra?N>+bWK%O8* zF46dT5<&SXMLY14{Q!Rp2tZd~V!K0|ja|C3dGg5`&Zo%ScLxC%z~lM(P<{51CZc5OTK@?9a2 zD^Bt?!_m%tyTK{Q1mQtF4pn|1^~D_G5p??DApz=ZA{6^c8;_`5ex3mEPma6T`#q<$ zz2VzgnIo6d`jTz5P)`(uf}xFBZN*PGRZ)zb^+gFPzCMCIE@sw}45LgOAF5-J{M-lJ z*>2Vi^oIr-am zwvuvCiiIs8T-f)E(;% z+XZ>5`m0=ay1M9+Ii!X{p`AuB;n>HY7zA<$&q8+#jk`;B{#tt5g-c5uIAYS3V(vEv z$QePNKtAp<(#YJ+%AKwkRl2Mw;!zZcL{&|kZ2_Ahd~=NFp-80@6zMDh@$`mk_Was( z1WskCLC7r0CGDvNi&*~vNYwuTP-q02&n;z_Oqgn=3ZzJ@5ZRHJA}~?Oz{l;A(oXHB zwVam^PV1!UB&4MwQU$5myWNFhz-Bz>`2(jVCwLmkDq8K7vd0WS5{*>;ggb#dcR6E` zzy-E}kAc<9z4?i0{+{J+w%i$rMif=qEC}a2!A^XBpbkHdjrz#$vyOk>5Hj{W<#wia z1vUGmhu~9$zZT1MXPNEwl(f*&jSIe_Cjs6{#*s&#XQnpn&JU7L+s|EfO$APmqomY& zv$K~4rCEteic5f{KDVWSsu_1N%VP@J9^=6SF59-M(wc$;b++jnjCI!O1IJYz6l{@6 zw+klqVvYycgP)FpZn9f%^p^-;v8v~`&R|rms4_GJWy-@J0ok34G@1Fp9#2FLp}WEn zm}F8+d_rIsu~-ep`RBp&cOnyYUF@}K>S$>@d!y3S*cCOi+)*Z(>IvaSLGOx zmOmW;-n)~lwLG9*^%V2(b0ag1L-i^eIB{NFk_!OTI&?a7bgj)BvVa{-MX54OH`xF&KKAZS3l)sgQs}>OkXc z>`QKiwN$kixz?iPbg7&sLo}|O+#v@sI$jHl0?$d)H>vB z+~z}-ExQLKvazBoiO0XE*sHXYAnP89sWbVFYg^LWI`^l}eTplr9bC}M&7Yiu-y~EnxG3Uq} zjCCDma;A#i6pI^`4YZQ-k@Bnm0PT*8ty&9G-(_k7*85%?WR-Ur#P0|3~d?71g(RS*)OjWl^X@0&(REgT>>nl4rOg{idp-()0$v7O_&};2) zVWLd|psbfjEAu4ktlw5)i+?W~$PU0%9Lpxv3(8;|^`!3Ch^(=+3)zTn)O79YQ`OVn zY1WEz5CbMhK6CkCHZsR9Nj+!o>{CyxYAPO%mfxoBlhq$yo=K6Wt7HHPA0$a4Cn0ja z265x2LlpM|9+!@3H725&YNk1|ykK7*WR?Qu*wziH21T?n_*{Xh;nTmp5s!YgLhb&1^ z$-vGBN1eXyUudjGj{B}@HP!i~l)P;omT#mHjk_5^mnD7KB%wTUfzhnf>CHtDvFJP0 zbV*H_CX%Qarj}11qaWI;6e}K70C#N%s-P2@_4WKgYXI3mCco>X(@O?QA8{cd4oCsV zHH<>wWQ2$o$!ZeT z+wJrhN$HHwPe&ukQA(KMS{WRPQ~~7(Va_w>pnWT;^){c>-`1?62d5O$#w3H&k);wZ zxXsQ^7!axk7Yg3&oK?~5D0L){J+i(Trkm7eGe}KRxIdV9R`7mvjz&J+6d=1jqXK0| zYJ)JOsammMm%kg9E*LNx*_Jx0j+;+iUNna736Yj1TADK&Q~-18CQdf+F@k(`HtBte ztlDk0_h_YtiXa`f1pz_df^n8_jQQjE=;KdNTdx(>Hi;_e6Y3>MJ=FgITLFlT?D%FU za`S?T`w~2KTT9r@0YwwsDDITE)>vApkEkib6*(#C;0^flxyS^3@zqSWHZiL%sSB12 zz)FxbDRSdBH@64-LYj+SwMDvX6&*SLnN==kr7=6k6>?F+sZtmmk>qoej(WJN*LAm= zHL*i;qNS#zh|g75S{Op;k@B&EOYUYWfGHc!J)jap62sfsjq0LJKNg;&r>a=WtkpZ7 z1PDoJ$~F$%wn^tF@6@Z;ZZ!^`wot0oL4BfjXBDqwj#W~J84`P#V5rC+AP#;m1|I9) z7CoBKXUmn`8j|-poU?Wp;7kgC>4f%+L^|hM+u52ZV5nqgSg5H4if|9A-lWpt@~Scp z?hZNu)Y|5{?@dpqtTk0jM+sm#*%y$0eBk8x;QVrWZ0p#xex%bdQcEVpSrr4g({D?6vsGH4md#ZiHB~8w zL|G?%3PM!iqX0`WBaqvPewCdGtL7Mw%DW6Y;t!<%oI60M%?Ea{rXxBH))oJifJy5 zXo-NDIO?h&>uTIDGDHvpgFFSnBoX}eO)Qs6Yk|JfUuTlFNZBIEhL;>1xIXsBpFg?k zM_Jezd<-RlO}b)LP+K_B6Lxqn*hI;O-#fBN%g! z8-DQR?n+x_+j^xHu)!wLPVA%BbvVg&ebDiY;Rqan7ol1@i`C|kUn7zz%q%4SqgKHN z;mM5vQdP6hx7q;l)g@l8uwCSmj;l@3nCYV34Sg(6O)QOqGF{6S8`$J91pI9rbpmFN z(OPYsCamK~Rvg;*=1-9>&urnXq<;|>D_ro<>Mq&S*HhHK*nsqEol_(d31r*ko_ydC zdS=+a5Wk1o84@27lNKTgxm$r z8L5-gT<&uzO@t13$Uph$PWz{{4xZgjS#!J1TM`1$!RSc?Hb3{MDTk^I9l{Y$+Qwa?dKP5GnXVjN6}& z9!7a1$4i}Iq7tKZJT+}ByMc`oHb#gavg6u5GTwiTbd9!M^=+akYb;@DVO`FP9Za8? zJnwYg1~JA@pRwxp)!km<3!eHZ>FuUbws)2XXe2ow^B+IA9D&hNu#m;$E~{kEnKQMN zQW?vJ4Z(x@o{?4kBKsbyGyEwachd9 z+8K+*EV8`P7f=^2&M*TV#gl0VfI!B2JFnGOyTz=Bg4ubfsDDaIswq||VPbzuZ(^kU z@;}?DR%-tM!}gI_DLav)^&~e&U#KxL6MCbar4}_AUKnyy0Dz^3Ra$Td8AH@lSF{!r zJX{7~Y+Euw7`#P?i4xPzcGaSq$E0YP>*f+dw2{KHG+~ej2#$X_2?umc8yFi$MRk3O z)RDslMyIKwrgNAfilU&hZJAIw{mWz|j}`1x1&%^>nlpMwLYH zr2(bi5CE0oR7i68!Q4n5apf_ntyiiGi_aBPRm9T6O-(AuvaF%h>{7g`0Qks1>C}Ko z3X26wMCDVU;ZS_-n{p>Vz3%tbeW{)_cxh|aSqL71x5(1B9!t?Aa?Anyt167~)eQG) zjXAcGEkSHrMK1)D6pK?RARMrGY8*5q_$5eeV14?jtA?2?t1Oz2Q5a%H%yH91Y9g3` z$8Hpjp5O;6K+bsTJr<$6SYroI!389g?6S39*ync0#@(_80ZAwHfHREsRZIyz(GN(M zGGwd@Ax_#bxXsJIKv#1Mjm z%Y}sijfo_jdE?`%xh99}+TjhlS_GCc8HC6p4IsuyL$@H~Bm!_r=qn_{vJ*62VT)&` zV%K8jv;Cm2`UNekP10VLTKH;xjM7rn5Dct+)bFqsO~c$9IOmXhzti`gBaCGFjG50rCq6n|Ug4^tbeUyVmPA|)vj;*4Br#BKWnn;8;u59s*l-EI8`3zW^yC}P5?r=Bf%Xe z^_O_HmZFvEXs(r(s(EJr0QpU}6?j>{xRHQ!$Ct<$`*l5<0d%+7AA^tH9%r=uguX_%Ye#aZEY8X4{X5PTlzi`2#ERb>xad1p>CWBgD{PWzjX`SE z^%V+H%$F3CIU@toh?x(JXVeGoFmcjYE#mz=7ZxvB9XSk?3W~;U^r2LJfw&CIq92V&^jx%v`wjX z4Se^j)f7!Bqk>OOX(dseyP4F;tX&4v6R6KQ9$xU4H1N`m3kZ#iO)#b~PfZ&Ek-g;F z5cu~lRCAN$^ttXo;#apidaZPy^dqUKjW<<6YpzC)6OsC=w(R2rA}?ScC#4NSrVx~- z+OZz}=N%-yg-Nr}snaJ6P2I$mFgw|F_{Z^$J?mu z_~4+mID21asOE{JaOy55Sz`(YPF5hK5zbC}yte)}G`;erC~7-pd;)aIF-;VR6mm|% zjHt&r`*jPMRHZUeZjpLQ9f2xXnNd8cV(!aG&F^B~Tj7}%=eJ+f-qk&ts^NWtBA&nW z=@L2FPrGC1XC(N}Gr%OCfUmRmpHfEl+oep}PM%j~nUyDl)OT!^G63n8RK{2hz+qVU z=+=w7-La^Ob7-ukjufS7Dk}{tK}|y;&eKyYq#!xK5#*9_$4R|O+`S2^WT3Is_eZsw z;VLDODrxm5o~qN8&;yK`YU^=moD=@d@^X!s+S_NPjYOP3)z?v zY}ELlh<+eNb${XOLh5_#9@zIBPVXPp#aT5FdJj!N^%T~iB`Q^40vQn)^S5`Nl=_C# zdC-^Y%`to*MN>%=7#>DL9I>2imM%zOKqM2AGsqn`wO3`kO>L-;QTF3h-L(DXB1uJ6 zU1GEXR`#O$j1Z{gfcYLqSDuDxJEdv8R70b?x25eDa;+sd^_k`^6d*UW$YF@ObH?Q( zo_QTTB+LQHevE$6$n6;vYgm*L5_hFolgs#xw7Id!XA7$p>XzA6ZK_Mn($z@ytMwI7 zRZ6~`WDr>$Fyt#Tu_GDr&rQo0cB$dhv=n{To`+Xkt}_a1s>m+$)XPfvC0o*P6-y>C z1_;620Ua#wS}u~S)~2!Fv~$u+G>K0g9-tXT2RZ!hq$%^0&T>9_3aZfZ&|U>CU1cSB zh~X&XY$q8Eq zJY^k@4&40x`Vx{z?-G1J5Y0Z;N@oEkP)`sR05;48vvM68H04%`wAAWKU3uD@hhc2V zB!)|U9sdALPYjgYXJgMxRLPUZN}>UPLW9v4bFDqG?me-eyPc=F-!4)5k+tVSQ~I$i zn>n76M5z)G3H>s}+>TU(j6xb#HA; zM6#pPdd(PtcN$uC*w2g-z!Jj;;Dh6(O$rs>1c=^G#gi!0fl`u47hpyBf&)F;-cf6E z{32D=$5lRp)f$H7T9|0;`h!N?thXZ(%cumaMN`O5VUvT&QO8j9S9^Q+_<7V(p5of| zedgIs5~OyGOK`ZuNf1!mB!%WyMUgjUUj>kXf(AjkrmEFCUXYD9sIK%Tp_H(rsVbo3 zhmry|6!Vf4Fzdy?x&8IDiQ31k?@PVCNmiP;qA(#_!?xSLy1?Jy&JbmUyb+c&^q+=wZ84T>V55nVO=UEBcDL!=FjV zI4p73uGshI@O9YHh!{?C~J?$yfEKz_Es|wbt-y`D2S#YpW$YBDtPq zxQSL3UzUzBl|19XK78Y#dOq=KT}xEdw&$Q~V-q|=Yot&!pd;P{oSnp+bDR$YtmkOf z+Dley>Ub_y6t*cQlm?E0o#{Z%9f(!IP?A;kI>N^XYbaUq)L*IP5BEC47ooj;?CCEwX>Fq2?=Ymh{T|BY(dlueL*bd z=UD#KGEh^|$0Rke)5$3UI+I;OfShF*ktoJ8IQ_qVrrovnkE*vK>K#R;9^|TD)Bdvs zQH-eyu6-sW0Aq$D`ROOEzX^JBs^BDs?OAG9ZO-*k%Q7h>?m2M#ehz&@JsW=G}Z$$e`J-SVIfKkQc~jD*zvJ( zoIC0(wCB~l_4&C8E2upb2ag2|@OU+?+Z?9T92`QA&%ekkl zr#T~FJ8;Z0GIRc&T|fAm)kd^B4j60g@x~*lM{V^^gCG@mU_r=S`iJL_w@M4Px74&( zDXH(By}!dGQaVXHRy55U9}JP7U_W9>QJiC^f$Yl@@4`5PDrV79nJppib4b;)D!Dq4 z4x503pQyOqWP%Eb3zj=)j;SM>BE%1TZu}G4KKqNcj1n25E*JnB2ai`?rW>H4knt78_ND>+ zw8<=U$;)J9{qfTvGlzbD?HL7WpM*?95*^Utw-WX|Pb=jD`j@fvaJ2K>s1`-0m+4JJ zPqsbW5xWO?%CPgsbI9wJ!|RSG6>^ijJ<87#$F`PvRJM>zX@bY*VU8XBVIJ)+m#R;iXq{dj2U>tmj1 z)Al27<^BQ24nXO7sYwlj{bOE^v1nPtRG<Zvp6Zlcj0sqn1oB57 z&kTbkkk0qqD7` zEkqY;&&#@06;ev>3JF~981(Qtz#R0V)4j4cJ9PJYok-NrK~ME>1vD~9{@(&O7*=LD zHFs$>Y0;ar?%0o9<4?)5=kQk@^Sjq zH^^@}>gwUz3Y}GIlDALT^yZc*(lRPn)>XF+wb`2icN`FQjiiI|&=t$HH~lL`P}f4! z(<=It&qqxwLPHQsdWUJ-jiHzT2IJ2?LR1!5E7Br#kC&9GlOzk9DVBPRxbpnjTWI@D zWmze0x`NvkZ1FNtm8XAL90P*GBn+0$;uzp`34XIx-{5<3tuow_O{T7%v=ntG*o=}O zZM=LHIXOAcLUMYRnwcbuo~AFSkO2!J4G!-F@!<2uN0HG+pVU`)>IKH#X{M+rA>>F< z2*CL*l1DfL9Fd%3JsBhr#sg6$a?~m%HMj52Q^z7(oJF$eoBo_L-mJQ|s-CqNs!WmM z9QiB>JRGCpht4@2JUb6)yK9X-f41E##mcsvMJ*B=okg%D>Q72U z{&@g_e?$iVV_xHux5aHG2gqn$Q=Kr`7{vcX4OF^M zP}A!wdg|(8f8#4+sizMceo&8k1G9GCSD)XGkQ&AduAaWt+l7KUNaLtVLX{JVWS4RR zSw;cpKjjz)!SmMG*Sfl<*{2uK($P>-8ffYa*P66xqH?2h8f~oX`QV)6jGPX7$q`xX zTH5t<6*^%(cAiO6qKF6{X2-BeBQQWY$v>+Aan&TuRMfs+a9XrWpsK>6RvP`**4dPU z&716^ma?U!m1S8Xfh6073aT5%0|9`4ft>Zy$wzXpo#vyWsC9wA;$(kNO)2> zI6RZ!bjY&ynugnQwbk5iRM!WnVJkpqk{V*nPSg($WOc^U%%wBP2b}cCzjprsP0JG7 zZ~e-OdfRk#C@;5dZpBNoMmrPA~xJN~1nrY_B)5e=i z$tlJ+5D@BldAXO4^i0K!qFJ5)=3ji6RfT0oLU@<>K-2^rvw{m;gJOEm4*Lg~sm z?sc;@O=x0KMJZt_k;?7+{{X4r9ykD-3Uv{Jis{B{rDac(4ZQ@af6-=(rM z&JP*;4y-QS&#Jfv6tXo98nXUUI9w0&kf00?ZUN6c4a9p#?%mVV^fXa47P_-Eh1F4A zg#39pK1W^*k-7msZDmS~xk^%>Hn2Sfvff3a<5kz`%e<1>^@Yl#Pu52k z#d<2YsHS*j1iMr;U=M}LVT$g-`RPq10(Xs5w;FOmCdyYk=R! zJsFEtruU`Z{jTdKo_T;2bad$O6a=Z`Ach3v1xW{zNCO70qqnzudP*6sRPtM4F7n3< zyndX)aHoF!7HkkVF~>eQ>R6L#a+-9xGR%Ue(Ek80L-l985qpCiA-?JA$Z75Ns%Yny z5RVixCd2JhlJPg5G5Sd#JxiA5T4aijrY14&4A92}X%eX!4(dGl@Bzx6K6+GAzM%_yNH1k()zSCS4L?e zffTyL@efTH=knPzpFblx80yNJvHrf1?QvpiMMd1~Rw-E>uv?5B%6LB0!RMn|55tzQ zG%q#cwvyQDs>>eWSO9zt#AASd{ts7{{@r$qO2Z6#+g{Q%tWdY1ab*M(?!g6>SPpT@ zx4{RiAu4Au3X1Ada3qy0a0Rtl^zAfqfQ+CXx0 z(_n5G;Z)?G_vxE!{2lhnIy8}DxK`D|OvzhKTqTJ4!#o54>(45$&T-G4llCs$b_(XT zH#J{a-YM=cE(etcB;`&Aoy2DyJG2IzrC24n+H0zPEu|r)w^F=+Q-U`zXxNni zXWsb8{{ViXXi@KlH%N*F$x6j4WfeQ(C3sI!iCnT9hH+nc{6ARfC?%khzeLhWSc59!CHu>5Wa!=7`~@;kK+ zeyF-s=<9fDnq?5uz{6|R1prMXZ248e{V3-z!1!a)2As27CAdo^byU@1rUr3U2!^m8 z364YHuLB_t%=pJbGnSC+yKs#uR1|7-(yXOKo4bO?dWHNP1}~9}$!Gj0!yPlmYp#`% zq89X{+8!73RI;G>&JXR^DE|NtdKTML{ZhO7y+m+2B=j}Y9LO7!wNM6rFfou1A02C2 z=&l+36`Yg;8`@kOuI3VX7Kf&Q$Y`5QNjccuzztN0CO%-KmjBiqR z{$Yr}CnF7pQ=U46RSOc3-uJ+2woKv_nRN>j7`Qoe7<_U$jpi`kvwq#_ns<)R+i30X z3m9%yG8ROr2Rl&-0kME~F=6NU=mLJ_-)hq%X)d=dElEG-`a*&mkJi&i7|vfM1C|HiDx7)hgGbo(J(i}W{{X`@6y;WD zidbx*;X-6=4xx(zdE}p;80o_E08$GG!IfJoh?y0L$20kgi*O*3h(4m+VHsnh?moDv zte~y0sgf^D$oBerkxuc9K51yG?vZ-}_K%%G8u0o$>ZAp4&V~$-=t~YRe?!d?ZdF#cewEA+cww9Lnc%FIW z22lkaGt@^CV=VK|cB+s$A$MST9Tx@>z7FBR}odm$c~a(%7vjcr|qO za^a(Eo6@Bg5gDNd08mwk|e3BuWn0AEki76 z9AI&yJyZh-8pp^xcG|;+zyR{t}YsCpPpt#2enf?q6}~>SWg%mabX?jP$e{ni!aX zaDp^D6LC;8x6eIP+VtJmQdL7Oi%r~WXy>O%WmzW?#Tq^7f9?N#4 zOv`@mP1!XiYZQ2mY*M8>XE_fnakk*TJ*>lb>A~c5h1L4%qC4g^l|v=4%4Lp!qFObZ zoCE2f2si*XFb9FvWfUlI7fJ_jN>FKfX9=P1}1jD@c{(a>NSA*=QJYcHs7yxJE?oUFI+G!5z$E~BIi6jxEGgK+{G6~_xDaHo` zbDjs!Ti&w8_*U0+k0mwM$d_A;MQLHVdUQ!oQ~lBVFSdiy%-M4mj5Xyc!5mIIw+ndI$lun|jJ)0~G{+g1Zo}$J2 zD2LJ$I$OT_w!sZGntQTWQ%4{CG6>j39ybtgC=@CYNuG10)8zR`}Uv{M|V9rElkGac<<8+ION zf66Ml*zVq~WKmBGLNQj*dVZ9Lf>!{S=RAG+J#&2JFpj?PdNC-df^@Tyssgxk8E+fEKkeRqTnbT zWVz269!5`9DH_1SDiWfp2MK1k<4+)Y#hei7JshajGL&};vI>a`xbu)3Cjk;%#V z>%Ew0%Ve-aauogcMF}urQ=G8C8#-0U50u2Ao=@ndNI6I z*z1yOrLv`{Xo6uVgmO5EakK>|!Oty>VED-EjeSm@x>ZG8PbDoaV$#5{qDIif(yD;* zF~aUg!NX(_$Hzm!LCy|#fSRBCBy=`5vHP)T;XP&c%&$eMqo|5GfRMpa`QsqtBp={- zAmgv->TVL-4?95N{MoE9AWSi+K#+DpWwgs(RK6S~*wey~Sn z)RWa0HF8BOvaaog!#igH9Gri!@CJJ4TU_ovFvJG-6UVf8Ae>-e{{S6$rMl1xxmt>m zBAJ+Od3a~Q$;X_3zJC3GQpDFfXJ~0@<4B1jNhQc9IRK7G`)42h`Y>WG37JSl!!rgE zY}zZTVGzLcK{PTduJG|oyeS+NI6Qy)^<1@omT3YsdCRm*mS+9N?;kxG(&-&#d3fn< zwn?hxbRsp%l_MvRN{&B%pG>7J8eemDsH_s9#D09D```%pYj|CkLJ?C8(qYZ-}ef5+u!B)C^tF z_iTtA)g-yi+T(~3FGF`L@fCB5I=UX$Ug)XXPiQ_|8QW+9}Qx~}X6a1{XBK>LBy zD*N0!4JY*Jb@db8jc2}jEw>=%oE0CE@#F~R@TY~y4eLy#%pQ|io9@0kV!Pfc7)ysDh^kApZbnIz=D($-p_~T z-jRsEh$f_9r}}L8iBLBm!m^+@eO_?~QC)2@Ug~c;DJQkiI70+7$ssWSi|QulJD6lP zJd7L+fu4!2Z>IH^r+KWlz~50ylTPuUrZn<2WbNFTC2|fjd=7c&DE6COR&HuadG5hM z1*zv(W<_l7btg-kx)~vGPv`BONC+$Vb%RZ_+w_pDSwBUXZ0~(Kx%@*zUc~ zTjK^)cHM3Ckk5ODYCly;+v((%EyD0hoPbZ9cead{vCvd{Z{M!jK~^PrB&L@C0833e$hj}QRJtxP%DNBdJxmt; zTDE#^DWh+iF7*pS=p?r48XC8jDRGuqgoFa2#Zm6HA;mIrKw zi{A)vcvq=tp1xUveLk78lG4{m?Ars z<~dWG;Tc`?_GDav$6Rgir=$9NZO(|-z-nY+EYYjHb7%BEa4_kD03$r|dJKa^>J1&K zV7O~4JM|45gakEk%Maz}Biwla93QtGHZGJFirA)grGjdhX1j==0m^BB>Qdxu%I^enUk%1rWCHDS!_k{`YI$OXqCT zOD#PGqG}uB15qVyC#>G6U>#XO;~??=pp1<4F?71=n~haP;?WAyQJL3dVs=oHkOngw zV6NiDcV~hD93G&gOjba_G6T%XlJjp6RN2m8=hYAPj9^!H1;+^KZGdkK{q*6#ha3>oS zV>mm%SKN+q_v5VMygA-GI(k6rl!Jo(;-(;mVjYvAwSel-OG|vdMzu>7qNveTx6{iy zPqAtOkZ>G~FwYoP@JHXG+gEfcZogMcTc@rNQ&g|!g{qbrHfI}DBVh?1GoAqcIs&1W zR#)0;Zctt>W}4kgvTC1L5fnt>l#Jjh$mMw%8R|W%3S^^_Sfqv*d=#sa$wZEn@Eltl677U^o)WO9ykSU7vOOSY%-B^vCtHQ$wfiRd+XvGe-o>%@Bf| z6O_)?+&RfCc_ZX>Q)SWiPSe|JCA!^ag6CQ{Q_|Y1*Xm@9CQi+Sg+rEHj|7v?8DD8x z=CxGRrMjYyo_Y$X4K+<%jUv>U0rc2Le714IV>mg-9a~s5R<+gl>guZMs#tDRG8k$r zBCbA_Tu+m{qXNN86T#c$exc7sOvf%Got-DM3X-XZ2xENBffniwt{uqDjjg|lTkf8S z+E-7@UerkHl{Hmu>KtzW0GKdC5ypAJ$H!eOKM+^T%{-L%TQ#)P2;Lb|35XIbi~4iP z!EE?p`bh)_XQ!?bcl z?X}Zr$ZQkiZanw^fs>Am5o@E0IlyG+*P7jlws&2Z5_5D&*YDx+z9GTV%YGr^%!;FD~bAiAl;9~=+IxZ+yKv0!6e#jI^5q9L2}3Q4DdUZ!$dWk{2_r9s1+aKf0q}axHrl&dR4qKx zn#+|;h2*G^BJOM`VU{Ng?7-*er!|hE?>)7xU*Y)fOIAPQ=#s5sTFAfwmZEjYD)0J} z`+{vJ2dYd!T6n`WwOx$xg#r?$-<#NgEir7-20_jWq=s1 zx`4N%QV=m*M*L$30peljl0fQ-nxr1hvW*pN=@l{!P?(Zcd)&FR-||heFe`l46*mZ~ zt@gO7Vxg2o-l2=^EQ%HcuYh7Zga`FXe$pwr>Kwzi~WMGdC)wRNU%`YYHQq?1OQ-PpR|qhK5c>T#231iEC%m{u7=**3Y`J6}&AV>!!LEoA8#qMcG%6jM`1 zh1vbQJf8>81Hc*R(@JU8w$s;ZOXVbMZlhL;S*KX!{LCCALQ%D!JaLksXXhOj->SQL zPSa~ibnuqzc96ZlPQfF`9A`KURO10qyKdPZEyuVHiXrSpcB`bNyzBerHO}Ixxoi|N zttBlIa(1E)1_n9CNj#paRugmY--KyOq+MviFX`ZJ-FNOi<9lft%KNIWb@Yv0O+&q& zO|jF{3@toB4XPD>4 z8z;_BJwd0{_4DqI9UDT?sV>1iR~bJiaVz|B)4KcG$gAzOSItjo5YipKmBMNhM^hLc zLoy&nQa{YQxbxDZQmhX~EjlYQQ>%w%49mFlf*HrZKXM09psc}yQdCmTB41&JMV#F! z97%BfK#JSut)_Z-lFx09hNFWcG)o>2$UoZOU`{&WORu#hAoXFW6F5~?Ew^R?KjlC8 zAoWXGrmm8MEZb5~)K*C{N#mRkJ$PLktF{3JhUh?V{gezr{{T<_08WBeG#f_rx;>gD z1T9HF>c5#VM^W5MrDRNQ7HecnN9->DQ@UI&oHb-Vo)T0X{#u?j|vei^|d&IU2^3FE6d^gTt!7M9aZ6-+^l zX(BJD^%ww^Rp$;q4}*@RiK|WG(vHrdm@t+dLVQksK2b@f-k{#0ty#=6La!pUOv=FH z9^IoL^PKV1cFo=`f2UYf+I3}cki^Ry%KoE3+$nWq%AX2(#t9t;(`c*ylZmIgQPa~! zR)^3nR-~Q3s5AcnX$OpPlat_e%AGG&QHbePrt}WR_Iz7$R|1D$J?xDr?lp?r@ADR(4x&0C4;bHgCjd-AOZmc zpOKJ$I@KMq)pt!W-${16ww<2oPd(!0{c2Vb)B zX6*@b=88$PyN4nOEczF37Z%Tr{Wi0rw_oI%O4_^CQr0A8rGUV=nKmje_Yt;KazZNZ zCwqa%Rl2g>YSacgS!tfu^kXkWMQnn$skak@B}$bXZ~zQdcmsi+uWEJ7dX9zv0L5Bz z;c2qcv}DI3!%)@ij2Nu@oUB+lB0T%YjQHt)SE+3k)pay|rN2!vX^|4$ai$LqE4VDf z*Dd2JKS>zydFgp`dpUy;W6`}n(WhUs#bq%~D!XZKdnv>7Q|JxVcCa>xdbZDc(w3`z z<5O0JqBU)@mPx&Rnj=yi4M{?1>KeQl@0ef<#`8fd!=!!p3`NXj)L1Om8g=~@>4?$ zc>p}>gE&9s$sRx*Zum5nwix94JADwiR=z4FW%VbhUVXC7Cgxx;4m@#@j=KK&~^-9oW^cQ!bp2gH78Y zG0{_`z?oS>lO9oqd0s$1)3_W1&ssvs+e>6q)TPe-H1j;jjT%kqLa`ITlEi}px}}6I2vlg<}y==`44;Hy1jum zj*Kdzr7}_-+yYd0FwB3HIJtY?5qe`!Yl;XUmgA`FfkoT}b$6hZ1pffcQ3RkM3Qh(% z&N5Gqx>V{an|(!X(vqrMX{4o7BT`hQ95T1Ks&E1}NL(l&l5_VV->9_Cn%9~(-K(C% za-gWMM0imuOGoJl*^UsGjnA+*kVp4((t}lKy*sRJpW)jknYvZiNQX7GYaC#0Qm1TV z2)I59;OC5!)6!}aG#3m;f8FDV+B+h348)N!(paR2bvrf`gn|w7$N~fJMRz~A`lnsp zp<9-qTB<4??@38W5o1Ru4A}q)*e(^NK6%@~=nCygTX>dAn3f_GBCIG>tc853IU^L3BkYtihkVVo~m!=RJc`FS9-8gTsTI;6^(evP&vo$dFLmucda*Yhw3a)H1$(c>^Jq5Gnu7VKEOXDMH2^g(89utUl}pdN zoQ3AOUulR&v~sNy&WdF87Bb}Im33^N zK75nU9y+(o%0igf^8KM{I}d2o)8iOdFGSdA&(@EnBV$Yq&uZ-X)Ra#b=HnC=!sG@4NWwma5p9kpxOowPDnm^ z>&~jv8edOWt-n^mO;u4eYb-`-C8m#N;-DE0OEy6)4hN1$j;LU{UbKBJMdHJ1wbO~E zV+6GF(^i#_j4DEm(i5NVE*GDUilU{Z&~xxGq=ZSLm=jem={I7hM^SBfo-YA<%kYET zYhA@=(KKy$qm(bcTC_)xK5>veL7sOI2+t$up$p$(yWK@_rlqUUce;tyKPFmaN%ENG zODFpApW~*Di}7!A(ziJ&b#=D;Bxu`YnRldz+rdbhPf1R2IN*+X>7%6o02lqD)l2AH zbv*WIByhyt?W_|c13sig{12VIAhpewS%KyFj`EIOC0wyH5|V$+0S*43!!74~ykuLh zlhvAD0sbJ;cbYiL7avtf$k_03NaTN;{W`8yd5BQbDu~ICY6o27g+C;JryuFp=wv#Z zOx~e279CSXUrC+s3^OaFfRBLrE0O!)_~k(?7MHPZ$_sz~`z;?%8+l{Vi-48e2vAE(__@%PS>HF z_x^uz(>FwGF4Dy$G?%>~Xk}@4HEi=V=ua32^o8dnXQ{W}!hdz)X(}zYY8t6gzvL|H ztk^%OZ#ZxDJy+9y7Jm@8dM{mjXLa;hh>cQc95W30SlqYA2Oq~vrfHR6$KLqgok=5U zqUA|FQb+Wg9|ny#+iv4_ih|`7hq|^(0ykm->PI4{l8(h0O5_d~81tT**8bsk-%!>7 zra)Q0@38k?%~I{1(SKNyp8(?n^VJZ3*dy(ZzdUsAhoYpHW#eU4!Wo1!P{<0HBqsnc zY#e8dk&ONN(|xqiv&9`9LgOPZA`Fb=5PA6>OzAzS?Pe3w z{{U!GhiWHhOis_E!a;KCbcY*>Q(s0LfsHRgs`T`f=91N`5u`1X1UE>k6`SPY7=z>Y z{(2DA9lq1Lp5Hx06jpi~$tDb43PUu5I1F7#DfjsY$p@wOveMRT4K3=~r!~!0uER7V z>ggkqWrcx0)ZT}16@n`@86 zPKwbMxuLY``a7L+sgY8S8R{crpCz)foCC&5&eQybE$i_m-W&C$t`^NtO-)S66mJL8 zMFYhlaHk=c!7hWz9_)@fytUO%V!$^0c|zMlkKvu3N*1Y#Dq-CrNzTBimeYALeLjb{ zK|?LCxVqvCT|GF4u4*bI4DGNGi|Vr$E#z+O5|JWHZPqrMp(u zhl*Az3Nfb(v=Rx($Rv(>TIxM{-0f7zZf!M0eyJci9+Y^>oGe5rBM007$nlFNvf$mIxyM&>l)Q3VDkP$%^>_rw53< zD56hIN!$oHDhiDA^MbxlRh2ch_PUC8dg}U$W^+43Q7fuSjl!uweYwvJ1_9^x>c1yX znI3R-{9Uz{FiV=3{{T-7{Pb=aMTLh(c7oYUR;i)2MQuD1ZdyaFt6`#3fHES7YXWwW zmXPs}f!E6xtI^h4TJ?{$lyT2l5P~YSz1^kyRop?ydZ`O?2Ox|O7~`q7O(eJY<*ufk z>E@%5tRkt3ZUkdyASwPsO#$Rtk!e-2-pS#_Zi7$F|yB-E9?`qqqI{*Lpspn8i(Wu-%!p z6``Du%RZIKe?y)|OO@x42gV2@RxZ2{?T4B_e3!P0pRT5Iw3aLP+nSsj2!PSQ4QMfSVACuGH`p%y2R*0vy zL2Xo}rl*T-Ey75u>En=XB*xImxtnRg_a+JVDdcn^C{r&G7CVknxBeupzaUF0b%A1} zq~p2O?QHVVwPZCE^qQzi9WH{ao~bGdt0i4b%*&0!GSUY3AU`ZP0G>(c<$HTIA21$Z3*d zxyr^F!koARk~zpE4tmWRqwx#hUf*7;E+e!yOE|tUs#}qus+d<(?jaeB6_V0|3wVuy z&yaeFwV5CbLmNBcscRjw)RLp4J8=Ts-r)E^zK^S+(b3aGERF7u(koFcvpl&6B`ISe z5OM;vgq(AO*KI+nEw=@PQ`_2R!upY!wo|eNV4;;TS%z?S;CyrEphI0c%N4HL@y4ik ziiu>}L+#p0J_bGs{{UXCYP8+fg3C{Eq}2ASlGaO)rb>|VsoF9bc7|UdWN>=-Xx-5m z)3$uCm=zP%-&XQN=esTJOFm5!wB7GhKx*i0RS{Wf42DXHAeKdjFmMVOoRynC(X=UU za>N0iufu8mQFNz<{ijk3&3F+sux^1CK%gr*bFhQ700U>Aj~x;A=E-rM&mhzk%LO8& zjT}`fu7hbC$}t|!0sjC`ob(f_sxCJ3Q*L{F41KBZT#}oJSKIykU~WIR&qO$oo8)0x zHEFD_RJmXU3piGM&SVV8cCc%?C0f6?GHFUWMreedsXJ_YU=JrebOkRL)1-A& z`!leVMQFu{wqJ7j`4+xV%+j6chWBX}O+{9*P$)>@s)9C(r7$ofF^&lsCvXQi#xu`S z;OzyUS5dw8lIKG#5Y+9hXPshv1_XjwkTQ%I7Qo{jDDRz~q@wA-aeD!qCN&3|1$`qTh>QFGqrXXsf0Z|&el0f9clo!%z-4AiAnphpI?Iyz= zD=NDzvPj*svi$ zz}?47m@g1cCZ?yTR+eOFA@n2-wOLA)A%f(uwoK%0BcC92Id!x;(M)OTX%=ZN30EZb z(mND{t03Q=F_qh&(xs0$$?9e)B{wlz3K`wIkjBr5Z?>d1c@9>*YFig_?V5_nnuknj zn#yn@+-fQjX$*3y2=U0uS|Z~l`5}Ug4}ciG$kKMY%GA^qwbP2Ig1l8Rit1Kk$AOZo z5gy;ufq*#q=^LW^&8jp_ZGD4J$!wa{c->O;^^h%05>zsbx22pbJSb=A)7qo80b>M2ruLB?F8D?_0&@kw(gf?+cVR3 zyVtf*LvwvKGBxi?>nSYs^RR-po@f`I5S8J%W z*0U9;NW^3+MvUtbM#8HypKw8r6e#1b4|lNBRkU!;RTrx_vwYu>QP-WXfsw;=lelLi zcV6!m8ZP^9T4*Y*)~RFsx_Y{cjIARap$j2$s8fbM%oS1&20Al@7kEZeS5^$Fi46G= z3ya>@4Xay$4AEs6Lw_O<|=d zH6_mXK~rw0qKcZ5h6yA^Ns&WL%n#~+X&&qWoDUm{pGw{J8dI*Cw?#{=Ql{ijB zVS=?#;aHvS1E+(zd=EJ2pD`j~0Y@$H6SDS=1f>~BA^!lD=fD@~;}!Zghfw!Tt94YA zRh4>m7}3z%DWg?ug-BER((*(~8wC5k-T*wFq@`A&?M3)s?yV8IPL8nBG^s4k$BeQm z-4cO-Gl7ibstXrv`@N^AzL!dyr}XJjqcT#|!RVDF?)M1_j(#$Fys>t_P}^?X>n(M1 zR_+Z|Uvd8cks1{Ta5>*I5#TAn@>xexrJ&hL1a5>)6y>QZl`Tn7Ha8thrQNv2PNlcq zHH-^Utu+Ng3aJ&=+^-|YgM*G(oxvH(?e`s}w(U@=2{{8jLS_Yq1-zj1X=c);@P!>gWnX>r>EdAX{{T%pVXF*t zr7h~(C?F3klT#Xrq}(_;at`u-e|~Y&r(S8MvsM^va9k&)RW8di`ZFhzci@r%%HfBc z@t-{&S$mNTD3NN4tDSW;k!+5xf~VH?5M+hmg#-)?o=2aayjnF!;l81qUTQA1^2a09 z82*Z)PgX;dv5K>+Z~@Lh&T>HGt0hKQbvg5eW>X5{pO!*WMsDN8I|kvz=NVS_qHc9k zi;XN*Qi%xksl*$I&+VUR=fTPGpE(R}*l68B9SoH>iRsnUA4-wo+bm~~agc5U+DY1R z;NFDk4*f7?HIkc*$pN$L)?l&ss}Fc4tj?^H0Zg))uQx zbkLaU9p#=zrKJiO5Wcg3@4ZR=8%ZaO06MDX%aPyI=?@}S(c5TM6@n4VAv>}$)yH+UHjmU)SE#C~t5!-VjXXYuMkT9xKqZ);YQ&&^ ztYqZ)1QVKNu<6T%$_XoL?h{I~C^Jv_@tD-{99wX3JZF$|&qq_t0FcLwEl)B`nX^%e z&~-vnb_8VB%;mo_GJmC(zgy7UHFs_A6;(oNfh@MqsZwMz5E?g7RilwfIF)i7V{(#D zL0WZg7n}4p>Z^TK)@q8898}bdDJzmek_xfHNXkcy4cNf|^VBO=nbbGoQx1``%TH~E zm(`Y;NO|Spi1Ie&vOx%>Fc~9`wBKvHZ(ZBXJvH`w0?l)#q>AWjt18jGI@m)Li8B~@ z0as;pC9+0$9P~PcAt)hicR_Y1%>2q`(iI`DqAzEAfE$AnMTaQmqqPo})f!wa)3_GO zO39^Y3S5vx(#*eduG?P+2OJVIIzR2VZLL~n;ZCru8T-_bSatR?RBN^wlysN_83AxjkuTE4UIw3VJTHA)vgN>{@n~ zO6$eUo7F;9JER$im3^FL0>-Jgz;lBY;jE!G!sdLJoDLG>g7;%(LKo^i|Z8{8U;C9;o2 zT2`isOl?ndt*U0i%^=A~bpbtALN{RLSKGiOb(-%}YR3_MG}?NmmYs7Pku-|IP6l^V zGXTG_#y;ILHBP;IHm#EG(k`K}->KlbQc}t4EKr${dgTP0O3GytXE_8Dw1L%TqL#?@ zQPtbDZ5=%oZ7gyyO|i6OgTXOmeUI<+)~!6g%>gZ*vGd)pFl|b;RpWNBNd&M1LI+0~ zo3sh9cKf4mwn!-H>EpG*ATtPSC8!=7CC24&4)!EqdCBqAn+M`+P}m+iX>^sXVW@4d z9Xm8-m4P4wgPov^ZY*+to}7JvnXVU#+ZA;cz3pxp;uFOUL#;pojwEFD2q=tL5)flP zcq|j!eF-hi#Z8)>sS+68u~w8KO6eiNR$fEk5rqScbxE_5l@u%U1*6$ZRFqGafw*^+ zl095%a^=4>YmW6-QCCxo{hc1FdSOv+p%;ppi71owhd#tsl%8?=LCD5BS=8QaRWeUK z9-5`5k@vBxu9AA1s)<;V&hoT`ZYRi5v|}9OJ#9O7f$U7TO+{~~vsH<0b0ilDtE8P; zH$1Y%Gmxcn*(U^N9C*m)tNb-~qIyW~6fa*rMZ!}A3r96PZU}B!dI1 z3kA+dmv-kZfCsgHe~!G~yFa2YdU`wEzVAhAl%XF*^}!VwXWHwH!+!y zC)Rd5jtTHrkU0BvUa8ytVW(Q`)@pisyKEulQ%@W}S0ayWWm9fYGFBGl(VeR9|LQUaxwn^o{u#D0CfKV z5Ej}BZ|S;<{Z&emA8er{ZKFS^KO~dR0X&XJS~E*rbo~_XL1Z*^xs54)mX@K?N~exI z&o8LzS8DEL0GteSfs|VszXHXKeUc^A3WKyPeCCncY0TbO~mBwkOArwjQd7B zd~~FrHfUTWaAvr5r!ohT}&r;0USxCANk4ngytI@O1>l#pEGxKzq(Xjr<`NG@Uk zgN>w?M!*;VfKLPO)efZX^`@FiNR0AWVXnhb2`WumR=9659fNoragaIc;$)`6KHia= zGFH>4m}jj35W?j~-|8iq&99tfsP6i_pH6COnuuaJ4?6mzAG2UL$Me_o)?1x^vR_ox zwpK`0N-lAaIq~}+Jw5d;qS2>oDlEFQOv5e0bQ3{Tl}|_&TOmiH5Gf;*$vMwM+IO{@ zf4DZwl(fgtxzM^8>1g0kT0TQF7Ui3a?FT(vBXM|P8%|Cx{Hz=B^|96MrmQoxS9W_` zT6BeKwd$)+{{T(S*M{qFdBEMtWo7$u$0zO9slW8`^8Wx;d|f-GEH@P_3Nfyo6{gAHr~qJbz!}d)-L}(OuTI^i zjyfr!i_>MSrxd=tQ?3XqLwiYV@^}~=^;IQH?zQ4qQ%z0{LRR>%g}^tqX}IL~$hMDN zH>>oN71CO(^+Z+FSTL-WH;g<^2auaVQJiuCBc>Ie$wsL)${imH%}gKYs%G0$ynsVE zn0uVIIR~Zn9)+*4>H9XMb~eg6rD0n^>(Zk}sxqHXB*Zdq8Ojsmk;Vr_7O8tfYHwQK zr>`R9!(%-ibWll*0tpFFs5w4clgY+9R(J{|i~GK??#fqY0GgnNVlgnW0k4FT1K3%P zWZEyS^mfUqnzpu(-u{^4QBPXFoGs^Rh=n;A<8%1?_0p#490nmKlDAtZ9fz$l{#22; z@SCF`e+5C$JqT5I9@}fUPe)&j)5kI;UnvW7Yg2@9`wJXDvez%$y-V?jV-))0Ks%P)A!l zf=Kk9r=oeBtrcZ7Z7QMRfN23ddE5?u=z1c*XDr%VlGl-b1?2~Rv&!e{ngeC8_ z_Zi}uEt2W!Y75NsO&wz{_>=-TnX?*7pJLdY7lMrG>aO@S0nqIk356YkGPaolvM9<(b|Jd zS5U=G@0PBDDAfb0%KEH_c_Sol%M5t%dS+C`EIA&&vCme})6lJ@sm+`^Tyq(V1AC02 zY1#VMlB%+3^&j-w^hFG^%fXYf(Yam!RyOu+vL{*x69@P8iZg!Ln`h7@$7tH| zM@&MWcc{#Kf^){~^WbN$H2T__&ahgq8ftrux8A5~F0s`}jBqlhWW+$@@h3)z$j)R31i&D!8J zNwMpT&C0T`Os#v3L2Lupto(_7rnt2j~(VAJQ%qz@^wnr-x zgSCX5xZ1b?^Tr1ws8xNslv1r_CcLp+7C+3Xo`w-0q5;mr%bb#+k~jm&>(y4Ev&C!m zV4bU-V?L8p724=<31Ym1k;0MpGHwqh~f%z;Cpe-GU{`{z#|-W9_LwJuWf1@6!kNvS_U!`EDxNj@x~j;9FLFNuU5&e zb(FN#vui6Y4$90;Pg_dNXjxZuZezjSPWd3P&eC~YVD!wfTlAgU!Fbd4c8g8&X`EbX z>TMp8dKuXMcPijCVR3=CkZ?b5DqsPG9#sO@G|XFTy{rwaMb2DxEt+oUeAHJdTHABk z^s%p~sZb6fz}!@f1H0Xhaq-j|zfSvcp5aq8RMYxstq11il}R6$jD-XC@J~(cO`-G+ z#+7c?T6d>^MWd;Sqi;&8Wf&!L4#i*q_Z(znb~xyEr?attRFcI-cY>Cjv}T5?w7N|L zI`35os!IUiVnoX0&Tw%@2ls~Mp)!7y;bsn&YqRUZ_R-_MfbK4wvD)cuRF`-x$x%F# z!6b^xT$b|n7&};uDFomU2OfGUxm{{vtD>Z}-!1ht^r7aONUBy@=|}_9bW$?lvB?0i z3J(K0={bJVx{j*m^tGa$YuZ^_pvZzax98sF!r(K3y>XP0bB=S=%0AWGsiF;ilvLK# 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 {x;L_!5 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 - + + + + + + + + + diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/no_bg_style.json b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/no_bg_style.json new file mode 100644 index 000000000..964eefc31 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/no_bg_style.json @@ -0,0 +1,43 @@ +{ + "version": 8, + "name": "Land", + "metadata": { + "mapbox:autocomposite": true, + }, + "sources": { + "composite": { + "url": "mapbox://mapbox.mapbox-terrain-v2", + "type": "vector" + } + }, + "sprite": "mapbox://sprites/mapbox/mapbox-terrain-v2", + "glyphs": "mapbox://fonts/mapbox/{fontstack}/{range}.pbf", + "layers": [ + { + "layout": { + "visibility": "visible" + }, + "type": "fill", + "source": "composite", + "id": "admin", + "paint": { + "fill-color": "hsl(359, 100%, 50%)", + "fill-opacity": 1 + }, + "source-layer": "landcover" + }, + { + "layout": { + "visibility": "visible" + }, + "type": "fill", + "source": "composite", + "id": "layer-0", + "paint": { + "fill-opacity": 1, + "fill-color": "hsl(359, 100%, 50%)" + }, + "source-layer": "Layer_0" + } + ] +} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml index e867046c8..51182f958 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml @@ -67,6 +67,7 @@ Use TextureView to render the map Resize a map rendered on a TextureView Animate a map rendered on a TextureView + Enable a transparent surface on TextureView Example Custom Geometry Source Suzhou using Droid Sans for Chinese glyphs Example raster-dem source and hillshade layer diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml index 47fee31c0..078ccb64a 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml @@ -67,6 +67,7 @@ TextureView debug TextureView resize TextureView animation + TextureView transparent background Grid Source Local CJK glyph generation Hillshade diff --git a/platform/android/scripts/exclude-activity-gen.json b/platform/android/scripts/exclude-activity-gen.json index c1a6b5bb4..2170c4d9f 100644 --- a/platform/android/scripts/exclude-activity-gen.json +++ b/platform/android/scripts/exclude-activity-gen.json @@ -27,10 +27,11 @@ "QueryRenderedFeaturesBoxHighlightActivity", "MultiMapActivity", "MapInDialogActivity", - "SimpleMapActivity", "ManualZoomActivity", "MaxMinZoomActivity", "ScrollByActivity", "ZoomFunctionSymbolLayerActivity", - "SymbolGeneratorActivity" + "SymbolGeneratorActivity", + "TextureViewTransparentBackgroundActivity", + "SimpleMapActivity" ] \ No newline at end of file From 35930614ae796f165974b6c6f5c70b7e5667942b Mon Sep 17 00:00:00 2001 From: Tobrun Date: Tue, 13 Feb 2018 13:01:36 +0100 Subject: [PATCH 039/264] [android] - remove lost dependency from the sdk --- platform/android/MapboxGLAndroidSDK/build.gradle | 5 ----- platform/android/MapboxGLAndroidSDK/proguard-rules.pro | 5 +---- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle index e2e088185..173b4fa7f 100644 --- a/platform/android/MapboxGLAndroidSDK/build.gradle +++ b/platform/android/MapboxGLAndroidSDK/build.gradle @@ -7,11 +7,6 @@ dependencies { implementation dependenciesList.supportFragmentV4 implementation dependenciesList.timber implementation dependenciesList.okhttp3 - compileOnly(dependenciesList.lost) { - exclude group: 'com.google.guava' - exclude group: 'com.android.support' - } - testImplementation dependenciesList.lost testImplementation dependenciesList.junit testImplementation dependenciesList.mockito testImplementation dependenciesList.robolectric diff --git a/platform/android/MapboxGLAndroidSDK/proguard-rules.pro b/platform/android/MapboxGLAndroidSDK/proguard-rules.pro index 3b8adac5a..b5a1d82c8 100644 --- a/platform/android/MapboxGLAndroidSDK/proguard-rules.pro +++ b/platform/android/MapboxGLAndroidSDK/proguard-rules.pro @@ -12,7 +12,4 @@ # config for okhttp 3.8.0, https://github.com/square/okhttp/pull/3354 -dontwarn okio.** -dontwarn javax.annotation.Nullable --dontwarn javax.annotation.ParametersAreNonnullByDefault - -# config for optional location provider https://github.com/mapbox/mapbox-gl-native/issues/10960 --dontwarn com.mapzen.android.lost.api** \ No newline at end of file +-dontwarn javax.annotation.ParametersAreNonnullByDefault \ No newline at end of file From 44c00ee63e49be05fba253c4bc44cfbaed36dc6b Mon Sep 17 00:00:00 2001 From: Tobrun Date: Tue, 13 Feb 2018 13:08:43 +0100 Subject: [PATCH 040/264] [android] - use ImageView instead of AppCompatImageView --- .../java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java index 45f72af1c..fb2c70cb7 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java @@ -1,15 +1,16 @@ package com.mapbox.mapboxsdk.maps.widgets; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.drawable.Drawable; import android.support.annotation.NonNull; import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewPropertyAnimatorCompat; import android.support.v4.view.ViewPropertyAnimatorListenerAdapter; -import android.support.v7.widget.AppCompatImageView; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; +import android.widget.ImageView; import com.mapbox.mapboxsdk.maps.MapboxMap; @@ -22,7 +23,8 @@ import com.mapbox.mapboxsdk.maps.MapboxMap; * use {@link com.mapbox.mapboxsdk.maps.UiSettings}. *

*/ -public final class CompassView extends AppCompatImageView implements Runnable { +@SuppressLint("AppCompatCustomView") +public final class CompassView extends ImageView implements Runnable { public static final long TIME_WAIT_IDLE = 500; public static final long TIME_MAP_NORTH_ANIMATION = 150; From 7742992692a26d30ea5c45fd6472704feac23be5 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Tue, 13 Feb 2018 12:56:43 +0100 Subject: [PATCH 041/264] [android] - deprecate locationengine --- .../src/main/java/com/mapbox/mapboxsdk/Mapbox.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java index 853ea1c11..536f5d02a 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java @@ -139,8 +139,10 @@ public final class Mapbox { * Returns the location engine used by the SDK. * * @return the location engine configured + * @deprecated use location layer plugin from + * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. */ - // TODO Do we need to expose this? + @Deprecated public static LocationEngine getLocationEngine() { return INSTANCE.locationEngine; } From c83dc310aa656132638a66e65ba8aa274b5e969f Mon Sep 17 00:00:00 2001 From: Tobrun Date: Tue, 13 Feb 2018 14:10:48 +0100 Subject: [PATCH 042/264] [android] - update changelog for 6.0.0-beta.2 --- platform/android/CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 2721d0caf..7b77582dd 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,6 +2,16 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. +## 6.0.0-beta.2 - February 13, 2018 + - Deprecate LocationEngine [#11185](https://github.com/mapbox/mapbox-gl-native/pull/11185) + - Remove LOST from SDK [11186](https://github.com/mapbox/mapbox-gl-native/pull/11186) + - Transparent surface configuration on TextureView [#11065](https://github.com/mapbox/mapbox-gl-native/pull/11065) + - Constrained setLatLng documentation, expose setLatLngZoom method [#11184](https://github.com/mapbox/mapbox-gl-native/pull/11184) + - Integration of new events library [#10999](https://github.com/mapbox/mapbox-gl-native/pull/10999) + - AddImage performance improvement [#11111](https://github.com/mapbox/mapbox-gl-native/pull/11111) + - Migrate MAS to 3.0.0, refactor GeoJson integration [#11149](https://github.com/mapbox/mapbox-gl-native/pull/11149) + - Remove @jar and @aar dependency suffixes [#11161](https://github.com/mapbox/mapbox-gl-native/pull/11161) + ## 5.4.1 - February 9, 2018 - Don't recreate TextureView surface as part of view resizing, solves OOM crashes [#11148](https://github.com/mapbox/mapbox-gl-native/pull/11148) - Don't invoke OnLowMemory before map is ready, solves startup crash on low memory devices [#11109](https://github.com/mapbox/mapbox-gl-native/pull/11109) From c2fe88815595808d40263e40d7999609ed642e79 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Date: Thu, 15 Feb 2018 12:50:35 -0500 Subject: [PATCH 043/264] [ios, macos] Added missing files --- platform/darwin/src/MGLHeatmapStyleLayer.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/platform/darwin/src/MGLHeatmapStyleLayer.h b/platform/darwin/src/MGLHeatmapStyleLayer.h index 35095fd52..ff2b92f22 100644 --- a/platform/darwin/src/MGLHeatmapStyleLayer.h +++ b/platform/darwin/src/MGLHeatmapStyleLayer.h @@ -90,11 +90,11 @@ MGL_EXPORT Primarily used for adjusting the heatmap based on zoom level. The default value of this property is an expression that evaluates to the float - `1`. Set this property to `nil` to reset it to the default value. + 1. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values no less than 0 * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -116,11 +116,11 @@ MGL_EXPORT The global opacity at which the heatmap layer will be drawn. The default value of this property is an expression that evaluates to the float - `1`. Set this property to `nil` to reset it to the default value. + 1. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values between 0 and 1 inclusive * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -145,11 +145,11 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - `30`. Set this property to `nil` to reset it to the default value. + 30. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values no less than 1 * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables @@ -171,11 +171,11 @@ MGL_EXPORT Especially useful when combined with clustering. The default value of this property is an expression that evaluates to the float - `1`. Set this property to `nil` to reset it to the default value. + 1. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: - * Constant numeric values + * Constant numeric values no less than 0 * Predefined functions, including mathematical and string operators * Conditional expressions * Variable assignments and references to assigned variables From b630dbc35755e4ac71ec8d82749d311893522457 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Date: Fri, 16 Feb 2018 12:42:01 -0500 Subject: [PATCH 044/264] [android] Reverted master changes that doesn't apply to boba. --- platform/android/MapboxGLAndroidSDK/gradle.properties | 2 +- .../java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java | 4 ++-- platform/android/scripts/exclude-activity-gen.json | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/gradle.properties b/platform/android/MapboxGLAndroidSDK/gradle.properties index 3796173fd..f00eb1d77 100644 --- a/platform/android/MapboxGLAndroidSDK/gradle.properties +++ b/platform/android/MapboxGLAndroidSDK/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.mapbox.mapboxsdk -VERSION_NAME=7.0.0-SNAPSHOT +VERSION_NAME=6.0.0-SNAPSHOT POM_DESCRIPTION=Mapbox GL Android SDK POM_URL=https://github.com/mapbox/mapbox-gl-native diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java index 67ecf072e..fb2c70cb7 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java @@ -7,10 +7,10 @@ import android.support.annotation.NonNull; import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewPropertyAnimatorCompat; import android.support.v4.view.ViewPropertyAnimatorListenerAdapter; -import android.support.v7.widget.AppCompatImageView; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; +import android.widget.ImageView; import com.mapbox.mapboxsdk.maps.MapboxMap; @@ -24,7 +24,7 @@ import com.mapbox.mapboxsdk.maps.MapboxMap; *

*/ @SuppressLint("AppCompatCustomView") -public final class CompassView extends AppCompatImageView implements Runnable { +public final class CompassView extends ImageView implements Runnable { public static final long TIME_WAIT_IDLE = 500; public static final long TIME_MAP_NORTH_ANIMATION = 150; diff --git a/platform/android/scripts/exclude-activity-gen.json b/platform/android/scripts/exclude-activity-gen.json index 1d61d9394..eff7fb9cb 100644 --- a/platform/android/scripts/exclude-activity-gen.json +++ b/platform/android/scripts/exclude-activity-gen.json @@ -27,7 +27,6 @@ "QueryRenderedFeaturesBoxHighlightActivity", "MultiMapActivity", "MapInDialogActivity", - "SimpleMapActivity", "ManualZoomActivity", "MaxMinZoomActivity", "ScrollByActivity", From 12103c95c03c8111995dd2ef4b9c7fb8ab671252 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Date: Fri, 16 Feb 2018 13:25:28 -0500 Subject: [PATCH 045/264] [ios, macos] Remove the duplicated entries in changelogs. --- platform/ios/CHANGELOG.md | 2 +- platform/macos/CHANGELOG.md | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 75a732117..cadbe9dd3 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -36,7 +36,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Fixed a memory leak that occurred when creating a map snapshot. ([#10585](https://github.com/mapbox/mapbox-gl-native/pull/10585)) -## Other changes +### Other changes * Feature querying results now account for the `MGLSymbolStyleLayer.circleStrokeWidth` property. ([#10897](https://github.com/mapbox/mapbox-gl-native/pull/10897)) * Fixed an issue preventing labels from being transliterated when VoiceOver was enabled on iOS 10._x_ and below. ([#10881](https://github.com/mapbox/mapbox-gl-native/pull/10881)) diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 0df8e9a80..316e09b5a 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -21,7 +21,6 @@ * Fixed an issue preventing `MGLImageSource`s from drawing on the map when the map is zoomed in and tilted. ([#10677](https://github.com/mapbox/mapbox-gl-native/pull/10677)) * Improved the sharpness of raster tiles on Retina displays. ([#10984](https://github.com/mapbox/mapbox-gl-native/pull/10984)) * Fixed a crash parsing a malformed style. ([#11001](https://github.com/mapbox/mapbox-gl-native/pull/11001)) -* Added the `MGLTileSourceOptionTileCoordinateBounds` option to create an `MGLTileSource` that only supplies tiles within a specific geographic bounding box. ([#11141](https://github.com/mapbox/mapbox-gl-native/pull/11141)) ### Map snapshots @@ -32,7 +31,6 @@ * Added Danish and Hebrew localizations. ([#10967](https://github.com/mapbox/mapbox-gl-native/pull/10967), [#11136](https://github.com/mapbox/mapbox-gl-native/pull/11134)) * Feature querying results now account for the `MGLSymbolStyleLayer.circleStrokeWidth` property. ([#10897](https://github.com/mapbox/mapbox-gl-native/pull/10897)) -* Added a Hebrew localization. ([#10967](https://github.com/mapbox/mapbox-gl-native/pull/10967)) ## v0.6.1 - January 16, 2018 From 604931247e8c78570058981ea17c034947c4430f Mon Sep 17 00:00:00 2001 From: Pablo Guardiola Date: Fri, 16 Feb 2018 22:12:45 +0100 Subject: [PATCH 046/264] [android] initialize events when creating mapbox instance (#11211) --- .../src/main/java/com/mapbox/mapboxsdk/Mapbox.java | 2 ++ .../src/main/java/com/mapbox/mapboxsdk/maps/Events.java | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java index 536f5d02a..e963993ae 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java @@ -13,6 +13,7 @@ import com.mapbox.android.core.location.LocationEnginePriority; import com.mapbox.android.core.location.LocationEngineProvider; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.exceptions.MapboxConfigurationException; +import com.mapbox.mapboxsdk.maps.Events; import com.mapbox.mapboxsdk.net.ConnectivityReceiver; /** @@ -52,6 +53,7 @@ public final class Mapbox { INSTANCE = new Mapbox(appContext, accessToken, locationEngine); locationEngine.setPriority(LocationEnginePriority.NO_POWER); + Events.initiliaze(); ConnectivityReceiver.instance(appContext); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Events.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Events.java index a68d4763a..a4f6b233c 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Events.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Events.java @@ -6,7 +6,7 @@ import com.mapbox.android.telemetry.TelemetryEnabler; import com.mapbox.mapboxsdk.BuildConfig; import com.mapbox.mapboxsdk.Mapbox; -class Events { +public class Events { static final String TWO_FINGER_TAP = "TwoFingerTap"; static final String DOUBLE_TAP = "DoubleTap"; static final String SINGLE_TAP = "SingleTap"; @@ -26,6 +26,10 @@ class Events { } } + public static void initiliaze() { + obtainTelemetry(); + } + private static class EventsHolder { private static final Events INSTANCE = new Events(); } From 9459cf6a91f98266f77960a8cf328cb9c0ec3034 Mon Sep 17 00:00:00 2001 From: Jordan Kiley Date: Fri, 16 Feb 2018 16:20:08 -0800 Subject: [PATCH 047/264] [ios, macos] Mark deprecated methods as unavailable (#11205) Fixes #10735 --- platform/darwin/src/MGLAccountManager.h | 2 +- platform/darwin/src/MGLAccountManager.m | 5 -- platform/darwin/src/MGLOfflineStorage.h | 8 +- platform/darwin/src/MGLOfflineStorage.mm | 4 - platform/darwin/src/MGLStyle.h | 59 +++---------- platform/darwin/src/MGLStyle.mm | 83 +------------------ platform/darwin/test/MGLStyleTests.mm | 28 +------ platform/default/mbgl/util/default_styles.hpp | 3 - platform/ios/CHANGELOG.md | 1 + platform/ios/app/MBXViewController.m | 6 +- platform/ios/src/MGLMapView.h | 26 ++---- platform/ios/src/MGLMapView.mm | 45 ---------- platform/macos/CHANGELOG.md | 1 + platform/macos/app/Base.lproj/MainMenu.xib | 10 --- platform/macos/app/Base.lproj/MapDocument.xib | 17 ++-- platform/macos/app/MapDocument.m | 14 ---- 16 files changed, 35 insertions(+), 277 deletions(-) diff --git a/platform/darwin/src/MGLAccountManager.h b/platform/darwin/src/MGLAccountManager.h index 741cc323c..b691c4c90 100644 --- a/platform/darwin/src/MGLAccountManager.h +++ b/platform/darwin/src/MGLAccountManager.h @@ -44,7 +44,7 @@ MGL_EXPORT */ + (nullable NSString *)accessToken; -+ (BOOL)mapboxMetricsEnabledSettingShownInApp __attribute__((deprecated("Telemetry settings are now always shown in the ℹ️ menu."))); ++ (BOOL)mapboxMetricsEnabledSettingShownInApp __attribute__((unavailable("Telemetry settings are now always shown in the ℹ️ menu."))); @end diff --git a/platform/darwin/src/MGLAccountManager.m b/platform/darwin/src/MGLAccountManager.m index 729caeaa3..5bd96ce33 100644 --- a/platform/darwin/src/MGLAccountManager.m +++ b/platform/darwin/src/MGLAccountManager.m @@ -54,11 +54,6 @@ return _sharedManager; } -+ (BOOL)mapboxMetricsEnabledSettingShownInApp { - NSLog(@"mapboxMetricsEnabledSettingShownInApp is no longer necessary; the Mapbox Maps SDK for iOS has changed to always provide a telemetry setting in-app."); - return YES; -} - + (void)setAccessToken:(NSString *)accessToken { accessToken = [accessToken stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]]; diff --git a/platform/darwin/src/MGLOfflineStorage.h b/platform/darwin/src/MGLOfflineStorage.h index b009f893b..b3c9a0ede 100644 --- a/platform/darwin/src/MGLOfflineStorage.h +++ b/platform/darwin/src/MGLOfflineStorage.h @@ -70,7 +70,7 @@ typedef NSString *MGLOfflinePackUserInfoKey NS_EXTENSIBLE_STRING_ENUM; */ extern MGL_EXPORT const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyState; -extern MGL_EXPORT NSString * const MGLOfflinePackStateUserInfoKey __attribute__((deprecated("Use MGLOfflinePackUserInfoKeyState"))); +extern MGL_EXPORT NSString * const MGLOfflinePackStateUserInfoKey __attribute__((unavailable("Use MGLOfflinePackUserInfoKeyState"))); /** The key for an `NSValue` object that indicates an offline pack’s current @@ -81,7 +81,7 @@ extern MGL_EXPORT NSString * const MGLOfflinePackStateUserInfoKey __attribute__( */ extern MGL_EXPORT const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyProgress; -extern MGL_EXPORT NSString * const MGLOfflinePackProgressUserInfoKey __attribute__((deprecated("Use MGLOfflinePackUserInfoKeyProgress"))); +extern MGL_EXPORT NSString * const MGLOfflinePackProgressUserInfoKey __attribute__((unavailable("Use MGLOfflinePackUserInfoKeyProgress"))); /** The key for an `NSError` object that is encountered in the course of @@ -91,7 +91,7 @@ extern MGL_EXPORT NSString * const MGLOfflinePackProgressUserInfoKey __attribute */ extern MGL_EXPORT const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyError; -extern MGL_EXPORT NSString * const MGLOfflinePackErrorUserInfoKey __attribute__((deprecated("Use MGLOfflinePackUserInfoKeyError"))); +extern MGL_EXPORT NSString * const MGLOfflinePackErrorUserInfoKey __attribute__((unavailable("Use MGLOfflinePackUserInfoKeyError"))); /** The key for an `NSNumber` object that indicates the maximum number of @@ -103,7 +103,7 @@ extern MGL_EXPORT NSString * const MGLOfflinePackErrorUserInfoKey __attribute__( */ extern MGL_EXPORT const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyMaximumCount; -extern MGL_EXPORT NSString * const MGLOfflinePackMaximumCountUserInfoKey __attribute__((deprecated("Use MGLOfflinePackUserInfoKeyMaximumCount"))); +extern MGL_EXPORT NSString * const MGLOfflinePackMaximumCountUserInfoKey __attribute__((unavailable("Use MGLOfflinePackUserInfoKeyMaximumCount"))); /** A block to be called once an offline pack has been completely created and diff --git a/platform/darwin/src/MGLOfflineStorage.mm b/platform/darwin/src/MGLOfflineStorage.mm index 7085aa58e..4d999144e 100644 --- a/platform/darwin/src/MGLOfflineStorage.mm +++ b/platform/darwin/src/MGLOfflineStorage.mm @@ -26,13 +26,9 @@ const NSNotificationName MGLOfflinePackErrorNotification = @"MGLOfflinePackError const NSNotificationName MGLOfflinePackMaximumMapboxTilesReachedNotification = @"MGLOfflinePackMaximumMapboxTilesReached"; const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyState = @"State"; -NSString * const MGLOfflinePackStateUserInfoKey = MGLOfflinePackUserInfoKeyState; const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyProgress = @"Progress"; -NSString * const MGLOfflinePackProgressUserInfoKey = MGLOfflinePackUserInfoKeyProgress; const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyError = @"Error"; -NSString * const MGLOfflinePackErrorUserInfoKey = MGLOfflinePackUserInfoKeyError; const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyMaximumCount = @"MaximumCount"; -NSString * const MGLOfflinePackMaximumCountUserInfoKey = MGLOfflinePackUserInfoKeyMaximumCount; @interface MGLOfflineStorage () diff --git a/platform/darwin/src/MGLStyle.h b/platform/darwin/src/MGLStyle.h index 0b360de8f..eb7554534 100644 --- a/platform/darwin/src/MGLStyle.h +++ b/platform/darwin/src/MGLStyle.h @@ -83,13 +83,7 @@ MGL_EXPORT */ + (NSURL *)streetsStyleURLWithVersion:(NSInteger)version; -/** - Returns the URL to version 8 of the -
Mapbox Emerald style. - - Emerald is a tactile style with subtle textures and dramatic hillshading. - */ -+ (NSURL *)emeraldStyleURL __attribute__((deprecated("Create an NSURL object with the string “mapbox://styles/mapbox/emerald-v8”."))); ++ (NSURL *)emeraldStyleURL __attribute__((unavailable("Create an NSURL object with the string “mapbox://styles/mapbox/emerald-v8”."))); /** Returns the URL to the current version of the @@ -191,16 +185,8 @@ MGL_EXPORT */ + (NSURL *)satelliteStyleURLWithVersion:(NSInteger)version; -/** - Returns the URL to version 8 of the - Mapbox Satellite Streets - style. - Satellite Streets combines the high-resolution satellite and aerial imagery of - Mapbox Satellite with unobtrusive labels and translucent roads from Mapbox - Streets. - */ -+ (NSURL *)hybridStyleURL __attribute__((deprecated("Use -satelliteStreetsStyleURL."))); ++ (NSURL *)hybridStyleURL __attribute__((unavailable("Use -satelliteStreetsStyleURL."))); /** Returns the URL to the current version of the @@ -232,39 +218,14 @@ MGL_EXPORT */ + (NSURL *)satelliteStreetsStyleURLWithVersion:(NSInteger)version; -/** - Returns the URL to version 2 of the - Mapbox Traffic Day - style. - */ -+ (NSURL *)trafficDayStyleURL __attribute__((deprecated("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-day-v2”."))); ++ (NSURL *)trafficDayStyleURL __attribute__((unavailable("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-day-v2”."))); -/** - Returns the URL to the given version of the - Mapbox Traffic Day - style as of publication. - - @param version A specific version of the style. - */ -+ (NSURL *)trafficDayStyleURLWithVersion:(NSInteger)version __attribute__((deprecated("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-day-v2”.")));; ++ (NSURL *)trafficDayStyleURLWithVersion:(NSInteger)version __attribute__((unavailable("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-day-v2”.")));; -/** - Returns the URL to the version 2 of the - Mapbox Traffic Night - style. ++ (NSURL *)trafficNightStyleURL __attribute__((unavailable("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-night-v2”."))); - */ -+ (NSURL *)trafficNightStyleURL __attribute__((deprecated("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-night-v2”."))); - -/** - Returns the URL to to the version 2 of the - Mapbox Traffic Night - style as of publication. - - @param version A specific version of the style. - */ -+ (NSURL *)trafficNightStyleURLWithVersion:(NSInteger)version __attribute__((deprecated("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-night-v2”."))); ++ (NSURL *)trafficNightStyleURLWithVersion:(NSInteger)version __attribute__((unavailable("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-night-v2”."))); #pragma mark Accessing Metadata About the Style @@ -458,22 +419,22 @@ MGL_EXPORT /** Support for style classes has been removed. This property always returns an empty array. */ -@property (nonatomic) NS_ARRAY_OF(NSString *) *styleClasses __attribute__((deprecated("This property is non-functional."))); +@property (nonatomic) NS_ARRAY_OF(NSString *) *styleClasses __attribute__((unavailable("This property is non-functional."))); /** Support for style classes has been removed. This method always returns NO. */ -- (BOOL)hasStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional."))); +- (BOOL)hasStyleClass:(NSString *)styleClass __attribute__((unavailable("This method is non-functional."))); /** Support for style classes has been removed. This method is a no-op. */ -- (void)addStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional."))); +- (void)addStyleClass:(NSString *)styleClass __attribute__((unavailable("This method is non-functional."))); /** Support for style classes has been removed. This method is a no-op. */ -- (void)removeStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional."))); +- (void)removeStyleClass:(NSString *)styleClass __attribute__((unavailable("This method is non-functional."))); #pragma mark Managing a Style’s Images diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm index f6fc5533b..199f6c2e0 100644 --- a/platform/darwin/src/MGLStyle.mm +++ b/platform/darwin/src/MGLStyle.mm @@ -116,58 +116,9 @@ MGL_DEFINE_STYLE(satelliteStreets, satellite-streets) // Make sure all the styles listed in mbgl::util::default_styles::orderedStyles // are defined above and also declared in MGLStyle.h. -static_assert(8 == mbgl::util::default_styles::numOrderedStyles, +static_assert(6 == mbgl::util::default_styles::numOrderedStyles, "mbgl::util::default_styles::orderedStyles and MGLStyle have different numbers of styles."); -// Hybrid has been renamed Satellite Streets, so the last Hybrid version is hard-coded here. -static NSURL *MGLStyleURL_hybrid; -+ (NSURL *)hybridStyleURL { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - MGLStyleURL_hybrid = [NSURL URLWithString:@"mapbox://styles/mapbox/satellite-hybrid-v8"]; - }); - return MGLStyleURL_hybrid; -} - -// Emerald is no longer getting new versions as a default style, so the current version is hard-coded here. -static NSURL *MGLStyleURL_emerald; -+ (NSURL *)emeraldStyleURL { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - MGLStyleURL_emerald = [NSURL URLWithString:@"mapbox://styles/mapbox/emerald-v8"]; - }); - return MGLStyleURL_emerald; -} - -// Traffic Day is no longer getting new versions as a default style, so the current version is hard-coded here. -static NSURL *MGLStyleURL_trafficDay; -+ (NSURL *)trafficDayStyleURL { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - MGLStyleURL_trafficDay = [NSURL URLWithString:@"mapbox://styles/mapbox/traffic-day-v2"]; - }); - return MGLStyleURL_trafficDay; -} - -+ (NSURL *)trafficDayStyleURLWithVersion:(NSInteger)version { - return [NSURL URLWithString:[@"mapbox://styles/mapbox/traffic-day-v" stringByAppendingFormat:@"%li", (long)version]]; -} - -// Traffic Night is no longer getting new versions as a default style, so the current version is hard-coded here. -static NSURL *MGLStyleURL_trafficNight; -+ (NSURL *)trafficNightStyleURL { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - MGLStyleURL_trafficNight = [NSURL URLWithString:@"mapbox://styles/mapbox/traffic-night-v2"]; - }); - return MGLStyleURL_trafficNight; -} - -+ (NSURL *)trafficNightStyleURLWithVersion:(NSInteger)version { - return [NSURL URLWithString:[@"mapbox://styles/mapbox/traffic-night-v" stringByAppendingFormat:@"%li", (long)version]]; -} - - #pragma mark - - (instancetype)initWithRawStyle:(mbgl::style::Style *)rawStyle mapView:(MGLMapView *)mapView { @@ -534,38 +485,6 @@ static NSURL *MGLStyleURL_trafficNight; [self didChangeValueForKey:@"layers"]; } -#pragma mark Style classes - -- (NS_ARRAY_OF(NSString *) *)styleClasses -{ - return @[]; -} - -- (void)setStyleClasses:(NS_ARRAY_OF(NSString *) *)appliedClasses -{ -} - -- (void)setStyleClasses:(NS_ARRAY_OF(NSString *) *)appliedClasses transitionDuration:(NSTimeInterval)transitionDuration -{ -} - -- (NSUInteger)countOfStyleClasses { - return 0; -} - -- (BOOL)hasStyleClass:(NSString *)styleClass -{ - return NO; -} - -- (void)addStyleClass:(NSString *)styleClass -{ -} - -- (void)removeStyleClass:(NSString *)styleClass -{ -} - #pragma mark Style images - (void)setImage:(MGLImage *)image forName:(NSString *)name diff --git a/platform/darwin/test/MGLStyleTests.mm b/platform/darwin/test/MGLStyleTests.mm index 8f610e338..95051cfae 100644 --- a/platform/darwin/test/MGLStyleTests.mm +++ b/platform/darwin/test/MGLStyleTests.mm @@ -64,12 +64,6 @@ XCTAssertEqualObjects([MGLStyle darkStyleURL].absoluteString, @(mbgl::util::default_styles::dark.url)); XCTAssertEqualObjects([MGLStyle satelliteStyleURL].absoluteString, @(mbgl::util::default_styles::satellite.url)); XCTAssertEqualObjects([MGLStyle satelliteStreetsStyleURL].absoluteString, @(mbgl::util::default_styles::satelliteStreets.url)); - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - XCTAssertEqualObjects([MGLStyle emeraldStyleURL].absoluteString, @"mapbox://styles/mapbox/emerald-v8"); - XCTAssertEqualObjects([MGLStyle hybridStyleURL].absoluteString, @"mapbox://styles/mapbox/satellite-hybrid-v8"); -#pragma clang diagnostic pop } - (void)testVersionedStyleURLs { @@ -99,19 +93,8 @@ @(mbgl::util::default_styles::satelliteStreets.url)); XCTAssertEqualObjects([MGLStyle satelliteStreetsStyleURLWithVersion:99].absoluteString, @"mapbox://styles/mapbox/satellite-streets-v99"); -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - XCTAssertEqualObjects([MGLStyle trafficDayStyleURLWithVersion:mbgl::util::default_styles::trafficDay.currentVersion].absoluteString, - @(mbgl::util::default_styles::trafficDay.url)); - XCTAssertEqualObjects([MGLStyle trafficDayStyleURLWithVersion:99].absoluteString, - @"mapbox://styles/mapbox/traffic-day-v99"); - XCTAssertEqualObjects([MGLStyle trafficNightStyleURLWithVersion:mbgl::util::default_styles::trafficNight.currentVersion].absoluteString, - @(mbgl::util::default_styles::trafficNight.url)); - XCTAssertEqualObjects([MGLStyle trafficNightStyleURLWithVersion:99].absoluteString, - @"mapbox://styles/mapbox/traffic-night-v99"); -#pragma clang diagnostic pop - static_assert(8 == mbgl::util::default_styles::numOrderedStyles, + static_assert(6 == mbgl::util::default_styles::numOrderedStyles, "MGLStyleTests isn’t testing all the styles in mbgl::util::default_styles."); } @@ -143,7 +126,7 @@ NSString *styleHeader = self.stringWithContentsOfStyleHeader; NSError *versionedMethodError; - NSString *versionedMethodExpressionString = @(R"RE(^\+\s*\(NSURL\s*\*\s*\)\s*\w+StyleURLWithVersion\s*:\s*\(\s*NSInteger\s*\)\s*version\s*\b)RE"); + NSString *versionedMethodExpressionString = @(R"RE(^\+\s*\(NSURL\s*\*\s*\)\s*(?!traffic)\w+StyleURLWithVersion\s*:\s*\(\s*NSInteger\s*\)\s*version\s*\b)RE"); NSRegularExpression *versionedMethodExpression = [NSRegularExpression regularExpressionWithPattern:versionedMethodExpressionString options:NSRegularExpressionAnchorsMatchLines error:&versionedMethodError]; XCTAssertNil(versionedMethodError, @"Error compiling regular expression to search for versioned methods."); NSUInteger numVersionedMethodDeclarations = [versionedMethodExpression numberOfMatchesInString:styleHeader options:0 range:NSMakeRange(0, styleHeader.length)]; @@ -383,13 +366,6 @@ return styleHeader; } -- (void)testClasses { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - XCTAssertEqual(self.style.styleClasses.count, 0); -#pragma clang diagnostic pop -} - - (void)testImages { NSString *imageName = @"TrackingLocationMask"; #if TARGET_OS_IPHONE diff --git a/platform/default/mbgl/util/default_styles.hpp b/platform/default/mbgl/util/default_styles.hpp index 43dafb808..13f08252a 100644 --- a/platform/default/mbgl/util/default_styles.hpp +++ b/platform/default/mbgl/util/default_styles.hpp @@ -19,12 +19,9 @@ constexpr const DefaultStyle light = { "mapbox://styles/mapbox/light- constexpr const DefaultStyle dark = { "mapbox://styles/mapbox/dark-v9", "Dark", 9 }; constexpr const DefaultStyle satellite = { "mapbox://styles/mapbox/satellite-v9", "Satellite", 9 }; constexpr const DefaultStyle satelliteStreets = { "mapbox://styles/mapbox/satellite-streets-v10", "Satellite Streets", 10 }; -constexpr const DefaultStyle trafficDay = { "mapbox://styles/mapbox/traffic-day-v2", "Traffic Day", 2 }; -constexpr const DefaultStyle trafficNight = { "mapbox://styles/mapbox/traffic-night-v2", "Traffic Night", 2 }; const DefaultStyle orderedStyles[] = { streets, outdoors, light, dark, satellite, satelliteStreets, - trafficDay, trafficNight, }; const size_t numOrderedStyles = sizeof(orderedStyles) / sizeof(DefaultStyle); diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index cadbe9dd3..c11fc63eb 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -8,6 +8,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Removed support for 32-bit simulators. ([#10962](https://github.com/mapbox/mapbox-gl-native/pull/10962)) * Added Danish and Hebrew localizations. ([#10967](https://github.com/mapbox/mapbox-gl-native/pull/10967), [#11136](https://github.com/mapbox/mapbox-gl-native/pull/11134)) +* Removed methods, properties, and constants that had been deprecated as of v3.7.4. ([#11205](https://github.com/mapbox/mapbox-gl-native/pull/11205)) ### Styles and rendering diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m index 282dc901d..7c8e9fe57 100644 --- a/platform/ios/app/MBXViewController.m +++ b/platform/ios/app/MBXViewController.m @@ -1600,8 +1600,6 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { @"Dark", @"Satellite", @"Satellite Streets", - @"Traffic Day", - @"Traffic Night", ]; styleURLs = @[ [MGLStyle streetsStyleURL], @@ -1609,9 +1607,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { [MGLStyle lightStyleURL], [MGLStyle darkStyleURL], [MGLStyle satelliteStyleURL], - [MGLStyle satelliteStreetsStyleURL], - [NSURL URLWithString:@"mapbox://styles/mapbox/traffic-day-v2"], - [NSURL URLWithString:@"mapbox://styles/mapbox/traffic-night-v2"], + [MGLStyle satelliteStreetsStyleURL] ]; NSAssert(styleNames.count == styleURLs.count, @"Style names and URLs don’t match."); diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h index 3c5aa2c12..a4dcc4cdb 100644 --- a/platform/ios/src/MGLMapView.h +++ b/platform/ios/src/MGLMapView.h @@ -189,13 +189,7 @@ MGL_EXPORT IB_DESIGNABLE */ @property (nonatomic, readonly, nullable) MGLStyle *style; -/** - URLs of the styles bundled with the library. - - @deprecated Call the relevant class method of `MGLStyle` for the URL of a - particular default style. - */ -@property (nonatomic, readonly) NS_ARRAY_OF(NSURL *) *bundledStyleURLs __attribute__((deprecated("Call the relevant class method of MGLStyle for the URL of a particular default style."))); +@property (nonatomic, readonly) NS_ARRAY_OF(NSURL *) *bundledStyleURLs __attribute__((unavailable("Call the relevant class method of MGLStyle for the URL of a particular default style."))); /** URL of the style currently displayed in the receiver. @@ -283,17 +277,13 @@ MGL_EXPORT IB_DESIGNABLE */ - (IBAction)showAttribution:(id)sender; -/// :nodoc: Support for style classes has been removed. This property always returns an empty array. -@property (nonatomic) NS_ARRAY_OF(NSString *) *styleClasses __attribute__((deprecated("This property is non-functional."))); +@property (nonatomic) NS_ARRAY_OF(NSString *) *styleClasses __attribute__((unavailable("This property is non-functional."))); -/// :nodoc: Support for style classes has been removed. This property always returns NO. -- (BOOL)hasStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional."))); +- (BOOL)hasStyleClass:(NSString *)styleClass __attribute__((unavailable("This method is non-functional."))); -/// :nodoc: Support for style classes has been removed. This property is a no-op. -- (void)addStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional."))); +- (void)addStyleClass:(NSString *)styleClass __attribute__((unavailable("This method is non-functional."))); -/// :nodoc: Support for style classes has been removed. This property is a no-op. -- (void)removeStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional."))); +- (void)removeStyleClass:(NSString *)styleClass __attribute__((unavailable("This method is non-functional."))); #pragma mark Displaying the User’s Location @@ -1462,11 +1452,11 @@ MGL_EXPORT IB_DESIGNABLE */ @property (nonatomic) MGLMapDebugMaskOptions debugMask; -@property (nonatomic, getter=isDebugActive) BOOL debugActive __attribute__((deprecated("Use -debugMask and -setDebugMask:."))); +@property (nonatomic, getter=isDebugActive) BOOL debugActive __attribute__((unavailable("Use -debugMask and -setDebugMask:."))); -- (void)toggleDebug __attribute__((deprecated("Use -setDebugMask:."))); +- (void)toggleDebug __attribute__((unavailable("Use -setDebugMask:."))); -- (void)emptyMemoryCache __attribute__((deprecated)); +- (void)emptyMemoryCache __attribute__((unavailable)); @end diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 79b02bef3..72fa4613f 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -2288,16 +2288,6 @@ public: MGLMapDebugCollisionBoxesMask) : 0; } -- (BOOL)isDebugActive -{ - return self.debugMask; -} - -- (void)toggleDebug -{ - self.debugActive = !self.debugActive; -} - - (void)resetNorth { [self resetNorthAnimated:YES]; @@ -2320,11 +2310,6 @@ public: heading:heading]; } -- (void)emptyMemoryCache -{ - _rendererFrontend->reduceMemoryUse(); -} - - (void)setZoomEnabled:(BOOL)zoomEnabled { _zoomEnabled = zoomEnabled; @@ -3519,36 +3504,6 @@ public: __PRETTY_FUNCTION__, styleID]; } -- (NS_ARRAY_OF(NSString *) *)styleClasses -{ - return [self.style styleClasses]; -} - -- (void)setStyleClasses:(NS_ARRAY_OF(NSString *) *)appliedClasses -{ - [self setStyleClasses:appliedClasses transitionDuration:0]; -} - -- (void)setStyleClasses:(NS_ARRAY_OF(NSString *) *)appliedClasses transitionDuration:(NSTimeInterval)transitionDuration -{ - [self.style setStyleClasses:appliedClasses transitionDuration:transitionDuration]; -} - -- (BOOL)hasStyleClass:(NSString *)styleClass -{ - return [self.style hasStyleClass:styleClass]; -} - -- (void)addStyleClass:(NSString *)styleClass -{ - [self.style addStyleClass:styleClass]; -} - -- (void)removeStyleClass:(NSString *)styleClass -{ - [self.style removeStyleClass:styleClass]; -} - #pragma mark - Annotations - - (nullable NS_ARRAY_OF(id ) *)annotations diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 316e09b5a..06fedf7cc 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -31,6 +31,7 @@ * Added Danish and Hebrew localizations. ([#10967](https://github.com/mapbox/mapbox-gl-native/pull/10967), [#11136](https://github.com/mapbox/mapbox-gl-native/pull/11134)) * Feature querying results now account for the `MGLSymbolStyleLayer.circleStrokeWidth` property. ([#10897](https://github.com/mapbox/mapbox-gl-native/pull/10897)) +* Removed methods, properties, and constants that had been deprecated as of v0.6.1. ([#11205](https://github.com/mapbox/mapbox-gl-native/pull/11205)) ## v0.6.1 - January 16, 2018 diff --git a/platform/macos/app/Base.lproj/MainMenu.xib b/platform/macos/app/Base.lproj/MainMenu.xib index 72e9c5a18..4cf8d8765 100644 --- a/platform/macos/app/Base.lproj/MainMenu.xib +++ b/platform/macos/app/Base.lproj/MainMenu.xib @@ -402,16 +402,6 @@ - - - - - - - - - - diff --git a/platform/macos/app/Base.lproj/MapDocument.xib b/platform/macos/app/Base.lproj/MapDocument.xib index 0394f3853..5d0525a29 100644 --- a/platform/macos/app/Base.lproj/MapDocument.xib +++ b/platform/macos/app/Base.lproj/MapDocument.xib @@ -1,7 +1,8 @@ - + - + + @@ -48,7 +49,7 @@ - + @@ -70,7 +71,7 @@ - + @@ -87,7 +88,7 @@ - + @@ -204,12 +205,6 @@ - - - - - - diff --git a/platform/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m index 7d39f9334..03557caca 100644 --- a/platform/macos/app/MapDocument.m +++ b/platform/macos/app/MapDocument.m @@ -265,12 +265,6 @@ NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id Date: Mon, 19 Feb 2018 10:25:11 -0500 Subject: [PATCH 048/264] [android] incorrect latlngBounds in the VisibleRegion with map is rotated smallest bounding box for 4 points cannot (#11226) be created using LatLngBounds.fromLatLngs() as the order matters in that method and that does not work for rotated map --- .../java/com/mapbox/mapboxsdk/maps/Projection.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java index 16c73b1ca..ae559189a 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java @@ -104,11 +104,12 @@ public class Projection { LatLng bottomLeft = fromScreenLocation(new PointF(left, bottom)); return new VisibleRegion(topLeft, topRight, bottomLeft, bottomRight, - LatLngBounds.from( - topRight.getLatitude(), - topRight.getLongitude(), - bottomLeft.getLatitude(), - bottomLeft.getLongitude()) + new LatLngBounds.Builder() + .include(topRight) + .include(bottomLeft) + .include(bottomRight) + .include(topLeft) + .build() ); } From dd822e8fde1cc18a0cac999d4e5f879129383981 Mon Sep 17 00:00:00 2001 From: Osana Babayan <32496536+osana@users.noreply.github.com> Date: Tue, 20 Feb 2018 09:23:24 -0500 Subject: [PATCH 049/264] [android] missing DeleteLocalRef --- .../src/geojson/feature_collection.cpp | 24 ++++++++++-------- platform/android/src/geojson/line_string.cpp | 3 ++- .../android/src/geojson/multi_polygon.cpp | 2 +- platform/android/src/geojson/point.cpp | 25 ++++++++++++------- 4 files changed, 33 insertions(+), 21 deletions(-) diff --git a/platform/android/src/geojson/feature_collection.cpp b/platform/android/src/geojson/feature_collection.cpp index 59f1e317e..18a41d48f 100644 --- a/platform/android/src/geojson/feature_collection.cpp +++ b/platform/android/src/geojson/feature_collection.cpp @@ -7,19 +7,23 @@ namespace android { namespace geojson { mbgl::FeatureCollection FeatureCollection::convert(jni::JNIEnv& env, jni::Object jCollection) { - auto jFeatureList = FeatureCollection::features(env, jCollection); - auto jFeatures = java::util::List::toArray(env, jFeatureList); - auto size = size_t(jFeatures.Length(env)); - auto collection = mbgl::FeatureCollection(); - collection.reserve(size); - for (size_t i = 0; i < size; i++) { - auto jFeature = jFeatures.Get(env, i); - collection.push_back(Feature::convert(env, jFeature)); - jni::DeleteLocalRef(env, jFeature); + if (jCollection) { + auto jFeatureList = FeatureCollection::features(env, jCollection); + auto jFeatures = java::util::List::toArray(env, jFeatureList); + auto size = size_t(jFeatures.Length(env)); + collection.reserve(size); + + for (size_t i = 0; i < size; i++) { + auto jFeature = jFeatures.Get(env, i); + collection.push_back(Feature::convert(env, jFeature)); + jni::DeleteLocalRef(env, jFeature); + } + + jni::DeleteLocalRef(env, jFeatures); + jni::DeleteLocalRef(env, jFeatureList); } - return collection; } diff --git a/platform/android/src/geojson/line_string.cpp b/platform/android/src/geojson/line_string.cpp index 9e99c72c4..8eebd5355 100644 --- a/platform/android/src/geojson/line_string.cpp +++ b/platform/android/src/geojson/line_string.cpp @@ -23,8 +23,9 @@ mapbox::geojson::line_string LineString::convert(jni::JNIEnv &env, jni::Object(env, jPointList); - auto size = jPointArray.Length(env); + lineString.reserve(size); + for (std::size_t i = 0; i < size; i++) { auto jPoint = jPointArray.Get(env, i); lineString.push_back(Point::convert(env, jPoint)); diff --git a/platform/android/src/geojson/multi_polygon.cpp b/platform/android/src/geojson/multi_polygon.cpp index f4eb0f6b2..aadba8c8a 100644 --- a/platform/android/src/geojson/multi_polygon.cpp +++ b/platform/android/src/geojson/multi_polygon.cpp @@ -22,8 +22,8 @@ mapbox::geojson::multi_polygon MultiPolygon::convert(jni::JNIEnv &env, jni::Obje jni::DeleteLocalRef(env, jPositionListsList); } - jni::DeleteLocalRef(env, jPointListsListList); jni::DeleteLocalRef(env, jPointListsListArray); + jni::DeleteLocalRef(env, jPointListsListList); } return multiPolygon; diff --git a/platform/android/src/geojson/point.cpp b/platform/android/src/geojson/point.cpp index 5feb1b852..e95376cd2 100644 --- a/platform/android/src/geojson/point.cpp +++ b/platform/android/src/geojson/point.cpp @@ -1,6 +1,7 @@ #include "point.hpp" #include "../java/util.hpp" #include "../java_types.hpp" +#include "../style/value.hpp" namespace mbgl { namespace android { @@ -19,17 +20,23 @@ mapbox::geojson::point Point::convert(jni::JNIEnv &env, jni::Object jPoin } mapbox::geojson::point Point::convert(jni::JNIEnv &env, jni::Object*/> jDoubleList) { - auto jDoubleArray = java::util::List::toArray(env, jDoubleList); + mapbox::geojson::point point; - jni::jdouble lon = jni::CallMethod(env, - jDoubleArray.Get(env, 0), - *java::Number::doubleValueMethodId); - jni::jdouble lat = jni::CallMethod(env, - jDoubleArray.Get(env, 1), - *java::Number::doubleValueMethodId); - mapbox::geojson::point point(lon, lat); - jni::DeleteLocalRef(env, jDoubleArray); + if (jDoubleList) { + auto jDoubleArray = java::util::List::toArray(env, jDoubleList); + auto lonObject = jDoubleArray.Get(env, 0); + auto latObject = jDoubleArray.Get(env, 1); + + point.x = jni::CallMethod(env, lonObject, + *java::Number::doubleValueMethodId); + point.y = jni::CallMethod(env, latObject, + *java::Number::doubleValueMethodId); + + jni::DeleteLocalRef(env, lonObject); + jni::DeleteLocalRef(env, latObject); + jni::DeleteLocalRef(env, jDoubleArray); + } return point; } From 2e0377b02eb5f590fcd76b4b791850d2f3c25ac1 Mon Sep 17 00:00:00 2001 From: Langston Smith Date: Tue, 20 Feb 2018 08:28:56 -0600 Subject: [PATCH 050/264] method name typo fix (#11245) --- .../src/main/java/com/mapbox/mapboxsdk/Mapbox.java | 2 +- .../src/main/java/com/mapbox/mapboxsdk/maps/Events.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java index e963993ae..db89f5efb 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java @@ -53,7 +53,7 @@ public final class Mapbox { INSTANCE = new Mapbox(appContext, accessToken, locationEngine); locationEngine.setPriority(LocationEnginePriority.NO_POWER); - Events.initiliaze(); + Events.initialize(); ConnectivityReceiver.instance(appContext); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Events.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Events.java index a4f6b233c..7ba592f12 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Events.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Events.java @@ -26,7 +26,7 @@ public class Events { } } - public static void initiliaze() { + public static void initialize() { obtainTelemetry(); } From 2e3145db475e296552db9d1c65c483b3f51b5237 Mon Sep 17 00:00:00 2001 From: Jesse Bounds Date: Tue, 20 Feb 2018 06:54:11 -0800 Subject: [PATCH 051/264] Replace embedded telem implementation with mapbox-mobile-events library (#10698) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [ios] Introduce mobile events dependency This adds the mobile events dependency including the events library's namespaced header file so that the related compiled symbols are prefixed to make them specific to the maps SDK. A pre-compiled header file is added so that the namespaced header file is added in all places where it is needed (most importantly every events library file). * [ios] Use new events library for telemetry events This makes the following significant changes: Refactors the MGLMapboxEvents singleton to delegate internally to an instance of MMEEventsManager that it keeps for itself. The MGLMapboxEvents public API is refactored to reflect this. Note: MGLMapboxEvents continues to handle checking for opt out Uses that new events API in MGLMapboxEvents to send all telem events (and turnstile) All embedded certs are removed since the new telem library uses public key info pinning Legacy telemetry utility classes for location and networking are removed since those are implemented in the telem library * [ios] Update submodules initialized by cmake We no longer bring in SMCalloutView as a submodule so it is removed. We now vendor the telemetry events library as a submodule so it is added. * Update mapbox-mobile-events * Update mobile events lib * Set events options with MGL user defaults values For options that historically could be set with user defaults in the Maps SDK, collect them and set the appropriate property values in the new events library. Also, check the existence of two new optional configuration values MGLTelemetryAccessToken and MGLTelemetryBaseURL so that the events endpoint and access token can be configured and the access token can easily be set dynamically and apart from the access token for the maps API. MGLTelemetryBaseURL replaces the legacy MGLTelemetryTestServerURL. This is ok because this value was never intended to be used by client applications. The new name better reflects the fact that the URL can be changed to any backend stack, not just a test server. Because it is possible for the configuration values to be read from user defaults before the events manager is fully set up, this also adds a local cache for the baseURL and accessToken values so that they can be stored and applied once it is time to setup the the events manager. * Update mapbox-mobile-events mapbox-mobile-events was forced pushed to include https://github.com/mapbox/mapbox-events-ios/pull/28 * Rename MGLMapboxEvents singleton access method Manager is not in the name of the class. Just call it an instance. * Update mapbox-mobile-events * Add comments about config value loading * Guard against creating events for IB * Refactor user defaults update handler This updates the handling logic for user defaults to break apart config changes that require a check for pausing or resuming the events lib’s telemetry collection from config changes that can happen with no update to the pause/resume state. It also ports the optimization from https://github.com/mapbox/mapbox-gl-native/pull/10803 so that the call to pause and resume is not performed unless the user defaults store has new values that have not yet been applied to the events library. * Update mapbox-mobile-events * Rename events submodule folder * Fix incorrect events library refs * Update mapbox-mobile-events * Update user user agent id value This value aligns with our schme of {source-sdk-platform} and will be used by the events library in the user agent and vendorid. * Update mapbox-mobile-events Pin to v3.0.0 (d522b18) * Improve code comments * [ios] Fixed a broken file reference to MGLTelemetryConfig. --- .gitmodules | 4 + cmake/mbgl.cmake | 2 +- platform/darwin/src/MGLAccountManager.m | 4 +- platform/ios/ios.xcodeproj/project.pbxproj | 413 +++++++-- .../api_mapbox_com-digicert_2016.der | Bin 1913 -> 0 bytes .../api_mapbox_com-digicert_2017.der | Bin 2030 -> 0 bytes .../api_mapbox_com-geotrust_2016.der | Bin 1757 -> 0 bytes .../api_mapbox_com-geotrust_2017.der | Bin 1758 -> 0 bytes platform/ios/resources/api_mapbox_staging.der | Bin 1334 -> 0 bytes platform/ios/src/MGLAPIClient.h | 15 - platform/ios/src/MGLAPIClient.m | 202 ----- platform/ios/src/MGLLocationManager.h | 25 - platform/ios/src/MGLLocationManager.m | 175 ---- platform/ios/src/MGLMapView.mm | 41 +- platform/ios/src/MGLMapboxEvents.h | 39 +- platform/ios/src/MGLMapboxEvents.m | 839 +++--------------- platform/ios/src/Mapbox-Prefix.pch | 1 + platform/ios/vendor/mapbox-events-ios | 1 + 18 files changed, 495 insertions(+), 1266 deletions(-) delete mode 100644 platform/ios/resources/api_mapbox_com-digicert_2016.der delete mode 100644 platform/ios/resources/api_mapbox_com-digicert_2017.der delete mode 100644 platform/ios/resources/api_mapbox_com-geotrust_2016.der delete mode 100644 platform/ios/resources/api_mapbox_com-geotrust_2017.der delete mode 100644 platform/ios/resources/api_mapbox_staging.der delete mode 100644 platform/ios/src/MGLAPIClient.h delete mode 100644 platform/ios/src/MGLAPIClient.m delete mode 100644 platform/ios/src/MGLLocationManager.h delete mode 100644 platform/ios/src/MGLLocationManager.m create mode 100644 platform/ios/src/Mapbox-Prefix.pch create mode 160000 platform/ios/vendor/mapbox-events-ios diff --git a/.gitmodules b/.gitmodules index 422fc3930..e5be61a92 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,7 @@ [submodule "mapbox-gl-js"] path = mapbox-gl-js url = https://github.com/mapbox/mapbox-gl-js.git +[submodule "platform/ios/vendor/mapbox-events-ios"] + path = platform/ios/vendor/mapbox-events-ios + url = https://github.com/mapbox/mapbox-events-ios.git + branch = boundsj-add-namespace-header diff --git a/cmake/mbgl.cmake b/cmake/mbgl.cmake index 3d8289953..f087c3251 100644 --- a/cmake/mbgl.cmake +++ b/cmake/mbgl.cmake @@ -110,7 +110,7 @@ endfunction() if(MBGL_PLATFORM STREQUAL "ios") execute_process( - COMMAND git submodule update --init platform/ios/vendor/SMCalloutView platform/ios/uitest/KIF platform/ios/uitest/OHHTTPStubs + COMMAND git submodule update --init platform/ios/vendor/mapbox-events-ios platform/ios/uitest/KIF platform/ios/uitest/OHHTTPStubs WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") endif() diff --git a/platform/darwin/src/MGLAccountManager.m b/platform/darwin/src/MGLAccountManager.m index 5bd96ce33..73da8ddd6 100644 --- a/platform/darwin/src/MGLAccountManager.m +++ b/platform/darwin/src/MGLAccountManager.m @@ -64,9 +64,7 @@ [MGLAccountManager sharedManager].accessToken = accessToken; #if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR - // Update MGLMapboxEvents - // NOTE: This is (likely) the initial setup of MGLMapboxEvents - [MGLMapboxEvents sharedManager]; + [MGLMapboxEvents setupWithAccessToken:accessToken]; #endif } diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index f66aa7496..f19047cdc 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -187,13 +187,77 @@ 404C26E51D89B877000AA13D /* MGLTileSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 404C26E11D89B877000AA13D /* MGLTileSource.mm */; }; 404C26E71D89C55D000AA13D /* MGLTileSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 404C26E61D89C515000AA13D /* MGLTileSource_Private.h */; }; 404C26E81D89C55D000AA13D /* MGLTileSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 404C26E61D89C515000AA13D /* MGLTileSource_Private.h */; }; - 40599F0C1DEE1B7600182B5D /* api_mapbox_staging.der in Resources */ = {isa = PBXBuildFile; fileRef = 40599F001DEE1B2400182B5D /* api_mapbox_staging.der */; }; - 40599F0D1DEE1B7A00182B5D /* api_mapbox_com-digicert_2016.der in Resources */ = {isa = PBXBuildFile; fileRef = 40599F011DEE1B2400182B5D /* api_mapbox_com-digicert_2016.der */; }; - 40599F0E1DEE1B7E00182B5D /* api_mapbox_com-geotrust_2016.der in Resources */ = {isa = PBXBuildFile; fileRef = 40599F021DEE1B2400182B5D /* api_mapbox_com-geotrust_2016.der */; }; + 406E99B91FFEFF1B00D9FFCC /* MMEEventLogReportViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 406E99B11FFEFED500D9FFCC /* MMEEventLogReportViewController.m */; }; + 406E99BA1FFEFF1B00D9FFCC /* MMEEventLogReportViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 406E99B11FFEFED500D9FFCC /* MMEEventLogReportViewController.m */; }; + 406E99BB1FFF006C00D9FFCC /* MMEUINavigation.m in Sources */ = {isa = PBXBuildFile; fileRef = 406E99B21FFEFED500D9FFCC /* MMEUINavigation.m */; }; + 406E99BC1FFF006D00D9FFCC /* MMEUINavigation.m in Sources */ = {isa = PBXBuildFile; fileRef = 406E99B21FFEFED500D9FFCC /* MMEUINavigation.m */; }; + 40834BE61FE05E1800C1BD0D /* CLLocation+MMEMobileEvents.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BC31FE05D6F00C1BD0D /* CLLocation+MMEMobileEvents.m */; }; + 40834BE71FE05E1800C1BD0D /* MMEAPIClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BA51FE05D6B00C1BD0D /* MMEAPIClient.m */; }; + 40834BE81FE05E1800C1BD0D /* MMECategoryLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BC41FE05D6F00C1BD0D /* MMECategoryLoader.m */; }; + 40834BE91FE05E1800C1BD0D /* MMECommonEventData.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BCE1FE05D7100C1BD0D /* MMECommonEventData.m */; }; + 40834BEA1FE05E1800C1BD0D /* MMEConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BC01FE05D6E00C1BD0D /* MMEConstants.m */; }; + 40834BEB1FE05E1800C1BD0D /* MMEDependencyManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BB41FE05D6D00C1BD0D /* MMEDependencyManager.m */; }; + 40834BEC1FE05E1800C1BD0D /* MMEEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BC71FE05D7000C1BD0D /* MMEEvent.m */; }; + 40834BED1FE05E1800C1BD0D /* MMEEventLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BB71FE05D6D00C1BD0D /* MMEEventLogger.m */; }; + 40834BEE1FE05E1800C1BD0D /* MMEEventsConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BB21FE05D6D00C1BD0D /* MMEEventsConfiguration.m */; }; + 40834BEF1FE05E1800C1BD0D /* MMEEventsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BA41FE05D6B00C1BD0D /* MMEEventsManager.m */; }; + 40834BF01FE05E1800C1BD0D /* MMELocationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BB81FE05D6D00C1BD0D /* MMELocationManager.m */; }; + 40834BF11FE05E1800C1BD0D /* MMENSDateWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BBC1FE05D6E00C1BD0D /* MMENSDateWrapper.m */; }; + 40834BF21FE05E1800C1BD0D /* MMENSURLSessionWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BC61FE05D7000C1BD0D /* MMENSURLSessionWrapper.m */; }; + 40834BF31FE05E1800C1BD0D /* MMETimerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BB91FE05D6E00C1BD0D /* MMETimerManager.m */; }; + 40834BF41FE05E1800C1BD0D /* MMETrustKitWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BC11FE05D6F00C1BD0D /* MMETrustKitWrapper.m */; }; + 40834BF51FE05E1800C1BD0D /* MMETypes.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BBD1FE05D6E00C1BD0D /* MMETypes.m */; }; + 40834BF61FE05E1800C1BD0D /* MMEUIApplicationWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BCA1FE05D7000C1BD0D /* MMEUIApplicationWrapper.m */; }; + 40834BF71FE05E1800C1BD0D /* MMEUniqueIdentifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BAD1FE05D6C00C1BD0D /* MMEUniqueIdentifier.m */; }; + 40834BF81FE05E1800C1BD0D /* NSData+MMEGZIP.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BAF1FE05D6C00C1BD0D /* NSData+MMEGZIP.m */; }; + 40834BF91FE05E1800C1BD0D /* MMEReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BCD1FE05D7100C1BD0D /* MMEReachability.m */; }; + 40834BFA1FE05E1800C1BD0D /* CLLocation+MMEMobileEvents.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BC31FE05D6F00C1BD0D /* CLLocation+MMEMobileEvents.m */; }; + 40834BFB1FE05E1800C1BD0D /* MMEAPIClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BA51FE05D6B00C1BD0D /* MMEAPIClient.m */; }; + 40834BFC1FE05E1800C1BD0D /* MMECategoryLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BC41FE05D6F00C1BD0D /* MMECategoryLoader.m */; }; + 40834BFD1FE05E1800C1BD0D /* MMECommonEventData.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BCE1FE05D7100C1BD0D /* MMECommonEventData.m */; }; + 40834BFE1FE05E1800C1BD0D /* MMEConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BC01FE05D6E00C1BD0D /* MMEConstants.m */; }; + 40834BFF1FE05E1800C1BD0D /* MMEDependencyManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BB41FE05D6D00C1BD0D /* MMEDependencyManager.m */; }; + 40834C001FE05E1800C1BD0D /* MMEEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BC71FE05D7000C1BD0D /* MMEEvent.m */; }; + 40834C011FE05E1800C1BD0D /* MMEEventLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BB71FE05D6D00C1BD0D /* MMEEventLogger.m */; }; + 40834C021FE05E1800C1BD0D /* MMEEventsConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BB21FE05D6D00C1BD0D /* MMEEventsConfiguration.m */; }; + 40834C031FE05E1800C1BD0D /* MMEEventsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BA41FE05D6B00C1BD0D /* MMEEventsManager.m */; }; + 40834C041FE05E1800C1BD0D /* MMELocationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BB81FE05D6D00C1BD0D /* MMELocationManager.m */; }; + 40834C051FE05E1800C1BD0D /* MMENSDateWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BBC1FE05D6E00C1BD0D /* MMENSDateWrapper.m */; }; + 40834C061FE05E1800C1BD0D /* MMENSURLSessionWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BC61FE05D7000C1BD0D /* MMENSURLSessionWrapper.m */; }; + 40834C071FE05E1800C1BD0D /* MMETimerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BB91FE05D6E00C1BD0D /* MMETimerManager.m */; }; + 40834C081FE05E1800C1BD0D /* MMETrustKitWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BC11FE05D6F00C1BD0D /* MMETrustKitWrapper.m */; }; + 40834C091FE05E1800C1BD0D /* MMETypes.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BBD1FE05D6E00C1BD0D /* MMETypes.m */; }; + 40834C0A1FE05E1800C1BD0D /* MMEUIApplicationWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BCA1FE05D7000C1BD0D /* MMEUIApplicationWrapper.m */; }; + 40834C0B1FE05E1800C1BD0D /* MMEUniqueIdentifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BAD1FE05D6C00C1BD0D /* MMEUniqueIdentifier.m */; }; + 40834C0C1FE05E1800C1BD0D /* NSData+MMEGZIP.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BAF1FE05D6C00C1BD0D /* NSData+MMEGZIP.m */; }; + 40834C0D1FE05E1800C1BD0D /* MMEReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BCD1FE05D7100C1BD0D /* MMEReachability.m */; }; + 40834C401FE05F7500C1BD0D /* configuration_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C101FE05F3600C1BD0D /* configuration_utils.m */; }; + 40834C411FE05F7500C1BD0D /* parse_configuration.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C141FE05F3600C1BD0D /* parse_configuration.m */; }; + 40834C421FE05F7500C1BD0D /* ssl_pin_verifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C171FE05F3600C1BD0D /* ssl_pin_verifier.m */; }; + 40834C431FE05F7500C1BD0D /* TSKSPKIHashCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C1A1FE05F3600C1BD0D /* TSKSPKIHashCache.m */; }; + 40834C441FE05F7500C1BD0D /* reporting_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C1D1FE05F3600C1BD0D /* reporting_utils.m */; }; + 40834C451FE05F7500C1BD0D /* TSKBackgroundReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C1F1FE05F3600C1BD0D /* TSKBackgroundReporter.m */; }; + 40834C461FE05F7500C1BD0D /* TSKPinFailureReport.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C211FE05F3600C1BD0D /* TSKPinFailureReport.m */; }; + 40834C471FE05F7500C1BD0D /* TSKReportsRateLimiter.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C231FE05F3600C1BD0D /* TSKReportsRateLimiter.m */; }; + 40834C481FE05F7500C1BD0D /* vendor_identifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C251FE05F3600C1BD0D /* vendor_identifier.m */; }; + 40834C491FE05F7500C1BD0D /* TrustKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C271FE05F3600C1BD0D /* TrustKit.m */; }; + 40834C4A1FE05F7500C1BD0D /* TSKPinningValidator.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C2A1FE05F3600C1BD0D /* TSKPinningValidator.m */; }; + 40834C4B1FE05F7500C1BD0D /* TSKPinningValidatorResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C2E1FE05F3600C1BD0D /* TSKPinningValidatorResult.m */; }; + 40834C4C1FE05F7500C1BD0D /* TSKTrustKitConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C311FE05F3600C1BD0D /* TSKTrustKitConfig.m */; }; + 40834C4D1FE05F7600C1BD0D /* configuration_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C101FE05F3600C1BD0D /* configuration_utils.m */; }; + 40834C4E1FE05F7600C1BD0D /* parse_configuration.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C141FE05F3600C1BD0D /* parse_configuration.m */; }; + 40834C4F1FE05F7600C1BD0D /* ssl_pin_verifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C171FE05F3600C1BD0D /* ssl_pin_verifier.m */; }; + 40834C501FE05F7600C1BD0D /* TSKSPKIHashCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C1A1FE05F3600C1BD0D /* TSKSPKIHashCache.m */; }; + 40834C511FE05F7600C1BD0D /* reporting_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C1D1FE05F3600C1BD0D /* reporting_utils.m */; }; + 40834C521FE05F7600C1BD0D /* TSKBackgroundReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C1F1FE05F3600C1BD0D /* TSKBackgroundReporter.m */; }; + 40834C531FE05F7600C1BD0D /* TSKPinFailureReport.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C211FE05F3600C1BD0D /* TSKPinFailureReport.m */; }; + 40834C541FE05F7600C1BD0D /* TSKReportsRateLimiter.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C231FE05F3600C1BD0D /* TSKReportsRateLimiter.m */; }; + 40834C551FE05F7600C1BD0D /* vendor_identifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C251FE05F3600C1BD0D /* vendor_identifier.m */; }; + 40834C561FE05F7600C1BD0D /* TrustKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C271FE05F3600C1BD0D /* TrustKit.m */; }; + 40834C571FE05F7600C1BD0D /* TSKPinningValidator.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C2A1FE05F3600C1BD0D /* TSKPinningValidator.m */; }; + 40834C581FE05F7600C1BD0D /* TSKPinningValidatorResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C2E1FE05F3600C1BD0D /* TSKPinningValidatorResult.m */; }; + 40834C591FE05F7600C1BD0D /* TSKTrustKitConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C311FE05F3600C1BD0D /* TSKTrustKitConfig.m */; }; 4085AF091D933DEA00F11B22 /* MGLTileSetTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4085AF081D933DEA00F11B22 /* MGLTileSetTests.mm */; }; - 408982E91DEE208200754016 /* api_mapbox_staging.der in Resources */ = {isa = PBXBuildFile; fileRef = 40599F001DEE1B2400182B5D /* api_mapbox_staging.der */; }; - 408982EA1DEE208B00754016 /* api_mapbox_com-digicert_2016.der in Resources */ = {isa = PBXBuildFile; fileRef = 40599F011DEE1B2400182B5D /* api_mapbox_com-digicert_2016.der */; }; - 408982EB1DEE209100754016 /* api_mapbox_com-geotrust_2016.der in Resources */ = {isa = PBXBuildFile; fileRef = 40599F021DEE1B2400182B5D /* api_mapbox_com-geotrust_2016.der */; }; 408AA8571DAEDA1700022900 /* NSDictionary+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 408AA8551DAEDA0800022900 /* NSDictionary+MGLAdditions.h */; }; 408AA8581DAEDA1E00022900 /* NSDictionary+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 408AA8561DAEDA0800022900 /* NSDictionary+MGLAdditions.mm */; }; 408AA8591DAEDA1E00022900 /* NSDictionary+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 408AA8561DAEDA0800022900 /* NSDictionary+MGLAdditions.mm */; }; @@ -201,10 +265,6 @@ 409F43FD1E9E781C0048729D /* MGLMapViewDelegateIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 409F43FC1E9E781C0048729D /* MGLMapViewDelegateIntegrationTests.swift */; }; 40CF6DBB1DAC3C6600A4D18B /* MGLShape_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 40CF6DBA1DAC3C1800A4D18B /* MGLShape_Private.h */; }; 40CFA6511D7875BB008103BD /* MGLShapeSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 40CFA6501D787579008103BD /* MGLShapeSourceTests.mm */; }; - 40EA6BC11EF4599600FCCDA2 /* api_mapbox_com-digicert_2017.der in Resources */ = {isa = PBXBuildFile; fileRef = 40EA6BBD1EF4598900FCCDA2 /* api_mapbox_com-digicert_2017.der */; }; - 40EA6BC21EF4599700FCCDA2 /* api_mapbox_com-digicert_2017.der in Resources */ = {isa = PBXBuildFile; fileRef = 40EA6BBD1EF4598900FCCDA2 /* api_mapbox_com-digicert_2017.der */; }; - 40EA6BC31EF4599D00FCCDA2 /* api_mapbox_com-geotrust_2017.der in Resources */ = {isa = PBXBuildFile; fileRef = 40EA6BBE1EF4598900FCCDA2 /* api_mapbox_com-geotrust_2017.der */; }; - 40EA6BC41EF4599D00FCCDA2 /* api_mapbox_com-geotrust_2017.der in Resources */ = {isa = PBXBuildFile; fileRef = 40EA6BBE1EF4598900FCCDA2 /* api_mapbox_com-geotrust_2017.der */; }; 40EDA1C01CFE0E0200D9EA68 /* MGLAnnotationContainerView.h in Headers */ = {isa = PBXBuildFile; fileRef = 40EDA1BD1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.h */; }; 40EDA1C11CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 40EDA1BE1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.m */; }; 40EDA1C21CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 40EDA1BE1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.m */; }; @@ -294,8 +354,6 @@ 96E516FA20005A3D00A02306 /* MGLUserLocationHeadingArrowLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 966FCF501F3C321000F2B6DE /* MGLUserLocationHeadingArrowLayer.h */; }; 96E516FB20005A4000A02306 /* MGLUserLocationHeadingBeamLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 966FCF4A1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.h */; }; 96E516FC20005A4400A02306 /* MGLUserLocationHeadingIndicator.h in Headers */ = {isa = PBXBuildFile; fileRef = 96F3F73B1F5711F1003E2D2C /* MGLUserLocationHeadingIndicator.h */; }; - 96E516FD20005A4700A02306 /* MGLAPIClient.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848421CBAFB9800AB86E3 /* MGLAPIClient.h */; }; - 96E516FE20005A4C00A02306 /* MGLLocationManager.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848461CBAFB9800AB86E3 /* MGLLocationManager.h */; }; 96E516FF20005A4F00A02306 /* MGLMapboxEvents.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848481CBAFB9800AB86E3 /* MGLMapboxEvents.h */; }; 96E5170020005A6100A02306 /* Fabric.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848831CBB033F00AB86E3 /* Fabric.h */; }; 96E5170120005A6400A02306 /* Fabric+FABKits.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848821CBB033F00AB86E3 /* Fabric+FABKits.h */; }; @@ -425,12 +483,8 @@ DA88483F1CBAFB8500AB86E3 /* MGLUserLocation.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848391CBAFB8500AB86E3 /* MGLUserLocation.h */; settings = {ATTRIBUTES = (Public, ); }; }; DA88484F1CBAFB9800AB86E3 /* MGLAnnotationImage_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848401CBAFB9800AB86E3 /* MGLAnnotationImage_Private.h */; }; DA8848501CBAFB9800AB86E3 /* MGLAnnotationImage.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848411CBAFB9800AB86E3 /* MGLAnnotationImage.m */; }; - DA8848511CBAFB9800AB86E3 /* MGLAPIClient.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848421CBAFB9800AB86E3 /* MGLAPIClient.h */; }; - DA8848521CBAFB9800AB86E3 /* MGLAPIClient.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848431CBAFB9800AB86E3 /* MGLAPIClient.m */; }; DA8848531CBAFB9800AB86E3 /* MGLCompactCalloutView.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848441CBAFB9800AB86E3 /* MGLCompactCalloutView.h */; }; DA8848541CBAFB9800AB86E3 /* MGLCompactCalloutView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848451CBAFB9800AB86E3 /* MGLCompactCalloutView.m */; }; - DA8848551CBAFB9800AB86E3 /* MGLLocationManager.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848461CBAFB9800AB86E3 /* MGLLocationManager.h */; }; - DA8848561CBAFB9800AB86E3 /* MGLLocationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848471CBAFB9800AB86E3 /* MGLLocationManager.m */; }; DA8848571CBAFB9800AB86E3 /* MGLMapboxEvents.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848481CBAFB9800AB86E3 /* MGLMapboxEvents.h */; }; DA8848581CBAFB9800AB86E3 /* MGLMapboxEvents.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848491CBAFB9800AB86E3 /* MGLMapboxEvents.m */; }; DA8848591CBAFB9800AB86E3 /* MGLMapView.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA88484A1CBAFB9800AB86E3 /* MGLMapView.mm */; }; @@ -478,9 +532,7 @@ DAA4E42A1CBB730400178DFB /* NSProcessInfo+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848161CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.m */; }; DAA4E42B1CBB730400178DFB /* NSString+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848181CBAFA6200AB86E3 /* NSString+MGLAdditions.m */; }; DAA4E42D1CBB730400178DFB /* MGLAnnotationImage.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848411CBAFB9800AB86E3 /* MGLAnnotationImage.m */; }; - DAA4E42E1CBB730400178DFB /* MGLAPIClient.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848431CBAFB9800AB86E3 /* MGLAPIClient.m */; }; DAA4E42F1CBB730400178DFB /* MGLCompactCalloutView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848451CBAFB9800AB86E3 /* MGLCompactCalloutView.m */; }; - DAA4E4301CBB730400178DFB /* MGLLocationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848471CBAFB9800AB86E3 /* MGLLocationManager.m */; }; DAA4E4311CBB730400178DFB /* MGLMapboxEvents.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848491CBAFB9800AB86E3 /* MGLMapboxEvents.m */; }; DAA4E4321CBB730400178DFB /* MGLMapView.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA88484A1CBAFB9800AB86E3 /* MGLMapView.mm */; }; DAA4E4331CBB730400178DFB /* MGLUserLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = DA88484C1CBAFB9800AB86E3 /* MGLUserLocation.m */; }; @@ -778,9 +830,84 @@ 404C26E01D89B877000AA13D /* MGLTileSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTileSource.h; sourceTree = ""; }; 404C26E11D89B877000AA13D /* MGLTileSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLTileSource.mm; sourceTree = ""; }; 404C26E61D89C515000AA13D /* MGLTileSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTileSource_Private.h; sourceTree = ""; }; - 40599F001DEE1B2400182B5D /* api_mapbox_staging.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = api_mapbox_staging.der; sourceTree = ""; }; - 40599F011DEE1B2400182B5D /* api_mapbox_com-digicert_2016.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "api_mapbox_com-digicert_2016.der"; sourceTree = ""; }; - 40599F021DEE1B2400182B5D /* api_mapbox_com-geotrust_2016.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "api_mapbox_com-geotrust_2016.der"; sourceTree = ""; }; + 406E99B11FFEFED500D9FFCC /* MMEEventLogReportViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMEEventLogReportViewController.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEEventLogReportViewController.m"; sourceTree = SOURCE_ROOT; }; + 406E99B21FFEFED500D9FFCC /* MMEUINavigation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMEUINavigation.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEUINavigation.m"; sourceTree = SOURCE_ROOT; }; + 406E99B31FFEFED600D9FFCC /* MMEUINavigation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMEUINavigation.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEUINavigation.h"; sourceTree = SOURCE_ROOT; }; + 406E99B51FFEFED600D9FFCC /* MMEEventLogReportViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMEEventLogReportViewController.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEEventLogReportViewController.h"; sourceTree = SOURCE_ROOT; }; + 40834AEF1FDF4F0100C1BD0D /* Mapbox-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "Mapbox-Prefix.pch"; path = "src/Mapbox-Prefix.pch"; sourceTree = SOURCE_ROOT; }; + 40834BA31FE05D6B00C1BD0D /* MMEEventsManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMEEventsManager.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEEventsManager.h"; sourceTree = SOURCE_ROOT; }; + 40834BA41FE05D6B00C1BD0D /* MMEEventsManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMEEventsManager.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEEventsManager.m"; sourceTree = SOURCE_ROOT; }; + 40834BA51FE05D6B00C1BD0D /* MMEAPIClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMEAPIClient.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEAPIClient.m"; sourceTree = SOURCE_ROOT; }; + 40834BA61FE05D6B00C1BD0D /* MMEEventLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMEEventLogger.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEEventLogger.h"; sourceTree = SOURCE_ROOT; }; + 40834BA71FE05D6B00C1BD0D /* MMETrustKitWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMETrustKitWrapper.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMETrustKitWrapper.h"; sourceTree = SOURCE_ROOT; }; + 40834BAA1FE05D6C00C1BD0D /* MMENSURLSessionWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMENSURLSessionWrapper.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMENSURLSessionWrapper.h"; sourceTree = SOURCE_ROOT; }; + 40834BAB1FE05D6C00C1BD0D /* MMEAPIClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMEAPIClient.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEAPIClient.h"; sourceTree = SOURCE_ROOT; }; + 40834BAC1FE05D6C00C1BD0D /* MapboxMobileEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MapboxMobileEvents.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MapboxMobileEvents.h"; sourceTree = SOURCE_ROOT; }; + 40834BAD1FE05D6C00C1BD0D /* MMEUniqueIdentifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMEUniqueIdentifier.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEUniqueIdentifier.m"; sourceTree = SOURCE_ROOT; }; + 40834BAE1FE05D6C00C1BD0D /* MMECommonEventData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMECommonEventData.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMECommonEventData.h"; sourceTree = SOURCE_ROOT; }; + 40834BAF1FE05D6C00C1BD0D /* NSData+MMEGZIP.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSData+MMEGZIP.m"; path = "vendor/mapbox-events-ios/MapboxMobileEvents/NSData+MMEGZIP.m"; sourceTree = SOURCE_ROOT; }; + 40834BB01FE05D6C00C1BD0D /* MMEConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMEConstants.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEConstants.h"; sourceTree = SOURCE_ROOT; }; + 40834BB11FE05D6D00C1BD0D /* MMEDependencyManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMEDependencyManager.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEDependencyManager.h"; sourceTree = SOURCE_ROOT; }; + 40834BB21FE05D6D00C1BD0D /* MMEEventsConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMEEventsConfiguration.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEEventsConfiguration.m"; sourceTree = SOURCE_ROOT; }; + 40834BB31FE05D6D00C1BD0D /* MMELocationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMELocationManager.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMELocationManager.h"; sourceTree = SOURCE_ROOT; }; + 40834BB41FE05D6D00C1BD0D /* MMEDependencyManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMEDependencyManager.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEDependencyManager.m"; sourceTree = SOURCE_ROOT; }; + 40834BB51FE05D6D00C1BD0D /* MMECategoryLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMECategoryLoader.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMECategoryLoader.h"; sourceTree = SOURCE_ROOT; }; + 40834BB61FE05D6D00C1BD0D /* MMETypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMETypes.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMETypes.h"; sourceTree = SOURCE_ROOT; }; + 40834BB71FE05D6D00C1BD0D /* MMEEventLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMEEventLogger.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEEventLogger.m"; sourceTree = SOURCE_ROOT; }; + 40834BB81FE05D6D00C1BD0D /* MMELocationManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMELocationManager.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMELocationManager.m"; sourceTree = SOURCE_ROOT; }; + 40834BB91FE05D6E00C1BD0D /* MMETimerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMETimerManager.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMETimerManager.m"; sourceTree = SOURCE_ROOT; }; + 40834BBA1FE05D6E00C1BD0D /* MMEEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMEEvent.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEEvent.h"; sourceTree = SOURCE_ROOT; }; + 40834BBB1FE05D6E00C1BD0D /* MMEEventsConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMEEventsConfiguration.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEEventsConfiguration.h"; sourceTree = SOURCE_ROOT; }; + 40834BBC1FE05D6E00C1BD0D /* MMENSDateWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMENSDateWrapper.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMENSDateWrapper.m"; sourceTree = SOURCE_ROOT; }; + 40834BBD1FE05D6E00C1BD0D /* MMETypes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMETypes.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMETypes.m"; sourceTree = SOURCE_ROOT; }; + 40834BBE1FE05D6E00C1BD0D /* MMEUIApplicationWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMEUIApplicationWrapper.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEUIApplicationWrapper.h"; sourceTree = SOURCE_ROOT; }; + 40834BBF1FE05D6E00C1BD0D /* MMEUniqueIdentifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMEUniqueIdentifier.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEUniqueIdentifier.h"; sourceTree = SOURCE_ROOT; }; + 40834BC01FE05D6E00C1BD0D /* MMEConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMEConstants.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEConstants.m"; sourceTree = SOURCE_ROOT; }; + 40834BC11FE05D6F00C1BD0D /* MMETrustKitWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMETrustKitWrapper.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMETrustKitWrapper.m"; sourceTree = SOURCE_ROOT; }; + 40834BC21FE05D6F00C1BD0D /* CLLocation+MMEMobileEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "CLLocation+MMEMobileEvents.h"; path = "vendor/mapbox-events-ios/MapboxMobileEvents/CLLocation+MMEMobileEvents.h"; sourceTree = SOURCE_ROOT; }; + 40834BC31FE05D6F00C1BD0D /* CLLocation+MMEMobileEvents.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "CLLocation+MMEMobileEvents.m"; path = "vendor/mapbox-events-ios/MapboxMobileEvents/CLLocation+MMEMobileEvents.m"; sourceTree = SOURCE_ROOT; }; + 40834BC41FE05D6F00C1BD0D /* MMECategoryLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMECategoryLoader.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMECategoryLoader.m"; sourceTree = SOURCE_ROOT; }; + 40834BC51FE05D6F00C1BD0D /* MMENSDateWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMENSDateWrapper.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMENSDateWrapper.h"; sourceTree = SOURCE_ROOT; }; + 40834BC61FE05D7000C1BD0D /* MMENSURLSessionWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMENSURLSessionWrapper.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMENSURLSessionWrapper.m"; sourceTree = SOURCE_ROOT; }; + 40834BC71FE05D7000C1BD0D /* MMEEvent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMEEvent.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEEvent.m"; sourceTree = SOURCE_ROOT; }; + 40834BC81FE05D7000C1BD0D /* MMENamespacedDependencies.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMENamespacedDependencies.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMENamespacedDependencies.h"; sourceTree = SOURCE_ROOT; }; + 40834BC91FE05D7000C1BD0D /* MMETimerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMETimerManager.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMETimerManager.h"; sourceTree = SOURCE_ROOT; }; + 40834BCA1FE05D7000C1BD0D /* MMEUIApplicationWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMEUIApplicationWrapper.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEUIApplicationWrapper.m"; sourceTree = SOURCE_ROOT; }; + 40834BCC1FE05D7100C1BD0D /* MMEReachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMEReachability.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/Reachability/MMEReachability.h"; sourceTree = SOURCE_ROOT; }; + 40834BCD1FE05D7100C1BD0D /* MMEReachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMEReachability.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/Reachability/MMEReachability.m"; sourceTree = SOURCE_ROOT; }; + 40834BCE1FE05D7100C1BD0D /* MMECommonEventData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMECommonEventData.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMECommonEventData.m"; sourceTree = SOURCE_ROOT; }; + 40834BCF1FE05D7100C1BD0D /* NSData+MMEGZIP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSData+MMEGZIP.h"; path = "vendor/mapbox-events-ios/MapboxMobileEvents/NSData+MMEGZIP.h"; sourceTree = SOURCE_ROOT; }; + 40834C0F1FE05F3600C1BD0D /* configuration_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = configuration_utils.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/configuration_utils.h"; sourceTree = SOURCE_ROOT; }; + 40834C101FE05F3600C1BD0D /* configuration_utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = configuration_utils.m; path = "vendor/mapbox-events-ios/vendor/TrustKit/configuration_utils.m"; sourceTree = SOURCE_ROOT; }; + 40834C131FE05F3600C1BD0D /* parse_configuration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = parse_configuration.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/parse_configuration.h"; sourceTree = SOURCE_ROOT; }; + 40834C141FE05F3600C1BD0D /* parse_configuration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = parse_configuration.m; path = "vendor/mapbox-events-ios/vendor/TrustKit/parse_configuration.m"; sourceTree = SOURCE_ROOT; }; + 40834C161FE05F3600C1BD0D /* ssl_pin_verifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ssl_pin_verifier.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/Pinning/ssl_pin_verifier.h"; sourceTree = SOURCE_ROOT; }; + 40834C171FE05F3600C1BD0D /* ssl_pin_verifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ssl_pin_verifier.m; path = "vendor/mapbox-events-ios/vendor/TrustKit/Pinning/ssl_pin_verifier.m"; sourceTree = SOURCE_ROOT; }; + 40834C181FE05F3600C1BD0D /* TSKPublicKeyAlgorithm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKPublicKeyAlgorithm.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/Pinning/TSKPublicKeyAlgorithm.h"; sourceTree = SOURCE_ROOT; }; + 40834C191FE05F3600C1BD0D /* TSKSPKIHashCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKSPKIHashCache.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/Pinning/TSKSPKIHashCache.h"; sourceTree = SOURCE_ROOT; }; + 40834C1A1FE05F3600C1BD0D /* TSKSPKIHashCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKSPKIHashCache.m; path = "vendor/mapbox-events-ios/vendor/TrustKit/Pinning/TSKSPKIHashCache.m"; sourceTree = SOURCE_ROOT; }; + 40834C1C1FE05F3600C1BD0D /* reporting_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = reporting_utils.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/Reporting/reporting_utils.h"; sourceTree = SOURCE_ROOT; }; + 40834C1D1FE05F3600C1BD0D /* reporting_utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = reporting_utils.m; path = "vendor/mapbox-events-ios/vendor/TrustKit/Reporting/reporting_utils.m"; sourceTree = SOURCE_ROOT; }; + 40834C1E1FE05F3600C1BD0D /* TSKBackgroundReporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKBackgroundReporter.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/Reporting/TSKBackgroundReporter.h"; sourceTree = SOURCE_ROOT; }; + 40834C1F1FE05F3600C1BD0D /* TSKBackgroundReporter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKBackgroundReporter.m; path = "vendor/mapbox-events-ios/vendor/TrustKit/Reporting/TSKBackgroundReporter.m"; sourceTree = SOURCE_ROOT; }; + 40834C201FE05F3600C1BD0D /* TSKPinFailureReport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKPinFailureReport.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/Reporting/TSKPinFailureReport.h"; sourceTree = SOURCE_ROOT; }; + 40834C211FE05F3600C1BD0D /* TSKPinFailureReport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKPinFailureReport.m; path = "vendor/mapbox-events-ios/vendor/TrustKit/Reporting/TSKPinFailureReport.m"; sourceTree = SOURCE_ROOT; }; + 40834C221FE05F3600C1BD0D /* TSKReportsRateLimiter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKReportsRateLimiter.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/Reporting/TSKReportsRateLimiter.h"; sourceTree = SOURCE_ROOT; }; + 40834C231FE05F3600C1BD0D /* TSKReportsRateLimiter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKReportsRateLimiter.m; path = "vendor/mapbox-events-ios/vendor/TrustKit/Reporting/TSKReportsRateLimiter.m"; sourceTree = SOURCE_ROOT; }; + 40834C241FE05F3600C1BD0D /* vendor_identifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vendor_identifier.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/Reporting/vendor_identifier.h"; sourceTree = SOURCE_ROOT; }; + 40834C251FE05F3600C1BD0D /* vendor_identifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = vendor_identifier.m; path = "vendor/mapbox-events-ios/vendor/TrustKit/Reporting/vendor_identifier.m"; sourceTree = SOURCE_ROOT; }; + 40834C261FE05F3600C1BD0D /* TrustKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TrustKit.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/TrustKit.h"; sourceTree = SOURCE_ROOT; }; + 40834C271FE05F3600C1BD0D /* TrustKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TrustKit.m; path = "vendor/mapbox-events-ios/vendor/TrustKit/TrustKit.m"; sourceTree = SOURCE_ROOT; }; + 40834C281FE05F3600C1BD0D /* TSKLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKLog.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/TSKLog.h"; sourceTree = SOURCE_ROOT; }; + 40834C291FE05F3600C1BD0D /* TSKPinningValidator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKPinningValidator.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/TSKPinningValidator.h"; sourceTree = SOURCE_ROOT; }; + 40834C2A1FE05F3600C1BD0D /* TSKPinningValidator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKPinningValidator.m; path = "vendor/mapbox-events-ios/vendor/TrustKit/TSKPinningValidator.m"; sourceTree = SOURCE_ROOT; }; + 40834C2B1FE05F3600C1BD0D /* TSKPinningValidator_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKPinningValidator_Private.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/TSKPinningValidator_Private.h"; sourceTree = SOURCE_ROOT; }; + 40834C2C1FE05F3600C1BD0D /* TSKPinningValidatorCallback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKPinningValidatorCallback.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/TSKPinningValidatorCallback.h"; sourceTree = SOURCE_ROOT; }; + 40834C2D1FE05F3600C1BD0D /* TSKPinningValidatorResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKPinningValidatorResult.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/TSKPinningValidatorResult.h"; sourceTree = SOURCE_ROOT; }; + 40834C2E1FE05F3600C1BD0D /* TSKPinningValidatorResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKPinningValidatorResult.m; path = "vendor/mapbox-events-ios/vendor/TrustKit/TSKPinningValidatorResult.m"; sourceTree = SOURCE_ROOT; }; + 40834C2F1FE05F3600C1BD0D /* TSKTrustDecision.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKTrustDecision.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/TSKTrustDecision.h"; sourceTree = SOURCE_ROOT; }; + 40834C301FE05F3600C1BD0D /* TSKTrustKitConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKTrustKitConfig.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/TSKTrustKitConfig.h"; sourceTree = SOURCE_ROOT; }; + 40834C311FE05F3600C1BD0D /* TSKTrustKitConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKTrustKitConfig.m; path = "vendor/mapbox-events-ios/vendor/TrustKit/TSKTrustKitConfig.m"; sourceTree = SOURCE_ROOT; }; 4085AF081D933DEA00F11B22 /* MGLTileSetTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLTileSetTests.mm; path = ../../darwin/test/MGLTileSetTests.mm; sourceTree = ""; }; 408AA8551DAEDA0800022900 /* NSDictionary+MGLAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+MGLAdditions.h"; sourceTree = ""; }; 408AA8561DAEDA0800022900 /* NSDictionary+MGLAdditions.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSDictionary+MGLAdditions.mm"; sourceTree = ""; }; @@ -788,8 +915,6 @@ 409F43FC1E9E781C0048729D /* MGLMapViewDelegateIntegrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MGLMapViewDelegateIntegrationTests.swift; sourceTree = ""; }; 40CF6DBA1DAC3C1800A4D18B /* MGLShape_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLShape_Private.h; sourceTree = ""; }; 40CFA6501D787579008103BD /* MGLShapeSourceTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLShapeSourceTests.mm; path = ../../darwin/test/MGLShapeSourceTests.mm; sourceTree = ""; }; - 40EA6BBD1EF4598900FCCDA2 /* api_mapbox_com-digicert_2017.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "api_mapbox_com-digicert_2017.der"; sourceTree = ""; }; - 40EA6BBE1EF4598900FCCDA2 /* api_mapbox_com-geotrust_2017.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "api_mapbox_com-geotrust_2017.der"; sourceTree = ""; }; 40EDA1BD1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationContainerView.h; sourceTree = ""; }; 40EDA1BE1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAnnotationContainerView.m; sourceTree = ""; }; 40F8876F1D7A1DB8008ECB67 /* MGLShapeSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLShapeSource_Private.h; sourceTree = ""; }; @@ -1004,12 +1129,8 @@ DA8848391CBAFB8500AB86E3 /* MGLUserLocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLUserLocation.h; sourceTree = ""; }; DA8848401CBAFB9800AB86E3 /* MGLAnnotationImage_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationImage_Private.h; sourceTree = ""; }; DA8848411CBAFB9800AB86E3 /* MGLAnnotationImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAnnotationImage.m; sourceTree = ""; }; - DA8848421CBAFB9800AB86E3 /* MGLAPIClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAPIClient.h; sourceTree = ""; }; - DA8848431CBAFB9800AB86E3 /* MGLAPIClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAPIClient.m; sourceTree = ""; }; DA8848441CBAFB9800AB86E3 /* MGLCompactCalloutView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCompactCalloutView.h; sourceTree = ""; }; DA8848451CBAFB9800AB86E3 /* MGLCompactCalloutView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLCompactCalloutView.m; sourceTree = ""; }; - DA8848461CBAFB9800AB86E3 /* MGLLocationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLocationManager.h; sourceTree = ""; }; - DA8848471CBAFB9800AB86E3 /* MGLLocationManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLLocationManager.m; sourceTree = ""; }; DA8848481CBAFB9800AB86E3 /* MGLMapboxEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMapboxEvents.h; sourceTree = ""; }; DA8848491CBAFB9800AB86E3 /* MGLMapboxEvents.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLMapboxEvents.m; sourceTree = ""; }; DA88484A1CBAFB9800AB86E3 /* MGLMapView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLMapView.mm; sourceTree = ""; }; @@ -1377,6 +1498,137 @@ name = "Test Helpers"; sourceTree = ""; }; + 40834BA11FE05CFD00C1BD0D /* Development */ = { + isa = PBXGroup; + children = ( + 9620BB361E69FE1700705A1D /* MGLSDKUpdateChecker.h */, + 9620BB371E69FE1700705A1D /* MGLSDKUpdateChecker.mm */, + ); + name = Development; + sourceTree = ""; + }; + 40834BA21FE05D3100C1BD0D /* Runtime */ = { + isa = PBXGroup; + children = ( + 40834BC81FE05D7000C1BD0D /* MMENamespacedDependencies.h */, + 40834BAC1FE05D6C00C1BD0D /* MapboxMobileEvents.h */, + DA8848481CBAFB9800AB86E3 /* MGLMapboxEvents.h */, + DA8848491CBAFB9800AB86E3 /* MGLMapboxEvents.m */, + 40834BC21FE05D6F00C1BD0D /* CLLocation+MMEMobileEvents.h */, + 40834BC31FE05D6F00C1BD0D /* CLLocation+MMEMobileEvents.m */, + 40834BAB1FE05D6C00C1BD0D /* MMEAPIClient.h */, + 40834BA51FE05D6B00C1BD0D /* MMEAPIClient.m */, + 40834BB51FE05D6D00C1BD0D /* MMECategoryLoader.h */, + 40834BC41FE05D6F00C1BD0D /* MMECategoryLoader.m */, + 40834BAE1FE05D6C00C1BD0D /* MMECommonEventData.h */, + 40834BCE1FE05D7100C1BD0D /* MMECommonEventData.m */, + 40834BB01FE05D6C00C1BD0D /* MMEConstants.h */, + 40834BC01FE05D6E00C1BD0D /* MMEConstants.m */, + 40834BB11FE05D6D00C1BD0D /* MMEDependencyManager.h */, + 40834BB41FE05D6D00C1BD0D /* MMEDependencyManager.m */, + 40834BBA1FE05D6E00C1BD0D /* MMEEvent.h */, + 40834BC71FE05D7000C1BD0D /* MMEEvent.m */, + 40834BA61FE05D6B00C1BD0D /* MMEEventLogger.h */, + 40834BB71FE05D6D00C1BD0D /* MMEEventLogger.m */, + 406E99B51FFEFED600D9FFCC /* MMEEventLogReportViewController.h */, + 406E99B11FFEFED500D9FFCC /* MMEEventLogReportViewController.m */, + 40834BBB1FE05D6E00C1BD0D /* MMEEventsConfiguration.h */, + 40834BB21FE05D6D00C1BD0D /* MMEEventsConfiguration.m */, + 40834BA31FE05D6B00C1BD0D /* MMEEventsManager.h */, + 40834BA41FE05D6B00C1BD0D /* MMEEventsManager.m */, + 40834BB31FE05D6D00C1BD0D /* MMELocationManager.h */, + 40834BB81FE05D6D00C1BD0D /* MMELocationManager.m */, + 40834BC51FE05D6F00C1BD0D /* MMENSDateWrapper.h */, + 40834BBC1FE05D6E00C1BD0D /* MMENSDateWrapper.m */, + 40834BAA1FE05D6C00C1BD0D /* MMENSURLSessionWrapper.h */, + 40834BC61FE05D7000C1BD0D /* MMENSURLSessionWrapper.m */, + 40834BC91FE05D7000C1BD0D /* MMETimerManager.h */, + 40834BB91FE05D6E00C1BD0D /* MMETimerManager.m */, + 40834BA71FE05D6B00C1BD0D /* MMETrustKitWrapper.h */, + 40834BC11FE05D6F00C1BD0D /* MMETrustKitWrapper.m */, + 40834BB61FE05D6D00C1BD0D /* MMETypes.h */, + 40834BBD1FE05D6E00C1BD0D /* MMETypes.m */, + 40834BBE1FE05D6E00C1BD0D /* MMEUIApplicationWrapper.h */, + 40834BCA1FE05D7000C1BD0D /* MMEUIApplicationWrapper.m */, + 406E99B31FFEFED600D9FFCC /* MMEUINavigation.h */, + 406E99B21FFEFED500D9FFCC /* MMEUINavigation.m */, + 40834BBF1FE05D6E00C1BD0D /* MMEUniqueIdentifier.h */, + 40834BAD1FE05D6C00C1BD0D /* MMEUniqueIdentifier.m */, + 40834BCF1FE05D7100C1BD0D /* NSData+MMEGZIP.h */, + 40834BAF1FE05D6C00C1BD0D /* NSData+MMEGZIP.m */, + 40834BCB1FE05D7100C1BD0D /* Reachability */, + 40834C0E1FE05F3600C1BD0D /* TrustKit */, + ); + name = Runtime; + sourceTree = ""; + }; + 40834BCB1FE05D7100C1BD0D /* Reachability */ = { + isa = PBXGroup; + children = ( + 40834BCC1FE05D7100C1BD0D /* MMEReachability.h */, + 40834BCD1FE05D7100C1BD0D /* MMEReachability.m */, + ); + name = Reachability; + path = "vendor/mapbox-events-ios/MapboxMobileEvents/Reachability"; + sourceTree = SOURCE_ROOT; + }; + 40834C0E1FE05F3600C1BD0D /* TrustKit */ = { + isa = PBXGroup; + children = ( + 40834C0F1FE05F3600C1BD0D /* configuration_utils.h */, + 40834C101FE05F3600C1BD0D /* configuration_utils.m */, + 40834C131FE05F3600C1BD0D /* parse_configuration.h */, + 40834C141FE05F3600C1BD0D /* parse_configuration.m */, + 40834C151FE05F3600C1BD0D /* Pinning */, + 40834C1B1FE05F3600C1BD0D /* Reporting */, + 40834C261FE05F3600C1BD0D /* TrustKit.h */, + 40834C271FE05F3600C1BD0D /* TrustKit.m */, + 40834C281FE05F3600C1BD0D /* TSKLog.h */, + 40834C291FE05F3600C1BD0D /* TSKPinningValidator.h */, + 40834C2A1FE05F3600C1BD0D /* TSKPinningValidator.m */, + 40834C2B1FE05F3600C1BD0D /* TSKPinningValidator_Private.h */, + 40834C2C1FE05F3600C1BD0D /* TSKPinningValidatorCallback.h */, + 40834C2D1FE05F3600C1BD0D /* TSKPinningValidatorResult.h */, + 40834C2E1FE05F3600C1BD0D /* TSKPinningValidatorResult.m */, + 40834C2F1FE05F3600C1BD0D /* TSKTrustDecision.h */, + 40834C301FE05F3600C1BD0D /* TSKTrustKitConfig.h */, + 40834C311FE05F3600C1BD0D /* TSKTrustKitConfig.m */, + ); + name = TrustKit; + path = "vendor/mapbox-events-ios/vendor/TrustKit"; + sourceTree = SOURCE_ROOT; + }; + 40834C151FE05F3600C1BD0D /* Pinning */ = { + isa = PBXGroup; + children = ( + 40834C161FE05F3600C1BD0D /* ssl_pin_verifier.h */, + 40834C171FE05F3600C1BD0D /* ssl_pin_verifier.m */, + 40834C181FE05F3600C1BD0D /* TSKPublicKeyAlgorithm.h */, + 40834C191FE05F3600C1BD0D /* TSKSPKIHashCache.h */, + 40834C1A1FE05F3600C1BD0D /* TSKSPKIHashCache.m */, + ); + name = Pinning; + path = "vendor/mapbox-events-ios/vendor/TrustKit/Pinning"; + sourceTree = SOURCE_ROOT; + }; + 40834C1B1FE05F3600C1BD0D /* Reporting */ = { + isa = PBXGroup; + children = ( + 40834C1C1FE05F3600C1BD0D /* reporting_utils.h */, + 40834C1D1FE05F3600C1BD0D /* reporting_utils.m */, + 40834C1E1FE05F3600C1BD0D /* TSKBackgroundReporter.h */, + 40834C1F1FE05F3600C1BD0D /* TSKBackgroundReporter.m */, + 40834C201FE05F3600C1BD0D /* TSKPinFailureReport.h */, + 40834C211FE05F3600C1BD0D /* TSKPinFailureReport.m */, + 40834C221FE05F3600C1BD0D /* TSKReportsRateLimiter.h */, + 40834C231FE05F3600C1BD0D /* TSKReportsRateLimiter.m */, + 40834C241FE05F3600C1BD0D /* vendor_identifier.h */, + 40834C251FE05F3600C1BD0D /* vendor_identifier.m */, + ); + name = Reporting; + path = "vendor/mapbox-events-ios/vendor/TrustKit/Reporting"; + sourceTree = SOURCE_ROOT; + }; 409F43FB1E9E77D10048729D /* Swift Integration */ = { isa = PBXGroup; children = ( @@ -1557,6 +1809,7 @@ isa = PBXGroup; children = ( DA88485E1CBAFC2E00AB86E3 /* Mapbox.h */, + 40834AEF1FDF4F0100C1BD0D /* Mapbox-Prefix.pch */, DA8847DE1CBAFA3E00AB86E3 /* Foundation */, DA8F25BC1D51D2570010E6B5 /* Foundation Templates */, DA8933B91CCD2C6700E68420 /* Foundation Resources */, @@ -1636,11 +1889,6 @@ DA89339F1CCC951200E68420 /* Localizable.strings */, DAC49C5F1CD02BC9009E1AA3 /* Localizable.stringsdict */, DA8933EF1CCD387900E68420 /* strip-frameworks.sh */, - 40599F001DEE1B2400182B5D /* api_mapbox_staging.der */, - 40599F011DEE1B2400182B5D /* api_mapbox_com-digicert_2016.der */, - 40599F021DEE1B2400182B5D /* api_mapbox_com-geotrust_2016.der */, - 40EA6BBD1EF4598900FCCDA2 /* api_mapbox_com-digicert_2017.der */, - 40EA6BBE1EF4598900FCCDA2 /* api_mapbox_com-geotrust_2017.der */, ); name = "Kit Resources"; path = resources; @@ -1874,16 +2122,10 @@ DAD165851CF4D08B001FF4B9 /* Telemetry */ = { isa = PBXGroup; children = ( - DA8848421CBAFB9800AB86E3 /* MGLAPIClient.h */, - DA8848431CBAFB9800AB86E3 /* MGLAPIClient.m */, - DA8848461CBAFB9800AB86E3 /* MGLLocationManager.h */, - DA8848471CBAFB9800AB86E3 /* MGLLocationManager.m */, - DA8848481CBAFB9800AB86E3 /* MGLMapboxEvents.h */, - DA8848491CBAFB9800AB86E3 /* MGLMapboxEvents.m */, - 9620BB361E69FE1700705A1D /* MGLSDKUpdateChecker.h */, - 9620BB371E69FE1700705A1D /* MGLSDKUpdateChecker.mm */, AC518DFD201BB55A00EBC820 /* MGLTelemetryConfig.h */, AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */, + 40834BA21FE05D3100C1BD0D /* Runtime */, + 40834BA11FE05CFD00C1BD0D /* Development */, ); name = Telemetry; sourceTree = ""; @@ -1918,7 +2160,6 @@ DA8848231CBAFA6200AB86E3 /* MGLOfflineStorage_Private.h in Headers */, 404326891D5B9B27007111BD /* MGLAnnotationContainerView_Private.h in Headers */, CA55CD41202C16AA00CE7095 /* MGLCameraChangeReason.h in Headers */, - 1FB7DAAF1F2A4DBD00410606 /* MGLVectorSource+MGLAdditions.h in Headers */, DA88483B1CBAFB8500AB86E3 /* MGLCalloutView.h in Headers */, 35E0CFE61D3E501500188327 /* MGLStyle_Private.h in Headers */, 3510FFF01D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */, @@ -1945,7 +2186,6 @@ FA68F14A1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h in Headers */, 353933FB1D3FB7C0003F57D7 /* MGLRasterStyleLayer.h in Headers */, DA8847EF1CBAFA5100AB86E3 /* MGLAccountManager.h in Headers */, - DA8848511CBAFB9800AB86E3 /* MGLAPIClient.h in Headers */, DA35A2C91CCAAAD200E826B2 /* NSValue+MGLAdditions.h in Headers */, 3510FFEA1D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.h in Headers */, DA6408DB1DA4E7D300908C90 /* MGLVectorStyleLayer.h in Headers */, @@ -1962,7 +2202,6 @@ 071BBB031EE76146001FB02A /* MGLImageSource.h in Headers */, DA8847F41CBAFA5100AB86E3 /* MGLOfflinePack.h in Headers */, DA88482E1CBAFA6200AB86E3 /* NSException+MGLAdditions.h in Headers */, - DA8848551CBAFB9800AB86E3 /* MGLLocationManager.h in Headers */, 96F3F73C1F57124B003E2D2C /* MGLUserLocationHeadingIndicator.h in Headers */, 408AA8571DAEDA1700022900 /* NSDictionary+MGLAdditions.h in Headers */, DA88483F1CBAFB8500AB86E3 /* MGLUserLocation.h in Headers */, @@ -2037,7 +2276,6 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 96E516FE20005A4C00A02306 /* MGLLocationManager.h in Headers */, 556660CA1E1BF3A900E2C41B /* MGLFoundation.h in Headers */, 96E516ED200058A200A02306 /* MGLComputedShapeSource.h in Headers */, 35B82BF91D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h in Headers */, @@ -2165,7 +2403,6 @@ DAF0D8191DFE6B2800B28378 /* MGLAttributionInfo_Private.h in Headers */, DABFB86A1CBE99E500D62B32 /* MGLStyle.h in Headers */, DA00FC8F1D5EEB0D009AABC8 /* MGLAttributionInfo.h in Headers */, - 96E516FD20005A4700A02306 /* MGLAPIClient.h in Headers */, 96E5170320005A6800A02306 /* FABKitProtocol.h in Headers */, 96E516E12000551100A02306 /* MGLMultiPoint_Private.h in Headers */, 3EA934623AD0000B7D99C3FB /* MGLRendererConfiguration.h in Headers */, @@ -2494,11 +2731,6 @@ DA8933F01CCD387900E68420 /* strip-frameworks.sh in Resources */, DAC49C5C1CD02BC9009E1AA3 /* Localizable.stringsdict in Resources */, DA8933BF1CCD2CAD00E68420 /* Foundation.stringsdict in Resources */, - 40EA6BC11EF4599600FCCDA2 /* api_mapbox_com-digicert_2017.der in Resources */, - 408982E91DEE208200754016 /* api_mapbox_staging.der in Resources */, - 408982EA1DEE208B00754016 /* api_mapbox_com-digicert_2016.der in Resources */, - 40EA6BC31EF4599D00FCCDA2 /* api_mapbox_com-geotrust_2017.der in Resources */, - 408982EB1DEE209100754016 /* api_mapbox_com-geotrust_2016.der in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2510,12 +2742,7 @@ DA8933DB1CCD31D400E68420 /* Foundation.strings in Resources */, 960D0C371ECF5AAF008E151F /* Images.xcassets in Resources */, DA8933DC1CCD31D400E68420 /* Foundation.stringsdict in Resources */, - 40EA6BC41EF4599D00FCCDA2 /* api_mapbox_com-geotrust_2017.der in Resources */, DAC49C5D1CD02BC9009E1AA3 /* Localizable.stringsdict in Resources */, - 40599F0C1DEE1B7600182B5D /* api_mapbox_staging.der in Resources */, - 40599F0D1DEE1B7A00182B5D /* api_mapbox_com-digicert_2016.der in Resources */, - 40599F0E1DEE1B7E00182B5D /* api_mapbox_com-geotrust_2016.der in Resources */, - 40EA6BC21EF4599700FCCDA2 /* api_mapbox_com-digicert_2017.der in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2625,28 +2852,44 @@ files = ( 35136D391D42271A00C20EFD /* MGLBackgroundStyleLayer.mm in Sources */, 3510FFEC1D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm in Sources */, + 40834C431FE05F7500C1BD0D /* TSKSPKIHashCache.m in Sources */, DAED38651D62D0FC00D7640F /* NSURL+MGLAdditions.m in Sources */, + 40834BEC1FE05E1800C1BD0D /* MMEEvent.m in Sources */, 9620BB3A1E69FE1700705A1D /* MGLSDKUpdateChecker.mm in Sources */, 354B83981D2E873E005D9406 /* MGLUserLocationAnnotationView.m in Sources */, + 40834BEE1FE05E1800C1BD0D /* MMEEventsConfiguration.m in Sources */, DA88485D1CBAFB9800AB86E3 /* MGLFaux3DUserLocationAnnotationView.m in Sources */, DAD165701CF41981001FF4B9 /* MGLFeature.mm in Sources */, 30E578191DAA855E0050F07E /* UIImage+MGLAdditions.mm in Sources */, 40EDA1C11CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */, + 40834C481FE05F7500C1BD0D /* vendor_identifier.m in Sources */, DA8848541CBAFB9800AB86E3 /* MGLCompactCalloutView.m in Sources */, + 40834BEB1FE05E1800C1BD0D /* MMEDependencyManager.m in Sources */, + 40834C411FE05F7500C1BD0D /* parse_configuration.m in Sources */, DA8848251CBAFA6200AB86E3 /* MGLPointAnnotation.mm in Sources */, + 40834BEA1FE05E1800C1BD0D /* MMEConstants.m in Sources */, 929EFFAB1F56DCD4003A77D5 /* MGLAnnotationView.mm in Sources */, 35136D3C1D42272500C20EFD /* MGLCircleStyleLayer.mm in Sources */, DD9BE4F81EB263C50079A3AF /* UIViewController+MGLAdditions.m in Sources */, 350098DE1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.mm in Sources */, DA6408DD1DA4E7D300908C90 /* MGLVectorStyleLayer.m in Sources */, + 40834BF71FE05E1800C1BD0D /* MMEUniqueIdentifier.m in Sources */, 3566C7681D4A77BA008152BC /* MGLShapeSource.mm in Sources */, + 40834C4A1FE05F7500C1BD0D /* TSKPinningValidator.m in Sources */, 400533021DB0862B0069F638 /* NSArray+MGLAdditions.mm in Sources */, 96036A03200565C700510F3D /* NSOrthography+MGLAdditions.m in Sources */, + 40834BF31FE05E1800C1BD0D /* MMETimerManager.m in Sources */, 35136D421D42274500C20EFD /* MGLRasterStyleLayer.mm in Sources */, 3538AA1F1D542239008EC33D /* MGLForegroundStyleLayer.mm in Sources */, + 40834BF11FE05E1800C1BD0D /* MMENSDateWrapper.m in Sources */, + 40834C461FE05F7500C1BD0D /* TSKPinFailureReport.m in Sources */, + 406E99B91FFEFF1B00D9FFCC /* MMEEventLogReportViewController.m in Sources */, + 40834BE61FE05E1800C1BD0D /* CLLocation+MMEMobileEvents.m in Sources */, DA00FC901D5EEB0D009AABC8 /* MGLAttributionInfo.mm in Sources */, DA88482D1CBAFA6200AB86E3 /* NSBundle+MGLAdditions.m in Sources */, + 406E99BB1FFF006C00D9FFCC /* MMEUINavigation.m in Sources */, 966FCF541F3C323300F2B6DE /* MGLUserLocationHeadingArrowLayer.m in Sources */, + 40834BE81FE05E1800C1BD0D /* MMECategoryLoader.m in Sources */, DA88485B1CBAFB9800AB86E3 /* MGLUserLocation.m in Sources */, 927FBD011F4DB05500F8BF1F /* MGLMapSnapshotter.mm in Sources */, 350098BD1D480108004B2AF0 /* MGLVectorSource.mm in Sources */, @@ -2657,27 +2900,37 @@ DA35A2B81CCA9A5D00E826B2 /* MGLClockDirectionFormatter.m in Sources */, DAD1657A1CF4CDFF001FF4B9 /* MGLShapeCollection.mm in Sources */, DAF25719201901E200367EF5 /* MGLHillshadeStyleLayer.mm in Sources */, + 40834BF51FE05E1800C1BD0D /* MMETypes.m in Sources */, 35136D451D42275100C20EFD /* MGLSymbolStyleLayer.mm in Sources */, + 40834C491FE05F7500C1BD0D /* TrustKit.m in Sources */, 35599DED1D46F14E0048254D /* MGLStyleValue.mm in Sources */, DA8848211CBAFA6200AB86E3 /* MGLOfflinePack.mm in Sources */, 0778DD441F67556C00A73B34 /* MGLComputedShapeSource.mm in Sources */, 3557F7B21E1D27D300CCA5E6 /* MGLDistanceFormatter.m in Sources */, + 40834C4B1FE05F7500C1BD0D /* TSKPinningValidatorResult.m in Sources */, + 40834BE71FE05E1800C1BD0D /* MMEAPIClient.m in Sources */, DA8848591CBAFB9800AB86E3 /* MGLMapView.mm in Sources */, DA8848501CBAFB9800AB86E3 /* MGLAnnotationImage.m in Sources */, + 40834BF01FE05E1800C1BD0D /* MMELocationManager.m in Sources */, DA8848281CBAFA6200AB86E3 /* MGLShape.mm in Sources */, DA35A2B31CCA141D00E826B2 /* MGLCompassDirectionFormatter.m in Sources */, DD0902A91DB1929D00C5BDCE /* MGLNetworkConfiguration.m in Sources */, 35D13AB91D3D15E300AFB4E0 /* MGLStyleLayer.mm in Sources */, + 40834C4C1FE05F7500C1BD0D /* TSKTrustKitConfig.m in Sources */, DA35A2CB1CCAAAD200E826B2 /* NSValue+MGLAdditions.m in Sources */, 071BBB001EE7613F001FB02A /* MGLImageSource.mm in Sources */, DA8848321CBAFA6200AB86E3 /* NSString+MGLAdditions.m in Sources */, + 40834C441FE05F7500C1BD0D /* reporting_utils.m in Sources */, 408AA8581DAEDA1E00022900 /* NSDictionary+MGLAdditions.mm in Sources */, DA35A2A11CC9E95F00E826B2 /* MGLCoordinateFormatter.m in Sources */, 35305D481D22AA680007D005 /* NSData+MGLAdditions.mm in Sources */, + 40834BF61FE05E1800C1BD0D /* MMEUIApplicationWrapper.m in Sources */, DA8848291CBAFA6200AB86E3 /* MGLStyle.mm in Sources */, 357FE2DF1E02D2B20068B753 /* NSCoder+MGLAdditions.mm in Sources */, + 40834BF81FE05E1800C1BD0D /* NSData+MMEGZIP.m in Sources */, DA88481C1CBAFA6200AB86E3 /* MGLGeometry.mm in Sources */, 558DE7A21E5615E400C7916D /* MGLFoundation.mm in Sources */, + 40834BE91FE05E1800C1BD0D /* MMECommonEventData.m in Sources */, 3510FFF21D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm in Sources */, DA88481F1CBAFA6200AB86E3 /* MGLMultiPoint.mm in Sources */, DA88482B1CBAFA6200AB86E3 /* MGLTypes.m in Sources */, @@ -2685,21 +2938,29 @@ 404C26E41D89B877000AA13D /* MGLTileSource.mm in Sources */, 07D947541F67489200E37934 /* MGLAbstractShapeSource.mm in Sources */, 355AE0011E9281DA00F3939D /* MGLScaleBar.mm in Sources */, + 40834C451FE05F7500C1BD0D /* TSKBackgroundReporter.m in Sources */, DA88481D1CBAFA6200AB86E3 /* MGLMapCamera.mm in Sources */, DACA86282019218600E9693A /* MGLRasterDEMSource.mm in Sources */, DA8848261CBAFA6200AB86E3 /* MGLPolygon.mm in Sources */, 35B82BFA1D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm in Sources */, - DA8848521CBAFB9800AB86E3 /* MGLAPIClient.m in Sources */, + 40834C401FE05F7500C1BD0D /* configuration_utils.m in Sources */, + 40834BF91FE05E1800C1BD0D /* MMEReachability.m in Sources */, + 40834BF21FE05E1800C1BD0D /* MMENSURLSessionWrapper.m in Sources */, + 40834C471FE05F7500C1BD0D /* TSKReportsRateLimiter.m in Sources */, 966FCF4E1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m in Sources */, 8989B17E201A48EB0081CF59 /* MGLHeatmapStyleLayer.mm in Sources */, DA8848301CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.m in Sources */, + 40834BED1FE05E1800C1BD0D /* MMEEventLogger.m in Sources */, 353AFA161D65AB17005A69F4 /* NSDate+MGLAdditions.mm in Sources */, + 40834BF41FE05E1800C1BD0D /* MMETrustKitWrapper.m in Sources */, + 40834BEF1FE05E1800C1BD0D /* MMEEventsManager.m in Sources */, 35D13AC51D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm in Sources */, DA8848241CBAFA6200AB86E3 /* MGLOfflineStorage.mm in Sources */, DA88482A1CBAFA6200AB86E3 /* MGLTilePyramidOfflineRegion.mm in Sources */, 4049C29F1DB6CD6C00B3F799 /* MGLPointCollection.mm in Sources */, 35136D3F1D42273000C20EFD /* MGLLineStyleLayer.mm in Sources */, DA704CC41F65A475004B3F28 /* MGLMapAccessibilityElement.mm in Sources */, + 40834C421FE05F7500C1BD0D /* ssl_pin_verifier.m in Sources */, DA72620D1DEEE3480043BB89 /* MGLOpenGLStyleLayer.mm in Sources */, DA88481A1CBAFA6200AB86E3 /* MGLAccountManager.m in Sources */, 3510FFFB1D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.mm in Sources */, @@ -2707,7 +2968,6 @@ DA8848271CBAFA6200AB86E3 /* MGLPolyline.mm in Sources */, DA8848581CBAFB9800AB86E3 /* MGLMapboxEvents.m in Sources */, 35CE61841D4165D9004F2359 /* UIColor+MGLAdditions.mm in Sources */, - DA8848561CBAFB9800AB86E3 /* MGLLocationManager.m in Sources */, 3EA93369F61CF70AFA50465D /* MGLRendererConfiguration.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2718,28 +2978,43 @@ files = ( 35136D3A1D42271A00C20EFD /* MGLBackgroundStyleLayer.mm in Sources */, 3510FFED1D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm in Sources */, + 40834C501FE05F7600C1BD0D /* TSKSPKIHashCache.m in Sources */, 354B83991D2E873E005D9406 /* MGLUserLocationAnnotationView.m in Sources */, + 40834C001FE05E1800C1BD0D /* MMEEvent.m in Sources */, 9620BB3B1E69FE1700705A1D /* MGLSDKUpdateChecker.mm in Sources */, DAA4E4221CBB730400178DFB /* MGLPointAnnotation.mm in Sources */, + 40834C021FE05E1800C1BD0D /* MMEEventsConfiguration.m in Sources */, DAED38661D62D0FC00D7640F /* NSURL+MGLAdditions.m in Sources */, DAD165711CF41981001FF4B9 /* MGLFeature.mm in Sources */, 30E5781A1DAA855E0050F07E /* UIImage+MGLAdditions.mm in Sources */, 40EDA1C21CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */, + 40834C551FE05F7600C1BD0D /* vendor_identifier.m in Sources */, DAA4E4291CBB730400178DFB /* NSBundle+MGLAdditions.m in Sources */, - DAA4E42E1CBB730400178DFB /* MGLAPIClient.m in Sources */, + 40834BFF1FE05E1800C1BD0D /* MMEDependencyManager.m in Sources */, + 40834C4E1FE05F7600C1BD0D /* parse_configuration.m in Sources */, + 40834BFE1FE05E1800C1BD0D /* MMEConstants.m in Sources */, 35136D3D1D42272500C20EFD /* MGLCircleStyleLayer.mm in Sources */, DD9BE4FA1EB263F40079A3AF /* UIViewController+MGLAdditions.m in Sources */, 350098DF1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.mm in Sources */, DA6408DE1DA4E7D300908C90 /* MGLVectorStyleLayer.m in Sources */, 3566C7691D4A77BA008152BC /* MGLShapeSource.mm in Sources */, + 40834C0B1FE05E1800C1BD0D /* MMEUniqueIdentifier.m in Sources */, 400533031DB086490069F638 /* NSArray+MGLAdditions.mm in Sources */, + 40834C571FE05F7600C1BD0D /* TSKPinningValidator.m in Sources */, 35136D431D42274500C20EFD /* MGLRasterStyleLayer.mm in Sources */, 96036A04200565C700510F3D /* NSOrthography+MGLAdditions.m in Sources */, + 40834C071FE05E1800C1BD0D /* MMETimerManager.m in Sources */, 3538AA201D542239008EC33D /* MGLForegroundStyleLayer.mm in Sources */, DA00FC911D5EEB0D009AABC8 /* MGLAttributionInfo.mm in Sources */, + 40834C051FE05E1800C1BD0D /* MMENSDateWrapper.m in Sources */, + 40834C531FE05F7600C1BD0D /* TSKPinFailureReport.m in Sources */, + 40834BFA1FE05E1800C1BD0D /* CLLocation+MMEMobileEvents.m in Sources */, + 406E99BA1FFEFF1B00D9FFCC /* MMEEventLogReportViewController.m in Sources */, DAA4E4201CBB730400178DFB /* MGLOfflinePack.mm in Sources */, 966FCF551F3C323500F2B6DE /* MGLUserLocationHeadingArrowLayer.m in Sources */, DAA4E4331CBB730400178DFB /* MGLUserLocation.m in Sources */, + 406E99BC1FFF006D00D9FFCC /* MMEUINavigation.m in Sources */, + 40834BFC1FE05E1800C1BD0D /* MMECategoryLoader.m in Sources */, 927FBD021F4DB05500F8BF1F /* MGLMapSnapshotter.mm in Sources */, 350098BE1D480108004B2AF0 /* MGLVectorSource.mm in Sources */, 3566C76F1D4A8DFA008152BC /* MGLRasterSource.mm in Sources */, @@ -2750,27 +3025,36 @@ DAA4E4251CBB730400178DFB /* MGLShape.mm in Sources */, 35136D461D42275100C20EFD /* MGLSymbolStyleLayer.mm in Sources */, DAF2571A201901E200367EF5 /* MGLHillshadeStyleLayer.mm in Sources */, + 40834C091FE05E1800C1BD0D /* MMETypes.m in Sources */, 35599DEE1D46F14E0048254D /* MGLStyleValue.mm in Sources */, + 40834C561FE05F7600C1BD0D /* TrustKit.m in Sources */, DAA4E42B1CBB730400178DFB /* NSString+MGLAdditions.m in Sources */, DAA4E4261CBB730400178DFB /* MGLStyle.mm in Sources */, DAA32CC31E4C6B65006F8D24 /* MGLDistanceFormatter.m in Sources */, DAA4E41D1CBB730400178DFB /* MGLGeometry.mm in Sources */, + 40834C581FE05F7600C1BD0D /* TSKPinningValidatorResult.m in Sources */, + 40834BFB1FE05E1800C1BD0D /* MMEAPIClient.m in Sources */, DAA4E41F1CBB730400178DFB /* MGLMultiPoint.mm in Sources */, DD0902AA1DB1929D00C5BDCE /* MGLNetworkConfiguration.m in Sources */, + 40834C041FE05E1800C1BD0D /* MMELocationManager.m in Sources */, DA35A2B41CCA141D00E826B2 /* MGLCompassDirectionFormatter.m in Sources */, 35D13ABA1D3D15E300AFB4E0 /* MGLStyleLayer.mm in Sources */, 071BBAFF1EE7613E001FB02A /* MGLImageSource.mm in Sources */, DA35A2CC1CCAAAD200E826B2 /* NSValue+MGLAdditions.m in Sources */, + 40834C591FE05F7600C1BD0D /* TSKTrustKitConfig.m in Sources */, 408AA8591DAEDA1E00022900 /* NSDictionary+MGLAdditions.mm in Sources */, DAA4E4281CBB730400178DFB /* MGLTypes.m in Sources */, DA35A2A21CC9E95F00E826B2 /* MGLCoordinateFormatter.m in Sources */, + 40834C511FE05F7600C1BD0D /* reporting_utils.m in Sources */, 35305D491D22AA680007D005 /* NSData+MGLAdditions.mm in Sources */, 357FE2E01E02D2B20068B753 /* NSCoder+MGLAdditions.mm in Sources */, DAA4E42D1CBB730400178DFB /* MGLAnnotationImage.m in Sources */, + 40834C0A1FE05E1800C1BD0D /* MMEUIApplicationWrapper.m in Sources */, 558DE7A31E5615E400C7916D /* MGLFoundation.mm in Sources */, 3510FFF31D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm in Sources */, - DAA4E4301CBB730400178DFB /* MGLLocationManager.m in Sources */, + 40834C0C1FE05E1800C1BD0D /* NSData+MMEGZIP.m in Sources */, DAA4E4321CBB730400178DFB /* MGLMapView.mm in Sources */, + 40834BFD1FE05E1800C1BD0D /* MMECommonEventData.m in Sources */, DAA4E41E1CBB730400178DFB /* MGLMapCamera.mm in Sources */, FA68F14E1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm in Sources */, 1F7454921ECBB42C00021D39 /* MGLLight.mm in Sources */, @@ -2778,21 +3062,30 @@ 355AE0021E9281DA00F3939D /* MGLScaleBar.mm in Sources */, 4018B1C81CDC287F00F666AF /* MGLAnnotationView.mm in Sources */, 07D8C6FB1F67560100381808 /* MGLComputedShapeSource.mm in Sources */, + 40834C521FE05F7600C1BD0D /* TSKBackgroundReporter.m in Sources */, DAA4E4341CBB730400178DFB /* MGLFaux3DUserLocationAnnotationView.m in Sources */, DACA86292019218600E9693A /* MGLRasterDEMSource.mm in Sources */, 35B82BFB1D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm in Sources */, + 40834C4D1FE05F7600C1BD0D /* configuration_utils.m in Sources */, + 40834C0D1FE05E1800C1BD0D /* MMEReachability.m in Sources */, + 40834C061FE05E1800C1BD0D /* MMENSURLSessionWrapper.m in Sources */, + 40834C541FE05F7600C1BD0D /* TSKReportsRateLimiter.m in Sources */, DAA4E4311CBB730400178DFB /* MGLMapboxEvents.m in Sources */, 966FCF4F1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m in Sources */, DAA4E4231CBB730400178DFB /* MGLPolygon.mm in Sources */, 8989B17F201A48EB0081CF59 /* MGLHeatmapStyleLayer.mm in Sources */, 353AFA171D65AB17005A69F4 /* NSDate+MGLAdditions.mm in Sources */, + 40834C011FE05E1800C1BD0D /* MMEEventLogger.m in Sources */, 35D13AC61D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm in Sources */, + 40834C081FE05E1800C1BD0D /* MMETrustKitWrapper.m in Sources */, + 40834C031FE05E1800C1BD0D /* MMEEventsManager.m in Sources */, DAA4E42A1CBB730400178DFB /* NSProcessInfo+MGLAdditions.m in Sources */, DAA4E4211CBB730400178DFB /* MGLOfflineStorage.mm in Sources */, 4049C2A01DB6CD6C00B3F799 /* MGLPointCollection.mm in Sources */, 07D8C6FC1F67560400381808 /* MGLAbstractShapeSource.mm in Sources */, 35136D401D42273000C20EFD /* MGLLineStyleLayer.mm in Sources */, DA704CC51F65A475004B3F28 /* MGLMapAccessibilityElement.mm in Sources */, + 40834C4F1FE05F7600C1BD0D /* ssl_pin_verifier.m in Sources */, DA72620E1DEEE3480043BB89 /* MGLOpenGLStyleLayer.mm in Sources */, DAA4E42F1CBB730400178DFB /* MGLCompactCalloutView.m in Sources */, 3510FFFC1D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.mm in Sources */, @@ -3347,6 +3640,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "$SRCROOT/src/Mapbox-Prefix.pch"; HEADER_SEARCH_PATHS = ( "$(mbgl_core_INCLUDE_DIRECTORIES)", "$(mbgl_filesource_INCLUDE_DIRECTORIES)", @@ -3389,6 +3683,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "$SRCROOT/src/Mapbox-Prefix.pch"; HEADER_SEARCH_PATHS = ( "$(mbgl_core_INCLUDE_DIRECTORIES)", "$(mbgl_filesource_INCLUDE_DIRECTORIES)", diff --git a/platform/ios/resources/api_mapbox_com-digicert_2016.der b/platform/ios/resources/api_mapbox_com-digicert_2016.der deleted file mode 100644 index e8ef427f3302ce3bbc2f024ed67bf5a6b8aae199..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1913 zcma)7do)ye9KZL@ona8eokWpasnx^y-9eMGMV^&VlgLuWxW+7Im>HzU##Blw9jp~S zob-@QMLJv2`{kUn5usM0HX$h~DXP79O3Uv4vG<&N&i%fA-_P&!`+YwbsI}sNniZVS zKwt#nz)LI7Wc=A${xULitv8xD3yfm1r=@tNyc-3>x=aYf5xRiQV)&sf4Dk~ZqX9-6 zI+&iTR3ddD6)JpzOiY-9F|=D3<0K>`@WQB&mi@aYf(oBpjr=;_SI-Bzne zSr{3HFBe5g!$c~nT!srtMLem%U7W%AF{6l?z@BdlG*2L9&I0zf1i`26K%j;hggF>T zuWX>l^;JmYMJf{aQAk8G=^D*q1O^OQy3<)O3abT<2+Yvnr-2T0BAB)-86`>(DM*3? z^y%f8n3g?XU^|oQWd_F59yW#%0>Ta_>{JoBP^BPA74Gd%=mR}EYKSpuOb#P?`ie~( z~VplJx4OTTi!mvmFw%FLp6= zx4si@jc5lhbEl1C_7YE&5 zz3*eRU}wllaa4=m@6p^jS;_3p-q)Kt+Om%BD)ev=y=2wYtss53RB2UMC0j0O+>|Eo zGMXB6&q}v){~j#w$Mox;OJhl7XJ2l1#^Sr2dC$TiAV~;=8iJ zRXz5H`&mrCR|Up9f70GFyI0;+8u0WjSE(+qmpt6C&lF+65L}>U$bp&>2`nfc%_pHI zz&MHfeXZ}I`uyVV-i0ALWbK5y{{DIcU`{9KqsAzg)8GfW>z_%lNc-a1qR!{|D&23QW+08TOa zx*xe%88ZSISo$psNpT<%mR^A;q8tV@NOlmWwZH;o?lOq0-EvIYzUO0Q&x~ zXX$8WzdjK`5mXIFQ?C@3KylE)v+P}ECIQ?n<=BDSk{BIF`yBNBxaKoY5~}noM>TOE z2wv8VJ!*p>lmWQ|R|Fs1<(k&|xRzU6cQtgoZtz+Ij#t1oyx~O>T`oXjIeVZ)(`y)pN_VXu_h%r;#?DnResH z{;?F90AWAY4O&S)p^HX zjhojI=;hBj3nMrhC6_t(1(7bWU1LWvR# znQdUX6?bK=SYa(mzu+`yho1npAs*TPDxP?ipBiC3U_L2k(?H&~Q@c;U7%ZvpdZP@_ zZtB%9-e2_F3>-1&4>yf{QRvY(4T|Wr7zEXR2Qt9^kHt1Dn^uW<$UK_#_ zPNyb?eQ3VqRJa_m5@x48e%|~b5DoeikX~^9LHCwroz>U%^rzIlu9>W;;qpQz HC(-*a0#U0t diff --git a/platform/ios/resources/api_mapbox_com-digicert_2017.der b/platform/ios/resources/api_mapbox_com-digicert_2017.der deleted file mode 100644 index 4a190085abc17a1755a03953327b27ba9398e3eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2030 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 On`+ad^_QACseb{^<7OlP diff --git a/platform/ios/resources/api_mapbox_com-geotrust_2017.der b/platform/ios/resources/api_mapbox_com-geotrust_2017.der deleted file mode 100644 index 7bb9befbbf5a9dc15c5e6f590997582cc5e6eb83..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1758 zcmbVMc~BE)6yM!!B8CKr;fP3N!HWS+zJvw^qm_UhA|r^3a)<$Ahy)UoKu{PkK|$LJ z6c9l{Kq(@Zs33<`TTd#7qGCZ28@wnoNZ}7f1)4<5=uD@7bZ2&E-}~PC{oeb1Zy!(- z9sxD}YB3Ih5yT{+b#P>*#@^dx$~F1UxJe%%6K2|{dZoT4!>}P90-h{GFq44Wh!UvC z#sHQ%Fx44ED%n#Y*&vIRD=_bv2nGuSb9H_k)neMedJ`59uoiQ3#g<^6-+;LmWY$vP z#Nq(`6Qnhrbtc=9E>7G{~ z)k7;YjDKBtFPnXE6(Ya%w#}yei0PA@#v5BJ?P|K!Y4^gK^BvdN$^MNhEisu$vrCLTR6jB!^Qy9&V?~aZL z*IkCrh>(ea>a;EzLsca}bsVU&2s8ClH)0DDfNCH3mSCW@@%R}CZ0@Lok!x|3N+m-m zkSKIBug8)J? z`|&4{%h*3{gp18G zjF$FwWzv&wnTq>z|FU%-N)QY5iagdkxAfcmOG( z(x#c2b+pZ}THcWgm-YU&yiOun z)p+7u!NVs0gui{!+HS81B1803W1Ti+YB=Ao$GP`@q)S(4zq|kW+y$>ZqCYV2<_})~ zpkciBgkqs#pGys~9ow2UJdt&!=J<}2#z|qdvhEb9FanK6X2gRZ!2Ev|mj;&(J-tEjYj z{_Ec3a6naf=?R$B6%*R9H^*1OU2JyV^(eb$L%zReo}W*u7p+q|SoMDMxHKvyVGHGb z^~7VRgPGfo*?B9@_^eGS>tGpdamw_v4L=mj7e@^|RNh{a%xlIDhsNi1uvN}DT3lAP X`ziZzd_%ivn5ZGFE)pcbVYYt*fl7FM diff --git a/platform/ios/resources/api_mapbox_staging.der b/platform/ios/resources/api_mapbox_staging.der deleted file mode 100644 index 45f7df7c490f7a8e1b8c55bd5bf4098b6e89f613..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1334 zcmXqLVl^^oVv$+SAZO- zXDDeP4t61rfRkLYerh#!HaV5ru{Yz}yS=r-?}Bv`USvLcC>{OU>A|-LFMkLd zvE(G)%IwV(*G+Fo^=wU>wyrz(c}cYK>eW7hN0~GZmkQiE@0YuuU8HV{&U3D%cY;YO-4eB(tU@239Dy$0e|tre*y;DZk_R4&$W;Dd7hB zlY0b=fBbuWz%}LUl>JfvcOGx4d3~m<)orTsHuq;fUc%bd%T`Hz6yy(^V0zEap(KAt z=!%m8hO8Qf??SRf6XSDWh&(os2L_|8GK++PSc3?EL!wNRVd7PNjpw~?7F%>49=rL-Ko+Ec zk420{L~iAi*t;*^bT8X0AnzL=#ClP(CBi@(BrV9IVxZK75fx2*Xi>ulQq0fD_@9M^ znTd4)#B5a-F#{1c4sA9@R#tXqMmUSfARDAyo+ZT~$v}UB?gH&L&5V+g0xNy}-}MBUWBvV52q_ZQ?^Cbp6AioHM!kNi{ z0XZ`OivVC|U}Wga`=pv=T6HJc)wg{5NwrHm_wIRKvetD;WBbHKQ@@_Rb^gXDG1li# zD*vw(oW)rE?WjuOCq@Yk6&7{3L#ee(DtNzmeNyLs@VokWSdRH!!!OA{+s>bu=x7|) z_bWxJNbmt{Wk z9{Tlg+QDO+y4UF0c2&e0bF{g#))l4PUfMVHM(A^i11<;TBToKO+1aqrwAWO|;yELe zL!^w>m(G({9&T~?RDZc{`me{HPZbKPr^S3Nz57~tFWdhBhmWTMUtZi%x28QZQnPs_ ZYj@!FM245Iu2gQRzNY$WN_vL^BLIrZ(PjVu diff --git a/platform/ios/src/MGLAPIClient.h b/platform/ios/src/MGLAPIClient.h deleted file mode 100644 index 4e5ea3b5e..000000000 --- a/platform/ios/src/MGLAPIClient.h +++ /dev/null @@ -1,15 +0,0 @@ -#import - -#import "MGLMapboxEvents.h" -#import "MGLTypes.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface MGLAPIClient : NSObject - -- (void)postEvents:(NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events completionHandler:(nullable void (^)(NSError * _Nullable error))completionHandler; -- (void)postEvent:(MGLMapboxEventAttributes *)event completionHandler:(nullable void (^)(NSError * _Nullable error))completionHandler; - -@end - -NS_ASSUME_NONNULL_END diff --git a/platform/ios/src/MGLAPIClient.m b/platform/ios/src/MGLAPIClient.m deleted file mode 100644 index 8a987d76d..000000000 --- a/platform/ios/src/MGLAPIClient.m +++ /dev/null @@ -1,202 +0,0 @@ -#import "MGLAPIClient.h" -#import "NSBundle+MGLAdditions.h" -#import "NSData+MGLAdditions.h" -#import "MGLAccountManager.h" - -static NSString * const MGLAPIClientUserAgentBase = @"MapboxEventsiOS"; -static NSString * const MGLAPIClientBaseURL = @"https://events.mapbox.com"; -static NSString * const MGLAPIClientEventsPath = @"events/v2"; - -static NSString * const MGLAPIClientHeaderFieldUserAgentKey = @"User-Agent"; -static NSString * const MGLAPIClientHeaderFieldContentTypeKey = @"Content-Type"; -static NSString * const MGLAPIClientHeaderFieldContentTypeValue = @"application/json"; -static NSString * const MGLAPIClientHeaderFieldContentEncodingKey = @"Content-Encoding"; -static NSString * const MGLAPIClientHTTPMethodPost = @"POST"; - -@interface MGLAPIClient () - -@property (nonatomic, copy) NSURLSession *session; -@property (nonatomic, copy) NSURL *baseURL; -@property (nonatomic, copy) NSData *digicertCert_2016; -@property (nonatomic, copy) NSData *geoTrustCert_2016; -@property (nonatomic, copy) NSData *digicertCert_2017; -@property (nonatomic, copy) NSData *geoTrustCert_2017; -@property (nonatomic, copy) NSData *testServerCert; -@property (nonatomic, copy) NSString *userAgent; -@property (nonatomic) BOOL usesTestServer; - -@end - -@implementation MGLAPIClient - -- (instancetype)init { - self = [super init]; - if (self) { - _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] - delegate:self delegateQueue:nil]; - [self loadCertificates]; - [self setupBaseURL]; - [self setupUserAgent]; - } - return self; -} - -#pragma mark Public API - -- (void)postEvents:(nonnull NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events completionHandler:(nullable void (^)(NSError * _Nullable error))completionHandler { - __block NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:[self requestForEvents:events] - completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; - NSError *statusError = nil; - if (httpResponse.statusCode >= 400) { - NSString *description = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"API_CLIENT_400_DESC", nil, nil, @"The session data task failed. Original request was: %@", nil), dataTask.originalRequest]; - NSString *reason = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"API_CLIENT_400_REASON", nil, nil, @"The status code was %ld", nil), (long)httpResponse.statusCode]; - NSDictionary *userInfo = @{NSLocalizedDescriptionKey: description, - NSLocalizedFailureReasonErrorKey: reason}; - statusError = [NSError errorWithDomain:MGLErrorDomain code:1 userInfo:userInfo]; - } - if (completionHandler) { - error = error ?: statusError; - completionHandler(error); - } - dataTask = nil; - }]; - [dataTask resume]; -} - -- (void)postEvent:(nonnull MGLMapboxEventAttributes *)event completionHandler:(nullable void (^)(NSError * _Nullable error))completionHandler { - [self postEvents:@[event] completionHandler:completionHandler]; -} - -#pragma mark Utilities - -- (NSURLRequest *)requestForEvents:(NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events { - NSString *path = [NSString stringWithFormat:@"%@?access_token=%@", MGLAPIClientEventsPath, [MGLAccountManager accessToken]]; - NSURL *url = [NSURL URLWithString:path relativeToURL:self.baseURL]; - NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; - [request setValue:self.userAgent forHTTPHeaderField:MGLAPIClientHeaderFieldUserAgentKey]; - [request setValue:MGLAPIClientHeaderFieldContentTypeValue forHTTPHeaderField:MGLAPIClientHeaderFieldContentTypeKey]; - [request setHTTPMethod:MGLAPIClientHTTPMethodPost]; - - NSData *jsonData = [self serializedDataForEvents:events]; - - // Compressing less than 3 events can have a negative impact on the size. - if (events.count > 2) { - NSData *compressedData = [jsonData mgl_compressedData]; - [request setValue:@"deflate" forHTTPHeaderField:MGLAPIClientHeaderFieldContentEncodingKey]; - [request setHTTPBody:compressedData]; - } - - // Set JSON data if events.count were less than 3 or something went wrong with compressing HTTP body data. - if (!request.HTTPBody) { - [request setValue:nil forHTTPHeaderField:MGLAPIClientHeaderFieldContentEncodingKey]; - [request setHTTPBody:jsonData]; - } - - return [request copy]; -} - -- (void)setupBaseURL { - NSString *testServerURLString = [[NSUserDefaults standardUserDefaults] stringForKey:@"MGLTelemetryTestServerURL"]; - NSURL *testServerURL = [NSURL URLWithString:testServerURLString]; - if (testServerURL && [testServerURL.scheme isEqualToString:@"https"]) { - self.baseURL = testServerURL; - self.usesTestServer = YES; - } else { - self.baseURL = [NSURL URLWithString:MGLAPIClientBaseURL]; - } -} - -- (void)loadCertificates { - NSData *certificate; - [self loadCertificate:&certificate withResource:@"api_mapbox_com-geotrust_2016"]; - self.geoTrustCert_2016 = certificate; - [self loadCertificate:&certificate withResource:@"api_mapbox_com-digicert_2016"]; - self.digicertCert_2016 = certificate; - [self loadCertificate:&certificate withResource:@"api_mapbox_com-geotrust_2017"]; - self.geoTrustCert_2017 = certificate; - [self loadCertificate:&certificate withResource:@"api_mapbox_com-digicert_2017"]; - self.digicertCert_2017 = certificate; - [self loadCertificate:&certificate withResource:@"api_mapbox_staging"]; - self.testServerCert = certificate; -} - -- (void)loadCertificate:(NSData **)certificate withResource:(NSString *)resource { - NSBundle *frameworkBundle = [NSBundle mgl_frameworkBundle]; - NSString *cerPath = [frameworkBundle pathForResource:resource ofType:@"der"]; - if (cerPath != nil) { - *certificate = [NSData dataWithContentsOfFile:cerPath]; - } -} - -- (void)setupUserAgent { - NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"]; - NSString *appVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; - NSString *appBuildNumber = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]; - NSString *semanticVersion = [NSBundle mgl_frameworkInfoDictionary][@"MGLSemanticVersionString"]; - NSString *shortVersion = [NSBundle mgl_frameworkInfoDictionary][@"CFBundleShortVersionString"]; - NSString *sdkVersion = semanticVersion ?: shortVersion; - _userAgent = [NSString stringWithFormat:@"%@/%@/%@ %@/%@", appName, appVersion, appBuildNumber, MGLAPIClientUserAgentBase, sdkVersion]; -} - -#pragma mark - JSON Serialization - -- (NSData *)serializedDataForEvents:(NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events { - return [NSJSONSerialization dataWithJSONObject:events options:0 error:nil]; -} - -#pragma mark NSURLSessionDelegate - -- (BOOL)evaluateCertificateWithCertificateData:(NSData *)certificateData keyCount:(CFIndex)keyCount serverTrust:(SecTrustRef)serverTrust challenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^) (NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { - for (int lc = 0; lc < keyCount; lc++) { - SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, lc); - NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate)); - if ([remoteCertificateData isEqualToData:certificateData]) { - completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); - return YES; - } - } - return NO; -} - -- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^) (NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { - - if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { - SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust]; - SecTrustResultType trustResult; - - // Validate the certificate chain with the device's trust store anyway this *might* use revocation checking - SecTrustEvaluate(serverTrust, &trustResult); - - BOOL found = NO; // For clarity; we start in a state where the challange has not been completed and no certificate has been found - - if (trustResult == kSecTrustResultUnspecified) { - // Look for a pinned certificate in the server's certificate chain - CFIndex numKeys = SecTrustGetCertificateCount(serverTrust); - - // Check certs in the following order: digicert 2016, digicert 2017, geotrust 2016, geotrust 2017 - found = [self evaluateCertificateWithCertificateData:self.digicertCert_2016 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler]; - if (!found) { - found = [self evaluateCertificateWithCertificateData:self.digicertCert_2017 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler]; - } - if (!found) { - found = [self evaluateCertificateWithCertificateData:self.geoTrustCert_2016 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler]; - } - if (!found) { - found = [self evaluateCertificateWithCertificateData:self.geoTrustCert_2017 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler]; - } - - // If challenge can't be completed with any of the above certs, then try the test server if the app is configured to use the test server - if (!found && _usesTestServer) { - found = [self evaluateCertificateWithCertificateData:self.testServerCert keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler]; - } - } - - if (!found) { - // No certificate was found so cancel the connection. - completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); - } - } -} - -@end diff --git a/platform/ios/src/MGLLocationManager.h b/platform/ios/src/MGLLocationManager.h deleted file mode 100644 index ea2380181..000000000 --- a/platform/ios/src/MGLLocationManager.h +++ /dev/null @@ -1,25 +0,0 @@ -#import -#import - -@protocol MGLLocationManagerDelegate; - -@interface MGLLocationManager : NSObject - -@property (nonatomic, weak) id delegate; - -- (void)startUpdatingLocation; -- (void)stopUpdatingLocation; - -@end - -@protocol MGLLocationManagerDelegate - -@optional - -- (void)locationManager:(MGLLocationManager *)locationManager didUpdateLocations:(NSArray *)locations; -- (void)locationManagerDidStartLocationUpdates:(MGLLocationManager *)locationManager; -- (void)locationManagerBackgroundLocationUpdatesDidTimeout:(MGLLocationManager *)locationManager; -- (void)locationManagerBackgroundLocationUpdatesDidAutomaticallyPause:(MGLLocationManager *)locationManager; -- (void)locationManagerDidStopLocationUpdates:(MGLLocationManager *)locationManager; - -@end diff --git a/platform/ios/src/MGLLocationManager.m b/platform/ios/src/MGLLocationManager.m deleted file mode 100644 index 85ef4ca48..000000000 --- a/platform/ios/src/MGLLocationManager.m +++ /dev/null @@ -1,175 +0,0 @@ -#import "MGLLocationManager.h" -#import "MGLTelemetryConfig.h" -#import - -static const NSTimeInterval MGLLocationManagerHibernationTimeout = 300.0; -static const NSTimeInterval MGLLocationManagerHibernationPollInterval = 5.0; -static const CLLocationDistance MGLLocationManagerDistanceFilter = 5.0; -static NSString * const MGLLocationManagerRegionIdentifier = @"MGLLocationManagerRegionIdentifier.fence.center"; - -@interface MGLLocationManager () - -@property (nonatomic) CLLocationManager *standardLocationManager; -@property (nonatomic) BOOL hostAppHasBackgroundCapability; -@property (nonatomic, getter=isUpdatingLocation) BOOL updatingLocation; -@property (nonatomic) NSDate *backgroundLocationServiceTimeoutAllowedDate; -@property (nonatomic) NSTimer *backgroundLocationServiceTimeoutTimer; - -@end - -@implementation MGLLocationManager - -- (instancetype)init { - self = [super init]; - if (self) { - NSArray *backgroundModes = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIBackgroundModes"]; - _hostAppHasBackgroundCapability = [backgroundModes containsObject:@"location"]; - } - return self; -} - -- (void)startUpdatingLocation { - if ([self isUpdatingLocation]) { - return; - } - - [self configurePassiveStandardLocationManager]; - [self startLocationServices]; -} - -- (void)stopUpdatingLocation { - if ([self isUpdatingLocation]) { - [self.standardLocationManager stopUpdatingLocation]; - [self.standardLocationManager stopMonitoringSignificantLocationChanges]; - self.updatingLocation = NO; - if ([self.delegate respondsToSelector:@selector(locationManagerDidStopLocationUpdates:)]) { - [self.delegate locationManagerDidStopLocationUpdates:self]; - } - } - if(self.standardLocationManager.monitoredRegions.count > 0) { - for(CLRegion *region in self.standardLocationManager.monitoredRegions) { - if([region.identifier isEqualToString:MGLLocationManagerRegionIdentifier]) { - [self.standardLocationManager stopMonitoringForRegion:region]; - } - } - } -} - -#pragma mark - Utilities - -- (void)configurePassiveStandardLocationManager { - if (!self.standardLocationManager) { - CLLocationManager *standardLocationManager = [[CLLocationManager alloc] init]; - standardLocationManager.delegate = self; - standardLocationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers; - standardLocationManager.distanceFilter = MGLLocationManagerDistanceFilter; - self.standardLocationManager = standardLocationManager; - } -} - -- (void)startLocationServices { - CLAuthorizationStatus authorizationStatus = [CLLocationManager authorizationStatus]; -#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000 - BOOL authorizedAlways = authorizationStatus == kCLAuthorizationStatusAuthorizedAlways; -#else - BOOL authorizedAlways = authorizationStatus == kCLAuthorizationStatusAuthorized; -#endif - if (authorizedAlways || authorizationStatus == kCLAuthorizationStatusAuthorizedWhenInUse) { - // If the host app can run in the background with `always` location permissions then allow background - // updates and start the significant location change service and background timeout timer - if (self.hostAppHasBackgroundCapability && authorizedAlways) { - [self.standardLocationManager startMonitoringSignificantLocationChanges]; - [self startBackgroundTimeoutTimer]; - // On iOS 9 and above also allow background location updates - if ([self.standardLocationManager respondsToSelector:@selector(allowsBackgroundLocationUpdates)]) { - self.standardLocationManager.allowsBackgroundLocationUpdates = YES; - } - } - - [self.standardLocationManager startUpdatingLocation]; - self.updatingLocation = YES; - if ([self.delegate respondsToSelector:@selector(locationManagerDidStartLocationUpdates:)]) { - [self.delegate locationManagerDidStartLocationUpdates:self]; - } - } -} - -- (void)timeoutAllowedCheck { - if (self.backgroundLocationServiceTimeoutAllowedDate == nil) { - return; - } - - if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive || - [UIApplication sharedApplication].applicationState == UIApplicationStateInactive ) { - [self startBackgroundTimeoutTimer]; - return; - } - - NSTimeInterval timeIntervalSinceTimeoutAllowed = [[NSDate date] timeIntervalSinceDate:self.backgroundLocationServiceTimeoutAllowedDate]; - if (timeIntervalSinceTimeoutAllowed > 0) { - [self.standardLocationManager stopUpdatingLocation]; - self.backgroundLocationServiceTimeoutAllowedDate = nil; - if ([self.delegate respondsToSelector:@selector(locationManagerBackgroundLocationUpdatesDidTimeout:)]) { - [self.delegate locationManagerBackgroundLocationUpdatesDidTimeout:self]; - } - } -} - -- (void)startBackgroundTimeoutTimer { - [self.backgroundLocationServiceTimeoutTimer invalidate]; - self.backgroundLocationServiceTimeoutAllowedDate = [[NSDate date] dateByAddingTimeInterval:MGLLocationManagerHibernationTimeout]; - self.backgroundLocationServiceTimeoutTimer = [NSTimer scheduledTimerWithTimeInterval:MGLLocationManagerHibernationPollInterval target:self selector:@selector(timeoutAllowedCheck) userInfo:nil repeats:YES]; -} - -- (void)establishRegionMonitoringForLocation:(CLLocation *)location { - CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:location.coordinate radius:MGLTelemetryConfig.sharedConfig.MGLLocationManagerHibernationRadius identifier:MGLLocationManagerRegionIdentifier]; - region.notifyOnEntry = NO; - region.notifyOnExit = YES; - [self.standardLocationManager startMonitoringForRegion:region]; -} - -#pragma mark - CLLocationManagerDelegate - -- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status { - switch (status) { -#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000 - case kCLAuthorizationStatusAuthorizedAlways: -#else - case kCLAuthorizationStatusAuthorized: -#endif - case kCLAuthorizationStatusAuthorizedWhenInUse: - [self startUpdatingLocation]; - break; - default: - [self stopUpdatingLocation]; - break; - } -} - -- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { - CLLocation *location = locations.lastObject; - if (location.speed > 0.0) { - [self startBackgroundTimeoutTimer]; - } - if (self.standardLocationManager.monitoredRegions.count == 0 || location.horizontalAccuracy < MGLTelemetryConfig.sharedConfig.MGLLocationManagerHibernationRadius) { - [self establishRegionMonitoringForLocation:location]; - } - if ([self.delegate respondsToSelector:@selector(locationManager:didUpdateLocations:)]) { - [self.delegate locationManager:self didUpdateLocations:locations]; - } -} - -- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region { - [self startBackgroundTimeoutTimer]; - [self.standardLocationManager startUpdatingLocation]; -} - -- (void)locationManagerDidPauseLocationUpdates:(CLLocationManager *)manager { - if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) { - if ([self.delegate respondsToSelector:@selector(locationManagerBackgroundLocationUpdatesDidAutomaticallyPause:)]) { - [self.delegate locationManagerBackgroundLocationUpdatesDidAutomaticallyPause:self]; - } - } -} - -@end diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 72fa4613f..927450b51 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -66,6 +66,7 @@ #import "MGLStyle_Private.h" #import "MGLStyleLayer_Private.h" #import "MGLMapboxEvents.h" +#import "MMEConstants.h" #import "MGLSDKUpdateChecker.h" #import "MGLCompactCalloutView.h" #import "MGLAnnotationContainerView.h" @@ -567,7 +568,8 @@ public: _targetCoordinate = kCLLocationCoordinate2DInvalid; if ([UIApplication sharedApplication].applicationState != UIApplicationStateBackground) { - [MGLMapboxEvents pushEvent:MGLEventTypeMapLoad withAttributes:@{}]; + [MGLMapboxEvents pushTurnstileEvent]; + [MGLMapboxEvents pushEvent:MMEEventTypeMapLoad withAttributes:@{}]; } } @@ -1219,7 +1221,8 @@ public: [self validateLocationServices]; - [MGLMapboxEvents pushEvent:MGLEventTypeMapLoad withAttributes:@{}]; + [MGLMapboxEvents pushTurnstileEvent]; + [MGLMapboxEvents pushEvent:MMEEventTypeMapLoad withAttributes:@{}]; } } @@ -1331,7 +1334,7 @@ public: if (pan.state == UIGestureRecognizerStateBegan) { - [self trackGestureEvent:MGLEventGesturePanStart forRecognizer:pan]; + [self trackGestureEvent:MMEEventGesturePanStart forRecognizer:pan]; self.userTrackingMode = MGLUserTrackingModeNone; @@ -1379,10 +1382,10 @@ public: CLLocationCoordinate2D panCoordinate = [self convertPoint:pointInView toCoordinateFromView:pan.view]; int zoom = round([self zoomLevel]); - [MGLMapboxEvents pushEvent:MGLEventTypeMapDragEnd withAttributes:@{ - MGLEventKeyLatitude: @(panCoordinate.latitude), - MGLEventKeyLongitude: @(panCoordinate.longitude), - MGLEventKeyZoomLevel: @(zoom) + [MGLMapboxEvents pushEvent:MMEEventTypeMapDragEnd withAttributes:@{ + MMEEventKeyLatitude: @(panCoordinate.latitude), + MMEEventKeyLongitude: @(panCoordinate.longitude), + MMEEventKeyZoomLevel: @(zoom) }]; } @@ -1401,7 +1404,7 @@ public: if (pinch.state == UIGestureRecognizerStateBegan) { - [self trackGestureEvent:MGLEventGesturePinchStart forRecognizer:pinch]; + [self trackGestureEvent:MMEEventGesturePinchStart forRecognizer:pinch]; self.scale = powf(2, _mbglMap->getZoom()); @@ -1500,7 +1503,7 @@ public: if (rotate.state == UIGestureRecognizerStateBegan) { - [self trackGestureEvent:MGLEventGestureRotateStart forRecognizer:rotate]; + [self trackGestureEvent:MMEEventGestureRotateStart forRecognizer:rotate]; self.angle = MGLRadiansFromDegrees(_mbglMap->getBearing()) * -1; @@ -1572,7 +1575,7 @@ public: { return; } - [self trackGestureEvent:MGLEventGestureSingleTap forRecognizer:singleTap]; + [self trackGestureEvent:MMEEventGestureSingleTap forRecognizer:singleTap]; if (self.mapViewProxyAccessibilityElement.accessibilityElementIsFocused) { @@ -1694,7 +1697,7 @@ public: if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera]) { - [self trackGestureEvent:MGLEventGestureDoubleTap forRecognizer:doubleTap]; + [self trackGestureEvent:MMEEventGestureDoubleTap forRecognizer:doubleTap]; mbgl::ScreenCoordinate center(gesturePoint.x, gesturePoint.y); _mbglMap->setZoom(newZoom, center, MGLDurationFromTimeInterval(MGLAnimationDuration)); @@ -1723,7 +1726,7 @@ public: if (twoFingerTap.state == UIGestureRecognizerStateBegan) { - [self trackGestureEvent:MGLEventGestureTwoFingerSingleTap forRecognizer:twoFingerTap]; + [self trackGestureEvent:MMEEventGestureTwoFingerSingleTap forRecognizer:twoFingerTap]; [self notifyGestureDidBegin]; } @@ -1762,7 +1765,7 @@ public: if (quickZoom.state == UIGestureRecognizerStateBegan) { - [self trackGestureEvent:MGLEventGestureQuickZoom forRecognizer:quickZoom]; + [self trackGestureEvent:MMEEventGestureQuickZoom forRecognizer:quickZoom]; self.scale = powf(2, _mbglMap->getZoom()); @@ -1807,7 +1810,7 @@ public: if (twoFingerDrag.state == UIGestureRecognizerStateBegan) { - [self trackGestureEvent:MGLEventGesturePitchStart forRecognizer:twoFingerDrag]; + [self trackGestureEvent:MMEEventGesturePitchStart forRecognizer:twoFingerDrag]; [self notifyGestureDidBegin]; } @@ -2012,11 +2015,11 @@ public: CLLocationCoordinate2D gestureCoordinate = [self convertPoint:pointInView toCoordinateFromView:recognizer.view]; int zoom = round([self zoomLevel]); - [MGLMapboxEvents pushEvent:MGLEventTypeMapTap withAttributes:@{ - MGLEventKeyLatitude: @(gestureCoordinate.latitude), - MGLEventKeyLongitude: @(gestureCoordinate.longitude), - MGLEventKeyZoomLevel: @(zoom), - MGLEventKeyGestureID: gestureID + [MGLMapboxEvents pushEvent:MMEEventTypeMapTap withAttributes:@{ + MMEEventKeyLatitude: @(gestureCoordinate.latitude), + MMEEventKeyLongitude: @(gestureCoordinate.longitude), + MMEEventKeyZoomLevel: @(zoom), + MMEEventKeyGestureID: gestureID }]; } diff --git a/platform/ios/src/MGLMapboxEvents.h b/platform/ios/src/MGLMapboxEvents.h index 98f59ffd3..cbac57879 100644 --- a/platform/ios/src/MGLMapboxEvents.h +++ b/platform/ios/src/MGLMapboxEvents.h @@ -1,44 +1,17 @@ #import - -#import "MGLTypes.h" +#import "MMEEventsManager.h" NS_ASSUME_NONNULL_BEGIN -// Event types -extern NSString *const MGLEventTypeAppUserTurnstile; -extern NSString *const MGLEventTypeMapLoad; -extern NSString *const MGLEventTypeMapTap; -extern NSString *const MGLEventTypeMapDragEnd; -extern NSString *const MGLEventTypeLocation; - -// Event keys -extern NSString *const MGLEventKeyLatitude; -extern NSString *const MGLEventKeyLongitude; -extern NSString *const MGLEventKeyZoomLevel; -extern NSString *const MGLEventKeyGestureID; - -// Gestures -extern NSString *const MGLEventGestureSingleTap; -extern NSString *const MGLEventGestureDoubleTap; -extern NSString *const MGLEventGestureTwoFingerSingleTap; -extern NSString *const MGLEventGestureQuickZoom; -extern NSString *const MGLEventGesturePanStart; -extern NSString *const MGLEventGesturePinchStart; -extern NSString *const MGLEventGestureRotateStart; -extern NSString *const MGLEventGesturePitchStart; - -typedef NS_DICTIONARY_OF(NSString *, id) MGLMapboxEventAttributes; -typedef NS_MUTABLE_DICTIONARY_OF(NSString *, id) MGLMutableMapboxEventAttributes; - @interface MGLMapboxEvents : NSObject -+ (nullable instancetype)sharedManager; ++ (nullable instancetype)sharedInstance; -// You must call these methods from the main thread. -// -+ (void)pushEvent:(NSString *)event withAttributes:(MGLMapboxEventAttributes *)attributeDictionary; -+ (void)ensureMetricsOptoutExists; ++ (void)setupWithAccessToken:(NSString *)accessToken; ++ (void)pushTurnstileEvent; ++ (void)pushEvent:(NSString *)event withAttributes:(MMEMapboxEventAttributes *)attributeDictionary; + (void)flush; ++ (void)ensureMetricsOptoutExists; @end diff --git a/platform/ios/src/MGLMapboxEvents.m b/platform/ios/src/MGLMapboxEvents.m index 273af5b3b..05f291d8a 100644 --- a/platform/ios/src/MGLMapboxEvents.m +++ b/platform/ios/src/MGLMapboxEvents.m @@ -1,646 +1,166 @@ #import "MGLMapboxEvents.h" -#import -#import -#import "MGLAccountManager.h" +#import "NSBundle+MGLAdditions.h" #import "NSProcessInfo+MGLAdditions.h" -#import "NSException+MGLAdditions.h" -#import "MGLAPIClient.h" -#import "MGLLocationManager.h" -#import "MGLTelemetryConfig.h" -#include -#include +static NSString * const MGLAPIClientUserAgentBase = @"mapbox-maps-ios"; +static NSString * const MGLMapboxAccountType = @"MGLMapboxAccountType"; +static NSString * const MGLMapboxMetricsEnabled = @"MGLMapboxMetricsEnabled"; +static NSString * const MGLMapboxMetricsDebugLoggingEnabled = @"MGLMapboxMetricsDebugLoggingEnabled"; +static NSString * const MGLTelemetryAccessToken = @"MGLTelemetryAccessToken"; +static NSString * const MGLTelemetryBaseURL = @"MGLTelemetryBaseURL"; -// Event types -NSString *const MGLEventTypeAppUserTurnstile = @"appUserTurnstile"; -NSString *const MGLEventTypeMapLoad = @"map.load"; -NSString *const MGLEventTypeMapTap = @"map.click"; -NSString *const MGLEventTypeMapDragEnd = @"map.dragend"; -NSString *const MGLEventTypeLocation = @"location"; -NSString *const MGLEventTypeLocalDebug = @"debug"; +@interface MGLMapboxEvents () -// Gestures -NSString *const MGLEventGestureSingleTap = @"SingleTap"; -NSString *const MGLEventGestureDoubleTap = @"DoubleTap"; -NSString *const MGLEventGestureTwoFingerSingleTap = @"TwoFingerTap"; -NSString *const MGLEventGestureQuickZoom = @"QuickZoom"; -NSString *const MGLEventGesturePanStart = @"Pan"; -NSString *const MGLEventGesturePinchStart = @"Pinch"; -NSString *const MGLEventGestureRotateStart = @"Rotation"; -NSString *const MGLEventGesturePitchStart = @"Pitch"; - -// Event keys -NSString *const MGLEventKeyLatitude = @"lat"; -NSString *const MGLEventKeyLongitude = @"lng"; -NSString *const MGLEventKeyZoomLevel = @"zoom"; -NSString *const MGLEventKeySpeed = @"speed"; -NSString *const MGLEventKeyCourse = @"course"; -NSString *const MGLEventKeyGestureID = @"gesture"; -NSString *const MGLEventHorizontalAccuracy = @"horizontalAccuracy"; -NSString *const MGLEventKeyLocalDebugDescription = @"debug.description"; - -static NSString *const MGLEventKeyEvent = @"event"; -static NSString *const MGLEventKeyCreated = @"created"; -static NSString *const MGLEventKeyVendorID = @"userId"; -static NSString *const MGLEventKeyModel = @"model"; -static NSString *const MGLEventKeyEnabledTelemetry = @"enabled.telemetry"; -static NSString *const MGLEventKeyOperatingSystem = @"operatingSystem"; -static NSString *const MGLEventKeyResolution = @"resolution"; -static NSString *const MGLEventKeyAccessibilityFontScale = @"accessibilityFontScale"; -static NSString *const MGLEventKeyOrientation = @"orientation"; -static NSString *const MGLEventKeyPluggedIn = @"pluggedIn"; -static NSString *const MGLEventKeyWifi = @"wifi"; -static NSString *const MGLEventKeySource = @"source"; -static NSString *const MGLEventKeySessionId = @"sessionId"; -static NSString *const MGLEventKeyApplicationState = @"applicationState"; -static NSString *const MGLEventKeyAltitude = @"altitude"; - -static NSString *const MGLMapboxAccountType = @"MGLMapboxAccountType"; -static NSString *const MGLMapboxMetricsEnabled = @"MGLMapboxMetricsEnabled"; - -// SDK event source -static NSString *const MGLEventSource = @"mapbox"; - -// Event application state -static NSString *const MGLApplicationStateForeground = @"Foreground"; -static NSString *const MGLApplicationStateBackground = @"Background"; -static NSString *const MGLApplicationStateInactive = @"Inactive"; -static NSString *const MGLApplicationStateUnknown = @"Unknown"; - -const NSUInteger MGLMaximumEventsPerFlush = 180; -const NSTimeInterval MGLFlushInterval = 180; - -@interface MGLMapboxEventsData : NSObject - -@property (nonatomic) NSString *vendorId; -@property (nonatomic) NSString *model; -@property (nonatomic) NSString *iOSVersion; -@property (nonatomic) CGFloat scale; +@property (nonatomic) MMEEventsManager *eventsManager; +@property (nonatomic) NSURL *baseURL; +@property (nonatomic, copy) NSString *accessToken; @end +@implementation MGLMapboxEvents -@implementation MGLMapboxEventsData - -- (instancetype)init { - if (self = [super init]) { - _vendorId = [[[UIDevice currentDevice] identifierForVendor] UUIDString]; - _model = [self sysInfoByName:"hw.machine"]; - _iOSVersion = [NSString stringWithFormat:@"%@ %@", [UIDevice currentDevice].systemName, [UIDevice currentDevice].systemVersion]; - if ([UIScreen instancesRespondToSelector:@selector(nativeScale)]) { - _scale = [UIScreen mainScreen].nativeScale; - } else { - _scale = [UIScreen mainScreen].scale; - } - } - return self; -} - -- (NSString *)sysInfoByName:(char *)typeSpecifier { - size_t size; - sysctlbyname(typeSpecifier, NULL, &size, NULL, 0); - - char *answer = malloc(size); - sysctlbyname(typeSpecifier, answer, &size, NULL, 0); - - NSString *results = [NSString stringWithCString:answer encoding: NSUTF8StringEncoding]; - - free(answer); - return results; -} - -@end - -@interface MGLMapboxEvents () - -@property (nonatomic) MGLMapboxEventsData *data; -@property (nonatomic, copy) NSString *appBundleId; -@property (nonatomic, readonly) NSString *instanceID; -@property (nonatomic, copy) NSString *dateForDebugLogFile; -@property (nonatomic) NSDateFormatter *rfc3339DateFormatter; -@property (nonatomic) MGLAPIClient *apiClient; -@property (nonatomic) BOOL usesTestServer; -@property (nonatomic) BOOL canEnableDebugLogging; -@property (nonatomic, getter=isPaused) BOOL paused; -@property (nonatomic) NS_MUTABLE_ARRAY_OF(MGLMapboxEventAttributes *) *eventQueue; -@property (nonatomic) dispatch_queue_t serialQueue; -@property (nonatomic) dispatch_queue_t debugLogSerialQueue; -@property (nonatomic) MGLLocationManager *locationManager; -@property (nonatomic) NSTimer *timer; -@property (nonatomic) NSDate *instanceIDRotationDate; -@property (nonatomic) NSDate *nextTurnstileSendDate; -@property (nonatomic) NSNumber *currentAccountTypeValue; -@property (nonatomic) BOOL currentMetricsEnabledValue; - -@end - -@implementation MGLMapboxEvents { - NSString *_instanceID; - UIBackgroundTaskIdentifier _backgroundTaskIdentifier; -} + (void)initialize { if (self == [MGLMapboxEvents class]) { NSBundle *bundle = [NSBundle mainBundle]; NSNumber *accountTypeNumber = [bundle objectForInfoDictionaryKey:MGLMapboxAccountType]; - [[NSUserDefaults standardUserDefaults] registerDefaults:@{ - MGLMapboxAccountType: accountTypeNumber ?: @0, - MGLMapboxMetricsEnabled: @YES, - @"MGLMapboxMetricsDebugLoggingEnabled": @NO, - }]; + [[NSUserDefaults standardUserDefaults] registerDefaults:@{MGLMapboxAccountType: accountTypeNumber ?: @0, + MGLMapboxMetricsEnabled: @YES, + MGLMapboxMetricsDebugLoggingEnabled: @NO}]; } } -+ (BOOL)isEnabled { -#if TARGET_OS_SIMULATOR - return NO; -#else - BOOL isLowPowerModeEnabled = NO; - if ([NSProcessInfo instancesRespondToSelector:@selector(isLowPowerModeEnabled)]) { - isLowPowerModeEnabled = [[NSProcessInfo processInfo] isLowPowerModeEnabled]; ++ (nullable instancetype)sharedInstance { + if (NSProcessInfo.processInfo.mgl_isInterfaceBuilderDesignablesAgent) { + return nil; } - return ([[NSUserDefaults standardUserDefaults] boolForKey:MGLMapboxMetricsEnabled] && - [[NSUserDefaults standardUserDefaults] integerForKey:MGLMapboxAccountType] == 0 && - !isLowPowerModeEnabled); -#endif + + static dispatch_once_t onceToken; + static MGLMapboxEvents *_sharedInstance; + dispatch_once(&onceToken, ^{ + _sharedInstance = [[self alloc] init]; + }); + return _sharedInstance; } - -- (BOOL)debugLoggingEnabled { - return (self.canEnableDebugLogging && - [[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsDebugLoggingEnabled"]); -} - -- (instancetype) init { +- (instancetype)init { self = [super init]; if (self) { - [MGLTelemetryConfig.sharedConfig configurationFromKey:[[NSUserDefaults standardUserDefaults] objectForKey:MGLMapboxMetricsProfile]]; + _eventsManager = [[MMEEventsManager alloc] init]; + _eventsManager.debugLoggingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:MGLMapboxMetricsDebugLoggingEnabled]; + _eventsManager.accountType = [[NSUserDefaults standardUserDefaults] integerForKey:MGLMapboxAccountType]; + _eventsManager.metricsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:MGLMapboxMetricsEnabled]; - _currentAccountTypeValue = @0; - _currentMetricsEnabledValue = YES; - - _appBundleId = [[NSBundle mainBundle] bundleIdentifier]; - _apiClient = [[MGLAPIClient alloc] init]; - - NSString *uniqueID = [[NSProcessInfo processInfo] globallyUniqueString]; - _serialQueue = dispatch_queue_create([[NSString stringWithFormat:@"%@.%@.events.serial", _appBundleId, uniqueID] UTF8String], DISPATCH_QUEUE_SERIAL); - - _locationManager = [[MGLLocationManager alloc] init]; - _locationManager.delegate = self; - _paused = YES; - [self resumeMetricsCollection]; - - // Events Control - _eventQueue = [[NSMutableArray alloc] init]; - - // Setup Date Format - _rfc3339DateFormatter = [[NSDateFormatter alloc] init]; - NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; - - [_rfc3339DateFormatter setLocale:enUSPOSIXLocale]; - [_rfc3339DateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"]; - // Clear Any System TimeZone Cache - [NSTimeZone resetSystemTimeZone]; - [_rfc3339DateFormatter setTimeZone:[NSTimeZone systemTimeZone]]; - - // Configure logging - if ([self isProbablyAppStoreBuild]) { - self.canEnableDebugLogging = NO; - - if ([[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsDebugLoggingEnabled"]) { - NSLog(@"Telemetry logging is only enabled in non-app store builds."); - } - } else { - self.canEnableDebugLogging = YES; + // It is possible for the shared instance of this class to be created because of a call to + // +[MGLAccountManager load] early on in the app lifecycle of the host application. + // If user default values for access token and base URL are available, they are stored here + // on local properties so that they can be applied later once MMEEventsManager is fully initialized + // (once -[MMEEventsManager initializeWithAccessToken:userAgentBase:hostSDKVersion:] is called. + // Normally, the telem access token and base URL are not set this way. However, overriding these values + // with user defaults can be useful for testing with an alternative (test) backend system. + if ([[[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys] containsObject:MGLTelemetryAccessToken]) { + self.accessToken = [[NSUserDefaults standardUserDefaults] objectForKey:MGLTelemetryAccessToken]; } - - // Watch for changes to telemetry settings by the user + if ([[[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys] containsObject:MGLTelemetryBaseURL]) { + self.baseURL = [NSURL URLWithString:[[NSUserDefaults standardUserDefaults] objectForKey:MGLTelemetryBaseURL]]; + } + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(userDefaultsDidChange:) name:NSUserDefaultsDidChangeNotification object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pauseOrResumeMetricsCollectionIfRequired) name:UIApplicationDidEnterBackgroundNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pauseOrResumeMetricsCollectionIfRequired) name:UIApplicationDidBecomeActiveNotification object:nil]; - - // Watch for Low Power Mode change events - if (&NSProcessInfoPowerStateDidChangeNotification != NULL) { - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pauseOrResumeMetricsCollectionIfRequired) name:NSProcessInfoPowerStateDidChangeNotification object:nil]; - } } return self; } -// Called implicitly from any public class convenience methods. -// May return nil if this feature is disabled. -// -+ (nullable instancetype)sharedManager { - if (NSProcessInfo.processInfo.mgl_isInterfaceBuilderDesignablesAgent) { - return nil; - } - static dispatch_once_t onceToken; - static MGLMapboxEvents *_sharedManager; - dispatch_once(&onceToken, ^{ - _sharedManager = [[self alloc] init]; - }); - return _sharedManager; -} - - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; - [self pauseMetricsCollection]; -} - -- (NSString *)instanceID { - if (self.instanceIDRotationDate && [[NSDate date] timeIntervalSinceDate:self.instanceIDRotationDate] >= 0) { - _instanceID = nil; - } - if (!_instanceID) { - _instanceID = [[NSUUID UUID] UUIDString]; - NSTimeInterval twentyFourHourTimeInterval = 24 * 3600; - self.instanceIDRotationDate = [[NSDate date] dateByAddingTimeInterval:twentyFourHourTimeInterval]; - } - return _instanceID; } - (void)userDefaultsDidChange:(NSNotification *)notification { + dispatch_async(dispatch_get_main_queue(), ^{ + [self updateNonDisablingConfigurationValues]; + [self updateDisablingConfigurationValuesWithNotification:notification]; + }); +} + +- (void)updateNonDisablingConfigurationValues { + self.eventsManager.debugLoggingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsDebugLoggingEnabled"]; + // It is possible for `MGLTelemetryAccessToken` to have been set yet `userDefaultsDidChange:` + // is called before `setupWithAccessToken:` is called. + // In that case, setting the access token here will have no effect. In practice, that's fine + // because the access token value will be resolved when `setupWithAccessToken:` is called eventually + if ([[[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys] containsObject:MGLTelemetryAccessToken]) { + self.eventsManager.accessToken = [[NSUserDefaults standardUserDefaults] objectForKey:MGLTelemetryAccessToken]; + } + + // It is possible for `MGLTelemetryBaseURL` to have been set yet `userDefaultsDidChange:` + // is called before setupWithAccessToken: is called. + // In that case, setting the base URL here will have no effect. In practice, that's fine + // because the base URL value will be resolved when `setupWithAccessToken:` is called eventually + if ([[[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys] containsObject:MGLTelemetryBaseURL]) { + NSURL *baseURL = [NSURL URLWithString:[[NSUserDefaults standardUserDefaults] objectForKey:MGLTelemetryBaseURL]]; + self.eventsManager.baseURL = baseURL; + } +} + +- (void)updateDisablingConfigurationValuesWithNotification:(NSNotification *)notification { // Guard against over calling pause / resume if the values this implementation actually - // cares about have not changed - + // cares about have not changed. We guard because the pause and resume method checks CoreLocation's + // authorization status and that can drag on the main thread if done too many times (e.g. if the host + // app heavily uses the user defaults API and this method is called very frequently) if ([[notification object] respondsToSelector:@selector(objectForKey:)]) { NSUserDefaults *userDefaults = [notification object]; - NSNumber *accountType = [userDefaults objectForKey:MGLMapboxAccountType]; - BOOL metricsEnabled = [[userDefaults objectForKey:MGLMapboxMetricsEnabled] boolValue]; + NSInteger accountType = [userDefaults integerForKey:MGLMapboxAccountType]; + BOOL metricsEnabled = [userDefaults boolForKey:MGLMapboxMetricsEnabled]; - if (![accountType isEqualToNumber:self.currentAccountTypeValue] || metricsEnabled != self.currentMetricsEnabledValue) { - [self pauseOrResumeMetricsCollectionIfRequired]; - self.currentAccountTypeValue = accountType; - self.currentMetricsEnabledValue = metricsEnabled; - } - } - -} - -- (void)pauseOrResumeMetricsCollectionIfRequired { - - // [CLLocationManager authorizationStatus] has been found to block in some cases so - // dispatch the call to a non-UI thread - dispatch_async(self.serialQueue, ^{ - CLAuthorizationStatus status = [CLLocationManager authorizationStatus]; - - // Checking application state must be done on the main thread for safety and - // to avoid a thread sanitizer error - dispatch_async(dispatch_get_main_queue(), ^{ - UIApplication *application = [UIApplication sharedApplication]; - UIApplicationState state = application.applicationState; + if (accountType != self.eventsManager.accountType || metricsEnabled != self.eventsManager.metricsEnabled) { + self.eventsManager.accountType = accountType; + self.eventsManager.metricsEnabled = metricsEnabled; - // Prevent blue status bar when host app has `when in use` permission only and it is not in foreground - if (status == kCLAuthorizationStatusAuthorizedWhenInUse && state == UIApplicationStateBackground) { - if (_backgroundTaskIdentifier == UIBackgroundTaskInvalid) { - _backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{ - [application endBackgroundTask:_backgroundTaskIdentifier]; - _backgroundTaskIdentifier = UIBackgroundTaskInvalid; - }]; - [self flush]; - } - [self pauseMetricsCollection]; - return; - } - - // Toggle pause based on current pause state, user opt-out state, and low-power state. - BOOL enabled = [[self class] isEnabled]; - if (self.paused && enabled) { - [self resumeMetricsCollection]; - } else if (!self.paused && !enabled) { - [self flush]; - [self pauseMetricsCollection]; - } - }); - }); + [self.eventsManager pauseOrResumeMetricsCollectionIfRequired]; + } + } } -- (void)pauseMetricsCollection { - if (self.paused) { - return; ++ (void)setupWithAccessToken:(NSString *)accessToken { + NSString *semanticVersion = [NSBundle mgl_frameworkInfoDictionary][@"MGLSemanticVersionString"]; + NSString *shortVersion = [NSBundle mgl_frameworkInfoDictionary][@"CFBundleShortVersionString"]; + NSString *sdkVersion = semanticVersion ?: shortVersion; + + // It is possible that an alternative access token was already set on this instance when the class was loaded + // Use it if it exists + NSString *resolvedAccessToken = [MGLMapboxEvents sharedInstance].accessToken ?: accessToken; + + [[[self sharedInstance] eventsManager] initializeWithAccessToken:resolvedAccessToken userAgentBase:MGLAPIClientUserAgentBase hostSDKVersion:sdkVersion]; + + // It is possible that an alternative base URL was set on this instance when the class was loaded + // Use it if it exists + if ([MGLMapboxEvents sharedInstance].baseURL) { + [[MGLMapboxEvents sharedInstance] eventsManager].baseURL = [MGLMapboxEvents sharedInstance].baseURL; } - - self.paused = YES; - [self.timer invalidate]; - self.timer = nil; - [self.eventQueue removeAllObjects]; - self.data = nil; - - [self.locationManager stopUpdatingLocation]; } -- (void)resumeMetricsCollection { - if (!self.paused || ![[self class] isEnabled]) { - return; - } ++ (void)pushTurnstileEvent { + [[[self sharedInstance] eventsManager] sendTurnstileEvent]; +} - self.paused = NO; - self.data = [[MGLMapboxEventsData alloc] init]; - - [self.locationManager startUpdatingLocation]; ++ (void)pushEvent:(NSString *)event withAttributes:(MMEMapboxEventAttributes *)attributeDictionary { + [[[self sharedInstance] eventsManager] enqueueEventWithName:event attributes:attributeDictionary]; } + (void)flush { - [[MGLMapboxEvents sharedManager] flush]; -} - -- (void)flush { - if ([MGLAccountManager accessToken] == nil) { - return; - } - - NSArray *events = [NSArray arrayWithArray:self.eventQueue]; - [self.eventQueue removeAllObjects]; - - [self postEvents:events]; - - if (self.timer) { - [self.timer invalidate]; - self.timer = nil; - } - - [self pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription:@"flush"}]; -} - -- (void)pushTurnstileEvent { - if (self.nextTurnstileSendDate && [[NSDate date] timeIntervalSinceDate:self.nextTurnstileSendDate] < 0) { - return; - } - - NSString *vendorID = [[[UIDevice currentDevice] identifierForVendor] UUIDString]; - if (!vendorID) { - return; - } - - NSDictionary *turnstileEventAttributes = @{MGLEventKeyEvent: MGLEventTypeAppUserTurnstile, - MGLEventKeyCreated: [self.rfc3339DateFormatter stringFromDate:[NSDate date]], - MGLEventKeyVendorID: vendorID, - MGLEventKeyEnabledTelemetry: @([[self class] isEnabled])}; - - if ([MGLAccountManager accessToken] == nil) { - return; - } - - __weak __typeof__(self) weakSelf = self; - [self.apiClient postEvent:turnstileEventAttributes completionHandler:^(NSError * _Nullable error) { - __strong __typeof__(weakSelf) strongSelf = weakSelf; - if (error) { - [strongSelf pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription: @"Network error", - @"error": error}]; - return; - } - [strongSelf writeEventToLocalDebugLog:turnstileEventAttributes]; - [strongSelf updateNextTurnstileSendDate]; - }]; -} - -- (void)updateNextTurnstileSendDate { - // Find the time a day from now (sometime tomorrow) - NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; - NSDateComponents *dayComponent = [[NSDateComponents alloc] init]; - dayComponent.day = 1; - NSDate *sometimeTomorrow = [calendar dateByAddingComponents:dayComponent toDate:[NSDate date] options:0]; - - // Find the start of tomorrow and use that as the next turnstile send date. The effect of this is that - // turnstile events can be sent as much as once per calendar day and always at the start of a session - // when a map load happens. - NSDate *startOfTomorrow = nil; - [calendar rangeOfUnit:NSCalendarUnitDay startDate:&startOfTomorrow interval:nil forDate:sometimeTomorrow]; - self.nextTurnstileSendDate = startOfTomorrow; -} - -+ (void)pushEvent:(NSString *)event withAttributes:(MGLMapboxEventAttributes *)attributeDictionary { - [[MGLMapboxEvents sharedManager] pushEvent:event withAttributes:attributeDictionary]; -} - -- (void)pushEvent:(NSString *)event withAttributes:(MGLMapboxEventAttributes *)attributeDictionary { - if (!event) { - return; - } - - if ([event isEqualToString:MGLEventTypeMapLoad]) { - [self pushTurnstileEvent]; - } - - if (self.paused) { - return; - } - - MGLMapboxEventAttributes *fullyFormedEvent = [self fullyFormedEventForEvent:event withAttributes:attributeDictionary]; - if (fullyFormedEvent) { - [self.eventQueue addObject:fullyFormedEvent]; - [self writeEventToLocalDebugLog:fullyFormedEvent]; - // Has Flush Limit Been Reached? - if (self.eventQueue.count >= MGLMaximumEventsPerFlush) { - [self flush]; - } else if (self.eventQueue.count == 1) { - // If this is first new event on queue start timer, - [self startTimer]; - } - } else { - [self pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription: @"Unknown event", - @"eventName": event, - @"event.attributes": attributeDictionary}]; - } -} - -#pragma mark Events - -- (MGLMapboxEventAttributes *)fullyFormedEventForEvent:(NSString *)event withAttributes:(MGLMapboxEventAttributes *)attributeDictionary { - if ([event isEqualToString:MGLEventTypeMapLoad]) { - return [self mapLoadEventWithAttributes:attributeDictionary]; - } else if ([event isEqualToString:MGLEventTypeMapTap]) { - return [self mapClickEventWithAttributes:attributeDictionary]; - } else if ([event isEqualToString:MGLEventTypeMapDragEnd]) { - return [self mapDragEndEventWithAttributes:attributeDictionary]; - } else if ([event isEqualToString:MGLEventTypeLocation]) { - return [self locationEventWithAttributes:attributeDictionary]; - } - return nil; -} - -- (MGLMapboxEventAttributes *)locationEventWithAttributes:(MGLMapboxEventAttributes *)attributeDictionary { - MGLMutableMapboxEventAttributes *attributes = [NSMutableDictionary dictionary]; - attributes[MGLEventKeyEvent] = MGLEventTypeLocation; - attributes[MGLEventKeySource] = MGLEventSource; - attributes[MGLEventKeySessionId] = self.instanceID; - attributes[MGLEventKeyOperatingSystem] = self.data.iOSVersion; - NSString *currentApplicationState = [self applicationState]; - if (![currentApplicationState isEqualToString:MGLApplicationStateUnknown]) { - attributes[MGLEventKeyApplicationState] = currentApplicationState; - } - - return [self eventForAttributes:attributes attributeDictionary:attributeDictionary]; -} - -- (MGLMapboxEventAttributes *)mapLoadEventWithAttributes:(MGLMapboxEventAttributes *)attributeDictionary { - MGLMutableMapboxEventAttributes *attributes = [NSMutableDictionary dictionary]; - attributes[MGLEventKeyEvent] = MGLEventTypeMapLoad; - attributes[MGLEventKeyCreated] = [self.rfc3339DateFormatter stringFromDate:[NSDate date]]; - attributes[MGLEventKeyVendorID] = self.data.vendorId; - attributes[MGLEventKeyModel] = self.data.model; - attributes[MGLEventKeyOperatingSystem] = self.data.iOSVersion; - attributes[MGLEventKeyResolution] = @(self.data.scale); - attributes[MGLEventKeyAccessibilityFontScale] = @([self contentSizeScale]); - attributes[MGLEventKeyOrientation] = [self deviceOrientation]; - attributes[MGLEventKeyWifi] = @([[MGLReachability reachabilityForLocalWiFi] isReachableViaWiFi]); - - return [self eventForAttributes:attributes attributeDictionary:attributeDictionary]; -} - -- (MGLMapboxEventAttributes *)mapClickEventWithAttributes:(MGLMapboxEventAttributes *)attributeDictionary { - MGLMutableMapboxEventAttributes *attributes = [self interactionEvent]; - attributes[MGLEventKeyEvent] = MGLEventTypeMapTap; - return [self eventForAttributes:attributes attributeDictionary:attributeDictionary]; -} - -- (MGLMapboxEventAttributes *)mapDragEndEventWithAttributes:(MGLMapboxEventAttributes *)attributeDictionary { - MGLMutableMapboxEventAttributes *attributes = [self interactionEvent]; - attributes[MGLEventKeyEvent] = MGLEventTypeMapDragEnd; - - return [self eventForAttributes:attributes attributeDictionary:attributeDictionary]; -} - -- (MGLMutableMapboxEventAttributes *)interactionEvent { - MGLMutableMapboxEventAttributes *attributes = [NSMutableDictionary dictionary]; - attributes[MGLEventKeyCreated] = [self.rfc3339DateFormatter stringFromDate:[NSDate date]]; - attributes[MGLEventKeyOrientation] = [self deviceOrientation]; - attributes[MGLEventKeyWifi] = @([[MGLReachability reachabilityForLocalWiFi] isReachableViaWiFi]); - - return attributes; -} - -- (MGLMapboxEventAttributes *)eventForAttributes:(MGLMutableMapboxEventAttributes *)attributes attributeDictionary:(MGLMapboxEventAttributes *)attributeDictionary { - [attributes addEntriesFromDictionary:attributeDictionary]; - - return [attributes copy]; -} - -// Called implicitly from public use of +flush. -// -- (void)postEvents:(NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events { - if (self.paused) { - return; - } - - __weak __typeof__(self) weakSelf = self; - dispatch_async(self.serialQueue, ^{ - __strong __typeof__(weakSelf) strongSelf = weakSelf; - [self.apiClient postEvents:events completionHandler:^(NSError * _Nullable error) { - if (error) { - [strongSelf pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription: @"Network error", - @"error": error}]; - } else { - [strongSelf pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription: @"post", - @"debug.eventsCount": @(events.count)}]; - } - [[UIApplication sharedApplication] endBackgroundTask:_backgroundTaskIdentifier]; - _backgroundTaskIdentifier = UIBackgroundTaskInvalid; - }]; - }); -} - -- (void)startTimer { - [self.timer invalidate]; - self.timer = [NSTimer scheduledTimerWithTimeInterval:MGLFlushInterval - target:self - selector:@selector(flush) - userInfo:nil - repeats:YES]; -} - -- (NSString *)deviceOrientation { - NSString *result; - - switch ([UIDevice currentDevice].orientation) { - case UIDeviceOrientationUnknown: - result = @"Unknown"; - break; - case UIDeviceOrientationPortrait: - result = @"Portrait"; - break; - case UIDeviceOrientationPortraitUpsideDown: - result = @"PortraitUpsideDown"; - break; - case UIDeviceOrientationLandscapeLeft: - result = @"LandscapeLeft"; - break; - case UIDeviceOrientationLandscapeRight: - result = @"LandscapeRight"; - break; - case UIDeviceOrientationFaceUp: - result = @"FaceUp"; - break; - case UIDeviceOrientationFaceDown: - result = @"FaceDown"; - break; - default: - result = @"Default - Unknown"; - break; - } - - return result; -} - -- (NSString *)applicationState { - switch ([UIApplication sharedApplication].applicationState) { - case UIApplicationStateActive: - return MGLApplicationStateForeground; - case UIApplicationStateInactive: - return MGLApplicationStateInactive; - case UIApplicationStateBackground: - return MGLApplicationStateBackground; - default: - return MGLApplicationStateUnknown; - } -} - -- (NSInteger)contentSizeScale { - NSInteger result = -9999; - - NSString *sc = [UIApplication sharedApplication].preferredContentSizeCategory; - - if ([sc isEqualToString:UIContentSizeCategoryExtraSmall]) { - result = -3; - } else if ([sc isEqualToString:UIContentSizeCategorySmall]) { - result = -2; - } else if ([sc isEqualToString:UIContentSizeCategoryMedium]) { - result = -1; - } else if ([sc isEqualToString:UIContentSizeCategoryLarge]) { - result = 0; - } else if ([sc isEqualToString:UIContentSizeCategoryExtraLarge]) { - result = 1; - } else if ([sc isEqualToString:UIContentSizeCategoryExtraExtraLarge]) { - result = 2; - } else if ([sc isEqualToString:UIContentSizeCategoryExtraExtraExtraLarge]) { - result = 3; - } else if ([sc isEqualToString:UIContentSizeCategoryAccessibilityMedium]) { - result = -11; - } else if ([sc isEqualToString:UIContentSizeCategoryAccessibilityLarge]) { - result = 10; - } else if ([sc isEqualToString:UIContentSizeCategoryAccessibilityExtraLarge]) { - result = 11; - } else if ([sc isEqualToString:UIContentSizeCategoryAccessibilityExtraExtraLarge]) { - result = 12; - } else if ([sc isEqualToString:UIContentSizeCategoryAccessibilityExtraExtraExtraLarge]) { - result = 13; - } - - return result; + [[[self sharedInstance] eventsManager] flush]; } + (void)ensureMetricsOptoutExists { NSNumber *shownInAppNumber = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"MGLMapboxMetricsEnabledSettingShownInApp"]; BOOL metricsEnabledSettingShownInAppFlag = [shownInAppNumber boolValue]; - + if (!metricsEnabledSettingShownInAppFlag && [[NSUserDefaults standardUserDefaults] integerForKey:MGLMapboxAccountType] == 0) { // Opt-out is not configured in UI, so check for Settings.bundle id defaultEnabledValue; NSString *appSettingsBundle = [[NSBundle mainBundle] pathForResource:@"Settings" ofType:@"bundle"]; - + if (appSettingsBundle) { // Dynamic Settings.bundle loading based on http://stackoverflow.com/a/510329/2094275 NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:[appSettingsBundle stringByAppendingPathComponent:@"Root.plist"]]; @@ -651,7 +171,7 @@ const NSTimeInterval MGLFlushInterval = 180; } } } - + if (!defaultEnabledValue) { [NSException raise:@"Telemetry opt-out missing" format: @"End users must be able to opt out of Mapbox Telemetry in your app, either inside Settings (via Settings.bundle) or inside this app. " @@ -663,153 +183,4 @@ const NSTimeInterval MGLFlushInterval = 180; } } -#pragma mark CLLocationManagerUtilityDelegate - -- (void)locationManager:(MGLLocationManager *)locationManager didUpdateLocations:(NSArray *)locations { - for (CLLocation *loc in locations) { - double accuracy = 10000000; - double lat = floor(loc.coordinate.latitude * accuracy) / accuracy; - double lng = floor(loc.coordinate.longitude * accuracy) / accuracy; - double horizontalAccuracy = round(loc.horizontalAccuracy); - NSString *formattedDate = [self.rfc3339DateFormatter stringFromDate:loc.timestamp]; - [MGLMapboxEvents pushEvent:MGLEventTypeLocation withAttributes:@{MGLEventKeyCreated: formattedDate, - MGLEventKeyLatitude: @(lat), - MGLEventKeyLongitude: @(lng), - MGLEventKeyAltitude: @(round(loc.altitude)), - MGLEventHorizontalAccuracy: @(horizontalAccuracy)}]; - } -} - -- (void)locationManagerBackgroundLocationUpdatesDidAutomaticallyPause:(MGLLocationManager *)locationManager { - [self pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription:@"locationManager.locationManagerAutoPause"}]; -} - -- (void)locationManagerBackgroundLocationUpdatesDidTimeout:(MGLLocationManager *)locationManager { - [self pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription:@"locationManager.locationManagerTimeout"}]; -} - -- (void)locationManagerDidStartLocationUpdates:(MGLLocationManager *)locationManager { - [self pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription:@"locationManager.locationManagerStartUpdates"}]; -} - -- (void)locationManagerDidStopLocationUpdates:(MGLLocationManager *)locationManager { - [self pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription: @"locationManager.locationManagerStopUpdates"}]; -} - -#pragma mark MGLMapboxEvents Debug - -- (void)pushDebugEvent:(NSString *)event withAttributes:(MGLMapboxEventAttributes *)attributeDictionary { - if (![self debugLoggingEnabled]) { - return; - } - - if (!event) { - return; - } - - MGLMutableMapboxEventAttributes *evt = [MGLMutableMapboxEventAttributes dictionaryWithDictionary:attributeDictionary]; - [evt setObject:event forKey:@"event"]; - [evt setObject:[self.rfc3339DateFormatter stringFromDate:[NSDate date]] forKey:@"created"]; - [evt setValue:[self applicationState] forKey:@"applicationState"]; - [evt setValue:@([[self class] isEnabled]) forKey:@"telemetryEnabled"]; - [evt setObject:self.instanceID forKey:@"instance"]; - - MGLMapboxEventAttributes *finalEvent = [NSDictionary dictionaryWithDictionary:evt]; - [self writeEventToLocalDebugLog:finalEvent]; -} - -- (void)writeEventToLocalDebugLog:(MGLMapboxEventAttributes *)event { - if (![self debugLoggingEnabled]) { - return; - } - - NSLog(@"%@", [self stringForDebugEvent:event]); - - if (!self.dateForDebugLogFile) { - NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; - [dateFormatter setDateFormat:@"yyyy'-'MM'-'dd"]; - [dateFormatter setTimeZone:[NSTimeZone systemTimeZone]]; - self.dateForDebugLogFile = [dateFormatter stringFromDate:[NSDate date]]; - } - - if (!self.debugLogSerialQueue) { - NSString *uniqueID = [[NSProcessInfo processInfo] globallyUniqueString]; - self.debugLogSerialQueue = dispatch_queue_create([[NSString stringWithFormat:@"%@.%@.events.debugLog", _appBundleId, uniqueID] UTF8String], DISPATCH_QUEUE_SERIAL); - } - - dispatch_async(self.debugLogSerialQueue, ^{ - if ([NSJSONSerialization isValidJSONObject:event]) { - NSData *jsonData = [NSJSONSerialization dataWithJSONObject:event options:NSJSONWritingPrettyPrinted error:nil]; - - NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; - jsonString = [jsonString stringByAppendingString:@",\n"]; - - NSString *logFilePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:[NSString stringWithFormat:@"telemetry_log-%@.json", self.dateForDebugLogFile]]; - - NSFileManager *fileManager = [[NSFileManager alloc] init]; - if ([fileManager fileExistsAtPath:logFilePath]) { - NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:logFilePath]; - [fileHandle seekToEndOfFile]; - [fileHandle writeData:[jsonString dataUsingEncoding:NSUTF8StringEncoding]]; - } else { - [fileManager createFileAtPath:logFilePath contents:[jsonString dataUsingEncoding:NSUTF8StringEncoding] attributes:@{ NSFileProtectionKey: NSFileProtectionCompleteUntilFirstUserAuthentication }]; - } - } - }); -} - -- (NSString *)stringForDebugEvent:(MGLMapboxEventAttributes *)event { - // redact potentially sensitive location details from system console log - if ([event[@"event"] isEqualToString:MGLEventTypeLocation]) { - MGLMutableMapboxEventAttributes *evt = [MGLMutableMapboxEventAttributes dictionaryWithDictionary:event]; - [evt setObject:@"" forKey:@"lat"]; - [evt setObject:@"" forKey:@"lng"]; - event = evt; - } - - return [NSString stringWithFormat:@"Mapbox Telemetry event %@", event]; -} - -- (BOOL)isProbablyAppStoreBuild { -#if TARGET_IPHONE_SIMULATOR - return NO; -#else - // BugshotKit by Marco Arment https://github.com/marcoarment/BugshotKit/ - // Adapted from https://github.com/blindsightcorp/BSMobileProvision - - NSString *binaryMobileProvision = [NSString stringWithContentsOfFile:[NSBundle.mainBundle pathForResource:@"embedded" ofType:@"mobileprovision"] encoding:NSISOLatin1StringEncoding error:NULL]; - if (!binaryMobileProvision) { - return YES; // no provision - } - - NSScanner *scanner = [NSScanner scannerWithString:binaryMobileProvision]; - NSString *plistString; - if (![scanner scanUpToString:@"" intoString:&plistString]) { - return YES; // no XML plist found in provision - } - plistString = [plistString stringByAppendingString:@""]; - - NSData *plistdata_latin1 = [plistString dataUsingEncoding:NSISOLatin1StringEncoding]; - NSError *error = nil; - NSDictionary *mobileProvision = [NSPropertyListSerialization propertyListWithData:plistdata_latin1 options:NSPropertyListImmutable format:NULL error:&error]; - if (error) { - return YES; // unknown plist format - } - - if (!mobileProvision || ! mobileProvision.count) { - return YES; // no entitlements - } - - if (mobileProvision[@"ProvisionsAllDevices"]) { - return NO; // enterprise provisioning - } - - if (mobileProvision[@"ProvisionedDevices"] && [mobileProvision[@"ProvisionedDevices"] count]) { - return NO; // development or ad-hoc - } - - return YES; // expected development/enterprise/ad-hoc entitlements not found -#endif -} - @end diff --git a/platform/ios/src/Mapbox-Prefix.pch b/platform/ios/src/Mapbox-Prefix.pch new file mode 100644 index 000000000..675402086 --- /dev/null +++ b/platform/ios/src/Mapbox-Prefix.pch @@ -0,0 +1 @@ +#import "MMENamespacedDependencies.h" diff --git a/platform/ios/vendor/mapbox-events-ios b/platform/ios/vendor/mapbox-events-ios new file mode 160000 index 000000000..d522b18d8 --- /dev/null +++ b/platform/ios/vendor/mapbox-events-ios @@ -0,0 +1 @@ +Subproject commit d522b18d8439de9073eafaad7d85e9f6a0d8d827 From d9e4c61923813b61ffccb6439d0fd3e9993a1a05 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Wed, 14 Feb 2018 12:36:23 +0100 Subject: [PATCH 052/264] [android] - remove camera api from MapboxMap, refactor test app code to CameraUpdateFactory api --- .../mapboxsdk/camera/CameraUpdateFactory.java | 20 +++ .../com/mapbox/mapboxsdk/maps/MapboxMap.java | 63 +------ .../mapbox/mapboxsdk/maps/MapViewUtils.java | 33 ---- .../testapp/camera/CameraInternalApiTest.java | 157 ------------------ .../camera/CameraAnimatorActivity.java | 20 ++- 5 files changed, 37 insertions(+), 256 deletions(-) delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapViewUtils.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraInternalApiTest.java diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java index 50e33f4f9..8ef0d5b52 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java @@ -148,6 +148,26 @@ public final class CameraUpdateFactory { return new ZoomUpdate(ZoomUpdate.ZOOM_TO, zoom); } + /** + * Returns a CameraUpdate that moves the camera viewpoint to a particular bearing. + * + * @param bearing Bearing to change to + * @return CameraUpdate Final Camera Position + */ + public static CameraUpdate bearingTo(double bearing) { + return new CameraPositionUpdate(bearing, null, -1, -1); + } + + /** + * Returns a CameraUpdate that moves the camera viewpoint to a particular tilt. + * + * @param tilt Tilt to change to + * @return CameraUpdate Final Camera Position + */ + public static CameraUpdate tiltTo(double tilt) { + return new CameraPositionUpdate(-1, null, tilt, -1); + } + // // CameraUpdate types // diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index 4f2c940ea..6085e2c2d 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java @@ -16,6 +16,7 @@ import android.text.TextUtils; import android.view.View; import android.view.ViewGroup; +import com.mapbox.android.core.location.LocationEngine; import com.mapbox.geojson.Feature; import com.mapbox.geojson.Geometry; import com.mapbox.mapboxsdk.annotations.Annotation; @@ -43,7 +44,6 @@ import com.mapbox.mapboxsdk.style.layers.Filter; import com.mapbox.mapboxsdk.style.layers.Layer; import com.mapbox.mapboxsdk.style.light.Light; import com.mapbox.mapboxsdk.style.sources.Source; -import com.mapbox.android.core.location.LocationEngine; import java.lang.reflect.ParameterizedType; import java.util.HashMap; @@ -601,7 +601,7 @@ public final class MapboxMap { } // - // + // Light // /** @@ -618,65 +618,6 @@ public final class MapboxMap { // Camera API // - /** - * Moves the center of the screen to a latitude and longitude specified by a LatLng object. This centers the - * camera on the LatLng object. - *

- * Note that at low zoom levels, setLatLng is constrained so that the entire viewport shows map data. - *

- * - * @param latLng Target location to change to - */ - public void setLatLng(@NonNull LatLng latLng) { - nativeMapView.setLatLng(latLng); - } - - /** - * Moves the camera viewpoint to a particular zoom level. - * - * @param zoom Zoom level to change to - */ - public void setZoom(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double zoom) { - if (focalPoint == null) { - focalPoint = new PointF(nativeMapView.getWidth() / 2, nativeMapView.getHeight() / 2); - } - nativeMapView.setZoom(zoom, focalPoint, 0); - } - - /** - * Moves the center and the zoom of the camera specified by a LatLng object and double zoom. - *

- * Note that at low zoom levels, setLatLng is constrained so that the entire viewport shows map data. - *

- * - * @param latLng Target location to change to - */ - public void setLatLngZoom(@NonNull LatLng latLng, - @FloatRange(from = MapboxConstants.MINIMUM_ZOOM, - to = MapboxConstants.MAXIMUM_ZOOM) double zoom) { - setZoom(zoom); - setLatLng(latLng); - } - - /** - * Moves the camera viewpoint angle to a particular angle in degrees. - * - * @param tilt Tilt angle to change to - */ - public void setTilt(@FloatRange(from = MapboxConstants.MINIMUM_TILT, to = MapboxConstants.MAXIMUM_TILT) double tilt) { - nativeMapView.setPitch(tilt, 0); - } - - /** - * Moves the camera viewpoint direction to a particular angle in degrees. - * - * @param bearing Direction angle to change to - */ - public void setBearing(@FloatRange(from = MapboxConstants.MINIMUM_DIRECTION, to = MapboxConstants.MAXIMUM_DIRECTION) - double bearing) { - nativeMapView.setBearing(bearing); - } - /** * Cancels ongoing animations. *

diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapViewUtils.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapViewUtils.java deleted file mode 100644 index 38d529729..000000000 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapViewUtils.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import com.mapbox.mapboxsdk.geometry.LatLng; - -/** - * Utility class to bypass package visibility - */ -public class MapViewUtils { - - public static void setDirection(MapboxMap mapboxMap, float direction) { - mapboxMap.getTransform().setBearing(direction); - } - - public static float getDirection(MapboxMap mapboxMap) { - return (float) mapboxMap.getTransform().getBearing(); - } - - public static void setTilt(MapboxMap mapboxMap, float tilt) { - mapboxMap.getTransform().setTilt((double) tilt); - } - - public static float getTilt(MapboxMap mapboxMap) { - return (float) mapboxMap.getTransform().getTilt(); - } - - public static void setLatLng(MapboxMap mapboxMap, LatLng latLng) { - mapboxMap.getTransform().setCenterCoordinate(latLng); - } - - public static LatLng getLatLng(MapboxMap mapboxMap) { - return mapboxMap.getTransform().getCenterCoordinate(); - } -} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraInternalApiTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraInternalApiTest.java deleted file mode 100644 index 883e76653..000000000 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraInternalApiTest.java +++ /dev/null @@ -1,157 +0,0 @@ -package com.mapbox.mapboxsdk.testapp.camera; - -import android.support.test.espresso.Espresso; -import android.support.test.espresso.UiController; -import android.support.test.espresso.ViewAction; -import android.view.View; - -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.maps.MapViewUtils; -import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest; -import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity; -import com.mapbox.mapboxsdk.testapp.utils.TestConstants; - -import org.hamcrest.Matcher; -import org.junit.After; -import org.junit.Ignore; -import org.junit.Test; - -import static android.support.test.espresso.Espresso.onView; -import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; -import static android.support.test.espresso.matcher.ViewMatchers.withId; -import static org.junit.Assert.assertEquals; - -/** - * Tests camera transformations that aren't part of our public API - */ -public class CameraInternalApiTest extends BaseActivityTest { - - @Override - protected Class getActivityClass() { - return EspressoTestActivity.class; - } - - @Test - @Ignore - public void testBearing() { - validateTestSetup(); - - CameraPosition initialPosition = new - CameraPosition.Builder().target(new LatLng()).zoom(1).bearing(0).tilt(0).build(); - CameraPosition cameraPosition = mapboxMap.getCameraPosition(); - assertEquals("Default camera position should match default", cameraPosition, initialPosition); - - onView(withId(R.id.mapView)).perform(new BearingAction(mapboxMap)); - assertEquals("Bearing should match", 45.1f, MapViewUtils.getDirection(mapboxMap), TestConstants.BEARING_DELTA); - } - - @Test - @Ignore - public void testTilt() { - validateTestSetup(); - - CameraPosition initialPosition = new CameraPosition.Builder().target( - new LatLng()).zoom(1).bearing(0).tilt(0).build(); - CameraPosition cameraPosition = mapboxMap.getCameraPosition(); - assertEquals("Default camera position should match default", cameraPosition, initialPosition); - - onView(withId(R.id.mapView)).perform(new TiltAction(mapboxMap)); - assertEquals("Tilt should match", 40.0f, MapViewUtils.getTilt(mapboxMap), TestConstants.TILT_DELTA); - } - - @Test - @Ignore - public void testLatLng() { - validateTestSetup(); - - CameraPosition initialPosition = new CameraPosition.Builder().target( - new LatLng()).zoom(1).bearing(0).tilt(0).build(); - CameraPosition cameraPosition = mapboxMap.getCameraPosition(); - assertEquals("Default camera position should match default", cameraPosition, initialPosition); - - onView(withId(R.id.mapView)).perform(new LatLngAction(mapboxMap)); - LatLng centerCoordinate = MapViewUtils.getLatLng(mapboxMap); - assertEquals("Latitude should match", 1.1f, centerCoordinate.getLatitude(), TestConstants.LAT_LNG_DELTA); - assertEquals("Longitude should match", 2.2f, centerCoordinate.getLongitude(), TestConstants.LAT_LNG_DELTA); - } - - @After - public void unregisterIdlingResource() { - Espresso.unregisterIdlingResources(idlingResource); - } - - private class BearingAction implements ViewAction { - - private MapboxMap mapboxMap; - - BearingAction(MapboxMap mapboxMap) { - this.mapboxMap = mapboxMap; - } - - @Override - public Matcher getConstraints() { - return isDisplayed(); - } - - @Override - public String getDescription() { - return getClass().getSimpleName(); - } - - @Override - public void perform(UiController uiController, View view) { - MapViewUtils.setDirection(mapboxMap, -45.1f); - } - } - - private class TiltAction implements ViewAction { - - private MapboxMap mapboxMap; - - TiltAction(MapboxMap mapboxMap) { - this.mapboxMap = mapboxMap; - } - - @Override - public Matcher getConstraints() { - return isDisplayed(); - } - - @Override - public String getDescription() { - return getClass().getSimpleName(); - } - - @Override - public void perform(UiController uiController, View view) { - MapViewUtils.setTilt(mapboxMap, 40.0f); - } - } - - private class LatLngAction implements ViewAction { - - private MapboxMap mapboxMap; - - LatLngAction(MapboxMap mapboxMap) { - this.mapboxMap = mapboxMap; - } - - @Override - public Matcher getConstraints() { - return isDisplayed(); - } - - @Override - public String getDescription() { - return getClass().getSimpleName(); - } - - @Override - public void perform(UiController uiController, View view) { - MapViewUtils.setLatLng(mapboxMap, new LatLng(1.1, 2.2)); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java index f25fd6ab2..6277dffe9 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java @@ -115,7 +115,9 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe ValueAnimator latLngAnimator = ValueAnimator.ofObject(new LatLngEvaluator(), currentPosition, targetPosition); latLngAnimator.setDuration((long) (1000 * ANIMATION_DELAY_FACTOR)); latLngAnimator.setInterpolator(new FastOutSlowInInterpolator()); - latLngAnimator.addUpdateListener(animation -> mapboxMap.setLatLng((LatLng) animation.getAnimatedValue())); + latLngAnimator.addUpdateListener(animation -> mapboxMap.moveCamera( + CameraUpdateFactory.newLatLng((LatLng) animation.getAnimatedValue())) + ); return latLngAnimator; } @@ -124,7 +126,9 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe zoomAnimator.setDuration((long) (2200 * ANIMATION_DELAY_FACTOR)); zoomAnimator.setStartDelay((long) (600 * ANIMATION_DELAY_FACTOR)); zoomAnimator.setInterpolator(new AnticipateOvershootInterpolator()); - zoomAnimator.addUpdateListener(animation -> mapboxMap.setZoom((Float) animation.getAnimatedValue())); + zoomAnimator.addUpdateListener(animation -> mapboxMap.moveCamera( + CameraUpdateFactory.zoomTo((Float) animation.getAnimatedValue())) + ); return zoomAnimator; } @@ -133,7 +137,9 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe bearingAnimator.setDuration((long) (1000 * ANIMATION_DELAY_FACTOR)); bearingAnimator.setStartDelay((long) (1000 * ANIMATION_DELAY_FACTOR)); bearingAnimator.setInterpolator(new FastOutLinearInInterpolator()); - bearingAnimator.addUpdateListener(animation -> mapboxMap.setBearing((Float) animation.getAnimatedValue())); + bearingAnimator.addUpdateListener(animation -> mapboxMap.moveCamera( + CameraUpdateFactory.bearingTo((Float) animation.getAnimatedValue())) + ); return bearingAnimator; } @@ -141,7 +147,9 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe ValueAnimator tiltAnimator = ValueAnimator.ofFloat((float) currentTilt, (float) targetTilt); tiltAnimator.setDuration((long) (1000 * ANIMATION_DELAY_FACTOR)); tiltAnimator.setStartDelay((long) (1500 * ANIMATION_DELAY_FACTOR)); - tiltAnimator.addUpdateListener(animation -> mapboxMap.setTilt((Float) animation.getAnimatedValue())); + tiltAnimator.addUpdateListener(animation -> mapboxMap.moveCamera( + CameraUpdateFactory.tiltTo((Float) animation.getAnimatedValue())) + ); return tiltAnimator; } @@ -192,7 +200,9 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe ValueAnimator zoomAnimator = ValueAnimator.ofFloat(11.0f, 16.0f); zoomAnimator.setDuration((long) (duration * ANIMATION_DELAY_FACTOR)); zoomAnimator.setInterpolator(interpolator); - zoomAnimator.addUpdateListener(animation -> mapboxMap.setZoom((Float) animation.getAnimatedValue())); + zoomAnimator.addUpdateListener(animation -> mapboxMap.moveCamera( + CameraUpdateFactory.zoomTo((Float) animation.getAnimatedValue())) + ); return zoomAnimator; } From f6d652ddddd1181604b41d38459e085314080575 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Tue, 13 Feb 2018 15:14:44 -0500 Subject: [PATCH 053/264] [ios] Fix safeAreaInsets availability warning in MGLMapViewLayoutTests Fixes: 'safeAreaInsets' is only available on iOS 11.0 or newer [-Wunguarded-availability-new] --- platform/ios/test/MGLMapViewLayoutTests.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/ios/test/MGLMapViewLayoutTests.m b/platform/ios/test/MGLMapViewLayoutTests.m index a41e7695f..e707cfdb4 100644 --- a/platform/ios/test/MGLMapViewLayoutTests.m +++ b/platform/ios/test/MGLMapViewLayoutTests.m @@ -67,7 +67,7 @@ CGFloat bottomSafeAreaInset = 0.0; double accuracy = 0.01; - if ( [self.mapView respondsToSelector:@selector(safeAreaInsets)] ) { + if (@available(iOS 11.0, *)) { bottomSafeAreaInset = self.mapView.safeAreaInsets.bottom; } From aa6e06392923b49963f59a06ccc9a80de1c729b1 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Tue, 13 Feb 2018 15:39:12 -0500 Subject: [PATCH 054/264] [ios] Fix type conversion warnings in MGLFeatureTests Fixes: object of type 'MGLPointAnnotation/MGLPolyline *' is not compatible with array element type 'MGLShape *' [-Wobjc-literal-conversion] --- platform/darwin/test/MGLFeatureTests.mm | 27 ++++++++++++++++--------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/platform/darwin/test/MGLFeatureTests.mm b/platform/darwin/test/MGLFeatureTests.mm index 818ad8200..14b64be85 100644 --- a/platform/darwin/test/MGLFeatureTests.mm +++ b/platform/darwin/test/MGLFeatureTests.mm @@ -298,29 +298,36 @@ } - (void)testShapeCollectionFeatureGeoJSONDictionary { - MGLPointAnnotation *pointFeature = [[MGLPointAnnotation alloc] init]; + MGLPointFeature *pointFeature = [[MGLPointFeature alloc] init]; CLLocationCoordinate2D pointCoordinate = { 10, 10 }; pointFeature.coordinate = pointCoordinate; CLLocationCoordinate2D coord1 = { 0, 0 }; CLLocationCoordinate2D coord2 = { 10, 10 }; CLLocationCoordinate2D coords[] = { coord1, coord2 }; - MGLPolyline *polyline = [MGLPolyline polylineWithCoordinates:coords count:2]; + MGLPolylineFeature *polylineFeature = [MGLPolylineFeature polylineWithCoordinates:coords count:2]; + + MGLShapeCollectionFeature *shapeCollectionFeature = [MGLShapeCollectionFeature shapeCollectionWithShapes:@[pointFeature, polylineFeature]]; - MGLShapeCollectionFeature *shapeCollectionFeature = [MGLShapeCollectionFeature shapeCollectionWithShapes:@[pointFeature, - polyline]]; // A GeoJSON feature NSDictionary *geoJSONFeature = [shapeCollectionFeature geoJSONDictionary]; // it has the correct geometry NSDictionary *expectedGeometry = @{@"type": @"GeometryCollection", @"geometries": @[ - @{@"type": @"Point", - @"coordinates": @[@(pointCoordinate.longitude), @(pointCoordinate.latitude)]}, - @{@"type": @"LineString", - @"coordinates": @[@[@(coord1.longitude), @(coord1.latitude)], - @[@(coord2.longitude), @(coord2.latitude)]]} - ]}; + @{ @"geometry": @{@"type": @"Point", + @"coordinates": @[@(pointCoordinate.longitude), @(pointCoordinate.latitude)]}, + @"properties": [NSNull null], + @"type": @"Feature", + }, + @{ @"geometry": @{@"type": @"LineString", + @"coordinates": @[@[@(coord1.longitude), @(coord1.latitude)], + @[@(coord2.longitude), @(coord2.latitude)]]}, + @"properties": [NSNull null], + @"type": @"Feature", + } + ] + }; XCTAssertEqualObjects(geoJSONFeature[@"geometry"], expectedGeometry); // When the shape collection is created with an empty array of shapes From 7e51e52f0cabd909557b763f10e90ac0444e90a1 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Tue, 13 Feb 2018 16:14:29 -0500 Subject: [PATCH 055/264] [ios] Fix iOS 8's broken pluralization in MGLCoordinateFormatterTests --- .../darwin/test/MGLCoordinateFormatterTests.m | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/platform/darwin/test/MGLCoordinateFormatterTests.m b/platform/darwin/test/MGLCoordinateFormatterTests.m index ac083fa10..d693f739e 100644 --- a/platform/darwin/test/MGLCoordinateFormatterTests.m +++ b/platform/darwin/test/MGLCoordinateFormatterTests.m @@ -24,7 +24,12 @@ coordinate = CLLocationCoordinate2DMake(38.9131982, -77.0325453144239); XCTAssertEqualObjects([shortFormatter stringFromCoordinate:coordinate], @"38°54′48″N, 77°1′57″W"); XCTAssertEqualObjects([mediumFormatter stringFromCoordinate:coordinate], @"38°54′48″ north, 77°1′57″ west"); - XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degrees, 54 minutes, and 48 seconds north by 77 degrees, 1 minute, and 57 seconds west"); + if (@available(iOS 9.0, *)) { + XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degrees, 54 minutes, and 48 seconds north by 77 degrees, 1 minute, and 57 seconds west"); + } else { + // Foundation in iOS 8 does not know how to pluralize coordinates. + XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degree(s), 54 minute(s), and 48 second(s) north by 77 degree(s), 1 minute(s), and 57 second(s) west"); + } shortFormatter.allowsSeconds = NO; mediumFormatter.allowsSeconds = NO; @@ -33,7 +38,12 @@ coordinate = CLLocationCoordinate2DMake(38.9131982, -77.0325453144239); XCTAssertEqualObjects([shortFormatter stringFromCoordinate:coordinate], @"38°55′N, 77°2′W"); XCTAssertEqualObjects([mediumFormatter stringFromCoordinate:coordinate], @"38°55′ north, 77°2′ west"); - XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degrees and 55 minutes north by 77 degrees and 2 minutes west"); + if (@available(iOS 9.0, *)) { + XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degrees and 55 minutes north by 77 degrees and 2 minutes west"); + } else { + // Foundation in iOS 8 does not know how to pluralize coordinates. + XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degree(s) and 55 minute(s) north by 77 degree(s) and 2 minute(s) west"); + } shortFormatter.allowsMinutes = NO; mediumFormatter.allowsMinutes = NO; @@ -42,7 +52,12 @@ coordinate = CLLocationCoordinate2DMake(38.9131982, -77.0325453144239); XCTAssertEqualObjects([shortFormatter stringFromCoordinate:coordinate], @"39°N, 77°W"); XCTAssertEqualObjects([mediumFormatter stringFromCoordinate:coordinate], @"39° north, 77° west"); - XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"39 degrees north by 77 degrees west"); + if (@available(iOS 9.0, *)) { + XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"39 degrees north by 77 degrees west"); + } else { + // Foundation in iOS 8 does not know how to pluralize coordinates. + XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"39 degree(s) north by 77 degree(s) west"); + } } @end From b981713f3a265786f4c8fcbe934260a09cc09da3 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Tue, 13 Feb 2018 19:11:22 -0500 Subject: [PATCH 056/264] [ios] Bump testMGLMapSnapshotter timeout to 5s Timing based tests are inherently flakey and prone to failure on slow CI: > Test case 'MGLDocumentationExampleTests.testMGLMapSnapshotter()' failed on 'iPhone X' (3.375 seconds) > Test case 'MGLDocumentationExampleTests.testMGLMapSnapshotter()' failed on 'iPhone 8' (3.413 seconds) > Test case 'MGLDocumentationExampleTests.testMGLMapSnapshotter()' failed on 'iPhone 7' (2.944 seconds) --- platform/darwin/test/MGLDocumentationExampleTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/darwin/test/MGLDocumentationExampleTests.swift b/platform/darwin/test/MGLDocumentationExampleTests.swift index 5a6e00bc4..a216d9ad1 100644 --- a/platform/darwin/test/MGLDocumentationExampleTests.swift +++ b/platform/darwin/test/MGLDocumentationExampleTests.swift @@ -391,7 +391,7 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate { } //#-end-example-code - wait(for: [expectation], timeout: 1) + wait(for: [expectation], timeout: 5) } // For testMGLMapView(). From 91cd7f5aa9962c9d5a65e1b37abb85d5fe02753e Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Thu, 15 Feb 2018 12:18:08 -0500 Subject: [PATCH 057/264] [ios] Disable -[MGLExpressionTests testConditionalExpressionObject] on iOS 8 Temporarily disable this test until iOS 8 compatibility is added. --- platform/darwin/test/MGLExpressionTests.mm | 38 ++++++++++++---------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/platform/darwin/test/MGLExpressionTests.mm b/platform/darwin/test/MGLExpressionTests.mm index 821c5dbdb..a5ed2f7bf 100644 --- a/platform/darwin/test/MGLExpressionTests.mm +++ b/platform/darwin/test/MGLExpressionTests.mm @@ -573,23 +573,27 @@ using namespace std::string_literals; } - (void)testConditionalExpressionObject { - { - NSPredicate *conditional = [NSPredicate predicateWithFormat:@"1 = 2"]; - NSExpression *trueExpression = [NSExpression expressionForConstantValue:@YES]; - NSExpression *falseExpression = [NSExpression expressionForConstantValue:@NO]; - NSExpression *expression = [NSExpression expressionForConditional:conditional trueExpression:trueExpression falseExpression:falseExpression]; - NSArray *jsonExpression = @[@"case", @[@"==", @1, @2], @YES, @NO]; - XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSExpression expressionWithFormat:@"TERNARY(1 = 2, TRUE, FALSE)"].mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO); - XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); - } - { - NSExpression *expression = [NSExpression expressionWithFormat:@"TERNARY(0 = 1, TRUE, TERNARY(1 = 2, TRUE, FALSE))"]; - NSArray *jsonExpression = @[@"case", @[@"==", @0, @1], @YES, @[@"==", @1, @2], @YES, @NO]; - XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO); - XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + // FIXME: This test crashes because iOS 8 doesn't have `+[NSExpression expressionForConditional:trueExpression:falseExpression:]`. + // https://github.com/mapbox/mapbox-gl-native/issues/11007 + if (@available(iOS 9.0, *)) { + { + NSPredicate *conditional = [NSPredicate predicateWithFormat:@"1 = 2"]; + NSExpression *trueExpression = [NSExpression expressionForConstantValue:@YES]; + NSExpression *falseExpression = [NSExpression expressionForConstantValue:@NO]; + NSExpression *expression = [NSExpression expressionForConditional:conditional trueExpression:trueExpression falseExpression:falseExpression]; + NSArray *jsonExpression = @[@"case", @[@"==", @1, @2], @YES, @NO]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithFormat:@"TERNARY(1 = 2, TRUE, FALSE)"].mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"TERNARY(0 = 1, TRUE, TERNARY(1 = 2, TRUE, FALSE))"]; + NSArray *jsonExpression = @[@"case", @[@"==", @0, @1], @YES, @[@"==", @1, @2], @YES, @NO]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } } } From 00abd2eaa6dc06cb11a0d24a7128934aea0b9c7d Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Fri, 16 Feb 2018 20:01:03 -0500 Subject: [PATCH 058/264] [ios] Fix loop when first asking for location permission (#11229) --- platform/ios/CHANGELOG.md | 4 ++++ platform/ios/src/MGLMapView.mm | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index c11fc63eb..ab3752657 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -44,6 +44,10 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Labels are now transliterated from more languages when VoiceOver is enabled. ([#10881](https://github.com/mapbox/mapbox-gl-native/pull/10881)) * Long-pressing the attribution button causes the SDK’s version number to be displayed in the action sheet that appears. ([#10650](https://github.com/mapbox/mapbox-gl-native/pull/10650)) +## 3.7.5 + +* Fixed an issue where requesting location services permission would trigger an unrecoverable loop. ([#11229](https://github.com/mapbox/mapbox-gl-native/pull/11229)) + ## 3.7.4 - February 12, 2018 * Added the `MGLTileSourceOptionTileCoordinateBounds` option to create an `MGLTileSource` that only supplies tiles within a specific geographic bounding box. ([#11141](https://github.com/mapbox/mapbox-gl-native/pull/11141)) diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 927450b51..c8e084795 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -544,8 +544,9 @@ public: [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willTerminate) name:UIApplicationWillTerminateNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sleepGL:) name:UIApplicationDidEnterBackgroundNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(wakeGL:) name:UIApplicationWillEnterForegroundNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sleepGL:) name:UIApplicationWillResignActiveNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(wakeGL:) name:UIApplicationDidBecomeActiveNotification object:nil]; + // As of 3.7.5, we intentionally do not listen for `UIApplicationWillResignActiveNotification` or call `sleepGL:` in response to it, as doing + // so causes a loop when asking for location permission. See: https://github.com/mapbox/mapbox-gl-native/issues/11225 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil]; From c2be33fa4135f3c132f31437cfc0cd759c273790 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Fri, 16 Feb 2018 20:06:32 -0500 Subject: [PATCH 059/264] [ios] Bump podspecs for 3.7.5 --- platform/ios/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index ab3752657..094634980 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -44,7 +44,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Labels are now transliterated from more languages when VoiceOver is enabled. ([#10881](https://github.com/mapbox/mapbox-gl-native/pull/10881)) * Long-pressing the attribution button causes the SDK’s version number to be displayed in the action sheet that appears. ([#10650](https://github.com/mapbox/mapbox-gl-native/pull/10650)) -## 3.7.5 +## 3.7.5 - February 16, 2018 * Fixed an issue where requesting location services permission would trigger an unrecoverable loop. ([#11229](https://github.com/mapbox/mapbox-gl-native/pull/11229)) From 054d4d5ad676ce1ca9a740bc99ccd7ff2f9fecec Mon Sep 17 00:00:00 2001 From: Asheem Mamoowala Date: Fri, 16 Feb 2018 12:38:23 -0800 Subject: [PATCH 060/264] [core] Support a range of zooms in TileRange. Accounts for TilePyramid requesting parent tiles of ideal zoom tiles. --- src/mbgl/renderer/tile_pyramid.cpp | 7 +++- src/mbgl/util/tile_range.hpp | 60 ++++++++++++++++++++---------- test/util/tile_range.test.cpp | 13 ++++++- 3 files changed, 57 insertions(+), 23 deletions(-) diff --git a/src/mbgl/renderer/tile_pyramid.cpp b/src/mbgl/renderer/tile_pyramid.cpp index c4372e711..8f83a0f98 100644 --- a/src/mbgl/renderer/tile_pyramid.cpp +++ b/src/mbgl/renderer/tile_pyramid.cpp @@ -143,10 +143,13 @@ void TilePyramid::update(const std::vector>& layer auto it = tiles.find(tileID); return it == tiles.end() ? nullptr : it->second.get(); }; - + + // The min and max zoom for TileRange are based on the updateRenderables algorithm. + // Tiles are created at the ideal tile zoom or at lower zoom levels. Child + // tiles are used from the cache, but not created. optional tileRange = {}; if (bounds) { - tileRange = util::TileRange::fromLatLngBounds(*bounds, std::min(tileZoom, (int32_t)zoomRange.max)); + tileRange = util::TileRange::fromLatLngBounds(*bounds, zoomRange.min, std::min(tileZoom, (int32_t)zoomRange.max)); } auto createTileFn = [&](const OverscaledTileID& tileID) -> Tile* { if (tileRange && !tileRange->contains(tileID.canonical)) { diff --git a/src/mbgl/util/tile_range.hpp b/src/mbgl/util/tile_range.hpp index f630a4907..8554cfb65 100644 --- a/src/mbgl/util/tile_range.hpp +++ b/src/mbgl/util/tile_range.hpp @@ -6,41 +6,61 @@ #include namespace mbgl { - namespace util { class TileRange { public: - Range> range; - uint8_t z; + Range> range; + Range zoomRange; + + // Compute the range of tiles covered by the bounds at maxZoom. + static TileRange fromLatLngBounds(const LatLngBounds& bounds, uint8_t minZoom, uint8_t maxZoom) { + if (minZoom > maxZoom) { + std::swap(minZoom, maxZoom); + } + + auto swProj = Projection::project(bounds.southwest().wrapped(), maxZoom); + auto ne = bounds.northeast(); + auto neProj = Projection::project(ne.longitude() > util::LONGITUDE_MAX ? ne.wrapped() : ne , maxZoom); + + const auto maxTile = std::pow(2.0, maxZoom); + const auto minX = static_cast(std::floor(swProj.x)); + const auto maxX = static_cast(std::floor(neProj.x)); + const auto minY = static_cast(util::clamp(std::floor(neProj.y), 0.0 , maxTile)); + const auto maxY = static_cast(util::clamp(std::floor(swProj.y), 0.0, maxTile)); + + return TileRange({ {minX, minY}, {maxX, maxY} }, {minZoom, maxZoom}); + } // Compute the range of tiles covered by the bounds. static TileRange fromLatLngBounds(const LatLngBounds& bounds, uint8_t z) { - auto swProj = Projection::project(bounds.southwest().wrapped(), z); - auto ne = bounds.northeast(); - auto neProj = Projection::project(ne.longitude() > util::LONGITUDE_MAX ? ne.wrapped() : ne , z); - const auto minX = std::floor(swProj.x); - const auto maxX = std::ceil(neProj.x); - const auto minY = std::floor(neProj.y); - const auto maxY = std::ceil(swProj.y); - return TileRange({ {minX, minY}, {maxX, maxY} }, z); + return fromLatLngBounds(bounds, z, z); } bool contains(const CanonicalTileID& tileID) { - return z == tileID.z && - (range.min.x >= range.max.x ? //For wrapped bounds - tileID.x >= range.min.x || tileID.x < range.max.x : - tileID.x < range.max.x && tileID.x >= range.min.x) && - tileID.y < range.max.y && - tileID.y >= range.min.y; + if (tileID.z <= zoomRange.max && tileID.z >= zoomRange.min) { + if (tileID.z == 0) { + return true; + } + uint8_t dz = (zoomRange.max - tileID.z); + auto x0 = range.min.x >> dz; + auto x1 = range.max.x >> dz; + auto y0 = range.min.y >> dz; + auto y1 = range.max.y >> dz; + return (range.min.x > range.max.x ? //For wrapped bounds + tileID.x >= x0 || tileID.x <= x1 : + tileID.x <= x1 && tileID.x >= x0) && + tileID.y <= y1 && + tileID.y >= y0; + } + return false; } private: - TileRange(Range> range_, uint8_t z_) + TileRange(Range> range_, Range z_) : range(range_), - z(z_) { + zoomRange(z_) { } - }; } // namespace util diff --git a/test/util/tile_range.test.cpp b/test/util/tile_range.test.cpp index dc8ae2870..c4c37c74d 100644 --- a/test/util/tile_range.test.cpp +++ b/test/util/tile_range.test.cpp @@ -1,4 +1,3 @@ - #include #include #include @@ -25,6 +24,18 @@ TEST(TileRange, ContainsBoundsFromTile) { EXPECT_TRUE(range.contains(CanonicalTileID(10, 162, 395))); } } + +TEST(TileRange, ContainsMultiZoom) { + auto wrappedBounds = LatLngBounds::hull({ 37.6609, -122.5744 }, { 37.8271, -122.3204 }); + auto range = util::TileRange::fromLatLngBounds(wrappedBounds, 5, 13); + EXPECT_FALSE(range.contains(CanonicalTileID(0, 0, 0))); + EXPECT_FALSE(range.contains(CanonicalTileID(5, 3, 11))); + EXPECT_FALSE(range.contains(CanonicalTileID(6, 9, 22))); + EXPECT_TRUE(range.contains(CanonicalTileID(5, 5, 12))); + EXPECT_TRUE(range.contains(CanonicalTileID(6, 10, 24))); + EXPECT_TRUE(range.contains(CanonicalTileID(13, 1310, 3166))); +} + TEST(TileRange, ContainsIntersectingTiles) { auto bounds = LatLngBounds::hull({ 37.6609, -122.5744 }, { 37.8271, -122.3204 }); auto range = util::TileRange::fromLatLngBounds(bounds, 13); From 25629c81a691c65572a69b30ec0a5866cdcdb819 Mon Sep 17 00:00:00 2001 From: Julian Rex Date: Tue, 20 Feb 2018 15:46:35 -0500 Subject: [PATCH 061/264] [ios] Podspec bump 4.0.0-beta.2 (#11263) * [ios] Podspec bump 4.0.0-beta.2 * [ios] Improved change log messaging. --- platform/ios/CHANGELOG.md | 3 +++ platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec | 2 +- platform/ios/Mapbox-iOS-SDK-symbols.podspec | 2 +- platform/ios/Mapbox-iOS-SDK.podspec | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 094634980..67a18d471 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -17,6 +17,8 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Added an `MGLComputedShapeSource` class that allows applications to supply vector data to a style layer on a per-tile basis. ([#9983](https://github.com/mapbox/mapbox-gl-native/pull/9983)) * A style can now display smooth hillshading and customize its appearance at runtime using the `MGLHillshadeStyleLayer` class. Hillshading is based on a rasterized digital elevation model supplied by the `MGLRasterDEMSource` class. ([#10642](https://github.com/mapbox/mapbox-gl-native/pull/10642)) * The `MGLSymbolStyleLayer.textFontNames` property can now depend on a feature’s attributes. ([#10850](https://github.com/mapbox/mapbox-gl-native/pull/10850)) +* Added `MGLShapeSourceOptionWrapsCoordinates`, to specify whether the shape of an `MGLComputedShapeSource` should be wrapped to accommodate coordinates with longitudes beyond −180 and 180; and `MGLShapeSourceOptionClipsCoordinates`, to specify whether the shape of an `MGLComputedShapeSource` should be clipped at the edge of each tile. ([#11041](https://github.com/mapbox/mapbox-gl-native/pull/11041)) +* Added support for Mapzen Terrarium DEM encoding. ([#11154](https://github.com/mapbox/mapbox-gl-native/pull/11154)) ### Map rendering @@ -43,6 +45,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Fixed an issue preventing labels from being transliterated when VoiceOver was enabled on iOS 10._x_ and below. ([#10881](https://github.com/mapbox/mapbox-gl-native/pull/10881)) * Labels are now transliterated from more languages when VoiceOver is enabled. ([#10881](https://github.com/mapbox/mapbox-gl-native/pull/10881)) * Long-pressing the attribution button causes the SDK’s version number to be displayed in the action sheet that appears. ([#10650](https://github.com/mapbox/mapbox-gl-native/pull/10650)) +* Reduced offline download size for styles with symbol layers that render only icons, and no text. ([#11055](https://github.com/mapbox/mapbox-gl-native/pull/11055)) ## 3.7.5 - February 16, 2018 diff --git a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec index 56714a521..56f068207 100644 --- a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec +++ b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '4.0.0-beta.1' + version = '4.0.0-beta.2' m.name = 'Mapbox-iOS-SDK-nightly-dynamic' m.version = "#{version}-nightly" diff --git a/platform/ios/Mapbox-iOS-SDK-symbols.podspec b/platform/ios/Mapbox-iOS-SDK-symbols.podspec index fb7e55bbd..6c9bc3b56 100644 --- a/platform/ios/Mapbox-iOS-SDK-symbols.podspec +++ b/platform/ios/Mapbox-iOS-SDK-symbols.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '4.0.0-beta.1' + version = '4.0.0-beta.2' m.name = 'Mapbox-iOS-SDK-symbols' m.version = "#{version}-symbols" diff --git a/platform/ios/Mapbox-iOS-SDK.podspec b/platform/ios/Mapbox-iOS-SDK.podspec index ee3510370..657417da1 100644 --- a/platform/ios/Mapbox-iOS-SDK.podspec +++ b/platform/ios/Mapbox-iOS-SDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '4.0.0-beta.1' + version = '4.0.0-beta.2' m.name = 'Mapbox-iOS-SDK' m.version = version From 0525fd1e4a207040b6268ee0b2a9634d8fa37753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Tue, 20 Feb 2018 12:57:59 -0800 Subject: [PATCH 062/264] [ios, macos] Unwrap coordinate bounds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updated coordinate bounds conversion methods to use the beyond-±180 method to indicate that the bounds extends beyond the antimeridian. Also copyedited conversion documentation. --- platform/ios/CHANGELOG.md | 1 + platform/ios/src/MGLMapView.h | 36 +++++++++++++++++------- platform/ios/src/MGLMapView.mm | 48 ++++++++++++-------------------- platform/macos/CHANGELOG.md | 1 + platform/macos/src/MGLMapView.h | 21 +++++++------- platform/macos/src/MGLMapView.mm | 44 ++++++++++++----------------- 6 files changed, 75 insertions(+), 76 deletions(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 67a18d471..7e82bf7dc 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -41,6 +41,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT ### Other changes +* The `-[MGLMapView convertRect:toCoordinateBoundsFromView:]` method and the `MGLMapView.visibleCoordinateBounds` property’s getter now indicate that the coordinate bounds straddles the antimeridian by extending one side beyond ±180 degrees longitude. ([#11265](https://github.com/mapbox/mapbox-gl-native/pull/11265)) * Feature querying results now account for the `MGLSymbolStyleLayer.circleStrokeWidth` property. ([#10897](https://github.com/mapbox/mapbox-gl-native/pull/10897)) * Fixed an issue preventing labels from being transliterated when VoiceOver was enabled on iOS 10._x_ and below. ([#10881](https://github.com/mapbox/mapbox-gl-native/pull/10881)) * Labels are now transliterated from more languages when VoiceOver is enabled. ([#10881](https://github.com/mapbox/mapbox-gl-native/pull/10881)) diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h index a4dcc4cdb..9c18965b0 100644 --- a/platform/ios/src/MGLMapView.h +++ b/platform/ios/src/MGLMapView.h @@ -659,11 +659,10 @@ MGL_EXPORT IB_DESIGNABLE want to animate the change, call `-setVisibleCoordinateBounds:animated:` instead. - If a longitude is less than −180 degrees or greater than 180 degrees, the visible - bounds straddles the antimeridian or international date line. - - For example, a visible bounds that stretches from Tokyo to San Francisco would have - coordinates of (35.68476, -220.24257) and (37.78428, -122.41310). + If a longitude is less than −180 degrees or greater than 180 degrees, the + visible bounds straddles the antimeridian or international date line. For + example, if both Tokyo and San Francisco are visible, the visible bounds might + extend from (35.68476, −220.24257) to (37.78428, −122.41310). */ @property (nonatomic) MGLCoordinateBounds visibleCoordinateBounds; @@ -671,11 +670,10 @@ MGL_EXPORT IB_DESIGNABLE Changes the receiver’s viewport to fit the given coordinate bounds, optionally animating the change. - To make the visible bounds go across the antimeridian or international date line, - specify some longitudes less than −180 degrees or greater than 180 degrees. - - For example, a visible bounds that stretches from Tokyo to San Francisco would have - coordinates of (35.68476, -220.24257) and (37.78428, -122.41310). + To bring both sides of the antimeridian or international date line into view, + specify some longitudes less than −180 degrees or greater than 180 degrees. For + example, to show both Tokyo and San Francisco simultaneously, you could set the + visible bounds to extend from (35.68476, −220.24257) to (37.78428, −122.41310). @param bounds The bounds that the viewport will show in its entirety. @param animated Specify `YES` to animate the change by smoothly scrolling @@ -686,6 +684,11 @@ MGL_EXPORT IB_DESIGNABLE /** Changes the receiver’s viewport to fit the given coordinate bounds and optionally some additional padding on each side. + + To bring both sides of the antimeridian or international date line into view, + specify some longitudes less than −180 degrees or greater than 180 degrees. For + example, to show both Tokyo and San Francisco simultaneously, you could set the + visible bounds to extend from (35.68476, −220.24257) to (37.78428, −122.41310). @param bounds The bounds that the viewport will show in its entirety. @param insets The minimum padding (in screen points) that will be visible @@ -698,6 +701,11 @@ MGL_EXPORT IB_DESIGNABLE /** Changes the receiver’s viewport to fit all of the given coordinates and optionally some additional padding on each side. + + To bring both sides of the antimeridian or international date line into view, + specify some longitudes less than −180 degrees or greater than 180 degrees. For + example, to show both Tokyo and San Francisco simultaneously, you could set the + visible coordinates to (35.68476, −220.24257) and (37.78428, −122.41310). @param coordinates The coordinates that the viewport will show. @param count The number of coordinates. This number must not be greater than @@ -712,6 +720,11 @@ MGL_EXPORT IB_DESIGNABLE /** Changes the receiver’s viewport to fit all of the given coordinates and optionally some additional padding on each side. + + To bring both sides of the antimeridian or international date line into view, + specify some longitudes less than −180 degrees or greater than 180 degrees. For + example, to show both Tokyo and San Francisco simultaneously, you could set the + visible coordinates to (35.68476, −220.24257) and (37.78428, −122.41310). @param coordinates The coordinates that the viewport will show. @param count The number of coordinates. This number must not be greater than @@ -994,6 +1007,9 @@ MGL_EXPORT IB_DESIGNABLE /** Converts a rectangle in the given view’s coordinate system to a geographic bounding box. + + If a longitude is less than −180 degrees or greater than 180 degrees, the + bounding box straddles the antimeridian or international date line. @param rect The rectangle to convert. @param view The view in whose coordinate system the rectangle is expressed. diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index c8e084795..1cb477955 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -3423,36 +3423,24 @@ public: /// bounding box. - (mbgl::LatLngBounds)convertRect:(CGRect)rect toLatLngBoundsFromView:(nullable UIView *)view { - mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty(); - bounds.extend([self convertPoint:rect.origin toLatLngFromView:view]); - bounds.extend([self convertPoint:{ CGRectGetMaxX(rect), CGRectGetMinY(rect) } toLatLngFromView:view]); - bounds.extend([self convertPoint:{ CGRectGetMaxX(rect), CGRectGetMaxY(rect) } toLatLngFromView:view]); - bounds.extend([self convertPoint:{ CGRectGetMinX(rect), CGRectGetMaxY(rect) } toLatLngFromView:view]); - - // The world is wrapping if a point just outside the bounds is also within - // the rect. - mbgl::LatLng outsideLatLng; - if (bounds.west() > -180) - { - outsideLatLng = { - (bounds.south() + bounds.north()) / 2, - bounds.west() - 1, - }; - } - else if (bounds.east() < 180) - { - outsideLatLng = { - (bounds.south() + bounds.north()) / 2, - bounds.east() + 1, - }; - } - - // If the world is wrapping, extend the bounds to cover all longitudes. - if (CGRectContainsPoint(rect, [self convertLatLng:outsideLatLng toPointToView:view])) - { - bounds.extend(mbgl::LatLng(bounds.south(), -180)); - bounds.extend(mbgl::LatLng(bounds.south(), 180)); - } + auto bounds = mbgl::LatLngBounds::empty(); + auto topLeft = [self convertPoint:{ CGRectGetMinX(rect), CGRectGetMinY(rect) } toLatLngFromView:view]; + auto topRight = [self convertPoint:{ CGRectGetMaxX(rect), CGRectGetMinY(rect) } toLatLngFromView:view]; + auto bottomRight = [self convertPoint:{ CGRectGetMaxX(rect), CGRectGetMaxY(rect) } toLatLngFromView:view]; + auto bottomLeft = [self convertPoint:{ CGRectGetMinX(rect), CGRectGetMaxY(rect) } toLatLngFromView:view]; + + // If the bounds straddles the antimeridian, unwrap it so that one side + // extends beyond ±180° longitude. + auto center = [self convertPoint:{ CGRectGetMidX(rect), CGRectGetMidY(rect) } toLatLngFromView:view]; + topLeft.unwrapForShortestPath(center); + topRight.unwrapForShortestPath(center); + bottomRight.unwrapForShortestPath(center); + bottomLeft.unwrapForShortestPath(center); + + bounds.extend(topLeft); + bounds.extend(topRight); + bounds.extend(bottomRight); + bounds.extend(bottomLeft); return bounds; } diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 06fedf7cc..342262c1d 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -30,6 +30,7 @@ ### Other changes * Added Danish and Hebrew localizations. ([#10967](https://github.com/mapbox/mapbox-gl-native/pull/10967), [#11136](https://github.com/mapbox/mapbox-gl-native/pull/11134)) +* The `-[MGLMapView convertRect:toCoordinateBoundsFromView:]` method and the `MGLMapView.visibleCoordinateBounds` property’s getter now indicate that the coordinate bounds straddles the antimeridian by extending one side beyond ±180 degrees longitude. ([#11265](https://github.com/mapbox/mapbox-gl-native/pull/11265)) * Feature querying results now account for the `MGLSymbolStyleLayer.circleStrokeWidth` property. ([#10897](https://github.com/mapbox/mapbox-gl-native/pull/10897)) * Removed methods, properties, and constants that had been deprecated as of v0.6.1. ([#11205](https://github.com/mapbox/mapbox-gl-native/pull/11205)) diff --git a/platform/macos/src/MGLMapView.h b/platform/macos/src/MGLMapView.h index 96b0932c1..050145b80 100644 --- a/platform/macos/src/MGLMapView.h +++ b/platform/macos/src/MGLMapView.h @@ -397,11 +397,10 @@ MGL_EXPORT IB_DESIGNABLE want to animate the change, use the `-setVisibleCoordinateBounds:animated:` method instead. - If a longitude is less than −180 degrees or greater than 180 degrees, the visible - bounds straddles the antimeridian or international date line. - - For example, a visible bounds that stretches from Tokyo to San Francisco would have - coordinates of (35.68476, -220.24257) and (37.78428, -122.41310). + If a longitude is less than −180 degrees or greater than 180 degrees, the + visible bounds straddles the antimeridian or international date line. For + example, if both Tokyo and San Francisco are visible, the visible bounds might + extend from (35.68476, −220.24257) to (37.78428, −122.41310). */ @property (nonatomic) MGLCoordinateBounds visibleCoordinateBounds; @@ -409,11 +408,10 @@ MGL_EXPORT IB_DESIGNABLE Changes the receiver’s viewport to fit the given coordinate bounds, optionally animating the change. - To make the visible bounds go across the antimeridian or international date line, - specify some longitudes less than −180 degrees or greater than 180 degrees. - - For example, a visible bounds that stretches from Tokyo to San Francisco would have - coordinates of (35.68476, -220.24257) and (37.78428, -122.41310). + To bring both sides of the antimeridian or international date line into view, + specify some longitudes less than −180 degrees or greater than 180 degrees. For + example, to show both Tokyo and San Francisco simultaneously, you could set the + visible bounds to extend from (35.68476, −220.24257) to (37.78428, −122.41310). @param bounds The bounds that the viewport will show in its entirety. @param animated Specify `YES` to animate the change by smoothly scrolling and @@ -1060,6 +1058,9 @@ MGL_EXPORT IB_DESIGNABLE /** Converts a rectangle in the given view’s coordinate system to a geographic bounding box. + + If a longitude is less than −180 degrees or greater than 180 degrees, the + bounding box straddles the antimeridian or international date line. @param rect The rectangle to convert. @param view The view in whose coordinate system the rectangle is expressed. diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm index 9ec901219..7902fb5b6 100644 --- a/platform/macos/src/MGLMapView.mm +++ b/platform/macos/src/MGLMapView.mm @@ -2717,32 +2717,24 @@ public: /// Converts a rectangle in the given view’s coordinate system to a geographic /// bounding box. - (mbgl::LatLngBounds)convertRect:(NSRect)rect toLatLngBoundsFromView:(nullable NSView *)view { - mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty(); - bounds.extend([self convertPoint:rect.origin toLatLngFromView:view]); - bounds.extend([self convertPoint:{ NSMaxX(rect), NSMinY(rect) } toLatLngFromView:view]); - bounds.extend([self convertPoint:{ NSMaxX(rect), NSMaxY(rect) } toLatLngFromView:view]); - bounds.extend([self convertPoint:{ NSMinX(rect), NSMaxY(rect) } toLatLngFromView:view]); - - // The world is wrapping if a point just outside the bounds is also within - // the rect. - mbgl::LatLng outsideLatLng; - if (bounds.west() > -180) { - outsideLatLng = { - (bounds.south() + bounds.north()) / 2, - bounds.west() - 1, - }; - } else if (bounds.northeast().longitude() < 180) { - outsideLatLng = { - (bounds.south() + bounds.north()) / 2, - bounds.east() + 1, - }; - } - - // If the world is wrapping, extend the bounds to cover all longitudes. - if (NSPointInRect([self convertLatLng:outsideLatLng toPointToView:view], rect)) { - bounds.extend(mbgl::LatLng(bounds.south(), -180)); - bounds.extend(mbgl::LatLng(bounds.south(), 180)); - } + auto bounds = mbgl::LatLngBounds::empty(); + auto bottomLeft = [self convertPoint:{ NSMinX(rect), NSMinY(rect) } toLatLngFromView:view]; + auto bottomRight = [self convertPoint:{ NSMaxX(rect), NSMinY(rect) } toLatLngFromView:view]; + auto topRight = [self convertPoint:{ NSMaxX(rect), NSMaxY(rect) } toLatLngFromView:view]; + auto topLeft = [self convertPoint:{ NSMinX(rect), NSMaxY(rect) } toLatLngFromView:view]; + + // If the bounds straddles the antimeridian, unwrap it so that one side + // extends beyond ±180° longitude. + auto center = [self convertPoint:{ NSMidX(rect), NSMidY(rect) } toLatLngFromView:view]; + bottomLeft.unwrapForShortestPath(center); + bottomRight.unwrapForShortestPath(center); + topRight.unwrapForShortestPath(center); + topLeft.unwrapForShortestPath(center); + + bounds.extend(bottomLeft); + bounds.extend(bottomRight); + bounds.extend(topRight); + bounds.extend(topLeft); return bounds; } From b16893f903b3f81803d59cdfa3253a3fb09cfd3e Mon Sep 17 00:00:00 2001 From: Anand Thakker Date: Wed, 21 Feb 2018 21:39:33 -0500 Subject: [PATCH 063/264] Correctly parse constant expressions in dds style properties (#11282) * Correctly parse constant expressions in dds style properties Closes #10849 * Clarify --- .../conversion/data_driven_property_value.hpp | 21 ++++++++++++++++--- include/mbgl/style/expression/literal.hpp | 4 ++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/include/mbgl/style/conversion/data_driven_property_value.hpp b/include/mbgl/style/conversion/data_driven_property_value.hpp index 8880d28fb..5f4395f35 100644 --- a/include/mbgl/style/conversion/data_driven_property_value.hpp +++ b/include/mbgl/style/conversion/data_driven_property_value.hpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include @@ -32,12 +34,25 @@ struct Converter> { return {}; } - if (isFeatureConstant(**expression)) { + bool featureConstant = isFeatureConstant(**expression); + bool zoomConstant = isZoomConstant(**expression); + + if (featureConstant && !zoomConstant) { return DataDrivenPropertyValue(CameraFunction(std::move(*expression))); - } else if (isZoomConstant(**expression)) { + } else if (!featureConstant && zoomConstant) { return DataDrivenPropertyValue(SourceFunction(std::move(*expression))); - } else { + } else if (!featureConstant && !zoomConstant) { return DataDrivenPropertyValue(CompositeFunction(std::move(*expression))); + } else { + // If an expression is neither zoom- nor feature-dependent, it + // should have been reduced to a Literal when it was parsed. + auto literal = dynamic_cast(expression->get()); + assert(literal); + optional constant = fromExpressionValue(literal->getValue()); + if (!constant) { + return {}; + } + return DataDrivenPropertyValue(*constant); } } else if (!isObject(value)) { optional constant = convert(value, error); diff --git a/include/mbgl/style/expression/literal.hpp b/include/mbgl/style/expression/literal.hpp index 82983d78a..a5c5fa109 100644 --- a/include/mbgl/style/expression/literal.hpp +++ b/include/mbgl/style/expression/literal.hpp @@ -32,6 +32,10 @@ public: std::vector> possibleOutputs() const override { return {{ value }}; } + + Value getValue() const { + return value; + } private: Value value; From 1862760faf84b2d5548d18aec677cbbdcca749e6 Mon Sep 17 00:00:00 2001 From: osana Date: Mon, 19 Feb 2018 22:55:43 -0500 Subject: [PATCH 064/264] [android] jni clean up - missing a couple DeleteLocalRef --- platform/android/src/conversion/collection.hpp | 1 + platform/android/src/file_source.cpp | 5 ++++- platform/android/src/map/camera_position.cpp | 4 +++- platform/android/src/map/image.cpp | 4 +++- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/platform/android/src/conversion/collection.hpp b/platform/android/src/conversion/collection.hpp index 549121c7e..2b953e73f 100644 --- a/platform/android/src/conversion/collection.hpp +++ b/platform/android/src/conversion/collection.hpp @@ -28,6 +28,7 @@ inline jni::jobject* toArrayList(JNIEnv& env, jni::jarray& array) { inline std::vector toVector(JNIEnv& env, jni::jarray& array) { std::vector vector; std::size_t len = jni::GetArrayLength(env, array); + vector.reserve(len); for (std::size_t i = 0; i < len; i++) { jni::jstring* jstr = reinterpret_cast(jni::GetObjectArrayElement(env, array, i)); diff --git a/platform/android/src/file_source.cpp b/platform/android/src/file_source.cpp index 6a9d7badb..612619a30 100644 --- a/platform/android/src/file_source.cpp +++ b/platform/android/src/file_source.cpp @@ -124,8 +124,11 @@ jni::Class FileSource::ResourceTransformC std::string FileSource::ResourceTransformCallback::onURL(jni::JNIEnv& env, jni::Object callback, int kind, std::string url_) { static auto method = FileSource::ResourceTransformCallback::javaClass.GetMethod(env, "onURL"); auto url = jni::Make(env, url_); + url = callback.Call(env, method, kind, url); - return jni::Make(env, url); + auto urlStr = jni::Make(env, url); + jni::DeleteLocalRef(env, url); + return urlStr; } } // namespace android diff --git a/platform/android/src/map/camera_position.cpp b/platform/android/src/map/camera_position.cpp index 1fc5f9789..01ffc6530 100644 --- a/platform/android/src/map/camera_position.cpp +++ b/platform/android/src/map/camera_position.cpp @@ -33,7 +33,9 @@ mbgl::CameraOptions CameraPosition::getCameraOptions(jni::JNIEnv& env, jni::Obje static auto tilt = CameraPosition::javaClass.GetField(env, "tilt"); static auto zoom = CameraPosition::javaClass.GetField(env, "zoom"); - auto center = LatLng::getLatLng(env, position.Get(env, target)); + auto jtarget = position.Get(env, target); + auto center = LatLng::getLatLng(env, jtarget); + jni::DeleteLocalRef(env, jtarget); return mbgl::CameraOptions { center, diff --git a/platform/android/src/map/image.cpp b/platform/android/src/map/image.cpp index 5f5c90edd..52e0e0d25 100644 --- a/platform/android/src/map/image.cpp +++ b/platform/android/src/map/image.cpp @@ -16,7 +16,9 @@ mbgl::style::Image Image::getImage(jni::JNIEnv& env, jni::Object image) { auto width = image.Get(env, widthField); auto pixelRatio = image.Get(env, pixelRatioField); auto pixels = image.Get(env, bufferField); - auto name = jni::Make(env, image.Get(env, nameField)); + auto jName = image.Get(env, nameField); + auto name = jni::Make(env, jName); + jni::DeleteLocalRef(env, jName); jni::NullCheck(env, &pixels); std::size_t size = pixels.Length(env); From 278cd580fc963ef8604de4e280501fd85564c274 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Tue, 20 Feb 2018 18:04:49 +0100 Subject: [PATCH 065/264] [android] - fix javadoc utf8 encoding issues, add missing package-info.java items --- .../com/mapbox/mapboxsdk/attribution/package-info.java | 4 ++++ .../main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java | 8 ++++---- .../java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java | 8 ++++---- .../mapbox/mapboxsdk/maps/renderer/egl/package-info.java | 4 ++++ .../maps/renderer/glsurfaceview/package-info.java | 4 ++++ .../com/mapbox/mapboxsdk/maps/renderer/package-info.java | 4 ++++ .../mapboxsdk/maps/renderer/textureview/package-info.java | 4 ++++ .../offline/OfflineTilePyramidRegionDefinition.java | 6 +++--- .../com/mapbox/mapboxsdk/snapshotter/package-info.java | 4 ++++ .../mapbox/mapboxsdk/style/expressions/package-info.java | 4 ++++ .../mapbox/mapboxsdk/style/functions/CameraFunction.java | 2 +- .../com/mapbox/mapboxsdk/style/functions/Function.java | 4 ++-- .../main/java/com/mapbox/mapboxsdk/style/light/Light.java | 4 ++-- .../java/com/mapbox/mapboxsdk/style/light/Position.java | 8 ++++---- .../main/java/com/mapbox/mapboxsdk/text/package-info.java | 4 ++++ platform/android/scripts/generate-style-code.js | 1 + 16 files changed, 53 insertions(+), 20 deletions(-) create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/package-info.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/package-info.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/glsurfaceview/package-info.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/package-info.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/package-info.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/package-info.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/package-info.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/package-info.java diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/package-info.java new file mode 100644 index 000000000..57d840af5 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/package-info.java @@ -0,0 +1,4 @@ +/** + * Contains the Mapbox Maps Android Attribution API classes. + */ +package com.mapbox.mapboxsdk.attribution; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index 6085e2c2d..5573e2d24 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java @@ -1651,13 +1651,13 @@ public final class MapboxMap { /** *

- * Sets the distance from the edges of the map view’s frame to the edges of the map - * view’s logical viewport. + * Sets the distance from the edges of the map view's frame to the edges of the map + * view's logical viewport. *

*

* When the value of this property is equal to {0,0,0,0}, viewport - * properties such as `centerCoordinate` assume a viewport that matches the map - * view’s frame. Otherwise, those properties are inset, excluding part of the + * properties such as 'centerCoordinate' assume a viewport that matches the map + * view's frame. Otherwise, those properties are inset, excluding part of the * frame from the viewport. For instance, if the only the top edge is inset, the * map center is effectively shifted downward. *

diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java index 49188a5a9..759a0a0f2 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java @@ -734,11 +734,11 @@ public class MapboxMapOptions implements Parcelable { } /** - * Set the font family for generating glyphs locally for ideographs in the ‘CJK Unified Ideographs’ - * and ‘Hangul Syllables’ ranges. + * Set the font family for generating glyphs locally for ideographs in the 'CJK Unified Ideographs' + * and 'Hangul Syllables' ranges. * * The font family argument is passed to {@link android.graphics.Typeface#create(String, int)}. - * Default system fonts are defined in '/system/etc/fonts.xml' + * Default system fonts are defined in '/system/etc/fonts.xml' * * @param fontFamily font family for local ideograph generation. * @return This @@ -1100,7 +1100,7 @@ public class MapboxMapOptions implements Parcelable { /** * Returns the font-family for locally overriding generation of glyphs in the - * ‘CJK Unified Ideographs’ and ‘Hangul Syllables’ ranges. + * 'CJK Unified Ideographs' and 'Hangul Syllables' ranges. * * @return Local ideograph font family name. */ diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/package-info.java new file mode 100644 index 000000000..b14b30265 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/package-info.java @@ -0,0 +1,4 @@ +/** + * Contains the Mapbox Maps Android EGL API classes. + */ +package com.mapbox.mapboxsdk.maps.renderer.egl; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/glsurfaceview/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/glsurfaceview/package-info.java new file mode 100644 index 000000000..aefcffef4 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/glsurfaceview/package-info.java @@ -0,0 +1,4 @@ +/** + * Contains the Mapbox Maps Android GLSurfaceView API classes. + */ +package com.mapbox.mapboxsdk.maps.renderer.glsurfaceview; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/package-info.java new file mode 100644 index 000000000..f5d8021ea --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/package-info.java @@ -0,0 +1,4 @@ +/** + * Contains the Mapbox Maps Android Renderer API classes. + */ +package com.mapbox.mapboxsdk.maps.renderer; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/package-info.java new file mode 100644 index 000000000..d3585d41f --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/package-info.java @@ -0,0 +1,4 @@ +/** + * Contains the Mapbox Maps Android TextureView API classes. + */ +package com.mapbox.mapboxsdk.maps.renderer.textureview; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineTilePyramidRegionDefinition.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineTilePyramidRegionDefinition.java index 2a32f0bdd..ea9a066df 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineTilePyramidRegionDefinition.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineTilePyramidRegionDefinition.java @@ -10,12 +10,12 @@ import com.mapbox.mapboxsdk.geometry.LatLngBounds; * An offline region defined by a style URL, geographic bounding box, zoom range, and * device pixel ratio. *

- * Both minZoom and maxZoom must be ≥ 0, and maxZoom must be ≥ minZoom. + * Both minZoom and maxZoom must be ≥ 0, and maxZoom must be ≥ minZoom. *

- * maxZoom may be ∞, in which case for each tile source, the region will include + * maxZoom may be ∞, in which case for each tile source, the region will include * tiles from minZoom up to the maximum zoom level provided by that source. *

- * pixelRatio must be ≥ 0 and should typically be 1.0 or 2.0. + * pixelRatio must be ≥ 0 and should typically be 1.0 or 2.0. */ public class OfflineTilePyramidRegionDefinition implements OfflineRegionDefinition, Parcelable { diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/package-info.java new file mode 100644 index 000000000..f1ce247ba --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/package-info.java @@ -0,0 +1,4 @@ +/** + * Contains the Mapbox Maps Android Snapshotter API classes. + */ +package com.mapbox.mapboxsdk.snapshotter; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/package-info.java new file mode 100644 index 000000000..ee2b96fa6 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/package-info.java @@ -0,0 +1,4 @@ +/** + * Contains the Mapbox Maps Android Expression API classes. + */ +package com.mapbox.mapboxsdk.style.expressions; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/CameraFunction.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/CameraFunction.java index bb87fe8a3..74c68cf57 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/CameraFunction.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/CameraFunction.java @@ -11,7 +11,7 @@ import com.mapbox.mapboxsdk.style.functions.stops.Stops; /** * Camera function. Functions that take camera properties as input (zoom for now) *

- * Zoom functions allow the appearance of a map feature to change with map’s zoom level. + * Zoom functions allow the appearance of a map feature to change with map's zoom level. * Zoom functions can be used to create the illusion of depth and control data density. * Each stop is an array with two elements: the first is a zoom level and the second is * a function output value. diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/Function.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/Function.java index e7bb52ebb..80ff4198f 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/Function.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/Function.java @@ -32,7 +32,7 @@ public class Function { /** * Create an exponential {@link CameraFunction} *

- * Zoom functions allow the appearance of a map feature to change with map’s zoom. + * Zoom functions allow the appearance of a map feature to change with map's zoom. * Zoom functions can be used to create the illusion of depth and control data density. * Each stop is an array with two elements, the first is a zoom and the second is a function output value. * @@ -53,7 +53,7 @@ public class Function { /** * Create an interval {@link CameraFunction} *

- * Zoom functions allow the appearance of a map feature to change with map’s zoom. + * Zoom functions allow the appearance of a map feature to change with map’s zoom. * Zoom functions can be used to create the illusion of depth and control data density. * Each stop is an array with two elements, the first is a zoom and the second is a function output value. * diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java index 8f23e7d01..7df48001c 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java @@ -48,7 +48,7 @@ public class Light { } /** - * Set the Position property. Position of the light source relative to lit (extruded) geometries, in [r radial coordinate, a azimuthal angle, p polar angle] where r indicates the distance from the center of the base of an object to its light, a indicates the position of the light relative to 0° (0° when `light.anchor` is set to `viewport` corresponds to the top of the viewport, or 0° when `light.anchor` is set to `map` corresponds to due north, and degrees proceed clockwise), and p indicates the height of the light (from 0°, directly above, to 180°, directly below). + * Set the Position property. Position of the light source relative to lit (extruded) geometries, in [r radial coordinate, a azimuthal angle, p polar angle] where r indicates the distance from the center of the base of an object to its light, a indicates the position of the light relative to 0° (0° when `light.anchor` is set to `viewport` corresponds to the top of the viewport, or 0° when `light.anchor` is set to `map` corresponds to due north, and degrees proceed clockwise), and p indicates the height of the light (from 0°, directly above, to 180°, directly below). * * @param position of the light */ @@ -57,7 +57,7 @@ public class Light { } /** - * Get the Position property. Position of the light source relative to lit (extruded) geometries, in [r radial coordinate, a azimuthal angle, p polar angle] where r indicates the distance from the center of the base of an object to its light, a indicates the position of the light relative to 0° (0° when `light.anchor` is set to `viewport` corresponds to the top of the viewport, or 0° when `light.anchor` is set to `map` corresponds to due north, and degrees proceed clockwise), and p indicates the height of the light (from 0°, directly above, to 180°, directly below). + * Get the Position property. Position of the light source relative to lit (extruded) geometries, in [r radial coordinate, a azimuthal angle, p polar angle] where r indicates the distance from the center of the base of an object to its light, a indicates the position of the light relative to 0° (0° when `light.anchor` is set to `viewport` corresponds to the top of the viewport, or 0° when `light.anchor` is set to `map` corresponds to due north, and degrees proceed clockwise), and p indicates the height of the light (from 0°, directly above, to 180°, directly below). * * @return position as Position */ diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Position.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Position.java index cd6218d3e..00f8486a1 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Position.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Position.java @@ -5,11 +5,11 @@ package com.mapbox.mapboxsdk.style.light; *

* The position is constructed out of a radial coordinate, an azimuthal angle and a polar angle. * where the radial coordinate indicates the distance from the center of the base of an object to its light, the - * azimuthal angle indicates the position of the light relative to 0° (0° when + * azimuthal angle indicates the position of the light relative to 0° (0° when * {@link com.mapbox.mapboxsdk.style.layers.Property.ANCHOR} is set to viewport corresponds to the top of the - * viewport, or 0° when {@link com.mapbox.mapboxsdk.style.layers.Property.ANCHOR} is set to map corresponds to due + * viewport, or 0° when {@link com.mapbox.mapboxsdk.style.layers.Property.ANCHOR} is set to map corresponds to due * north, and degrees proceed clockwise), and polar indicates the height of the light - * (from 0°, directly above, to 180°, directly below). + * (from 0°, directly above, to 180°, directly below). */ public class Position { @@ -21,7 +21,7 @@ public class Position { * Creates a Position from a radial coordinate, an azimuthal angle and a polar angle. * * @param radialCoordinate the distance from the center of the base of an object to its light - * @param azimuthalAngle the position of the light relative to 0° + * @param azimuthalAngle the position of the light relative to 0° * @param polarAngle the height of the light */ public Position(float radialCoordinate, float azimuthalAngle, float polarAngle) { diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/package-info.java new file mode 100644 index 000000000..52c7014bc --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/package-info.java @@ -0,0 +1,4 @@ +/** + * Contains the Mapbox Maps Android Text API classes. + */ +package com.mapbox.mapboxsdk.text; diff --git a/platform/android/scripts/generate-style-code.js b/platform/android/scripts/generate-style-code.js index 6e6d3cfa6..8a6648cc7 100755 --- a/platform/android/scripts/generate-style-code.js +++ b/platform/android/scripts/generate-style-code.js @@ -13,6 +13,7 @@ const lightProperties = Object.keys(spec[`light`]).reduce((memo, name) => { var property = spec[`light`][name]; property.name = name; property['light-property'] = true; + property.doc = property.doc.replace(/°/g,'°'); memo.push(property); return memo; }, []); From b79a1cc82e21bb8249d871691d6a4d250cd6a97c Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Wed, 21 Feb 2018 15:48:51 -0800 Subject: [PATCH 066/264] [core] Don't reuse heatmap render texture on viewport resize. Fixes issue #11228. --- src/mbgl/renderer/layers/render_heatmap_layer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mbgl/renderer/layers/render_heatmap_layer.cpp b/src/mbgl/renderer/layers/render_heatmap_layer.cpp index 0f9e3239e..4f2e89922 100644 --- a/src/mbgl/renderer/layers/render_heatmap_layer.cpp +++ b/src/mbgl/renderer/layers/render_heatmap_layer.cpp @@ -67,7 +67,7 @@ void RenderHeatmapLayer::render(PaintParameters& parameters, RenderSource*) { } } - if (!renderTexture) { + if (!parameters.context.supportsHalfFloatTextures || !renderTexture) { renderTexture = OffscreenTexture(parameters.context, size, gl::TextureType::UnsignedByte); renderTexture->bind(); } From dfeb5b25d3b78b238253ff2866aaab207fd5daa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Wed, 21 Feb 2018 12:35:44 +0100 Subject: [PATCH 067/264] [core] continue loading style even if we mutate it When we load a stale style from cache, and the user immediately starts mutating it, we should continue loading the style so that we'll get a fresh copy of the data into our cache and avoid perpetually showing the stale style. --- src/mbgl/style/style_impl.cpp | 5 ----- test/map/map.test.cpp | 21 ++++++++++++++++----- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/mbgl/style/style_impl.cpp b/src/mbgl/style/style_impl.cpp index d330b3120..0c7f92491 100644 --- a/src/mbgl/style/style_impl.cpp +++ b/src/mbgl/style/style_impl.cpp @@ -55,11 +55,6 @@ void Style::Impl::loadURL(const std::string& url_) { url = url_; styleRequest = fileSource.request(Resource::style(url), [this](Response res) { - // Once we get a fresh style, or the style is mutated, stop revalidating. - if (res.isFresh() || mutated) { - styleRequest.reset(); - } - // Don't allow a loaded, mutated style to be overwritten with a new version. if (mutated && loaded) { return; diff --git a/test/map/map.test.cpp b/test/map/map.test.cpp index 9b34ea89b..30c076ad8 100644 --- a/test/map/map.test.cpp +++ b/test/map/map.test.cpp @@ -252,7 +252,7 @@ TEST(Map, DoubleStyleLoad) { } TEST(Map, StyleFresh) { - // The map should not revalidate fresh styles. + // The map should continue to revalidate fresh styles. MapTest test; @@ -264,11 +264,11 @@ TEST(Map, StyleFresh) { response.expires = Timestamp::max(); test.fileSource.respond(Resource::Style, response); - EXPECT_EQ(0u, test.fileSource.requests.size()); + EXPECT_EQ(1u, test.fileSource.requests.size()); } TEST(Map, StyleExpired) { - // The map should allow expired styles to be revalidated, so long as no mutations are made. + // The map should allow expired styles to be revalidated until we get a fresh style. using namespace std::chrono_literals; @@ -284,11 +284,22 @@ TEST(Map, StyleExpired) { test.fileSource.respond(Resource::Style, response); EXPECT_EQ(1u, test.fileSource.requests.size()); + // Mutate layer. From now on, sending a response to the style won't overwrite it anymore, but + // we should continue to wait for a fresh response. test.map.getStyle().addLayer(std::make_unique("bg")); EXPECT_EQ(1u, test.fileSource.requests.size()); + // Send another expired response, and confirm that we didn't overwrite the style, but continue + // to wait for a fresh response. test.fileSource.respond(Resource::Style, response); - EXPECT_EQ(0u, test.fileSource.requests.size()); + EXPECT_EQ(1u, test.fileSource.requests.size()); + EXPECT_NE(nullptr, test.map.getStyle().getLayer("bg")); + + // Send a fresh response, and confirm that we didn't overwrite the style, but continue to wait + // for a fresh response. + response.expires = util::now() + 1h; + test.fileSource.respond(Resource::Style, response); + EXPECT_EQ(1u, test.fileSource.requests.size()); EXPECT_NE(nullptr, test.map.getStyle().getLayer("bg")); } @@ -352,7 +363,7 @@ TEST(Map, StyleEarlyMutation) { response.data = std::make_shared(util::read_file("test/fixtures/api/water.json")); test.fileSource.respond(Resource::Style, response); - EXPECT_EQ(0u, test.fileSource.requests.size()); + EXPECT_EQ(1u, test.fileSource.requests.size()); EXPECT_NE(nullptr, test.map.getStyle().getLayer("water")); } From 8e1fc8ff384cc7dd19707ba5168603c171d14571 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Fri, 23 Feb 2018 16:24:29 -0800 Subject: [PATCH 068/264] [core, node] Hold on to map handle during NodeMap::request. Avoids a potential crash if garbage collection happens in the middle of a call to NodeMap::request from a map that's eligible for GC. Fixes issue #11281 --- platform/node/src/node_map.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp index ac14df022..0fe69e8ac 100644 --- a/platform/node/src/node_map.cpp +++ b/platform/node/src/node_map.cpp @@ -1154,6 +1154,10 @@ NodeMap::~NodeMap() { std::unique_ptr NodeMap::request(const mbgl::Resource& resource, mbgl::FileSource::Callback callback_) { Nan::HandleScope scope; + // Because this method may be called while this NodeMap is already eligible for garbage collection, + // we need to explicitly hold onto our own handle here so that GC during a v8 call doesn't destroy + // *this while we're still executing code. + handle(); v8::Local argv[] = { Nan::New(this), From db0f6377fc58b08e8cf9d965aabe5c52d243778c Mon Sep 17 00:00:00 2001 From: Jordan Kiley Date: Fri, 23 Feb 2018 22:19:51 -0800 Subject: [PATCH 069/264] Fixed DDS polygon example (#11286) * [ios] Updated expression * [ios] Removed centering code --- platform/ios/app/MBXViewController.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m index 7c8e9fe57..3d0abf0a2 100644 --- a/platform/ios/app/MBXViewController.m +++ b/platform/ios/app/MBXViewController.m @@ -1429,7 +1429,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { // source, categorical function that sets any feature with a "fill" attribute value of true to red color and anything without to green MGLFillStyleLayer *fillStyleLayer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"fill-layer" source:shapeSource]; - fillStyleLayer.fillColor = [NSExpression expressionWithFormat:@"TERNARY(fill, %@, %@)", [UIColor greenColor], [UIColor redColor]]; + fillStyleLayer.fillColor = [NSExpression expressionWithFormat:@"TERNARY(fill == YES, %@, %@)", [UIColor greenColor], [UIColor redColor]]; // source, identity function that sets any feature with an "opacity" attribute to use that value and anything without to 1.0 fillStyleLayer.fillOpacity = [NSExpression expressionWithFormat:@"TERNARY(opacity != nil, opacity, 1.0)"]; From 727afc4afc3a5ca26a5eca953293fcba7e2f4cbb Mon Sep 17 00:00:00 2001 From: Tobrun Date: Thu, 15 Feb 2018 10:40:23 +0100 Subject: [PATCH 070/264] [android] - remove generics from expression api --- .../style/expressions/Expression.java | 857 +++++++++--------- 1 file changed, 426 insertions(+), 431 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java index 0f76f590c..f336f7330 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java @@ -46,10 +46,8 @@ import java.util.List; * Such composite expressions allows a layer's appearance to be determined by * a combination of the zoom level and individual feature properties. *

- * - * @param the type of the expression */ -public class Expression { +public class Expression { private final String operator; private final Expression[] arguments; @@ -68,161 +66,19 @@ public class Expression { * @param operator the expression operator * @param arguments expressions input */ - @SafeVarargs public Expression(@NonNull String operator, @Nullable Expression... arguments) { this.operator = operator; this.arguments = arguments; } - /** - * Converts the expression to Object array representation. - *

- * The output will later be converted to a JSON Object array. - *

- * - * @return the converted object array expression - */ - @NonNull - public Object[] toArray() { - List array = new ArrayList<>(); - array.add(operator); - if (arguments != null) { - for (Expression argument : arguments) { - if (argument instanceof Expression.ExpressionLiteral) { - array.add(toValue((ExpressionLiteral) argument)); - } else { - array.add(argument.toArray()); - } - } - } - return array.toArray(); - } - - /** - * Converts the expression value to an Object. - * - * @param expressionValue the expression value to convert - * @return the converted object expression - */ - private Object toValue(ExpressionLiteral expressionValue) { - Object value = expressionValue.toValue(); - if (value instanceof Expression.Color) { - return ((Expression.Color) value).convertColor(); - } else if (value instanceof Expression.ExpressionLiteral) { - return toValue((ExpressionLiteral) value); - } else if (value instanceof Expression) { - return ((Expression) value).toArray(); - } - return value; - } - - /** - * ExpressionLiteral wraps an object to be used as a literal in an expression. - *

- * ExpressionLiteral is created with {@link #literal(Number)}, {@link #literal(boolean)}, - * {@link #literal(String)} and {@link #literal(Object)}. - *

- * - * @param - */ - private static class ExpressionLiteral extends Expression { - - protected T object; - - /** - * Create an ExpressionValue wrapper. - * - * @param object the object to be wrapped - */ - ExpressionLiteral(@NonNull T object) { - this.object = object; - } - - /** - * Get the wrapped object. - * - * @return the wrapped object - */ - Object toValue() { - return object; - } - } - - // - // Types - // - - /** - * Expression interpolator type. - *

- * Is used for first parameter of {@link #interpolate(Expression, Expression, Stop...)}. - *

- */ - public static class Interpolator { - } - - /** - * Expression color type. - */ - public static class Color { - - private int color; - - /** - * Creates a color color type from a color int. - * - * @param color the int color - */ - public Color(@ColorInt int color) { - this.color = color; - } - - /** - * Converts the int color to rgba(d, d, d, d) string representation - * - * @return - */ - public String convertColor() { - return PropertyFactory.colorToRgbaString(color); - } - } - - /** - * Expression array type. - */ - public static class Array { - } - - /** - * Expression stop type. - *

- * Can be used for {@link #stop(Object, Object)} as part of varargs parameter in - * {@link #step(Number, Expression, Stop...)} or {@link #interpolate(Expression, Expression, Stop...)}. - *

- */ - public static class Stop { - - private Object value; - private Object output; - - public Stop(Object value, Object output) { - this.value = value; - this.output = output; - } - } - - // - // Literals - // - /** * Create a literal number expression. * * @param number the number * @return the expression */ - public static Expression literal(@NonNull Number number) { - return new ExpressionLiteral<>(number); + public static Expression literal(@NonNull Number number) { + return new ExpressionLiteral(number); } /** @@ -231,8 +87,8 @@ public class Expression { * @param string the string * @return the expression */ - public static Expression literal(@NonNull String string) { - return new ExpressionLiteral<>(string); + public static Expression literal(@NonNull String string) { + return new ExpressionLiteral(string); } /** @@ -241,32 +97,32 @@ public class Expression { * @param bool the boolean * @return the expression */ - public static Expression literal(boolean bool) { - return new ExpressionLiteral<>(bool); + public static Expression literal(boolean bool) { + return new ExpressionLiteral(bool); } + // + // Types + // + /** * Create a literal object expression. * * @param object the object * @return the expression */ - public static Expression literal(@NonNull Object object) { - return new ExpressionLiteral<>(object); + public static Expression literal(@NonNull Object object) { + return new ExpressionLiteral(object); } - // - // Color - // - /** * Expression literal utility method to convert a color int to an color expression * * @param color the int color * @return the color expression */ - public static Expression color(@ColorInt int color) { - return new ExpressionLiteral<>(new Color(color)); + public static Expression color(@ColorInt int color) { + return new ExpressionLiteral(new Color(color)); } /** @@ -282,9 +138,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression rgb(@NonNull Expression red, @NonNull Expression green, - @NonNull Expression blue) { - return new Expression<>("rgb", red, green, blue); + public static Expression rgb(@NonNull Expression red, @NonNull Expression green, @NonNull Expression blue) { + return new Expression("rgb", red, green, blue); } /** @@ -300,10 +155,14 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression rgb(@NonNull Number red, @NonNull Number green, @NonNull Number blue) { + public static Expression rgb(@NonNull Number red, @NonNull Number green, @NonNull Number blue) { return rgb(literal(red), literal(green), literal(blue)); } + // + // Literals + // + /** * Creates a color value from red, green, blue components, which must range between 0 and 255, * and an alpha component which must range between 0 and 1. @@ -318,9 +177,9 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression rgba(@NonNull Expression red, @NonNull Expression green, - @NonNull Expression blue, @NonNull Expression alpha) { - return new Expression<>("rgba", red, green, blue, alpha); + public static Expression rgba(@NonNull Expression red, @NonNull Expression green, + @NonNull Expression blue, @NonNull Expression alpha) { + return new Expression("rgba", red, green, blue, alpha); } /** @@ -337,7 +196,7 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression rgba(@NonNull Number red, @NonNull Number green, @NonNull Number blue, @NonNull Number alpha) { + public static Expression rgba(@NonNull Number red, @NonNull Number green, @NonNull Number blue, @NonNull Number alpha) { return rgba(literal(red), literal(green), literal(blue), literal(alpha)); } @@ -348,14 +207,10 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression toRgba(@NonNull Expression expression) { - return new Expression<>("to-rgba", expression); + public static Expression toRgba(@NonNull Expression expression) { + return new Expression("to-rgba", expression); } - // - // Decision - // - /** * Returns true if the input values are equal, false otherwise. * The inputs must be numbers, strings, or booleans, and both of the same type. @@ -365,10 +220,14 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression eq(@NonNull Expression compareOne, @NonNull Expression compareTwo) { - return new Expression<>("==", compareOne, compareTwo); + public static Expression eq(@NonNull Expression compareOne, @NonNull Expression compareTwo) { + return new Expression("==", compareOne, compareTwo); } + // + // Color + // + /** * Returns true if the input values are equal, false otherwise. * @@ -377,7 +236,7 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression eq(boolean compareOne, boolean compareTwo) { + public static Expression eq(boolean compareOne, boolean compareTwo) { return eq(literal(compareOne), literal(compareTwo)); } @@ -389,7 +248,7 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression eq(@NonNull String compareOne, @NonNull String compareTwo) { + public static Expression eq(@NonNull String compareOne, @NonNull String compareTwo) { return eq(literal(compareOne), literal(compareTwo)); } @@ -401,7 +260,7 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression eq(@NonNull Number compareOne, @NonNull Number compareTwo) { + public static Expression eq(@NonNull Number compareOne, @NonNull Number compareTwo) { return eq(literal(compareOne), literal(compareTwo)); } @@ -414,8 +273,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression neq(@NonNull Expression compareOne, @NonNull Expression compareTwo) { - return new Expression<>("!=", compareOne, compareTwo); + public static Expression neq(@NonNull Expression compareOne, @NonNull Expression compareTwo) { + return new Expression("!=", compareOne, compareTwo); } /** @@ -426,8 +285,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression neq(boolean compareOne, boolean compareTwo) { - return new Expression<>("!=", literal(compareOne), literal(compareTwo)); + public static Expression neq(boolean compareOne, boolean compareTwo) { + return new Expression("!=", literal(compareOne), literal(compareTwo)); } /** @@ -438,10 +297,14 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression neq(@NonNull String compareOne, @NonNull String compareTwo) { - return new Expression<>("!=", literal(compareOne), literal(compareTwo)); + public static Expression neq(@NonNull String compareOne, @NonNull String compareTwo) { + return new Expression("!=", literal(compareOne), literal(compareTwo)); } + // + // Decision + // + /** * Returns `true` if the input values are not equal, `false` otherwise. * @@ -450,8 +313,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression neq(@NonNull Number compareOne, @NonNull Number compareTwo) { - return new Expression<>("!=", literal(compareOne), literal(compareTwo)); + public static Expression neq(@NonNull Number compareOne, @NonNull Number compareTwo) { + return new Expression("!=", literal(compareOne), literal(compareTwo)); } /** @@ -463,8 +326,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression gt(@NonNull Expression compareOne, @NonNull Expression compareTwo) { - return new Expression<>(">", compareOne, compareTwo); + public static Expression gt(@NonNull Expression compareOne, @NonNull Expression compareTwo) { + return new Expression(">", compareOne, compareTwo); } /** @@ -475,8 +338,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression gt(@NonNull Number compareOne, @NonNull Number compareTwo) { - return new Expression<>(">", literal(compareOne), literal(compareTwo)); + public static Expression gt(@NonNull Number compareOne, @NonNull Number compareTwo) { + return new Expression(">", literal(compareOne), literal(compareTwo)); } /** @@ -487,8 +350,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression gt(@NonNull String compareOne, @NonNull String compareTwo) { - return new Expression<>(">", literal(compareOne), literal(compareTwo)); + public static Expression gt(@NonNull String compareOne, @NonNull String compareTwo) { + return new Expression(">", literal(compareOne), literal(compareTwo)); } /** @@ -500,8 +363,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression lt(@NonNull Expression compareOne, @NonNull Expression compareTwo) { - return new Expression<>("<", compareOne, compareTwo); + public static Expression lt(@NonNull Expression compareOne, @NonNull Expression compareTwo) { + return new Expression("<", compareOne, compareTwo); } /** @@ -512,8 +375,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression lt(@NonNull Number compareOne, @NonNull Number compareTwo) { - return new Expression<>("<", literal(compareOne), literal(compareTwo)); + public static Expression lt(@NonNull Number compareOne, @NonNull Number compareTwo) { + return new Expression("<", literal(compareOne), literal(compareTwo)); } /** @@ -524,8 +387,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression lt(@NonNull String compareOne, @NonNull String compareTwo) { - return new Expression<>("<", literal(compareOne), literal(compareTwo)); + public static Expression lt(@NonNull String compareOne, @NonNull String compareTwo) { + return new Expression("<", literal(compareOne), literal(compareTwo)); } /** @@ -537,8 +400,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression gte(@NonNull Expression compareOne, @NonNull Expression compareTwo) { - return new Expression<>(">=", compareOne, compareTwo); + public static Expression gte(@NonNull Expression compareOne, @NonNull Expression compareTwo) { + return new Expression(">=", compareOne, compareTwo); } /** @@ -549,8 +412,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression gte(@NonNull Number compareOne, @NonNull Number compareTwo) { - return new Expression<>(">=", literal(compareOne), literal(compareTwo)); + public static Expression gte(@NonNull Number compareOne, @NonNull Number compareTwo) { + return new Expression(">=", literal(compareOne), literal(compareTwo)); } /** @@ -561,8 +424,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression gte(@NonNull String compareOne, @NonNull String compareTwo) { - return new Expression<>(">=", literal(compareOne), literal(compareTwo)); + public static Expression gte(@NonNull String compareOne, @NonNull String compareTwo) { + return new Expression(">=", literal(compareOne), literal(compareTwo)); } /** @@ -574,8 +437,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression lte(@NonNull Expression compareOne, @NonNull Expression compareTwo) { - return new Expression<>("<=", compareOne, compareTwo); + public static Expression lte(@NonNull Expression compareOne, @NonNull Expression compareTwo) { + return new Expression("<=", compareOne, compareTwo); } /** @@ -586,8 +449,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression lte(@NonNull Number compareOne, @NonNull Number compareTwo) { - return new Expression<>("<=", literal(compareOne), literal(compareTwo)); + public static Expression lte(@NonNull Number compareOne, @NonNull Number compareTwo) { + return new Expression("<=", literal(compareOne), literal(compareTwo)); } /** @@ -598,8 +461,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression lte(@NonNull String compareOne, @NonNull String compareTwo) { - return new Expression<>("<=", literal(compareOne), literal(compareTwo)); + public static Expression lte(@NonNull String compareOne, @NonNull String compareTwo) { + return new Expression("<=", literal(compareOne), literal(compareTwo)); } /** @@ -614,9 +477,9 @@ public class Expression { * @return expression * @see Style specification */ - @SafeVarargs - public static Expression all(@NonNull Expression... input) { - return new Expression<>("all", input); + + public static Expression all(@NonNull Expression... input) { + return new Expression("all", input); } /** @@ -631,9 +494,9 @@ public class Expression { * @return expression * @see Style specification */ - @SafeVarargs - public static Expression any(@NonNull Expression... input) { - return new Expression<>("any", input); + + public static Expression any(@NonNull Expression... input) { + return new Expression("any", input); } /** @@ -643,8 +506,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression not(@NonNull Expression input) { - return new Expression<>("!", input); + public static Expression not(@NonNull Expression input) { + return new Expression("!", input); } /** @@ -654,7 +517,7 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression not(boolean input) { + public static Expression not(boolean input) { return not(literal(input)); } @@ -669,7 +532,7 @@ public class Expression { * @return expression * @see Style specification */ - @SafeVarargs + public static Expression switchCase(@NonNull @Size(min = 1) Expression... input) { return new Expression("case", input); } @@ -716,10 +579,6 @@ public class Expression { return new Expression("coalesce", input); } - // - // FeatureData - // - /** * Gets the feature properties object. *

@@ -729,8 +588,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression properties() { - return new Expression<>("properties"); + public static Expression properties() { + return new Expression("properties"); } /** @@ -739,8 +598,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression geometryType() { - return new Expression<>("geometry-type"); + public static Expression geometryType() { + return new Expression("geometry-type"); } /** @@ -749,14 +608,10 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression id() { - return new Expression<>("id"); + public static Expression id() { + return new Expression("id"); } - // - // Heatmap - // - /** * Gets the kernel density estimation of a pixel in a heatmap layer, * which is a relative measure of how many data points are crowded around a particular pixel. @@ -765,24 +620,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression heatmapDensity() { - return new Expression<>("heatmap-density"); - } - - // - // Lookup - // - - /** - * Retrieves an item from an array. - * - * @param number the index expression - * @param expression the array expression - * @return expression - * @see Style specification - */ - public static Expression at(@NonNull Expression number, @NonNull Expression expression) { - return new Expression<>("at", number, expression); + public static Expression heatmapDensity() { + return new Expression("heatmap-density"); } /** @@ -793,7 +632,19 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression at(@NonNull Number number, @NonNull Expression expression) { + public static Expression at(@NonNull Expression number, @NonNull Expression expression) { + return new Expression("at", number, expression); + } + + /** + * Retrieves an item from an array. + * + * @param number the index expression + * @param expression the array expression + * @return expression + * @see Style specification + */ + public static Expression at(@NonNull Number number, @NonNull Expression expression) { return at(literal(number), expression); } @@ -806,10 +657,14 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression get(@NonNull Expression input) { - return new Expression<>("get", input); + public static Expression get(@NonNull Expression input) { + return new Expression("get", input); } + // + // FeatureData + // + /** * Retrieves a property value from the current feature's properties, * or from another object if a second argument is provided. @@ -832,8 +687,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression get(@NonNull Expression key, @NonNull Expression object) { - return new Expression<>("get", key, object); + public static Expression get(@NonNull Expression key, @NonNull Expression object) { + return new Expression("get", key, object); } /** @@ -845,10 +700,14 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression get(@NonNull String key, @NonNull Expression object) { + public static Expression get(@NonNull String key, @NonNull Expression object) { return get(literal(key), object); } + // + // Heatmap + // + /** * Tests for the presence of an property value in the current feature's properties. * @@ -856,10 +715,14 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression has(@NonNull Expression key) { - return new Expression<>("has", key); + public static Expression has(@NonNull Expression key) { + return new Expression("has", key); } + // + // Lookup + // + /** * Tests for the presence of an property value in the current feature's properties. * @@ -867,7 +730,7 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression has(@NonNull String key) { + public static Expression has(@NonNull String key) { return has(literal(key)); } @@ -879,8 +742,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression has(@NonNull Expression key, @NonNull Expression object) { - return new Expression<>("has", key, object); + public static Expression has(@NonNull Expression key, @NonNull Expression object) { + return new Expression("has", key, object); } /** @@ -891,7 +754,7 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression has(@NonNull String key, @NonNull Expression object) { + public static Expression has(@NonNull String key, @NonNull Expression object) { return has(literal(key), object); } @@ -902,8 +765,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression length(@NonNull Expression expression) { - return new Expression<>("length", expression); + public static Expression length(@NonNull Expression expression) { + return new Expression("length", expression); } /** @@ -913,22 +776,18 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression length(@NonNull String input) { + public static Expression length(@NonNull String input) { return length(literal(input)); } - // - // Math - // - /** * Returns mathematical constant ln(2). * * @return expression * @see Style specification */ - public static Expression ln2() { - return new Expression<>("ln2"); + public static Expression ln2() { + return new Expression("ln2"); } /** @@ -937,8 +796,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression pi() { - return new Expression<>("pi"); + public static Expression pi() { + return new Expression("pi"); } /** @@ -947,8 +806,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression e() { - return new Expression<>("e"); + public static Expression e() { + return new Expression("e"); } /** @@ -958,9 +817,9 @@ public class Expression { * @return expression * @see Style specification */ - @SafeVarargs - public static Expression sum(@Size(min = 2) Expression... numbers) { - return new Expression<>("+", numbers); + + public static Expression sum(@Size(min = 2) Expression... numbers) { + return new Expression("+", numbers); } /** @@ -970,9 +829,9 @@ public class Expression { * @return expression * @see Style specification */ - @SuppressWarnings("unchecked") - public static Expression sum(@Size(min = 2) Number... numbers) { - Expression[] numberExpression = (Expression[]) new Expression[numbers.length]; + + public static Expression sum(@Size(min = 2) Number... numbers) { + Expression[] numberExpression = new Expression[numbers.length]; for (int i = 0; i < numbers.length; i++) { numberExpression[i] = literal(numbers[i]); } @@ -986,9 +845,9 @@ public class Expression { * @return expression * @see Style specification */ - @SafeVarargs - public static Expression product(@Size(min = 2) Expression... numbers) { - return new Expression<>("*", numbers); + + public static Expression product(@Size(min = 2) Expression... numbers) { + return new Expression("*", numbers); } /** @@ -998,15 +857,19 @@ public class Expression { * @return expression * @see Style specification */ - @SuppressWarnings("unchecked") - public static Expression product(@Size(min = 2) Number... numbers) { - Expression[] numberExpression = (Expression[]) new Expression[numbers.length]; + + public static Expression product(@Size(min = 2) Number... numbers) { + Expression[] numberExpression = new Expression[numbers.length]; for (int i = 0; i < numbers.length; i++) { numberExpression[i] = literal(numbers[i]); } return product(numberExpression); } + // + // Math + // + /** * Returns the result of subtracting a number from 0. * @@ -1014,8 +877,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression subtract(@NonNull Expression number) { - return new Expression<>("-", number); + public static Expression subtract(@NonNull Expression number) { + return new Expression("-", number); } /** @@ -1025,7 +888,7 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression subtract(@NonNull Number number) { + public static Expression subtract(@NonNull Number number) { return subtract(literal(number)); } @@ -1037,8 +900,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression subtract(@NonNull Expression first, @NonNull Expression second) { - return new Expression<>("-", first, second); + public static Expression subtract(@NonNull Expression first, @NonNull Expression second) { + return new Expression("-", first, second); } /** @@ -1049,7 +912,7 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression subtract(@NonNull Number first, @NonNull Number second) { + public static Expression subtract(@NonNull Number first, @NonNull Number second) { return subtract(literal(first), literal(second)); } @@ -1061,9 +924,8 @@ public class Expression { * @return expression * @see Style specification */ - @SuppressWarnings("unchecked") - public static Expression division(@NonNull Expression first, @NonNull Expression second) { - return new Expression<>("/", first, second); + public static Expression division(@NonNull Expression first, @NonNull Expression second) { + return new Expression("/", first, second); } /** @@ -1074,8 +936,7 @@ public class Expression { * @return expression * @see Style specification */ - @SuppressWarnings("unchecked") - public static Expression division(@NonNull Number first, @NonNull Number second) { + public static Expression division(@NonNull Number first, @NonNull Number second) { return division(literal(first), literal(second)); } @@ -1087,8 +948,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression mod(@NonNull Expression first, @NonNull Expression second) { - return new Expression<>("%", first, second); + public static Expression mod(@NonNull Expression first, @NonNull Expression second) { + return new Expression("%", first, second); } /** @@ -1099,7 +960,7 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression mod(@NonNull Number first, @NonNull Number second) { + public static Expression mod(@NonNull Number first, @NonNull Number second) { return mod(literal(first), literal(second)); } @@ -1111,8 +972,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression pow(@NonNull Expression first, @NonNull Expression second) { - return new Expression<>("^", first, second); + public static Expression pow(@NonNull Expression first, @NonNull Expression second) { + return new Expression("^", first, second); } /** @@ -1123,7 +984,7 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression pow(@NonNull Number first, @NonNull Number second) { + public static Expression pow(@NonNull Number first, @NonNull Number second) { return pow(literal(first), literal(second)); } @@ -1134,8 +995,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression sqrt(@NonNull Expression number) { - return new Expression<>("sqrt", number); + public static Expression sqrt(@NonNull Expression number) { + return new Expression("sqrt", number); } /** @@ -1145,7 +1006,7 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression sqrt(@NonNull Number number) { + public static Expression sqrt(@NonNull Number number) { return sqrt(literal(number)); } @@ -1156,8 +1017,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression log10(@NonNull Expression number) { - return new Expression<>("log10", number); + public static Expression log10(@NonNull Expression number) { + return new Expression("log10", number); } /** @@ -1167,7 +1028,7 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression log10(@NonNull Number number) { + public static Expression log10(@NonNull Number number) { return log10(literal(number)); } @@ -1178,8 +1039,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression ln(Expression number) { - return new Expression<>("ln", number); + public static Expression ln(Expression number) { + return new Expression("ln", number); } /** @@ -1189,7 +1050,7 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression ln(Number number) { + public static Expression ln(Number number) { return ln(literal(number)); } @@ -1200,8 +1061,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression log2(@NonNull Expression number) { - return new Expression<>("log2", number); + public static Expression log2(@NonNull Expression number) { + return new Expression("log2", number); } /** @@ -1211,7 +1072,7 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression log2(@NonNull Number number) { + public static Expression log2(@NonNull Number number) { return log2(literal(number)); } @@ -1222,8 +1083,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression sin(@NonNull Expression number) { - return new Expression<>("sin", number); + public static Expression sin(@NonNull Expression number) { + return new Expression("sin", number); } /** @@ -1233,7 +1094,7 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression sin(@NonNull Number number) { + public static Expression sin(@NonNull Number number) { return sin(literal(number)); } @@ -1244,8 +1105,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression cos(@NonNull Expression number) { - return new Expression<>("cos", number); + public static Expression cos(@NonNull Expression number) { + return new Expression("cos", number); } /** @@ -1255,8 +1116,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression cos(@NonNull Number number) { - return new Expression<>("cos", literal(number)); + public static Expression cos(@NonNull Number number) { + return new Expression("cos", literal(number)); } /** @@ -1266,8 +1127,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression tan(@NonNull Expression number) { - return new Expression<>("tan", number); + public static Expression tan(@NonNull Expression number) { + return new Expression("tan", number); } /** @@ -1277,8 +1138,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression tan(@NonNull Number number) { - return new Expression<>("tan", literal(number)); + public static Expression tan(@NonNull Number number) { + return new Expression("tan", literal(number)); } /** @@ -1288,8 +1149,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression asin(@NonNull Expression number) { - return new Expression<>("asin", number); + public static Expression asin(@NonNull Expression number) { + return new Expression("asin", number); } /** @@ -1299,7 +1160,7 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression asin(@NonNull Number number) { + public static Expression asin(@NonNull Number number) { return asin(literal(number)); } @@ -1310,8 +1171,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression acos(@NonNull Expression number) { - return new Expression<>("acos", number); + public static Expression acos(@NonNull Expression number) { + return new Expression("acos", number); } /** @@ -1321,7 +1182,7 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression acos(@NonNull Number number) { + public static Expression acos(@NonNull Number number) { return acos(literal(number)); } @@ -1332,7 +1193,7 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression atan(@NonNull Expression number) { + public static Expression atan(@NonNull Expression number) { return new Expression("atan", number); } @@ -1343,7 +1204,7 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression atan(@NonNull Number number) { + public static Expression atan(@NonNull Number number) { return atan(literal(number)); } @@ -1354,9 +1215,9 @@ public class Expression { * @return expression * @see Style specification */ - @SafeVarargs - public static Expression min(@Size(min = 1) Expression... numbers) { - return new Expression<>("min", numbers); + + public static Expression min(@Size(min = 1) Expression... numbers) { + return new Expression("min", numbers); } /** @@ -1366,9 +1227,9 @@ public class Expression { * @return expression * @see Style specification */ - @SuppressWarnings("unchecked") - public static Expression min(@Size(min = 1) Number... numbers) { - Expression[] numberExpression = (Expression[]) new Expression[numbers.length]; + + public static Expression min(@Size(min = 1) Number... numbers) { + Expression[] numberExpression = new Expression[numbers.length]; for (int i = 0; i < numbers.length; i++) { numberExpression[i] = literal(numbers[i]); } @@ -1382,9 +1243,9 @@ public class Expression { * @return expression * @see Style specification */ - @SafeVarargs - public static Expression max(@Size(min = 1) Expression... numbers) { - return new Expression<>("max", numbers); + + public static Expression max(@Size(min = 1) Expression... numbers) { + return new Expression("max", numbers); } /** @@ -1394,19 +1255,15 @@ public class Expression { * @return expression * @see Style specification */ - @SuppressWarnings("unchecked") - public static Expression max(@Size(min = 1) Number... numbers) { - Expression[] numberExpression = (Expression[]) new Expression[numbers.length]; + + public static Expression max(@Size(min = 1) Number... numbers) { + Expression[] numberExpression = new Expression[numbers.length]; for (int i = 0; i < numbers.length; i++) { numberExpression[i] = literal(numbers[i]); } return max(numberExpression); } - // - // String - // - /** * Returns the input string converted to uppercase. *

@@ -1418,8 +1275,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression upcase(@NonNull Expression string) { - return new Expression<>("upcase", string); + public static Expression upcase(@NonNull Expression string) { + return new Expression("upcase", string); } /** @@ -1433,7 +1290,7 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression upcase(@NonNull String string) { + public static Expression upcase(@NonNull String string) { return upcase(literal(string)); } @@ -1448,8 +1305,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression downcase(@NonNull Expression input) { - return new Expression<>("downcase", input); + public static Expression downcase(@NonNull Expression input) { + return new Expression("downcase", input); } /** @@ -1463,7 +1320,7 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression downcase(@NonNull String input) { + public static Expression downcase(@NonNull String input) { return downcase(literal(input)); } @@ -1474,9 +1331,9 @@ public class Expression { * @return expression * @see Style specification */ - @SafeVarargs - public static Expression concat(@NonNull Expression... input) { - return new Expression<>("concat", input); + + public static Expression concat(@NonNull Expression... input) { + return new Expression("concat", input); } /** @@ -1486,19 +1343,15 @@ public class Expression { * @return expression * @see Style specification */ - @SuppressWarnings("unchecked") - public static Expression concat(@NonNull String... input) { - Expression[] stringExpression = (Expression[]) new Expression[input.length]; + + public static Expression concat(@NonNull String... input) { + Expression[] stringExpression = new Expression[input.length]; for (int i = 0; i < input.length; i++) { stringExpression[i] = literal(input[i]); } return concat(stringExpression); } - // - // Types - // - /** * Asserts that the input is an array (optionally with a specific item type and length). * If, when the input expression is evaluated, it is not of the asserted type, @@ -1508,10 +1361,14 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression array(@NonNull Expression input) { - return new Expression<>("array", input); + public static Expression array(@NonNull Expression input) { + return new Expression("array", input); } + // + // String + // + /** * Returns a string describing the type of the given value. * @@ -1519,8 +1376,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression typeOf(@NonNull Expression input) { - return new Expression<>("typeof", input); + public static Expression typeOf(@NonNull Expression input) { + return new Expression("typeof", input); } /** @@ -1532,8 +1389,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression string(@NonNull Expression input) { - return new Expression<>("string", input); + public static Expression string(@NonNull Expression input) { + return new Expression("string", input); } /** @@ -1545,8 +1402,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression number(@NonNull Expression input) { - return new Expression<>("number", input); + public static Expression number(@NonNull Expression input) { + return new Expression("number", input); } /** @@ -1558,8 +1415,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression bool(@NonNull Expression input) { - return new Expression<>("boolean", input); + public static Expression bool(@NonNull Expression input) { + return new Expression("boolean", input); } /** @@ -1569,8 +1426,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression object(@NonNull Expression input) { - return new Expression<>("object", input); + public static Expression object(@NonNull Expression input) { + return new Expression("object", input); } /** @@ -1587,10 +1444,14 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression toString(@NonNull Expression input) { - return new Expression<>("to-string", input); + public static Expression toString(@NonNull Expression input) { + return new Expression("to-string", input); } + // + // Types + // + /** * Converts the input value to a number, if possible. * If the input is null or false, the result is 0. @@ -1603,8 +1464,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression toNumber(@NonNull Expression input) { - return new Expression<>("to-number", input); + public static Expression toNumber(@NonNull Expression input) { + return new Expression("to-number", input); } /** @@ -1615,8 +1476,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression toBool(@NonNull Expression input) { - return new Expression<>("to-boolean", input); + public static Expression toBool(@NonNull Expression input) { + return new Expression("to-boolean", input); } /** @@ -1628,14 +1489,10 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression toColor(@NonNull Expression input) { - return new Expression<>("to-color", input); + public static Expression toColor(@NonNull Expression input) { + return new Expression("to-color", input); } - // - // Variable binding - // - /** * Binds input to named variables, * which can then be referenced in the result expression using {@link #var(String)} or {@link #var(Expression)}. @@ -1644,9 +1501,9 @@ public class Expression { * @return expression * @see Style specification */ - @SafeVarargs + public static Expression let(@Size(min = 1) Expression... input) { - return new Expression<>("let", input); + return new Expression("let", input); } /** @@ -1656,8 +1513,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression var(@NonNull Expression expression) { - return new Expression<>("var", expression); + public static Expression var(@NonNull Expression expression) { + return new Expression("var", expression); } /** @@ -1671,10 +1528,6 @@ public class Expression { return var(literal(variableName)); } - // - // Zoom - // - /** * Gets the current zoom level. *

@@ -1685,18 +1538,14 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression zoom() { - return new Expression<>("zoom"); + public static Expression zoom() { + return new Expression("zoom"); } - // - // Ramps, scales, curves - // - /** * Produces a stop value to be used as part of the step expression. * - * @param stop the stop input + * @param stop the stop input * @param value the stop output * @return the stop */ @@ -1716,7 +1565,7 @@ public class Expression { * @return expression * @see Style specification */ - @SafeVarargs + public static Expression step(@NonNull Number input, @NonNull Expression expression, Expression... stops) { return step(literal(input), expression, stops); } @@ -1733,11 +1582,15 @@ public class Expression { * @return expression * @see Style specification */ - @SafeVarargs - public static Expression step(@NonNull Expression input, @NonNull Expression expression, Expression... stops) { + + public static Expression step(@NonNull Expression input, @NonNull Expression expression, Expression... stops) { return new Expression("step", join(new Expression[] {input, expression}, stops)); } + // + // Variable binding + // + /** * Produces discrete, stepped results by evaluating a piecewise-constant function defined by pairs of * input and output values (\"stops\"). The `input` may be any numeric expression (e.g., `[\"get\", \"population\"]`). @@ -1750,7 +1603,7 @@ public class Expression { * @return expression * @see Style specification */ - @SafeVarargs + public static Expression step(@NonNull Number input, @NonNull Expression expression, Stop... stops) { Expression[] expressions = new Expression[stops.length * 2]; for (int i = 0; i < stops.length; i++) { @@ -1772,8 +1625,8 @@ public class Expression { * @return expression * @see Style specification */ - @SafeVarargs - public static Expression step(@NonNull Expression input, @NonNull Expression expression, Stop... stops) { + + public static Expression step(@NonNull Expression input, @NonNull Expression expression, Stop... stops) { Expression[] expressions = new Expression[stops.length * 2]; for (int i = 0; i < stops.length; i++) { expressions[i * 2] = literal(stops[i].value); @@ -1794,12 +1647,16 @@ public class Expression { * @return expression * @see Style specification */ - @SafeVarargs - public static Expression interpolate(@NonNull Expression interpolation, - @NonNull Expression number, Expression... stops) { + + public static Expression interpolate(@NonNull Interpolator interpolation, + @NonNull Expression number, Expression... stops) { return new Expression("interpolate", join(new Expression[] {interpolation, number}, stops)); } + // + // Zoom + // + /** * Produces continuous, smooth results by interpolating between pairs of input and output values (\"stops\"). * The `input` may be any numeric expression (e.g., `[\"get\", \"population\"]`). @@ -1812,9 +1669,9 @@ public class Expression { * @return expression * @see Style specification */ - @SafeVarargs - public static Expression interpolate(@NonNull Expression interpolation, - @NonNull Expression number, Stop... stops) { + + public static Expression interpolate(@NonNull Interpolator interpolation, + @NonNull Expression number, Stop... stops) { Expression[] expressions = new Expression[stops.length * 2]; for (int i = 0; i < stops.length; i++) { expressions[i * 2] = literal(stops[i].value); @@ -1823,14 +1680,18 @@ public class Expression { return interpolate(interpolation, number, expressions); } + // + // Ramps, scales, curves + // + /** * interpolates linearly between the pair of stops just less than and just greater than the input. * * @return expression * @see Style specification */ - public static Expression linear() { - return new Expression<>("linear"); + public static Interpolator linear() { + return new Interpolator("linear"); } /** @@ -1843,7 +1704,7 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression exponential(@NonNull Number base) { + public static Interpolator exponential(@NonNull Number base) { return exponential(literal(base)); } @@ -1857,8 +1718,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression exponential(@NonNull Expression expression) { - return new Expression<>("exponential", expression); + public static Interpolator exponential(@NonNull Expression expression) { + return new Interpolator("exponential", expression); } /** @@ -1871,9 +1732,9 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression cubicBezier(@NonNull Expression x1, @NonNull Expression y1, - @NonNull Expression x2, @NonNull Expression y2) { - return new Expression<>("cubic-bezier", x1, y1, x2, y2); + public static Interpolator cubicBezier(@NonNull Expression x1, @NonNull Expression y1, + @NonNull Expression x2, @NonNull Expression y2) { + return new Interpolator("cubic-bezier", x1, y1, x2, y2); } /** @@ -1886,8 +1747,8 @@ public class Expression { * @return expression * @see Style specification */ - public static Expression cubicBezier(@NonNull Number x1, @NonNull Number y1, - @NonNull Number x2, @NonNull Number y2) { + public static Interpolator cubicBezier(@NonNull Number x1, @NonNull Number y1, + @NonNull Number x2, @NonNull Number y2) { return cubicBezier(literal(x1), literal(y1), literal(x2), literal(y2)); } @@ -1908,6 +1769,47 @@ public class Expression { return output; } + /** + * Converts the expression to Object array representation. + *

+ * The output will later be converted to a JSON Object array. + *

+ * + * @return the converted object array expression + */ + @NonNull + public Object[] toArray() { + List array = new ArrayList<>(); + array.add(operator); + if (arguments != null) { + for (Expression argument : arguments) { + if (argument instanceof Expression.ExpressionLiteral) { + array.add(toValue((ExpressionLiteral) argument)); + } else { + array.add(argument.toArray()); + } + } + } + return array.toArray(); + } + + /** + * Converts the expression value to an Object. + * + * @param expressionValue the expression value to convert + * @return the converted object expression + */ + private Object toValue(ExpressionLiteral expressionValue) { + Object value = expressionValue.toValue(); + if (value instanceof Expression.Color) { + return ((Expression.Color) value).convertColor(); + } else if (value instanceof Expression.ExpressionLiteral) { + return toValue((ExpressionLiteral) value); + } else if (value instanceof Expression) { + return ((Expression) value).toArray(); + } + return value; + } /** * Returns a string representation of the object that matches the definition set in the style specification. @@ -1919,7 +1821,7 @@ public class Expression { StringBuilder builder = new StringBuilder(); builder.append("[\"").append(operator).append("\""); if (arguments != null) { - for (Expression argument : arguments) { + for (Object argument : arguments) { builder.append(", "); if (argument instanceof ExpressionLiteral) { builder.append(((ExpressionLiteral) argument).toValue()); @@ -1931,4 +1833,97 @@ public class Expression { builder.append("]"); return builder.toString(); } + + /** + * ExpressionLiteral wraps an object to be used as a literal in an expression. + *

+ * ExpressionLiteral is created with {@link #literal(Number)}, {@link #literal(boolean)}, + * {@link #literal(String)} and {@link #literal(Object)}. + *

+ */ + private static class ExpressionLiteral extends Expression { + + protected Object literal; + + /** + * Create an ExpressionValue wrapper. + * + * @param object the object to be wrapped + */ + ExpressionLiteral(@NonNull Object object) { + this.literal = object; + } + + /** + * Get the wrapped object. + * + * @return the wrapped object + */ + Object toValue() { + return literal; + } + } + + /** + * Expression interpolator type. + *

+ * Is used for first parameter of {@link #interpolate(Interpolator, Expression, Stop...)}. + *

+ */ + public static class Interpolator extends Expression { + + Interpolator(@NonNull String operator, @Nullable Expression... arguments) { + super(operator, arguments); + } + } + + /** + * Expression color type. + */ + public static class Color { + + private int color; + + /** + * Creates a color color type from a color int. + * + * @param color the int color + */ + public Color(@ColorInt int color) { + this.color = color; + } + + /** + * Converts the int color to rgba(d, d, d, d) string representation + * + * @return + */ + public String convertColor() { + return PropertyFactory.colorToRgbaString(color); + } + } + + /** + * Expression array type. + */ + public static class Array { + } + + /** + * Expression stop type. + *

+ * Can be used for {@link #stop(Object, Object)} as part of varargs parameter in + * {@link #step(Number, Expression, Stop...)} or {@link #interpolate(Interpolator, Expression, Stop...)}. + *

+ */ + public static class Stop { + + private Object value; + private Object output; + + public Stop(Object value, Object output) { + this.value = value; + this.output = output; + } + } } \ No newline at end of file From 317794746e51ad801c9b53f987fc602acc533cc3 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Mon, 26 Feb 2018 13:22:15 -0800 Subject: [PATCH 071/264] [core,node] Pin 'nan' to ~2.8 to avoid Callback::Call deprecation. Alternative fix to #11288, until we figure out a proper async_hooks implementation. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4ff4b3273..977cd2b09 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ }, "license": "BSD-2-Clause", "dependencies": { - "nan": "^2.6.2", + "nan": "~2.8", "node-pre-gyp": "^0.6.37", "npm-run-all": "^4.0.2" }, From 977e9f118c8f3a8d0b230b5c973530fb07018c48 Mon Sep 17 00:00:00 2001 From: Osana Babayan <32496536+osana@users.noreply.github.com> Date: Tue, 27 Feb 2018 18:18:05 +0100 Subject: [PATCH 072/264] [android] LatLngBounds can go over the antimeridian / date line. --- .../constants/GeometryConstants.java | 14 ++ .../mapboxsdk/geometry/LatLngBounds.java | 112 ++++++++++++--- .../mapboxsdk/geometry/LatLngBoundsTest.java | 131 ++++++++++++++++++ 3 files changed, 236 insertions(+), 21 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeometryConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeometryConstants.java index 1a7544d33..7a17e500c 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeometryConstants.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeometryConstants.java @@ -36,6 +36,20 @@ public class GeometryConstants { */ public static final double MIN_LATITUDE = -90; + /** + * This constant represents the latitude span when representing a geolocation. + * + * @since 6.0.0 + */ + public static final double LATITUDE_SPAN = 180; + + /** + * This constant represents the longitude span when representing a geolocation. + * + * @since 6.0.0 + */ + public static final double LONGITUDE_SPAN = 360; + /** * This constant represents the highest latitude value available to represent a geolocation. * diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java index cf647224a..fc8d2ec8f 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java @@ -29,6 +29,10 @@ public class LatLngBounds implements Parcelable { * Construct a new LatLngBounds based on its corners, given in NESW * order. * + * If eastern longitude is smaller than the western one, bounds will include antimeridian. + * For example, if the NE point is (10, -170) and the SW point is (-10, 170), then bounds will span over 20 degrees + * and cross the antimeridian. + * * @param northLatitude Northern Latitude * @param eastLongitude Eastern Longitude * @param southLatitude Southern Latitude @@ -48,10 +52,9 @@ public class LatLngBounds implements Parcelable { * @return the bounds representing the world */ public static LatLngBounds world() { - return new LatLngBounds.Builder() - .include(new LatLng(GeometryConstants.MAX_LATITUDE, GeometryConstants.MAX_LONGITUDE)) - .include(new LatLng(GeometryConstants.MIN_LATITUDE, GeometryConstants.MIN_LONGITUDE)) - .build(); + return LatLngBounds.from( + GeometryConstants.MAX_LATITUDE, GeometryConstants.MAX_LONGITUDE, + GeometryConstants.MIN_LATITUDE, GeometryConstants.MIN_LONGITUDE); } /** @@ -61,8 +64,21 @@ public class LatLngBounds implements Parcelable { * @return LatLng center of this LatLngBounds */ public LatLng getCenter() { - return new LatLng((this.latitudeNorth + this.latitudeSouth) / 2, - (this.longitudeEast + this.longitudeWest) / 2); + double latCenter = (this.latitudeNorth + this.latitudeSouth) / 2.0; + double longCenter; + + if (this.longitudeEast > this.longitudeWest) { + longCenter = (this.longitudeEast + this.longitudeWest) / 2; + } else { + double halfSpan = (GeometryConstants.LONGITUDE_SPAN + this.longitudeEast - this.longitudeWest) / 2.0; + longCenter = this.longitudeWest + halfSpan; + if (longCenter >= GeometryConstants.MAX_LONGITUDE) { + longCenter = this.longitudeEast - halfSpan; + } + return new LatLng(latCenter, longCenter); + } + + return new LatLng(latCenter, longCenter); } /** @@ -163,10 +179,26 @@ public class LatLngBounds implements Parcelable { * @return Span distance */ public double getLongitudeSpan() { - return Math.abs(this.longitudeEast - this.longitudeWest); + double longSpan = Math.abs(this.longitudeEast - this.longitudeWest); + if (this.longitudeEast > this.longitudeWest) { + return longSpan; + } + + // shortest span contains antimeridian + return GeometryConstants.LONGITUDE_SPAN - longSpan; } + static double getLongitudeSpan(final double longEast, final double longWest) { + double longSpan = Math.abs(longEast - longWest); + if (longEast > longWest) { + return longSpan; + } + + // shortest span contains antimeridian + return GeometryConstants.LONGITUDE_SPAN - longSpan; + } + /** * Validate if LatLngBounds is empty, determined if absolute distance is * @@ -196,21 +228,44 @@ public class LatLngBounds implements Parcelable { */ static LatLngBounds fromLatLngs(final List latLngs) { double minLat = GeometryConstants.MAX_LATITUDE; - double minLon = GeometryConstants.MAX_LONGITUDE; double maxLat = GeometryConstants.MIN_LATITUDE; - double maxLon = GeometryConstants.MIN_LONGITUDE; + + double eastLon = latLngs.get(0).getLongitude(); + double westLon = latLngs.get(1).getLongitude(); + double lonSpan = Math.abs(eastLon - westLon); + if (lonSpan < GeometryConstants.LONGITUDE_SPAN / 2) { + if (eastLon < westLon) { + double temp = eastLon; + eastLon = westLon; + westLon = temp; + } + } else { + lonSpan = GeometryConstants.LONGITUDE_SPAN - lonSpan; + if (westLon < eastLon) { + double temp = eastLon; + eastLon = westLon; + westLon = temp; + } + } for (final ILatLng gp : latLngs) { final double latitude = gp.getLatitude(); - final double longitude = gp.getLongitude(); - minLat = Math.min(minLat, latitude); - minLon = Math.min(minLon, longitude); maxLat = Math.max(maxLat, latitude); - maxLon = Math.max(maxLon, longitude); + + final double longitude = gp.getLongitude(); + if (!containsLongitude(eastLon, westLon, longitude)) { + final double eastSpan = getLongitudeSpan(longitude, westLon); + final double westSpan = getLongitudeSpan(eastLon, longitude); + if (eastSpan <= westSpan) { + eastLon = longitude; + } else { + westLon = longitude; + } + } } - return new LatLngBounds(maxLat, maxLon, minLat, minLon); + return new LatLngBounds(maxLat, eastLon, minLat, westLon); } /** @@ -322,6 +377,24 @@ public class LatLngBounds implements Parcelable { return false; } + + private boolean containsLatitude(final double latitude) { + return (latitude <= this.latitudeNorth) + && (latitude >= this.latitudeSouth); + } + + private boolean containsLongitude(final double longitude) { + return containsLongitude(this.longitudeEast, this.longitudeWest, longitude); + } + + static boolean containsLongitude(final double eastLon, final double westLon, final double longitude) { + if (eastLon > westLon) { + return (longitude <= eastLon) + && (longitude >= westLon); + } + return (longitude < eastLon) || (longitude > westLon); + } + /** * Determines whether this LatLngBounds contains a point. * @@ -329,12 +402,8 @@ public class LatLngBounds implements Parcelable { * @return true, if the point is contained within the bounds */ public boolean contains(final ILatLng latLng) { - final double latitude = latLng.getLatitude(); - final double longitude = latLng.getLongitude(); - return ((latitude <= this.latitudeNorth) - && (latitude >= this.latitudeSouth)) - && ((longitude <= this.longitudeEast) - && (longitude >= this.longitudeWest)); + return containsLatitude(latLng.getLatitude()) + && containsLongitude(latLng.getLongitude()); } /** @@ -344,7 +413,8 @@ public class LatLngBounds implements Parcelable { * @return true, if the bounds is contained within the bounds */ public boolean contains(final LatLngBounds other) { - return contains(other.getNorthEast()) && contains(other.getSouthWest()); + return contains(other.getNorthEast()) + && contains(other.getSouthWest()); } /** diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java index e6c1fdd0c..f03bbdb11 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java @@ -70,6 +70,82 @@ public class LatLngBoundsTest { assertEquals("LatLngSpan should be the same", new LatLngSpan(2, 2), latLngSpan); } + @Test + public void dateLineSpanBuilder1() { + latLngBounds = new LatLngBounds.Builder() + .include(new LatLng(10, -170)) + .include(new LatLng(-10, 170)) + .build(); + + LatLngSpan latLngSpan = latLngBounds.getSpan(); + assertEquals("LatLngSpan should be shortest distance", new LatLngSpan(20, 20), + latLngSpan); + } + + @Test + public void dateLineSpanBuilder2() { + latLngBounds = new LatLngBounds.Builder() + .include(new LatLng(-10, -170)) + .include(new LatLng(10, 170)) + .build(); + + LatLngSpan latLngSpan = latLngBounds.getSpan(); + assertEquals("LatLngSpan should be shortest distance", new LatLngSpan(20, 20), + latLngSpan); + } + + @Test + public void dateLineSpanFrom1() { + latLngBounds = LatLngBounds.from(10, -170, -10, 170); + LatLngSpan latLngSpan = latLngBounds.getSpan(); + assertEquals("LatLngSpan should be shortest distance", new LatLngSpan(20, 20), + latLngSpan); + } + + @Test + public void dateLineSpanFrom2() { + latLngBounds = LatLngBounds.from(10, 170, -10, -170); + LatLngSpan latLngSpan = latLngBounds.getSpan(); + assertEquals("LatLngSpan should be shortest distance", new LatLngSpan(20, 340), + latLngSpan); + } + + @Test + public void nearDateLineCenter1() { + latLngBounds = LatLngBounds.from(10, -175, -10, 165); + LatLng center = latLngBounds.getCenter(); + assertEquals("Center should match", new LatLng(0, 175), center); + } + + @Test + public void nearDateLineCenter2() { + latLngBounds = LatLngBounds.from(10, -165, -10, 175); + LatLng center = latLngBounds.getCenter(); + assertEquals("Center should match", new LatLng(0, -175), center); + } + + @Test + public void nearDateLineCenter3() { + latLngBounds = LatLngBounds.from(10, -170, -10, 170); + LatLng center = latLngBounds.getCenter(); + assertEquals("Center should match", new LatLng(0, -180), center); + } + + @Test + public void nearDateLineCenter4() { + latLngBounds = LatLngBounds.from(10, -180, -10, 0); + LatLng center = latLngBounds.getCenter(); + assertEquals("Center should match", new LatLng(0, 90), center); + } + + @Test + public void nearDateLineCenter5() { + latLngBounds = LatLngBounds.from(10, 180, -10, 0); + LatLng center = latLngBounds.getCenter(); + assertEquals("Center should match", new LatLng(0, 90), center); + } + + @Test public void center() { LatLng center = latLngBounds.getCenter(); @@ -120,6 +196,46 @@ public class LatLngBoundsTest { assertEquals("LatLngBounds should match", latLngBounds1, latLngBounds2); } + @Test + public void includesOverDateline1() { + + LatLngBounds latLngBounds = new LatLngBounds.Builder() + .include(new LatLng(10, -170)) + .include(new LatLng(-10, -175)) + .include(new LatLng(0, 170)) + .build(); + + assertEquals("LatLngSpan should be the same", + new LatLngSpan(20, 20), latLngBounds.getSpan()); + } + + @Test + public void includesOverDateline2() { + + LatLngBounds latLngBounds = new LatLngBounds.Builder() + .include(new LatLng(10, 170)) + .include(new LatLng(-10, 175)) + .include(new LatLng(0, -170)) + .build(); + + assertEquals("LatLngSpan should be the same", + new LatLngSpan(20, 20), latLngBounds.getSpan()); + } + + @Test + public void includesOverDateline3() { + + LatLngBounds latLngBounds = new LatLngBounds.Builder() + .include(new LatLng(10, 170)) + .include(new LatLng(-10, -170)) + .include(new LatLng(0, -180)) + .include(new LatLng(5, 180)) + .build(); + + assertEquals("LatLngSpan should be the same", + new LatLngSpan(20, 20), latLngBounds.getSpan()); + } + @Test public void containsNot() { assertFalse("LatLng should not be included", latLngBounds.contains(new LatLng(3, 1))); @@ -130,6 +246,21 @@ public class LatLngBoundsTest { assertTrue("LatLngBounds should be contained in the world", LatLngBounds.world().contains(latLngBounds)); } + @Test + public void worldSpan() { + assertEquals("LatLngBounds world span should be 180, 360", + GeometryConstants.LATITUDE_SPAN, LatLngBounds.world().getLatitudeSpan(), DELTA); + assertEquals("LatLngBounds world span should be 180, 360", + GeometryConstants.LONGITUDE_SPAN, LatLngBounds.world().getLongitudeSpan(), DELTA); + } + + @Test + public void emptySpan() { + LatLngBounds latLngBounds = LatLngBounds.from(GeometryConstants.MIN_LATITUDE, GeometryConstants.MAX_LONGITUDE, + GeometryConstants.MIN_LATITUDE, GeometryConstants.MAX_LONGITUDE); + assertTrue("LatLngBounds empty span", latLngBounds.isEmptySpan()); + } + @Test public void containsBounds() { LatLngBounds inner = new LatLngBounds.Builder() From 43a7db0b0bfd7d626ffed9adff3446fbbe111c15 Mon Sep 17 00:00:00 2001 From: Osana Babayan <32496536+osana@users.noreply.github.com> Date: Tue, 27 Feb 2018 18:27:22 +0100 Subject: [PATCH 073/264] [android] LatLngBounds: latNorth should be greater or equal than latSouth --- .../java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java | 6 ++++++ .../com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java index fc8d2ec8f..df8f764f4 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java @@ -283,6 +283,8 @@ public class LatLngBounds implements Parcelable { * This values of latNorth and latSouth should be in the range of [-90, 90], * see {@link GeometryConstants#MIN_LATITUDE} and {@link GeometryConstants#MAX_LATITUDE}, * otherwise IllegalArgumentException will be thrown. + * latNorth should be greater or equal latSouth, otherwise IllegalArgumentException will be thrown. + * *

* This method doesn't recalculate most east or most west boundaries. * Note that lonEast and lonWest will be wrapped to be in the range of [-180, 180], @@ -312,6 +314,10 @@ public class LatLngBounds implements Parcelable { throw new IllegalArgumentException("latitude must be between -90 and 90"); } + if (latNorth < latSouth) { + throw new IllegalArgumentException("LatSouth cannot be less than latNorth"); + } + lonEast = LatLng.wrap(lonEast, GeometryConstants.MIN_LONGITUDE, GeometryConstants.MAX_LONGITUDE); lonWest = LatLng.wrap(lonWest, GeometryConstants.MIN_LONGITUDE, GeometryConstants.MAX_LONGITUDE); diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java index f03bbdb11..29ffd6bbb 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java @@ -496,4 +496,11 @@ public class LatLngBoundsTest { exception.expectMessage("longitude must not be infinite"); LatLngBounds.from(20, 20, 0, Double.POSITIVE_INFINITY); } + + @Test + public void testConstructorCheckLatSouthGreaterLatNorth() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("LatSouth cannot be less than latNorth"); + LatLngBounds.from(0, 20, 20, 0); + } } From 848d7dff4e682c3d2485366f95649a5de4b1bd75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Tue, 27 Feb 2018 18:35:05 +0100 Subject: [PATCH 074/264] [android ] - new gestures library (cp #11221) (#11332) * [android] new gesture library - added SNAPSHOT dependency * [android] new gesture library - cleaned up redundant classes * [android] new gesture library - limiting scale when rotating * [android] new gesture library - shove gesture filtering * [android] new gesture library - increase rotation threshold when scaling * [android] new gesture library - minimum angular velocity * [android] new gesture library - exposed gestures execution listeners * [android] new gesture library - notifying new listeners tests * [android] new gesture library - removed tracking setting * [android] new gesture library - resetting focal point with every scale/rotate callback * [android] new gesture library - fixed camera change dispatcher callbacks * [android] new gesture library - cancel velocity animations in maps onStop() * [android] new gesture library - extracted telemetry pushes to a method * [android] new gesture library - deprecated onScrollListener * [android] new gesture library - unified shove listener name (cherry picked from commit 7905bd6) --- .../android/MapboxGLAndroidSDK/build.gradle | 1 + .../gesturedetectors/BaseGestureDetector.java | 160 --- .../gesturedetectors/MoveGestureDetector.java | 185 --- .../RotateGestureDetector.java | 180 --- .../ShoveGestureDetector.java | 214 --- .../TwoFingerGestureDetector.java | 224 --- .../gesturedetectors/package-info.java | 4 - .../mapboxsdk/constants/MapboxConstants.java | 29 +- .../mapboxsdk/maps/MapGestureDetector.java | 1280 +++++++++-------- .../mapbox/mapboxsdk/maps/MapKeyListener.java | 8 +- .../com/mapbox/mapboxsdk/maps/MapView.java | 73 +- .../com/mapbox/mapboxsdk/maps/MapboxMap.java | 202 ++- .../com/mapbox/mapboxsdk/maps/Transform.java | 34 +- .../src/main/res/values/dimens.xml | 9 + .../mapboxsdk/maps/MapTouchListenersTest.java | 132 +- .../mapbox/mapboxsdk/maps/MapboxMapTest.java | 2 +- platform/android/gradle/dependencies.gradle | 24 +- 17 files changed, 1072 insertions(+), 1689 deletions(-) delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/BaseGestureDetector.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/MoveGestureDetector.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/RotateGestureDetector.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/ShoveGestureDetector.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/package-info.java diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle index 173b4fa7f..94b816c1a 100644 --- a/platform/android/MapboxGLAndroidSDK/build.gradle +++ b/platform/android/MapboxGLAndroidSDK/build.gradle @@ -3,6 +3,7 @@ apply plugin: 'com.android.library' dependencies { api dependenciesList.mapboxAndroidTelemetry api dependenciesList.mapboxJavaGeoJSON + api dependenciesList.mapboxAndroidGestures implementation dependenciesList.supportAnnotations implementation dependenciesList.supportFragmentV4 implementation dependenciesList.timber diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/BaseGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/BaseGestureDetector.java deleted file mode 100644 index b7bcb925a..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/BaseGestureDetector.java +++ /dev/null @@ -1,160 +0,0 @@ -package com.almeros.android.multitouch.gesturedetectors; - -import android.content.Context; -import android.view.MotionEvent; - -/** - * @author Almer Thie (code.almeros.com) Copyright (c) 2013, Almer Thie - * (code.almeros.com) - *

- * All rights reserved. - *

- * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - *

- * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - *

- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -public abstract class BaseGestureDetector { - protected final Context context; - protected boolean gestureInProgress; - - protected MotionEvent prevEvent; - protected MotionEvent currEvent; - - protected float currPressure; - protected float prevPressure; - protected long timeDelta; - - /** - * This value is the threshold ratio between the previous combined pressure - * and the current combined pressure. When pressure decreases rapidly - * between events the position values can often be imprecise, as it usually - * indicates that the user is in the process of lifting a pointer off of the - * device. This value was tuned experimentally. - */ - protected static final float PRESSURE_THRESHOLD = 0.67f; - - public BaseGestureDetector(Context context) { - this.context = context; - } - - /** - * All gesture detectors need to be called through this method to be able to - * detect gestures. This method delegates work to handler methods - * (handleStartProgressEvent, handleInProgressEvent) implemented in - * extending classes. - * - * @param event MotionEvent - * @return {@code true} as handled - */ - public boolean onTouchEvent(MotionEvent event) { - final int actionCode = event.getAction() & MotionEvent.ACTION_MASK; - if (!gestureInProgress) { - handleStartProgressEvent(actionCode, event); - } else { - handleInProgressEvent(actionCode, event); - } - return true; - } - - /** - * Called when the current event occurred when NO gesture is in progress - * yet. The handling in this implementation may set the gesture in progress - * (via gestureInProgress) or out of progress - * - * @param actionCode Action Code from MotionEvent - * @param event MotionEvent - */ - protected abstract void handleStartProgressEvent(int actionCode, - MotionEvent event); - - /** - * Called when the current event occurred when a gesture IS in progress. The - * handling in this implementation may set the gesture out of progress (via - * gestureInProgress). - * - * @param actionCode Action Code from MotionEvent - * @param event MotionEvent - */ - protected abstract void handleInProgressEvent(int actionCode, - MotionEvent event); - - protected void updateStateByEvent(MotionEvent curr) { - final MotionEvent prev = prevEvent; - - // Reset currEvent - if (currEvent != null) { - currEvent.recycle(); - currEvent = null; - } - currEvent = MotionEvent.obtain(curr); - - // Delta time - timeDelta = curr.getEventTime() - prev.getEventTime(); - - // Pressure - currPressure = curr.getPressure(curr.getActionIndex()); - prevPressure = prev.getPressure(prev.getActionIndex()); - } - - protected void resetState() { - if (prevEvent != null) { - prevEvent.recycle(); - prevEvent = null; - } - if (currEvent != null) { - currEvent.recycle(); - currEvent = null; - } - gestureInProgress = false; - } - - /** - * Returns {@code true} if a gesture is currently in progress. - * - * @return {@code true} if a gesture is currently in progress, {@code false} - * otherwise. - */ - public boolean isInProgress() { - return gestureInProgress; - } - - /** - * Return the time difference in milliseconds between the previous accepted - * GestureDetector event and the current GestureDetector event. - * - * @return Time difference since the last move event in milliseconds. - */ - public long getTimeDelta() { - return timeDelta; - } - - /** - * Return the event time of the current GestureDetector event being - * processed. - * - * @return Current GestureDetector event time in milliseconds. - */ - public long getEventTime() { - return currEvent.getEventTime(); - } - -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/MoveGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/MoveGestureDetector.java deleted file mode 100644 index bc7dda615..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/MoveGestureDetector.java +++ /dev/null @@ -1,185 +0,0 @@ -package com.almeros.android.multitouch.gesturedetectors; - -import android.content.Context; -import android.graphics.PointF; -import android.view.MotionEvent; - -/** - * @author Almer Thie (code.almeros.com) Copyright (c) 2013, Almer Thie - * (code.almeros.com) - *

- * All rights reserved. - *

- * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - *

- * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - *

- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -public class MoveGestureDetector extends BaseGestureDetector { - - /** - * Listener which must be implemented which is used by MoveGestureDetector - * to perform callbacks to any implementing class which is registered to a - * MoveGestureDetector via the constructor. - * - * @see MoveGestureDetector.SimpleOnMoveGestureListener - */ - public interface OnMoveGestureListener { - public boolean onMove(MoveGestureDetector detector); - - public boolean onMoveBegin(MoveGestureDetector detector); - - public void onMoveEnd(MoveGestureDetector detector); - } - - /** - * Helper class which may be extended and where the methods may be - * implemented. This way it is not necessary to implement all methods of - * OnMoveGestureListener. - */ - public static class SimpleOnMoveGestureListener implements - OnMoveGestureListener { - public boolean onMove(MoveGestureDetector detector) { - return false; - } - - public boolean onMoveBegin(MoveGestureDetector detector) { - return true; - } - - public void onMoveEnd(MoveGestureDetector detector) { - // Do nothing, overridden implementation may be used - } - } - - private static final PointF FOCUS_DELTA_ZERO = new PointF(); - - private final OnMoveGestureListener listener; - - private PointF focusExternal = new PointF(); - private PointF focusDeltaExternal = new PointF(); - - public MoveGestureDetector(Context context, OnMoveGestureListener listener) { - super(context); - this.listener = listener; - } - - @Override - protected void handleStartProgressEvent(int actionCode, MotionEvent event) { - switch (actionCode) { - case MotionEvent.ACTION_DOWN: - resetState(); // In case we missed an UP/CANCEL event - - prevEvent = MotionEvent.obtain(event); - timeDelta = 0; - - updateStateByEvent(event); - break; - - case MotionEvent.ACTION_MOVE: - gestureInProgress = listener.onMoveBegin(this); - break; - } - } - - @Override - protected void handleInProgressEvent(int actionCode, MotionEvent event) { - switch (actionCode) { - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - listener.onMoveEnd(this); - resetState(); - break; - - case MotionEvent.ACTION_MOVE: - updateStateByEvent(event); - - // Only accept the event if our relative pressure is within - // a certain limit. This can help filter shaky data as a - // finger is lifted. - if (currPressure / prevPressure > PRESSURE_THRESHOLD) { - final boolean updatePrevious = listener.onMove(this); - if (updatePrevious) { - prevEvent.recycle(); - prevEvent = MotionEvent.obtain(event); - } - } - break; - } - } - - protected void updateStateByEvent(MotionEvent curr) { - super.updateStateByEvent(curr); - - final MotionEvent prev = prevEvent; - - // Focus intenal - PointF currFocusInternal = determineFocalPoint(curr); - PointF prevFocusInternal = determineFocalPoint(prev); - - // Focus external - // - Prevent skipping of focus delta when a finger is added or removed - boolean skipNextMoveEvent = prev.getPointerCount() != curr - .getPointerCount(); - focusDeltaExternal = skipNextMoveEvent ? FOCUS_DELTA_ZERO - : new PointF(currFocusInternal.x - prevFocusInternal.x, - currFocusInternal.y - prevFocusInternal.y); - - // - Don't directly use mFocusInternal (or skipping will occur). Add - // unskipped delta values to focusExternal instead. - focusExternal.x += focusDeltaExternal.x; - focusExternal.y += focusDeltaExternal.y; - } - - /** - * Determine (multi)finger focal point (a.k.a. center point between all - * fingers) - * - * @param motionEvent a {@link MotionEvent} object. - * @return PointF focal point - */ - private PointF determineFocalPoint(MotionEvent motionEvent) { - // Number of fingers on screen - final int pCount = motionEvent.getPointerCount(); - float x = 0.0f; - float y = 0.0f; - - for (int i = 0; i < pCount; i++) { - x += motionEvent.getX(i); - y += motionEvent.getY(i); - } - - return new PointF(x / pCount, y / pCount); - } - - public float getFocusX() { - return focusExternal.x; - } - - public float getFocusY() { - return focusExternal.y; - } - - public PointF getFocusDelta() { - return focusDeltaExternal; - } - -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/RotateGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/RotateGestureDetector.java deleted file mode 100644 index 8c111a68d..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/RotateGestureDetector.java +++ /dev/null @@ -1,180 +0,0 @@ -package com.almeros.android.multitouch.gesturedetectors; - -import android.content.Context; -import android.view.MotionEvent; - -/** - * @author Almer Thie (code.almeros.com) Copyright (c) 2013, Almer Thie - * (code.almeros.com) - *

- * All rights reserved. - *

- * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - *

- * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - *

- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -public class RotateGestureDetector extends TwoFingerGestureDetector { - - /** - * Listener which must be implemented which is used by RotateGestureDetector - * to perform callbacks to any implementing class which is registered to a - * RotateGestureDetector via the constructor. - * - * @see RotateGestureDetector.SimpleOnRotateGestureListener - */ - public interface OnRotateGestureListener { - public boolean onRotate(RotateGestureDetector detector); - - public boolean onRotateBegin(RotateGestureDetector detector); - - public void onRotateEnd(RotateGestureDetector detector); - } - - /** - * Helper class which may be extended and where the methods may be - * implemented. This way it is not necessary to implement all methods of - * OnRotateGestureListener. - */ - public static class SimpleOnRotateGestureListener implements - OnRotateGestureListener { - public boolean onRotate(RotateGestureDetector detector) { - return false; - } - - public boolean onRotateBegin(RotateGestureDetector detector) { - return true; - } - - public void onRotateEnd(RotateGestureDetector detector) { - // Do nothing, overridden implementation may be used - } - } - - private final OnRotateGestureListener listener; - private boolean sloppyGesture; - - public RotateGestureDetector(Context context, - OnRotateGestureListener listener) { - super(context); - this.listener = listener; - } - - @Override - protected void handleStartProgressEvent(int actionCode, MotionEvent event) { - switch (actionCode) { - case MotionEvent.ACTION_POINTER_DOWN: - // At least the second finger is on screen now - - resetState(); // In case we missed an UP/CANCEL event - prevEvent = MotionEvent.obtain(event); - timeDelta = 0; - - updateStateByEvent(event); - - // See if we have a sloppy gesture - sloppyGesture = isSloppyGesture(event); - if (!sloppyGesture) { - // No, start gesture now - gestureInProgress = listener.onRotateBegin(this); - } - break; - - case MotionEvent.ACTION_MOVE: - if (!sloppyGesture) { - break; - } - - // See if we still have a sloppy gesture - sloppyGesture = isSloppyGesture(event); - if (!sloppyGesture) { - // No, start normal gesture now - gestureInProgress = listener.onRotateBegin(this); - } - - break; - - case MotionEvent.ACTION_POINTER_UP: - if (!sloppyGesture) { - break; - } - - break; - } - } - - @Override - protected void handleInProgressEvent(int actionCode, MotionEvent event) { - switch (actionCode) { - case MotionEvent.ACTION_POINTER_UP: - // Gesture ended but - updateStateByEvent(event); - - if (!sloppyGesture) { - listener.onRotateEnd(this); - } - - resetState(); - break; - - case MotionEvent.ACTION_CANCEL: - if (!sloppyGesture) { - listener.onRotateEnd(this); - } - - resetState(); - break; - - case MotionEvent.ACTION_MOVE: - updateStateByEvent(event); - - // Only accept the event if our relative pressure is within - // a certain limit. This can help filter shaky data as a - // finger is lifted. - if (currPressure / prevPressure > PRESSURE_THRESHOLD) { - final boolean updatePrevious = listener.onRotate(this); - if (updatePrevious) { - prevEvent.recycle(); - prevEvent = MotionEvent.obtain(event); - } - } - break; - } - } - - @Override - protected void resetState() { - super.resetState(); - sloppyGesture = false; - } - - /** - * Return the rotation difference from the previous rotate event to the - * current event. - * - * @return The current rotation //difference in degrees. - */ - public float getRotationDegreesDelta() { - double diffRadians = Math.atan2(prevFingerDiffY, prevFingerDiffX) - - Math.atan2(currFingerDiffY, currFingerDiffX); - return (float) (diffRadians * 180.0 / Math.PI); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/ShoveGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/ShoveGestureDetector.java deleted file mode 100644 index 9396578e4..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/ShoveGestureDetector.java +++ /dev/null @@ -1,214 +0,0 @@ -package com.almeros.android.multitouch.gesturedetectors; - -import android.content.Context; -import android.view.MotionEvent; - -/** - * @author Robert Nordan (robert.nordan@norkart.no) - *

- * Copyright (c) 2013, Norkart AS - *

- * All rights reserved. - *

- * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - *

- * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - *

- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -public class ShoveGestureDetector extends TwoFingerGestureDetector { - - /** - * Listener which must be implemented which is used by ShoveGestureDetector - * to perform callbacks to any implementing class which is registered to a - * ShoveGestureDetector via the constructor. - * - * @see ShoveGestureDetector.SimpleOnShoveGestureListener - */ - public interface OnShoveGestureListener { - public boolean onShove(ShoveGestureDetector detector); - - public boolean onShoveBegin(ShoveGestureDetector detector); - - public void onShoveEnd(ShoveGestureDetector detector); - } - - /** - * Helper class which may be extended and where the methods may be - * implemented. This way it is not necessary to implement all methods of - * OnShoveGestureListener. - */ - public static class SimpleOnShoveGestureListener implements - OnShoveGestureListener { - public boolean onShove(ShoveGestureDetector detector) { - return false; - } - - public boolean onShoveBegin(ShoveGestureDetector detector) { - return true; - } - - public void onShoveEnd(ShoveGestureDetector detector) { - // Do nothing, overridden implementation may be used - } - } - - private float prevAverageY; - private float currAverageY; - - private final OnShoveGestureListener listener; - private boolean sloppyGesture; - - public ShoveGestureDetector(Context context, OnShoveGestureListener listener) { - super(context); - this.listener = listener; - } - - @Override - protected void handleStartProgressEvent(int actionCode, MotionEvent event) { - switch (actionCode) { - case MotionEvent.ACTION_POINTER_DOWN: - // At least the second finger is on screen now - - resetState(); // In case we missed an UP/CANCEL event - prevEvent = MotionEvent.obtain(event); - timeDelta = 0; - - updateStateByEvent(event); - - // See if we have a sloppy gesture - sloppyGesture = isSloppyGesture(event); - if (!sloppyGesture) { - // No, start gesture now - gestureInProgress = listener.onShoveBegin(this); - } - break; - - case MotionEvent.ACTION_MOVE: - if (!sloppyGesture) { - break; - } - - // See if we still have a sloppy gesture - sloppyGesture = isSloppyGesture(event); - if (!sloppyGesture) { - // No, start normal gesture now - gestureInProgress = listener.onShoveBegin(this); - } - - break; - - case MotionEvent.ACTION_POINTER_UP: - if (!sloppyGesture) { - break; - } - - break; - } - } - - @Override - protected void handleInProgressEvent(int actionCode, MotionEvent event) { - switch (actionCode) { - case MotionEvent.ACTION_POINTER_UP: - // Gesture ended but - updateStateByEvent(event); - - if (!sloppyGesture) { - listener.onShoveEnd(this); - } - - resetState(); - break; - - case MotionEvent.ACTION_CANCEL: - if (!sloppyGesture) { - listener.onShoveEnd(this); - } - - resetState(); - break; - - case MotionEvent.ACTION_MOVE: - updateStateByEvent(event); - - // Only accept the event if our relative pressure is within - // a certain limit. This can help filter shaky data as a - // finger is lifted. Also check that shove is meaningful. - if (currPressure / prevPressure > PRESSURE_THRESHOLD - && Math.abs(getShovePixelsDelta()) > 0.5f) { - final boolean updatePrevious = listener.onShove(this); - if (updatePrevious) { - prevEvent.recycle(); - prevEvent = MotionEvent.obtain(event); - } - } - break; - } - } - - @Override - protected void resetState() { - super.resetState(); - sloppyGesture = false; - prevAverageY = 0.0f; - currAverageY = 0.0f; - } - - @Override - protected void updateStateByEvent(MotionEvent curr) { - super.updateStateByEvent(curr); - - final MotionEvent prev = prevEvent; - float py0 = prev.getY(0); - float py1 = prev.getY(1); - prevAverageY = (py0 + py1) / 2.0f; - - float cy0 = curr.getY(0); - float cy1 = curr.getY(1); - currAverageY = (cy0 + cy1) / 2.0f; - } - - @Override - protected boolean isSloppyGesture(MotionEvent event) { - boolean sloppy = super.isSloppyGesture(event); - if (sloppy) { - return true; - } - - // If it's not traditionally sloppy, we check if the angle between - // fingers - // is acceptable. - double angle = Math.abs(Math.atan2(currFingerDiffY, currFingerDiffX)); - // about 20 degrees, left or right - return !((0.0f < angle && angle < 0.35f) || 2.79f < angle - && angle < Math.PI); - } - - /** - * Return the distance in pixels from the previous shove event to the - * current event. - * - * @return The current distance in pixels. - */ - public float getShovePixelsDelta() { - return currAverageY - prevAverageY; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java deleted file mode 100644 index db492b655..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java +++ /dev/null @@ -1,224 +0,0 @@ -package com.almeros.android.multitouch.gesturedetectors; - -import android.content.Context; -import android.graphics.PointF; -import android.util.DisplayMetrics; -import android.view.MotionEvent; -import android.view.ViewConfiguration; - -/** - * @author Almer Thie (code.almeros.com) Copyright (c) 2013, Almer Thie - * (code.almeros.com) - *

- * All rights reserved. - *

- * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - *

- * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - *

- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -public abstract class TwoFingerGestureDetector extends BaseGestureDetector { - - private final float edgeSlop; - - protected float prevFingerDiffX; - protected float prevFingerDiffY; - protected float currFingerDiffX; - protected float currFingerDiffY; - - private float currLen; - private float prevLen; - - private PointF focus; - - public TwoFingerGestureDetector(Context context) { - super(context); - - ViewConfiguration config = ViewConfiguration.get(context); - - edgeSlop = config.getScaledEdgeSlop(); - } - - @Override - protected abstract void handleStartProgressEvent(int actionCode, - MotionEvent event); - - @Override - protected abstract void handleInProgressEvent(int actionCode, - MotionEvent event); - - protected void updateStateByEvent(MotionEvent curr) { - super.updateStateByEvent(curr); - - final MotionEvent prev = prevEvent; - - currLen = -1; - prevLen = -1; - - // Previous - final float px0 = prev.getX(0); - final float py0 = prev.getY(0); - final float px1 = prev.getX(1); - final float py1 = prev.getY(1); - final float pvx = px1 - px0; - final float pvy = py1 - py0; - prevFingerDiffX = pvx; - prevFingerDiffY = pvy; - - // Current - final float cx0 = curr.getX(0); - final float cy0 = curr.getY(0); - final float cx1 = curr.getX(1); - final float cy1 = curr.getY(1); - final float cvx = cx1 - cx0; - final float cvy = cy1 - cy0; - currFingerDiffX = cvx; - currFingerDiffY = cvy; - focus = determineFocalPoint(curr); - } - - /** - * Return the current distance between the two pointers forming the gesture - * in progress. - * - * @return Distance between pointers in pixels. - */ - public float getCurrentSpan() { - if (currLen == -1) { - final float cvx = currFingerDiffX; - final float cvy = currFingerDiffY; - currLen = (float) Math.sqrt(cvx * cvx + cvy * cvy); - } - return currLen; - } - - /** - * Return the previous distance between the two pointers forming the gesture - * in progress. - * - * @return Previous distance between pointers in pixels. - */ - public float getPreviousSpan() { - if (prevLen == -1) { - final float pvx = prevFingerDiffX; - final float pvy = prevFingerDiffY; - prevLen = (float) Math.sqrt(pvx * pvx + pvy * pvy); - } - return prevLen; - } - - /** - * MotionEvent has no getRawX(int) method; simulate it pending future API - * approval. - * - * @param event Motion Event - * @param pointerIndex Pointer Index - * @return Raw x value or 0 - */ - protected static float getRawX(MotionEvent event, int pointerIndex) { - float offset = event.getRawX() - event.getX(); - if (pointerIndex < event.getPointerCount()) { - return event.getX(pointerIndex) + offset; - } - return 0.0f; - } - - /** - * MotionEvent has no getRawY(int) method; simulate it pending future API - * approval. - * - * @param event Motion Event - * @param pointerIndex Pointer Index - * @return Raw y value or 0 - */ - protected static float getRawY(MotionEvent event, int pointerIndex) { - float offset = event.getRawY() - event.getY(); - if (pointerIndex < event.getPointerCount()) { - return event.getY(pointerIndex) + offset; - } - return 0.0f; - } - - /** - * Check if we have a sloppy gesture. Sloppy gestures can happen if the edge - * of the user's hand is touching the screen, for example. - * - * @param event Motion Event - * @return {@code true} if is sloppy gesture, {@code false} if not - */ - protected boolean isSloppyGesture(MotionEvent event) { - // As orientation can change, query the metrics in touch down - DisplayMetrics metrics = context.getResources().getDisplayMetrics(); - float rightSlopEdge = metrics.widthPixels - edgeSlop; - float bottomSlopEdge = metrics.heightPixels - edgeSlop; - - final float edgeSlop = this.edgeSlop; - - final float x0 = event.getRawX(); - final float y0 = event.getRawY(); - final float x1 = getRawX(event, 1); - final float y1 = getRawY(event, 1); - - boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop || x0 > rightSlopEdge - || y0 > bottomSlopEdge; - boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop || x1 > rightSlopEdge - || y1 > bottomSlopEdge; - - if (p0sloppy && p1sloppy) { - return true; - } else if (p0sloppy) { - return true; - } else if (p1sloppy) { - return true; - } - return false; - } - - /** - * Determine (multi)finger focal point (a.k.a. center point between all - * fingers) - * - * @param motionEvent Motion Event - * @return PointF focal point - */ - public static PointF determineFocalPoint(MotionEvent motionEvent) { - // Number of fingers on screen - final int pCount = motionEvent.getPointerCount(); - float x = 0.0f; - float y = 0.0f; - - for (int i = 0; i < pCount; i++) { - x += motionEvent.getX(i); - y += motionEvent.getY(i); - } - - return new PointF(x / pCount, y / pCount); - } - - public float getFocusX() { - return focus.x; - } - - public float getFocusY() { - return focus.y; - } - -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/package-info.java deleted file mode 100644 index cff2f086d..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Do not use this package. Internal use only. - */ -package com.almeros.android.multitouch.gesturedetectors; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java index 60362dd2e..6f263e463 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java @@ -47,6 +47,31 @@ public class MapboxConstants { */ public static final long VELOCITY_THRESHOLD_IGNORE_FLING = 1000; + /** + * Value by which the default rotation threshold will be increased when scaling + */ + public static final float ROTATION_THRESHOLD_INCREASE_WHEN_SCALING = 25f; + + /** + * Time within which user needs to lift fingers for velocity animation to start. + */ + public static final long SCHEDULED_ANIMATION_TIMEOUT = 150L; + + /** + * Minimum angular velocity for rotation animation + */ + public static final float MINIMUM_ANGULAR_VELOCITY = 1.5f; + + /** + * Maximum angular velocity for rotation animation + */ + public static final float MAXIMUM_ANGULAR_VELOCITY = 20f; + + /** + * Factor to calculate tilt change based on pixel change during shove gesture. + */ + public static final float SHOVE_PIXEL_CHANGE_FACTOR = 0.1f; + /** * The currently supported minimum zoom level. */ @@ -78,14 +103,14 @@ public class MapboxConstants { public static final double MINIMUM_DIRECTION = 0; /** - * The currently used minimun scale factor to clamp to when a quick zoom gesture occurs + * The currently used minimum scale factor to clamp to when a quick zoom gesture occurs */ public static final float MINIMUM_SCALE_FACTOR_CLAMP = 0.00f; /** * The currently used maximum scale factor to clamp to when a quick zoom gesture occurs */ - public static final float MAXIMUM_SCALE_FACTOR_CLAMP = 0.45f; + public static final float MAXIMUM_SCALE_FACTOR_CLAMP = 0.15f; /** * Fragment Argument Key for MapboxMapOptions diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java index 8047e1980..5f5a10d0d 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java @@ -5,26 +5,31 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.PointF; +import android.os.Handler; import android.support.annotation.Nullable; -import android.support.v4.view.GestureDetectorCompat; -import android.support.v4.view.ScaleGestureDetectorCompat; -import android.support.v4.view.animation.FastOutSlowInInterpolator; import android.view.InputDevice; import android.view.MotionEvent; -import android.view.ScaleGestureDetector; -import android.view.VelocityTracker; -import android.view.ViewConfiguration; +import android.view.animation.DecelerateInterpolator; -import com.almeros.android.multitouch.gesturedetectors.RotateGestureDetector; -import com.almeros.android.multitouch.gesturedetectors.ShoveGestureDetector; -import com.almeros.android.multitouch.gesturedetectors.TwoFingerGestureDetector; +import com.mapbox.android.gestures.AndroidGesturesManager; +import com.mapbox.android.gestures.MoveGestureDetector; +import com.mapbox.android.gestures.MultiFingerTapGestureDetector; +import com.mapbox.android.gestures.RotateGestureDetector; +import com.mapbox.android.gestures.ShoveGestureDetector; +import com.mapbox.android.gestures.StandardGestureDetector; +import com.mapbox.android.gestures.StandardScaleGestureDetector; import com.mapbox.android.telemetry.Event; import com.mapbox.android.telemetry.MapEventFactory; import com.mapbox.android.telemetry.MapState; +import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.utils.MathUtils; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener.REASON_API_ANIMATION; @@ -32,24 +37,15 @@ import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener.RE /** * Manages gestures events on a MapView. - *

- * Relies on gesture detection code in almeros.android.multitouch.gesturedetectors. - *

*/ final class MapGestureDetector { private final Transform transform; private final Projection projection; private final UiSettings uiSettings; - private final TrackingSettings trackingSettings; private final AnnotationManager annotationManager; private final CameraChangeDispatcher cameraChangeDispatcher; - private GestureDetectorCompat gestureDetector; - private ScaleGestureDetector scaleGestureDetector; - private RotateGestureDetector rotateGestureDetector; - private ShoveGestureDetector shoveGestureDetector; - // deprecated map touch API private MapboxMap.OnMapClickListener onMapClickListener; private MapboxMap.OnMapLongClickListener onMapLongClickListener; @@ -69,43 +65,73 @@ final class MapGestureDetector { private final CopyOnWriteArrayList onScrollListenerList = new CopyOnWriteArrayList<>(); + private final CopyOnWriteArrayList onMoveListenerList + = new CopyOnWriteArrayList<>(); + + private final CopyOnWriteArrayList onRotateListenerList + = new CopyOnWriteArrayList<>(); + + private final CopyOnWriteArrayList onScaleListenerList + = new CopyOnWriteArrayList<>(); + + private final CopyOnWriteArrayList onShoveListenerList + = new CopyOnWriteArrayList<>(); + + /** + * User-set focal point. + */ private PointF focalPoint; - private boolean twoTap; - private boolean quickZoom; - private boolean tiltGestureOccurred; - private boolean scrollGestureOccurred; + private AndroidGesturesManager gesturesManager; + private boolean executeDoubleTap; - private boolean scaleGestureOccurred; - private boolean recentScaleGestureOccurred; - private long scaleBeginTime; + private Animator scaleAnimator; + private Animator rotateAnimator; + private final List scheduledAnimators = new ArrayList<>(); - private boolean scaleAnimating; - private boolean rotateAnimating; - - private VelocityTracker velocityTracker; - private boolean wasZoomingIn; - private boolean wasClockwiseRotating; - private boolean rotateGestureOccurred; + /** + * Cancels scheduled velocity animations if user doesn't lift fingers within + * {@link MapboxConstants#SCHEDULED_ANIMATION_TIMEOUT} + */ + private Handler animationsTimeoutHandler = new Handler(); MapGestureDetector(Context context, Transform transform, Projection projection, UiSettings uiSettings, - TrackingSettings trackingSettings, AnnotationManager annotationManager, - CameraChangeDispatcher cameraChangeDispatcher) { + AnnotationManager annotationManager, CameraChangeDispatcher cameraChangeDispatcher) { this.annotationManager = annotationManager; this.transform = transform; this.projection = projection; this.uiSettings = uiSettings; - this.trackingSettings = trackingSettings; this.cameraChangeDispatcher = cameraChangeDispatcher; - // Touch gesture detectors + // Checking for context != null for testing purposes if (context != null) { - gestureDetector = new GestureDetectorCompat(context, new GestureListener()); - gestureDetector.setIsLongpressEnabled(true); - scaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureListener()); - ScaleGestureDetectorCompat.setQuickScaleEnabled(scaleGestureDetector, true); - rotateGestureDetector = new RotateGestureDetector(context, new RotateGestureListener()); - shoveGestureDetector = new ShoveGestureDetector(context, new ShoveGestureListener()); + gesturesManager = new AndroidGesturesManager(context); + + Set shoveScaleSet = new HashSet<>(); + shoveScaleSet.add(AndroidGesturesManager.GESTURE_TYPE_SHOVE); + shoveScaleSet.add(AndroidGesturesManager.GESTURE_TYPE_SCALE); + + Set shoveRotateSet = new HashSet<>(); + shoveRotateSet.add(AndroidGesturesManager.GESTURE_TYPE_SHOVE); + shoveRotateSet.add(AndroidGesturesManager.GESTURE_TYPE_ROTATE); + + Set ScaleLongPressSet = new HashSet<>(); + ScaleLongPressSet.add(AndroidGesturesManager.GESTURE_TYPE_SCALE); + ScaleLongPressSet.add(AndroidGesturesManager.GESTURE_TYPE_LONG_PRESS); + + gesturesManager.setMutuallyExclusiveGestures(shoveScaleSet, shoveRotateSet, ScaleLongPressSet); + + gesturesManager.setStandardGestureListener(new StandardGestureListener()); + gesturesManager.setMoveGestureListener(new MoveGestureListener()); + gesturesManager.setStandardScaleGestureListener(new ScaleGestureListener( + context.getResources().getDimension(R.dimen.mapbox_minimum_scale_velocity) + )); + gesturesManager.setRotateGestureListener(new RotateGestureListener( + context.getResources().getDimension(R.dimen.mapbox_minimum_scale_span_when_rotating), + context.getResources().getDimension(R.dimen.mapbox_minimum_angular_velocity) + )); + gesturesManager.setShoveGestureListener(new ShoveGestureListener()); + gesturesManager.setMultiFingerTapGestureListener(new TapGestureListener()); } } @@ -132,8 +158,9 @@ final class MapGestureDetector { /** * Get the current active gesture focal point. *

- * This could be either the user provided focal point in {@link UiSettings#setFocalPoint(PointF)} or the focal point - * defined as a result of {@link TrackingSettings#setMyLocationEnabled(boolean)}. + * This could be either the user provided focal point in + * {@link UiSettings#setFocalPoint(PointF)}or null. + * If it's null, gestures will use focal pointed returned by the detector. *

* * @return the current active gesture focal point. @@ -149,118 +176,79 @@ final class MapGestureDetector { * Forwards event to the related gesture detectors. *

* - * @param event the MotionEvent + * @param motionEvent the MotionEvent * @return True if touch event is handled */ - boolean onTouchEvent(MotionEvent event) { - // framework can return null motion events in edge cases #9432 - if (event == null) { + boolean onTouchEvent(MotionEvent motionEvent) { + // Framework can return null motion events in edge cases #9432 + if (motionEvent == null) { return false; } // Check and ignore non touch or left clicks - if ((event.getButtonState() != 0) && (event.getButtonState() != MotionEvent.BUTTON_PRIMARY)) { + if ((motionEvent.getButtonState() != 0) && (motionEvent.getButtonState() != MotionEvent.BUTTON_PRIMARY)) { return false; } - // Check two finger gestures first - scaleGestureDetector.onTouchEvent(event); - rotateGestureDetector.onTouchEvent(event); - shoveGestureDetector.onTouchEvent(event); + boolean result = gesturesManager.onTouchEvent(motionEvent); - // Handle two finger tap - switch (event.getActionMasked()) { + switch (motionEvent.getActionMasked()) { case MotionEvent.ACTION_DOWN: - if (velocityTracker == null) { - velocityTracker = VelocityTracker.obtain(); - } else { - velocityTracker.clear(); - } - velocityTracker.addMovement(event); - // First pointer down, reset scaleGestureOccurred, used to avoid triggering a fling after a scale gesture #7666 - recentScaleGestureOccurred = false; + cancelAnimators(); transform.setGestureInProgress(true); break; - - case MotionEvent.ACTION_POINTER_DOWN: - // Second pointer down - twoTap = event.getPointerCount() == 2 - && uiSettings.isZoomGesturesEnabled(); - if (twoTap) { - // Confirmed 2nd Finger Down - if (isZoomValid(transform)) { - MapEventFactory mapEventFactory = new MapEventFactory(); - LatLng latLng = projection.fromScreenLocation(new PointF(event.getX(), event.getY())); - MapState twoFingerTap = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); - twoFingerTap.setGesture(Events.TWO_FINGER_TAP); - Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, twoFingerTap)); - } - } - break; - - case MotionEvent.ACTION_POINTER_UP: - // Second pointer up - break; - case MotionEvent.ACTION_UP: - // First pointer up - long tapInterval = event.getEventTime() - event.getDownTime(); - boolean isTap = tapInterval <= ViewConfiguration.getTapTimeout(); - boolean inProgress = rotateGestureDetector.isInProgress() - || scaleGestureDetector.isInProgress() - || shoveGestureDetector.isInProgress(); - - if (twoTap && isTap && !inProgress) { - if (focalPoint != null) { - transform.zoom(false, focalPoint); - } else { - PointF focalPoint = TwoFingerGestureDetector.determineFocalPoint(event); - transform.zoom(false, focalPoint); - } - twoTap = false; - return true; - } - - // Scroll / Pan Has Stopped - if (scrollGestureOccurred) { - if (isZoomValid(transform)) { - MapEventFactory mapEventFactory = new MapEventFactory(); - LatLng latLng = projection.fromScreenLocation(new PointF(event.getX(), event.getY())); - MapState dragend = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); - Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_DRAGEND, dragend)); - } - scrollGestureOccurred = false; - - if (!scaleAnimating && !rotateAnimating) { - cameraChangeDispatcher.onCameraIdle(); - } - } - - twoTap = false; transform.setGestureInProgress(false); - if (velocityTracker != null) { - velocityTracker.recycle(); + + // Start all awaiting velocity animations + animationsTimeoutHandler.removeCallbacksAndMessages(null); + for (Animator animator : scheduledAnimators) { + animator.start(); } - velocityTracker = null; + scheduledAnimators.clear(); break; case MotionEvent.ACTION_CANCEL: - twoTap = false; + scheduledAnimators.clear(); transform.setGestureInProgress(false); - if (velocityTracker != null) { - velocityTracker.recycle(); - } - velocityTracker = null; - break; - case MotionEvent.ACTION_MOVE: - if (velocityTracker != null) { - velocityTracker.addMovement(event); - velocityTracker.computeCurrentVelocity(1000); - } break; } - return gestureDetector.onTouchEvent(event); + return result; + } + + void cancelAnimators() { + if (scaleAnimator != null) { + scaleAnimator.cancel(); + } + if (rotateAnimator != null) { + rotateAnimator.cancel(); + } + + animationsTimeoutHandler.removeCallbacksAndMessages(null); + scheduledAnimators.clear(); + } + + /** + * Posted on main thread with {@link #animationsTimeoutHandler}. Cancels all scheduled animators if needed. + */ + private Runnable cancelAnimatorsRunnable = new Runnable() { + @Override + public void run() { + cancelAnimators(); + } + }; + + /** + * Schedules a velocity animator to be executed when user lift fingers, + * unless canceled by the {@link #cancelAnimatorsRunnable}. + * + * @param animator animator ot be scheduled + */ + private void scheduleAnimator(Animator animator) { + scheduledAnimators.add(animator); + animationsTimeoutHandler.removeCallbacksAndMessages(null); + animationsTimeoutHandler.postDelayed(cancelAnimatorsRunnable, MapboxConstants.SCHEDULED_ANIMATION_TIMEOUT); } /** @@ -269,7 +257,7 @@ final class MapGestureDetector { * Examples of such events are mouse scroll events, mouse moves, joystick & trackpad. *

* - * @param event The MotionEvent occured + * @param event The MotionEvent occurred * @return True is the event is handled */ boolean onGenericMotionEvent(MotionEvent event) { @@ -291,7 +279,7 @@ final class MapGestureDetector { float scrollDist = event.getAxisValue(MotionEvent.AXIS_VSCROLL); // Scale the map by the appropriate power of two factor - transform.zoomBy(scrollDist, event.getX(), event.getY()); + transform.zoomBy(scrollDist, new PointF(event.getX(), event.getY())); return true; @@ -305,61 +293,14 @@ final class MapGestureDetector { return false; } - /** - * Responsible for handling one finger gestures. - */ - private class GestureListener extends android.view.GestureDetector.SimpleOnGestureListener { - + private final class StandardGestureListener extends StandardGestureDetector.SimpleStandardOnGestureListener { @Override - public boolean onDown(MotionEvent event) { - return true; - } - - @Override - public boolean onDoubleTapEvent(MotionEvent e) { - if (!uiSettings.isZoomGesturesEnabled() || !uiSettings.isDoubleTapGesturesEnabled()) { - return false; - } - - switch (e.getAction()) { - case MotionEvent.ACTION_DOWN: - break; - case MotionEvent.ACTION_MOVE: - break; - case MotionEvent.ACTION_UP: - if (quickZoom) { - cameraChangeDispatcher.onCameraIdle(); - quickZoom = false; - break; - } - - // notify camera change listener - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - - // Single finger double tap - if (focalPoint != null) { - // User provided focal point - transform.zoom(true, focalPoint); - } else { - // Zoom in on gesture - transform.zoom(true, new PointF(e.getX(), e.getY())); - } - if (isZoomValid(transform)) { - MapEventFactory mapEventFactory = new MapEventFactory(); - LatLng latLng = projection.fromScreenLocation(new PointF(e.getX(), e.getY())); - MapState doubleTap = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); - doubleTap.setGesture(Events.DOUBLE_TAP); - Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, doubleTap)); - } - break; - } - + public boolean onDown(MotionEvent motionEvent) { return true; } @Override public boolean onSingleTapUp(MotionEvent motionEvent) { - // Cancel any animation transform.cancelTransitions(); return true; } @@ -378,31 +319,51 @@ final class MapGestureDetector { notifyOnMapClickListeners(tapPoint); } - if (isZoomValid(transform)) { - MapEventFactory mapEventFactory = new MapEventFactory(); - LatLng latLng = projection.fromScreenLocation(new PointF(motionEvent.getX(), motionEvent.getY())); - MapState singleTap = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); - singleTap.setGesture(Events.SINGLE_TAP); - Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, singleTap)); - } + sendTelemetryEvent(Events.SINGLE_TAP, new PointF(motionEvent.getX(), motionEvent.getY())); return true; } + @Override + public boolean onDoubleTapEvent(MotionEvent motionEvent) { + int action = motionEvent.getActionMasked(); + if (action == MotionEvent.ACTION_DOWN) { + executeDoubleTap = true; + } + if (motionEvent.getActionMasked() == MotionEvent.ACTION_UP) { + if (!uiSettings.isZoomGesturesEnabled() || !uiSettings.isDoubleTapGesturesEnabled() || !executeDoubleTap) { + return false; + } + + transform.cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); + + // Single finger double tap + if (focalPoint != null) { + // User provided focal point + transform.zoomIn(focalPoint); + } else { + // Zoom in on gesture + transform.zoomIn(new PointF(motionEvent.getX(), motionEvent.getY())); + } + + sendTelemetryEvent(Events.DOUBLE_TAP, new PointF(motionEvent.getX(), motionEvent.getY())); + + return true; + } + return super.onDoubleTapEvent(motionEvent); + } + @Override public void onLongPress(MotionEvent motionEvent) { PointF longClickPoint = new PointF(motionEvent.getX(), motionEvent.getY()); - - if (!quickZoom) { - notifyOnMapLongClickListeners(longClickPoint); - } + notifyOnMapLongClickListeners(longClickPoint); } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - if ((!trackingSettings.isScrollGestureCurrentlyEnabled()) || recentScaleGestureOccurred) { + if ((!uiSettings.isScrollGesturesEnabled())) { // don't allow a fling is scroll is disabled - // and ignore when a scale gesture has occurred return false; } @@ -415,11 +376,7 @@ final class MapGestureDetector { return false; } - trackingSettings.resetTrackingModesIfRequired(true, false, false); - - // cancel any animation transform.cancelTransitions(); - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); // tilt results in a bigger translation, limiting input for #5281 @@ -435,47 +392,416 @@ final class MapGestureDetector { transform.moveBy(offsetX, offsetY, animationTime); notifyOnFlingListeners(); + + return true; + } + } + + private final class MoveGestureListener extends MoveGestureDetector.SimpleOnMoveGestureListener { + @Override + public boolean onMoveBegin(MoveGestureDetector detector) { + if (!uiSettings.isScrollGesturesEnabled()) { + return false; + } + + transform.cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); + + sendTelemetryEvent(Events.PAN, detector.getFocalPoint()); + + notifyOnMoveBeginListeners(detector); + return true; } - // Called for drags @Override - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - if (!trackingSettings.isScrollGestureCurrentlyEnabled()) { - return false; - } - - if (tiltGestureOccurred) { - return false; - } - - if (!scrollGestureOccurred) { - scrollGestureOccurred = true; - - // Cancel any animation - if (!scaleGestureOccurred) { - transform.cancelTransitions(); - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - } - - if (isZoomValid(transform)) { - MapEventFactory mapEventFactory = new MapEventFactory(); - LatLng latLng = projection.fromScreenLocation(new PointF(e1.getX(), e1.getY())); - MapState pan = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); - pan.setGesture(Events.PAN); - Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, pan)); - } - } - - // reset tracking if needed - trackingSettings.resetTrackingModesIfRequired(true, false, false); + public boolean onMove(MoveGestureDetector detector, float distanceX, float distanceY) { + // dispatching start even once more if another detector ended, and this one didn't + cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE); // Scroll the map transform.moveBy(-distanceX, -distanceY, 0 /*no duration*/); notifyOnScrollListeners(); + notifyOnMoveListeners(detector); return true; } + + @Override + public void onMoveEnd(MoveGestureDetector detector, float velocityX, float velocityY) { + cameraChangeDispatcher.onCameraIdle(); + notifyOnMoveEndListeners(detector); + } + } + + private final class ScaleGestureListener extends StandardScaleGestureDetector.SimpleStandardOnScaleGestureListener { + + private final float minimumVelocity; + + private PointF scaleFocalPoint; + private boolean quickZoom; + + ScaleGestureListener(float minimumVelocity) { + this.minimumVelocity = minimumVelocity; + } + + @Override + public boolean onScaleBegin(StandardScaleGestureDetector detector) { + if (!uiSettings.isZoomGesturesEnabled()) { + return false; + } + + transform.cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); + + quickZoom = detector.getPointersCount() == 1; + if (quickZoom) { + // when quickzoom, dismiss double tap and disable move gesture + executeDoubleTap = false; + gesturesManager.getMoveGestureDetector().setEnabled(false); + } + + // increase rotate angle threshold when scale is detected first + gesturesManager.getRotateGestureDetector().setAngleThreshold( + gesturesManager.getRotateGestureDetector().getDefaultAngleThreshold() + + MapboxConstants.ROTATION_THRESHOLD_INCREASE_WHEN_SCALING + ); + + // setting focalPoint in #onScaleBegin() as well, because #onScale() might not get called before #onScaleEnd() + setScaleFocalPoint(detector); + + sendTelemetryEvent(Events.PINCH, scaleFocalPoint); + + notifyOnScaleBeginListeners(detector); + + return true; + } + + @Override + public boolean onScale(StandardScaleGestureDetector detector) { + // dispatching start even once more if another detector ended, and this one didn't + cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE); + + setScaleFocalPoint(detector); + + float scaleFactor = detector.getScaleFactor(); + double zoomBy = getNewZoom(scaleFactor, quickZoom); + transform.zoomBy(zoomBy, scaleFocalPoint); + + notifyOnScaleListeners(detector); + + return true; + } + + @Override + public void onScaleEnd(StandardScaleGestureDetector detector, float velocityX, float velocityY) { + cameraChangeDispatcher.onCameraIdle(); + + if (quickZoom) { + //if quickzoom, re-enabling move gesture detector + gesturesManager.getMoveGestureDetector().setEnabled(true); + } + + // resetting default angle threshold + gesturesManager.getRotateGestureDetector().setAngleThreshold( + gesturesManager.getRotateGestureDetector().getDefaultAngleThreshold() + ); + + float velocityXY = Math.abs(velocityX) + Math.abs(velocityY); + if (velocityXY > minimumVelocity) { + double zoomAddition = calculateScale(velocityXY, detector.isScalingOut()); + double currentZoom = transform.getRawZoom(); + long animationTime = (long) (Math.abs(zoomAddition) * 1000 / 4); + scaleAnimator = createScaleAnimator(currentZoom, zoomAddition, animationTime); + scheduleAnimator(scaleAnimator); + } + + notifyOnScaleEndListeners(detector); + } + + private void setScaleFocalPoint(StandardScaleGestureDetector detector) { + if (focalPoint != null) { + // around user provided focal point + scaleFocalPoint = focalPoint; + } else if (quickZoom) { + // around center + scaleFocalPoint = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); + } else { + // around gesture + scaleFocalPoint = detector.getFocalPoint(); + } + } + + private double calculateScale(double velocityXY, boolean isScalingOut) { + double zoomAddition = (float) Math.log(velocityXY / 1000 + 1); + if (isScalingOut) { + zoomAddition = -zoomAddition; + } + return zoomAddition; + } + + private Animator createScaleAnimator(double currentZoom, double zoomAddition, long animationTime) { + ValueAnimator animator = ValueAnimator.ofFloat((float) currentZoom, (float) (currentZoom + zoomAddition)); + animator.setDuration(animationTime); + animator.setInterpolator(new DecelerateInterpolator()); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + transform.setZoom((Float) animation.getAnimatedValue(), scaleFocalPoint, 0); + } + }); + + animator.addListener(new AnimatorListenerAdapter() { + + @Override + public void onAnimationStart(Animator animation) { + transform.cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_ANIMATION); + } + + @Override + public void onAnimationCancel(Animator animation) { + transform.cancelTransitions(); + } + + @Override + public void onAnimationEnd(Animator animation) { + cameraChangeDispatcher.onCameraIdle(); + } + }); + return animator; + } + + private double getNewZoom(float scaleFactor, boolean quickZoom) { + double zoomBy = Math.log(scaleFactor) / Math.log(Math.PI / 2); + if (quickZoom) { + // clamp scale factors we feed to core #7514 + boolean negative = zoomBy < 0; + zoomBy = MathUtils.clamp(Math.abs(zoomBy), + MapboxConstants.MINIMUM_SCALE_FACTOR_CLAMP, + MapboxConstants.MAXIMUM_SCALE_FACTOR_CLAMP); + return negative ? -zoomBy : zoomBy; + } + return zoomBy; + } + } + + private final class RotateGestureListener extends RotateGestureDetector.SimpleOnRotateGestureListener { + private PointF rotateFocalPoint; + private final float minimumScaleSpanWhenRotating; + private final float minimumAngularVelocity; + + RotateGestureListener(float minimumScaleSpanWhenRotating, float minimumAngularVelocity) { + this.minimumScaleSpanWhenRotating = minimumScaleSpanWhenRotating; + this.minimumAngularVelocity = minimumAngularVelocity; + } + + @Override + public boolean onRotateBegin(RotateGestureDetector detector) { + if (!uiSettings.isRotateGesturesEnabled()) { + return false; + } + + transform.cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); + + // when rotation starts, interrupting scale and increasing the threshold + // to make rotation without scaling easier + gesturesManager.getStandardScaleGestureDetector().setSpanSinceStartThreshold(minimumScaleSpanWhenRotating); + gesturesManager.getStandardScaleGestureDetector().interrupt(); + + // setting in #onRotateBegin() as well, because #onRotate() might not get called before #onRotateEnd() + setRotateFocalPoint(detector); + + sendTelemetryEvent(Events.ROTATION, rotateFocalPoint); + + notifyOnRotateBeginListeners(detector); + + return true; + } + + @Override + public boolean onRotate(RotateGestureDetector detector, float rotationDegreesSinceLast, + float rotationDegreesSinceFirst) { + // dispatching start even once more if another detector ended, and this one didn't + cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE); + + setRotateFocalPoint(detector); + + // Calculate map bearing value + double bearing = transform.getRawBearing() + rotationDegreesSinceLast; + + // Rotate the map + transform.setBearing(bearing, rotateFocalPoint.x, rotateFocalPoint.y); + + notifyOnRotateListeners(detector); + + return true; + } + + @Override + public void onRotateEnd(RotateGestureDetector detector, float velocityX, float velocityY, float angularVelocity) { + cameraChangeDispatcher.onCameraIdle(); + + // resetting default scale threshold values + gesturesManager.getStandardScaleGestureDetector().setSpanSinceStartThreshold( + gesturesManager.getStandardScaleGestureDetector().getDefaultSpanSinceStartThreshold()); + + if (Math.abs(angularVelocity) < minimumAngularVelocity) { + return; + } + + boolean negative = angularVelocity < 0; + angularVelocity = (float) Math.pow(angularVelocity, 2); + angularVelocity = MathUtils.clamp( + angularVelocity, MapboxConstants.MINIMUM_ANGULAR_VELOCITY, MapboxConstants.MAXIMUM_ANGULAR_VELOCITY); + + long animationTime = (long) (Math.log(angularVelocity + 1) * 500); + + if (negative) { + angularVelocity = -angularVelocity; + } + + rotateAnimator = createRotateAnimator(angularVelocity, animationTime); + scheduleAnimator(rotateAnimator); + + notifyOnRotateEndListeners(detector); + } + + private void setRotateFocalPoint(RotateGestureDetector detector) { + if (focalPoint != null) { + // User provided focal point + rotateFocalPoint = focalPoint; + } else { + // around gesture + rotateFocalPoint = detector.getFocalPoint(); + } + } + + private Animator createRotateAnimator(float angularVelocity, long animationTime) { + ValueAnimator animator = ValueAnimator.ofFloat(angularVelocity, 0f); + animator.setDuration(animationTime); + animator.setInterpolator(new DecelerateInterpolator()); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + transform.setBearing( + transform.getRawBearing() + (float) animation.getAnimatedValue(), + rotateFocalPoint.x, rotateFocalPoint.y, + 0L + ); + } + }); + + animator.addListener(new AnimatorListenerAdapter() { + + @Override + public void onAnimationStart(Animator animation) { + transform.cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_ANIMATION); + } + + @Override + public void onAnimationCancel(Animator animation) { + cameraChangeDispatcher.onCameraIdle(); + } + + @Override + public void onAnimationEnd(Animator animation) { + cameraChangeDispatcher.onCameraIdle(); + } + }); + + return animator; + } + } + + private final class ShoveGestureListener extends ShoveGestureDetector.SimpleOnShoveGestureListener { + @Override + public boolean onShoveBegin(ShoveGestureDetector detector) { + if (!uiSettings.isTiltGesturesEnabled()) { + return false; + } + + transform.cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); + + sendTelemetryEvent(Events.PITCH, detector.getFocalPoint()); + + // disabling move gesture during shove + gesturesManager.getMoveGestureDetector().setEnabled(false); + + notifyOnShoveBeginListeners(detector); + + return true; + } + + @Override + public boolean onShove(ShoveGestureDetector detector, float deltaPixelsSinceLast, float deltaPixelsSinceStart) { + // dispatching start even once more if another detector ended, and this one didn't + cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE); + + // Get tilt value (scale and clamp) + double pitch = transform.getTilt(); + pitch -= MapboxConstants.SHOVE_PIXEL_CHANGE_FACTOR * deltaPixelsSinceLast; + pitch = MathUtils.clamp(pitch, MapboxConstants.MINIMUM_TILT, MapboxConstants.MAXIMUM_TILT); + + // Tilt the map + transform.setTilt(pitch); + + notifyOnShoveListeners(detector); + + return true; + } + + @Override + public void onShoveEnd(ShoveGestureDetector detector, float velocityX, float velocityY) { + cameraChangeDispatcher.onCameraIdle(); + + // re-enabling move gesture + gesturesManager.getMoveGestureDetector().setEnabled(true); + + notifyOnShoveEndListeners(detector); + } + } + + private final class TapGestureListener implements MultiFingerTapGestureDetector.OnMultiFingerTapGestureListener { + @Override + public boolean onMultiFingerTap(MultiFingerTapGestureDetector detector, int pointersCount) { + if (!uiSettings.isZoomGesturesEnabled() || pointersCount != 2) { + return false; + } + + transform.cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); + + if (focalPoint != null) { + transform.zoomOut(focalPoint); + } else { + transform.zoomOut(detector.getFocalPoint()); + } + + return true; + } + } + + private void sendTelemetryEvent(String eventType, PointF focalPoint) { + if (isZoomValid(transform)) { + MapEventFactory mapEventFactory = new MapEventFactory(); + LatLng latLng = projection.fromScreenLocation(focalPoint); + MapState state = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); + state.setGesture(eventType); + Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, state)); + } + } + + private boolean isZoomValid(Transform transform) { + if (transform == null) { + return false; + } + double mapZoom = transform.getZoom(); + return mapZoom >= MapboxConstants.MINIMUM_ZOOM && mapZoom <= MapboxConstants.MAXIMUM_ZOOM; } void notifyOnMapClickListeners(PointF tapPoint) { @@ -526,410 +852,75 @@ final class MapGestureDetector { } } - /** - * Responsible for handling two finger gestures and double-tap drag gestures. - */ - private class ScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { - - private static final int ANIMATION_TIME_MULTIPLIER = 77; - private static final double ZOOM_DISTANCE_DIVIDER = 5; - - private float scaleFactor = 1.0f; - private PointF scalePointBegin; - - // Called when two fingers first touch the screen - @Override - public boolean onScaleBegin(ScaleGestureDetector detector) { - if (!uiSettings.isZoomGesturesEnabled()) { - return false; - } - - recentScaleGestureOccurred = true; - scalePointBegin = new PointF(detector.getFocusX(), detector.getFocusY()); - scaleBeginTime = detector.getEventTime(); - if (isZoomValid(transform)) { - MapEventFactory mapEventFactory = new MapEventFactory(); - LatLng latLng = projection.fromScreenLocation(new PointF(detector.getFocusX(), detector.getFocusY())); - MapState pinch = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); - pinch.setGesture(Events.PINCH); - Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, pinch)); - } - return true; - } - - // Called each time a finger moves - // Called for pinch zooms and quickzooms/quickscales - @Override - public boolean onScale(ScaleGestureDetector detector) { - if (!uiSettings.isZoomGesturesEnabled()) { - return super.onScale(detector); - } - - wasZoomingIn = (Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2)) > 0; - if (tiltGestureOccurred) { - return false; - } - - // Ignore short touches in case it is a tap - // Also ignore small scales - long time = detector.getEventTime(); - long interval = time - scaleBeginTime; - if (!scaleGestureOccurred && (interval <= ViewConfiguration.getTapTimeout())) { - return false; - } - - // If scale is large enough ignore a tap - scaleFactor *= detector.getScaleFactor(); - if ((scaleFactor > 1.1f) || (scaleFactor < 0.9f)) { - // notify camera change listener - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - scaleGestureOccurred = true; - } - - if (!scaleGestureOccurred) { - return false; - } - - // Gesture is a quickzoom if there aren't two fingers - if (!quickZoom && !twoTap) { - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - } - quickZoom = !twoTap; - - // make an assumption here; if the zoom center is specified by the gesture, it's NOT going - // to be in the center of the map. Therefore the zoom will translate the map center, so tracking - // should be disabled. - trackingSettings.resetTrackingModesIfRequired(!quickZoom, false, false); - // Scale the map - if (focalPoint != null) { - // arround user provided focal point - transform.zoomBy(Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2), focalPoint.x, focalPoint.y); - } else if (quickZoom) { - cameraChangeDispatcher.onCameraMove(); - // clamp scale factors we feed to core #7514 - float scaleFactor = detector.getScaleFactor(); - // around center map - double zoomBy = Math.log(scaleFactor) / Math.log(Math.PI / 2); - boolean negative = zoomBy < 0; - zoomBy = MathUtils.clamp(Math.abs(zoomBy), - MapboxConstants.MINIMUM_SCALE_FACTOR_CLAMP, - MapboxConstants.MAXIMUM_SCALE_FACTOR_CLAMP); - transform.zoomBy(negative ? -zoomBy : zoomBy, uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); - recentScaleGestureOccurred = true; - } else { - // around gesture - transform.zoomBy(Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2), - scalePointBegin.x, scalePointBegin.y); - } - return true; - } - - // Called when fingers leave screen - @Override - public void onScaleEnd(final ScaleGestureDetector detector) { - if (velocityTracker == null) { - return; - } - - if (rotateGestureOccurred || quickZoom) { - reset(); - return; - } - - double velocityXY = Math.abs(velocityTracker.getYVelocity()) + Math.abs(velocityTracker.getXVelocity()); - if (velocityXY > MapboxConstants.VELOCITY_THRESHOLD_IGNORE_FLING / 2) { - scaleAnimating = true; - double zoomAddition = calculateScale(velocityXY); - double currentZoom = transform.getRawZoom(); - long animationTime = (long) (Math.log(velocityXY) * ANIMATION_TIME_MULTIPLIER); - createScaleAnimator(currentZoom, zoomAddition, animationTime).start(); - } else if (!scaleAnimating) { - reset(); - } - } - - private void reset() { - scaleAnimating = false; - scaleGestureOccurred = false; - scaleBeginTime = 0; - scaleFactor = 1.0f; - cameraChangeDispatcher.onCameraIdle(); - } - - private double calculateScale(double velocityXY) { - double zoomAddition = (float) (Math.log(velocityXY) / ZOOM_DISTANCE_DIVIDER); - if (!wasZoomingIn) { - zoomAddition = -zoomAddition; - } - return zoomAddition; - } - - private Animator createScaleAnimator(double currentZoom, double zoomAddition, long animationTime) { - ValueAnimator animator = ValueAnimator.ofFloat((float) currentZoom, (float) (currentZoom + zoomAddition)); - animator.setDuration(animationTime); - animator.setInterpolator(new FastOutSlowInInterpolator()); - animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - transform.setZoom((Float) animation.getAnimatedValue(), scalePointBegin, 0, true); - } - }); - animator.addListener(new AnimatorListenerAdapter() { - - @Override - public void onAnimationStart(Animator animation) { - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_ANIMATION); - } - - @Override - public void onAnimationCancel(Animator animation) { - reset(); - } - - @Override - public void onAnimationEnd(Animator animation) { - reset(); - } - }); - return animator; + void notifyOnMoveBeginListeners(MoveGestureDetector detector) { + for (MapboxMap.OnMoveListener listener : onMoveListenerList) { + listener.onMoveBegin(detector); } } - /** - * Responsible for handling rotation gestures. - */ - private class RotateGestureListener extends RotateGestureDetector.SimpleOnRotateGestureListener { - - private static final float ROTATE_INVOKE_ANGLE = 15.30f; - private static final float ROTATE_LIMITATION_ANGLE = 3.35f; - private static final float ROTATE_LIMITATION_DURATION = ROTATE_LIMITATION_ANGLE * 1.85f; - - private long beginTime = 0; - private boolean started = false; - - // Called when two fingers first touch the screen - @Override - public boolean onRotateBegin(RotateGestureDetector detector) { - if (!trackingSettings.isRotateGestureCurrentlyEnabled()) { - return false; - } - - // notify camera change listener - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - - beginTime = detector.getEventTime(); - return true; - } - - // Called each time one of the two fingers moves - // Called for rotation - @Override - public boolean onRotate(RotateGestureDetector detector) { - if (!trackingSettings.isRotateGestureCurrentlyEnabled() || tiltGestureOccurred) { - return false; - } - - // If rotate is large enough ignore a tap - // Also is zoom already started, don't rotate - float angle = detector.getRotationDegreesDelta(); - if (Math.abs(angle) >= ROTATE_INVOKE_ANGLE) { - if (isZoomValid(transform)) { - MapEventFactory mapEventFactory = new MapEventFactory(); - LatLng latLng = projection.fromScreenLocation(new PointF(detector.getFocusX(), detector.getFocusY())); - MapState rotation = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); - rotation.setGesture(Events.ROTATION); - Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, rotation)); - } - started = true; - } - - if (!started) { - return false; - } - - wasClockwiseRotating = detector.getRotationDegreesDelta() > 0; - if (scaleBeginTime != 0) { - rotateGestureOccurred = true; - } - - // rotation constitutes translation of anything except the center of - // rotation, so cancel both location and bearing tracking if required - trackingSettings.resetTrackingModesIfRequired(true, true, false); - - // Calculate map bearing value - double bearing = transform.getRawBearing() + angle; - - // Rotate the map - if (focalPoint != null) { - // User provided focal point - transform.setBearing(bearing, focalPoint.x, focalPoint.y); - } else { - // around gesture - transform.setBearing(bearing, detector.getFocusX(), detector.getFocusY()); - } - return true; - } - - // Called when the fingers leave the screen - @Override - public void onRotateEnd(RotateGestureDetector detector) { - long interval = detector.getEventTime() - beginTime; - if ((!started && (interval <= ViewConfiguration.getTapTimeout())) || scaleAnimating || interval > 500) { - reset(); - return; - } - - double angularVelocity = calculateVelocityVector(detector); - if (Math.abs(angularVelocity) > 0.001 && rotateGestureOccurred && !rotateAnimating) { - animateRotateVelocity(); - } else if (!rotateAnimating) { - reset(); - } - } - - private void reset() { - beginTime = 0; - started = false; - rotateAnimating = false; - rotateGestureOccurred = false; - - if (!twoTap) { - cameraChangeDispatcher.onCameraIdle(); - } - } - - private void animateRotateVelocity() { - rotateAnimating = true; - double currentRotation = transform.getRawBearing(); - double rotateAdditionDegrees = calculateVelocityInDegrees(); - createAnimator(currentRotation, rotateAdditionDegrees).start(); - } - - private double calculateVelocityVector(RotateGestureDetector detector) { - return ((detector.getFocusX() * velocityTracker.getYVelocity()) - + (detector.getFocusY() * velocityTracker.getXVelocity())) - / (Math.pow(detector.getFocusX(), 2) + Math.pow(detector.getFocusY(), 2)); - } - - private double calculateVelocityInDegrees() { - double angleRadians = Math.atan2(velocityTracker.getXVelocity(), velocityTracker.getYVelocity()); - double angle = angleRadians / (Math.PI / 180); - if (angle <= 0) { - angle += 360; - } - - // limit the angle - angle = angle / ROTATE_LIMITATION_ANGLE; - - // correct direction - if (!wasClockwiseRotating) { - angle = -angle; - } - - return angle; - } - - private Animator createAnimator(double currentRotation, double rotateAdditionDegrees) { - ValueAnimator animator = ValueAnimator.ofFloat( - (float) currentRotation, - (float) (currentRotation + rotateAdditionDegrees) - ); - animator.setDuration((long) (Math.abs(rotateAdditionDegrees) * ROTATE_LIMITATION_DURATION)); - animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - transform.setBearing((Float) animation.getAnimatedValue()); - } - }); - animator.addListener(new AnimatorListenerAdapter() { - - @Override - public void onAnimationStart(Animator animation) { - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_ANIMATION); - } - - @Override - public void onAnimationCancel(Animator animation) { - reset(); - } - - @Override - public void onAnimationEnd(Animator animation) { - reset(); - } - }); - return animator; + void notifyOnMoveListeners(MoveGestureDetector detector) { + for (MapboxMap.OnMoveListener listener : onMoveListenerList) { + listener.onMove(detector); } } - /** - * Responsible for handling 2 finger shove gestures. - */ - private class ShoveGestureListener implements ShoveGestureDetector.OnShoveGestureListener { - - private long beginTime = 0; - private float totalDelta = 0.0f; - - @Override - public boolean onShoveBegin(ShoveGestureDetector detector) { - if (!uiSettings.isTiltGesturesEnabled()) { - return false; - } - - // notify camera change listener - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - return true; + void notifyOnMoveEndListeners(MoveGestureDetector detector) { + for (MapboxMap.OnMoveListener listener : onMoveListenerList) { + listener.onMoveEnd(detector); } + } - @Override - public void onShoveEnd(ShoveGestureDetector detector) { - beginTime = 0; - totalDelta = 0.0f; - tiltGestureOccurred = false; + void notifyOnRotateBeginListeners(RotateGestureDetector detector) { + for (MapboxMap.OnRotateListener listener : onRotateListenerList) { + listener.onRotateBegin(detector); } + } - @Override - public boolean onShove(ShoveGestureDetector detector) { - if (!uiSettings.isTiltGesturesEnabled()) { - return false; - } + void notifyOnRotateListeners(RotateGestureDetector detector) { + for (MapboxMap.OnRotateListener listener : onRotateListenerList) { + listener.onRotate(detector); + } + } - // Ignore short touches in case it is a tap - // Also ignore small tilt - long time = detector.getEventTime(); - long interval = time - beginTime; - if (!tiltGestureOccurred && (interval <= ViewConfiguration.getTapTimeout())) { - return false; - } + void notifyOnRotateEndListeners(RotateGestureDetector detector) { + for (MapboxMap.OnRotateListener listener : onRotateListenerList) { + listener.onRotateEnd(detector); + } + } - // If tilt is large enough ignore a tap - // Also if zoom already started, don't tilt - totalDelta += detector.getShovePixelsDelta(); - if (!tiltGestureOccurred && ((totalDelta > 10.0f) || (totalDelta < -10.0f))) { - tiltGestureOccurred = true; - beginTime = detector.getEventTime(); - if (isZoomValid(transform)) { - MapEventFactory mapEventFactory = new MapEventFactory(); - LatLng latLng = projection.fromScreenLocation(new PointF(detector.getFocusX(), detector.getFocusY())); - MapState pitch = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); - pitch.setGesture(Events.PITCH); - Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, pitch)); - } - } + void notifyOnScaleBeginListeners(StandardScaleGestureDetector detector) { + for (MapboxMap.OnScaleListener listener : onScaleListenerList) { + listener.onScaleBegin(detector); + } + } - if (!tiltGestureOccurred) { - return false; - } + void notifyOnScaleListeners(StandardScaleGestureDetector detector) { + for (MapboxMap.OnScaleListener listener : onScaleListenerList) { + listener.onScale(detector); + } + } - // Get tilt value (scale and clamp) - double pitch = transform.getTilt(); - pitch -= 0.1 * detector.getShovePixelsDelta(); - pitch = Math.max(MapboxConstants.MINIMUM_TILT, Math.min(MapboxConstants.MAXIMUM_TILT, pitch)); + void notifyOnScaleEndListeners(StandardScaleGestureDetector detector) { + for (MapboxMap.OnScaleListener listener : onScaleListenerList) { + listener.onScaleEnd(detector); + } + } - // Tilt the map - transform.setTilt(pitch); - return true; + void notifyOnShoveBeginListeners(ShoveGestureDetector detector) { + for (MapboxMap.OnShoveListener listener : onShoveListenerList) { + listener.onShoveBegin(detector); + } + } + + void notifyOnShoveListeners(ShoveGestureDetector detector) { + for (MapboxMap.OnShoveListener listener : onShoveListenerList) { + listener.onShove(detector); + } + } + + void notifyOnShoveEndListeners(ShoveGestureDetector detector) { + for (MapboxMap.OnShoveListener listener : onShoveListenerList) { + listener.onShoveEnd(detector); } } @@ -981,14 +972,43 @@ final class MapGestureDetector { onScrollListenerList.remove(onScrollListener); } - private boolean isZoomValid(Transform transform) { - if (transform == null) { - return false; - } - double mapZoom = transform.getZoom(); - if (mapZoom < MapboxConstants.MINIMUM_ZOOM || mapZoom > MapboxConstants.MAXIMUM_ZOOM) { - return false; - } - return true; + void addOnMoveListener(MapboxMap.OnMoveListener listener) { + onMoveListenerList.add(listener); } -} + + void removeOnMoveListener(MapboxMap.OnMoveListener listener) { + onMoveListenerList.remove(listener); + } + + void addOnRotateListener(MapboxMap.OnRotateListener listener) { + onRotateListenerList.add(listener); + } + + void removeOnRotateListener(MapboxMap.OnRotateListener listener) { + onRotateListenerList.remove(listener); + } + + void addOnScaleListener(MapboxMap.OnScaleListener listener) { + onScaleListenerList.add(listener); + } + + void removeOnScaleListener(MapboxMap.OnScaleListener listener) { + onScaleListenerList.remove(listener); + } + + void addShoveListener(MapboxMap.OnShoveListener listener) { + onShoveListenerList.add(listener); + } + + void removeShoveListener(MapboxMap.OnShoveListener listener) { + onShoveListenerList.remove(listener); + } + + AndroidGesturesManager getGesturesManager() { + return gesturesManager; + } + + void setGesturesManager(AndroidGesturesManager gesturesManager) { + this.gesturesManager = gesturesManager; + } +} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java index d1f01a30f..9bd9499ff 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java @@ -128,7 +128,7 @@ final class MapKeyListener { // Zoom out PointF focalPoint = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); - transform.zoom(false, focalPoint); + transform.zoomOut(focalPoint); return true; default: @@ -164,7 +164,7 @@ final class MapKeyListener { // Zoom in PointF focalPoint = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); - transform.zoom(true, focalPoint); + transform.zoomIn(focalPoint); return true; } @@ -219,7 +219,7 @@ final class MapKeyListener { if (currentTrackballLongPressTimeOut != null) { // Zoom in PointF focalPoint = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); - transform.zoom(true, focalPoint); + transform.zoomIn(focalPoint); } return true; @@ -261,7 +261,7 @@ final class MapKeyListener { if (!cancelled) { // Zoom out PointF pointF = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); - transform.zoom(false, pointF); + transform.zoomOut(pointF); // Ensure the up action is not run currentTrackballLongPressTimeOut = null; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index cf70300c6..9ace60b48 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -23,6 +23,7 @@ import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ZoomButtonsController; +import com.mapbox.android.gestures.AndroidGesturesManager; import com.mapbox.android.telemetry.AppUserTurnstile; import com.mapbox.android.telemetry.Event; import com.mapbox.android.telemetry.MapEventFactory; @@ -42,8 +43,6 @@ import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings; import com.mapbox.mapboxsdk.net.ConnectivityReceiver; import com.mapbox.mapboxsdk.storage.FileSource; -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.opengles.GL10; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; @@ -52,6 +51,9 @@ import java.util.Iterator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + import timber.log.Timber; import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_MAP_NORTH_ANIMATION; @@ -149,7 +151,7 @@ public class MapView extends FrameLayout { focalPointInvalidator.addListener(createFocalPointChangeListener()); // callback for registering touch listeners - RegisterTouchListener registerTouchListener = new RegisterTouchListener(); + GesturesManagerInteractionListener registerTouchListener = new GesturesManagerInteractionListener(); // callback for zooming in the camera CameraZoomInvalidator zoomInvalidator = new CameraZoomInvalidator(); @@ -184,7 +186,7 @@ public class MapView extends FrameLayout { mapCallback.attachMapboxMap(mapboxMap); // user input - mapGestureDetector = new MapGestureDetector(context, transform, proj, uiSettings, trackingSettings, + mapGestureDetector = new MapGestureDetector(context, transform, proj, uiSettings, annotationManager, cameraChangeDispatcher); mapKeyListener = new MapKeyListener(transform, trackingSettings, uiSettings); @@ -390,6 +392,7 @@ public class MapView extends FrameLayout { public void onStop() { if (mapboxMap != null) { // map was destroyed before it was started + mapGestureDetector.cancelAnimators(); mapboxMap.onStop(); } @@ -923,7 +926,7 @@ public class MapView extends FrameLayout { } } - private class RegisterTouchListener implements MapboxMap.OnRegisterTouchListener { + private class GesturesManagerInteractionListener implements MapboxMap.OnGesturesManagerInteractionListener { @Override public void onSetMapClickListener(MapboxMap.OnMapClickListener listener) { @@ -984,6 +987,56 @@ public class MapView extends FrameLayout { public void onRemoveFlingListener(MapboxMap.OnFlingListener listener) { mapGestureDetector.removeOnFlingListener(listener); } + + @Override + public void onAddMoveListener(MapboxMap.OnMoveListener listener) { + mapGestureDetector.addOnMoveListener(listener); + } + + @Override + public void onRemoveMoveListener(MapboxMap.OnMoveListener listener) { + mapGestureDetector.removeOnMoveListener(listener); + } + + @Override + public void onAddRotateListener(MapboxMap.OnRotateListener listener) { + mapGestureDetector.addOnRotateListener(listener); + } + + @Override + public void onRemoveRotateListener(MapboxMap.OnRotateListener listener) { + mapGestureDetector.removeOnRotateListener(listener); + } + + @Override + public void onAddScaleListener(MapboxMap.OnScaleListener listener) { + mapGestureDetector.addOnScaleListener(listener); + } + + @Override + public void onRemoveScaleListener(MapboxMap.OnScaleListener listener) { + mapGestureDetector.removeOnScaleListener(listener); + } + + @Override + public void onAddShoveListener(MapboxMap.OnShoveListener listener) { + mapGestureDetector.addShoveListener(listener); + } + + @Override + public void onRemoveShoveListener(MapboxMap.OnShoveListener listener) { + mapGestureDetector.removeShoveListener(listener); + } + + @Override + public AndroidGesturesManager getGesturesManager() { + return mapGestureDetector.getGesturesManager(); + } + + @Override + public void setGesturesManager(AndroidGesturesManager gesturesManager) { + mapGestureDetector.setGesturesManager(gesturesManager); + } } private static class MapZoomControllerListener implements ZoomButtonsController.OnZoomListener { @@ -1021,11 +1074,13 @@ public class MapView extends FrameLayout { } private void onZoom(boolean zoomIn, @Nullable PointF focalPoint) { - if (focalPoint != null) { - transform.zoom(zoomIn, focalPoint); + if (focalPoint == null) { + focalPoint = new PointF(mapWidth / 2, mapHeight / 2); + } + if (zoomIn) { + transform.zoomIn(focalPoint); } else { - PointF centerPoint = new PointF(mapWidth / 2, mapHeight / 2); - transform.zoom(zoomIn, centerPoint); + transform.zoomOut(focalPoint); } } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index 5573e2d24..69fc4d2f2 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java @@ -17,6 +17,11 @@ import android.view.View; import android.view.ViewGroup; import com.mapbox.android.core.location.LocationEngine; +import com.mapbox.android.gestures.AndroidGesturesManager; +import com.mapbox.android.gestures.MoveGestureDetector; +import com.mapbox.android.gestures.RotateGestureDetector; +import com.mapbox.android.gestures.ShoveGestureDetector; +import com.mapbox.android.gestures.StandardScaleGestureDetector; import com.mapbox.geojson.Feature; import com.mapbox.geojson.Geometry; import com.mapbox.mapboxsdk.annotations.Annotation; @@ -73,13 +78,13 @@ public final class MapboxMap { private final MyLocationViewSettings myLocationViewSettings; private final CameraChangeDispatcher cameraChangeDispatcher; - private final OnRegisterTouchListener onRegisterTouchListener; + private final OnGesturesManagerInteractionListener onGesturesManagerInteractionListener; private MapboxMap.OnFpsChangedListener onFpsChangedListener; private PointF focalPoint; MapboxMap(NativeMapView map, Transform transform, UiSettings ui, TrackingSettings tracking, - MyLocationViewSettings myLocationView, Projection projection, OnRegisterTouchListener listener, + MyLocationViewSettings myLocationView, Projection projection, OnGesturesManagerInteractionListener listener, AnnotationManager annotations, CameraChangeDispatcher cameraChangeDispatcher) { this.nativeMapView = map; this.uiSettings = ui; @@ -88,7 +93,7 @@ public final class MapboxMap { this.myLocationViewSettings = myLocationView; this.annotationManager = annotations.bind(this); this.transform = transform; - this.onRegisterTouchListener = listener; + this.onGesturesManagerInteractionListener = listener; this.cameraChangeDispatcher = cameraChangeDispatcher; } @@ -1845,7 +1850,7 @@ public final class MapboxMap { */ @Deprecated public void setOnScrollListener(@Nullable OnScrollListener listener) { - onRegisterTouchListener.onSetScrollListener(listener); + onGesturesManagerInteractionListener.onSetScrollListener(listener); } /** @@ -1855,7 +1860,7 @@ public final class MapboxMap { * To unset the callback, use null. */ public void addOnScrollListener(@Nullable OnScrollListener listener) { - onRegisterTouchListener.onAddScrollListener(listener); + onGesturesManagerInteractionListener.onAddScrollListener(listener); } /** @@ -1865,7 +1870,7 @@ public final class MapboxMap { * To unset the callback, use null. */ public void removeOnScrollListener(@Nullable OnScrollListener listener) { - onRegisterTouchListener.onRemoveScrollListener(listener); + onGesturesManagerInteractionListener.onRemoveScrollListener(listener); } /** @@ -1877,7 +1882,7 @@ public final class MapboxMap { */ @Deprecated public void setOnFlingListener(@Nullable OnFlingListener listener) { - onRegisterTouchListener.onSetFlingListener(listener); + onGesturesManagerInteractionListener.onSetFlingListener(listener); } /** @@ -1887,7 +1892,7 @@ public final class MapboxMap { * To unset the callback, use null. */ public void addOnFlingListener(@Nullable OnFlingListener listener) { - onRegisterTouchListener.onAddFlingListener(listener); + onGesturesManagerInteractionListener.onAddFlingListener(listener); } /** @@ -1897,7 +1902,98 @@ public final class MapboxMap { * To unset the callback, use null. */ public void removeOnFlingListener(@Nullable OnFlingListener listener) { - onRegisterTouchListener.onRemoveFlingListener(listener); + onGesturesManagerInteractionListener.onRemoveFlingListener(listener); + } + + /** + * Adds a callback that's invoked when the map is moved. + * + * @param listener The callback that's invoked when the map is moved. + */ + public void addOnMoveListener(OnMoveListener listener) { + onGesturesManagerInteractionListener.onAddMoveListener(listener); + } + + /** + * Removes a callback that's invoked when the map is moved. + * + * @param listener The callback that's invoked when the map is moved. + */ + public void removeOnMoveListener(OnMoveListener listener) { + onGesturesManagerInteractionListener.onRemoveMoveListener(listener); + } + + /** + * Adds a callback that's invoked when the map is rotated. + * + * @param listener The callback that's invoked when the map is rotated. + */ + public void addOnRotateListener(OnRotateListener listener) { + onGesturesManagerInteractionListener.onAddRotateListener(listener); + } + + /** + * Removes a callback that's invoked when the map is rotated. + * + * @param listener The callback that's invoked when the map is rotated. + */ + public void removeOnRotateListener(OnRotateListener listener) { + onGesturesManagerInteractionListener.onRemoveRotateListener(listener); + } + + /** + * Adds a callback that's invoked when the map is scaled. + * + * @param listener The callback that's invoked when the map is scaled. + */ + public void addOnScaleListener(OnScaleListener listener) { + onGesturesManagerInteractionListener.onAddScaleListener(listener); + } + + /** + * Removes a callback that's invoked when the map is scaled. + * + * @param listener The callback that's invoked when the map is scaled. + */ + public void removeOnScaleListener(OnScaleListener listener) { + onGesturesManagerInteractionListener.onRemoveScaleListener(listener); + } + + /** + * Adds a callback that's invoked when the map is tilted. + * + * @param listener The callback that's invoked when the map is tilted. + */ + public void addOnShoveListener(OnShoveListener listener) { + onGesturesManagerInteractionListener.onAddShoveListener(listener); + } + + /** + * Remove a callback that's invoked when the map is tilted. + * + * @param listener The callback that's invoked when the map is tilted. + */ + public void removeOnShoveListener(OnShoveListener listener) { + onGesturesManagerInteractionListener.onRemoveShoveListener(listener); + } + + /** + * Sets a custom {@link AndroidGesturesManager} to handle {@link android.view.MotionEvent}s registered by the map. + * + * @param androidGesturesManager Gestures manager that interprets gestures based on the motion events. + * @see mapbox-gestures-android library + */ + public void setGesturesManager(AndroidGesturesManager androidGesturesManager) { + onGesturesManagerInteractionListener.setGesturesManager(androidGesturesManager); + } + + /** + * Get current {@link AndroidGesturesManager} that handles {@link android.view.MotionEvent}s registered by the map. + * + * @return Current gestures manager. + */ + public AndroidGesturesManager getGesturesManager() { + return onGesturesManagerInteractionListener.getGesturesManager(); } /** @@ -1909,7 +2005,7 @@ public final class MapboxMap { */ @Deprecated public void setOnMapClickListener(@Nullable OnMapClickListener listener) { - onRegisterTouchListener.onSetMapClickListener(listener); + onGesturesManagerInteractionListener.onSetMapClickListener(listener); } /** @@ -1919,7 +2015,7 @@ public final class MapboxMap { * To unset the callback, use null. */ public void addOnMapClickListener(@Nullable OnMapClickListener listener) { - onRegisterTouchListener.onAddMapClickListener(listener); + onGesturesManagerInteractionListener.onAddMapClickListener(listener); } /** @@ -1929,7 +2025,7 @@ public final class MapboxMap { * To unset the callback, use null. */ public void removeOnMapClickListener(@Nullable OnMapClickListener listener) { - onRegisterTouchListener.onRemoveMapClickListener(listener); + onGesturesManagerInteractionListener.onRemoveMapClickListener(listener); } /** @@ -1941,7 +2037,7 @@ public final class MapboxMap { */ @Deprecated public void setOnMapLongClickListener(@Nullable OnMapLongClickListener listener) { - onRegisterTouchListener.onSetMapLongClickListener(listener); + onGesturesManagerInteractionListener.onSetMapLongClickListener(listener); } /** @@ -1951,7 +2047,7 @@ public final class MapboxMap { * To unset the callback, use null. */ public void addOnMapLongClickListener(@Nullable OnMapLongClickListener listener) { - onRegisterTouchListener.onAddMapLongClickListener(listener); + onGesturesManagerInteractionListener.onAddMapLongClickListener(listener); } /** @@ -1961,7 +2057,7 @@ public final class MapboxMap { * To unset the callback, use null. */ public void removeOnMapLongClickListener(@Nullable OnMapLongClickListener listener) { - onRegisterTouchListener.onRemoveMapLongClickListener(listener); + onGesturesManagerInteractionListener.onRemoveMapLongClickListener(listener); } /** @@ -2220,7 +2316,9 @@ public final class MapboxMap { * Interface definition for a callback to be invoked when the map is scrolled. * * @see MapboxMap#setOnScrollListener(OnScrollListener) + * @deprecated Use {@link OnMoveListener} instead. */ + @Deprecated public interface OnScrollListener { /** * Called when the map is scrolled. @@ -2228,6 +2326,58 @@ public final class MapboxMap { void onScroll(); } + /** + * Interface definition for a callback to be invoked when the map is moved. + * + * @see MapboxMap#addOnMoveListener(OnMoveListener) + */ + public interface OnMoveListener { + void onMoveBegin(MoveGestureDetector detector); + + void onMove(MoveGestureDetector detector); + + void onMoveEnd(MoveGestureDetector detector); + } + + /** + * Interface definition for a callback to be invoked when the map is rotated. + * + * @see MapboxMap#addOnRotateListener(OnRotateListener) + */ + public interface OnRotateListener { + void onRotateBegin(RotateGestureDetector detector); + + void onRotate(RotateGestureDetector detector); + + void onRotateEnd(RotateGestureDetector detector); + } + + /** + * Interface definition for a callback to be invoked when the map is scaled. + * + * @see MapboxMap#addOnScaleListener(OnScaleListener) + */ + public interface OnScaleListener { + void onScaleBegin(StandardScaleGestureDetector detector); + + void onScale(StandardScaleGestureDetector detector); + + void onScaleEnd(StandardScaleGestureDetector detector); + } + + /** + * Interface definition for a callback to be invoked when the map is tilted. + * + * @see MapboxMap#addOnShoveListener(OnShoveListener) + */ + public interface OnShoveListener { + void onShoveBegin(ShoveGestureDetector detector); + + void onShove(ShoveGestureDetector detector); + + void onShoveEnd(ShoveGestureDetector detector); + } + /** * Interface definition for a callback to be invoked when the camera changes position. * @@ -2330,7 +2480,7 @@ public final class MapboxMap { * Interface definition for a callback to be invoked when a user registers an listener that is * related to touch and click events. */ - interface OnRegisterTouchListener { + interface OnGesturesManagerInteractionListener { void onSetMapClickListener(OnMapClickListener listener); void onAddMapClickListener(OnMapClickListener listener); @@ -2354,6 +2504,26 @@ public final class MapboxMap { void onAddFlingListener(OnFlingListener listener); void onRemoveFlingListener(OnFlingListener listener); + + void onAddMoveListener(OnMoveListener listener); + + void onRemoveMoveListener(OnMoveListener listener); + + void onAddRotateListener(OnRotateListener listener); + + void onRemoveRotateListener(OnRotateListener listener); + + void onAddScaleListener(OnScaleListener listener); + + void onRemoveScaleListener(OnScaleListener listener); + + void onAddShoveListener(OnShoveListener listener); + + void onRemoveShoveListener(OnShoveListener listener); + + AndroidGesturesManager getGesturesManager(); + + void setGesturesManager(AndroidGesturesManager gesturesManager); } /** diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java index 84a601039..43c943a16 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java @@ -205,6 +205,8 @@ final class Transform implements MapView.OnMapChangedListener { // cancel ongoing transitions mapView.cancelTransitions(); + + cameraChangeDispatcher.onCameraIdle(); } @UiThread @@ -235,39 +237,37 @@ final class Transform implements MapView.OnMapChangedListener { return mapView.getZoom(); } - void zoom(boolean zoomIn, @NonNull PointF focalPoint) { + void zoomIn(@NonNull PointF focalPoint) { CameraPosition cameraPosition = invalidateCameraPosition(); if (cameraPosition != null) { - int newZoom = (int) Math.round(cameraPosition.zoom + (zoomIn ? 1 : -1)); - setZoom(newZoom, focalPoint, MapboxConstants.ANIMATION_DURATION, false); - } else { - // we are not transforming, notify about being idle - cameraChangeDispatcher.onCameraIdle(); + int newZoom = (int) Math.round(cameraPosition.zoom + 1); + setZoom(newZoom, focalPoint, MapboxConstants.ANIMATION_DURATION); } } - void zoom(double zoomAddition, @NonNull PointF focalPoint, long duration) { + void zoomOut(@NonNull PointF focalPoint) { CameraPosition cameraPosition = invalidateCameraPosition(); if (cameraPosition != null) { - int newZoom = (int) Math.round(cameraPosition.zoom + zoomAddition); - setZoom(newZoom, focalPoint, duration, false); - } else { - // we are not transforming, notify about being idle - cameraChangeDispatcher.onCameraIdle(); + int newZoom = (int) Math.round(cameraPosition.zoom - 1); + setZoom(newZoom, focalPoint, MapboxConstants.ANIMATION_DURATION); } } + void zoomBy(double zoomAddition, @NonNull PointF focalPoint) { + setZoom(mapView.getZoom() + zoomAddition, focalPoint, 0); + } + void setZoom(double zoom, @NonNull PointF focalPoint) { - setZoom(zoom, focalPoint, 0, false); + setZoom(zoom, focalPoint, 0); } - void setZoom(double zoom, @NonNull PointF focalPoint, long duration, final boolean isAnimator) { + void setZoom(double zoom, @NonNull PointF focalPoint, long duration) { if (mapView != null) { mapView.addOnMapChangedListener(new MapView.OnMapChangedListener() { @Override public void onMapChanged(int change) { if (change == MapView.REGION_DID_CHANGE_ANIMATED) { - if (!isAnimator) { + if (duration > 0) { cameraChangeDispatcher.onCameraIdle(); } mapView.removeOnMapChangedListener(this); @@ -361,10 +361,6 @@ final class Transform implements MapView.OnMapChangedListener { } } - void zoomBy(double z, float x, float y) { - mapView.setZoom(mapView.getZoom() + z, new PointF(x, y), 0); - } - void moveBy(double offsetX, double offsetY, long duration) { if (duration > 0) { mapView.addOnMapChangedListener(new MapView.OnMapChangedListener() { diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml index 1c6a26558..00fc05cf6 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml @@ -6,4 +6,13 @@ 8dp 92dp 18dp + + + 150dp + + + 100dp + + + 0.025dp diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapTouchListenersTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapTouchListenersTest.java index eeb00355b..5de55f47c 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapTouchListenersTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapTouchListenersTest.java @@ -2,8 +2,13 @@ package com.mapbox.mapboxsdk.maps; import android.graphics.PointF; +import com.mapbox.android.gestures.MoveGestureDetector; +import com.mapbox.android.gestures.RotateGestureDetector; +import com.mapbox.android.gestures.ShoveGestureDetector; +import com.mapbox.android.gestures.StandardScaleGestureDetector; import com.mapbox.mapboxsdk.geometry.LatLng; +import org.junit.Before; import org.junit.Test; import static org.mockito.Mockito.mock; @@ -13,16 +18,23 @@ import static org.mockito.Mockito.when; public class MapTouchListenersTest { - @Test - public void onMapClickListenerTest() throws Exception { - LatLng latLng = new LatLng(); - PointF pointF = new PointF(); + private MapGestureDetector mapGestureDetector; + private LatLng latLng; + private PointF pointF; + + @Before + public void setUp() throws Exception { + latLng = new LatLng(); + pointF = new PointF(); Projection projection = mock(Projection.class); when(projection.fromScreenLocation(pointF)).thenReturn(latLng); - MapGestureDetector mapGestureDetector = new MapGestureDetector(null, - null, projection, null, null, null, null); + mapGestureDetector = new MapGestureDetector(null, + null, projection, null, null, null); + } + @Test + public void onMapClickListenerTest() throws Exception { MapboxMap.OnMapClickListener listener = mock(MapboxMap.OnMapClickListener.class); mapGestureDetector.addOnMapClickListener(listener); mapGestureDetector.notifyOnMapClickListeners(pointF); @@ -35,14 +47,6 @@ public class MapTouchListenersTest { @Test public void onMapLongClickListenerTest() throws Exception { - LatLng latLng = new LatLng(); - PointF pointF = new PointF(); - - Projection projection = mock(Projection.class); - when(projection.fromScreenLocation(pointF)).thenReturn(latLng); - MapGestureDetector mapGestureDetector = new MapGestureDetector(null, - null, projection, null, null, null, null); - MapboxMap.OnMapLongClickListener listener = mock(MapboxMap.OnMapLongClickListener.class); mapGestureDetector.addOnMapLongClickListener(listener); mapGestureDetector.notifyOnMapLongClickListeners(pointF); @@ -55,14 +59,6 @@ public class MapTouchListenersTest { @Test public void onFlingListenerTest() throws Exception { - LatLng latLng = new LatLng(); - PointF pointF = new PointF(); - - Projection projection = mock(Projection.class); - when(projection.fromScreenLocation(pointF)).thenReturn(latLng); - MapGestureDetector mapGestureDetector = new MapGestureDetector(null, - null, projection, null, null, null, null); - MapboxMap.OnFlingListener listener = mock(MapboxMap.OnFlingListener.class); mapGestureDetector.addOnFlingListener(listener); mapGestureDetector.notifyOnFlingListeners(); @@ -75,14 +71,6 @@ public class MapTouchListenersTest { @Test public void onScrollListenerTest() throws Exception { - LatLng latLng = new LatLng(); - PointF pointF = new PointF(); - - Projection projection = mock(Projection.class); - when(projection.fromScreenLocation(pointF)).thenReturn(latLng); - MapGestureDetector mapGestureDetector = new MapGestureDetector(null, - null, projection, null, null, null, null); - MapboxMap.OnScrollListener listener = mock(MapboxMap.OnScrollListener.class); mapGestureDetector.addOnScrollListener(listener); mapGestureDetector.notifyOnScrollListeners(); @@ -92,4 +80,88 @@ public class MapTouchListenersTest { mapGestureDetector.notifyOnScrollListeners(); verify(listener, times(1)).onScroll(); } + + @Test + public void onMoveListenerTest() throws Exception { + MapboxMap.OnMoveListener listener = mock(MapboxMap.OnMoveListener.class); + MoveGestureDetector detector = mock(MoveGestureDetector.class); + mapGestureDetector.addOnMoveListener(listener); + mapGestureDetector.notifyOnMoveBeginListeners(detector); + mapGestureDetector.notifyOnMoveListeners(detector); + mapGestureDetector.notifyOnMoveEndListeners(detector); + verify(listener, times(1)).onMoveBegin(detector); + verify(listener, times(1)).onMove(detector); + verify(listener, times(1)).onMoveEnd(detector); + + mapGestureDetector.removeOnMoveListener(listener); + mapGestureDetector.notifyOnMoveBeginListeners(detector); + mapGestureDetector.notifyOnMoveListeners(detector); + mapGestureDetector.notifyOnMoveEndListeners(detector); + verify(listener, times(1)).onMoveBegin(detector); + verify(listener, times(1)).onMove(detector); + verify(listener, times(1)).onMoveEnd(detector); + } + + @Test + public void onRotateListenerTest() throws Exception { + MapboxMap.OnRotateListener listener = mock(MapboxMap.OnRotateListener.class); + RotateGestureDetector detector = mock(RotateGestureDetector.class); + mapGestureDetector.addOnRotateListener(listener); + mapGestureDetector.notifyOnRotateBeginListeners(detector); + mapGestureDetector.notifyOnRotateListeners(detector); + mapGestureDetector.notifyOnRotateEndListeners(detector); + verify(listener, times(1)).onRotateBegin(detector); + verify(listener, times(1)).onRotate(detector); + verify(listener, times(1)).onRotateEnd(detector); + + mapGestureDetector.removeOnRotateListener(listener); + mapGestureDetector.notifyOnRotateBeginListeners(detector); + mapGestureDetector.notifyOnRotateListeners(detector); + mapGestureDetector.notifyOnRotateEndListeners(detector); + verify(listener, times(1)).onRotateBegin(detector); + verify(listener, times(1)).onRotate(detector); + verify(listener, times(1)).onRotateEnd(detector); + } + + @Test + public void onScaleListenerTest() throws Exception { + MapboxMap.OnScaleListener listener = mock(MapboxMap.OnScaleListener.class); + StandardScaleGestureDetector detector = mock(StandardScaleGestureDetector.class); + mapGestureDetector.addOnScaleListener(listener); + mapGestureDetector.notifyOnScaleBeginListeners(detector); + mapGestureDetector.notifyOnScaleListeners(detector); + mapGestureDetector.notifyOnScaleEndListeners(detector); + verify(listener, times(1)).onScaleBegin(detector); + verify(listener, times(1)).onScale(detector); + verify(listener, times(1)).onScaleEnd(detector); + + mapGestureDetector.removeOnScaleListener(listener); + mapGestureDetector.notifyOnScaleBeginListeners(detector); + mapGestureDetector.notifyOnScaleListeners(detector); + mapGestureDetector.notifyOnScaleEndListeners(detector); + verify(listener, times(1)).onScaleBegin(detector); + verify(listener, times(1)).onScale(detector); + verify(listener, times(1)).onScaleEnd(detector); + } + + @Test + public void onShoveListenerTest() throws Exception { + MapboxMap.OnShoveListener listener = mock(MapboxMap.OnShoveListener.class); + ShoveGestureDetector detector = mock(ShoveGestureDetector.class); + mapGestureDetector.addShoveListener(listener); + mapGestureDetector.notifyOnShoveBeginListeners(detector); + mapGestureDetector.notifyOnShoveListeners(detector); + mapGestureDetector.notifyOnShoveEndListeners(detector); + verify(listener, times(1)).onShoveBegin(detector); + verify(listener, times(1)).onShove(detector); + verify(listener, times(1)).onShoveEnd(detector); + + mapGestureDetector.removeShoveListener(listener); + mapGestureDetector.notifyOnShoveBeginListeners(detector); + mapGestureDetector.notifyOnShoveListeners(detector); + mapGestureDetector.notifyOnShoveEndListeners(detector); + verify(listener, times(1)).onShoveBegin(detector); + verify(listener, times(1)).onShove(detector); + verify(listener, times(1)).onShoveEnd(detector); + } } diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java index 5e9f94db2..d61947f00 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java @@ -23,7 +23,7 @@ public class MapboxMapTest { mock(TrackingSettings.class), mock(MyLocationViewSettings.class), mock(Projection.class), - mock(MapboxMap.OnRegisterTouchListener.class), + mock(MapboxMap.OnGesturesManagerInteractionListener.class), mock(AnnotationManager.class), mock(CameraChangeDispatcher.class)); } diff --git a/platform/android/gradle/dependencies.gradle b/platform/android/gradle/dependencies.gradle index 4ef4ae2f7..550f44ee2 100644 --- a/platform/android/gradle/dependencies.gradle +++ b/platform/android/gradle/dependencies.gradle @@ -8,24 +8,26 @@ ext { ] versions = [ - mapboxServices: '3.0.0-beta.2', + mapboxServices : '3.0.0-beta.2', mapboxTelemetry: '3.0.0-beta.1', - supportLib : '25.4.0', - espresso : '3.0.1', - testRunner : '1.0.1', - leakCanary : '1.5.1', - lost : '3.0.4', - junit : '4.12', - mockito : '2.10.0', - robolectric : '3.5.1', - timber : '4.5.1', - okhttp : '3.9.1' + mapboxGestures : '0.1.0-20180227.133736-12', + supportLib : '25.4.0', + espresso : '3.0.1', + testRunner : '1.0.1', + leakCanary : '1.5.1', + lost : '3.0.4', + junit : '4.12', + mockito : '2.10.0', + robolectric : '3.5.1', + timber : '4.5.1', + okhttp : '3.9.1' ] dependenciesList = [ mapboxJavaServices : "com.mapbox.mapboxsdk:mapbox-sdk-services:${versions.mapboxServices}", mapboxJavaGeoJSON : "com.mapbox.mapboxsdk:mapbox-sdk-geojson:${versions.mapboxServices}", mapboxAndroidTelemetry: "com.mapbox.mapboxsdk:mapbox-android-telemetry:${versions.mapboxTelemetry}", + mapboxAndroidGestures : "com.mapbox.mapboxsdk:mapbox-android-gestures:${versions.mapboxGestures}@aar", // for testApp mapboxJavaTurf : "com.mapbox.mapboxsdk:mapbox-sdk-turf:${versions.mapboxServices}", From 2dddcba3fc6f99de3046a6b5c69651a690b58509 Mon Sep 17 00:00:00 2001 From: Molly Lloyd Date: Tue, 27 Feb 2018 15:12:25 -0800 Subject: [PATCH 075/264] [ios, macos] cherry-pick darwin changes to tileset encoding param onto boba (#11324) * [ios, macos] add tileset encoding option to darwin sources (#11274) * add tileset encoding option to darwin sources * correct style guide * link out to dem encoding docs * markdownify comments * correct changelog --- .../docs/guides/For Style Authors.md.ejs | 1 + platform/darwin/src/MGLTileSource.h | 30 +++++++++++++++++++ platform/darwin/src/MGLTileSource.mm | 18 +++++++++++ platform/darwin/test/MGLTileSetTests.mm | 17 +++++++++++ platform/ios/CHANGELOG.md | 2 +- platform/ios/docs/guides/For Style Authors.md | 1 + .../macos/docs/guides/For Style Authors.md | 1 + 7 files changed, 69 insertions(+), 1 deletion(-) diff --git a/platform/darwin/docs/guides/For Style Authors.md.ejs b/platform/darwin/docs/guides/For Style Authors.md.ejs index 37af150ac..2ba7757fd 100644 --- a/platform/darwin/docs/guides/For Style Authors.md.ejs +++ b/platform/darwin/docs/guides/For Style Authors.md.ejs @@ -205,6 +205,7 @@ In style JSON | In TileJSON | In the SDK `tileSize` | — | `MGLTileSourceOptionTileSize` `attribution` | `attribution` | `MGLTileSourceOptionAttributionHTMLString` (but consider specifying `MGLTileSourceOptionAttributionInfos` instead for improved security) `scheme` | `scheme` | `MGLTileSourceOptionTileCoordinateSystem` +`encoding` | – | `MGLTileSourceOptionDEMEncoding` ### Shape sources diff --git a/platform/darwin/src/MGLTileSource.h b/platform/darwin/src/MGLTileSource.h index 3dc268db6..2d75fa14d 100644 --- a/platform/darwin/src/MGLTileSource.h +++ b/platform/darwin/src/MGLTileSource.h @@ -117,6 +117,7 @@ extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionAttributionInfos; */ extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionTileCoordinateSystem; + /** Tile coordinate systems that determine how tile coordinates in tile URLs are interpreted. @@ -141,6 +142,35 @@ typedef NS_ENUM(NSUInteger, MGLTileCoordinateSystem) { MGLTileCoordinateSystemTMS }; + +/** + An `NSNumber` object containing an unsigned integer that specifies the encoding + formula for raster-dem tilesets. The integer corresponds to one of + the constants described in `MGLDEMEncoding`. + + The default value for this option is `MGLDEMEncodingMapbox`. + + This option is not supported by the TileJSON spec. + */ +extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionDEMEncoding; + +/** + The encoding formula used to generate the raster-dem tileset +*/ + +typedef NS_ENUM(NSUInteger, MGLDEMEncoding) { + + /** + Raster tiles generated with the [Mapbox encoding formula](https://www.mapbox.com/help/access-elevation-data/#mapbox-terrain-rgb). + */ + MGLDEMEncodingMapbox = 0, + + /** + Raster tiles generated with the [Mapzen Terrarium encoding formula](https://aws.amazon.com/public-datasets/terrain/). + */ + MGLDEMEncodingTerrarium +}; + /** `MGLTileSource` is a map content source that supplies map tiles to be shown on the map. The location of and metadata about the tiles are defined either by an diff --git a/platform/darwin/src/MGLTileSource.mm b/platform/darwin/src/MGLTileSource.mm index bc985bd72..c37812ab8 100644 --- a/platform/darwin/src/MGLTileSource.mm +++ b/platform/darwin/src/MGLTileSource.mm @@ -19,6 +19,7 @@ const MGLTileSourceOption MGLTileSourceOptionCoordinateBounds = @"MGLTileSourceO const MGLTileSourceOption MGLTileSourceOptionAttributionHTMLString = @"MGLTileSourceOptionAttributionHTMLString"; const MGLTileSourceOption MGLTileSourceOptionAttributionInfos = @"MGLTileSourceOptionAttributionInfos"; const MGLTileSourceOption MGLTileSourceOptionTileCoordinateSystem = @"MGLTileSourceOptionTileCoordinateSystem"; +const MGLTileSourceOption MGLTileSourceOptionDEMEncoding = @"MGLTileSourceOptionDEMEncoding"; @implementation MGLTileSource @@ -129,5 +130,22 @@ mbgl::Tileset MGLTileSetFromTileURLTemplates(NS_ARRAY_OF(NSString *) *tileURLTem } } + if (NSNumber *demEncodingNumber = options[MGLTileSourceOptionDEMEncoding]) { + if (![demEncodingNumber isKindOfClass:[NSValue class]]) { + [NSException raise:NSInvalidArgumentException + format:@"MGLTileSourceOptionDEMEncoding must be set to an NSValue or NSNumber."]; + } + MGLDEMEncoding demEncoding; + [demEncodingNumber getValue:&demEncoding]; + switch (demEncoding) { + case MGLDEMEncodingMapbox: + tileSet.encoding = mbgl::Tileset::DEMEncoding::Mapbox; + break; + case MGLDEMEncodingTerrarium: + tileSet.encoding = mbgl::Tileset::DEMEncoding::Terrarium; + break; + } + } + return tileSet; } diff --git a/platform/darwin/test/MGLTileSetTests.mm b/platform/darwin/test/MGLTileSetTests.mm index 4d5e1fcd0..74c84184e 100644 --- a/platform/darwin/test/MGLTileSetTests.mm +++ b/platform/darwin/test/MGLTileSetTests.mm @@ -102,6 +102,23 @@ // the scheme is reflected by the mbgl tileset XCTAssertEqual(tileSet.scheme, mbgl::Tileset::Scheme::TMS); + + // when the dem enciding is changed using an NSNumber + tileSet = MGLTileSetFromTileURLTemplates(tileURLTemplates, @{ + MGLTileSourceOptionDEMEncoding: @(MGLDEMEncodingTerrarium), + }); + + // the encoding is reflected by the mbgl tileset + XCTAssertEqual(tileSet.encoding, mbgl::Tileset::DEMEncoding::Terrarium); + + // when the dem enciding is changed using an NSValue + MGLDEMEncoding terrarium = MGLDEMEncodingTerrarium; + tileSet = MGLTileSetFromTileURLTemplates(tileURLTemplates, @{ + MGLTileSourceOptionDEMEncoding: [NSValue value:&terrarium withObjCType:@encode(MGLDEMEncoding)], + }); + + // the encoding is reflected by the mbgl tileset + XCTAssertEqual(tileSet.encoding, mbgl::Tileset::DEMEncoding::Terrarium); } - (void)testInvalidTileSet { diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 7e82bf7dc..739ff50a2 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -18,7 +18,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * A style can now display smooth hillshading and customize its appearance at runtime using the `MGLHillshadeStyleLayer` class. Hillshading is based on a rasterized digital elevation model supplied by the `MGLRasterDEMSource` class. ([#10642](https://github.com/mapbox/mapbox-gl-native/pull/10642)) * The `MGLSymbolStyleLayer.textFontNames` property can now depend on a feature’s attributes. ([#10850](https://github.com/mapbox/mapbox-gl-native/pull/10850)) * Added `MGLShapeSourceOptionWrapsCoordinates`, to specify whether the shape of an `MGLComputedShapeSource` should be wrapped to accommodate coordinates with longitudes beyond −180 and 180; and `MGLShapeSourceOptionClipsCoordinates`, to specify whether the shape of an `MGLComputedShapeSource` should be clipped at the edge of each tile. ([#11041](https://github.com/mapbox/mapbox-gl-native/pull/11041)) -* Added support for Mapzen Terrarium DEM encoding. ([#11154](https://github.com/mapbox/mapbox-gl-native/pull/11154)) +* Added support for Mapzen Terrarium DEM encoding. ([#11274](https://github.com/mapbox/mapbox-gl-native/pull/11274)) ### Map rendering diff --git a/platform/ios/docs/guides/For Style Authors.md b/platform/ios/docs/guides/For Style Authors.md index 02d23067e..1f2803f3e 100644 --- a/platform/ios/docs/guides/For Style Authors.md +++ b/platform/ios/docs/guides/For Style Authors.md @@ -154,6 +154,7 @@ In style JSON | In TileJSON | In the SDK `tileSize` | — | `MGLTileSourceOptionTileSize` `attribution` | `attribution` | `MGLTileSourceOptionAttributionHTMLString` (but consider specifying `MGLTileSourceOptionAttributionInfos` instead for improved security) `scheme` | `scheme` | `MGLTileSourceOptionTileCoordinateSystem` +`encoding` | – | `MGLTileSourceOptionDEMEncoding` ### Shape sources diff --git a/platform/macos/docs/guides/For Style Authors.md b/platform/macos/docs/guides/For Style Authors.md index de838cd78..636cfa7eb 100644 --- a/platform/macos/docs/guides/For Style Authors.md +++ b/platform/macos/docs/guides/For Style Authors.md @@ -141,6 +141,7 @@ In style JSON | In TileJSON | In the SDK `tileSize` | — | `MGLTileSourceOptionTileSize` `attribution` | `attribution` | `MGLTileSourceOptionAttributionHTMLString` (but consider specifying `MGLTileSourceOptionAttributionInfos` instead for improved security) `scheme` | `scheme` | `MGLTileSourceOptionTileCoordinateSystem` +`encoding` | – | `MGLTileSourceOptionDEMEncoding` ### Shape sources From 66609c975108d44483f4976dc2d73bd26849fac6 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Thu, 15 Feb 2018 09:51:34 -0800 Subject: [PATCH 076/264] [ios] Add "clear tile cache on entering background" to changelog. --- platform/ios/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 739ff50a2..9eb522f2b 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -29,6 +29,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Fixed an issue preventing `MGLImageSource`s from drawing on the map when the map is zoomed in and tilted. ([#10677](https://github.com/mapbox/mapbox-gl-native/pull/10677)) * Improved the sharpness of raster tiles on Retina displays. ([#10984](https://github.com/mapbox/mapbox-gl-native/pull/10984)) * Fixed a crash parsing a malformed style. ([#11001](https://github.com/mapbox/mapbox-gl-native/pull/11001)) +* Reduced memory usage by clearing in-memory tile cache before entering background. ([#11197](https://github.com/mapbox/mapbox-gl-native/pull/11197)) ### Annotations From 68a9e2ea926db80010ade77cda77ced53d412b28 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Fri, 5 Jan 2018 04:06:44 -0500 Subject: [PATCH 077/264] [ios] Provide haptic feedback when user rotates to due north --- platform/ios/CHANGELOG.md | 1 + platform/ios/src/MGLMapView.h | 13 +++++++++++++ platform/ios/src/MGLMapView.mm | 21 +++++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 9eb522f2b..b0f3e741e 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -48,6 +48,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Labels are now transliterated from more languages when VoiceOver is enabled. ([#10881](https://github.com/mapbox/mapbox-gl-native/pull/10881)) * Long-pressing the attribution button causes the SDK’s version number to be displayed in the action sheet that appears. ([#10650](https://github.com/mapbox/mapbox-gl-native/pull/10650)) * Reduced offline download size for styles with symbol layers that render only icons, and no text. ([#11055](https://github.com/mapbox/mapbox-gl-native/pull/11055)) +* Added haptic feedback that occurs when the user rotates the map to due north, configurable via `MGLMapView.hapticFeedbackEnabled`. ([#10847](https://github.com/mapbox/mapbox-gl-native/pull/10847)) ## 3.7.5 - February 16, 2018 diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h index 9c18965b0..44130f3c6 100644 --- a/platform/ios/src/MGLMapView.h +++ b/platform/ios/src/MGLMapView.h @@ -483,6 +483,19 @@ MGL_EXPORT IB_DESIGNABLE */ @property(nonatomic, getter=isPitchEnabled) BOOL pitchEnabled; +/** + A Boolean value that determines whether the user will receive haptic feedback + for certain interactions with the map. + + When this property is set to `YES`, the default, a `UIImpactFeedbackStyleLight` + haptic feedback event be played when the user rotates the map to due north + (0°). + + This feature requires a device that supports haptic feedback, running iOS 10 or + newer. + */ +@property(nonatomic, getter=isHapticFeedbackEnabled) BOOL hapticFeedbackEnabled; + /** A floating-point value that determines the rate of deceleration after the user lifts their finger. diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 1cb477955..bb8948150 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -228,6 +228,7 @@ public: @property (nonatomic) CGFloat quickZoomStart; @property (nonatomic, getter=isDormant) BOOL dormant; @property (nonatomic, readonly, getter=isRotationAllowed) BOOL rotationAllowed; +@property (nonatomic) BOOL shouldTriggerHapticFeedbackForCompass; @property (nonatomic) MGLMapViewProxyAccessibilityElement *mapViewProxyAccessibilityElement; @property (nonatomic) MGLAnnotationContainerView *annotationContainerView; @property (nonatomic) MGLUserLocation *userLocation; @@ -525,6 +526,8 @@ public: [_twoFingerTap requireGestureRecognizerToFail:_twoFingerDrag]; [self addGestureRecognizer:_twoFingerTap]; + _hapticFeedbackEnabled = YES; + _decelerationRate = MGLMapViewDecelerationRateNormal; _quickZoom = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleQuickZoomGesture:)]; @@ -1513,6 +1516,8 @@ public: self.userTrackingMode = MGLUserTrackingModeFollow; } + self.shouldTriggerHapticFeedbackForCompass = NO; + [self notifyGestureDidBegin]; } else if (rotate.state == UIGestureRecognizerStateChanged) @@ -1535,6 +1540,22 @@ public: } [self cameraIsChanging]; + + // Trigger a light haptic feedback event when the user rotates to due north. + if (@available(iOS 10.0, *)) + { + if (self.isHapticFeedbackEnabled && fabs(newDegrees) <= 1 && self.shouldTriggerHapticFeedbackForCompass) + { + UIImpactFeedbackGenerator *hapticFeedback = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight]; + [hapticFeedback impactOccurred]; + + self.shouldTriggerHapticFeedbackForCompass = NO; + } + else if (fabs(newDegrees) > 1) + { + self.shouldTriggerHapticFeedbackForCompass = YES; + } + } } else if (rotate.state == UIGestureRecognizerStateEnded || rotate.state == UIGestureRecognizerStateCancelled) { From ab27e24ab0dd906668d73d35cbece8768dfd02c8 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Fri, 5 Jan 2018 04:08:44 -0500 Subject: [PATCH 078/264] [ios] Restore iosapp's HUD visibility, adjust placement --- platform/ios/app/MBXViewController.m | 9 ++++- platform/ios/app/Main.storyboard | 60 +++++++++++++++------------- 2 files changed, 40 insertions(+), 29 deletions(-) diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m index 3d0abf0a2..5b9363af2 100644 --- a/platform/ios/app/MBXViewController.m +++ b/platform/ios/app/MBXViewController.m @@ -166,7 +166,6 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { self.debugLoggingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsDebugLoggingEnabled"]; self.mapView.scaleBar.hidden = NO; self.mapView.showsUserHeadingIndicator = YES; - self.hudLabel.hidden = YES; if ([UIFont respondsToSelector:@selector(monospacedDigitSystemFontOfSize:weight:)]) { self.hudLabel.titleLabel.font = [UIFont monospacedDigitSystemFontOfSize:10 weight:UIFontWeightRegular]; } @@ -216,6 +215,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { [defaults setInteger:self.mapView.userTrackingMode forKey:@"MBXUserTrackingMode"]; [defaults setBool:self.mapView.showsUserLocation forKey:@"MBXShowsUserLocation"]; [defaults setInteger:self.mapView.debugMask forKey:@"MBXDebugMask"]; + [defaults setBool:self.showZoomLevelEnabled forKey:@"MBXShowsZoomLevelHUD"]; [defaults synchronize]; } @@ -241,6 +241,11 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { { self.mapView.debugMask = (MGLMapDebugMaskOptions)uncheckedDebugMask; } + if ([defaults boolForKey:@"MBXShowsZoomLevelHUD"]) + { + self.showZoomLevelEnabled = YES; + [self updateHUD]; + } } - (UIInterfaceOrientationMask)supportedInterfaceOrientations @@ -1898,6 +1903,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { - (void)updateHUD { if (!self.reuseQueueStatsEnabled && !self.showZoomLevelEnabled) return; + if (self.hudLabel.hidden) self.hudLabel.hidden = NO; + NSString *hudString; if (self.reuseQueueStatsEnabled) { diff --git a/platform/ios/app/Main.storyboard b/platform/ios/app/Main.storyboard index 507582213..04e4f9ab4 100644 --- a/platform/ios/app/Main.storyboard +++ b/platform/ios/app/Main.storyboard @@ -1,11 +1,11 @@ - + - + @@ -26,40 +26,44 @@ + + + + + + + + - - - - @@ -133,14 +137,14 @@