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