Home | History | Annotate | Download | only in qs
      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