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 tags.add(new Tag("Channel", UIHelper.getColorForName("Channel",true)));
157 for (Element element : getChildren()) {
158 if (element.getName().equals("group") && element.getContent() != null) {
159 String group = element.getContent();
160 tags.add(new Tag(group, UIHelper.getColorForName(group,true)));
161 }
162 }
163 return tags;
164 }
165
166 public String getNick() {
167 return this.findChildContent("nick");
168 }
169
170 public void setNick(String nick) {
171 Element element = this.findChild("nick");
172 if (element == null) {
173 element = this.addChild("nick");
174 }
175 element.setContent(nick);
176 }
177
178 public boolean autojoin() {
179 return this.getAttributeAsBoolean("autojoin");
180 }
181
182 public String getPassword() {
183 return this.findChildContent("password");
184 }
185
186 public void setPassword(String password) {
187 Element element = this.findChild("password");
188 if (element != null) {
189 element.setContent(password);
190 }
191 }
192
193 @Override
194 public boolean match(Context context, String needle) {
195 if (needle == null) {
196 return true;
197 }
198 needle = needle.toLowerCase(Locale.US);
199 final Jid jid = getJid();
200 return (jid != null && jid.toString().contains(needle)) ||
201 getDisplayName().toLowerCase(Locale.US).contains(needle) ||
202 matchInTag(context, needle);
203 }
204
205 private boolean matchInTag(Context context, String needle) {
206 needle = needle.toLowerCase(Locale.US);
207 for (Tag tag : getTags(context)) {
208 if (tag.getName().toLowerCase(Locale.US).contains(needle)) {
209 return true;
210 }
211 }
212 return false;
213 }
214
215 public Account getAccount() {
216 return this.account;
217 }
218
219 public synchronized Conversation getConversation() {
220 return this.conversation != null ? this.conversation.get() : null;
221 }
222
223 public synchronized void setConversation(Conversation conversation) {
224 if (this.conversation != null) {
225 this.conversation.clear();
226 }
227 if (conversation == null) {
228 this.conversation = null;
229 } else {
230 this.conversation = new WeakReference<>(conversation);
231 conversation.getMucOptions().notifyOfBookmarkNick(getNick());
232 }
233 }
234
235 public String getBookmarkName() {
236 return this.getAttribute("name");
237 }
238
239 public boolean setBookmarkName(String name) {
240 String before = getBookmarkName();
241 if (name != null) {
242 this.setAttribute("name", name);
243 } else {
244 this.removeAttribute("name");
245 }
246 return StringUtils.changed(before, name);
247 }
248
249 @Override
250 public int getAvatarBackgroundColor() {
251 return UIHelper.getColorForName(jid != null ? jid.asBareJid().toString() : getDisplayName());
252 }
253
254 @Override
255 public String getAvatarName() {
256 return getDisplayName();
257 }
258}