add the 4 most frequently contacted contacts as app shortcuts
This commit is contained in:
parent
e48517b0c8
commit
2cf05528b4
|
@ -83,6 +83,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
|
||||||
}
|
}
|
||||||
mXmppConnectionService.updateConversationUi();
|
mXmppConnectionService.updateConversationUi();
|
||||||
mXmppConnectionService.updateRosterUi();
|
mXmppConnectionService.updateRosterUi();
|
||||||
|
mXmppConnectionService.getShortcutService().refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String avatarData(final IqPacket packet) {
|
public String avatarData(final IqPacket packet) {
|
||||||
|
|
|
@ -49,6 +49,7 @@ import eu.siacs.conversations.entities.Message;
|
||||||
import eu.siacs.conversations.entities.PresenceTemplate;
|
import eu.siacs.conversations.entities.PresenceTemplate;
|
||||||
import eu.siacs.conversations.entities.Roster;
|
import eu.siacs.conversations.entities.Roster;
|
||||||
import eu.siacs.conversations.entities.ServiceDiscoveryResult;
|
import eu.siacs.conversations.entities.ServiceDiscoveryResult;
|
||||||
|
import eu.siacs.conversations.services.ShortcutService;
|
||||||
import eu.siacs.conversations.utils.MimeUtils;
|
import eu.siacs.conversations.utils.MimeUtils;
|
||||||
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
|
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
|
||||||
import eu.siacs.conversations.xmpp.jid.Jid;
|
import eu.siacs.conversations.xmpp.jid.Jid;
|
||||||
|
@ -1423,4 +1424,21 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
db.execSQL("delete from " + START_TIMES_TABLE);
|
db.execSQL("delete from " + START_TIMES_TABLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<ShortcutService.FrequentContact> getFrequentContacts(int days) {
|
||||||
|
SQLiteDatabase db = this.getReadableDatabase();
|
||||||
|
final String SQL = "select "+Conversation.TABLENAME+"."+Conversation.ACCOUNT+","+Conversation.TABLENAME+"."+Conversation.CONTACTJID+" from "+Conversation.TABLENAME+" join "+Message.TABLENAME+" on conversations.uuid=messages.conversationUuid where messages.status!=0 and carbon==0 and conversations.mode=0 and messages.timeSent>=? group by conversations.uuid order by count(body) desc limit 4;";
|
||||||
|
String[] whereArgs = new String[]{String.valueOf(System.currentTimeMillis() - (Config.MILLISECONDS_IN_DAY * days))};
|
||||||
|
Cursor cursor = db.rawQuery(SQL,whereArgs);
|
||||||
|
ArrayList<ShortcutService.FrequentContact> contacts = new ArrayList<>();
|
||||||
|
while(cursor.moveToNext()) {
|
||||||
|
try {
|
||||||
|
contacts.add(new ShortcutService.FrequentContact(cursor.getString(0), Jid.fromString(cursor.getString(1))));
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.d(Config.LOGTAG,e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cursor.close();
|
||||||
|
return contacts;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,12 @@ package eu.siacs.conversations.services;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.PorterDuff;
|
||||||
|
import android.graphics.PorterDuffXfermode;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.Typeface;
|
import android.graphics.Typeface;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -65,6 +68,24 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded {
|
||||||
return avatar;
|
return avatar;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Bitmap getRoundedShortcut(final Contact contact) {
|
||||||
|
DisplayMetrics metrics = mXmppConnectionService.getResources().getDisplayMetrics();
|
||||||
|
int size = Math.round(metrics.density * 48);
|
||||||
|
Bitmap bitmap = get(contact,size);
|
||||||
|
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
|
||||||
|
Canvas canvas = new Canvas(output);
|
||||||
|
|
||||||
|
final Paint paint = new Paint();
|
||||||
|
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
|
||||||
|
|
||||||
|
paint.setAntiAlias(true);
|
||||||
|
canvas.drawARGB(0, 0, 0, 0);
|
||||||
|
canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2, bitmap.getWidth() / 2, paint);
|
||||||
|
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
|
||||||
|
canvas.drawBitmap(bitmap, rect, rect, paint);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
public Bitmap get(final MucOptions.User user, final int size, boolean cachedOnly) {
|
public Bitmap get(final MucOptions.User user, final int size, boolean cachedOnly) {
|
||||||
Contact c = user.getContact();
|
Contact c = user.getContact();
|
||||||
if (c != null && (c.getProfilePhoto() != null || c.getAvatar() != null || user.getAvatar() == null)) {
|
if (c != null && (c.getProfilePhoto() != null || c.getAvatar() != null || user.getAvatar() == null)) {
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
package eu.siacs.conversations.services;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.ShortcutInfo;
|
||||||
|
import android.content.pm.ShortcutManager;
|
||||||
|
import android.graphics.drawable.Icon;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.Config;
|
||||||
|
import eu.siacs.conversations.entities.Account;
|
||||||
|
import eu.siacs.conversations.entities.Contact;
|
||||||
|
import eu.siacs.conversations.ui.StartConversationActivity;
|
||||||
|
import eu.siacs.conversations.utils.ReplacingSerialSingleThreadExecutor;
|
||||||
|
import eu.siacs.conversations.xmpp.jid.Jid;
|
||||||
|
|
||||||
|
public class ShortcutService {
|
||||||
|
|
||||||
|
private final XmppConnectionService xmppConnectionService;
|
||||||
|
private final ReplacingSerialSingleThreadExecutor replacingSerialSingleThreadExecutor = new ReplacingSerialSingleThreadExecutor(false);
|
||||||
|
|
||||||
|
public ShortcutService(XmppConnectionService xmppConnectionService) {
|
||||||
|
this.xmppConnectionService = xmppConnectionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refresh() {
|
||||||
|
refresh(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refresh(final boolean forceUpdate) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
||||||
|
final Runnable r = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
refreshImpl(forceUpdate);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
replacingSerialSingleThreadExecutor.execute(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(25)
|
||||||
|
public void report(Contact contact) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
||||||
|
ShortcutManager shortcutManager = xmppConnectionService.getSystemService(ShortcutManager.class);
|
||||||
|
shortcutManager.reportShortcutUsed(getShortcutId(contact));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(25)
|
||||||
|
private void refreshImpl(boolean forceUpdate) {
|
||||||
|
List<FrequentContact> frequentContacts = xmppConnectionService.databaseBackend.getFrequentContacts(30);
|
||||||
|
HashMap<String,Account> accounts = new HashMap<>();
|
||||||
|
for(Account account : xmppConnectionService.getAccounts()) {
|
||||||
|
accounts.put(account.getUuid(),account);
|
||||||
|
}
|
||||||
|
List<Contact> contacts = new ArrayList<>();
|
||||||
|
for(FrequentContact frequentContact : frequentContacts) {
|
||||||
|
Account account = accounts.get(frequentContact.account);
|
||||||
|
if (account != null) {
|
||||||
|
contacts.add(account.getRoster().getContact(frequentContact.contact));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ShortcutManager shortcutManager = xmppConnectionService.getSystemService(ShortcutManager.class);
|
||||||
|
boolean needsUpdate = forceUpdate || contactsChanged(contacts,shortcutManager.getDynamicShortcuts());
|
||||||
|
if (!needsUpdate) {
|
||||||
|
Log.d(Config.LOGTAG,"skipping shortcut update");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<ShortcutInfo> newDynamicShortCuts = new ArrayList<>();
|
||||||
|
for (Contact contact : contacts) {
|
||||||
|
ShortcutInfo shortcut = new ShortcutInfo.Builder(xmppConnectionService, getShortcutId(contact))
|
||||||
|
.setShortLabel(contact.getDisplayName())
|
||||||
|
.setIntent(getShortcutIntent(contact))
|
||||||
|
.setIcon(Icon.createWithBitmap(xmppConnectionService.getAvatarService().getRoundedShortcut(contact)))
|
||||||
|
.build();
|
||||||
|
newDynamicShortCuts.add(shortcut);
|
||||||
|
}
|
||||||
|
if (shortcutManager.setDynamicShortcuts(newDynamicShortCuts)) {
|
||||||
|
Log.d(Config.LOGTAG,"updated dynamic shortcuts");
|
||||||
|
} else {
|
||||||
|
Log.d(Config.LOGTAG, "unable to update dynamic shortcuts");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean contactsChanged(List<Contact> needles, List<ShortcutInfo> haystack) {
|
||||||
|
for(Contact needle : needles) {
|
||||||
|
if(!contactExists(needle,haystack)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return needles.size() != haystack.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(25)
|
||||||
|
private static boolean contactExists(Contact needle, List<ShortcutInfo> haystack) {
|
||||||
|
for(ShortcutInfo shortcutInfo : haystack) {
|
||||||
|
if (getShortcutId(needle).equals(shortcutInfo.getId()) && needle.getDisplayName().equals(shortcutInfo.getShortLabel())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getShortcutId(Contact contact) {
|
||||||
|
return contact.getAccount().getJid().toBareJid().toPreppedString()+"#"+contact.getJid().toBareJid().toPreppedString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Intent getShortcutIntent(Contact contact) {
|
||||||
|
Intent intent = new Intent(xmppConnectionService, StartConversationActivity.class);
|
||||||
|
intent.setAction(Intent.ACTION_VIEW);
|
||||||
|
intent.setData(Uri.parse("xmpp:"+contact.getJid().toBareJid().toString()));
|
||||||
|
intent.putExtra("account",contact.getAccount().getJid().toBareJid().toString());
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class FrequentContact {
|
||||||
|
private final String account;
|
||||||
|
private final Jid contact;
|
||||||
|
|
||||||
|
public FrequentContact(String account, Jid contact) {
|
||||||
|
this.account = account;
|
||||||
|
this.contact = contact;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -65,6 +65,7 @@ import java.util.ListIterator;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
import de.duenndns.ssl.MemorizingTrustManager;
|
import de.duenndns.ssl.MemorizingTrustManager;
|
||||||
|
@ -182,8 +183,9 @@ public class XmppConnectionService extends Service {
|
||||||
};
|
};
|
||||||
private FileBackend fileBackend = new FileBackend(this);
|
private FileBackend fileBackend = new FileBackend(this);
|
||||||
private MemorizingTrustManager mMemorizingTrustManager;
|
private MemorizingTrustManager mMemorizingTrustManager;
|
||||||
private NotificationService mNotificationService = new NotificationService(
|
private NotificationService mNotificationService = new NotificationService(this);
|
||||||
this);
|
private ShortcutService mShortcutService = new ShortcutService(this);
|
||||||
|
private AtomicBoolean mInitialAddressbookSyncCompleted = new AtomicBoolean(false);
|
||||||
private OnMessagePacketReceived mMessageParser = new MessageParser(this);
|
private OnMessagePacketReceived mMessageParser = new MessageParser(this);
|
||||||
private OnPresencePacketReceived mPresenceParser = new PresenceParser(this);
|
private OnPresencePacketReceived mPresenceParser = new PresenceParser(this);
|
||||||
private IqParser mIqParser = new IqParser(this);
|
private IqParser mIqParser = new IqParser(this);
|
||||||
|
@ -1553,6 +1555,7 @@ public class XmppConnectionService extends Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log.d(Config.LOGTAG, "finished merging phone contacts");
|
Log.d(Config.LOGTAG, "finished merging phone contacts");
|
||||||
|
mShortcutService.refresh(mInitialAddressbookSyncCompleted.compareAndSet(false,true));
|
||||||
updateAccountUi();
|
updateAccountUi();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -3616,10 +3619,11 @@ public class XmppConnectionService extends Service {
|
||||||
return this.mMessageArchiveService;
|
return this.mMessageArchiveService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Contact> findContacts(Jid jid) {
|
public List<Contact> findContacts(Jid jid, String accountJid) {
|
||||||
ArrayList<Contact> contacts = new ArrayList<>();
|
ArrayList<Contact> contacts = new ArrayList<>();
|
||||||
for (Account account : getAccounts()) {
|
for (Account account : getAccounts()) {
|
||||||
if (!account.isOptionSet(Account.OPTION_DISABLED)) {
|
if (!account.isOptionSet(Account.OPTION_DISABLED)
|
||||||
|
&& (accountJid == null || accountJid.equals(account.getJid().toBareJid().toString()))) {
|
||||||
Contact contact = account.getRoster().getContactFromRoster(jid);
|
Contact contact = account.getRoster().getContactFromRoster(jid);
|
||||||
if (contact != null) {
|
if (contact != null) {
|
||||||
contacts.add(contact);
|
contacts.add(contact);
|
||||||
|
@ -3964,6 +3968,10 @@ public class XmppConnectionService extends Service {
|
||||||
return getPreferences().getBoolean(SettingsActivity.BLIND_TRUST_BEFORE_VERIFICATION, true);
|
return getPreferences().getBoolean(SettingsActivity.BLIND_TRUST_BEFORE_VERIFICATION, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ShortcutService getShortcutService() {
|
||||||
|
return mShortcutService;
|
||||||
|
}
|
||||||
|
|
||||||
public interface OnMamPreferencesFetched {
|
public interface OnMamPreferencesFetched {
|
||||||
void onPreferencesFetched(Element prefs);
|
void onPreferencesFetched(Element prefs);
|
||||||
void onPreferencesFetchFailed();
|
void onPreferencesFetchFailed();
|
||||||
|
|
|
@ -832,7 +832,9 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
|
||||||
case Intent.ACTION_VIEW:
|
case Intent.ACTION_VIEW:
|
||||||
Uri uri = intent.getData();
|
Uri uri = intent.getData();
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
return new Invite(intent.getData(),false).invite();
|
Invite invite = new Invite(intent.getData(),false);
|
||||||
|
invite.account = intent.getStringExtra("account");
|
||||||
|
return invite.invite();
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -871,7 +873,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
|
||||||
finish();
|
finish();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
List<Contact> contacts = xmppConnectionService.findContacts(invite.getJid());
|
List<Contact> contacts = xmppConnectionService.findContacts(invite.getJid(),invite.account);
|
||||||
if (invite.isMuc()) {
|
if (invite.isMuc()) {
|
||||||
Conversation muc = xmppConnectionService.findFirstMuc(invite.getJid());
|
Conversation muc = xmppConnectionService.findFirstMuc(invite.getJid());
|
||||||
if (muc != null) {
|
if (muc != null) {
|
||||||
|
@ -894,6 +896,9 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
|
||||||
Toast.makeText(this,R.string.verified_fingerprints,Toast.LENGTH_SHORT).show();
|
Toast.makeText(this,R.string.verified_fingerprints,Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (invite.account != null) {
|
||||||
|
xmppConnectionService.getShortcutService().report(contact);
|
||||||
|
}
|
||||||
switchToConversation(contact, invite.getBody());
|
switchToConversation(contact, invite.getBody());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -1183,6 +1188,8 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
|
||||||
super(uri,safeSource);
|
super(uri,safeSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String account;
|
||||||
|
|
||||||
boolean invite() {
|
boolean invite() {
|
||||||
if (getJid() != null) {
|
if (getJid() != null) {
|
||||||
return handleJid(this);
|
return handleJid(this);
|
||||||
|
|
Loading…
Reference in a new issue