Home | History | Annotate | Download | only in browse
      1 /*
      2  * Copyright (C) 2012 Google Inc.
      3  * Licensed to The Android Open Source Project.
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package com.android.mail.browse;
     19 
     20 import android.graphics.Canvas;
     21 import android.graphics.Paint;
     22 import android.graphics.Paint.FontMetricsInt;
     23 import android.text.Spanned;
     24 import android.text.TextPaint;
     25 import android.text.TextUtils;
     26 import android.text.style.CharacterStyle;
     27 import android.text.style.ReplacementSpan;
     28 
     29 /**
     30  * A replacement span to use when displaying folders in conversation view. Prevents a folder name
     31  * from wrapping mid-name, and ellipsizes very long folder names that can't fit on a single line.
     32  * Also ensures that folder text is drawn vertically centered within the background color chip.
     33  *
     34  */
     35 public class FolderSpan extends ReplacementSpan {
     36 
     37     public interface FolderSpanDimensions {
     38         int getPadding();
     39 
     40         /**
     41          * Returns the padding value that corresponds to the horizontal padding
     42          * surrounding the text inside the background color.
     43          */
     44         int getPaddingExtraWidth();
     45 
     46         /**
     47          * Returns the padding value for the space between folders.
     48          */
     49         int getPaddingBefore();
     50 
     51         /**
     52          * Returns the spacing above each line outside of the .
     53          */
     54         int getPaddingAbove();
     55         int getMaxWidth();
     56     }
     57 
     58     private TextPaint mWorkPaint = new TextPaint();
     59     /**
     60      * A reference to the enclosing Spanned object to collect other CharacterStyle spans and take
     61      * them into account when drawing.
     62      */
     63     private final Spanned mSpanned;
     64     private final FolderSpanDimensions mDims;
     65 
     66     public FolderSpan(Spanned spanned, FolderSpanDimensions dims) {
     67         mSpanned = spanned;
     68         mDims = dims;
     69     }
     70 
     71     @Override
     72     public int getSize(Paint paint, CharSequence text, int start, int end, FontMetricsInt fm) {
     73         if (fm != null) {
     74             final int paddingV = mDims.getPadding();
     75             paint.getFontMetricsInt(fm);
     76             // The magic set of measurements to vertically center text within a colored region!
     77             fm.ascent -= paddingV;
     78             fm.top = fm.ascent;
     79             fm.bottom += paddingV;
     80             fm.descent += paddingV;
     81         }
     82         return measureWidth(paint, text, start, end, true);
     83     }
     84 
     85     private int measureWidth(Paint paint, CharSequence text, int start, int end,
     86             boolean includePaddingBefore) {
     87         final int paddingW = mDims.getPadding() + mDims.getPaddingExtraWidth();
     88         final int maxWidth = mDims.getMaxWidth();
     89 
     90         int w = (int) paint.measureText(text, start, end) + 2 * paddingW;
     91         if (includePaddingBefore) {
     92             w += mDims.getPaddingBefore();
     93         }
     94         // TextView doesn't handle spans that are wider than the view very well, so cap their widths
     95         if (w > maxWidth) {
     96             w = maxWidth;
     97         }
     98         return w;
     99     }
    100 
    101     @Override
    102     public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top,
    103             int y, int bottom, Paint paint) {
    104 
    105         final int paddingW = mDims.getPadding() + mDims.getPaddingExtraWidth();
    106         final int paddingBefore = mDims.getPaddingBefore();
    107         final int paddingAbove = mDims.getPaddingAbove();
    108         final int maxWidth = mDims.getMaxWidth();
    109 
    110         mWorkPaint.set(paint);
    111 
    112         // take into account the foreground/background color spans when painting
    113         final CharacterStyle[] otherSpans = mSpanned.getSpans(start, end, CharacterStyle.class);
    114         for (CharacterStyle otherSpan : otherSpans) {
    115             otherSpan.updateDrawState(mWorkPaint);
    116         }
    117 
    118         final int bgWidth = measureWidth(mWorkPaint, text, start, end, false);
    119 
    120         // paint a background if present
    121         if (mWorkPaint.bgColor != 0) {
    122             final int prevColor = mWorkPaint.getColor();
    123             final Paint.Style prevStyle = mWorkPaint.getStyle();
    124 
    125             mWorkPaint.setColor(mWorkPaint.bgColor);
    126             mWorkPaint.setStyle(Paint.Style.FILL);
    127             canvas.drawRect(x + paddingBefore, top + paddingAbove, x + bgWidth + paddingBefore, bottom,
    128                     mWorkPaint);
    129 
    130             mWorkPaint.setColor(prevColor);
    131             mWorkPaint.setStyle(prevStyle);
    132         }
    133 
    134         // middle-ellipsize long strings
    135         if (bgWidth == maxWidth) {
    136             text = TextUtils.ellipsize(text.subSequence(start, end).toString(), mWorkPaint,
    137                     bgWidth - 2 * paddingW, TextUtils.TruncateAt.MIDDLE);
    138             start = 0;
    139             end = text.length();
    140         }
    141         canvas.drawText(text, start, end, x + paddingW + paddingBefore, y + paddingAbove, mWorkPaint);
    142     }
    143 
    144 }
    145