1 /*
2  * Copyright (C) 2018 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.internal.widget;
18 
19 import android.annotation.AttrRes;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.StyleRes;
23 import android.app.Notification;
24 import android.content.Context;
25 import android.text.Layout;
26 import android.text.PrecomputedText;
27 import android.util.AttributeSet;
28 import android.util.Log;
29 import android.view.LayoutInflater;
30 import android.widget.RemoteViews;
31 
32 import com.android.internal.R;
33 
34 /**
35  * A message of a {@link MessagingLayout}.
36  */
37 @RemoteViews.RemoteView
38 public class MessagingTextMessage extends ImageFloatingTextView implements MessagingMessage {
39 
40     private static final String TAG = "MessagingTextMessage";
41     private static final MessagingPool<MessagingTextMessage> sInstancePool =
42             new MessagingPool<>(20);
43     private final MessagingMessageState mState = new MessagingMessageState(this);
44 
45     private PrecomputedText mPrecomputedText = null;
46 
MessagingTextMessage(@onNull Context context)47     public MessagingTextMessage(@NonNull Context context) {
48         super(context);
49     }
50 
MessagingTextMessage(@onNull Context context, @Nullable AttributeSet attrs)51     public MessagingTextMessage(@NonNull Context context, @Nullable AttributeSet attrs) {
52         super(context, attrs);
53     }
54 
MessagingTextMessage(@onNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr)55     public MessagingTextMessage(@NonNull Context context, @Nullable AttributeSet attrs,
56             @AttrRes int defStyleAttr) {
57         super(context, attrs, defStyleAttr);
58     }
59 
MessagingTextMessage(@onNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes)60     public MessagingTextMessage(@NonNull Context context, @Nullable AttributeSet attrs,
61             @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
62         super(context, attrs, defStyleAttr, defStyleRes);
63     }
64 
65     @Override
getState()66     public MessagingMessageState getState() {
67         return mState;
68     }
69 
70     @Override
setMessage(Notification.MessagingStyle.Message message, boolean usePrecomputedText)71     public boolean setMessage(Notification.MessagingStyle.Message message,
72             boolean usePrecomputedText) {
73         MessagingMessage.super.setMessage(message, usePrecomputedText);
74         if (usePrecomputedText) {
75             mPrecomputedText = PrecomputedText.create(message.getText(), getTextMetricsParams());
76         } else {
77             setText(message.getText());
78             mPrecomputedText = null;
79         }
80 
81         return true;
82     }
83 
createMessage(IMessagingLayout layout, Notification.MessagingStyle.Message m, boolean usePrecomputedText)84     static MessagingMessage createMessage(IMessagingLayout layout,
85             Notification.MessagingStyle.Message m, boolean usePrecomputedText) {
86         MessagingLinearLayout messagingLinearLayout = layout.getMessagingLinearLayout();
87         MessagingTextMessage createdMessage = sInstancePool.acquire();
88         if (createdMessage == null) {
89             createdMessage = (MessagingTextMessage) LayoutInflater.from(
90                     layout.getContext()).inflate(
91                     R.layout.notification_template_messaging_text_message,
92                     messagingLinearLayout,
93                     false);
94             createdMessage.addOnLayoutChangeListener(MessagingLayout.MESSAGING_PROPERTY_ANIMATOR);
95         }
96         createdMessage.setMessage(m, usePrecomputedText);
97         return createdMessage;
98     }
99 
recycle()100     public void recycle() {
101         MessagingMessage.super.recycle();
102         sInstancePool.release(this);
103     }
104 
dropCache()105     public static void dropCache() {
106         sInstancePool.clear();
107     }
108 
109     @Override
getMeasuredType()110     public int getMeasuredType() {
111         boolean measuredTooSmall = getMeasuredHeight()
112                 < getLayoutHeight() + getPaddingTop() + getPaddingBottom();
113         if (measuredTooSmall && getLineCount() <= 1) {
114             return MEASURED_TOO_SMALL;
115         } else {
116             Layout layout = getLayout();
117             if (layout == null) {
118                 return MEASURED_TOO_SMALL;
119             }
120             if (layout.getEllipsisCount(layout.getLineCount() - 1) > 0) {
121                 return MEASURED_SHORTENED;
122             } else {
123                 return MEASURED_NORMAL;
124             }
125         }
126     }
127 
128     @Override
129     public void setMaxDisplayedLines(int lines) {
130         setMaxLines(lines);
131     }
132 
133     @Override
134     public int getConsumedLines() {
135         return getLineCount();
136     }
137 
138     public int getLayoutHeight() {
139         Layout layout = getLayout();
140         if (layout == null) {
141             return 0;
142         }
143         return layout.getHeight();
144     }
145 
146     @Override
147     public void setColor(int color) {
148         setTextColor(color);
149     }
150 
151     @Override
152     public void finalizeInflate() {
153         try {
154             setText(mPrecomputedText != null ? mPrecomputedText
155                     : getState().getMessage().getText());
156         } catch (IllegalArgumentException exception) {
157             Log.wtf(
158                     /* tag = */ TAG,
159                     /* msg = */ "PrecomputedText setText failed for TextView:" + this,
160                     /* tr = */ exception
161             );
162             mPrecomputedText = null;
163             setText(getState().getMessage().getText());
164         }
165     }
166 }
167