Browse Source

Show contact photos

Fixes #61
main
M66B 6 years ago
parent
commit
7b74f87e0f
4 changed files with 78 additions and 3 deletions
  1. +1
    -1
      FAQ.md
  2. +64
    -0
      app/src/main/java/eu/faircode/email/AdapterMessage.java
  3. +12
    -1
      app/src/main/res/layout/item_message.xml
  4. +1
    -1
      app/src/main/res/values/strings.xml

+ 1
- 1
FAQ.md View File

@ -14,7 +14,7 @@ If you have a question, please check the frequently asked questions below first.
* Run at startup (RECEIVE_BOOT_COMPLETED): to start monitoring on device start * Run at startup (RECEIVE_BOOT_COMPLETED): to start monitoring on device start
* In-app billing (BILLING): to allow in-app purchases * In-app billing (BILLING): to allow in-app purchases
* Foreground service (FOREGROUND_SERVICE): to run a foreground service on Android 9 Pie and later, see also the next question * Foreground service (FOREGROUND_SERVICE): to run a foreground service on Android 9 Pie and later, see also the next question
* Optional: read your contacts (READ_CONTACTS): to autocomplete addresses
* Optional: read your contacts (READ_CONTACTS): to autocomplete addresses and to show photos
* Optional: find accounts on the device (GET_ACCOUNTS): to use [OAuth](https://en.wikipedia.org/wiki/OAuth) instead of passwords * Optional: find accounts on the device (GET_ACCOUNTS): to use [OAuth](https://en.wikipedia.org/wiki/OAuth) instead of passwords
<a name="FAQ2"></a> <a name="FAQ2"></a>


+ 64
- 0
app/src/main/java/eu/faircode/email/AdapterMessage.java View File

@ -19,11 +19,19 @@ package eu.faircode.email;
Copyright 2018 by Marcel Bokhorst (M66B) Copyright 2018 by Marcel Bokhorst (M66B)
*/ */
import android.Manifest;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.provider.ContactsContract;
import android.text.format.DateUtils; import android.text.format.DateUtils;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
@ -35,13 +43,17 @@ import android.widget.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import java.io.InputStream;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import javax.mail.internet.InternetAddress;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.widget.PopupMenu; import androidx.appcompat.widget.PopupMenu;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.Observer; import androidx.lifecycle.Observer;
import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.localbroadcastmanager.content.LocalBroadcastManager;
@ -55,6 +67,7 @@ public class AdapterMessage extends PagedListAdapter<TupleMessageEx, AdapterMess
private ViewType viewType; private ViewType viewType;
private boolean debug; private boolean debug;
private boolean hasContactsPermission;
private DateFormat df = SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.SHORT, SimpleDateFormat.LONG); private DateFormat df = SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.SHORT, SimpleDateFormat.LONG);
enum ViewType {UNIFIED, FOLDER, THREAD, SEARCH} enum ViewType {UNIFIED, FOLDER, THREAD, SEARCH}
@ -62,6 +75,7 @@ public class AdapterMessage extends PagedListAdapter<TupleMessageEx, AdapterMess
public class ViewHolder extends RecyclerView.ViewHolder public class ViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener, View.OnLongClickListener { implements View.OnClickListener, View.OnLongClickListener {
View itemView; View itemView;
ImageView ivAvatar;
ImageView ivFlagged; ImageView ivFlagged;
TextView tvFrom; TextView tvFrom;
TextView tvTime; TextView tvTime;
@ -80,6 +94,7 @@ public class AdapterMessage extends PagedListAdapter<TupleMessageEx, AdapterMess
super(itemView); super(itemView);
this.itemView = itemView; this.itemView = itemView;
ivAvatar = itemView.findViewById(R.id.ivAvatar);
ivFlagged = itemView.findViewById(R.id.ivFlagged); ivFlagged = itemView.findViewById(R.id.ivFlagged);
tvFrom = itemView.findViewById(R.id.tvFrom); tvFrom = itemView.findViewById(R.id.tvFrom);
tvTime = itemView.findViewById(R.id.tvTime); tvTime = itemView.findViewById(R.id.tvTime);
@ -119,6 +134,54 @@ public class AdapterMessage extends PagedListAdapter<TupleMessageEx, AdapterMess
private void bindTo(final TupleMessageEx message) { private void bindTo(final TupleMessageEx message) {
pbLoading.setVisibility(View.GONE); pbLoading.setVisibility(View.GONE);
ivAvatar.setVisibility(View.GONE);
if (hasContactsPermission && message.from != null && message.from.length > 0) {
itemView.setHasTransientState(true);
Bundle args = new Bundle();
args.putSerializable("from", message.from[0]);
new SimpleTask<Drawable>() {
@Override
protected Drawable onLoad(Context context, Bundle args) {
Cursor cursor = null;
try {
InternetAddress from = (InternetAddress) args.getSerializable("from");
ContentResolver resolver = context.getContentResolver();
cursor = resolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI,
new String[]{ContactsContract.CommonDataKinds.Photo.CONTACT_ID},
ContactsContract.CommonDataKinds.Email.ADDRESS + " = ?",
new String[]{from.getAddress()}, null);
if (cursor.moveToNext()) {
int colContactId = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Photo.CONTACT_ID);
long contactId = cursor.getLong(colContactId);
Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, contactId);
InputStream is = ContactsContract.Contacts.openContactPhotoInputStream(resolver, uri);
return Drawable.createFromStream(is, from.getPersonal());
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
@Override
protected void onLoaded(Bundle args, Drawable photo) {
if (photo != null) {
ivAvatar.setImageDrawable(photo);
ivAvatar.setVisibility(View.VISIBLE);
}
itemView.setHasTransientState(false);
}
@Override
protected void onException(Bundle args, Throwable ex) {
itemView.setHasTransientState(false);
}
}.load(context, owner, args);
}
ivFlagged.setVisibility(message.ui_flagged ? View.VISIBLE : View.GONE); ivFlagged.setVisibility(message.ui_flagged ? View.VISIBLE : View.GONE);
if (EntityFolder.DRAFTS.equals(message.folderType) || if (EntityFolder.DRAFTS.equals(message.folderType) ||
@ -276,6 +339,7 @@ public class AdapterMessage extends PagedListAdapter<TupleMessageEx, AdapterMess
this.context = context; this.context = context;
this.owner = owner; this.owner = owner;
this.viewType = viewType; this.viewType = viewType;
this.hasContactsPermission = (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED);
this.debug = PreferenceManager.getDefaultSharedPreferences(context).getBoolean("debug", false); this.debug = PreferenceManager.getDefaultSharedPreferences(context).getBoolean("debug", false);
} }


+ 12
- 1
app/src/main/res/layout/item_message.xml View File

@ -16,6 +16,17 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/tvFrom" /> app:layout_constraintTop_toTopOf="@+id/tvFrom" />
<ImageView
android:id="@+id/ivAvatar"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="6dp"
android:src="@drawable/baseline_person_24"
app:layout_constraintBottom_toBottomOf="@+id/tvFrom"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintStart_toEndOf="@id/ivFlagged"
app:layout_constraintTop_toTopOf="@+id/tvFrom" />
<TextView <TextView
android:id="@+id/tvFrom" android:id="@+id/tvFrom"
android:layout_width="0dp" android:layout_width="0dp"
@ -27,7 +38,7 @@
android:text="From" android:text="From"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textAppearance="@style/TextAppearance.AppCompat.Medium"
app:layout_constraintEnd_toStartOf="@+id/tvTime" app:layout_constraintEnd_toStartOf="@+id/tvTime"
app:layout_constraintStart_toEndOf="@id/ivFlagged"
app:layout_constraintStart_toEndOf="@id/ivAvatar"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<TextView <TextView


+ 1
- 1
app/src/main/res/values/strings.xml View File

@ -65,7 +65,7 @@
<string name="title_setup_doze_instructions">In the next dialog, select \"All apps\" at the top, select this app and select and confirm \"Don\'t optimize\"</string> <string name="title_setup_doze_instructions">In the next dialog, select \"All apps\" at the top, select this app and select and confirm \"Don\'t optimize\"</string>
<string name="title_setup_data">Disable data saving</string> <string name="title_setup_data">Disable data saving</string>
<string name="title_setup_permissions">Grant permissions</string> <string name="title_setup_permissions">Grant permissions</string>
<string name="title_setup_permissions_remark">To autocomplete addresses (optional)</string>
<string name="title_setup_permissions_remark">To get contact information (optional)</string>
<string name="title_setup_to_do">To do</string> <string name="title_setup_to_do">To do</string>
<string name="title_setup_done">Done</string> <string name="title_setup_done">Done</string>
<string name="title_setup_light_theme">Light theme</string> <string name="title_setup_light_theme">Light theme</string>


Loading…
Cancel
Save