1 /*
2  * Copyright (C) 2017 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.app.ActivityManager;
20 import android.app.Notification;
21 import android.view.View;
22 
23 import java.util.ArrayList;
24 import java.util.Objects;
25 
26 /**
27  * A message of a {@link MessagingLayout}.
28  */
29 public interface MessagingMessage extends MessagingLinearLayout.MessagingChild {
30 
31     /**
32      * Prefix for supported image MIME types
33      **/
34     String IMAGE_MIME_TYPE_PREFIX = "image/";
35 
createMessage(IMessagingLayout layout, Notification.MessagingStyle.Message m, ImageResolver resolver, boolean usePrecomputedText)36     static MessagingMessage createMessage(IMessagingLayout layout,
37             Notification.MessagingStyle.Message m, ImageResolver resolver,
38             boolean usePrecomputedText) {
39         if (hasImage(m) && !ActivityManager.isLowRamDeviceStatic()) {
40             return MessagingImageMessage.createMessage(layout, m, resolver, usePrecomputedText);
41         } else {
42             return MessagingTextMessage.createMessage(layout, m, usePrecomputedText);
43         }
44     }
45 
dropCache()46     static void dropCache() {
47         MessagingTextMessage.dropCache();
48         MessagingImageMessage.dropCache();
49     }
50 
hasImage(Notification.MessagingStyle.Message m)51     static boolean hasImage(Notification.MessagingStyle.Message m) {
52         return m.getDataUri() != null
53                 && m.getDataMimeType() != null
54                 && m.getDataMimeType().startsWith(IMAGE_MIME_TYPE_PREFIX);
55     }
56 
57     /**
58      * Set a message for this view.
59      *
60      * @return true if setting the message worked
61      */
setMessage(Notification.MessagingStyle.Message message, boolean usePrecomputedText)62     default boolean setMessage(Notification.MessagingStyle.Message message,
63             boolean usePrecomputedText) {
64         getState().setMessage(message);
65         return true;
66     }
67 
getMessage()68     default Notification.MessagingStyle.Message getMessage() {
69         return getState().getMessage();
70     }
71 
sameAs(Notification.MessagingStyle.Message message)72     default boolean sameAs(Notification.MessagingStyle.Message message) {
73         Notification.MessagingStyle.Message ownMessage = getMessage();
74         // We have to make sure both messages are not null to go further comparison
75         if (message == null || ownMessage == null) {
76             return message == ownMessage;
77         }
78         if (!Objects.equals(message.getText(), ownMessage.getText())) {
79             return false;
80         }
81         if (!Objects.equals(message.getSender(), ownMessage.getSender())) {
82             return false;
83         }
84         boolean hasRemoteInputHistoryChanged = message.isRemoteInputHistory()
85                 != ownMessage.isRemoteInputHistory();
86         // When the remote input history has changed, we want to regard messages equal even when
87         // the timestamp changes. The main reason is that the message that the system inserts
88         // will have a different time set than the one that the app will update us with and we
89         // still want to reuse that message.
90         if (!hasRemoteInputHistoryChanged
91                 && !Objects.equals(message.getTimestamp(), ownMessage.getTimestamp())) {
92             return false;
93         }
94         if (!Objects.equals(message.getDataMimeType(), ownMessage.getDataMimeType())) {
95             return false;
96         }
97         if (!Objects.equals(message.getDataUri(), ownMessage.getDataUri())) {
98             return false;
99         }
100         return true;
101     }
102 
sameAs(MessagingMessage message)103     default boolean sameAs(MessagingMessage message) {
104         return sameAs(message.getMessage());
105     }
106 
removeMessage(ArrayList<MessagingLinearLayout.MessagingChild> toRecycle)107     default void removeMessage(ArrayList<MessagingLinearLayout.MessagingChild> toRecycle) {
108         getGroup().removeMessage(this, toRecycle);
109     }
110 
setMessagingGroup(MessagingGroup group)111     default void setMessagingGroup(MessagingGroup group) {
112         getState().setGroup(group);
113     }
114 
setIsHistoric(boolean isHistoric)115     default void setIsHistoric(boolean isHistoric) {
116         getState().setIsHistoric(isHistoric);
117     }
118 
getGroup()119     default MessagingGroup getGroup() {
120         return getState().getGroup();
121     }
122 
setIsHidingAnimated(boolean isHiding)123     default void setIsHidingAnimated(boolean isHiding) {
124         getState().setIsHidingAnimated(isHiding);
125     }
126 
127     @Override
isHidingAnimated()128     default boolean isHidingAnimated() {
129         return getState().isHidingAnimated();
130     }
131 
132     @Override
hideAnimated()133     default void hideAnimated() {
134         setIsHidingAnimated(true);
135         getGroup().performRemoveAnimation(getView(), () -> setIsHidingAnimated(false));
136     }
137 
hasOverlappingRendering()138     default boolean hasOverlappingRendering() {
139         return false;
140     }
141 
recycle()142     default void recycle() {
143         getState().recycle();
144     }
145 
getView()146     default View getView() {
147         return (View) this;
148     }
149 
setColor(int textColor)150     default void setColor(int textColor) {}
151 
getState()152     MessagingMessageState getState();
153 
setVisibility(int visibility)154     void setVisibility(int visibility);
155 
getVisibility()156     int getVisibility();
157 
158     /**
159      * Finalize inflation of the MessagingMessages, which should be called on Main Thread.
160      * @hide
161      */
finalizeInflate()162     void finalizeInflate();
163 }
164