Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Route Duration Annotations #2734

Merged
merged 24 commits into from
Mar 5, 2021
Merged

Route Duration Annotations #2734

merged 24 commits into from
Mar 5, 2021

Conversation

avi-c
Copy link
Contributor

@avi-c avi-c commented Nov 10, 2020

Add callout annotations showing route durations for comparing alternate routes during preview.

Will include noting which routes have tolls when that information is available.

@avi-c avi-c self-assigned this Nov 10, 2020
@avi-c avi-c added the ⚠️ DO NOT MERGE PR should not be merged! label Nov 10, 2020
@avi-c avi-c requested a review from 1ec5 November 10, 2020 02:16
Copy link
Contributor

@1ec5 1ec5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, it really looks the part! A couple initial thoughts:



extension MGLStyle {
func addDebugLineLayer(identifier: String, coordinates: [CLLocationCoordinate2D], color: UIColor = UIColor.purple) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ultimately, we’ll need to move these extensions from the example application to MapboxNavigation so that developers don’t have to copy the whole file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


extension MGLMapView {
func coordinateBoundsInset(_ inset: CGSize) -> MGLCoordinateBounds {
return convert(bounds.insetBy(dx: inset.width, dy: inset.height), toCoordinateBoundsFrom: nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MGLMapView.convert(_:toCoordinateBoundsFrom:) is inaccurate on a tilted map: mapbox/mapbox-gl-native#2259.

More broadly, I think we should investigate positioning the labels based on screen coordinates rather than geographic coordinates. Core GL’s own symbol collision avoidance code is based on projected coordinates, which account for rotation and tilting, so it would make sense to be consistent with core GL labeling on that respect.

Imagine taking the maneuver coordinates for a given route, converting them all to screen points, and stringing them together in a UIBezierPath. Calculating the distance between two points is suddenly just a Pythagorean distance problem.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed this extension since I no longer needed it. I am still doing a lot of my calculations in geographic coordinates, but do filter them to only use ones that are onscreen. Also, when coordinates are chosen for the annotations I use the on-screen position to decide where to put the "stem" of the bubble callout.

@avi-c
Copy link
Contributor Author

avi-c commented Nov 18, 2020

Made a number of updates to my algorithm that simplify it and reduce the total number of needed additions and extensions. I did add the need for an algorithm to simplify a polyline and chose to use the one from: https://github.com/tomislav/Simplify-Swift. I resorted to compiling in the source file since the library is not available through Carthage but we can figure out another solution, including re-implementing the algorithm.

Copy link
Contributor

@MaximAlien MaximAlien left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this PR. I like current look of delay views. In my opinion we should aim for implementation on Navigation SDK side. There are also other changes being made to NavigaitonMapView at this point (route styling, congestion consistency, vanishing route line calculation improvements) and it has become pretty large class. I think it'd make sense to extract certain areas of functionality into separate entities (e.g. at least NavigationMapView extensions).

@@ -94,6 +330,10 @@ class ViewController: UIViewController {
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
appDelegate.currentAppRootViewController = self
}

dateComponentsFormatter.maximumUnitCount = 3
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see that CPTrip extension already has some additional DateComponentsFormatters. Probably would make sense to create extension on MapboxNavigation side for DateComponentsFormatter which contains all properties.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll look at consolidating all of these within MapboxNavigation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved the CPTrip DateComponentsFormatter extensions to a dedicated file within MapboxNavigation and changed to use those instead. Removed the use of a local DateComponentsFormatter within the Route Annotations file.


Useful for debugging or visualizing data.
*/
public func addDebugCircleLayer(identifier: String, coordinate: CLLocationCoordinate2D, color: UIColor = UIColor.purple) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I can see @1ec5 proposed to move these methods to MapboxNavigation. I wonder if we really need these methods here, as they're mostly used for debugging purpose. Probably would make more sense to keep them in Example app, as they're meant to be used mostly by us for troubleshooting?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, they are just for debugging, so I can move them around, but we can also drop them from being in the project. I find them very useful but could just keep them in my own repo or gist or something. @1ec5 - Do you think there is a place for this type of debugging code in the actual project or just something to keep for ourselves?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are useful not just for our own debugging, they are a good way to see geo info on the map with just simple coordinates. If @1ec5 thinks it is ok to add debug functions to the SDK then that's fine with me. If not, I would move them to the Example app and leave them there.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn’t realize they were debugging methods when I suggested moving them to MapboxNavigation. It makes sense to keep them limited to the example application. Sorry for the confusion.


private let annotationLayerIdentifier = "RouteETAAnnotations"

private func addRouteAnnotationSymbolLayer(features: [MGLPointFeature], style: MGLStyle) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we plan to integrate this functionality to MapboxNavigation it'd probably make sense to move layer addition to NavigationMapView.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I move this PR to come on top of some of the other work you have in progress? What would you suggest as a way to coordinate this rework/refactor of the NavigationMapView class?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately there are multiple PRs which contain changes to NavigationMapView: #2741, #2737 and #2719. Easiest way would be to rebase onto main for now. I can then help with tackling conflicts (if there are any) after mentioned PRs are merged into main.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that this extensive runtime styling code should go in NavigationMapView so developers can take advantage of it instead of having to roll their own annotations.

trueExpression: NSExpression(forConstantValue: UIColor.white),
falseExpression: NSExpression(forConstantValue: UIColor.black))

shapeLayer.textFontNames = NSExpression(forConstantValue: ["DIN Pro Medium"])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I can see on low zoom levels shape size of MGLSymbolStyleLayer will stay the same. Shall we consider a possibility of changing delay view size depending on zoom level. E.g. I was experimenting with iconScale and iconOpacity in #2634.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some good discussions to have on exactly what visual treatment we want. I did make the text size vary by zoom as I saw that being done in some other nav apps.

I'd prefer to investigate the designs in a separate ticket later though since it's bound to take a bit longer to come to agreement on the details.

}
}

private func updateRouteAnnotations(_ routes: [Route]?, style: MGLStyle) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is making pretty heavy computation each time. I'd probably consider outlining only specific actions when we should update delay view location (e.g. it doesn't make sense to show delay view on low zoom levels, since route is not really visible anyway). We can also move certain calculations to background thread.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@1ec5 @MaximAlien - This brings up a question I have. Can we perform map style updates from a background thread?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I don't think this is actually that heavy in terms of performance. I profiled several recomputations of the route annotation coordinates by repeatedly scrolling the map view (which causes a re-layout) and the time spent in this routine is only about 1% of the CPU time. It also will only happen when the map is moved or otherwise redrawn.

