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