pull/183/head
M66B 5 years ago
parent f22b6e93de
commit 0030dce5cc

@ -91,6 +91,8 @@
<intent> <intent>
<action android:name="org.openintents.openpgp.IOpenPgpService2" /> <action android:name="org.openintents.openpgp.IOpenPgpService2" />
</intent> </intent>
<package android:name="com.google.android.tts" />
</queries> </queries>
<application <application

@ -167,6 +167,9 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> {
case EntityRule.TYPE_ANSWER: case EntityRule.TYPE_ANSWER:
tvAction.setText(R.string.title_rule_answer); tvAction.setText(R.string.title_rule_answer);
break; break;
case EntityRule.TYPE_TTS:
tvAction.setText(R.string.title_rule_tts);
break;
case EntityRule.TYPE_AUTOMATION: case EntityRule.TYPE_AUTOMATION:
tvAction.setText(R.string.title_rule_automation); tvAction.setText(R.string.title_rule_automation);
break; break;

@ -98,6 +98,7 @@ public class EntityRule {
static final int TYPE_KEYWORD = 11; static final int TYPE_KEYWORD = 11;
static final int TYPE_HIDE = 12; static final int TYPE_HIDE = 12;
static final int TYPE_IMPORTANCE = 13; static final int TYPE_IMPORTANCE = 13;
static final int TYPE_TTS = 14;
static final String ACTION_AUTOMATION = BuildConfig.APPLICATION_ID + ".AUTOMATION"; static final String ACTION_AUTOMATION = BuildConfig.APPLICATION_ID + ".AUTOMATION";
static final String EXTRA_RULE = "rule"; static final String EXTRA_RULE = "rule";
@ -323,6 +324,8 @@ public class EntityRule {
return onActionCopy(context, message, jaction); return onActionCopy(context, message, jaction);
case TYPE_ANSWER: case TYPE_ANSWER:
return onActionAnswer(context, message, jaction); return onActionAnswer(context, message, jaction);
case TYPE_TTS:
return onActionTts(context, message, jaction);
case TYPE_AUTOMATION: case TYPE_AUTOMATION:
return onActionAutomation(context, message, jaction); return onActionAutomation(context, message, jaction);
default: default:
@ -480,6 +483,24 @@ public class EntityRule {
return true; return true;
} }
private boolean onActionTts(Context context, EntityMessage message, JSONObject jargs) {
StringBuilder sb = new StringBuilder();
sb.append(context.getString(R.string.title_rule_tts_prefix));
if (message.from != null && message.from.length > 0)
sb.append(' ').append(context.getString(R.string.title_from))
.append(' ').append(MessageHelper.formatAddressesShort(message.from));
if (!TextUtils.isEmpty(message.subject))
sb.append(' ').append(context.getString(R.string.title_subject))
.append(' ').append(message.subject);
EntityLog.log(context, "TTS queued language=" + message.language + " text=" + sb.toString());
TTSHelper.speak(context, "rule:" + message.id, sb.toString(), message.language);
return true;
}
private boolean onActionSnooze(Context context, EntityMessage message, JSONObject jargs) throws JSONException { private boolean onActionSnooze(Context context, EntityMessage message, JSONObject jargs) throws JSONException {
int duration = jargs.getInt("duration"); int duration = jargs.getInt("duration");
boolean schedule_end = jargs.optBoolean("schedule_end", false); boolean schedule_end = jargs.optBoolean("schedule_end", false);

@ -31,6 +31,7 @@ import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import android.speech.tts.TextToSpeech;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.format.DateFormat; import android.text.format.DateFormat;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -133,6 +134,8 @@ public class FragmentRule extends FragmentBase {
private Spinner spAnswer; private Spinner spAnswer;
private CheckBox cbCc; private CheckBox cbCc;
private Button btnTts;
private TextView tvAutomation; private TextView tvAutomation;
private BottomNavigationView bottom_navigation; private BottomNavigationView bottom_navigation;
@ -146,6 +149,7 @@ public class FragmentRule extends FragmentBase {
private Group grpMove; private Group grpMove;
private Group grpMoveProp; private Group grpMoveProp;
private Group grpAnswer; private Group grpAnswer;
private Group grpTts;
private Group grpAutomation; private Group grpAutomation;
private ArrayAdapter<String> adapterDay; private ArrayAdapter<String> adapterDay;
@ -168,6 +172,7 @@ public class FragmentRule extends FragmentBase {
private final static int REQUEST_DELETE = 4; private final static int REQUEST_DELETE = 4;
private final static int REQUEST_SCHEDULE_START = 5; private final static int REQUEST_SCHEDULE_START = 5;
private final static int REQUEST_SCHEDULE_END = 6; private final static int REQUEST_SCHEDULE_END = 6;
private final static int REQUEST_TTS = 7;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
@ -247,6 +252,7 @@ public class FragmentRule extends FragmentBase {
spAnswer = view.findViewById(R.id.spAnswer); spAnswer = view.findViewById(R.id.spAnswer);
cbCc = view.findViewById(R.id.cbCc); cbCc = view.findViewById(R.id.cbCc);
btnTts = view.findViewById(R.id.btnTts);
tvAutomation = view.findViewById(R.id.tvAutomation); tvAutomation = view.findViewById(R.id.tvAutomation);
bottom_navigation = view.findViewById(R.id.bottom_navigation); bottom_navigation = view.findViewById(R.id.bottom_navigation);
@ -261,6 +267,7 @@ public class FragmentRule extends FragmentBase {
grpMove = view.findViewById(R.id.grpMove); grpMove = view.findViewById(R.id.grpMove);
grpMoveProp = view.findViewById(R.id.grpMoveProp); grpMoveProp = view.findViewById(R.id.grpMoveProp);
grpAnswer = view.findViewById(R.id.grpAnswer); grpAnswer = view.findViewById(R.id.grpAnswer);
grpTts = view.findViewById(R.id.grpTts);
grpAutomation = view.findViewById(R.id.grpAutomation); grpAutomation = view.findViewById(R.id.grpAutomation);
ibSender.setOnClickListener(new View.OnClickListener() { ibSender.setOnClickListener(new View.OnClickListener() {
@ -391,6 +398,7 @@ public class FragmentRule extends FragmentBase {
actions.add(new Action(EntityRule.TYPE_COPY, getString(R.string.title_rule_copy))); actions.add(new Action(EntityRule.TYPE_COPY, getString(R.string.title_rule_copy)));
} }
actions.add(new Action(EntityRule.TYPE_ANSWER, getString(R.string.title_rule_answer))); actions.add(new Action(EntityRule.TYPE_ANSWER, getString(R.string.title_rule_answer)));
actions.add(new Action(EntityRule.TYPE_TTS, getString(R.string.title_rule_tts)));
actions.add(new Action(EntityRule.TYPE_AUTOMATION, getString(R.string.title_rule_automation))); actions.add(new Action(EntityRule.TYPE_AUTOMATION, getString(R.string.title_rule_automation)));
adapterAction.addAll(actions); adapterAction.addAll(actions);
@ -454,6 +462,15 @@ public class FragmentRule extends FragmentBase {
spIdent.setOnItemSelectedListener(onItemSelectedListener); spIdent.setOnItemSelectedListener(onItemSelectedListener);
spAnswer.setOnItemSelectedListener(onItemSelectedListener); spAnswer.setOnItemSelectedListener(onItemSelectedListener);
btnTts.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent tts = new Intent();
tts.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
startActivityForResult(tts, REQUEST_TTS);
}
});
tvAutomation.setText(getString(R.string.title_rule_automation_hint, tvAutomation.setText(getString(R.string.title_rule_automation_hint,
EntityRule.ACTION_AUTOMATION, EntityRule.ACTION_AUTOMATION,
TextUtils.join(",", new String[]{ TextUtils.join(",", new String[]{
@ -491,6 +508,7 @@ public class FragmentRule extends FragmentBase {
grpMove.setVisibility(View.GONE); grpMove.setVisibility(View.GONE);
grpMoveProp.setVisibility(View.GONE); grpMoveProp.setVisibility(View.GONE);
grpAnswer.setVisibility(View.GONE); grpAnswer.setVisibility(View.GONE);
grpTts.setVisibility(View.GONE);
grpAutomation.setVisibility(View.GONE); grpAutomation.setVisibility(View.GONE);
pbWait.setVisibility(View.VISIBLE); pbWait.setVisibility(View.VISIBLE);
@ -634,6 +652,15 @@ public class FragmentRule extends FragmentBase {
if (resultCode == RESULT_OK) if (resultCode == RESULT_OK)
onScheduleEnd(data.getBundleExtra("args")); onScheduleEnd(data.getBundleExtra("args"));
break; break;
case REQUEST_TTS:
if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS)
ToastEx.makeText(getContext(), android.R.string.ok, Toast.LENGTH_LONG).show();
else {
Intent tts = new Intent();
tts.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
startActivity(tts);
}
break;
} }
} catch (Throwable ex) { } catch (Throwable ex) {
Log.e(ex); Log.e(ex);
@ -881,6 +908,7 @@ public class FragmentRule extends FragmentBase {
grpMove.setVisibility(type == EntityRule.TYPE_MOVE || type == EntityRule.TYPE_COPY ? View.VISIBLE : View.GONE); grpMove.setVisibility(type == EntityRule.TYPE_MOVE || type == EntityRule.TYPE_COPY ? View.VISIBLE : View.GONE);
grpMoveProp.setVisibility(type == EntityRule.TYPE_MOVE ? View.VISIBLE : View.GONE); grpMoveProp.setVisibility(type == EntityRule.TYPE_MOVE ? View.VISIBLE : View.GONE);
grpAnswer.setVisibility(type == EntityRule.TYPE_ANSWER ? View.VISIBLE : View.GONE); grpAnswer.setVisibility(type == EntityRule.TYPE_ANSWER ? View.VISIBLE : View.GONE);
grpTts.setVisibility(type == EntityRule.TYPE_TTS ? View.VISIBLE : View.GONE);
grpAutomation.setVisibility(type == EntityRule.TYPE_AUTOMATION ? View.VISIBLE : View.GONE); grpAutomation.setVisibility(type == EntityRule.TYPE_AUTOMATION ? View.VISIBLE : View.GONE);
} }

@ -0,0 +1,44 @@
package eu.faircode.email;
import android.content.Context;
import android.speech.tts.TextToSpeech;
import java.util.Locale;
public class TTSHelper {
private static boolean initialized;
private static TextToSpeech instance;
static void speak(Context context, final String utteranceId, final String text, final String language) {
// https://developer.android.com/reference/android/speech/tts/TextToSpeech
// https://android-developers.googleblog.com/2009/09/introduction-to-text-to-speech-in.html
final Runnable speak = new Runnable() {
@Override
public void run() {
if (language != null) {
Locale loc = new Locale(language);
if (instance.setLanguage(loc) < 0)
EntityLog.log(context, "TTS unavailable language=" + loc);
}
instance.speak(text, TextToSpeech.QUEUE_ADD, null, utteranceId);
}
};
if (initialized) {
speak.run();
return;
}
instance = new TextToSpeech(context, new TextToSpeech.OnInitListener() {
@Override
public void onInit(int status) {
initialized = (status == TextToSpeech.SUCCESS);
Log.i("TTS status=" + status + " ok=" + initialized);
if (initialized)
speak.run();
}
});
}
}

@ -689,6 +689,16 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbCc" /> app:layout_constraintTop_toBottomOf="@id/cbCc" />
<Button
android:id="@+id/btnTts"
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/title_rule_tts_setup"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvAnswerRemark" />
<eu.faircode.email.FixedTextView <eu.faircode.email.FixedTextView
android:id="@+id/tvAutomation" android:id="@+id/tvAutomation"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -697,7 +707,7 @@
android:text="@string/title_rule_automation_hint" android:text="@string/title_rule_automation_hint"
android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvAnswerRemark" /> app:layout_constraintTop_toBottomOf="@+id/btnTts" />
<androidx.constraintlayout.widget.Group <androidx.constraintlayout.widget.Group
android:id="@+id/grpReady" android:id="@+id/grpReady"
@ -755,6 +765,12 @@
android:layout_height="0dp" android:layout_height="0dp"
app:constraint_referenced_ids="tvAnswerIdentity,spIdent,tvAnswerTemplate,spAnswer,cbCc,tvAnswerRemark" /> app:constraint_referenced_ids="tvAnswerIdentity,spIdent,tvAnswerTemplate,spAnswer,cbCc,tvAnswerRemark" />
<androidx.constraintlayout.widget.Group
android:id="@+id/grpTts"
android:layout_width="0dp"
android:layout_height="0dp"
app:constraint_referenced_ids="btnTts" />
<androidx.constraintlayout.widget.Group <androidx.constraintlayout.widget.Group
android:id="@+id/grpAutomation" android:id="@+id/grpAutomation"
android:layout_width="0dp" android:layout_width="0dp"

@ -1055,6 +1055,7 @@
<string name="title_rule_move">Move</string> <string name="title_rule_move">Move</string>
<string name="title_rule_copy">Copy (label)</string> <string name="title_rule_copy">Copy (label)</string>
<string name="title_rule_answer">Reply</string> <string name="title_rule_answer">Reply</string>
<string name="title_rule_tts">Text to speech</string>
<string name="title_rule_automation">Automation</string> <string name="title_rule_automation">Automation</string>
<string name="title_rule_caption">Edit rule</string> <string name="title_rule_caption">Edit rule</string>
@ -1095,6 +1096,9 @@
<string name="title_rule_matched">Matching messages</string> <string name="title_rule_matched">Matching messages</string>
<string name="title_rule_no_matches">No matching messages</string> <string name="title_rule_no_matches">No matching messages</string>
<string name="title_rule_tts_setup">Check setup</string>
<string name="title_rule_tts_prefix">New message</string>
<string name="title_legend_section_synchronize">Synchronize</string> <string name="title_legend_section_synchronize">Synchronize</string>
<string name="title_legend_section_folders">Folders</string> <string name="title_legend_section_folders">Folders</string>
<string name="title_legend_section_messages">Messages</string> <string name="title_legend_section_messages">Messages</string>

Loading…
Cancel
Save