mirror of https://github.com/M66B/FairEmail.git
Added Gmail label support
This commit is contained in:
parent
8454f981ad
commit
058cfb031f
1
FAQ.md
1
FAQ.md
|
@ -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
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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>
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue