Support for IMAP answered flag

This commit is contained in:
M66B 2018-11-24 19:14:28 +01:00
parent cdd34e26f7
commit 5246629f67
12 changed files with 1214 additions and 7 deletions

4
FAQ.md
View File

@ -22,7 +22,6 @@ For:
## Planned features
* IMAP answered flag
* IMAP namespaces
* Microsoft OAuth ([blocking issue](https://github.com/AzureAD/microsoft-authentication-library-for-android/issues/354))
@ -101,7 +100,8 @@ The low priority status bar notification shows the number of pending operations,
* delete: delete message from remote folder
* send: send message
* seen: mark message as seen/unseen in remote folder
* flag: add/remove stars
* answered: mark message as answered in remote folder
* flag: add/remove star in remote folder
* headers: download message headers
* body: download message text
* attachment: download attachment

File diff suppressed because it is too large Load Diff

View File

@ -141,6 +141,7 @@ public class AdapterMessage extends PagedListAdapter<TupleMessageEx, AdapterMess
private TextView tvFrom;
private TextView tvSize;
private TextView tvTime;
private ImageView ivAnswered;
private ImageView ivAttachments;
private TextView tvSubject;
private TextView tvFolder;
@ -196,6 +197,7 @@ public class AdapterMessage extends PagedListAdapter<TupleMessageEx, AdapterMess
tvTime = itemView.findViewById(R.id.tvTime);
tvTimeSent = itemView.findViewById(R.id.tvTimeSent);
tvTimeReceived = itemView.findViewById(R.id.tvTimeReceived);
ivAnswered = itemView.findViewById(R.id.ivAnswered);
ivAttachments = itemView.findViewById(R.id.ivAttachments);
tvSubject = itemView.findViewById(R.id.tvSubject);
tvPreview = itemView.findViewById(R.id.tvPreview);
@ -273,6 +275,7 @@ public class AdapterMessage extends PagedListAdapter<TupleMessageEx, AdapterMess
ivAddContact.setVisibility(View.GONE);
tvSize.setText(null);
tvTime.setText(null);
ivAnswered.setVisibility(View.GONE);
ivAttachments.setVisibility(View.GONE);
tvSubject.setText(null);
tvFolder.setText(null);
@ -346,6 +349,7 @@ public class AdapterMessage extends PagedListAdapter<TupleMessageEx, AdapterMess
tvSize.setAlpha(message.content ? 1.0f : 0.5f);
tvSize.setVisibility(message.size == null ? View.GONE : View.VISIBLE);
ivAnswered.setVisibility(message.ui_answered ? View.VISIBLE : View.GONE);
ivAttachments.setVisibility(message.attachments > 0 ? View.VISIBLE : View.GONE);
tvSubject.setText(message.subject);

View File

@ -46,7 +46,7 @@ import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory;
// https://developer.android.com/topic/libraries/architecture/room.html
@Database(
version = 6,
version = 7,
entities = {
EntityIdentity.class,
EntityAccount.class,
@ -162,6 +162,14 @@ public abstract class DB extends RoomDatabase {
db.execSQL("ALTER TABLE `account` ADD COLUMN `notify` INTEGER NOT NULL DEFAULT 0");
}
})
.addMigrations(new Migration(6, 7) {
@Override
public void migrate(SupportSQLiteDatabase db) {
Log.i(Helper.TAG, "DB migration from version " + startVersion + " to " + endVersion);
db.execSQL("ALTER TABLE `message` ADD COLUMN `answered` INTEGER NOT NULL DEFAULT 0");
db.execSQL("ALTER TABLE `message` ADD COLUMN `ui_answered` INTEGER NOT NULL DEFAULT 0");
}
})
.build();
}

View File

@ -255,6 +255,12 @@ public interface DaoMessage {
@Query("UPDATE message SET ui_seen = :ui_seen WHERE id = :id")
int setMessageUiSeen(long id, boolean ui_seen);
@Query("UPDATE message SET answered = :answered WHERE id = :id")
int setMessageAnswered(long id, boolean answered);
@Query("UPDATE message SET ui_answered = :ui_answered WHERE id = :id")
int setMessageUiAnswered(long id, boolean ui_answered);
@Query("UPDATE message SET flagged = :flagged WHERE id = :id")
int setMessageFlagged(long id, boolean flagged);

View File

@ -118,10 +118,14 @@ public class EntityMessage implements Serializable {
@NonNull
public Boolean seen;
@NonNull
public Boolean answered;
@NonNull
public Boolean flagged;
@NonNull
public Boolean ui_seen;
@NonNull
public Boolean ui_answered;
@NonNull
public Boolean ui_flagged;
@NonNull
public Boolean ui_hide;
@ -272,8 +276,10 @@ public class EntityMessage implements Serializable {
this.received.equals(other.received) &&
this.stored.equals(other.stored) &&
this.seen.equals(other.seen) &&
this.answered.equals(other.answered) &&
this.flagged.equals(other.flagged) &&
this.ui_seen.equals(other.ui_seen) &&
this.ui_answered.equals(other.ui_answered) &&
this.ui_flagged.equals(other.ui_flagged) &&
this.ui_hide.equals(other.ui_hide) &&
this.ui_found.equals(other.ui_found) &&

View File

@ -70,6 +70,7 @@ public class EntityOperation {
public static final String MOVE = "move";
public static final String DELETE = "delete";
public static final String SEND = "send";
public static final String ANSWERED = "answered";
public static final String HEADERS = "headers";
public static final String BODY = "body";
public static final String ATTACHMENT = "attachment";

View File

@ -1205,8 +1205,10 @@ public class FragmentCompose extends FragmentEx {
result.draft.content = true;
result.draft.received = new Date().getTime();
result.draft.seen = false;
result.draft.ui_seen = false;
result.draft.answered = false;
result.draft.flagged = false;
result.draft.ui_seen = false;
result.draft.ui_answered = false;
result.draft.ui_flagged = false;
result.draft.ui_hide = false;
result.draft.ui_found = false;
@ -1640,6 +1642,12 @@ public class FragmentCompose extends FragmentEx {
}
EntityOperation.queue(db, draft, EntityOperation.SEND);
if (draft.replying != null) {
EntityMessage replying = db.message().getMessage(draft.replying);
db.message().setMessageUiAnswered(replying.id, true);
EntityOperation.queue(db, replying, EntityOperation.ANSWERED, true);
}
}
db.setTransactionSuccessful();

View File

@ -356,6 +356,10 @@ public class MessageHelper {
return imessage.isSet(Flags.Flag.SEEN);
}
boolean getAnsered() throws MessagingException {
return imessage.isSet(Flags.Flag.ANSWERED);
}
boolean getFlagged() throws MessagingException {
return imessage.isSet(Flags.Flag.FLAGGED);
}

View File

@ -1348,6 +1348,9 @@ public class ServiceSynchronize extends LifecycleService {
if (EntityOperation.SEEN.equals(op.name))
doSeen(folder, ifolder, message, jargs, db);
else if (EntityOperation.ANSWERED.equals(op.name))
doAnswered(folder, ifolder, message, jargs, db);
else if (EntityOperation.FLAG.equals(op.name))
doFlag(folder, ifolder, message, jargs, db);
@ -1430,6 +1433,21 @@ public class ServiceSynchronize extends LifecycleService {
db.message().setMessageSeen(message.id, seen);
}
private void doAnswered(EntityFolder folder, IMAPFolder ifolder, EntityMessage message, JSONArray jargs, DB db) throws MessagingException, JSONException {
// Mark message (un)answered
boolean answered = jargs.getBoolean(0);
if (message.answered != answered)
return;
Message imessage = ifolder.getMessageByUID(message.uid);
if (imessage == null)
throw new MessageRemovedException();
imessage.setFlag(Flags.Flag.ANSWERED, answered);
db.message().setMessageAnswered(message.id, answered);
}
private void doFlag(EntityFolder folder, IMAPFolder ifolder, EntityMessage message, JSONArray jargs, DB db) throws MessagingException, JSONException {
// Star/unstar message
boolean flagged = jargs.getBoolean(0);
@ -1907,6 +1925,7 @@ public class ServiceSynchronize extends LifecycleService {
MessageHelper helper = new MessageHelper(imessage);
boolean seen = helper.getSeen();
boolean answered = helper.getAnsered();
boolean flagged = helper.getFlagged();
DB db = DB.getInstance(context);
@ -2009,8 +2028,10 @@ public class ServiceSynchronize extends LifecycleService {
message.received = imessage.getReceivedDate().getTime();
message.sent = (imessage.getSentDate() == null ? null : imessage.getSentDate().getTime());
message.seen = seen;
message.ui_seen = seen;
message.answered = answered;
message.flagged = false;
message.ui_seen = seen;
message.ui_answered = answered;
message.ui_flagged = false;
message.ui_hide = false;
message.ui_found = found;
@ -2042,6 +2063,13 @@ public class ServiceSynchronize extends LifecycleService {
Log.i(Helper.TAG, folder.name + " updated id=" + message.id + " uid=" + message.uid + " seen=" + seen);
}
if (message.answered != answered || message.answered != message.ui_answered) {
message.answered = answered;
message.ui_answered = answered;
db.message().updateMessage(message);
Log.i(Helper.TAG, folder.name + " updated id=" + message.id + " uid=" + message.uid + " answered=" + answered);
}
if (message.flagged != flagged || message.flagged != message.ui_flagged) {
message.flagged = flagged;
message.ui_flagged = flagged;

View File

@ -108,6 +108,16 @@
app:layout_constraintEnd_toStartOf="@+id/paddingEnd"
app:layout_constraintTop_toTopOf="@id/tvFrom" />
<ImageView
android:id="@+id/ivAnswered"
android:layout_width="21dp"
android:layout_height="21dp"
android:layout_marginStart="6dp"
android:src="@drawable/baseline_reply_24"
app:layout_constraintBottom_toBottomOf="@id/tvSubject"
app:layout_constraintStart_toEndOf="@id/paddingStart"
app:layout_constraintTop_toTopOf="@+id/tvSubject" />
<ImageView
android:id="@+id/ivAttachments"
android:layout_width="21dp"
@ -115,7 +125,7 @@
android:layout_marginStart="6dp"
android:src="@drawable/baseline_attachment_24"
app:layout_constraintBottom_toBottomOf="@id/tvSubject"
app:layout_constraintStart_toEndOf="@id/paddingStart"
app:layout_constraintStart_toEndOf="@id/ivAnswered"
app:layout_constraintTop_toTopOf="@+id/tvSubject" />
<TextView

View File

@ -105,6 +105,16 @@
app:layout_constraintEnd_toStartOf="@+id/paddingEnd"
app:layout_constraintTop_toTopOf="@id/tvFrom" />
<ImageView
android:id="@+id/ivAnswered"
android:layout_width="21dp"
android:layout_height="21dp"
android:layout_marginStart="6dp"
android:src="@drawable/baseline_reply_24"
app:layout_constraintBottom_toBottomOf="@id/tvSubject"
app:layout_constraintStart_toEndOf="@id/paddingStart"
app:layout_constraintTop_toTopOf="@+id/tvSubject" />
<ImageView
android:id="@+id/ivAttachments"
android:layout_width="21dp"
@ -112,7 +122,7 @@
android:layout_marginStart="6dp"
android:src="@drawable/baseline_attachment_24"
app:layout_constraintBottom_toBottomOf="@id/tvSubject"
app:layout_constraintStart_toEndOf="@id/paddingStart"
app:layout_constraintStart_toEndOf="@id/ivAnswered"
app:layout_constraintTop_toTopOf="@+id/tvSubject" />
<TextView