diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2019-06-30 23:12:48 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2019-06-30 23:12:48 +0000 |
commit | 5c702f281408525a19a8a6799055612b387a0241 (patch) | |
tree | 17c5d545238ea11b087a4717886f463514d17882 | |
parent | 3743e8a09c053a23b54cacf24c92f33cce532991 (diff) | |
parent | f4ed44d3ff5f0312a496bc793bb6183812e7406a (diff) | |
download | Cluster-5c702f281408525a19a8a6799055612b387a0241.tar.gz |
Snap for 5699409 from f4ed44d3ff5f0312a496bc793bb6183812e7406a to qt-aml-release
Change-Id: I80adcaefbb619a26f777c6d75ac112f985228306
-rw-r--r-- | Android.mk | 4 | ||||
-rw-r--r-- | AndroidManifest.xml | 2 | ||||
-rw-r--r-- | res/values/dimens.xml | 2 | ||||
-rw-r--r-- | src/android/car/cluster/ClusterRenderingService.java | 79 | ||||
-rw-r--r-- | src/android/car/cluster/CueView.java | 32 | ||||
-rw-r--r-- | src/android/car/cluster/ImageResolver.java | 84 | ||||
-rw-r--r-- | src/android/car/cluster/LaneView.java | 18 | ||||
-rw-r--r-- | src/android/car/cluster/LoggingClusterRenderingService.java | 34 | ||||
-rw-r--r-- | src/android/car/cluster/MainClusterActivity.java | 10 | ||||
-rw-r--r-- | src/android/car/cluster/NavStateController.java | 74 | ||||
-rw-r--r-- | tests/robotests/Android.mk | 6 | ||||
-rw-r--r-- | tests/robotests/src/android/car/cluster/ImageResolverTest.java | 34 |
12 files changed, 192 insertions, 187 deletions
@@ -35,11 +35,13 @@ LOCAL_USE_AAPT2 := true LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res +LOCAL_STATIC_JAVA_LIBRARIES := \ + android.car.cluster.navigation + LOCAL_JAVA_LIBRARIES += android.car LOCAL_STATIC_ANDROID_LIBRARIES += \ androidx.legacy_legacy-support-v4 \ androidx-constraintlayout_constraintlayout \ - androidx.car_car-cluster \ car-arch-common \ car-media-common \ car-telephony-common \ diff --git a/AndroidManifest.xml b/AndroidManifest.xml index fc9246d..7dc2bb6 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -49,6 +49,8 @@ <uses-permission android:name="android.car.permission.CAR_SPEED"/> <uses-permission android:name="android.car.permission.CAR_ENGINE_DETAILED"/> + <uses-permission android:name="android.permission.READ_PHONE_STATE" /> + <application android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:directBootAware="true"> diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 5afdb28..bff5c60 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -103,5 +103,5 @@ <dimen name="user_profile_title_padding_top">@*android:dimen/car_padding_3</dimen> <dimen name="user_profile_body_padding_top">@*android:dimen/car_padding_3</dimen> - <dimen name="large_avatar_icon_size">@dimen/car_large_avatar_size</dimen> + <dimen name="large_avatar_icon_size">96dp</dimen> </resources> diff --git a/src/android/car/cluster/ClusterRenderingService.java b/src/android/car/cluster/ClusterRenderingService.java index 85dacdf..6c924c4 100644 --- a/src/android/car/cluster/ClusterRenderingService.java +++ b/src/android/car/cluster/ClusterRenderingService.java @@ -21,8 +21,7 @@ import static java.lang.Integer.parseInt; import android.app.ActivityOptions; import android.car.CarNotConnectedException; -import android.car.cluster.CarInstrumentClusterManager; -import android.car.cluster.ClusterActivityState; +import android.car.cluster.navigation.NavigationState.NavigationStateProto; import android.car.cluster.renderer.InstrumentClusterRenderingService; import android.car.cluster.renderer.NavigationRenderer; import android.car.navigation.CarNavigationInstrumentCluster; @@ -41,8 +40,7 @@ import android.view.Display; import android.view.InputDevice; import android.view.KeyEvent; -import androidx.car.cluster.navigation.NavigationState; -import androidx.versionedparcelable.ParcelUtils; +import com.google.protobuf.InvalidProtocolBufferException; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -63,7 +61,7 @@ public class ClusterRenderingService extends InstrumentClusterRenderingService i static final int NAV_STATE_EVENT_ID = 1; static final String LOCAL_BINDING_ACTION = "local"; - static final String NAV_STATE_BUNDLE_KEY = "navstate"; + static final String NAV_STATE_PROTO_BUNDLE_KEY = "navstate2"; private List<ServiceClient> mClients = new ArrayList<>(); private ClusterDisplayProvider mDisplayProvider; @@ -73,7 +71,8 @@ public class ClusterRenderingService extends InstrumentClusterRenderingService i public interface ServiceClient { void onKeyEvent(KeyEvent keyEvent); - void onNavigationStateChange(NavigationState navState); + + void onNavigationStateChange(NavigationStateProto navState); } public class LocalBinder extends Binder { @@ -190,30 +189,34 @@ public class ClusterRenderingService extends InstrumentClusterRenderingService i @Override public void onEvent(int eventType, Bundle bundle) { - try { - StringBuilder bundleSummary = new StringBuilder(); - if (eventType == NAV_STATE_EVENT_ID) { - bundle.setClassLoader(ParcelUtils.class.getClassLoader()); - NavigationState navState = NavigationState - .fromParcelable(bundle.getParcelable(NAV_STATE_BUNDLE_KEY)); - bundleSummary.append(navState.toString()); - - // Update clients - broadcastClientEvent(client -> client.onNavigationStateChange(navState)); - } else { - for (String key : bundle.keySet()) { - bundleSummary.append(key); - bundleSummary.append("="); - bundleSummary.append(bundle.get(key)); - bundleSummary.append(" "); + StringBuilder bundleSummary = new StringBuilder(); + if (eventType == NAV_STATE_EVENT_ID) { + // Attempt to read proto byte array + byte[] protoBytes = bundle.getByteArray(NAV_STATE_PROTO_BUNDLE_KEY); + if (protoBytes != null) { + try { + NavigationStateProto navState = NavigationStateProto.parseFrom( + protoBytes); + bundleSummary.append(navState.toString()); + + // Update clients + broadcastClientEvent( + client -> client.onNavigationStateChange(navState)); + } catch (InvalidProtocolBufferException e) { + Log.e(TAG, "Error parsing navigation state proto", e); } + } else { + Log.e(TAG, "Received nav state byte array is null"); + } + } else { + for (String key : bundle.keySet()) { + bundleSummary.append(key); + bundleSummary.append("="); + bundleSummary.append(bundle.get(key)); + bundleSummary.append(" "); } - Log.d(TAG, "onEvent(" + eventType + ", " + bundleSummary + ")"); - } catch (Exception e) { - Log.e(TAG, "Error parsing event data (" + eventType + ", " + bundle + ")", e); - NavigationState navState = new NavigationState.Builder().build(); - broadcastClientEvent(client -> client.onNavigationStateChange(navState)); } + Log.d(TAG, "onEvent(" + eventType + ", " + bundleSummary + ")"); } }; @@ -251,17 +254,17 @@ public class ClusterRenderingService extends InstrumentClusterRenderingService i scanCode = 106; } return KeyEvent.obtain( - downTime, - eventTime, - action, - keyCode, - 0 /* repeat */, - 0 /* meta state */, - 0 /* deviceId*/, - scanCode /* scancode */, - KeyEvent.FLAG_FROM_SYSTEM /* flags */, - InputDevice.SOURCE_KEYBOARD, - null /* characters */); + downTime, + eventTime, + action, + keyCode, + 0 /* repeat */, + 0 /* meta state */, + 0 /* deviceId*/, + scanCode /* scancode */, + KeyEvent.FLAG_FROM_SYSTEM /* flags */, + InputDevice.SOURCE_KEYBOARD, + null /* characters */); } private void execShellCommand(String[] args) { diff --git a/src/android/car/cluster/CueView.java b/src/android/car/cluster/CueView.java index f33834e..fa08846 100644 --- a/src/android/car/cluster/CueView.java +++ b/src/android/car/cluster/CueView.java @@ -25,9 +25,9 @@ import android.util.AttributeSet; import android.util.Log; import android.widget.TextView; -import androidx.car.cluster.navigation.ImageReference; -import androidx.car.cluster.navigation.RichText; -import androidx.car.cluster.navigation.RichTextElement; +import android.car.cluster.navigation.NavigationState.ImageReference; +import android.car.cluster.navigation.NavigationState.Cue; +import android.car.cluster.navigation.NavigationState.Cue.CueElement; import java.util.Collections; import java.util.List; @@ -45,7 +45,7 @@ public class CueView extends TextView { private String mImageSpanText; private CompletableFuture<?> mFuture; private Handler mHandler = new Handler(); - private RichText mContent; + private Cue mContent; public CueView(Context context) { super(context); @@ -66,41 +66,41 @@ public class CueView extends TextView { mImageSpanText = context.getString(R.string.span_image); } - public void setRichText(RichText richText, ImageResolver imageResolver) { - if (richText == null) { + public void setCue(Cue cue, ImageResolver imageResolver) { + if (cue == null) { setText(null); return; } - if (mFuture != null && !Objects.equals(richText, mContent)) { + if (mFuture != null && !Objects.equals(cue, mContent)) { mFuture.cancel(true); } - List<ImageReference> imageReferences = richText.getElements().stream() - .filter(element -> element.getImage() != null) + List<ImageReference> imageReferences = cue.getElementsList().stream() + .filter(element -> element.hasImage()) .map(element -> element.getImage()) .collect(Collectors.toList()); mFuture = imageResolver .getBitmaps(imageReferences, 0, getLineHeight()) .thenAccept(bitmaps -> { - mHandler.post(() -> update(richText, bitmaps)); + mHandler.post(() -> update(cue, bitmaps)); mFuture = null; }) .exceptionally(ex -> { if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Unable to fetch images for cue: " + richText); + Log.d(TAG, "Unable to fetch images for cue: " + cue); } - mHandler.post(() -> update(richText, Collections.emptyMap())); + mHandler.post(() -> update(cue, Collections.emptyMap())); return null; }); - mContent = richText; + mContent = cue; } - private void update(RichText richText, Map<ImageReference, Bitmap> bitmaps) { + private void update(Cue cue, Map<ImageReference, Bitmap> bitmaps) { SpannableStringBuilder builder = new SpannableStringBuilder(); - for (RichTextElement element : richText.getElements()) { - if (element.getImage() != null) { + for (CueElement element : cue.getElementsList()) { + if (element.hasImage()) { Bitmap bitmap = bitmaps.get(element.getImage()); if (bitmap != null) { String imageText = element.getText().isEmpty() ? mImageSpanText : diff --git a/src/android/car/cluster/ImageResolver.java b/src/android/car/cluster/ImageResolver.java index e425bbd..45fcf31 100644 --- a/src/android/car/cluster/ImageResolver.java +++ b/src/android/car/cluster/ImageResolver.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,15 +15,14 @@ */ package android.car.cluster; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.car.cluster.navigation.NavigationState.ImageReference; +import android.car.cluster.renderer.InvalidSizeException; import android.graphics.Bitmap; import android.graphics.Point; import android.net.Uri; import android.util.Log; -import android.util.LruCache; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.car.cluster.navigation.ImageReference; import java.util.List; import java.util.Map; @@ -32,22 +31,21 @@ import java.util.stream.Collectors; /** * Class for retrieving bitmap images from a ContentProvider + * + * @hide */ public class ImageResolver { private static final String TAG = "Cluster.ImageResolver"; - private static final int IMAGE_CACHE_SIZE_BYTES = 4 * 1024 * 1024; /* 4 mb */ - private final BitmapFetcher mFetcher; - private final LruCache<String, Bitmap> mCache = new LruCache<String, Bitmap>( - IMAGE_CACHE_SIZE_BYTES) { - @Override - protected int sizeOf(String key, Bitmap value) { - return value.getByteCount(); - } - }; + /** + * Interface used for fetching bitmaps from a content resolver + */ public interface BitmapFetcher { - Bitmap getBitmap(Uri uri); + /** + * Returns a {@link Bitmap} given a request Uri and dimensions + */ + Bitmap getBitmap(Uri uri, int width, int height) throws InvalidSizeException; } /** @@ -62,45 +60,43 @@ public class ImageResolver { * This image would fit inside the provided size. Either width, height or both should be greater * than 0. * - * @param width required width, or 0 if width is flexible based on height. + * @param width required width, or 0 if width is flexible based on height. * @param height required height, or 0 if height is flexible based on width. */ @NonNull public CompletableFuture<Bitmap> getBitmap(@NonNull ImageReference img, int width, int height) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, String.format("Requesting image %s (width: %d, height: %d)", - img.getRawContentUri(), width, height)); + img.getContentUri(), width, height)); } return CompletableFuture.supplyAsync(() -> { // Adjust the size to fit in the requested box. - Point adjusted = getAdjustedSize(img.getOriginalWidth(), img.getOriginalHeight(), width, - height); + Point adjusted = getAdjustedSize(img.getAspectRatio(), width, height); if (adjusted == null) { - Log.e(TAG, "The provided image has no original size: " + img.getRawContentUri()); + Log.e(TAG, "The provided image has no aspect ratio: " + img.getContentUri()); return null; } - Uri uri = img.getContentUri(adjusted.x, adjusted.y); - Bitmap bitmap = mCache.get(uri.toString()); + + Uri uri = Uri.parse(img.getContentUri()); + Bitmap bitmap = null; + try { + bitmap = mFetcher.getBitmap(uri, adjusted.x, adjusted.y); + } catch (InvalidSizeException e) { + Log.e(TAG, "Bitmap must have positive width and height"); + } if (bitmap == null) { - bitmap = mFetcher.getBitmap(uri); - if (bitmap == null) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Unable to fetch image: " + uri); - } - return null; + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "Unable to fetch image: " + uri); } - if (bitmap.getWidth() != adjusted.x || bitmap.getHeight() != adjusted.y) { - bitmap = Bitmap.createScaledBitmap(bitmap, adjusted.x, adjusted.y, true); - } - mCache.put(uri.toString(), bitmap); + return null; } + if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, String.format("Returning image %s (width: %d, height: %d)", - img.getRawContentUri(), width, height)); + img.getContentUri(), width, height)); } - return bitmap != null ? Bitmap.createScaledBitmap(bitmap, adjusted.x, adjusted.y, true) - : null; + return bitmap; }); } @@ -109,7 +105,7 @@ public class ImageResolver { * returning {@link CompletableFuture} will contain a map from each {@link ImageReference} to * its bitmap. If any image fails to be fetched, the whole future completes exceptionally. * - * @param width required width, or 0 if width is flexible based on height. + * @param width required width, or 0 if width is flexible based on height. * @param height required height, or 0 if height is flexible based on width. */ @NonNull @@ -141,15 +137,14 @@ public class ImageResolver { * Returns an image size that exactly fits inside a requested box, maintaining an original size * aspect ratio. * - * @param originalWidth original width (must be != 0) - * @param originalHeight original height (must be != 0) - * @param requestedWidth required width, or 0 if width is flexible based on height. + * @param imageRatio original aspect ratio (must be > 0) + * @param requestedWidth required width, or 0 if width is flexible based on height. * @param requestedHeight required height, or 0 if height is flexible based on width. */ @Nullable - public Point getAdjustedSize(int originalWidth, int originalHeight, int requestedWidth, + public Point getAdjustedSize(double imageRatio, int requestedWidth, int requestedHeight) { - if (originalWidth <= 0 || originalHeight <= 0) { + if (imageRatio <= 0) { return null; } else if (requestedWidth == 0 && requestedHeight == 0) { throw new IllegalArgumentException("At least one of width or height must be != 0"); @@ -157,12 +152,11 @@ public class ImageResolver { // If width is flexible or if both width and height are set and the original image is wider // than the space provided, then scale the width. float requiredRatio = requestedHeight > 0 ? ((float) requestedWidth) / requestedHeight : 0; - float imageRatio = ((float) originalWidth) / originalHeight; Point res = new Point(requestedWidth, requestedHeight); if (requestedWidth == 0 || (requestedHeight != 0 && imageRatio < requiredRatio)) { - res.x = (int) (((float) requestedHeight / originalHeight) * originalWidth); + res.x = (int) (imageRatio * requestedHeight); } else { - res.y = (int) (((float) requestedWidth / originalWidth) * originalHeight); + res.y = (int) (requestedWidth / imageRatio); } return res; } diff --git a/src/android/car/cluster/LaneView.java b/src/android/car/cluster/LaneView.java index 2b629c6..2a5ca58 100644 --- a/src/android/car/cluster/LaneView.java +++ b/src/android/car/cluster/LaneView.java @@ -29,9 +29,9 @@ import android.util.Log; import android.widget.ImageView; import android.widget.LinearLayout; -import androidx.car.cluster.navigation.ImageReference; -import androidx.car.cluster.navigation.Lane; -import androidx.car.cluster.navigation.LaneDirection; +import android.car.cluster.navigation.NavigationState.ImageReference; +import android.car.cluster.navigation.NavigationState.Lane; +import android.car.cluster.navigation.NavigationState.Lane.LaneDirection; import java.util.ArrayList; import java.util.List; @@ -104,7 +104,7 @@ public class LaneView extends LinearLayout { } private Bitmap combineBitmapFromLane(Lane lane) { - if (lane.getDirections().isEmpty()) { + if (lane.getLaneDirectionsList().isEmpty()) { return null; } @@ -113,14 +113,14 @@ public class LaneView extends LinearLayout { Shift shift = getShift(lane); - for (LaneDirection laneDir : lane.getDirections()) { - if (!laneDir.isHighlighted()) { + for (LaneDirection laneDir : lane.getLaneDirectionsList()) { + if (!laneDir.getIsHighlighted()) { drawToCanvas(laneDir, canvas, false, shift); } } - for (LaneDirection laneDir : lane.getDirections()) { - if (laneDir.isHighlighted()) { + for (LaneDirection laneDir : lane.getLaneDirectionsList()) { + if (laneDir.getIsHighlighted()) { drawToCanvas(laneDir, canvas, true, shift); } } @@ -148,7 +148,7 @@ public class LaneView extends LinearLayout { boolean containsLeft = false; boolean containsStraight = false; - for (LaneDirection laneDir : lane.getDirections()) { + for (LaneDirection laneDir : lane.getLaneDirectionsList()) { if (laneDir.getShape().equals(LaneDirection.Shape.NORMAL_RIGHT) || laneDir.getShape().equals(LaneDirection.Shape.SLIGHT_RIGHT) || laneDir.getShape().equals(LaneDirection.Shape.SHARP_RIGHT) diff --git a/src/android/car/cluster/LoggingClusterRenderingService.java b/src/android/car/cluster/LoggingClusterRenderingService.java index e58390b..89990f9 100644 --- a/src/android/car/cluster/LoggingClusterRenderingService.java +++ b/src/android/car/cluster/LoggingClusterRenderingService.java @@ -23,17 +23,15 @@ import android.os.Bundle; import android.os.UserHandle; import android.util.Log; -import androidx.car.cluster.navigation.NavigationState; -import androidx.versionedparcelable.ParcelUtils; - import com.google.android.collect.Lists; +import com.google.protobuf.InvalidProtocolBufferException; /** * Dummy implementation of {@link LoggingClusterRenderingService} to log all interaction. */ public class LoggingClusterRenderingService extends InstrumentClusterRenderingService { private static final String TAG = LoggingClusterRenderingService.class.getSimpleName(); - private static final String NAV_STATE_BUNDLE_KEY = "navstate"; + private static final String NAV_STATE_PROTO_BUNDLE_KEY = "navstate2"; private static final int NAV_STATE_EVENT_ID = 1; @Override @@ -53,15 +51,27 @@ public class LoggingClusterRenderingService extends InstrumentClusterRenderingSe public void onEvent(int eventType, Bundle bundle) { StringBuilder bundleSummary = new StringBuilder(); if (eventType == NAV_STATE_EVENT_ID) { - bundle.setClassLoader(ParcelUtils.class.getClassLoader()); - NavigationState navState = NavigationState - .fromParcelable(bundle.getParcelable(NAV_STATE_BUNDLE_KEY)); - bundleSummary.append(navState.toString()); + // Attempt to read proto byte array + byte[] protoBytes = bundle.getByteArray(NAV_STATE_PROTO_BUNDLE_KEY); + if (protoBytes != null) { + try { + android.car.cluster.navigation.NavigationState.NavigationStateProto + navState = + android.car.cluster.navigation.NavigationState.NavigationStateProto.parseFrom( + protoBytes); + bundleSummary.append(navState.toString()); - // Sending broadcast for testing. - Intent intent = new Intent("android.car.cluster.NAVIGATION_STATE_UPDATE"); - intent.putExtra(NAV_STATE_BUNDLE_KEY, bundle); - sendBroadcastAsUser(intent, UserHandle.ALL); + // Sending broadcast for testing. + Intent intent = new Intent( + "android.car.cluster.NAVIGATION_STATE_UPDATE"); + intent.putExtra(NAV_STATE_PROTO_BUNDLE_KEY, bundle); + sendBroadcastAsUser(intent, UserHandle.ALL); + } catch (InvalidProtocolBufferException e) { + Log.e(TAG, "Error parsing navigation state proto", e); + } + } else { + Log.e(TAG, "Received nav state byte array is null"); + } } else { for (String key : bundle.keySet()) { bundleSummary.append(key); diff --git a/src/android/car/cluster/MainClusterActivity.java b/src/android/car/cluster/MainClusterActivity.java index d4138f2..cf5699e 100644 --- a/src/android/car/cluster/MainClusterActivity.java +++ b/src/android/car/cluster/MainClusterActivity.java @@ -23,8 +23,7 @@ import static android.content.PermissionChecker.PERMISSION_GRANTED; import android.app.ActivityManager; import android.app.ActivityOptions; import android.car.Car; -import android.car.cluster.CarInstrumentClusterManager; -import android.car.cluster.ClusterActivityState; +import android.car.cluster.navigation.NavigationState.NavigationStateProto; import android.car.cluster.sensors.Sensors; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; @@ -50,7 +49,6 @@ import android.view.inputmethod.InputMethodManager; import android.widget.Button; import android.widget.TextView; -import androidx.car.cluster.navigation.NavigationState; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; @@ -65,7 +63,6 @@ import java.lang.ref.WeakReference; import java.lang.reflect.InvocationTargetException; import java.net.URISyntaxException; import java.util.HashMap; -import java.util.List; import java.util.Map; /** @@ -94,7 +91,8 @@ public class MainClusterActivity extends FragmentActivity implements private static final int MEDIA_FACET_ID = 2; private static final int INFO_FACET_ID = 3; - private static final NavigationState NULL_NAV_STATE = new NavigationState.Builder().build(); + private static final NavigationStateProto NULL_NAV_STATE = + NavigationStateProto.getDefaultInstance(); private static final int NO_DISPLAY = -1; private ViewPager mPager; @@ -315,7 +313,7 @@ public class MainClusterActivity extends FragmentActivity implements } @Override - public void onNavigationStateChange(NavigationState state) { + public void onNavigationStateChange(NavigationStateProto state) { Log.d(TAG, "onNavigationStateChange: " + state); if (mNavStateController != null) { mNavStateController.update(state); diff --git a/src/android/car/cluster/NavStateController.java b/src/android/car/cluster/NavStateController.java index 6e37e42..a793032 100644 --- a/src/android/car/cluster/NavStateController.java +++ b/src/android/car/cluster/NavStateController.java @@ -16,6 +16,15 @@ package android.car.cluster; import android.annotation.Nullable; +import android.car.cluster.navigation.NavigationState.Destination; +import android.car.cluster.navigation.NavigationState.Destination.Traffic; +import android.car.cluster.navigation.NavigationState.Distance; +import android.car.cluster.navigation.NavigationState.ImageReference; +import android.car.cluster.navigation.NavigationState.Maneuver; +import android.car.cluster.navigation.NavigationState.NavigationStateProto; +import android.car.cluster.navigation.NavigationState.Road; +import android.car.cluster.navigation.NavigationState.Step; +import android.car.cluster.navigation.NavigationState.Timestamp; import android.content.Context; import android.graphics.drawable.Drawable; import android.os.Handler; @@ -24,17 +33,7 @@ import android.view.View; import android.widget.ImageView; import android.widget.TextView; -import androidx.car.cluster.navigation.Destination; -import androidx.car.cluster.navigation.Destination.Traffic; -import androidx.car.cluster.navigation.Distance; -import androidx.car.cluster.navigation.ImageReference; -import androidx.car.cluster.navigation.Maneuver; -import androidx.car.cluster.navigation.NavigationState; -import androidx.car.cluster.navigation.Segment; -import androidx.car.cluster.navigation.Step; - -import java.time.Duration; -import java.time.ZonedDateTime; +import java.time.Instant; /** * View controller for navigation state rendering. @@ -81,30 +80,36 @@ public class NavStateController { /** * Updates views to reflect the provided navigation state */ - public void update(@Nullable NavigationState state) { + public void update(@Nullable NavigationStateProto state) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Updating nav state: " + state); } - Step step = state != null && state.getSteps().size() > 0 ? state.getSteps().get(0) : null; - Destination destination = state != null && !state.getDestinations().isEmpty() - ? state.getDestinations().get(0) : null; - ZonedDateTime eta = destination != null ? destination.getEta() : null; + Step step = state != null && state.getStepsCount() > 0 ? state.getSteps(0) : null; + Destination destination = state != null && state.getDestinationsCount() > 0 + ? state.getDestinations(0) : null; Traffic traffic = destination != null ? destination.getTraffic() : null; - - mEta.setText(eta != null ? formatEta(eta) : null); + String eta = destination != null + ? destination.getFormattedDurationUntilArrival().isEmpty() + ? formatEta(destination.getEstimatedTimeAtArrival()) + : destination.getFormattedDurationUntilArrival() + : null; + mEta.setText(eta); mEta.setTextColor(getTrafficColor(traffic)); mManeuver.setImageDrawable(getManeuverIcon(step != null ? step.getManeuver() : null)); - setProvidedManeuverIcon(mProvidedManeuver, - step != null ? step.getManeuver().getIcon() : null); + setProvidedManeuverIcon(mProvidedManeuver, step != null + ? step.getManeuver().hasIcon() ? step.getManeuver().getIcon() : null + : null); mDistance.setText(formatDistance(step != null ? step.getDistance() : null)); - mSegment.setText(state != null ? getSegmentString(state.getCurrentSegment()) : null); - mCue.setRichText(step != null ? step.getCue() : null, mImageResolver); + mSegment.setText(state != null ? getSegmentString(state.getCurrentRoad()) : null); + mCue.setCue(step != null ? step.getCue() : null, mImageResolver); - if (step != null && step.getLanes().size() > 0) { - mProvidedLane.setLanes(step.getLanesImage(), mImageResolver); - mProvidedLane.setVisibility(View.VISIBLE); + if (step != null && step.getLanesCount() > 0) { + if (step.hasLanesImage()) { + mProvidedLane.setLanes(step.getLanesImage(), mImageResolver); + mProvidedLane.setVisibility(View.VISIBLE); + } - mLane.setLanes(step.getLanes()); + mLane.setLanes(step.getLanesList()); mLane.setVisibility(View.VISIBLE); } else { mLane.setVisibility(View.GONE); @@ -112,7 +117,6 @@ public class NavStateController { } } - private int getTrafficColor(@Nullable Traffic traffic) { if (traffic == Traffic.LOW) { return mContext.getColor(R.color.low_traffic); @@ -125,12 +129,12 @@ public class NavStateController { return mContext.getColor(R.color.unknown_traffic); } - private String formatEta(@Nullable ZonedDateTime eta) { - ZonedDateTime now = ZonedDateTime.now(); - Duration duration = Duration.between(now, eta); - long seconds = duration.getSeconds(); + private String formatEta(@Nullable Timestamp eta) { + long seconds = eta.getSeconds() - Instant.now().getEpochSecond(); + + // Round up to the nearest minute + seconds = (long) Math.ceil(seconds / 60d) * 60; - // TODO: move formatting into common lib somewhere long minutes = (seconds / 60) % 60; long hours = (seconds / 3600) % 24; long days = seconds / (3600 * 24); @@ -144,7 +148,7 @@ public class NavStateController { } } - private String getSegmentString(Segment segment) { + private String getSegmentString(Road segment) { if (segment != null) { return segment.getName(); } @@ -291,13 +295,13 @@ public class NavStateController { } private String formatDistance(@Nullable Distance distance) { - if (distance == null || distance.getDisplayUnit() == Distance.Unit.UNKNOWN) { + if (distance == null || distance.getDisplayUnits() == Distance.Unit.UNKNOWN) { return null; } String unit = ""; - switch (distance.getDisplayUnit()) { + switch (distance.getDisplayUnits()) { case METERS: unit = "m"; break; diff --git a/tests/robotests/Android.mk b/tests/robotests/Android.mk index c02a598..631a403 100644 --- a/tests/robotests/Android.mk +++ b/tests/robotests/Android.mk @@ -13,7 +13,8 @@ LOCAL_JAVA_LIBRARIES := \ robolectric_android-all-stub \ Robolectric_all-target \ mockito-robolectric-prebuilt \ - truth-prebuilt + truth-prebuilt \ + android.car LOCAL_INSTRUMENTATION_FOR := DirectRenderingCluster @@ -35,7 +36,8 @@ LOCAL_JAVA_LIBRARIES := \ robolectric_android-all-stub \ Robolectric_all-target \ mockito-robolectric-prebuilt \ - truth-prebuilt + truth-prebuilt \ + android.car LOCAL_TEST_PACKAGE := DirectRenderingCluster diff --git a/tests/robotests/src/android/car/cluster/ImageResolverTest.java b/tests/robotests/src/android/car/cluster/ImageResolverTest.java index 76db419..0c05390 100644 --- a/tests/robotests/src/android/car/cluster/ImageResolverTest.java +++ b/tests/robotests/src/android/car/cluster/ImageResolverTest.java @@ -32,71 +32,61 @@ public class ImageResolverTest { @Before public void setup() { - mImageResolver = new ImageResolver((uri) -> null); + mImageResolver = new ImageResolver((uri, w, h) -> null); } @Test public void adjustedSize_widerImageInSquareBox() { - assertEquals(new Point(20, 10), mImageResolver.getAdjustedSize(40, 20, 20, 20)); + assertEquals(new Point(20, 10), mImageResolver.getAdjustedSize(2, 20, 20)); } @Test public void adjustedSize_tallerImageInSquareBox() { - assertEquals(new Point(10, 20), mImageResolver.getAdjustedSize(20, 40, 20, 20)); + assertEquals(new Point(10, 20), mImageResolver.getAdjustedSize(0.5, 20, 20)); } @Test public void adjustedSize_narrowerImageInSquareBox() { - assertEquals(new Point(10, 20), mImageResolver.getAdjustedSize(5, 10, 20, 20)); + assertEquals(new Point(10, 20), mImageResolver.getAdjustedSize(0.5, 20, 20)); } @Test public void adjustedSize_shorterImageInSquareBox() { - assertEquals(new Point(20, 8), mImageResolver.getAdjustedSize(5, 2, 20, 20)); + assertEquals(new Point(20, 8), mImageResolver.getAdjustedSize(2.5, 20, 20)); } @Test public void adjustedSize_widerImageInTallRectangle() { - assertEquals(new Point(20, 10), mImageResolver.getAdjustedSize(40, 20, 20, 40)); + assertEquals(new Point(20, 10), mImageResolver.getAdjustedSize(2, 20, 40)); } @Test public void adjustedSize_tallerImageInTallRectangle() { - assertEquals(new Point(20, 40), mImageResolver.getAdjustedSize(5, 10, 20, 40)); + assertEquals(new Point(20, 40), mImageResolver.getAdjustedSize(0.5, 20, 40)); } @Test public void adjustedSize_widerImageInWideRectangle() { - assertEquals(new Point(40, 20), mImageResolver.getAdjustedSize(10, 5, 40, 20)); + assertEquals(new Point(40, 20), mImageResolver.getAdjustedSize(2, 40, 20)); } @Test public void adjustedSize_tallerImageInWideRectangle() { - assertEquals(new Point(10, 20), mImageResolver.getAdjustedSize(5, 10, 40, 20)); - } - - @Test - public void adjustedSize_nullIfNoOriginalWidth() { - assertEquals(null, mImageResolver.getAdjustedSize(0, 10, 40, 20)); - } - - @Test - public void adjustedSize_nullIfNoOriginalHeight() { - assertEquals(null, mImageResolver.getAdjustedSize(5, 0, 40, 20)); + assertEquals(new Point(10, 20), mImageResolver.getAdjustedSize(0.5, 40, 20)); } @Test(expected = IllegalArgumentException.class) public void adjustedSize_exceptionIfRequestedWidthAndHeightNoProvided() { - assertEquals(null, mImageResolver.getAdjustedSize(5, 10, 0, 0)); + assertEquals(null, mImageResolver.getAdjustedSize(0.5, 0, 0)); } @Test public void adjustedSize_flexibleWidth() { - assertEquals(new Point(20, 30), mImageResolver.getAdjustedSize(40, 60, 0, 30)); + assertEquals(new Point(20, 30), mImageResolver.getAdjustedSize(0.66667, 0, 30)); } @Test public void adjustedSize_flexibleHeight() { - assertEquals(new Point(20, 20), mImageResolver.getAdjustedSize(40, 40, 20, 0)); + assertEquals(new Point(20, 20), mImageResolver.getAdjustedSize(1, 20, 0)); } } |