It's also much, much smaller than the delay in retrieving the new route from the Directions API so causes only imperceptible delays in the case when a new route is being fetched.

I'd argue it's better just to leave it as is than to try and add in some concurrency.

Hopefully @1ec5 can weigh in on this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be worth moving the calculations to a background thread just in case, but most likely the reason it hasn’t come up as a performance bottleneck yet is that it only runs once per camera change and not continuously (which is fine).


private func updateRouteAnnotations(_ routes: [Route]?, style: MGLStyle) {
// remove any existing route annotation
removeRouteAnnotationsLayerFromStyle(style)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When route is selected ViewController.response will trigger clearMapView(), which will in turn clear delay view. Since this feature is going to be part of MapboxNavigation it makes sense to move existing logic into NavigationMapView instead of improving implementation in Example app.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be also good to rebase current changes onto main as it now contains at least one fix to example app which can be useful here (previously after pressing on route clear button would disappear).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Completed the rebase.

// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import Foundation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I'm not really fond of storing 3rdparty sources as is. Not sure it's good idea to have separate dependency either. It'd be great if we can have our own implementation of simplification functionality.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. It isn't something I am very familiar with though. @1ec5 - Is there a mapbox implementation that we could port over to the Nav SDK so I could drop any form of dependency?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will move to a simplify algorithm I added to Turf: mapbox/turf-swift#124
Once that PR is approved and merged I will update the Turf version in Nav SDK and use it instead of another dependency.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turf v1.2.0-beta.1 has been released. MapboxDirections doesn’t require it yet, but you can have the navigation SDK require it by modifying the Cartfile in this repository. The CocoaPods integration test will fail until there’s a final release of Turf v1.2.0, but at least you’ll be able to prepare this PR for the moment when that happens.

diff --git a/Cartfile b/Cartfile
index ba3e3e59..1cf9c2ca 100644
--- a/Cartfile
+++ b/Cartfile
@@ -3,7 +3,7 @@ binary "https://api.mapbox.com/downloads/v2/carthage/mobile-navigation-native/Ma
 binary "https://api.mapbox.com/downloads/v2/carthage/mobile-maps/mapbox-ios-sdk-dynamic.json" ~> 6.0
 binary "https://api.mapbox.com/downloads/v2/carthage/mapbox-common/MapboxCommon-ios.json" == 7.1.2
 github "mapbox/mapbox-directions-swift" ~> 1.0
-github "mapbox/turf-swift" ~> 1.0
+github "mapbox/turf-swift" ~> 1.2.0-beta.1
 github "mapbox/mapbox-events-ios" ~> 0.10.2
 github "ceeK/Solar" ~> 2.1.0
 github "mapbox/mapbox-speech-swift" ~> 1.0
diff --git a/Cartfile.resolved b/Cartfile.resolved
index 1b02b2c1..6042bedd 100644
--- a/Cartfile.resolved
+++ b/Cartfile.resolved
@@ -10,8 +10,8 @@ github "ceeK/Solar" "2.1.0"
 github "linksmt/OHHTTPStubs" "563f48d3fab84ef04639649c770b00f4fa502cca"
 github "mapbox/MapboxGeocoder.swift" "v0.10.2"
 github "mapbox/mapbox-directions-swift" "v1.1.0"
-github "mapbox/mapbox-events-ios" "v0.10.6"
+github "mapbox/mapbox-events-ios" "v0.10.7"
 github "mapbox/mapbox-speech-swift" "v1.0.0"
-github "mapbox/turf-swift" "v1.1.0"
+github "mapbox/turf-swift" "v1.2.0-beta.1"
 github "raphaelmor/Polyline" "v5.0.2"
 github "uber/ios-snapshot-test-case" "5.0.2"

@avi-c
Copy link
Contributor Author

avi-c commented Dec 17, 2020

@MaximAlien @1ec5 - I reworked the algorithm a bit because I wasn't satisfied with how the annotations were landing.

It has a number of steps but should actually be much simpler computation wise since it is mainly things like coordinate comparisons, point in polygon checks, and a few closestCoordinateTo computations.

See what you think. I tried to make sure the comments were giving a clear step-by-step overview of what is happening.

@avi-c avi-c removed the ⚠️ DO NOT MERGE PR should not be merged! label Dec 21, 2020
@avi-c
Copy link
Contributor Author

avi-c commented Jan 11, 2021

@1ec5 @MaximAlien - I'll fix the merge conflicts but would love to get this merged and out of the way this week. If there are changes you'd like to see please update and I'll address those too.

@avi-c avi-c force-pushed the ac-RouteDurationAnnotations branch from 6f68946 to 5ed8211 Compare January 11, 2021 21:52

private let annotationLayerIdentifier = "RouteETAAnnotations"

private func addRouteAnnotationSymbolLayer(features: [MGLPointFeature], style: MGLStyle) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that this extensive runtime styling code should go in NavigationMapView so developers can take advantage of it instead of having to roll their own annotations.

Example/ViewController.swift Outdated Show resolved Hide resolved
Example/ViewController.swift Outdated Show resolved Hide resolved
Example/ViewController.swift Outdated Show resolved Hide resolved
Example/ViewController.swift Outdated Show resolved Hide resolved
// Take care to snap that coordinate to one that lays on the original route line.
// If the chosen snapped coordinate is not visible onthe screen, then we walk back along the route coordinates looking for one that is.
// If none of the earlier points are on screen then we walk forward along the route coordinates until we find one that is.
if let distance = continuousLine.distance(), let sampleCoordinate = continuousLine.indexedCoordinateFromStart(distance: distance * CLLocationDistance.random(in: 0.3...0.8))?.coordinate, let routeShape = route.shape, let snappedCoordinate = routeShape.closestCoordinate(to: sampleCoordinate) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What’s the significance of the start and end ranges of this random distance? Should it depend on the zoom level or the style? Does the range hold up better in some kinds of routes versus others?

I’m still concerned that, by focusing on geographic coordinates rather than screen coordinates as suggested in #2734 (comment), the whole algorithm is vulnerable to a variety of factors that are hard to control for at this high level of the stack.

In particular, LineString.indexedCoordinateFromStart(distance:) can only return one of the route line’s vertices, but there’s no guarantee about the density of vertices along the route line. For example, in some places in OpenStreetMap, a vertex may occur only once every quarter mile at an intersection, while in other places, it may occur every few car lengths because turn lanes and parking restrictions have been mapped in fine detail.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intent is to pick a coordinate along the line some distance away from the start and end points of the route, but with some variation of the distance. The goal is to not hard code something like halfway through the route.

I went with the indexed coordinate in order to reduce the computation burden of calculating a point along the line somewhere between two vertices. It might be that the overall algorithm is simplified enough that we wouldn't need to be calculating as many points and it would be worth it.

In my testing, the annotations generally work out very well, but do sometimes collide. I suspect that the code required to calculate things in screen space so that they would never collide would be significantly more complicated than just doing the geographic calculations.

My hope is that the logic we have is good enough for most of the time and that any customer who found the collision avoidance to not work to their liking would just figure out what works best for them.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At a glance, probably the biggest risk at the moment would be that the map happens to be rotated or tilted differently than the positioning code was designed for. As long as these degrees of freedom are possible, it’ll remain difficult to completely account for them in geographic coordinate space. But to the extent that this poses a problem, we can treat it as tail work, perhaps awaiting new map SDK functionality to make such calculations easier.

Example/ViewController.swift Outdated Show resolved Hide resolved
Example/ViewController.swift Outdated Show resolved Hide resolved
Example/ViewController.swift Outdated Show resolved Hide resolved
@avi-c avi-c force-pushed the ac-RouteDurationAnnotations branch from 5ed8211 to dec8a5d Compare January 16, 2021 04:27
@avi-c
Copy link
Contributor Author

avi-c commented Jan 16, 2021

Responded to the review feedback, including moving the annotations from the Example project over to NavigationMapView.

MapboxNavigation/DayStyle.swift Outdated Show resolved Hide resolved
MapboxNavigation/NavigationMapView.swift Outdated Show resolved Hide resolved
@@ -958,6 +960,226 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate {
SourceIdentifier.arrowSymbol,
].compactMap { style.source(withIdentifier: $0) }))
}

