Added Gmail label support

This commit is contained in:
M66B 2020-06-25 09:14:05 +02:00
parent 8454f981ad
commit 058cfb031f
18 changed files with 2560 additions and 16 deletions

1
FAQ.md
View File

@ -380,6 +380,7 @@ The low priority status bar notification shows the number of pending operations,
* *answered*: mark message as answered in remote folder
* *flag*: add/remove star in remote folder
* *keyword*: add/remove IMAP flag in remote folder
* *label*: set/reset Gmail label in remote folder
* *headers*: download message headers
* *raw*: download raw message
* *body*: download message text

File diff suppressed because it is too large Load Diff

View File

@ -156,6 +156,7 @@ import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.text.Collator;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
@ -241,6 +242,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
private String subject_ellipsize;
private boolean keywords_header;
private boolean labels_header;
private boolean flags;
private boolean flags_background;
private boolean preview;
@ -341,6 +343,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
private TextView tvSubject;
private TextView tvKeywords;
private TextView tvFolder;
private TextView tvLabels;
private TextView tvCount;
private ImageView ivThread;
private TextView tvExpand;
@ -410,6 +413,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
private ImageButton ibRule;
private ImageButton ibUnsubscribe;
private ImageButton ibAnswer;
private ImageButton ibLabels;
private ImageButton ibMove;
private ImageButton ibArchive;
private ImageButton ibTrash;
@ -493,6 +497,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
tvExpand = itemView.findViewById(R.id.tvExpand);
tvPreview = itemView.findViewById(R.id.tvPreview);
tvFolder = itemView.findViewById(R.id.tvFolder);
tvLabels = itemView.findViewById(R.id.tvLabels);
tvCount = itemView.findViewById(R.id.tvCount);
ivThread = itemView.findViewById(R.id.ivThread);
tvError = itemView.findViewById(R.id.tvError);
@ -611,6 +616,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
ibRule = vsBody.findViewById(R.id.ibRule);
ibUnsubscribe = vsBody.findViewById(R.id.ibUnsubscribe);
ibAnswer = vsBody.findViewById(R.id.ibAnswer);
ibLabels = vsBody.findViewById(R.id.ibLabels);
ibMove = vsBody.findViewById(R.id.ibMove);
ibArchive = vsBody.findViewById(R.id.ibArchive);
ibTrash = vsBody.findViewById(R.id.ibTrash);
@ -701,6 +707,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
ibVerify.setOnClickListener(this);
ibUndo.setOnClickListener(this);
ibAnswer.setOnClickListener(this);
ibLabels.setOnClickListener(this);
ibMove.setOnClickListener(this);
ibArchive.setOnClickListener(this);
ibTrash.setOnClickListener(this);
@ -788,6 +795,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
ibVerify.setOnClickListener(null);
ibUndo.setOnClickListener(null);
ibAnswer.setOnClickListener(null);
ibLabels.setOnClickListener(null);
ibMove.setOnClickListener(null);
ibArchive.setOnClickListener(null);
ibTrash.setOnClickListener(null);
@ -841,6 +849,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
tvSubject.setText(null);
tvKeywords.setVisibility(View.GONE);
tvFolder.setText(null);
tvLabels.setVisibility(View.GONE);
tvCount.setText(null);
ivThread.setVisibility(View.GONE);
tvExpand.setVisibility(View.GONE);
@ -876,6 +885,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
tvSubject.setTextSize(TypedValue.COMPLEX_UNIT_PX, fz_subject);
tvKeywords.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize * 0.9f);
tvFolder.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize * 0.9f);
tvLabels.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize * 0.9f);
tvPreview.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize * 0.9f);
if (avatars) {
@ -920,6 +930,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
tvSubject.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
tvKeywords.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
tvFolder.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
tvLabels.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
tvCount.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ivThread.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
tvPreview.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
@ -1055,6 +1066,11 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
tvFolder.setVisibility(compact && viewType != ViewType.THREAD ? View.GONE : View.VISIBLE);
tvLabels.setText(message.labels == null ? null : TextUtils.join(", ", message.labels));
tvLabels.setVisibility(
labels_header && message.labels != null && message.labels.length > 0
? View.VISIBLE : View.GONE);
boolean selected = properties.getValue("selected", message.id);
if (viewType == ViewType.THREAD || (!threading && !selected)) {
tvCount.setVisibility(View.GONE);
@ -1282,6 +1298,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
ibRule.setVisibility(View.GONE);
ibUnsubscribe.setVisibility(View.GONE);
ibAnswer.setVisibility(View.GONE);
ibLabels.setVisibility(View.GONE);
ibMove.setVisibility(View.GONE);
ibArchive.setVisibility(View.GONE);
ibTrash.setVisibility(View.GONE);
@ -1412,6 +1429,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
ibRule.setVisibility(View.GONE);
ibUnsubscribe.setVisibility(View.GONE);
ibAnswer.setVisibility(View.GONE);
ibLabels.setVisibility(View.GONE);
ibMove.setVisibility(View.GONE);
ibArchive.setVisibility(View.GONE);
ibTrash.setVisibility(View.GONE);
@ -1485,8 +1503,13 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
new SimpleTask<List<EntityFolder>>() {
@Override
protected List<EntityFolder> onExecute(Context context, Bundle args) {
long account = args.getLong("account");
return DB.getInstance(context).folder().getSystemFolders(account);
long aid = args.getLong("account");
DB db = DB.getInstance(context);
EntityAccount account = db.account().getAccount(aid);
args.putBoolean("labels", account != null && account.isGmail());
return db.folder().getSystemFolders(aid);
}
@Override
@ -1500,6 +1523,8 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
if (!show_expanded)
return;
boolean labels = args.getBoolean("labels");
boolean hasArchive = false;
boolean hasTrash = false;
boolean hasJunk = false;
@ -1542,6 +1567,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
message.accountProtocol == EntityAccount.TYPE_IMAP ? View.VISIBLE : View.GONE);
ibUnsubscribe.setVisibility(!tools || message.unsubscribe == null ? View.GONE : View.VISIBLE);
ibAnswer.setVisibility(!tools || outbox || (!expand_all && expand_one) ? View.GONE : View.VISIBLE);
ibLabels.setVisibility(tools && labels_header && labels ? View.VISIBLE : View.GONE);
ibMove.setVisibility(tools && move ? View.VISIBLE : View.GONE);
ibArchive.setVisibility(tools && extras && archive ? View.VISIBLE : View.GONE);
ibTrash.setVisibility(tools && extras && trash ? View.VISIBLE : View.GONE);
@ -2742,6 +2768,9 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
case R.id.ibAnswer:
onActionAnswer(message, ibAnswer);
break;
case R.id.ibLabels:
onActionLabels(message);
break;
case R.id.ibMove:
onActionMove(message, false);
break;
@ -3530,6 +3559,50 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
properties.reply(message, getSelectedText(), anchor);
}
private void onActionLabels(final TupleMessageEx message) {
Bundle args = new Bundle();
args.putLong("id", message.id);
args.putLong("account", message.account);
new SimpleTask<String[]>() {
@Override
protected String[] onExecute(Context context, Bundle args) {
long account = args.getLong("account");
DB db = DB.getInstance(context);
List<EntityFolder> folders = db.folder().getFolders(account, true, true);
List<String> result = new ArrayList<>();
if (folders != null)
for (EntityFolder folder : folders)
if (!EntityFolder.INBOX.equals(folder.type))
result.add(folder.name);
Collator collator = Collator.getInstance(Locale.getDefault());
collator.setStrength(Collator.SECONDARY); // Case insensitive, process accents etc
Collections.sort(result, collator);
return result.toArray(new String[0]);
}
@Override
protected void onExecuted(Bundle args, String[] folders) {
args.putStringArray("labels", message.labels);
args.putStringArray("folders", folders);
FragmentDialogLabelsManage fragment = new FragmentDialogLabelsManage();
fragment.setArguments(args);
fragment.show(parentFragment.getParentFragmentManager(), "labels:manage");
}
@Override
protected void onException(Bundle args, Throwable ex) {
Log.unexpectedError(parentFragment.getParentFragmentManager(), ex);
}
}.execute(context, owner, args, "labels:fetch");
}
private void onActionMove(TupleMessageEx message, final boolean copy) {
Bundle args = new Bundle();
args.putString("title", context.getString(copy ? R.string.title_copy_to : R.string.title_move_to_folder));
@ -4849,6 +4922,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
this.subject_italic = prefs.getBoolean("subject_italic", true);
this.subject_ellipsize = prefs.getString("subject_ellipsize", "middle");
this.keywords_header = prefs.getBoolean("keywords_header", false);
this.labels_header = prefs.getBoolean("labels_header", true);
this.flags = prefs.getBoolean("flags", true);
this.flags_background = prefs.getBoolean("flags_background", false);
this.preview = prefs.getBoolean("preview", false);
@ -5047,6 +5121,10 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
same = false;
log("keywords changed", next.id);
}
if (!Helper.equal(prev.labels, next.labels)) {
same = false;
log("labels changed", next.id);
}
// notifying
// fts
if (!prev.ui_seen.equals(next.ui_seen)) {
@ -6044,4 +6122,59 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
.create();
}
}
public static class FragmentDialogLabelsManage extends FragmentDialogBase {
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
final long id = getArguments().getLong("id");
String[] labels = getArguments().getStringArray("labels");
final String[] folders = getArguments().getStringArray("folders");
List<String> l = new ArrayList<>();
if (labels != null)
l.addAll(Arrays.asList(labels));
boolean[] checked = new boolean[folders.length];
for (int i = 0; i < folders.length; i++)
if (l.contains(folders[i]))
checked[i] = true;
return new AlertDialog.Builder(getContext())
.setTitle(R.string.title_manage_labels)
.setMultiChoiceItems(folders, checked, new DialogInterface.OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
Bundle args = new Bundle();
args.putLong("id", id);
args.putString("label", folders[which]);
args.putBoolean("set", isChecked);
new SimpleTask<Void>() {
@Override
protected Void onExecute(Context context, Bundle args) {
long id = args.getLong("id");
String label = args.getString("label");
boolean set = args.getBoolean("set");
DB db = DB.getInstance(context);
EntityMessage message = db.message().getMessage(id);
if (message == null)
return null;
EntityOperation.queue(context, message, EntityOperation.LABEL, label, set);
return null;
}
@Override
protected void onException(Bundle args, Throwable ex) {
Log.unexpectedError(getParentFragmentManager(), ex);
}
}.execute(FragmentDialogLabelsManage.this, args, "label:set");
}
})
.create();
}
}
}

