ShortcutService.java

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