Added direct reply

This commit is contained in:
M66B 2019-08-24 15:00:14 +02:00
parent cf56913a1b
commit 6edad1dce7
5 changed files with 163 additions and 40 deletions

View File

@ -35,6 +35,7 @@ import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.core.app.NotificationCompat;
import androidx.core.app.RemoteInput;
import androidx.preference.PreferenceManager;
import com.sun.mail.iap.BadCommandException;
@ -2080,6 +2081,7 @@ class Core {
boolean notify_trash = (prefs.getBoolean("notify_trash", true) || !pro);
boolean notify_archive = (prefs.getBoolean("notify_archive", true) || !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);
boolean notify_seen = (prefs.getBoolean("notify_seen", true) || !pro);
boolean light = prefs.getBoolean("light", false);
@ -2279,6 +2281,25 @@ class Core {
mbuilder.addAction(actionReply.build());
}
if (notify_reply_direct &&
message.content &&
message.identity != null &&
message.from != null && message.from.length > 0 &&
db.folder().getOutbox() != null) {
Intent reply = new Intent(context, ServiceUI.class)
.setAction("reply:" + message.id)
.putExtra("group", group);
PendingIntent piReply = PendingIntent.getService(context, ServiceUI.PI_REPLY_DIRECT, reply, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Action.Builder actionReply = new NotificationCompat.Action.Builder(
R.drawable.baseline_reply_24,
context.getString(R.string.title_advanced_notify_action_reply_direct),
piReply);
RemoteInput.Builder input = new RemoteInput.Builder("text")
.setLabel(context.getString(R.string.title_advanced_notify_action_reply));
actionReply.addRemoteInput(input.build()).setAllowGeneratedReplies(false);
mbuilder.addAction(actionReply.build());
}
if (notify_flag) {
Intent flag = new Intent(context, ServiceUI.class)
.setAction("flag:" + message.id)

View File

@ -56,6 +56,7 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared
private CheckBox cbNotifyActionTrash;
private CheckBox cbNotifyActionArchive;
private CheckBox cbNotifyActionReply;
private CheckBox cbNotifyActionReplyDirect;
private CheckBox cbNotifyActionFlag;
private CheckBox cbNotifyActionSeen;
private TextView tvNotifyActionsPro;
@ -70,7 +71,7 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared
private final static String[] RESET_OPTIONS = new String[]{
"badge", "unseen_ignored",
"notify_preview", "notify_trash", "notify_archive", "notify_reply", "notify_flag", "notify_seen", "biometrics_notify",
"notify_preview", "notify_trash", "notify_archive", "notify_reply", "notify_reply_direct", "notify_flag", "notify_seen", "biometrics_notify",
"light", "sound"
};
@ -90,6 +91,7 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared
cbNotifyActionTrash = view.findViewById(R.id.cbNotifyActionTrash);
cbNotifyActionArchive = view.findViewById(R.id.cbNotifyActionArchive);
cbNotifyActionReply = view.findViewById(R.id.cbNotifyActionReply);
cbNotifyActionReplyDirect = view.findViewById(R.id.cbNotifyActionReplyDirect);
cbNotifyActionFlag = view.findViewById(R.id.cbNotifyActionFlag);
cbNotifyActionSeen = view.findViewById(R.id.cbNotifyActionSeen);
tvNotifyActionsPro = view.findViewById(R.id.tvNotifyActionsPro);
@ -153,6 +155,13 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared
}
});
cbNotifyActionReplyDirect.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean checked) {
prefs.edit().putBoolean("notify_reply_direct", checked).apply();
}
});
cbNotifyActionFlag.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean checked) {
@ -273,6 +282,7 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared
cbNotifyActionTrash.setChecked(prefs.getBoolean("notify_trash", true) || !pro);
cbNotifyActionArchive.setChecked(prefs.getBoolean("notify_archive", true) || !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);
cbNotifyActionSeen.setChecked(prefs.getBoolean("notify_seen", true) || !pro);

View File

@ -24,20 +24,30 @@ import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.core.app.RemoteInput;
import androidx.preference.PreferenceManager;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.regex.Pattern;
import javax.mail.Address;
import javax.mail.internet.InternetAddress;
public class ServiceUI extends IntentService {
static final int PI_CLEAR = 1;
static final int PI_TRASH = 2;
static final int PI_ARCHIVE = 3;
static final int PI_FLAG = 4;
static final int PI_SEEN = 5;
static final int PI_IGNORED = 6;
static final int PI_SNOOZED = 7;
static final int PI_REPLY_DIRECT = 4;
static final int PI_FLAG = 5;
static final int PI_SEEN = 6;
static final int PI_IGNORED = 7;
static final int PI_SNOOZED = 8;
public ServiceUI() {
this(ServiceUI.class.getName());
@ -74,47 +84,58 @@ public class ServiceUI extends IntentService {
if (action == null)
return;
String[] parts = action.split(":");
long id = (parts.length > 1 ? Long.parseLong(parts[1]) : -1);
try {
String[] parts = action.split(":");
long id = (parts.length > 1 ? Long.parseLong(parts[1]) : -1);
String group = intent.getStringExtra("group");
switch (parts[0]) {
case "clear":
onClear();
break;
switch (parts[0]) {
case "clear":
onClear();
break;
case "trash":
cancel(intent.getStringExtra("group"), id);
onTrash(id);
break;
case "trash":
cancel(group, id);
onTrash(id);
break;
case "archive":
cancel(intent.getStringExtra("group"), id);
onArchive(id);
break;
case "archive":
cancel(group, id);
onArchive(id);
break;
case "flag":
cancel(intent.getStringExtra("group"), id);
onFlag(id);
break;
case "reply":
onReplyDirect(id, intent);
cancel(group, id);
onSeen(id);
break;
case "seen":
cancel(intent.getStringExtra("group"), id);
onSeen(id);
break;
case "flag":
cancel(group, id);
onFlag(id);
break;
case "ignore":
onIgnore(id);
break;
case "seen":
cancel(group, id);
onSeen(id);
break;
case "snooze":
// AlarmManager.RTC_WAKEUP
// When the alarm is dispatched, the app will also be added to the system's temporary whitelist
// for approximately 10 seconds to allow that application to acquire further wake locks in which to complete its work.
// https://developer.android.com/reference/android/app/AlarmManager
onSnooze(id);
break;
default:
Log.w("Unknown action: " + parts[0]);
case "ignore":
onIgnore(id);
break;
case "snooze":
// AlarmManager.RTC_WAKEUP
// When the alarm is dispatched, the app will also be added to the system's temporary whitelist
// for approximately 10 seconds to allow that application to acquire further wake locks in which to complete its work.
// https://developer.android.com/reference/android/app/AlarmManager
onSnooze(id);
break;
default:
throw new IllegalArgumentException("Unknown UI action: " + parts[0]);
}
} catch (Throwable ex) {
Log.e(ex);
}
}
@ -167,6 +188,67 @@ public class ServiceUI extends IntentService {
}
}
private void onReplyDirect(long id, Intent intent) throws IOException {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
boolean prefix_once = prefs.getBoolean("prefix_once", true);
boolean plain_only = prefs.getBoolean("plain_only", false);
Bundle results = RemoteInput.getResultsFromIntent(intent);
String text = results.getString("text");
DB db = DB.getInstance(this);
try {
db.beginTransaction();
EntityMessage ref = db.message().getMessage(id);
if (ref == null)
throw new IllegalArgumentException("message not found");
EntityIdentity identity = db.identity().getIdentity(ref.identity);
if (identity == null)
throw new IllegalArgumentException("identity not found");
EntityFolder outbox = db.folder().getOutbox();
if (outbox == null)
throw new IllegalArgumentException("outbox not found");
String subject = (ref.subject == null ? "" : ref.subject);
if (prefix_once) {
String re = getString(R.string.title_subject_reply, "");
subject = subject.replaceAll("(?i)" + Pattern.quote(re.trim()), "").trim();
}
EntityMessage reply = new EntityMessage();
reply.account = identity.account;
reply.folder = outbox.id;
reply.identity = identity.id;
reply.msgid = EntityMessage.generateMessageId();
reply.inreplyto = ref.msgid;
reply.thread = ref.thread;
reply.to = ref.from;
reply.from = new Address[]{new InternetAddress(identity.email, identity.name)};
reply.subject = getString(R.string.title_subject_reply, subject);
reply.received = new Date().getTime();
reply.seen = true;
reply.ui_seen = true;
reply.id = db.message().insertMessage(reply);
Helper.writeText(reply.getFile(this), text);
db.message().setMessageContent(reply.id,
true,
plain_only || ref.plain_only,
HtmlHelper.getPreview(text),
null);
EntityOperation.queue(this, reply, EntityOperation.SEND);
db.setTransactionSuccessful();
ToastEx.makeText(this, R.string.title_queued, Toast.LENGTH_LONG).show();
} finally {
db.endTransaction();
}
}
private void onFlag(long id) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
boolean threading = prefs.getBoolean("threading", true);

View File

@ -117,6 +117,15 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbNotifyActionArchive" />
<CheckBox
android:id="@+id/cbNotifyActionReplyDirect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:text="@string/title_advanced_notify_action_reply_direct"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbNotifyActionReply" />
<CheckBox
android:id="@+id/cbNotifyActionFlag"
android:layout_width="wrap_content"
@ -124,7 +133,7 @@
android:layout_marginTop="6dp"
android:text="@string/title_advanced_notify_action_flag"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbNotifyActionReply" />
app:layout_constraintTop_toBottomOf="@id/cbNotifyActionReplyDirect" />
<CheckBox
android:id="@+id/cbNotifyActionSeen"

View File

@ -276,6 +276,7 @@
<string name="title_advanced_notify_action_trash">Trash</string>
<string name="title_advanced_notify_action_archive">Archive</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>
<string name="title_advanced_notify_action_seen">Read</string>
<string name="title_advanced_biometrics_notify">Show notification content when using biometric authentication</string>