View File

@ -499,8 +499,10 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
//fp.add(IMAPFolder.FetchProfileItem.MESSAGE);
fp.add(FetchProfile.Item.SIZE);
fp.add(IMAPFolder.FetchProfileItem.INTERNALDATE);
if (account.isGmail())
if (account.isGmail()) {
fp.add(GmailFolder.FetchProfileItem.THRID);
fp.add(GmailFolder.FetchProfileItem.LABELS);
}
state.ifolder.fetch(add.toArray(new Message[0]), fp);
}

View File

@ -43,6 +43,7 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.preference.PreferenceManager;
import com.sun.mail.gimap.GmailFolder;
import com.sun.mail.gimap.GmailMessage;
import com.sun.mail.iap.BadCommandException;
import com.sun.mail.iap.CommandFailedException;
import com.sun.mail.iap.ConnectionException;
@ -324,6 +325,10 @@ class Core {
onKeyword(context, jargs, folder, message, (IMAPFolder) ifolder);
break;
case EntityOperation.LABEL:
onLabel(context, jargs, folder, message, (IMAPStore) istore, (IMAPFolder) ifolder, state);
break;
case EntityOperation.ADD:
onAdd(context, jargs, folder, message, (IMAPStore) istore, (IMAPFolder) ifolder, state);
break;
@ -688,6 +693,26 @@ class Core {
imessage.setFlags(flags, set);
}
private static void onLabel(Context context, JSONArray jargs, EntityFolder folder, EntityMessage message, IMAPStore istore, IMAPFolder ifolder, State state) throws JSONException, MessagingException, IOException {
// Set/clear Gmail label
String label = jargs.getString(0);
boolean set = jargs.getBoolean(1);
Message imessage = ifolder.getMessageByUID(message.uid);
if (imessage == null)
throw new MessageRemovedException();
if (!(imessage instanceof GmailMessage))
throw new IllegalArgumentException("GmailMessage");
((GmailMessage) imessage).setLabels(new String[]{label}, set);
// Gmail does not push label changes
JSONArray fargs = new JSONArray();
fargs.put(message.uid);
onFetch(context, fargs, folder, istore, ifolder, state);
}
private static void onAdd(Context context, JSONArray jargs, EntityFolder folder, EntityMessage message, IMAPStore istore, IMAPFolder ifolder, State state) throws MessagingException, IOException {
// Add message
DB db = DB.getInstance(context);
@ -985,8 +1010,10 @@ class Core {
//fp.add(IMAPFolder.FetchProfileItem.MESSAGE);
fp.add(FetchProfile.Item.SIZE);
fp.add(IMAPFolder.FetchProfileItem.INTERNALDATE);
if (account.isGmail())
if (account.isGmail()) {
fp.add(GmailFolder.FetchProfileItem.THRID);
fp.add(GmailFolder.FetchProfileItem.LABELS);
}
ifolder.fetch(new Message[]{imessage}, fp);
EntityMessage message = synchronizeMessage(context, account, folder, istore, ifolder, imessage, false, download, rules, state);
@ -1895,6 +1922,8 @@ class Core {
FetchProfile fp = new FetchProfile();
fp.add(UIDFolder.FetchProfileItem.UID); // To check if message exists
fp.add(FetchProfile.Item.FLAGS); // To update existing messages
if (account.isGmail())
fp.add(GmailFolder.FetchProfileItem.LABELS);
ifolder.fetch(imessages, fp);
long fetch = SystemClock.elapsedRealtime();
@ -2213,6 +2242,7 @@ class Core {
boolean flagged = helper.getFlagged();
String flags = helper.getFlags();
String[] keywords = helper.getKeywords();
String[] labels = helper.getLabels();
boolean update = false;
boolean process = false;
@ -2324,6 +2354,7 @@ class Core {
message.flagged = flagged;
message.flags = flags;
message.keywords = keywords;
message.labels = labels;
message.ui_seen = seen;
message.ui_answered = answered;
message.ui_flagged = flagged;
@ -2557,6 +2588,13 @@ class Core {
" keywords=" + TextUtils.join(" ", keywords));
}
if (!Helper.equal(message.labels, labels)) {
update = true;
message.labels = labels;
Log.i(folder.name + " updated id=" + message.id + " uid=" + message.uid +
" labels=" + (labels == null ? null : TextUtils.join(" ", labels)));
}
if (message.hash == null || process) {
update = true;
message.hash = helper.getHash();
@ -2859,8 +2897,10 @@ class Core {
//fp.add(IMAPFolder.FetchProfileItem.MESSAGE);
//fp.add(FetchProfile.Item.SIZE);
//fp.add(IMAPFolder.FetchProfileItem.INTERNALDATE);
//if (account.isGmail())
//if (account.isGmail()) {
// fp.add(GmailFolder.FetchProfileItem.THRID);
// fp.add(GmailFolder.FetchProfileItem.LABELS);
//}
//ifolder.fetch(new Message[]{imessage}, fp);
MessageHelper helper = new MessageHelper(imessage, context);

View File

@ -3,6 +3,7 @@ package eu.faircode.email;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.net.Uri;
import android.text.TextUtils;
import androidx.annotation.NonNull;
@ -60,7 +61,7 @@ import io.requery.android.database.sqlite.SQLiteDatabase;
// https://developer.android.com/topic/libraries/architecture/room.html
@Database(
version = 166,
version = 167,
entities = {
EntityIdentity.class,
EntityAccount.class,
@ -1641,6 +1642,13 @@ public abstract class DB extends RoomDatabase {
public void migrate(@NonNull SupportSQLiteDatabase db) {
db.execSQL("DROP INDEX `index_attachment_message_type`");
}
})
.addMigrations(new Migration(166, 167) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase db) {
Log.i("DB migration from version " + startVersion + " to " + endVersion);
db.execSQL("ALTER TABLE `message` ADD COLUMN `labels` TEXT");
}
});
}
@ -1667,16 +1675,25 @@ public abstract class DB extends RoomDatabase {
public static String[] toStringArray(String value) {
if (value == null)
return new String[0];
else
return TextUtils.split(value, " ");
else {
String[] result = TextUtils.split(value, " ");
for (int i = 0; i < result.length; i++)
result[i] = Uri.decode(result[i]);
return result;
}
}
@TypeConverter
public static String fromStringArray(String[] value) {
if (value == null || value.length == 0)
return null;
else
return TextUtils.join(" ", value);
else {
String[] copy = new String[value.length];
System.arraycopy(value, 0, copy, 0, value.length);
for (int i = 0; i < copy.length; i++)
copy[i] = Uri.encode(copy[i]);
return TextUtils.join(" ", copy);
}
}
@TypeConverter

View File

@ -161,6 +161,7 @@ public class EntityMessage implements Serializable {
public Boolean flagged = false;
public String flags; // system flags
public String[] keywords; // user flags
public String[] labels; // Gmail
@NonNull
public Integer notifying = 0;
@NonNull

View File

@ -89,6 +89,7 @@ public class EntityOperation {
static final String ANSWERED = "answered";
static final String FLAG = "flag";
static final String KEYWORD = "keyword";
static final String LABEL = "label"; // Gmail
static final String HEADERS = "headers";
static final String RAW = "raw";
static final String BODY = "body";

View File

@ -83,7 +83,7 @@ public class FragmentOptions extends FragmentBase {
"avatars", "gravatars", "generated_icons", "identicons", "circular", "saturation", "brightness", "threshold",
"name_email", "prefer_contact", "distinguish_contacts", "show_recipients", "authentication",
"subject_top", "font_size_sender", "font_size_subject", "subject_italic", "highlight_subject", "subject_ellipsize",
"keywords_header", "flags", "flags_background", "preview", "preview_italic", "preview_lines",
"keywords_header", "labels_header", "flags", "flags_background", "preview", "preview_italic", "preview_lines",
"addresses", "attachments_alt",
"contrast", "monospaced", "text_color", "text_size",
"inline_images", "collapse_quotes", "seekbar", "actionbar", "actionbar_color", "navbar_colorize",

View File

@ -95,6 +95,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
private SwitchCompat swHighlightSubject;
private Spinner spSubjectEllipsize;
private SwitchCompat swKeywords;
private SwitchCompat swLabels;
private SwitchCompat swFlags;
private SwitchCompat swFlagsBackground;
private SwitchCompat swPreview;
@ -122,7 +123,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
"avatars", "gravatars", "generated_icons", "identicons", "circular", "saturation", "brightness", "threshold",
"name_email", "prefer_contact", "distinguish_contacts", "show_recipients",
"subject_top", "font_size_sender", "font_size_subject", "subject_italic", "highlight_subject", "subject_ellipsize",
"keywords_header", "flags", "flags_background",
"keywords_header", "labels_header", "flags", "flags_background",
"preview", "preview_italic", "preview_lines",
"addresses",
"contrast", "monospaced", "text_color", "text_size", "text_align",
@ -180,6 +181,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
swHighlightSubject = view.findViewById(R.id.swHighlightSubject);
spSubjectEllipsize = view.findViewById(R.id.spSubjectEllipsize);
swKeywords = view.findViewById(R.id.swKeywords);
swLabels = view.findViewById(R.id.swLabels);
swFlags = view.findViewById(R.id.swFlags);
swFlagsBackground = view.findViewById(R.id.swFlagsBackground);
swPreview = view.findViewById(R.id.swPreview);
@ -529,6 +531,13 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
}
});
swLabels.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
prefs.edit().putBoolean("labels_header", checked).apply();
}
});
swFlags.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
@ -776,6 +785,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
}
swKeywords.setChecked(prefs.getBoolean("keywords_header", false));
swLabels.setChecked(prefs.getBoolean("labels_header", true));
swFlags.setChecked(prefs.getBoolean("flags", true));
swFlagsBackground.setChecked(prefs.getBoolean("flags_background", false));
swPreview.setChecked(prefs.getBoolean("preview", false));

View File

@ -1387,6 +1387,12 @@ public class Helper {
}
static boolean equal(String[] a1, String[] a2) {
if (a1 == null && a2 == null)
return true;
if (a1 == null || a2 == null)
return false;
if (a1.length != a2.length)
return false;

View File

@ -944,6 +944,20 @@ public class MessageHelper {
return thread;
}
String[] getLabels() throws MessagingException {
//ensureMessage(false);
List<String> labels = new ArrayList<>();
if (imessage instanceof GmailMessage)
for (String label : ((GmailMessage) imessage).getLabels())
if (!label.startsWith("\\"))
labels.add(label);
Collections.sort(labels);
return labels.toArray(new String[0]);
}
Integer getPriority() throws MessagingException {
Integer priority = null;

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="M17.63,5.84C17.27,5.33 16.67,5 16,5L5,5.01C3.9,5.01 3,5.9 3,7v10c0,1.1 0.9,1.99 2,1.99L16,19c0.67,0 1.27,-0.33 1.63,-0.84L22,12l-4.37,-6.16z"/>
</vector>

View File

@ -661,6 +661,17 @@
app:layout_constraintTop_toBottomOf="@id/spSubjectEllipsize"
app:switchPadding="12dp" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/swLabels"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/title_advanced_labels"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/swKeywords"
app:switchPadding="12dp" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/swFlags"
android:layout_width="0dp"
@ -670,7 +681,7 @@
android:text="@string/title_advanced_flags"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/swKeywords"
app:layout_constraintTop_toBottomOf="@id/swLabels"
app:switchPadding="12dp" />
<androidx.appcompat.widget.SwitchCompat

View File

@ -316,6 +316,18 @@
app:layout_constraintStart_toEndOf="@id/ibAvatar"
app:layout_constraintTop_toBottomOf="@id/tvKeywords" />
<eu.faircode.email.FixedTextView
android:id="@+id/tvLabels"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:text="label1,label2,label3"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintEnd_toStartOf="@+id/paddingEnd"
app:layout_constraintStart_toEndOf="@id/paddingStart"
app:layout_constraintTop_toBottomOf="@id/tvFolder" />
<eu.faircode.email.FixedTextView
android:id="@+id/tvExpand"
android:layout_width="0dp"
@ -328,7 +340,7 @@
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@+id/paddingEnd"
app:layout_constraintStart_toEndOf="@id/paddingStart"
app:layout_constraintTop_toBottomOf="@id/tvFolder" />
app:layout_constraintTop_toBottomOf="@id/tvLabels" />
<eu.faircode.email.FixedTextView
android:id="@+id/tvPreview"

View File

@ -41,7 +41,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="3dp"
app:constraint_referenced_ids="ibMore,ibInbox,ibJunk,ibTrash,ibArchive,ibMove,ibAnswer,ibUnsubscribe,ibRule,ibUndo"
app:constraint_referenced_ids="ibMore,ibInbox,ibJunk,ibTrash,ibArchive,ibMove,ibLabels,ibAnswer,ibUnsubscribe,ibRule,ibUndo"
app:flow_horizontalBias="0"
app:flow_horizontalGap="3dp"
app:flow_horizontalStyle="packed"
@ -122,6 +122,16 @@
app:srcCompat="@drawable/baseline_folder_24"
tools:ignore="MissingConstraints" />
<ImageButton
android:id="@+id/ibLabels"
android:layout_width="36dp"
android:layout_height="36dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:padding="6dp"
android:scaleType="fitCenter"
app:srcCompat="@drawable/baseline_label_24"
tools:ignore="MissingConstraints" />
<ImageButton
android:id="@+id/ibAnswer"
android:layout_width="36dp"

View File

@ -311,6 +311,19 @@
app:layout_constraintTop_toTopOf="@+id/tvFolder"
app:srcCompat="@drawable/baseline_message_24" />
<eu.faircode.email.FixedTextView
android:id="@+id/tvLabels"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginTop="3dp"
android:layout_marginEnd="6dp"
android:text="label1,label2,label3"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintEnd_toStartOf="@+id/paddingEnd"
app:layout_constraintStart_toEndOf="@id/paddingStart"
app:layout_constraintTop_toBottomOf="@id/tvFolder" />
<eu.faircode.email.FixedTextView
android:id="@+id/tvExpand"
android:layout_width="0dp"
@ -324,7 +337,7 @@
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@+id/paddingEnd"
app:layout_constraintStart_toEndOf="@id/paddingStart"
app:layout_constraintTop_toBottomOf="@id/tvFolder" />
app:layout_constraintTop_toBottomOf="@id/tvLabels" />
<eu.faircode.email.FixedTextView
android:id="@+id/tvPreview"

View File

@ -347,6 +347,7 @@
<string name="title_advanced_subject_italic">Show subject in italics</string>
<string name="title_advanced_subject_elipsed">When needed, shorten the subject</string>
<string name="title_advanced_keywords">Show keywords in message header</string>
<string name="title_advanced_labels">Show Gmail labels in message header</string>
<string name="title_advanced_flags">Show stars</string>
<string name="title_advanced_flags_background">Show colored background instead of colored stars</string>
<string name="title_advanced_preview">Show message preview</string>
@ -746,6 +747,7 @@
<string name="title_raw_save">Save raw message</string>
<string name="title_raw_send">Send as attachment</string>
<string name="title_manage_keywords">Manage keywords</string>
<string name="title_manage_labels">Manage Gmail labels</string>
<string name="title_add_keyword">Add keyword</string>
<string name="title_show_inline">Show inline attachments</string>
<string name="title_download_all">Download all</string>