QSDualTileLabel.java revision baa958dc447c4f5c3261d9288c81b3fc7464126c
1/*
2 * Copyright (C) 2014 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
17package com.android.systemui.qs;
18
19import android.content.Context;
20import android.graphics.Typeface;
21import android.graphics.drawable.Drawable;
22import android.text.TextUtils;
23import android.text.TextUtils.TruncateAt;
24import android.view.Gravity;
25import android.view.View;
26import android.widget.FrameLayout;
27import android.widget.ImageView;
28import android.widget.LinearLayout;
29import android.widget.TextView;
30
31import com.android.systemui.R;
32
33import java.util.Objects;
34
35/**
36 * Text displayed over one or two lines, centered horizontally.  A caret is always drawn at the end
37 * of the first line, and considered part of the content for centering purposes.
38 *
39 * Text overflow rules:
40 *   First line: break on a word, unless a single word takes up the entire line - in which case
41 *               truncate.
42 *   Second line: ellipsis if necessary
43 */
44public class QSDualTileLabel extends FrameLayout {
45
46    private final Context mContext;
47    private final TextView mFirstLine;
48    private final ImageView mFirstLineCaret;
49    private final TextView mSecondLine;
50    private final int mHorizontalPaddingPx;
51
52    private String mText;
53
54    public QSDualTileLabel(Context context) {
55        super(context);
56        mContext = context;
57
58        mHorizontalPaddingPx = mContext.getResources()
59                .getDimensionPixelSize(R.dimen.qs_dual_tile_padding_horizontal);
60
61        mFirstLine = initTextView();
62        mFirstLine.setPadding(mHorizontalPaddingPx, 0, mHorizontalPaddingPx, 0);
63        final LinearLayout firstLineLayout = new LinearLayout(mContext);
64        firstLineLayout.setPadding(0, 0, 0, 0);
65        firstLineLayout.setOrientation(LinearLayout.HORIZONTAL);
66        firstLineLayout.setClickable(false);
67        firstLineLayout.setBackground(null);
68        firstLineLayout.addView(mFirstLine);
69        mFirstLineCaret = new ImageView(mContext);
70        mFirstLineCaret.setScaleType(ImageView.ScaleType.MATRIX);
71        mFirstLineCaret.setClickable(false);
72        firstLineLayout.addView(mFirstLineCaret);
73        addView(firstLineLayout, newFrameLayoutParams());
74
75        mSecondLine = initTextView();
76        mSecondLine.setPadding(mHorizontalPaddingPx, 0, mHorizontalPaddingPx, 0);
77        mSecondLine.setEllipsize(TruncateAt.END);
78        mSecondLine.setVisibility(GONE);
79        addView(mSecondLine, newFrameLayoutParams());
80
81        addOnLayoutChangeListener(new OnLayoutChangeListener() {
82            @Override
83            public void onLayoutChange(View v, int left, int top, int right,
84                    int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
85                if ((oldRight - oldLeft) != (right - left)) {
86                    rescheduleUpdateText();
87                }
88            }
89        });
90    }
91
92    private static LayoutParams newFrameLayoutParams() {
93        final LayoutParams lp =
94                new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
95        lp.gravity = Gravity.CENTER_HORIZONTAL;
96        return lp;
97    }
98
99    public void setFirstLineCaret(Drawable d) {
100        mFirstLineCaret.setImageDrawable(d);
101        if (d != null) {
102            final int h = d.getIntrinsicHeight();
103            final LayoutParams lp = (LayoutParams) mSecondLine.getLayoutParams();
104            lp.topMargin = h * 4 / 5;
105            mSecondLine.setLayoutParams(lp);
106            mFirstLine.setMinHeight(h);
107            mFirstLine.setPadding(mHorizontalPaddingPx, 0, 0, 0);
108        }
109    }
110
111    private TextView initTextView() {
112        final TextView tv = new TextView(mContext);
113        tv.setPadding(0, 0, 0, 0);
114        tv.setGravity(Gravity.CENTER_VERTICAL);
115        tv.setSingleLine(true);
116        tv.setClickable(false);
117        tv.setBackground(null);
118        return tv;
119    }
120
121    public void setText(CharSequence text) {
122        final String newText = text == null ? null : text.toString().trim();
123        if (Objects.equals(newText, mText)) return;
124        mText = newText;
125        rescheduleUpdateText();
126    }
127
128    public String getText() {
129        return mText;
130    }
131
132    public void setTextSize(int unit, float size) {
133        mFirstLine.setTextSize(unit, size);
134        mSecondLine.setTextSize(unit, size);
135        rescheduleUpdateText();
136    }
137
138    public void setTextColor(int color) {
139        mFirstLine.setTextColor(color);
140        mSecondLine.setTextColor(color);
141        rescheduleUpdateText();
142    }
143
144    public void setTypeface(Typeface tf) {
145        mFirstLine.setTypeface(tf);
146        mSecondLine.setTypeface(tf);
147        rescheduleUpdateText();
148    }
149
150    private void rescheduleUpdateText() {
151        removeCallbacks(mUpdateText);
152        post(mUpdateText);
153    }
154
155    private void updateText() {
156        if (getWidth() == 0) return;
157        if (TextUtils.isEmpty(mText)) {
158            mFirstLine.setText(null);
159            mSecondLine.setText(null);
160            mSecondLine.setVisibility(GONE);
161            return;
162        }
163        final float maxWidth = getWidth() - mFirstLineCaret.getWidth() - mHorizontalPaddingPx
164                - getPaddingLeft() - getPaddingRight();
165        float width = mFirstLine.getPaint().measureText(mText);
166        if (width <= maxWidth) {
167            mFirstLine.setText(mText);
168            mSecondLine.setText(null);
169            mSecondLine.setVisibility(GONE);
170            return;
171        }
172        final int n = mText.length();
173        int lastWordBoundary = -1;
174        boolean inWhitespace = false;
175        int i = 0;
176        for (i = 1; i < n; i++) {
177            width = mFirstLine.getPaint().measureText(mText.substring(0, i));
178            final boolean done = width > maxWidth;
179            if (Character.isWhitespace(mText.charAt(i))) {
180                if (!inWhitespace && !done) {
181                    lastWordBoundary = i;
182                }
183                inWhitespace = true;
184            } else {
185                inWhitespace = false;
186            }
187            if (done) {
188                break;
189            }
190        }
191        if (lastWordBoundary == -1) {
192            lastWordBoundary = i - 1;
193        }
194        mFirstLine.setText(mText.substring(0, lastWordBoundary));
195        mSecondLine.setText(mText.substring(lastWordBoundary).trim());
196        mSecondLine.setVisibility(VISIBLE);
197    }
198
199    private final Runnable mUpdateText = new Runnable() {
200        @Override
201        public void run() {
202            updateText();
203        }
204    };
205}
206