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
18package com.android.mail.browse;
19
20import android.graphics.Canvas;
21import android.graphics.Paint;
22import android.graphics.Paint.FontMetricsInt;
23import android.text.Spanned;
24import android.text.TextPaint;
25import android.text.TextUtils;
26import android.text.style.CharacterStyle;
27import 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 */
35public 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