Added notification move action

This commit is contained in:
M66B 2019-10-23 12:51:20 +02:00
parent ee39642758
commit f262eb3e3e
11 changed files with 2084 additions and 28 deletions

2
FAQ.md
View File

@ -75,8 +75,8 @@ Related questions:
* ~~Remind to attach files~~
* ~~Select domains to show images for~~ (this will be too complicated to use)
* ~~Unified starred messages view~~ (there is already a special search for this)
* ~~Notification move action~~
* Search for settings
* Notification move action
Anything on this list is in random order and *might* be added in the near future.

File diff suppressed because it is too large Load Diff

View File

@ -2768,6 +2768,7 @@ class Core {
boolean notify_trash = (prefs.getBoolean("notify_trash", true) || !pro);
boolean notify_junk = (prefs.getBoolean("notify_junk", false) && pro);
boolean notify_archive = (prefs.getBoolean("notify_archive", true) || !pro);
boolean notify_move = (prefs.getBoolean("notify_move", false) && pro);
boolean notify_reply = (prefs.getBoolean("notify_reply", false) && pro);
boolean notify_reply_direct = (prefs.getBoolean("notify_reply_direct", false) && pro);
boolean notify_flag = (prefs.getBoolean("notify_flag", false) && flags && pro);
@ -2993,6 +2994,27 @@ class Core {
wactions.add(actionArchive.build());
}
if (notify_move) {
EntityAccount account = db.account().getAccount(message.account);
if (account != null && account.move_to != null) {
EntityFolder folder = db.folder().getFolder(account.move_to);
if (folder != null) {
Intent move = new Intent(context, ServiceUI.class)
.setAction("move:" + message.id)
.putExtra("group", group);
PendingIntent piMove = PendingIntent.getService(context, ServiceUI.PI_MOVE, move, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Action.Builder actionMove = new NotificationCompat.Action.Builder(
R.drawable.baseline_folder_24,
folder.getDisplayName(context),
piMove)
.setAllowGeneratedReplies(false);
mbuilder.addAction(actionMove.build());
wactions.add(actionMove.build());
}
}
}
if (notify_reply && message.content &&
db.identity().getComposableIdentities(message.account).size() > 0) {
Intent reply = new Intent(context, ActivityCompose.class)

View File

@ -56,7 +56,7 @@ import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory;
// https://developer.android.com/topic/libraries/architecture/room.html
@Database(
version = 111,
version = 112,
entities = {
EntityIdentity.class,
EntityAccount.class,
@ -1079,6 +1079,13 @@ public abstract class DB extends RoomDatabase {
db.execSQL("ALTER TABLE `rule` ADD COLUMN `applied` INTEGER NOT NULL DEFAULT 0");
}
})
.addMigrations(new Migration(111, 112) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase db) {
Log.i("DB migration from version " + startVersion + " to " + endVersion);
db.execSQL("ALTER TABLE `account` ADD COLUMN `move_to` INTEGER");
}
})
.build();
}

View File

