blob: 09f7282f097a02532cbd40fc45c7769cd6d24223 [file] [log] [blame]
Selim Cinek4fb12d32015-11-19 18:10:48 -08001/*
2 * Copyright (C) 2015 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.internal.widget;
18
19import android.annotation.Nullable;
20import android.content.Context;
21import android.text.BoringLayout;
22import android.text.Layout;
23import android.text.StaticLayout;
24import android.text.TextUtils;
Selim Cinek47ec3392017-07-10 17:41:16 +020025import android.text.method.TransformationMethod;
Selim Cinek4fb12d32015-11-19 18:10:48 -080026import android.util.AttributeSet;
27import android.view.RemotableViewMethod;
28import android.widget.RemoteViews;
29import android.widget.TextView;
30
Selim Cinek37878682016-05-02 13:40:58 -070031import com.android.internal.R;
32
Selim Cinek4fb12d32015-11-19 18:10:48 -080033/**
34 * A TextView that can float around an image on the end.
35 *
36 * @hide
37 */
38@RemoteViews.RemoteView
39public class ImageFloatingTextView extends TextView {
40
Adrian Roosc1a80b02016-04-05 14:54:55 -070041 /** Number of lines from the top to indent */
42 private int mIndentLines;
Selim Cinek4fb12d32015-11-19 18:10:48 -080043
dongwan0605.kimdd8611f2016-09-16 16:26:57 +090044 /** Resolved layout direction */
45 private int mResolvedDirection = LAYOUT_DIRECTION_UNDEFINED;
Selim Cinek64aed1a2017-02-08 14:10:03 -080046 private int mMaxLinesForHeight = -1;
47 private boolean mFirstMeasure = true;
48 private int mLayoutMaxLines = -1;
49 private boolean mBlockLayouts;
dongwan0605.kimdd8611f2016-09-16 16:26:57 +090050
Selim Cinek4fb12d32015-11-19 18:10:48 -080051 public ImageFloatingTextView(Context context) {
52 this(context, null);
53 }
54
55 public ImageFloatingTextView(Context context, @Nullable AttributeSet attrs) {
56 this(context, attrs, 0);
57 }
58
59 public ImageFloatingTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
60 this(context, attrs, defStyleAttr, 0);
61 }
62
63 public ImageFloatingTextView(Context context, AttributeSet attrs, int defStyleAttr,
64 int defStyleRes) {
65 super(context, attrs, defStyleAttr, defStyleRes);
66 }
67
68 @Override
69 protected Layout makeSingleLayout(int wantWidth, BoringLayout.Metrics boring, int ellipsisWidth,
70 Layout.Alignment alignment, boolean shouldEllipsize,
71 TextUtils.TruncateAt effectiveEllipsize, boolean useSaved) {
Selim Cinek47ec3392017-07-10 17:41:16 +020072 TransformationMethod transformationMethod = getTransformationMethod();
73 CharSequence text = getText();
74 if (transformationMethod != null) {
75 text = transformationMethod.getTransformation(text, this);
76 }
77 text = text == null ? "" : text;
Selim Cinek4fb12d32015-11-19 18:10:48 -080078 StaticLayout.Builder builder = StaticLayout.Builder.obtain(text, 0, text.length(),
79 getPaint(), wantWidth)
80 .setAlignment(alignment)
81 .setTextDirection(getTextDirectionHeuristic())
82 .setLineSpacing(getLineSpacingExtra(), getLineSpacingMultiplier())
83 .setIncludePad(getIncludeFontPadding())
Roozbeh Pournader5caf5a62017-08-22 18:08:09 -070084 .setUseLineSpacingFromFallbacks(true)
Selim Cinek4fb12d32015-11-19 18:10:48 -080085 .setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY)
Selim Cinek64aed1a2017-02-08 14:10:03 -080086 .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
87 int maxLines;
88 if (mMaxLinesForHeight > 0) {
89 maxLines = mMaxLinesForHeight;
90 } else {
91 maxLines = getMaxLines() >= 0 ? getMaxLines() : Integer.MAX_VALUE;
92 }
93 builder.setMaxLines(maxLines);
94 mLayoutMaxLines = maxLines;
Adrian Roos62be4392016-12-05 13:59:52 -080095 if (shouldEllipsize) {
96 builder.setEllipsize(effectiveEllipsize)
97 .setEllipsizedWidth(ellipsisWidth);
98 }
99
Adrian Roosc1a80b02016-04-05 14:54:55 -0700100 // we set the endmargin on the requested number of lines.
101 int endMargin = getContext().getResources().getDimensionPixelSize(
102 R.dimen.notification_content_picture_margin);
103 int[] margins = null;
104 if (mIndentLines > 0) {
105 margins = new int[mIndentLines + 1];
106 for (int i = 0; i < mIndentLines; i++) {
107 margins[i] = endMargin;
108 }
109 }
dongwan0605.kimdd8611f2016-09-16 16:26:57 +0900110 if (mResolvedDirection == LAYOUT_DIRECTION_RTL) {
Selim Cinek4fb12d32015-11-19 18:10:48 -0800111 builder.setIndents(margins, null);
112 } else {
113 builder.setIndents(null, margins);
114 }
115
116 return builder.build();
117 }
118
dongwan0605.kimdd8611f2016-09-16 16:26:57 +0900119 @Override
Selim Cinek64aed1a2017-02-08 14:10:03 -0800120 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
121 int height = MeasureSpec.getSize(heightMeasureSpec);
122 // Lets calculate how many lines the given measurement allows us.
123 int availableHeight = height - mPaddingTop - mPaddingBottom;
124 int maxLines = availableHeight / getLineHeight();
Selim Cinek9281e5e2017-03-08 17:29:55 -0800125 maxLines = Math.max(1, maxLines);
Selim Cinek64aed1a2017-02-08 14:10:03 -0800126 if (getMaxLines() > 0) {
127 maxLines = Math.min(getMaxLines(), maxLines);
128 }
129 if (maxLines != mMaxLinesForHeight) {
130 mMaxLinesForHeight = maxLines;
131 if (getLayout() != null && mMaxLinesForHeight != mLayoutMaxLines) {
132 // Invalidate layout.
133 mBlockLayouts = true;
134 setHint(getHint());
135 mBlockLayouts = false;
136 }
137 }
138 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
139 }
140
141 @Override
142 public void requestLayout() {
143 if (!mBlockLayouts) {
144 super.requestLayout();
145 }
146 }
147
148 @Override
dongwan0605.kimdd8611f2016-09-16 16:26:57 +0900149 public void onRtlPropertiesChanged(int layoutDirection) {
150 super.onRtlPropertiesChanged(layoutDirection);
151
152 if (layoutDirection != mResolvedDirection && isLayoutDirectionResolved()) {
153 mResolvedDirection = layoutDirection;
154 if (mIndentLines > 0) {
155 // Invalidate layout.
156 setHint(getHint());
157 }
158 }
159 }
160
Selim Cinek4fb12d32015-11-19 18:10:48 -0800161 @RemotableViewMethod
162 public void setHasImage(boolean hasImage) {
Selim Cinek37878682016-05-02 13:40:58 -0700163 setNumIndentLines(hasImage ? 2 : 0);
Selim Cinek4fb12d32015-11-19 18:10:48 -0800164 }
Adrian Roosc1a80b02016-04-05 14:54:55 -0700165
166 /**
167 * @param lines the number of lines at the top that should be indented by indentEnd
168 * @return whether a change was made
169 */
170 public boolean setNumIndentLines(int lines) {
171 if (mIndentLines != lines) {
172 mIndentLines = lines;
173 // Invalidate layout.
174 setHint(getHint());
175 return true;
176 }
177 return false;
178 }
Selim Cinek4fb12d32015-11-19 18:10:48 -0800179}