Added pinning of contacts

This commit is contained in:
M66B 2020-03-22 13:43:12 +01:00
parent ecd72103d6
commit 3c49c14511
6 changed files with 156 additions and 45 deletions

View File

@ -42,6 +42,8 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.PopupMenu;
import androidx.core.content.pm.ShortcutInfoCompat;
import androidx.core.content.pm.ShortcutManagerCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
@ -235,7 +237,9 @@ public class AdapterContact extends RecyclerView.Adapter<AdapterContact.ViewHold
popupMenu.getMenu().add(Menu.NONE, R.string.title_advanced_never_favorite, 1, R.string.title_advanced_never_favorite);
if (share.resolveActivity(context.getPackageManager()) != null)
popupMenu.getMenu().add(Menu.NONE, R.string.title_share, 2, R.string.title_share);
popupMenu.getMenu().add(Menu.NONE, R.string.title_delete, 3, R.string.title_delete);
if (ShortcutManagerCompat.isRequestPinShortcutSupported(context))
popupMenu.getMenu().add(Menu.NONE, R.string.title_pin, 3, R.string.title_pin);
popupMenu.getMenu().add(Menu.NONE, R.string.title_delete, 4, R.string.title_delete);
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
@ -247,6 +251,9 @@ public class AdapterContact extends RecyclerView.Adapter<AdapterContact.ViewHold
case R.string.title_share:
onActionShare();
return true;
case R.string.title_pin:
onActionPin();
return true;
case R.string.title_delete:
onActionDelete();
return true;
@ -290,6 +297,11 @@ public class AdapterContact extends RecyclerView.Adapter<AdapterContact.ViewHold
}
}
private void onActionPin() {
ShortcutInfoCompat.Builder builder = Shortcuts.getShortcut(context, contact);
ShortcutManagerCompat.requestPinShortcut(context, builder.build(), null);
}
private void onActionDelete() {
Bundle args = new Bundle();
args.putLong("id", contact.id);

View File

@ -114,6 +114,8 @@ import androidx.constraintlayout.helper.widget.Flow;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.Group;
import androidx.core.content.FileProvider;
import androidx.core.content.pm.ShortcutInfoCompat;
import androidx.core.content.pm.ShortcutManagerCompat;
import androidx.core.graphics.ColorUtils;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Lifecycle;
@ -212,6 +214,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
private int colorSeparator;
private boolean hasWebView;
private boolean pin;
private boolean contacts;
private float textSize;
@ -359,6 +362,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
private ImageButton ibSearchContact;
private ImageButton ibNotifyContact;
private ImageButton ibPinContact;
private ImageButton ibAddContact;
private TextView tvSubmitterTitle;
@ -529,6 +533,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
ibSearchContact = vsBody.findViewById(R.id.ibSearchContact);
ibNotifyContact = vsBody.findViewById(R.id.ibNotifyContact);
ibPinContact = vsBody.findViewById(R.id.ibPinContact);
ibAddContact = vsBody.findViewById(R.id.ibAddContact);
tvSubmitterTitle = vsBody.findViewById(R.id.tvSubmitterTitle);
@ -670,6 +675,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
ibExpanderAddress.setOnClickListener(this);
ibSearchContact.setOnClickListener(this);
ibNotifyContact.setOnClickListener(this);
ibPinContact.setOnClickListener(this);
ibAddContact.setOnClickListener(this);
btnSaveAttachments.setOnClickListener(this);
@ -743,6 +749,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
ibExpanderAddress.setOnClickListener(null);
ibSearchContact.setOnClickListener(null);
ibNotifyContact.setOnClickListener(null);
ibPinContact.setOnClickListener(null);
ibAddContact.setOnClickListener(null);
btnSaveAttachments.setOnClickListener(null);
@ -1141,6 +1148,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
ibSearchContact.setVisibility(View.GONE);
ibNotifyContact.setVisibility(View.GONE);
ibPinContact.setVisibility(View.GONE);
ibAddContact.setVisibility(View.GONE);
tvSubmitterTitle.setVisibility(View.GONE);
@ -1350,6 +1358,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
ibSearchContact.setVisibility(show_addresses && (hasFrom || hasTo) ? View.VISIBLE : View.GONE);
ibNotifyContact.setVisibility(show_addresses && hasChannel && hasFrom ? View.VISIBLE : View.GONE);
ibPinContact.setVisibility(show_addresses && pin && hasFrom ? View.VISIBLE : View.GONE);
ibAddContact.setVisibility(show_addresses && contacts && hasFrom ? View.VISIBLE : View.GONE);
tvSubmitterTitle.setVisibility(show_addresses && !TextUtils.isEmpty(submitter) ? View.VISIBLE : View.GONE);
@ -2541,6 +2550,8 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
onSearchContact(message);
else if (view.getId() == R.id.ibNotifyContact)
onNotifyContact(message);
else if (view.getId() == R.id.ibPinContact)
onPinContact(message);
else if (view.getId() == R.id.ibAddContact)
onAddContact(message);
else if (viewType == ViewType.THREAD) {
@ -2929,7 +2940,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
final NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
final String channelId = message.getNotificationChannelId();
PopupMenuLifecycle popupMenu = new PopupMenuLifecycle(context, powner, ibAddContact);
PopupMenuLifecycle popupMenu = new PopupMenuLifecycle(context, powner, ibNotifyContact);
NotificationChannel channel = nm.getNotificationChannel(channelId);
if (channel == null)
popupMenu.getMenu().add(Menu.NONE, R.string.title_create_channel, 1, R.string.title_create_channel);
@ -2994,6 +3005,12 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
popupMenu.show();
}
private void onPinContact(TupleMessageEx message) {
ShortcutInfoCompat.Builder builder =
Shortcuts.getShortcut(context, (InternetAddress) message.from[0]);
ShortcutManagerCompat.requestPinShortcut(context, builder.build(), null);
}
private void onAddContact(TupleMessageEx message) {
for (Address address : message.from) {
InternetAddress ia = (InternetAddress) address;
@ -4401,6 +4418,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
this.colorSeparator = Helper.resolveColor(context, R.attr.colorSeparator);
this.hasWebView = Helper.hasWebView(context);
this.pin = ShortcutManagerCompat.isRequestPinShortcutSupported(context);
this.contacts = Helper.hasPermission(context, Manifest.permission.READ_CONTACTS);
this.textSize = Helper.getTextSize(context, zoom);

View File

@ -21,6 +21,7 @@ package eu.faircode.email;
import android.Manifest;
import android.annotation.TargetApi;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@ -32,6 +33,7 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.text.TextUtils;
import android.widget.Toast;
import androidx.core.app.Person;
@ -41,6 +43,8 @@ import androidx.core.graphics.drawable.IconCompat;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.PreferenceManager;
import org.jetbrains.annotations.NotNull;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
@ -48,6 +52,8 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.mail.internet.InternetAddress;
class Shortcuts {
private static final int MAX_SHORTCUTS = 4;
@ -87,48 +93,10 @@ class Shortcuts {
if (emails.contains(email))
continue;
emails.add(email);
EntityLog.log(context, "Shortcut email=" + email);
Intent intent = new Intent(context, ActivityMain.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.setAction(Intent.ACTION_SEND);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setData(Uri.parse("mailto:" + email));
IconCompat icon = null;
if (avatar != null &&
Helper.hasPermission(context, Manifest.permission.READ_CONTACTS)) {
// Create icon from bitmap because launcher might not have contacts permission
InputStream is = ContactsContract.Contacts.openContactPhotoInputStream(
context.getContentResolver(), Uri.parse(avatar));
Bitmap bitmap = BitmapFactory.decodeStream(is);
if (bitmap != null)
icon = IconCompat.createWithBitmap(bitmap);
}
if (icon == null)
icon = IconCompat.createWithResource(context, R.drawable.ic_shortcut_email);
Set<String> categories = new HashSet<>(Arrays.asList("eu.faircode.email.TEXT_SHARE_TARGET"));
String id = (name == null ? email : "\"" + name + "\" <" + email + ">");
ShortcutInfoCompat.Builder builder = new ShortcutInfoCompat.Builder(context, id)
.setIcon(icon)
.setRank(shortcuts.size() + 1)
.setShortLabel(name == null ? email : name)
.setLongLabel(name == null ? email : name)
.setCategories(categories)
.setIntent(intent);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
Person.Builder person = new Person.Builder()
.setIcon(icon)
.setName(name == null ? email : name)
.setImportant(true);
if (avatar != null)
person.setUri(avatar);
builder.setPerson(person.build());
}
ShortcutInfoCompat.Builder builder = getShortcut(context, email, name, avatar);
builder.setRank(shortcuts.size() + 1);
shortcuts.add(builder.build());
}
}
@ -149,4 +117,94 @@ class Shortcuts {
}
}.execute(context, owner, new Bundle(), "shortcuts:update");
}
@NotNull
static ShortcutInfoCompat.Builder getShortcut(Context context, InternetAddress address) {
String name = address.getPersonal();
String email = address.getAddress();
Uri lookupUri = null;
boolean contacts = Helper.hasPermission(context, Manifest.permission.READ_CONTACTS);
if (contacts) {
ContentResolver resolver = context.getContentResolver();
try (Cursor cursor = resolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI,
new String[]{
ContactsContract.CommonDataKinds.Photo.CONTACT_ID,
ContactsContract.Contacts.LOOKUP_KEY,
ContactsContract.Contacts.DISPLAY_NAME
},
ContactsContract.CommonDataKinds.Email.ADDRESS + " = ?",
new String[]{email}, null)) {
if (cursor != null && cursor.moveToNext()) {
int colContactId = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Photo.CONTACT_ID);
int colLookupKey = cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY);
int colDisplayName = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
long contactId = cursor.getLong(colContactId);
String lookupKey = cursor.getString(colLookupKey);
String displayName = cursor.getString(colDisplayName);
lookupUri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey);
if (!TextUtils.isEmpty(displayName))
name = displayName;
}
}
}
return getShortcut(context, email, name, lookupUri);
}
@NotNull
static ShortcutInfoCompat.Builder getShortcut(Context context, EntityContact contact) {
return getShortcut(context, contact.email, contact.name, contact.avatar);
}
@NotNull
private static ShortcutInfoCompat.Builder getShortcut(Context context, String email, String name, String avatar) {
return getShortcut(context, email, name, avatar == null ? null : Uri.parse(avatar));
}
@NotNull
private static ShortcutInfoCompat.Builder getShortcut(Context context, String email, String name, Uri avatar) {
Intent intent = new Intent(context, ActivityMain.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.setAction(Intent.ACTION_SEND);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setData(Uri.parse("mailto:" + email));
IconCompat icon = null;
if (avatar != null &&
Helper.hasPermission(context, Manifest.permission.READ_CONTACTS)) {
// Create icon from bitmap because launcher might not have contacts permission
InputStream is = ContactsContract.Contacts.openContactPhotoInputStream(
context.getContentResolver(), avatar);
Bitmap bitmap = BitmapFactory.decodeStream(is);
if (bitmap != null)
icon = IconCompat.createWithBitmap(bitmap);
}
if (icon == null)
icon = IconCompat.createWithResource(context, R.drawable.ic_shortcut_email);
Set<String> categories = new HashSet<>(Arrays.asList("eu.faircode.email.TEXT_SHARE_TARGET"));
String id = (name == null ? email : "\"" + name + "\" <" + email + ">");
ShortcutInfoCompat.Builder builder = new ShortcutInfoCompat.Builder(context, id)
.setIcon(icon)
.setShortLabel(name == null ? email : name)
.setLongLabel(name == null ? email : name)
.setCategories(categories)
.setIntent(intent);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
Person.Builder person = new Person.Builder()
.setIcon(icon)
.setName(name == null ? email : name)
.setImportant(true);
if (avatar != null)
person.setUri(avatar.toString());
builder.setPerson(person.build());
}
return builder;
}
}

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C8.14,2 5,5.14 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13C19,5.14 15.86,2 12,2zM12,4c1.1,0 2,0.9 2,2c0,1.11 -0.9,2 -2,2s-2,-0.89 -2,-2C10,4.9 10.9,4 12,4zM12,14c-1.67,0 -3.14,-0.85 -4,-2.15c0.02,-1.32 2.67,-2.05 4,-2.05s3.98,0.73 4,2.05C15.14,13.15 13.67,14 12,14z"/>
</vector>