public func showRouteDurationAnnotations(_ routes: [Route]?) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this something that should always happen when showing multiple routes? If so, maybe we can make it an optional argument to show(_:) instead? Either way, remember to document whatever you make public.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some documentation.

Right now the user will need to call the showRouteDurationAnnotations function themselves. I did it this way since I was trying to minimize the changes I was making to existing behavior and wanted to give people more fine-grained control on when the annotations show up, when they are refreshed, and when they are removed.

Not sure if that's the best way to go but it fits my "low impact" intention.

MapboxNavigation/NavigationMapView.swift Outdated Show resolved Hide resolved
// pick the orientation of the bubble "stem" based on how close to the edge of the screen it is
if tailPosition == .left && unprojectedCoordinate.x > bounds.width * 0.75 {
tailPosition = .right
} else if tailPosition == .right && unprojectedCoordinate.x < bounds.width * 0.25 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bounds may be inset by an arbitrary amount, depending on the application’s design. Should we track accounting for the content insets as tail work?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened #2785 to track this future investigation.

MapboxNavigation/NavigationMapView.swift Outdated Show resolved Hide resolved
MapboxNavigation/NavigationMapView.swift Outdated Show resolved Hide resolved
// Take care to snap that coordinate to one that lays on the original route line.
// If the chosen snapped coordinate is not visible onthe screen, then we walk back along the route coordinates looking for one that is.
// If none of the earlier points are on screen then we walk forward along the route coordinates until we find one that is.
if let distance = continuousLine.distance(), let sampleCoordinate = continuousLine.indexedCoordinateFromStart(distance: distance * CLLocationDistance.random(in: 0.3...0.8))?.coordinate, let routeShape = route.shape, let snappedCoordinate = routeShape.closestCoordinate(to: sampleCoordinate) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At a glance, probably the biggest risk at the moment would be that the map happens to be rotated or tilted differently than the positioning code was designed for. As long as these degrees of freedom are possible, it’ll remain difficult to completely account for them in geographic coordinate space. But to the extent that this poses a problem, we can treat it as tail work, perhaps awaiting new map SDK functionality to make such calculations easier.

