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 17 package com.android.systemui.qs; 18 19 import android.content.Context; 20 import android.graphics.Typeface; 21 import android.graphics.drawable.Drawable; 22 import android.text.TextUtils; 23 import android.text.TextUtils.TruncateAt; 24 import android.view.Gravity; 25 import android.view.View; 26 import android.widget.ImageView; 27 import android.widget.LinearLayout; 28 import android.widget.TextView; 29 30 import com.android.systemui.R; 31 32 import java.util.Objects; 33 34 /** 35 * Text displayed over one or two lines, centered horizontally. A caret is always drawn at the end 36 * of the first line, and considered part of the content for centering purposes. 37 * 38 * Text overflow rules: 39 * First line: break on a word, unless a single word takes up the entire line - in which case 40 * truncate. 41 * Second line: ellipsis if necessary 42 */ 43 public class QSDualTileLabel extends LinearLayout { 44 45 private final Context mContext; 46 private final TextView mFirstLine; 47 private final ImageView mFirstLineCaret; 48 private final TextView mSecondLine; 49 private final int mHorizontalPaddingPx; 50 51 private String mText; 52 53 public QSDualTileLabel(Context context) { 54 super(context); 55 mContext = context; 56 setOrientation(LinearLayout.VERTICAL); 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, newLinearLayoutParams()); 74 75 mSecondLine = initTextView(); 76 mSecondLine.setPadding(mHorizontalPaddingPx, 0, mHorizontalPaddingPx, 0); 77 mSecondLine.setEllipsize(TruncateAt.END); 78 mSecondLine.setVisibility(GONE); 79 addView(mSecondLine, newLinearLayoutParams()); 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 newLinearLayoutParams() { 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 mFirstLine.setMinHeight(h); 104 mFirstLine.setPadding(mHorizontalPaddingPx, 0, 0, 0); 105 } 106 } 107 108 private TextView initTextView() { 109 final TextView tv = new TextView(mContext); 110 tv.setPadding(0, 0, 0, 0); 111 tv.setGravity(Gravity.CENTER_VERTICAL); 112 tv.setSingleLine(true); 113 tv.setClickable(false); 114 tv.setBackground(null); 115 return tv; 116 } 117 118 public void setText(CharSequence text) { 119 final String newText = text == null ? null : text.toString().trim(); 120 if (Objects.equals(newText, mText)) return; 121 mText = newText; 122 rescheduleUpdateText(); 123 } 124 125 public String getText() { 126 return mText; 127 } 128 129 public void setTextSize(int unit, float size) { 130 mFirstLine.setTextSize(unit, size); 131 mSecondLine.setTextSize(unit, size); 132 rescheduleUpdateText(); 133 } 134 135 public void setTextColor(int color) { 136 mFirstLine.setTextColor(color); 137 mSecondLine.setTextColor(color); 138 rescheduleUpdateText(); 139 } 140 141 public void setTypeface(Typeface tf) { 142 mFirstLine.setTypeface(tf); 143 mSecondLine.setTypeface(tf); 144 rescheduleUpdateText(); 145 } 146 147 private void rescheduleUpdateText() { 148 removeCallbacks(mUpdateText); 149 post(mUpdateText); 150 } 151 152 private void updateText() { 153 if (getWidth() == 0) return; 154 if (TextUtils.isEmpty(mText)) { 155 mFirstLine.setText(null); 156 mSecondLine.setText(null); 157 mSecondLine.setVisibility(GONE); 158 return; 159 } 160 final float maxWidth = getWidth() - mFirstLineCaret.getWidth() - mHorizontalPaddingPx 161 - getPaddingLeft() - getPaddingRight(); 162 float width = mFirstLine.getPaint().measureText(mText); 163 if (width <= maxWidth) { 164 mFirstLine.setText(mText); 165 mSecondLine.setText(null); 166 mSecondLine.setVisibility(GONE); 167 return; 168 } 169 final int n = mText.length(); 170 int lastWordBoundary = -1; 171 boolean inWhitespace = false; 172 int i = 0; 173 for (i = 1; i < n; i++) { 174 width = mFirstLine.getPaint().measureText(mText.substring(0, i)); 175 final boolean done = width > maxWidth; 176 if (Character.isWhitespace(mText.charAt(i))) { 177 if (!inWhitespace && !done) { 178 lastWordBoundary = i; 179 } 180 inWhitespace = true; 181 } else { 182 inWhitespace = false; 183 } 184 if (done) { 185 break; 186 } 187 } 188 if (lastWordBoundary == -1) { 189 lastWordBoundary = i - 1; 190 } 191 mFirstLine.setText(mText.substring(0, lastWordBoundary)); 192 mSecondLine.setText(mText.substring(lastWordBoundary).trim()); 193 mSecondLine.setVisibility(VISIBLE); 194 } 195 196 private final Runnable mUpdateText = new Runnable() { 197 @Override 198 public void run() { 199 updateText(); 200 } 201 }; 202 } 203