1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.car.cluster; 17 18 import android.content.Context; 19 import android.graphics.Bitmap; 20 import android.graphics.drawable.BitmapDrawable; 21 import android.os.Handler; 22 import android.text.SpannableStringBuilder; 23 import android.text.style.ImageSpan; 24 import android.util.AttributeSet; 25 import android.util.Log; 26 import android.widget.TextView; 27 28 import android.car.cluster.navigation.NavigationState.ImageReference; 29 import android.car.cluster.navigation.NavigationState.Cue; 30 import android.car.cluster.navigation.NavigationState.Cue.CueElement; 31 32 import java.util.Collections; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.Objects; 36 import java.util.concurrent.CompletableFuture; 37 import java.util.stream.Collectors; 38 39 /** 40 * View component that displays the Cue information on the instrument cluster display 41 */ 42 public class CueView extends TextView { 43 private static final String TAG = "Cluster.CueView"; 44 45 private String mImageSpanText; 46 private CompletableFuture<?> mFuture; 47 private Handler mHandler = new Handler(); 48 private Cue mContent; 49 CueView(Context context)50 public CueView(Context context) { 51 super(context); 52 init(context); 53 } 54 CueView(Context context, AttributeSet attrs)55 public CueView(Context context, AttributeSet attrs) { 56 super(context, attrs); 57 init(context); 58 } 59 CueView(Context context, AttributeSet attrs, int defStyle)60 public CueView(Context context, AttributeSet attrs, int defStyle) { 61 super(context, attrs, defStyle); 62 init(context); 63 } 64 init(Context context)65 private void init(Context context) { 66 mImageSpanText = context.getString(R.string.span_image); 67 } 68 setCue(Cue cue, ImageResolver imageResolver)69 public void setCue(Cue cue, ImageResolver imageResolver) { 70 if (cue == null) { 71 setText(null); 72 return; 73 } 74 75 if (mFuture != null && !Objects.equals(cue, mContent)) { 76 mFuture.cancel(true); 77 } 78 79 List<ImageReference> imageReferences = cue.getElementsList().stream() 80 .filter(element -> element.hasImage()) 81 .map(element -> element.getImage()) 82 .collect(Collectors.toList()); 83 mFuture = imageResolver 84 .getBitmaps(imageReferences, 0, getLineHeight()) 85 .thenAccept(bitmaps -> { 86 mHandler.post(() -> update(cue, bitmaps)); 87 mFuture = null; 88 }) 89 .exceptionally(ex -> { 90 if (Log.isLoggable(TAG, Log.DEBUG)) { 91 Log.d(TAG, "Unable to fetch images for cue: " + cue); 92 } 93 mHandler.post(() -> update(cue, Collections.emptyMap())); 94 return null; 95 }); 96 mContent = cue; 97 } 98 update(Cue cue, Map<ImageReference, Bitmap> bitmaps)99 private void update(Cue cue, Map<ImageReference, Bitmap> bitmaps) { 100 SpannableStringBuilder builder = new SpannableStringBuilder(); 101 102 for (CueElement element : cue.getElementsList()) { 103 if (element.hasImage()) { 104 Bitmap bitmap = bitmaps.get(element.getImage()); 105 if (bitmap != null) { 106 String imageText = element.getText().isEmpty() ? mImageSpanText : 107 element.getText(); 108 int start = builder.length(); 109 int end = start + imageText.length(); 110 builder.append(imageText); 111 BitmapDrawable drawable = new BitmapDrawable(getResources(), bitmap); 112 drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight()); 113 builder.setSpan(new ImageSpan(drawable), start, end, 0); 114 } 115 } else if (!element.getText().isEmpty()) { 116 builder.append(element.getText()); 117 } 118 } 119 120 setText(builder); 121 } 122 } 123