1 /* 2 * Copyright (C) 2006 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 17 package android.text.style; 18 19 import android.annotation.DrawableRes; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.compat.annotation.UnsupportedAppUsage; 23 import android.content.Context; 24 import android.graphics.Bitmap; 25 import android.graphics.BitmapFactory; 26 import android.graphics.drawable.BitmapDrawable; 27 import android.graphics.drawable.Drawable; 28 import android.net.Uri; 29 import android.util.Log; 30 31 import java.io.InputStream; 32 33 /** 34 * Span that replaces the text it's attached to with a {@link Drawable} that can be aligned with 35 * the bottom or with the baseline of the surrounding text. The drawable can be constructed from 36 * varied sources: 37 * <ul> 38 * <li>{@link Bitmap} - see {@link #ImageSpan(Context, Bitmap)} and 39 * {@link #ImageSpan(Context, Bitmap, int)} 40 * </li> 41 * <li>{@link Drawable} - see {@link #ImageSpan(Drawable, int)}</li> 42 * <li>resource id - see {@link #ImageSpan(Context, int, int)}</li> 43 * <li>{@link Uri} - see {@link #ImageSpan(Context, Uri, int)}</li> 44 * </ul> 45 * The default value for the vertical alignment is {@link DynamicDrawableSpan#ALIGN_BOTTOM} 46 * <p> 47 * For example, an <code>ImagedSpan</code> can be used like this: 48 * <pre> 49 * SpannableString string = new SpannableString("Bottom: span.\nBaseline: span."); 50 * // using the default alignment: ALIGN_BOTTOM 51 * string.setSpan(new ImageSpan(this, R.mipmap.ic_launcher), 7, 8, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 52 * string.setSpan(new ImageSpan(this, R.mipmap.ic_launcher, DynamicDrawableSpan.ALIGN_BASELINE), 53 * 22, 23, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 54 * </pre> 55 * <img src="{@docRoot}reference/android/images/text/style/imagespan.png" /> 56 * <figcaption>Text with <code>ImageSpan</code>s aligned bottom and baseline.</figcaption> 57 */ 58 public class ImageSpan extends DynamicDrawableSpan { 59 60 @Nullable 61 @UnsupportedAppUsage 62 private Drawable mDrawable; 63 @Nullable 64 private Uri mContentUri; 65 @DrawableRes 66 private int mResourceId; 67 @Nullable 68 private Context mContext; 69 @Nullable 70 private String mSource; 71 72 /** 73 * @deprecated Use {@link #ImageSpan(Context, Bitmap)} instead. 74 */ 75 @Deprecated ImageSpan(@onNull Bitmap b)76 public ImageSpan(@NonNull Bitmap b) { 77 this(null, b, ALIGN_BOTTOM); 78 } 79 80 /** 81 * @deprecated Use {@link #ImageSpan(Context, Bitmap, int)} instead. 82 */ 83 @Deprecated ImageSpan(@onNull Bitmap b, int verticalAlignment)84 public ImageSpan(@NonNull Bitmap b, int verticalAlignment) { 85 this(null, b, verticalAlignment); 86 } 87 88 /** 89 * Constructs an {@link ImageSpan} from a {@link Context} and a {@link Bitmap} with the default 90 * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM} 91 * 92 * @param context context used to create a drawable from {@param bitmap} based on the display 93 * metrics of the resources 94 * @param bitmap bitmap to be rendered 95 */ ImageSpan(@onNull Context context, @NonNull Bitmap bitmap)96 public ImageSpan(@NonNull Context context, @NonNull Bitmap bitmap) { 97 this(context, bitmap, ALIGN_BOTTOM); 98 } 99 100 /** 101 * Constructs an {@link ImageSpan} from a {@link Context}, a {@link Bitmap} and a vertical 102 * alignment. 103 * 104 * @param context context used to create a drawable from {@param bitmap} based on 105 * the display metrics of the resources 106 * @param bitmap bitmap to be rendered 107 * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or 108 * {@link DynamicDrawableSpan#ALIGN_BASELINE} 109 */ ImageSpan(@onNull Context context, @NonNull Bitmap bitmap, int verticalAlignment)110 public ImageSpan(@NonNull Context context, @NonNull Bitmap bitmap, int verticalAlignment) { 111 super(verticalAlignment); 112 mContext = context; 113 mDrawable = context != null 114 ? new BitmapDrawable(context.getResources(), bitmap) 115 : new BitmapDrawable(bitmap); 116 int width = mDrawable.getIntrinsicWidth(); 117 int height = mDrawable.getIntrinsicHeight(); 118 mDrawable.setBounds(0, 0, width > 0 ? width : 0, height > 0 ? height : 0); 119 } 120 121 /** 122 * Constructs an {@link ImageSpan} from a drawable with the default 123 * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}. 124 * 125 * @param drawable drawable to be rendered 126 */ ImageSpan(@onNull Drawable drawable)127 public ImageSpan(@NonNull Drawable drawable) { 128 this(drawable, ALIGN_BOTTOM); 129 } 130 131 /** 132 * Constructs an {@link ImageSpan} from a drawable and a vertical alignment. 133 * 134 * @param drawable drawable to be rendered 135 * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or 136 * {@link DynamicDrawableSpan#ALIGN_BASELINE} 137 */ ImageSpan(@onNull Drawable drawable, int verticalAlignment)138 public ImageSpan(@NonNull Drawable drawable, int verticalAlignment) { 139 super(verticalAlignment); 140 mDrawable = drawable; 141 } 142 143 /** 144 * Constructs an {@link ImageSpan} from a drawable and a source with the default 145 * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM} 146 * 147 * @param drawable drawable to be rendered 148 * @param source drawable's Uri source 149 */ ImageSpan(@onNull Drawable drawable, @NonNull String source)150 public ImageSpan(@NonNull Drawable drawable, @NonNull String source) { 151 this(drawable, source, ALIGN_BOTTOM); 152 } 153 154 /** 155 * Constructs an {@link ImageSpan} from a drawable, a source and a vertical alignment. 156 * 157 * @param drawable drawable to be rendered 158 * @param source drawable's uri source 159 * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or 160 * {@link DynamicDrawableSpan#ALIGN_BASELINE} 161 */ ImageSpan(@onNull Drawable drawable, @NonNull String source, int verticalAlignment)162 public ImageSpan(@NonNull Drawable drawable, @NonNull String source, int verticalAlignment) { 163 super(verticalAlignment); 164 mDrawable = drawable; 165 mSource = source; 166 } 167 168 /** 169 * Constructs an {@link ImageSpan} from a {@link Context} and a {@link Uri} with the default 170 * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}. The Uri source can be retrieved via 171 * {@link #getSource()} 172 * 173 * @param context context used to create a drawable from {@param bitmap} based on the display 174 * metrics of the resources 175 * @param uri {@link Uri} used to construct the drawable that will be rendered 176 */ ImageSpan(@onNull Context context, @NonNull Uri uri)177 public ImageSpan(@NonNull Context context, @NonNull Uri uri) { 178 this(context, uri, ALIGN_BOTTOM); 179 } 180 181 /** 182 * Constructs an {@link ImageSpan} from a {@link Context}, a {@link Uri} and a vertical 183 * alignment. The Uri source can be retrieved via {@link #getSource()} 184 * 185 * @param context context used to create a drawable from {@param bitmap} based on 186 * the display 187 * metrics of the resources 188 * @param uri {@link Uri} used to construct the drawable that will be rendered. 189 * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or 190 * {@link DynamicDrawableSpan#ALIGN_BASELINE} 191 */ ImageSpan(@onNull Context context, @NonNull Uri uri, int verticalAlignment)192 public ImageSpan(@NonNull Context context, @NonNull Uri uri, int verticalAlignment) { 193 super(verticalAlignment); 194 mContext = context; 195 mContentUri = uri; 196 mSource = uri.toString(); 197 } 198 199 /** 200 * Constructs an {@link ImageSpan} from a {@link Context} and a resource id with the default 201 * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM} 202 * 203 * @param context context used to retrieve the drawable from resources 204 * @param resourceId drawable resource id based on which the drawable is retrieved 205 */ ImageSpan(@onNull Context context, @DrawableRes int resourceId)206 public ImageSpan(@NonNull Context context, @DrawableRes int resourceId) { 207 this(context, resourceId, ALIGN_BOTTOM); 208 } 209 210 /** 211 * Constructs an {@link ImageSpan} from a {@link Context}, a resource id and a vertical 212 * alignment. 213 * 214 * @param context context used to retrieve the drawable from resources 215 * @param resourceId drawable resource id based on which the drawable is retrieved. 216 * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or 217 * {@link DynamicDrawableSpan#ALIGN_BASELINE} 218 */ ImageSpan(@onNull Context context, @DrawableRes int resourceId, int verticalAlignment)219 public ImageSpan(@NonNull Context context, @DrawableRes int resourceId, 220 int verticalAlignment) { 221 super(verticalAlignment); 222 mContext = context; 223 mResourceId = resourceId; 224 } 225 226 @Override getDrawable()227 public Drawable getDrawable() { 228 Drawable drawable = null; 229 230 if (mDrawable != null) { 231 drawable = mDrawable; 232 } else if (mContentUri != null) { 233 Bitmap bitmap = null; 234 try { 235 InputStream is = mContext.getContentResolver().openInputStream( 236 mContentUri); 237 bitmap = BitmapFactory.decodeStream(is); 238 drawable = new BitmapDrawable(mContext.getResources(), bitmap); 239 drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), 240 drawable.getIntrinsicHeight()); 241 is.close(); 242 } catch (Exception e) { 243 Log.e("ImageSpan", "Failed to loaded content " + mContentUri, e); 244 } 245 } else { 246 try { 247 drawable = mContext.getDrawable(mResourceId); 248 drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), 249 drawable.getIntrinsicHeight()); 250 } catch (Exception e) { 251 Log.e("ImageSpan", "Unable to find resource: " + mResourceId); 252 } 253 } 254 255 return drawable; 256 } 257 258 /** 259 * Returns the source string that was saved during construction. 260 * 261 * @return the source string that was saved during construction 262 * @see #ImageSpan(Drawable, String) 263 * @see #ImageSpan(Context, Uri) 264 */ 265 @Nullable getSource()266 public String getSource() { 267 return mSource; 268 } 269 } 270