@ -92,6 +92,7 @@ public class EntityAccount extends EntityOrder implements Serializable {
public Character separator;
public Long swipe_left;
public Long swipe_right;
public Long move_to;
@NonNull
public Integer poll_interval = DEFAULT_KEEP_ALIVE_INTERVAL; // keep-alive interval
@NonNull

View File

@ -122,6 +122,8 @@ public class FragmentAccount extends FragmentBase {
private Spinner spLeft;
private Spinner spRight;
private Spinner spMove;
private Button btnSave;
private ContentLoadingProgressBar pbSave;
private CheckBox cbIdentity;
@ -217,6 +219,7 @@ public class FragmentAccount extends FragmentBase {
spJunk = view.findViewById(R.id.spJunk);
spLeft = view.findViewById(R.id.spLeft);
spRight = view.findViewById(R.id.spRight);
spMove = view.findViewById(R.id.spMove);
btnSave = view.findViewById(R.id.btnSave);
pbSave = view.findViewById(R.id.pbSave);
@ -406,6 +409,8 @@ public class FragmentAccount extends FragmentBase {
spLeft.setAdapter(adapterSwipe);
spRight.setAdapter(adapterSwipe);
spMove.setAdapter(adapter);
// Initialize
Helper.setViewsEnabled(view, false);
@ -668,6 +673,7 @@ public class FragmentAccount extends FragmentBase {
EntityFolder junk = (EntityFolder) spJunk.getSelectedItem();
EntityFolder left = (EntityFolder) spLeft.getSelectedItem();
EntityFolder right = (EntityFolder) spRight.getSelectedItem();
EntityFolder move = (EntityFolder) spMove.getSelectedItem();
if (drafts != null && drafts.id != null && drafts.id == 0L)
drafts = null;
@ -685,6 +691,9 @@ public class FragmentAccount extends FragmentBase {
if (right != null && right.id != null && right.id == 0L)
right = null;
if (move != null && move.id != null && move.id == 0L)
move = null;
Bundle args = new Bundle();
args.putLong("id", id);
@ -716,6 +725,7 @@ public class FragmentAccount extends FragmentBase {
args.putSerializable("junk", junk);
args.putSerializable("left", left);
args.putSerializable("right", right);
args.putSerializable("move", move);
args.putBoolean("should", should);
@ -772,6 +782,7 @@ public class FragmentAccount extends FragmentBase {
EntityFolder junk = (EntityFolder) args.getSerializable("junk");
EntityFolder left = (EntityFolder) args.getSerializable("left");
EntityFolder right = (EntityFolder) args.getSerializable("right");
EntityFolder move = (EntityFolder) args.getSerializable("move");
boolean pro = ActivityBilling.isPro(context);
boolean should = args.getBoolean("should");
@ -872,6 +883,9 @@ public class FragmentAccount extends FragmentBase {
if (!Objects.equals(account.swipe_right, right == null ? null : right.id))
return true;
if (!Objects.equals(account.move_to, move == null ? null : move.id))
return true;
if (account.error != null)
return true;
@ -1045,6 +1059,19 @@ public class FragmentAccount extends FragmentBase {
}
}
if (move != null && !(move.id != null && move.id < 0)) {
boolean found = false;
for (EntityFolder folder : folders)
if (move.name.equals(folder.name)) {
found = true;
break;
}
if (!found) {
move.type = EntityFolder.USER;
folders.add(move);
}
}
db.folder().setFoldersUser(account.id);
for (EntityFolder folder : folders) {
@ -1061,6 +1088,7 @@ public class FragmentAccount extends FragmentBase {
account.swipe_left = (left == null ? null : left.id);
account.swipe_right = (right == null ? null : right.id);
account.move_to = (move == null ? null : move.id);
db.account().updateAccount(account);
db.setTransactionSuccessful();
@ -1441,6 +1469,10 @@ public class FragmentAccount extends FragmentBase {
spTrash.setSelection(pos);
else if (EntityFolder.JUNK.equals(folder.type))
spJunk.setSelection(pos);
if (account != null &&
account.move_to != null && account.move_to.equals(folder.id))
spMove.setSelection(pos);
}
}

View File

@ -60,6 +60,7 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared
private CheckBox cbNotifyActionTrash;
private CheckBox cbNotifyActionJunk;
private CheckBox cbNotifyActionArchive;
private CheckBox cbNotifyActionMove;
private CheckBox cbNotifyActionReply;
private CheckBox cbNotifyActionReplyDirect;
private CheckBox cbNotifyActionFlag;
@ -81,7 +82,8 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared
private final static String[] RESET_OPTIONS = new String[]{
"badge", "unseen_ignored",
"notify_summary", "notify_remove", "notify_preview", "wearable_preview",
"notify_trash", "notify_junk", "notify_archive", "notify_reply", "notify_reply_direct",
"notify_trash", "notify_junk", "notify_archive", "notify_move",
"notify_reply", "notify_reply_direct",
"notify_flag", "notify_seen", "notify_snooze",
"biometrics_notify",
"light", "sound", "alert_once"
@ -106,6 +108,7 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared
cbNotifyActionTrash = view.findViewById(R.id.cbNotifyActionTrash);
cbNotifyActionJunk = view.findViewById(R.id.cbNotifyActionJunk);
cbNotifyActionArchive = view.findViewById(R.id.cbNotifyActionArchive);
cbNotifyActionMove = view.findViewById(R.id.cbNotifyActionMove);
cbNotifyActionReply = view.findViewById(R.id.cbNotifyActionReply);
cbNotifyActionReplyDirect = view.findViewById(R.id.cbNotifyActionReplyDirect);
cbNotifyActionFlag = view.findViewById(R.id.cbNotifyActionFlag);
@ -198,6 +201,13 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared
}
});
cbNotifyActionMove.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean checked) {
prefs.edit().putBoolean("notify_move", checked).apply();
}
});
cbNotifyActionReply.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean checked) {
@ -358,6 +368,7 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared
cbNotifyActionTrash.setChecked(prefs.getBoolean("notify_trash", true) || !pro);
cbNotifyActionJunk.setChecked(prefs.getBoolean("notify_junk", false) && pro);
cbNotifyActionArchive.setChecked(prefs.getBoolean("notify_archive", true) || !pro);
cbNotifyActionMove.setChecked(prefs.getBoolean("notify_move", false) && pro);
cbNotifyActionReply.setChecked(prefs.getBoolean("notify_reply", false) && pro);
cbNotifyActionReplyDirect.setChecked(prefs.getBoolean("notify_reply_direct", false) && pro);
cbNotifyActionFlag.setChecked(prefs.getBoolean("notify_flag", false) && pro);
@ -375,19 +386,20 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared
private void enableOptions() {
boolean pro = ActivityBilling.isPro(getContext());
boolean checked = swNotifySummary.isChecked();
boolean summary = swNotifySummary.isChecked();
swNotifyPreview.setEnabled(!checked);
swWearablePreview.setEnabled(!checked && swNotifyPreview.isChecked());
cbNotifyActionTrash.setEnabled(pro && !checked);
cbNotifyActionJunk.setEnabled(pro && !checked);
cbNotifyActionArchive.setEnabled(pro && !checked);
cbNotifyActionReply.setEnabled(pro && !checked);
cbNotifyActionReplyDirect.setEnabled(pro && !checked);
cbNotifyActionFlag.setEnabled(pro && !checked);
cbNotifyActionSeen.setEnabled(pro && !checked);
cbNotifyActionSnooze.setEnabled(pro && !checked);
swBiometricsNotify.setEnabled(!checked);
swNotifyPreview.setEnabled(!summary);
swWearablePreview.setEnabled(!summary && swNotifyPreview.isChecked());
cbNotifyActionTrash.setEnabled(pro && !summary);
cbNotifyActionJunk.setEnabled(pro && !summary);
cbNotifyActionArchive.setEnabled(pro && !summary);
cbNotifyActionMove.setEnabled(pro && !summary);
cbNotifyActionReply.setEnabled(pro && !summary);
cbNotifyActionReplyDirect.setEnabled(pro && !summary);
cbNotifyActionFlag.setEnabled(pro && !summary);
cbNotifyActionSeen.setEnabled(pro && !summary);
cbNotifyActionSnooze.setEnabled(pro && !summary);
swBiometricsNotify.setEnabled(!summary);
}
@Override

View File

@ -44,12 +44,13 @@ public class ServiceUI extends IntentService {
static final int PI_TRASH = 2;
static final int PI_JUNK = 3;
static final int PI_ARCHIVE = 4;
static final int PI_REPLY_DIRECT = 5;
static final int PI_FLAG = 6;
static final int PI_SEEN = 7;
static final int PI_SNOOZE = 8;
static final int PI_IGNORED = 9;
static final int PI_WAKEUP = 10;
static final int PI_MOVE = 5;
static final int PI_REPLY_DIRECT = 6;
static final int PI_FLAG = 7;
static final int PI_SEEN = 8;
static final int PI_SNOOZE = 9;
static final int PI_IGNORED = 10;
static final int PI_WAKEUP = 11;
public ServiceUI() {
this(ServiceUI.class.getName());
@ -111,6 +112,11 @@ public class ServiceUI extends IntentService {
onMove(id, EntityFolder.ARCHIVE);
break;
case "move":
cancel(group, id);
onMove(id);
break;
case "reply":
onReplyDirect(id, intent);
cancel(group, id);
@ -173,9 +179,30 @@ public class ServiceUI extends IntentService {
if (message == null)
return;
EntityFolder trash = db.folder().getFolderByType(message.account, folderType);
if (trash != null)
EntityOperation.queue(this, message, EntityOperation.MOVE, trash.id);
EntityFolder folder = db.folder().getFolderByType(message.account, folderType);
if (folder != null)
EntityOperation.queue(this, message, EntityOperation.MOVE, folder.id);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
private void onMove(long id) {
DB db = DB.getInstance(this);
try {
db.beginTransaction();
EntityMessage message = db.message().getMessage(id);
if (message == null)
return;
EntityAccount account = db.account().getAccount(message.account);
if (account == null || account.move_to == null)
return;
EntityOperation.queue(this, message, EntityOperation.MOVE, account.move_to);
db.setTransactionSuccessful();
} finally {

View File

@ -619,13 +619,23 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/spRight" />
<TextView
android:id="@+id/tvMove"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title_account_move"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintBottom_toBottomOf="@+id/spMove"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/spMove" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier_folders"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
app:barrierDirection="end"
app:constraint_referenced_ids="tvDrafts,tvSent,tvArchive,tvTrash,tvJunk,tvLeft,tvRight" />
app:constraint_referenced_ids="tvDrafts,tvSent,tvArchive,tvTrash,tvJunk,tvLeft,tvRight,tvMove" />
<Spinner
android:id="@+id/spDrafts"
@ -716,6 +726,25 @@
app:layout_constraintStart_toEndOf="@id/barrier_folders"
app:layout_constraintTop_toBottomOf="@id/spLeft" />
<View
android:id="@+id/vSeparatorMove"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="12dp"
android:background="?attr/colorSeparator"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/spRight" />
<Spinner
android:id="@+id/spMove"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginTop="12dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/barrier_folders"
app:layout_constraintTop_toBottomOf="@id/vSeparatorMove" />
<!-- save -->
<Button
@ -726,7 +755,7 @@
android:tag="disable"
android:text="@string/title_save"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/spRight" />
app:layout_constraintTop_toBottomOf="@id/spMove" />
<eu.faircode.email.ContentLoadingProgressBar
android:id="@+id/pbSave"
@ -845,6 +874,7 @@
tvArchive,spArchive,
tvTrash,spTrash,
tvJunk,spJunk,
vSeparatorSwipe,tvLeft,spLeft,tvRight,spRight" />
vSeparatorSwipe,tvLeft,spLeft,tvRight,spRight,
vSeparatorMove,tvMove,spMove" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

View File

@ -169,6 +169,15 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbNotifyActionJunk" />
<CheckBox
android:id="@+id/cbNotifyActionMove"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:text="@string/title_advanced_notify_action_move"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbNotifyActionArchive" />
<CheckBox
android:id="@+id/cbNotifyActionReply"
android:layout_width="wrap_content"
@ -176,7 +185,7 @@
android:layout_marginTop="6dp"
android:text="@string/title_advanced_notify_action_reply"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbNotifyActionArchive" />
app:layout_constraintTop_toBottomOf="@id/cbNotifyActionMove" />
<CheckBox
android:id="@+id/cbNotifyActionReplyDirect"

View File

@ -304,6 +304,7 @@
<string name="title_advanced_notify_action_trash">Trash</string>
<string name="title_advanced_notify_action_junk">Spam</string>
<string name="title_advanced_notify_action_archive">Archive</string>
<string name="title_advanced_notify_action_move">Move</string>
<string name="title_advanced_notify_action_reply">Reply</string>
<string name="title_advanced_notify_action_reply_direct">Direct reply</string>
<string name="title_advanced_notify_action_flag">Star</string>
@ -406,6 +407,7 @@
<string name="title_account_notify">Separate notifications</string>
<string name="title_account_left">Swipe left</string>
<string name="title_account_right">Swipe right</string>
<string name="title_account_move">Default move to</string>
<string name="title_domain">Domain name</string>
<string name="title_autoconfig">Get settings</string>
<string name="title_imap" translatable="false">IMAP</string>