MapboxNavigation/Route.swift Outdated Show resolved Hide resolved

extension Turf.BoundingBox {
init(coordinateBounds: MGLCoordinateBounds) {
self.init(coordinateBounds.sw, coordinateBounds.ne)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I somehow missed that the BoundingBox initializer has unlabeled arguments. We should fix that independently of this PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was fixed in mapbox/turf-swift#132, which will be coming in v2.x.

let imageScale = scale
let contextBounds = CGRect(origin: .zero, size: imageSize)

UIGraphicsBeginImageContextWithOptions(imageSize, false, imageScale)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the way, UIGraphicsImageRenderer is also available on iOS 10 and above. It does have some shortcomings compared to the C-style UIGraphicsBeginImageContextWithOptions(_:_:_:) around transformations, but we’re already using it in several parts of the SDK.

MapboxNavigation/Array.swift Outdated Show resolved Hide resolved
Sources/MapboxNavigation/NavigationMapView.swift Outdated Show resolved Hide resolved
Sources/MapboxNavigation/NavigationMapView.swift Outdated Show resolved Hide resolved
Sources/MapboxNavigation/NavigationMapView.swift Outdated Show resolved Hide resolved

extension Turf.BoundingBox {
init(coordinateBounds: MGLCoordinateBounds) {
self.init(coordinateBounds.sw, coordinateBounds.ne)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was fixed in mapbox/turf-swift#132, which will be coming in v2.x.

Copy link
Contributor

@1ec5 1ec5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some minor stylistic suggestions, documentation notes, and performance optimizations. (GitHub split up the review, so see some comments above too.)

Sources/MapboxNavigation/Array.swift Outdated Show resolved Hide resolved
Sources/MapboxNavigation/MGLStyle.swift Show resolved Hide resolved
Sources/MapboxNavigation/NavigationMapView.swift Outdated Show resolved Hide resolved
@1ec5 1ec5 added this to the e (android-v1.5.0 / ios-v1.3.0) milestone Mar 5, 2021
@1ec5 1ec5 added feature New feature request. UI Work related to visual components, Android Auto, Camera, 3D, voice, etc. labels Mar 5, 2021
@1ec5 1ec5 mentioned this pull request Mar 5, 2021
avi-c added 23 commits March 5, 2021 10:36
…notations. Set symbol sort order based on index of routes in the array. More comments.
…sider ones that are within the visible map bounds.
…are onscreen. Remove an unneeded extension to MGLMapView.
…from the Example app to MapboxNavigation target.
…le in main Nav SDK. Use these extensions in Route Annotation Anotations and CPTrip. Fix new source files that were omitted from the Example-CarPlay application target.
…le project. Entails also moving a number of class extensions and image resources over as well.
…ute Duration Annotations via the Style mechanism. Add more documentation.
…ame some methods according to style guidelines. Ensure new image resources go into the proper SPM locations.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New feature request. UI Work related to visual components, Android Auto, Camera, 3D, voice, etc.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants