From 4238c45e258e7258500dde960a395e7725183319 Mon Sep 17 00:00:00 2001 From: M66B Date: Thu, 31 Jul 2025 08:11:57 +0200 Subject: [PATCH] Added long press for custom prompt --- app/src/main/java/eu/faircode/email/AI.java | 6 +- .../eu/faircode/email/AdapterMessage.java | 17 ++-- .../java/eu/faircode/email/EntityRule.java | 2 +- .../faircode/email/FragmentDialogPrompt.java | 68 ++++++++++++++++ .../email/FragmentDialogSummarize.java | 77 ++++++++++--------- .../eu/faircode/email/FragmentMessages.java | 32 +++++++- app/src/main/res/layout/dialog_prompt.xml | 41 ++++++++++ 7 files changed, 194 insertions(+), 49 deletions(-) create mode 100644 app/src/main/java/eu/faircode/email/FragmentDialogPrompt.java create mode 100644 app/src/main/res/layout/dialog_prompt.xml diff --git a/app/src/main/java/eu/faircode/email/AI.java b/app/src/main/java/eu/faircode/email/AI.java index 46e709e33d..570abce6bd 100644 --- a/app/src/main/java/eu/faircode/email/AI.java +++ b/app/src/main/java/eu/faircode/email/AI.java @@ -151,7 +151,7 @@ public class AI { return context.getString(R.string.title_summarize); } - static Spanned getSummaryText(Context context, EntityMessage message, long template) throws JSONException, IOException { + static Spanned getSummaryText(Context context, EntityMessage message, long template, String prompt) throws JSONException, IOException { File file = message.getFile(context); if (!file.exists()) return null; @@ -171,8 +171,8 @@ public class AI { if (TextUtils.isEmpty(body)) return null; - String templatePrompt = null; - if (template > 0L) { + String templatePrompt = prompt; + if (templatePrompt == null && template > 0L) { DB db = DB.getInstance(context); EntityAnswer t = db.answer().getAnswer(template); if (t != null) { diff --git a/app/src/main/java/eu/faircode/email/AdapterMessage.java b/app/src/main/java/eu/faircode/email/AdapterMessage.java index f0e84ba15f..55c597e850 100644 --- a/app/src/main/java/eu/faircode/email/AdapterMessage.java +++ b/app/src/main/java/eu/faircode/email/AdapterMessage.java @@ -4665,7 +4665,7 @@ public class AdapterMessage extends RecyclerView.Adapter. + + Copyright 2018-2025 by Marcel Bokhorst (M66B) +*/ + +import static android.app.Activity.RESULT_OK; + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager; +import android.widget.EditText; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; + +public class FragmentDialogPrompt extends FragmentDialogBase { + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + final Context context = getContext(); + + View view = LayoutInflater.from(context).inflate(R.layout.dialog_prompt, null); + final EditText etPrompt = view.findViewById(R.id.etPrompt); + + etPrompt.setText(null); + + AlertDialog.Builder builder = new AlertDialog.Builder(context) + .setView(view) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Bundle args = getArguments(); + args.putString("prompt", etPrompt.getText().toString()); + sendResult(RESULT_OK); + } + }) + .setNegativeButton(android.R.string.cancel, null); + + return builder.create(); + } + + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); + } +} diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogSummarize.java b/app/src/main/java/eu/faircode/email/FragmentDialogSummarize.java index b81f25bb8b..31a5e02e41 100644 --- a/app/src/main/java/eu/faircode/email/FragmentDialogSummarize.java +++ b/app/src/main/java/eu/faircode/email/FragmentDialogSummarize.java @@ -94,37 +94,41 @@ public class FragmentDialogSummarize extends FragmentDialogBase { Bundle args = getArguments(); long template = args.getLong("template"); + String prompt = args.getString("prompt"); + + if (prompt == null) + if (template <= 0) + tvCaption.setText(AI.getSummarizePrompt(context)); + else { + tvCaption.setText(null); + new SimpleTask() { + @Override + protected String onExecute(Context context, Bundle args) throws Throwable { + long template = args.getLong("template"); + + DB db = DB.getInstance(context); + EntityAnswer prompt = db.answer().getAnswer(template); + if (prompt == null) + return null; + + Document doc = JsoupEx.parse(prompt.getData(context, null).getHtml()); + Spanned spanned = HtmlHelper.fromDocument(context, doc, null, null); + return spanned.toString(); + } - if (template <= 0) - tvCaption.setText(AI.getSummarizePrompt(context)); - else { - tvCaption.setText(null); - new SimpleTask() { - @Override - protected String onExecute(Context context, Bundle args) throws Throwable { - long template = args.getLong("template"); - - DB db = DB.getInstance(context); - EntityAnswer prompt = db.answer().getAnswer(template); - if (prompt == null) - return null; - - Document doc = JsoupEx.parse(prompt.getData(context, null).getHtml()); - Spanned spanned = HtmlHelper.fromDocument(context, doc, null, null); - return spanned.toString(); - } - - @Override - protected void onExecuted(Bundle args, String prompt) { - tvCaption.setText(prompt); - } + @Override + protected void onExecuted(Bundle args, String prompt) { + tvCaption.setText(prompt); + } - @Override - protected void onException(Bundle args, Throwable ex) { - Log.unexpectedError(getParentFragmentManager(), ex); - } - }.execute(this, args, "ai:prompt"); - } + @Override + protected void onException(Bundle args, Throwable ex) { + Log.unexpectedError(getParentFragmentManager(), ex); + } + }.execute(this, args, "ai:prompt"); + } + else + tvCaption.setText(prompt); tvFrom.setText(args.getString("from")); tvSubject.setText(args.getString("subject")); @@ -155,7 +159,7 @@ public class FragmentDialogSummarize extends FragmentDialogBase { return null; long start = new Date().getTime(); - Spanned summary = AI.getSummaryText(context, message, template); + Spanned summary = AI.getSummaryText(context, message, template, prompt); args.putLong("elapsed", new Date().getTime() - start); return summary; @@ -184,9 +188,9 @@ public class FragmentDialogSummarize extends FragmentDialogBase { return builder.create(); } - public static void summarize(EntityMessage message, FragmentManager fm, View anchor, LifecycleOwner owner) { - if (anchor == null) { - summarize(message, fm, null); + public static void summarize(EntityMessage message, FragmentManager fm, View anchor, LifecycleOwner owner, String prompt) { + if (anchor == null || prompt != null) { + summarize(message, fm, null, prompt); return; } @@ -202,7 +206,7 @@ public class FragmentDialogSummarize extends FragmentDialogBase { @Override protected void onExecuted(Bundle args, List prompts) { if (prompts == null || prompts.isEmpty()) - summarize(message, fm, null); + summarize(message, fm, null, prompt); else { PopupMenuLifecycle popupMenu = new PopupMenuLifecycle(context, owner, anchor); @@ -224,7 +228,7 @@ public class FragmentDialogSummarize extends FragmentDialogBase { @Override public boolean onMenuItemClick(MenuItem item) { long id = item.getIntent().getLongExtra("id", -1L); - summarize(message, fm, id); + summarize(message, fm, id, prompt); return true; } }); @@ -240,12 +244,13 @@ public class FragmentDialogSummarize extends FragmentDialogBase { }.execute(context, owner, new Bundle(), "AI:select"); } - private static void summarize(EntityMessage message, FragmentManager fm, Long template) { + private static void summarize(EntityMessage message, FragmentManager fm, Long template, String prompt) { Bundle args = new Bundle(); args.putLong("id", message.id); args.putString("from", MessageHelper.formatAddresses(message.from)); args.putString("subject", message.subject); args.putLong("template", template == null ? -1L : template); + args.putString("prompt", prompt); FragmentDialogSummarize fragment = new FragmentDialogSummarize(); fragment.setArguments(args); diff --git a/app/src/main/java/eu/faircode/email/FragmentMessages.java b/app/src/main/java/eu/faircode/email/FragmentMessages.java index 69ca48b5c5..c1620b7796 100644 --- a/app/src/main/java/eu/faircode/email/FragmentMessages.java +++ b/app/src/main/java/eu/faircode/email/FragmentMessages.java @@ -456,6 +456,7 @@ public class FragmentMessages extends FragmentBase static final int REQUEST_EDIT_SUBJECT = 30; private static final int REQUEST_ANSWER_SETTINGS = 31; private static final int REQUEST_DESELECT = 32; + static final int REQUEST_PROMPT = 33; static final String ACTION_STORE_RAW = BuildConfig.APPLICATION_ID + ".STORE_RAW"; static final String ACTION_VERIFYDECRYPT = BuildConfig.APPLICATION_ID + ".VERIFYDECRYPT"; @@ -1660,7 +1661,7 @@ public class FragmentMessages extends FragmentBase if (result == null || result.single == null || !result.single.content) return; - FragmentDialogSummarize.summarize(result.single, getParentFragmentManager(), ibSummarize, getViewLifecycleOwner()); + FragmentDialogSummarize.summarize(result.single, getParentFragmentManager(), ibSummarize, getViewLifecycleOwner(), null); } }); @@ -3818,7 +3819,7 @@ public class FragmentMessages extends FragmentBase private void onSwipeSummarize(final @NonNull TupleMessageEx message) { final Context context = getContext(); if (AI.isAvailable(context)) - FragmentDialogSummarize.summarize(message, getParentFragmentManager(), null, getViewLifecycleOwner()); + FragmentDialogSummarize.summarize(message, getParentFragmentManager(), null, getViewLifecycleOwner(), null); else context.startActivity(new Intent(context, ActivitySetup.class) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK) @@ -9661,6 +9662,10 @@ public class FragmentMessages extends FragmentBase if (selectionTracker != null) selectionTracker.clearSelection(); break; + case REQUEST_PROMPT: + if (resultCode == RESULT_OK) + onActionSummarize(data.getBundleExtra("args")); + break; } } catch (Throwable ex) { Log.e(ex); @@ -11191,6 +11196,29 @@ public class FragmentMessages extends FragmentBase }.execute(this, args, "edit:subject"); } + private void onActionSummarize(Bundle args) { + new SimpleTask() { + @Override + protected EntityMessage onExecute(Context context, Bundle args) throws Throwable { + long id = args.getLong("id"); + DB db = DB.getInstance(context); + return db.message().getMessage(id); + } + + @Override + protected void onExecuted(Bundle args, EntityMessage message) { + if (message == null) + return; + FragmentDialogSummarize.summarize(message, getParentFragmentManager(), null, getViewLifecycleOwner(), args.getString("prompt")); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Log.unexpectedError(getParentFragmentManager(), ex); + } + }.execute(this, args, "prompt"); + } + private void onMoveAskAcross(final ArrayList result) { boolean across = false; for (MessageTarget target : result) diff --git a/app/src/main/res/layout/dialog_prompt.xml b/app/src/main/res/layout/dialog_prompt.xml new file mode 100644 index 0000000000..062dad5917 --- /dev/null +++ b/app/src/main/res/layout/dialog_prompt.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + \ No newline at end of file