1package eu.siacs.conversations.entities;
2
3import android.content.Context;
4
5import androidx.annotation.NonNull;
6import androidx.annotation.Nullable;
7
8import java.lang.ref.WeakReference;
9import java.util.ArrayList;
10import java.util.Collections;
11import java.util.HashMap;
12import java.util.List;
13import java.util.Locale;
14import java.util.Map;
15
16import eu.siacs.conversations.utils.StringUtils;
17import eu.siacs.conversations.utils.UIHelper;
18import eu.siacs.conversations.xml.Element;
19import eu.siacs.conversations.xml.Namespace;
20import eu.siacs.conversations.xmpp.InvalidJid;
21import eu.siacs.conversations.xmpp.Jid;
22
23public class Bookmark extends Element implements ListItem {
24
25 private final Account account;
26 private WeakReference<Conversation> conversation;
27 private Jid jid;
28
29 public Bookmark(final Account account, final Jid jid) {
30 super("conference");
31 this.jid = jid;
32 this.setAttribute("jid", jid);
33 this.account = account;
34 }
35
36 private Bookmark(Account account) {
37 super("conference");
38 this.account = account;
39 }
40
41 public static Map<Jid, Bookmark> parseFromStorage(Element storage, Account account) {
42 if (storage == null) {
43 return Collections.emptyMap();
44 }
45 final HashMap<Jid, Bookmark> bookmarks = new HashMap<>();
46 for (final Element item : storage.getChildren()) {
47 if (item.getName().equals("conference")) {
48 final Bookmark bookmark = Bookmark.parse(item, account);
49 if (bookmark != null) {
50 final Bookmark old = bookmarks.put(bookmark.jid, bookmark);
51 if (old != null && old.getBookmarkName() != null && bookmark.getBookmarkName() == null) {
52 bookmark.setBookmarkName(old.getBookmarkName());
53 }
54 }
55 }
56 }
57 return bookmarks;
58 }
59
60 public static Map<Jid, Bookmark> parseFromPubsub(Element pubsub, 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 = InvalidJid.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 = InvalidJid.getNullForInvalid(item.getAttributeAsJid("id"));
98 if (bookmark.jid == null) {
99 return null;
100 }
101 bookmark.setBookmarkName(conference.getAttribute("name"));
102 bookmark.setAutojoin(conference.getAttributeAsBoolean("autojoin"));
103 bookmark.setNick(conference.findChildContent("nick"));
104 return bookmark;
105 }
106
107 public void setAutojoin(boolean autojoin) {
108 if (autojoin) {
109 this.setAttribute("autojoin", "true");
110 } else {
111 this.setAttribute("autojoin", "false");
112 }
113 }
114
115 @Override
116 public int compareTo(final @NonNull ListItem another) {
117 return this.getDisplayName().compareToIgnoreCase(
118 another.getDisplayName());
119 }
120
121 @Override
122 public String getDisplayName() {
123 final Conversation c = getConversation();
124 final String name = getBookmarkName();
125 if (c != null) {
126 return c.getName().toString();
127 } else if (printableValue(name, false)) {
128 return name.trim();
129 } else {
130 Jid jid = this.getJid();
131 return jid != null && jid.getLocal() != null ? jid.getLocal() : "";
132 }
133 }
134
135 public static boolean printableValue(@Nullable String value, boolean permitNone) {
136 return value != null && !value.trim().isEmpty() && (permitNone || !"None".equals(value));
137 }
138
139 public static boolean printableValue(@Nullable String value) {
140 return printableValue(value, true);
141 }
142
143 @Override
144 public Jid getJid() {
145 return this.jid;
146 }
147
148 public Jid getFullJid() {
149 final String nick = getNick();
150 return jid == null || nick == null || nick.trim().isEmpty() ? jid : jid.withResource(nick);
151 }
152
153 @Override
154 public List<Tag> getTags(Context context) {
155 ArrayList<Tag> tags = new ArrayList<>();
156 for (Element element : getChildren()) {
157 if (element.getName().equals("group") && element.getContent() != null) {
158 String group = element.getContent();
159 tags.add(new Tag(group, UIHelper.getColorForName(group,true)));
160 }
161 }
162 return tags;
163 }
164
165 public String getNick() {
166 return this.findChildContent("nick");
167 }
168
169 public void setNick(String nick) {
170 Element element = this.findChild("nick");
171 if (element == null) {
172 element = this.addChild("nick");
173 }
174 element.setContent(nick);
175 }
176
177 public boolean autojoin() {
178 return this.getAttributeAsBoolean("autojoin");
179 }
180
181 public String getPassword() {
182 return this.findChildContent("password");
183 }
184
185 public void setPassword(String password) {
186 Element element = this.findChild("password");
187 if (element != null) {
188 element.setContent(password);
189 }
190 }
191
192 @Override
193 public boolean match(Context context, String needle) {
194 if (needle == null) {
195 return true;
196 }
197 needle = needle.toLowerCase(Locale.US);
198 final Jid jid = getJid();
199 return (jid != null && jid.toString().contains(needle)) ||
200 getDisplayName().toLowerCase(Locale.US).contains(needle) ||
201 matchInTag(context, needle);
202 }
203
204 private boolean matchInTag(Context context, String needle) {
205 needle = needle.toLowerCase(Locale.US);
206 for (Tag tag : getTags(context)) {
207 if (tag.getName().toLowerCase(Locale.US).contains(needle)) {
208 return true;
209 }
210 }
211 return false;
212 }
213
214 public Account getAccount() {
215 return this.account;
216 }
217
218 public synchronized Conversation getConversation() {
219 return this.conversation != null ? this.conversation.get() : null;
220 }
221
222 public synchronized void setConversation(Conversation conversation) {
223 if (this.conversation != null) {
224 this.conversation.clear();
225 }
226 if (conversation == null) {
227 this.conversation = null;
228 } else {
229 this.conversation = new WeakReference<>(conversation);
230 conversation.getMucOptions().notifyOfBookmarkNick(getNick());
231 }
232 }
233
234 public String getBookmarkName() {
235 return this.getAttribute("name");
236 }
237
238 public boolean setBookmarkName(String name) {
239 String before = getBookmarkName();
240 if (name != null) {
241 this.setAttribute("name", name);
242 } else {
243 this.removeAttribute("name");
244 }
245 return StringUtils.changed(before, name);
246 }
247
248 @Override
249 public int getAvatarBackgroundColor() {
250 return UIHelper.getColorForName(jid != null ? jid.asBareJid().toString() : getDisplayName());
251 }
252
253 @Override
254 public String getAvatarName() {
255 return getDisplayName();
256 }
257}