1package eu.siacs.conversations.services;
2
3import android.annotation.TargetApi;
4import android.content.Intent;
5import android.content.pm.ShortcutInfo;
6import android.content.pm.ShortcutManager;
7import android.graphics.Bitmap;
8import android.graphics.drawable.Icon;
9import android.net.Uri;
10import android.os.Build;
11import android.util.Log;
12
13import androidx.annotation.NonNull;
14
15import java.util.ArrayList;
16import java.util.HashMap;
17import java.util.List;
18
19import eu.siacs.conversations.Config;
20import eu.siacs.conversations.entities.Account;
21import eu.siacs.conversations.entities.Contact;
22import eu.siacs.conversations.ui.StartConversationActivity;
23import eu.siacs.conversations.utils.ReplacingSerialSingleThreadExecutor;
24import eu.siacs.conversations.xmpp.Jid;
25
26public class ShortcutService {
27
28 private final XmppConnectionService xmppConnectionService;
29 private final ReplacingSerialSingleThreadExecutor replacingSerialSingleThreadExecutor = new ReplacingSerialSingleThreadExecutor(ShortcutService.class.getSimpleName());
30
31 public ShortcutService(XmppConnectionService xmppConnectionService) {
32 this.xmppConnectionService = xmppConnectionService;
33 }
34
35 public void refresh() {
36 refresh(false);
37 }
38
39 public void refresh(final boolean forceUpdate) {
40 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
41 final Runnable r = new Runnable() {
42 @Override
43 public void run() {
44 refreshImpl(forceUpdate);
45 }
46 };
47 replacingSerialSingleThreadExecutor.execute(r);
48 }
49 }
50
51 @TargetApi(25)
52 public void report(Contact contact) {
53 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
54 ShortcutManager shortcutManager = xmppConnectionService.getSystemService(ShortcutManager.class);
55 shortcutManager.reportShortcutUsed(getShortcutId(contact));
56 }
57 }
58
59 @TargetApi(25)
60 private void refreshImpl(boolean forceUpdate) {
61 List<FrequentContact> frequentContacts = xmppConnectionService.databaseBackend.getFrequentContacts(30);
62 HashMap<String,Account> accounts = new HashMap<>();
63 for(Account account : xmppConnectionService.getAccounts()) {
64 accounts.put(account.getUuid(),account);
65 }
66 List<Contact> contacts = new ArrayList<>();
67 for(FrequentContact frequentContact : frequentContacts) {
68 Account account = accounts.get(frequentContact.account);
69 if (account != null) {
70 contacts.add(account.getRoster().getContact(frequentContact.contact));
71 }
72 }
73 ShortcutManager shortcutManager = xmppConnectionService.getSystemService(ShortcutManager.class);
74 boolean needsUpdate = forceUpdate || contactsChanged(contacts,shortcutManager.getDynamicShortcuts());
75 if (!needsUpdate) {
76 Log.d(Config.LOGTAG,"skipping shortcut update");
77 return;
78 }
79 List<ShortcutInfo> newDynamicShortCuts = new ArrayList<>();
80 for (Contact contact : contacts) {
81 ShortcutInfo shortcut = getShortcutInfo(contact);
82 newDynamicShortCuts.add(shortcut);
83 }
84 if (shortcutManager.setDynamicShortcuts(newDynamicShortCuts)) {
85 Log.d(Config.LOGTAG,"updated dynamic shortcuts");
86 } else {
87 Log.d(Config.LOGTAG, "unable to update dynamic shortcuts");
88 }
89 }
90
91 @TargetApi(Build.VERSION_CODES.N_MR1)
92 private ShortcutInfo getShortcutInfo(Contact contact) {
93 return new ShortcutInfo.Builder(xmppConnectionService, getShortcutId(contact))
94 .setShortLabel(contact.getDisplayName())
95 .setIntent(getShortcutIntent(contact))
96 .setIcon(Icon.createWithBitmap(xmppConnectionService.getAvatarService().getRoundedShortcut(contact)))
97 .build();
98 }
99
100 private static boolean contactsChanged(List<Contact> needles, List<ShortcutInfo> haystack) {
101 for(Contact needle : needles) {
102 if(!contactExists(needle,haystack)) {
103 return true;
104 }
105 }
106 return needles.size() != haystack.size();
107 }
108
109 @TargetApi(25)
110 private static boolean contactExists(Contact needle, List<ShortcutInfo> haystack) {
111 for(ShortcutInfo shortcutInfo : haystack) {
112 if (getShortcutId(needle).equals(shortcutInfo.getId()) && needle.getDisplayName().equals(shortcutInfo.getShortLabel())) {
113 return true;
114 }
115 }
116 return false;
117 }
118
119 private static String getShortcutId(Contact contact) {
120 return contact.getAccount().getJid().asBareJid().toEscapedString()+"#"+contact.getJid().asBareJid().toEscapedString();
121 }
122
123 private Intent getShortcutIntent(Contact contact) {
124 Intent intent = new Intent(xmppConnectionService, StartConversationActivity.class);
125 intent.setAction(Intent.ACTION_VIEW);
126 intent.setData(Uri.parse("xmpp:"+contact.getJid().asBareJid().toEscapedString()));
127 intent.putExtra("account",contact.getAccount().getJid().asBareJid().toString());
128 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP| Intent.FLAG_ACTIVITY_SINGLE_TOP);
129 return intent;
130 }
131
132 @NonNull
133 public Intent createShortcut(Contact contact, boolean legacy) {
134 Intent intent;
135 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !legacy) {
136 ShortcutInfo shortcut = getShortcutInfo(contact);
137 ShortcutManager shortcutManager = xmppConnectionService.getSystemService(ShortcutManager.class);
138 intent = shortcutManager.createShortcutResultIntent(shortcut);
139 } else {
140 intent = createShortcutResultIntent(contact);
141 }
142 return intent;
143 }
144
145 @NonNull
146 private Intent createShortcutResultIntent(Contact contact) {
147 AvatarService avatarService = xmppConnectionService.getAvatarService();
148 Bitmap icon = avatarService.getRoundedShortcutWithIcon(contact);
149 Intent intent = new Intent();
150 intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, contact.getDisplayName());
151 intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon);
152 intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, getShortcutIntent(contact));
153 return intent;
154 }
155
156 public static class FrequentContact {
157 private final String account;
158 private final Jid contact;
159
160 public FrequentContact(String account, Jid contact) {
161 this.account = account;
162 this.contact = contact;
163 }
164 }
165
166}