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