/* * Copyright (C) 2018 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. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.car.cluster; import android.content.Context; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.os.Handler; import android.text.SpannableStringBuilder; import android.text.style.ImageSpan; import android.util.AttributeSet; import android.util.Log; import android.widget.TextView; 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; import java.util.Map; import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; /** * View component that displays the Cue information on the instrument cluster display */ public class CueView extends TextView { private static final String TAG = "Cluster.CueView"; private String mImageSpanText; private CompletableFuture mFuture; private Handler mHandler = new Handler(); private Cue mContent; public CueView(Context context) { super(context); init(context); } public CueView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public CueView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } private void init(Context context) { mImageSpanText = context.getString(R.string.span_image); } public void setCue(Cue cue, ImageResolver imageResolver) { if (cue == null) { setText(null); return; } if (mFuture != null && !Objects.equals(cue, mContent)) { mFuture.cancel(true); } List 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(cue, bitmaps)); mFuture = null; }) .exceptionally(ex -> { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Unable to fetch images for cue: " + cue); } mHandler.post(() -> update(cue, Collections.emptyMap())); return null; }); mContent = cue; } private void update(Cue cue, Map bitmaps) { SpannableStringBuilder builder = new SpannableStringBuilder(); for (CueElement element : cue.getElementsList()) { if (element.hasImage()) { Bitmap bitmap = bitmaps.get(element.getImage()); if (bitmap != null) { String imageText = element.getText().isEmpty() ? mImageSpanText : element.getText(); int start = builder.length(); int end = start + imageText.length(); builder.append(imageText); BitmapDrawable drawable = new BitmapDrawable(getResources(), bitmap); drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight()); builder.setSpan(new ImageSpan(drawable), start, end, 0); } } else if (!element.getText().isEmpty()) { builder.append(element.getText()); } } setText(builder); } }