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