support contact shortcuts (#2918)
* support contact shortcuts * make ShortcutActivity extends AbstractSearchableListItemActivity * Draw the app icon in the corner of the icon and modify the name of the widget * updated label and icon size
This commit is contained in:
parent
85caaf084b
commit
044ea5c5a9
|
@ -106,6 +106,9 @@
|
||||||
android:name=".ui.StartConversationActivity"
|
android:name=".ui.StartConversationActivity"
|
||||||
android:label="@string/title_activity_start_conversation"
|
android:label="@string/title_activity_start_conversation"
|
||||||
android:launchMode="singleTop">
|
android:launchMode="singleTop">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.WelcomeActivity"
|
android:name=".ui.WelcomeActivity"
|
||||||
|
@ -222,7 +225,12 @@
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:grantUriPermissions="true"/>
|
android:grantUriPermissions="true"/>
|
||||||
|
|
||||||
|
<activity android:name=".ui.ShortcutActivity"
|
||||||
|
android:label="@string/contact">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.CREATE_SHORTCUT" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package eu.siacs.conversations.services;
|
package eu.siacs.conversations.services;
|
||||||
|
|
||||||
|
import android.content.res.Resources;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.PorterDuff;
|
import android.graphics.PorterDuff;
|
||||||
|
@ -21,6 +23,7 @@ import java.util.Locale;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import eu.siacs.conversations.Config;
|
import eu.siacs.conversations.Config;
|
||||||
|
import eu.siacs.conversations.R;
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.entities.Bookmark;
|
import eu.siacs.conversations.entities.Bookmark;
|
||||||
import eu.siacs.conversations.entities.Contact;
|
import eu.siacs.conversations.entities.Contact;
|
||||||
|
@ -76,21 +79,48 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bitmap getRoundedShortcut(final Contact contact) {
|
public Bitmap getRoundedShortcut(final Contact contact) {
|
||||||
|
return getRoundedShortcut(contact,false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bitmap getRoundedShortcutWithIcon(final Contact contact){
|
||||||
|
return getRoundedShortcut(contact,true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Bitmap getRoundedShortcut(final Contact contact,boolean withIcon) {
|
||||||
DisplayMetrics metrics = mXmppConnectionService.getResources().getDisplayMetrics();
|
DisplayMetrics metrics = mXmppConnectionService.getResources().getDisplayMetrics();
|
||||||
int size = Math.round(metrics.density * 48);
|
int size = Math.round(metrics.density * 48);
|
||||||
Bitmap bitmap = get(contact,size);
|
Bitmap bitmap = get(contact,size);
|
||||||
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
|
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
|
||||||
Canvas canvas = new Canvas(output);
|
Canvas canvas = new Canvas(output);
|
||||||
|
|
||||||
final Paint paint = new Paint();
|
final Paint paint = new Paint();
|
||||||
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
|
|
||||||
|
|
||||||
|
drawAvatar(bitmap, canvas, paint);
|
||||||
|
if(withIcon){
|
||||||
|
drawIcon(canvas, paint);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawAvatar(Bitmap bitmap, Canvas canvas, Paint paint) {
|
||||||
|
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
|
||||||
paint.setAntiAlias(true);
|
paint.setAntiAlias(true);
|
||||||
canvas.drawARGB(0, 0, 0, 0);
|
canvas.drawARGB(0, 0, 0, 0);
|
||||||
canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2, bitmap.getWidth() / 2, paint);
|
canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2, bitmap.getWidth() / 2, paint);
|
||||||
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
|
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
|
||||||
canvas.drawBitmap(bitmap, rect, rect, paint);
|
canvas.drawBitmap(bitmap, rect, rect, paint);
|
||||||
return output;
|
}
|
||||||
|
|
||||||
|
private void drawIcon(Canvas canvas, Paint paint) {
|
||||||
|
BitmapFactory.Options opts = new BitmapFactory.Options();
|
||||||
|
opts.inSampleSize = 3;
|
||||||
|
Resources resources = mXmppConnectionService.getResources();
|
||||||
|
Bitmap icon = BitmapFactory.decodeResource(resources, R.drawable.ic_launcher, opts);
|
||||||
|
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
|
||||||
|
|
||||||
|
int left = canvas.getWidth() - icon.getWidth();
|
||||||
|
int top = canvas.getHeight() - icon.getHeight();
|
||||||
|
final Rect rect = new Rect(left, top, left + icon.getWidth(), top + icon.getHeight());
|
||||||
|
canvas.drawBitmap(icon, null, rect, paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bitmap get(final MucOptions.User user, final int size, boolean cachedOnly) {
|
public Bitmap get(final MucOptions.User user, final int size, boolean cachedOnly) {
|
||||||
|
|
|
@ -4,9 +4,11 @@ import android.annotation.TargetApi;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.ShortcutInfo;
|
import android.content.pm.ShortcutInfo;
|
||||||
import android.content.pm.ShortcutManager;
|
import android.content.pm.ShortcutManager;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.drawable.Icon;
|
import android.graphics.drawable.Icon;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -75,11 +77,7 @@ public class ShortcutService {
|
||||||
}
|
}
|
||||||
List<ShortcutInfo> newDynamicShortCuts = new ArrayList<>();
|
List<ShortcutInfo> newDynamicShortCuts = new ArrayList<>();
|
||||||
for (Contact contact : contacts) {
|
for (Contact contact : contacts) {
|
||||||
ShortcutInfo shortcut = new ShortcutInfo.Builder(xmppConnectionService, getShortcutId(contact))
|
ShortcutInfo shortcut = getShortcutInfo(contact);
|
||||||
.setShortLabel(contact.getDisplayName())
|
|
||||||
.setIntent(getShortcutIntent(contact))
|
|
||||||
.setIcon(Icon.createWithBitmap(xmppConnectionService.getAvatarService().getRoundedShortcut(contact)))
|
|
||||||
.build();
|
|
||||||
newDynamicShortCuts.add(shortcut);
|
newDynamicShortCuts.add(shortcut);
|
||||||
}
|
}
|
||||||
if (shortcutManager.setDynamicShortcuts(newDynamicShortCuts)) {
|
if (shortcutManager.setDynamicShortcuts(newDynamicShortCuts)) {
|
||||||
|
@ -89,6 +87,15 @@ public class ShortcutService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.N_MR1)
|
||||||
|
private ShortcutInfo getShortcutInfo(Contact contact) {
|
||||||
|
return new ShortcutInfo.Builder(xmppConnectionService, getShortcutId(contact))
|
||||||
|
.setShortLabel(contact.getDisplayName())
|
||||||
|
.setIntent(getShortcutIntent(contact))
|
||||||
|
.setIcon(Icon.createWithBitmap(xmppConnectionService.getAvatarService().getRoundedShortcut(contact)))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean contactsChanged(List<Contact> needles, List<ShortcutInfo> haystack) {
|
private static boolean contactsChanged(List<Contact> needles, List<ShortcutInfo> haystack) {
|
||||||
for(Contact needle : needles) {
|
for(Contact needle : needles) {
|
||||||
if(!contactExists(needle,haystack)) {
|
if(!contactExists(needle,haystack)) {
|
||||||
|
@ -120,6 +127,30 @@ public class ShortcutService {
|
||||||
return intent;
|
return intent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public Intent createShortcut(Contact contact) {
|
||||||
|
Intent intent;
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
ShortcutInfo shortcut = getShortcutInfo(contact);
|
||||||
|
ShortcutManager shortcutManager = xmppConnectionService.getSystemService(ShortcutManager.class);
|
||||||
|
intent = shortcutManager.createShortcutResultIntent(shortcut);
|
||||||
|
} else {
|
||||||
|
intent = createShortcutResultIntent(contact);
|
||||||
|
}
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private Intent createShortcutResultIntent(Contact contact) {
|
||||||
|
Intent intent;AvatarService avatarService = xmppConnectionService.getAvatarService();
|
||||||
|
Bitmap icon = avatarService.getRoundedShortcutWithIcon(contact);
|
||||||
|
intent = new Intent();
|
||||||
|
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, contact.getDisplayName());
|
||||||
|
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon);
|
||||||
|
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, getShortcutIntent(contact));
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
|
||||||
public static class FrequentContact {
|
public static class FrequentContact {
|
||||||
private final String account;
|
private final String account;
|
||||||
private final Jid contact;
|
private final Jid contact;
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
package eu.siacs.conversations.ui;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v7.app.ActionBar;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.R;
|
||||||
|
import eu.siacs.conversations.entities.Account;
|
||||||
|
import eu.siacs.conversations.entities.Contact;
|
||||||
|
import eu.siacs.conversations.entities.ListItem;
|
||||||
|
|
||||||
|
public class ShortcutActivity extends AbstractSearchableListItemActivity{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void refreshUiReal() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
getListView().setOnItemClickListener((parent, view, position, id) -> {
|
||||||
|
final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
imm.hideSoftInputFromWindow(getSearchEditText().getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
|
||||||
|
|
||||||
|
ListItem listItem = getListItems().get(position);
|
||||||
|
Intent shortcut = xmppConnectionService.getShortcutService().createShortcut(((Contact) listItem));
|
||||||
|
setResult(RESULT_OK,shortcut);
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
binding.fab.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
ActionBar bar = getSupportActionBar();
|
||||||
|
if(bar != null){
|
||||||
|
bar.setTitle(R.string.create_shortcut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void filterContacts(String needle) {
|
||||||
|
getListItems().clear();
|
||||||
|
if (xmppConnectionService == null) {
|
||||||
|
getListItemAdapter().notifyDataSetChanged();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (final Account account : xmppConnectionService.getAccounts()) {
|
||||||
|
if (account.getStatus() != Account.State.DISABLED) {
|
||||||
|
for (final Contact contact : account.getRoster().getContacts()) {
|
||||||
|
if (contact.showInRoster()
|
||||||
|
&& contact.match(this, needle)) {
|
||||||
|
getListItems().add(contact);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Collections.sort(getListItems());
|
||||||
|
getListItemAdapter().notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
}
|
|
@ -49,6 +49,7 @@
|
||||||
<string name="start_conversation">Start conversation</string>
|
<string name="start_conversation">Start conversation</string>
|
||||||
<string name="invite_contact">Invite contact</string>
|
<string name="invite_contact">Invite contact</string>
|
||||||
<string name="contacts">Contacts</string>
|
<string name="contacts">Contacts</string>
|
||||||
|
<string name="contact">Contact</string>
|
||||||
<string name="cancel">Cancel</string>
|
<string name="cancel">Cancel</string>
|
||||||
<string name="set">Set</string>
|
<string name="set">Set</string>
|
||||||
<string name="add">Add</string>
|
<string name="add">Add</string>
|
||||||
|
@ -739,6 +740,7 @@
|
||||||
<string name="pref_omemo_setting_summary_always">OMEMO will always be used for one-on-one and private group chats.</string>
|
<string name="pref_omemo_setting_summary_always">OMEMO will always be used for one-on-one and private group chats.</string>
|
||||||
<string name="pref_omemo_setting_summary_default_on">OMEMO will be used by default for new conversations.</string>
|
<string name="pref_omemo_setting_summary_default_on">OMEMO will be used by default for new conversations.</string>
|
||||||
<string name="pref_omemo_setting_summary_default_off">OMEMO will have to be turned on explicitly for new conversations.</string>
|
<string name="pref_omemo_setting_summary_default_off">OMEMO will have to be turned on explicitly for new conversations.</string>
|
||||||
|
<string name="create_shortcut">Create Shortcut</string>
|
||||||
<string name="pref_font_size">Font Size</string>
|
<string name="pref_font_size">Font Size</string>
|
||||||
<string name="pref_font_size_summary">The relative font size used within the app.</string>
|
<string name="pref_font_size_summary">The relative font size used within the app.</string>
|
||||||
<string name="default_on">On by default</string>
|
<string name="default_on">On by default</string>
|
||||||
|
|
Loading…
Reference in New Issue