summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2019-06-30 23:12:48 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2019-06-30 23:12:48 +0000
commit5c702f281408525a19a8a6799055612b387a0241 (patch)
tree17c5d545238ea11b087a4717886f463514d17882
parent3743e8a09c053a23b54cacf24c92f33cce532991 (diff)
parentf4ed44d3ff5f0312a496bc793bb6183812e7406a (diff)
downloadCluster-5c702f281408525a19a8a6799055612b387a0241.tar.gz
Snap for 5699409 from f4ed44d3ff5f0312a496bc793bb6183812e7406a to qt-aml-release
Change-Id: I80adcaefbb619a26f777c6d75ac112f985228306
-rw-r--r--Android.mk4
-rw-r--r--AndroidManifest.xml2
-rw-r--r--res/values/dimens.xml2
-rw-r--r--src/android/car/cluster/ClusterRenderingService.java79
-rw-r--r--src/android/car/cluster/CueView.java32
-rw-r--r--src/android/car/cluster/ImageResolver.java84
-rw-r--r--src/android/car/cluster/LaneView.java18
-rw-r--r--src/android/car/cluster/LoggingClusterRenderingService.java34
-rw-r--r--src/android/car/cluster/MainClusterActivity.java10
-rw-r--r--src/android/car/cluster/NavStateController.java74
-rw-r--r--tests/robotests/Android.mk6
-rw-r--r--tests/robotests/src/android/car/cluster/ImageResolverTest.java34
12 files changed, 192 insertions, 187 deletions
diff --git a/Android.mk b/Android.mk
index bd9c68d..ef92d7e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -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));
}
}