View File

@ -75,10 +75,22 @@
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/title_legend_notify"
android:tooltipText="@string/title_legend_notify"
app:layout_constraintEnd_toStartOf="@+id/ibAddContact"
app:layout_constraintEnd_toStartOf="@+id/ibPinContact"
app:layout_constraintTop_toBottomOf="@id/ibExpanderAddress"
app:srcCompat="@drawable/baseline_notifications_24" />
<ImageButton
android:id="@+id/ibPinContact"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/title_legend_notify"
android:tooltipText="@string/title_pin"
app:layout_constraintEnd_toStartOf="@+id/ibAddContact"
app:layout_constraintTop_toBottomOf="@id/ibExpanderAddress"
app:srcCompat="@drawable/baseline_person_pin_circle_24" />
<ImageButton
android:id="@+id/ibAddContact"
android:layout_width="wrap_content"
@ -96,7 +108,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="ibSearchContact,ibNotifyContact,ibAddContact" />
app:constraint_referenced_ids="ibSearchContact,ibNotifyContact,ibPinContact,ibAddContact" />
<eu.faircode.email.FixedTextView
android:id="@+id/tvSubmitterTitle"

View File

@ -687,6 +687,7 @@
<string name="title_editasnew">Edit as new</string>
<string name="title_create_rule">Create rule &#8230;</string>
<string name="title_share">Share</string>
<string name="title_pin">Add shortcut</string>
<string name="title_print">Print</string>
<string name="title_show_headers">Show headers</string>
<string name="title_raw_save">Save raw message</string>