Added long press for custom prompt

master
M66B 2 months ago
parent 3b4857c472
commit 4238c45e25

@ -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) {

@ -4665,7 +4665,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
} else if (id == R.id.ibTts) {
onActionTts(message);
} else if (id == R.id.ibSummarize) {
onActionSummarize(message, ibSummarize);
onActionSummarize(message, ibSummarize, null);
} else if (id == R.id.ibFullScreen)
onActionOpenFull(message);
else if (id == R.id.ibForceLight) {
@ -4870,9 +4870,12 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
onMenuShareHtml(message);
return true;
} else if (id == R.id.ibSummarize) {
context.startActivity(new Intent(context, ActivitySetup.class)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)
.putExtra("tab", "integrations"));
Bundle args = new Bundle();
args.putLong("id", message.id);
FragmentDialogBase fragment = new FragmentDialogPrompt();
fragment.setArguments(args);
fragment.setTargetFragment(parentFragment, FragmentMessages.REQUEST_PROMPT);
fragment.show(parentFragment.getParentFragmentManager(), "prompt");
return true;
} else if (id == R.id.ibFull) {
boolean full = properties.getValue("full", message.id);
@ -6592,7 +6595,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
onActionTranslate(message);
return true;
} else if (itemId == R.id.menu_summarize) {
onActionSummarize(message, null);
onActionSummarize(message, null, null);
return true;
} else if (itemId == R.id.menu_force_light) {
onActionForceLight(message);
@ -7564,8 +7567,8 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
}.execute(context, owner, args, "tts");
}
private void onActionSummarize(TupleMessageEx message, View anchor) {
FragmentDialogSummarize.summarize(message, parentFragment.getParentFragmentManager(), anchor, owner);
private void onActionSummarize(TupleMessageEx message, View anchor, String prompt) {
FragmentDialogSummarize.summarize(message, parentFragment.getParentFragmentManager(), anchor, owner, prompt);
}
private void onActionForceLight(TupleMessageEx message) {

@ -1638,7 +1638,7 @@ public class EntityRule {
}
try {
Spanned summary = AI.getSummaryText(context, message, -1L);
Spanned summary = AI.getSummaryText(context, message, -1L, null);
if (summary != null)
message.preview = summary.toString().trim();
} catch (Throwable ex) {

@ -0,0 +1,68 @@
package eu.faircode.email;
/*
This file is part of FairEmail.
FairEmail is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
FairEmail is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with FairEmail. If not, see <http://www.gnu.org/licenses/>.
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);
}
}

@ -94,7 +94,9 @@ 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 {
@ -125,6 +127,8 @@ public class FragmentDialogSummarize extends FragmentDialogBase {
}
}.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<EntityAnswer> 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);

@ -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<EntityMessage>() {
@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<MessageTarget> result) {
boolean across = false;
for (MessageTarget target : result)

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="24dp"
android:scrollbarStyle="outsideOverlay">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tvPrompt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableStart="@drawable/twotone_sticky_note_2_24"
android:drawablePadding="6dp"
android:labelFor="@+id/etPrompt"
android:text="@string/title_advanced_summarize_prompt"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<eu.faircode.email.EditTextPlain
android:id="@+id/etPrompt"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:imeOptions="actionDone"
android:inputType="textCapSentences|textMultiLine|textAutoCorrect"
android:text="Prompt"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvPrompt">
<requestFocus />
</eu.faircode.email.EditTextPlain>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
Loading…
Cancel
Save