diff --git a/app/src/main/java/eu/faircode/email/ActivitySetup.java b/app/src/main/java/eu/faircode/email/ActivitySetup.java index 3ebca349cf..12b298efe3 100644 --- a/app/src/main/java/eu/faircode/email/ActivitySetup.java +++ b/app/src/main/java/eu/faircode/email/ActivitySetup.java @@ -133,16 +133,17 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac private static final int KEY_ITERATIONS = 65536; private static final int KEY_LENGTH = 256; - static final int REQUEST_SOUND = 1; - static final int REQUEST_EXPORT = 2; - static final int REQUEST_IMPORT = 3; - static final int REQUEST_CHOOSE_ACCOUNT = 4; - static final int REQUEST_DONE = 5; - static final int REQUEST_IMPORT_CERTIFICATE = 6; - static final int REQUEST_OAUTH = 7; - static final int REQUEST_STILL = 8; - static final int REQUEST_DELETE_ACCOUNT = 9; - static final int REQUEST_IMPORT_PROVIDERS = 10; + static final int REQUEST_SOUND_INBOUND = 1; + static final int REQUEST_SOUND_OUTBOUND = 2; + static final int REQUEST_EXPORT = 3; + static final int REQUEST_IMPORT = 4; + static final int REQUEST_CHOOSE_ACCOUNT = 5; + static final int REQUEST_DONE = 6; + static final int REQUEST_IMPORT_CERTIFICATE = 7; + static final int REQUEST_OAUTH = 8; + static final int REQUEST_STILL = 9; + static final int REQUEST_DELETE_ACCOUNT = 10; + static final int REQUEST_IMPORT_PROVIDERS = 11; static final int PI_MISC = 1; diff --git a/app/src/main/java/eu/faircode/email/EntityRule.java b/app/src/main/java/eu/faircode/email/EntityRule.java index be6c09c51f..bc741fbadc 100644 --- a/app/src/main/java/eu/faircode/email/EntityRule.java +++ b/app/src/main/java/eu/faircode/email/EntityRule.java @@ -987,18 +987,7 @@ public class EntityRule { message.ui_silent = true; db.message().setMessageUiSilent(message.id, message.ui_silent); - executor.submit(new Runnable() { - @Override - public void run() { - try { - if (MediaPlayerHelper.isInCall(context)) - return; - MediaPlayerHelper.play(context, uri, alarm, duration); - } catch (Throwable ex) { - Log.e(ex); - } - } - }); + MediaPlayerHelper.queue(context, uri, alarm, duration); return true; } diff --git a/app/src/main/java/eu/faircode/email/FragmentOptionsNotifications.java b/app/src/main/java/eu/faircode/email/FragmentOptionsNotifications.java index c99be2fc28..0184df3979 100644 --- a/app/src/main/java/eu/faircode/email/FragmentOptionsNotifications.java +++ b/app/src/main/java/eu/faircode/email/FragmentOptionsNotifications.java @@ -414,7 +414,7 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true); intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true); intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, sound == null ? null : Uri.parse(sound)); - startActivityForResult(Helper.getChooser(getContext(), intent), ActivitySetup.REQUEST_SOUND); + startActivityForResult(Helper.getChooser(getContext(), intent), ActivitySetup.REQUEST_SOUND_INBOUND); } }); @@ -703,7 +703,7 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared try { switch (requestCode) { - case ActivitySetup.REQUEST_SOUND: + case ActivitySetup.REQUEST_SOUND_INBOUND: if (resultCode == RESULT_OK && data != null) onSelectSound(data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)); break; diff --git a/app/src/main/java/eu/faircode/email/FragmentOptionsSend.java b/app/src/main/java/eu/faircode/email/FragmentOptionsSend.java index 69d4b5d534..9de45f38f2 100644 --- a/app/src/main/java/eu/faircode/email/FragmentOptionsSend.java +++ b/app/src/main/java/eu/faircode/email/FragmentOptionsSend.java @@ -19,8 +19,12 @@ package eu.faircode.email; Copyright 2018-2022 by Marcel Bokhorst (M66B) */ +import static android.app.Activity.RESULT_OK; + import android.content.Intent; import android.content.SharedPreferences; +import android.media.RingtoneManager; +import android.net.Uri; import android.os.Bundle; import android.text.SpannableStringBuilder; import android.text.Spanned; @@ -67,6 +71,7 @@ public class FragmentOptionsSend extends FragmentBase implements SharedPreferenc private SwitchCompat swAttachNew; private Spinner spAnswerAction; private SwitchCompat swSendPending; + private Button btnSound; private Spinner spComposeFont; private SwitchCompat swSeparateReply; @@ -98,7 +103,7 @@ public class FragmentOptionsSend extends FragmentBase implements SharedPreferenc "suggest_names", "suggest_sent", "suggested_received", "suggest_frequently", "alt_re", "alt_fwd", "send_reminders", "send_chips", "send_delayed", - "attach_new", "answer_action", "send_pending", + "attach_new", "answer_action", "send_pending", "sound_sent", "compose_font", "prefix_once", "prefix_count", "separate_reply", "extended_reply", "write_below", "quote_reply", "quote_limit", "resize_reply", "signature_location", "signature_new", "signature_reply", "signature_forward", "discard_delete", "reply_move", @@ -133,6 +138,7 @@ public class FragmentOptionsSend extends FragmentBase implements SharedPreferenc swAttachNew = view.findViewById(R.id.swAttachNew); spAnswerAction = view.findViewById(R.id.spAnswerAction); swSendPending = view.findViewById(R.id.swSendPending); + btnSound = view.findViewById(R.id.btnSound); spComposeFont = view.findViewById(R.id.spComposeFont); swSeparateReply = view.findViewById(R.id.swSeparateReply); @@ -316,6 +322,20 @@ public class FragmentOptionsSend extends FragmentBase implements SharedPreferenc } }); + btnSound.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + String sound = prefs.getString("sound_sent", null); + Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER); + intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION); + intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, getString(R.string.title_advanced_sound)); + intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true); + intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true); + intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, sound == null ? null : Uri.parse(sound)); + startActivityForResult(Helper.getChooser(getContext(), intent), ActivitySetup.REQUEST_SOUND_OUTBOUND); + } + }); + spComposeFont.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView adapterView, View view, int position, long id) { @@ -635,4 +655,35 @@ public class FragmentOptionsSend extends FragmentBase implements SharedPreferenc swLookupMx.setChecked(prefs.getBoolean("lookup_mx", false)); } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + try { + switch (requestCode) { + case ActivitySetup.REQUEST_SOUND_OUTBOUND: + if (resultCode == RESULT_OK && data != null) + onSelectSound(data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)); + break; + } + } catch (Throwable ex) { + Log.e(ex); + } + } + + private void onSelectSound(Uri uri) { + Log.i("Selected ringtone=" + uri); + + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + + if (uri == null) // no/silent sound + prefs.edit().remove("sound_sent").apply(); + else { + if ("content".equals(uri.getScheme())) + prefs.edit().putString("sound_sent", uri.toString()).apply(); + else + prefs.edit().remove("sound_sent").apply(); + } + } } diff --git a/app/src/main/java/eu/faircode/email/MediaPlayerHelper.java b/app/src/main/java/eu/faircode/email/MediaPlayerHelper.java index 4b6014eddf..6614a61bdc 100644 --- a/app/src/main/java/eu/faircode/email/MediaPlayerHelper.java +++ b/app/src/main/java/eu/faircode/email/MediaPlayerHelper.java @@ -7,13 +7,42 @@ import android.media.MediaPlayer; import android.net.Uri; import java.io.IOException; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; public class MediaPlayerHelper { + static final int DEFAULT_SOUND_DURATION = 30; // seconds static final int DEFAULT_ALARM_DURATION = 30; // seconds - static void play(Context context, Uri uri, boolean alarm, int duration) throws IOException { + private static ExecutorService executor = Helper.getBackgroundExecutor(1, "media"); + + static void queue(Context context, String uri) { + try { + queue(context, Uri.parse(uri), false, DEFAULT_SOUND_DURATION); + } catch (Throwable ex) { + Log.e(ex); + } + } + + static void queue(Context context, Uri uri, boolean alarm, int duration) { + Log.i("Queuing sound=" + uri); + + executor.submit(new Runnable() { + @Override + public void run() { + try { + if (MediaPlayerHelper.isInCall(context)) + return; + MediaPlayerHelper.play(context, uri, alarm, duration); + } catch (Throwable ex) { + Log.e(ex); + } + } + }); + } + + private static void play(Context context, Uri uri, boolean alarm, int duration) throws IOException { Semaphore sem = new Semaphore(0); AudioAttributes attrs = new AudioAttributes.Builder() diff --git a/app/src/main/java/eu/faircode/email/ServiceSend.java b/app/src/main/java/eu/faircode/email/ServiceSend.java index b3ff878279..c49739e090 100644 --- a/app/src/main/java/eu/faircode/email/ServiceSend.java +++ b/app/src/main/java/eu/faircode/email/ServiceSend.java @@ -90,7 +90,7 @@ public class ServiceSend extends ServiceBase implements SharedPreferences.OnShar EntityLog.log(this, "Service send create"); super.onCreate(); startForeground(NotificationHelper.NOTIFICATION_SEND, - getNotificationService(false).build()); + getNotificationService().build()); owner = new TwoStateOwner(this, "send"); @@ -110,7 +110,7 @@ public class ServiceSend extends ServiceBase implements SharedPreferences.OnShar NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); nm.notify(NotificationHelper.NOTIFICATION_SEND, - getNotificationService(false).build()); + getNotificationService().build()); } catch (Throwable ex) { Log.w(ex); } @@ -205,7 +205,7 @@ public class ServiceSend extends ServiceBase implements SharedPreferences.OnShar public int onStartCommand(Intent intent, int flags, int startId) { super.onStartCommand(intent, flags, startId); startForeground(NotificationHelper.NOTIFICATION_SEND, - getNotificationService(false).build()); + getNotificationService().build()); Log.i("Send intent=" + intent); Log.logExtras(intent); @@ -213,7 +213,7 @@ public class ServiceSend extends ServiceBase implements SharedPreferences.OnShar return START_STICKY; } - NotificationCompat.Builder getNotificationService(boolean alert) { + NotificationCompat.Builder getNotificationService() { NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "send") .setSmallIcon(R.drawable.baseline_send_white_24) @@ -221,7 +221,7 @@ public class ServiceSend extends ServiceBase implements SharedPreferences.OnShar .setContentIntent(getPendingIntent(this)) .setAutoCancel(false) .setShowWhen(true) - .setOnlyAlertOnce(!alert) + .setOnlyAlertOnce(true) .setDefaults(0) // disable sound on pre Android 8 .setLocalOnly(true) .setPriority(NotificationCompat.PRIORITY_MIN) @@ -334,7 +334,7 @@ public class ServiceSend extends ServiceBase implements SharedPreferences.OnShar NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); nm.notify(NotificationHelper.NOTIFICATION_SEND, - getNotificationService(false).build()); + getNotificationService().build()); } catch (Throwable ex) { Log.w(ex); } @@ -521,7 +521,7 @@ public class ServiceSend extends ServiceBase implements SharedPreferences.OnShar NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - nm.notify(NotificationHelper.NOTIFICATION_SEND, getNotificationService(true).build()); + nm.notify(NotificationHelper.NOTIFICATION_SEND, getNotificationService().build()); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); boolean reply_move = prefs.getBoolean("reply_move", false); @@ -735,7 +735,7 @@ public class ServiceSend extends ServiceBase implements SharedPreferences.OnShar if (now > last + PROGRESS_UPDATE_INTERVAL) { last = now; lastProgress = progress; - nm.notify(NotificationHelper.NOTIFICATION_SEND, getNotificationService(false).build()); + nm.notify(NotificationHelper.NOTIFICATION_SEND, getNotificationService().build()); } } } @@ -776,7 +776,7 @@ public class ServiceSend extends ServiceBase implements SharedPreferences.OnShar iservice.close(); if (lastProgress >= 0) { lastProgress = -1; - nm.notify(NotificationHelper.NOTIFICATION_SEND, getNotificationService(false).build()); + nm.notify(NotificationHelper.NOTIFICATION_SEND, getNotificationService().build()); } db.identity().setIdentityState(ident.id, null); } @@ -827,6 +827,11 @@ public class ServiceSend extends ServiceBase implements SharedPreferences.OnShar nm.cancel("send:" + message.id, NotificationHelper.NOTIFICATION_TAGGED); + // Play sent sound + String sound = prefs.getString("sound_sent", null); + if (!TextUtils.isEmpty(sound)) + MediaPlayerHelper.queue(ServiceSend.this, sound); + // Check sent message if (sid != null) { try { diff --git a/app/src/main/res/layout/fragment_options_send.xml b/app/src/main/res/layout/fragment_options_send.xml index 432e93a135..844c03021c 100644 --- a/app/src/main/res/layout/fragment_options_send.xml +++ b/app/src/main/res/layout/fragment_options_send.xml @@ -378,6 +378,18 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/spAnswerAction" app:switchPadding="12dp" /> + +