1package eu.siacs.conversations.entities;
2
3import android.content.Context;
4import androidx.annotation.NonNull;
5import androidx.annotation.Nullable;
6import com.google.common.base.Strings;
7import com.google.common.collect.ImmutableList;
8import eu.siacs.conversations.utils.StringUtils;
9import eu.siacs.conversations.utils.UIHelper;
10import eu.siacs.conversations.xml.Element;
11import eu.siacs.conversations.xml.Namespace;
12import eu.siacs.conversations.xmpp.Jid;
13import java.lang.ref.WeakReference;
14import java.util.Collections;
15import java.util.HashMap;
16import java.util.List;
17import java.util.Locale;
18import java.util.Map;
19
20public class Bookmark extends Element implements ListItem {
21
22 private final Account account;
23 private WeakReference<Conversation> conversation;
24 private Jid jid;
25 protected Element extensions = new Element("extensions", Namespace.BOOKMARKS2);
26
27 public Bookmark(final Account account, final Jid jid) {
28 super("conference");
29 this.jid = jid;
30 this.setAttribute("jid", jid);
31 this.account = account;
32 }
33
34 private Bookmark(Account account) {
35 super("conference");
36 this.account = account;
37 }
38
39 public static Map<Jid, Bookmark> parseFromStorage(Element storage, Account account) {
40 if (storage == null) {
41 return Collections.emptyMap();
42 }
43 final HashMap<Jid, Bookmark> bookmarks = new HashMap<>();
44 for (final Element item : storage.getChildren()) {
45 if (item.getName().equals("conference")) {
46 final Bookmark bookmark = Bookmark.parse(item, account);
47 if (bookmark != null) {
48 final Bookmark old = bookmarks.put(bookmark.jid, bookmark);
49 if (old != null
50 && old.getBookmarkName() != null
51 && bookmark.getBookmarkName() == null) {
52 bookmark.setBookmarkName(old.getBookmarkName());
53 }
54 }
55 }
56 }
57 return bookmarks;
58 }
59
60 public static Map<Jid, Bookmark> parseFromPubSub(final Element pubSub, final Account account) {
61 if (pubSub == null) {
62 return Collections.emptyMap();
63 }
64 final Element items = pubSub.findChild("items");
65 if (items != null && Namespace.BOOKMARKS2.equals(items.getAttribute("node"))) {
66 final Map<Jid, Bookmark> bookmarks = new HashMap<>();
67 for (Element item : items.getChildren()) {
68 if (item.getName().equals("item")) {
69 final Bookmark bookmark = Bookmark.parseFromItem(item, account);
70 if (bookmark != null) {
71 bookmarks.put(bookmark.jid, bookmark);
72 }
73 }
74 }
75 return bookmarks;
76 }
77 return Collections.emptyMap();
78 }
79
80 public static Bookmark parse(Element element, Account account) {
81 Bookmark bookmark = new Bookmark(account);
82 bookmark.setAttributes(element.getAttributes());
83 bookmark.setChildren(element.getChildren());
84 bookmark.jid = Jid.Invalid.getNullForInvalid(bookmark.getAttributeAsJid("jid"));
85 if (bookmark.jid == null) {
86 return null;
87 }
88 return bookmark;
89 }
90
91 public static Bookmark parseFromItem(Element item, Account account) {
92 final Element conference = item.findChild("conference", Namespace.BOOKMARKS2);
93 if (conference == null) {
94 return null;
95 }
96 final Bookmark bookmark = new Bookmark(account);
97 bookmark.jid = Jid.Invalid.getNullForInvalid(item.getAttributeAsJid("id"));
98 // TODO verify that we only use bare jids and ignore full jids
99 if (bookmark.jid == null) {
100 return null;
101 }
102 bookmark.setBookmarkName(conference.getAttribute("name"));
103 bookmark.setAutojoin(conference.getAttributeAsBoolean("autojoin"));
104 bookmark.setNick(conference.findChildContent("nick"));
105 bookmark.setPassword(conference.findChildContent("password"));
106 final Element extensions = conference.findChild("extensions", Namespace.BOOKMARKS2);
107 if (extensions != null) {
108 bookmark.extensions = extensions;
109 }
110 return bookmark;
111 }
112
113 public Element getExtensions() {
114 return extensions;
115 }
116
117 public void setAutojoin(boolean autojoin) {
118 if (autojoin) {
119 this.setAttribute("autojoin", "true");
120 } else {
121 this.setAttribute("autojoin", "false");
122 }
123 }
124
125 @Override
126 public int compareTo(final @NonNull ListItem another) {
127 return this.getDisplayName().compareToIgnoreCase(another.getDisplayName());
128 }
129
130 @Override
131 public String getDisplayName() {
132 final Conversation c = getConversation();
133 final String name = getBookmarkName();
134 if (c != null) {
135 return c.getName().toString();
136 } else if (printableValue(name, false)) {
137 return name.trim();
138 } else {
139 Jid jid = this.getJid();
140 return jid != null && jid.getLocal() != null ? jid.getLocal() : "";
141 }
142 }
143
144 public static boolean printableValue(@Nullable String value, boolean permitNone) {
145 return value != null && !value.trim().isEmpty() && (permitNone || !"None".equals(value));
146 }
147
148 public static boolean printableValue(@Nullable String value) {
149 return printableValue(value, true);
150 }
151
152 @Override
153 public Jid getJid() {
154 return this.jid;
155 }
156
157 public Jid getFullJid() {
158 final String nick = Strings.nullToEmpty(getNick()).trim();
159 if (jid == null || nick.isEmpty()) {
160 return jid;
161 }
162 try {
163 return jid.withResource(nick);
164 } catch (final IllegalArgumentException e) {
165 return jid;
166 }
167 }
168
169 @Override
170 public List<Tag> getTags(final Context context) {
171 final ImmutableList.Builder<Tag> tags = new ImmutableList.Builder<>();
172 for (final Element element : getChildren()) {
173 final String content = element.getContent();
174 if (Strings.isNullOrEmpty(content)) {
175 continue;
176 }
177 if (element.getName().equals("group")) {
178 tags.add(new Tag(content));
179 }
180 }
181 return tags.build();
182 }
183
184 public String getNick() {
185 return Strings.emptyToNull(this.findChildContent("nick"));
186 }
187
188 public void setNick(String nick) {
189 Element element = this.findChild("nick");
190 if (element == null) {
191 element = this.addChild("nick");
192 }
193 element.setContent(nick);
194 }
195
196 public boolean autojoin() {
197 return this.getAttributeAsBoolean("autojoin");
198 }
199
200 public String getPassword() {
201 return this.findChildContent("password");
202 }
203
204 public void setPassword(String password) {
205 Element element = this.findChild("password");
206 if (element != null) {
207 element.setContent(password);
208 }
209 }
210
211 @Override
212 public boolean match(Context context, String needle) {
213 if (needle == null) {
214 return true;
215 }
216 needle = needle.toLowerCase(Locale.US);
217 final Jid jid = getJid();
218 return (jid != null && jid.toString().contains(needle))
219 || getDisplayName().toLowerCase(Locale.US).contains(needle)
220 || matchInTag(context, needle);
221 }
222
223 private boolean matchInTag(Context context, String needle) {
224 needle = needle.toLowerCase(Locale.US);
225 for (Tag tag : getTags(context)) {
226 if (tag.getName().toLowerCase(Locale.US).contains(needle)) {
227 return true;
228 }
229 }
230 return false;
231 }
232
233 public Account getAccount() {
234 return this.account;
235 }
236
237 public synchronized Conversation getConversation() {
238 return this.conversation != null ? this.conversation.get() : null;
239 }
240
241 public synchronized void setConversation(Conversation conversation) {
242 if (this.conversation != null) {
243 this.conversation.clear();
244 }
245 if (conversation == null) {
246 this.conversation = null;
247 } else {
248 this.conversation = new WeakReference<>(conversation);
249 }
250 }
251
252 public String getBookmarkName() {
253 return this.getAttribute("name");
254 }
255
256 public boolean setBookmarkName(String name) {
257 String before = getBookmarkName();
258 if (name != null) {
259 this.setAttribute("name", name);
260 } else {
261 this.removeAttribute("name");
262 }
263 return StringUtils.changed(before, name);
264 }
265
266 @Override
267 public int getAvatarBackgroundColor() {
268 return UIHelper.getColorForName(
269 jid != null ? jid.asBareJid().toString() : getDisplayName());
270 }
271
272 @Override
273 public String getAvatarName() {
274 return getDisplayName();
275 }
276}