diff --git a/app/src/main/java/com/todoroo/astrid/service/TaskCompleter.kt b/app/src/main/java/com/todoroo/astrid/service/TaskCompleter.kt
index 01c104c97..1a9c9bc7a 100644
--- a/app/src/main/java/com/todoroo/astrid/service/TaskCompleter.kt
+++ b/app/src/main/java/com/todoroo/astrid/service/TaskCompleter.kt
@@ -1,16 +1,25 @@
package com.todoroo.astrid.service
+import android.app.NotificationManager
+import android.app.NotificationManager.INTERRUPTION_FILTER_ALL
+import android.content.Context
+import android.media.MediaPlayer
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task
+import dagger.hilt.android.qualifiers.ApplicationContext
import org.tasks.data.GoogleTaskDao
+import org.tasks.preferences.Preferences
import timber.log.Timber
import javax.inject.Inject
class TaskCompleter @Inject internal constructor(
- private val taskDao: TaskDao,
- private val googleTaskDao: GoogleTaskDao) {
-
+ @ApplicationContext private val context: Context,
+ private val taskDao: TaskDao,
+ private val googleTaskDao: GoogleTaskDao,
+ private val preferences: Preferences,
+ private val notificationManager: NotificationManager,
+) {
suspend fun setComplete(taskId: Long) =
taskDao
.fetch(taskId)
@@ -43,5 +52,15 @@ class TaskCompleter @Inject internal constructor(
}
taskDao.save(task)
}
+ if (
+ tasks.size == 1 &&
+ completionDate > 0 &&
+ notificationManager.currentInterruptionFilter == INTERRUPTION_FILTER_ALL
+ ) {
+ preferences
+ .completionSound
+ ?.takeUnless { preferences.isCurrentlyQuietHours }
+ ?.let { MediaPlayer.create(context, it).start() }
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/org/tasks/extensions/Context.kt b/app/src/main/java/org/tasks/extensions/Context.kt
index 73cb53648..f45cb50cf 100644
--- a/app/src/main/java/org/tasks/extensions/Context.kt
+++ b/app/src/main/java/org/tasks/extensions/Context.kt
@@ -1,11 +1,13 @@
package org.tasks.extensions
import android.content.ActivityNotFoundException
+import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.content.Intent.ACTION_VIEW
import android.net.Uri
import android.widget.Toast
+import androidx.annotation.AnyRes
import androidx.browser.customtabs.CustomTabsIntent
import org.tasks.R
@@ -46,4 +48,11 @@ object Context {
fun Context.toast(text: String?, duration: Int = Toast.LENGTH_LONG) =
text?.let { Toast.makeText(this, it, duration).show() }
+
+ fun Context.getResourceUri(@AnyRes res: Int) =
+ Uri.Builder()
+ .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
+ .authority(packageName)
+ .path(res.toString())
+ .build()
}
diff --git a/app/src/main/java/org/tasks/injection/ApplicationModule.kt b/app/src/main/java/org/tasks/injection/ApplicationModule.kt
index 0358b9fdb..0a7cec470 100644
--- a/app/src/main/java/org/tasks/injection/ApplicationModule.kt
+++ b/app/src/main/java/org/tasks/injection/ApplicationModule.kt
@@ -1,5 +1,6 @@
package org.tasks.injection
+import android.app.NotificationManager
import android.content.Context
import com.todoroo.astrid.dao.Database
import dagger.Module
@@ -113,4 +114,8 @@ class ApplicationModule {
fun providesCoroutineScope(
@DefaultDispatcher defaultDispatcher: CoroutineDispatcher
): CoroutineScope = CoroutineScope(SupervisorJob() + defaultDispatcher)
+
+ @Provides
+ fun providesNotificationManager(@ApplicationContext context: Context) =
+ context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
}
\ No newline at end of file
diff --git a/app/src/main/java/org/tasks/preferences/Preferences.kt b/app/src/main/java/org/tasks/preferences/Preferences.kt
index 6b7d86b54..448af898d 100644
--- a/app/src/main/java/org/tasks/preferences/Preferences.kt
+++ b/app/src/main/java/org/tasks/preferences/Preferences.kt
@@ -10,6 +10,7 @@ import android.media.RingtoneManager
import android.net.Uri
import android.os.Binder
import androidx.core.app.NotificationCompat
+import androidx.core.net.toUri
import androidx.documentfile.provider.DocumentFile
import androidx.preference.PreferenceManager
import com.todoroo.andlib.utility.DateUtilities
@@ -25,6 +26,7 @@ import org.tasks.R
import org.tasks.Strings.isNullOrEmpty
import org.tasks.billing.Purchase
import org.tasks.data.TaskAttachment
+import org.tasks.extensions.Context.getResourceUri
import org.tasks.themes.ColorProvider
import org.tasks.themes.ThemeBase
import org.tasks.time.DateTime
@@ -141,13 +143,25 @@ class Preferences @JvmOverloads constructor(
}
val ringtone: Uri?
- get() {
- val ringtone = getStringValue(R.string.p_rmd_ringtone)
- ?: return RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
- return if ("" == ringtone) {
- null
- } else Uri.parse(ringtone)
+ get() = getRingtone(
+ R.string.p_rmd_ringtone,
+ RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
+ )
+
+ val completionSound: Uri?
+ get() = getRingtone(
+ R.string.p_completion_ringtone,
+ context.getResourceUri(R.raw.long_rising_tone)
+ )
+
+ private fun getRingtone(pref: Int, default: Uri): Uri? {
+ val ringtone = getStringValue(pref)
+ return when {
+ ringtone == null -> default
+ ringtone.isNotBlank() -> ringtone.toUri()
+ else -> null
}
+ }
val isTrackingEnabled: Boolean
get() = getBoolean(R.string.p_collect_statistics, true)
diff --git a/app/src/main/java/org/tasks/preferences/fragments/Notifications.kt b/app/src/main/java/org/tasks/preferences/fragments/Notifications.kt
index c2607bdf3..206db5310 100644
--- a/app/src/main/java/org/tasks/preferences/fragments/Notifications.kt
+++ b/app/src/main/java/org/tasks/preferences/fragments/Notifications.kt
@@ -9,6 +9,7 @@ import android.os.Bundle
import android.os.PowerManager
import android.provider.Settings
import android.speech.tts.TextToSpeech
+import androidx.core.net.toUri
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import androidx.preference.SwitchPreferenceCompat
@@ -21,6 +22,7 @@ import org.tasks.LocalBroadcastManager
import org.tasks.R
import org.tasks.activities.FilterSelectionActivity
import org.tasks.dialogs.MyTimePickerDialog.Companion.newTimePicker
+import org.tasks.extensions.Context.getResourceUri
import org.tasks.injection.InjectingPreferenceFragment
import org.tasks.preferences.DefaultFilterProvider
import org.tasks.preferences.Preferences
@@ -37,6 +39,7 @@ private const val REQUEST_DEFAULT_REMIND = 10003
private const val REQUEST_BADGE_LIST = 10004
private const val REQUEST_CODE_ALERT_RINGTONE = 10005
private const val REQUEST_CODE_TTS_CHECK = 10006
+private const val REQUEST_CODE_COMPLETION_SOUND = 10007
@AndroidEntryPoint
class Notifications : InjectingPreferenceFragment() {
@@ -59,6 +62,7 @@ class Notifications : InjectingPreferenceFragment() {
rescheduleNotificationsOnChange(true, R.string.p_bundle_notifications)
initializeRingtonePreference()
+ initializeCompletionSoundPreference()
initializeTimePreference(getDefaultRemindTimePreference()!!, REQUEST_DEFAULT_REMIND)
initializeTimePreference(getQuietStartPreference()!!, REQUEST_QUIET_START)
initializeTimePreference(getQuietEndPreference()!!, REQUEST_QUIET_END)
@@ -175,19 +179,27 @@ class Notifications : InjectingPreferenceFragment() {
}
override fun onPreferenceTreeClick(preference: Preference?): Boolean =
- if (preference!!.key == getString(R.string.p_rmd_ringtone)) {
+ when (preference!!.key) {
+ getString(R.string.p_rmd_ringtone),
+ getString(R.string.p_completion_ringtone) -> {
val intent = Intent(RingtoneManager.ACTION_RINGTONE_PICKER)
- intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION)
+ intent.putExtra(
+ RingtoneManager.EXTRA_RINGTONE_TYPE,
+ RingtoneManager.TYPE_NOTIFICATION
+ )
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true)
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true)
intent.putExtra(
RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI,
Settings.System.DEFAULT_NOTIFICATION_URI
)
- val existingValue: String? = preferences.getStringValue(R.string.p_rmd_ringtone)
+ val existingValue: String? = preferences.getStringValue(preference.key)
if (existingValue != null) {
if (existingValue.isEmpty()) {
- intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, null as Uri?)
+ intent.putExtra(
+ RingtoneManager.EXTRA_RINGTONE_EXISTING_URI,
+ null as Uri?
+ )
} else {
intent.putExtra(
RingtoneManager.EXTRA_RINGTONE_EXISTING_URI,
@@ -200,11 +212,20 @@ class Notifications : InjectingPreferenceFragment() {
Settings.System.DEFAULT_NOTIFICATION_URI
)
}
- startActivityForResult(intent, REQUEST_CODE_ALERT_RINGTONE)
+ startActivityForResult(
+ intent,
+ if (preference.key == getString(R.string.p_rmd_ringtone)) {
+ REQUEST_CODE_ALERT_RINGTONE
+ } else {
+ REQUEST_CODE_COMPLETION_SOUND
+ }
+ )
true
- } else {
+ }
+ else -> {
super.onPreferenceTreeClick(preference)
}
+ }
private fun getQuietStartPreference(): TimePreference? =
getTimePreference(R.string.p_rmd_quietStart)
@@ -227,29 +248,41 @@ class Notifications : InjectingPreferenceFragment() {
}
}
- private fun initializeRingtonePreference() {
+ private fun initializeRingtonePreference() =
+ initializeRingtonePreference(
+ R.string.p_rmd_ringtone,
+ R.string.silent,
+ )
+
+ private fun initializeCompletionSoundPreference() =
+ initializeRingtonePreference(
+ R.string.p_completion_ringtone,
+ R.string.none,
+ requireContext().getResourceUri(R.raw.long_rising_tone)
+ )
+
+ private fun initializeRingtonePreference(pref: Int, noneRes: Int, default: Uri? = null) {
val ringtoneChangedListener =
Preference.OnPreferenceChangeListener { preference: Preference, value: Any? ->
if ("" == value) {
- preference.setSummary(R.string.silent)
+ preference.setSummary(noneRes)
} else {
- val ringtone = RingtoneManager.getRingtone(
- context,
- if (value == null) Settings.System.DEFAULT_NOTIFICATION_URI else Uri.parse(
- value as String?
- )
- )
- preference.summary = if (ringtone == null) "" else ringtone.getTitle(context)
+ val uri =
+ (value as? String?)?.toUri()
+ ?: default
+ ?: Settings.System.DEFAULT_RINGTONE_URI
+ preference.summary = if (uri == default) {
+ getString(R.string.settings_default)
+ } else {
+ RingtoneManager.getRingtone(context, uri).getTitle(context)
+ }
}
true
}
- val ringtoneKey = R.string.p_rmd_ringtone
- val ringtonePreference: Preference = findPreference(ringtoneKey)
+ val ringtonePreference = findPreference(pref)
ringtonePreference.onPreferenceChangeListener = ringtoneChangedListener
- ringtoneChangedListener.onPreferenceChange(
- ringtonePreference,
- preferences.getStringValue(ringtoneKey)
- )
+ ringtoneChangedListener
+ .onPreferenceChange(ringtonePreference, preferences.getStringValue(pref))
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
@@ -264,6 +297,16 @@ class Notifications : InjectingPreferenceFragment() {
}
initializeRingtonePreference()
}
+ REQUEST_CODE_COMPLETION_SOUND -> if (resultCode == RESULT_OK && data != null) {
+ val ringtone: Uri? =
+ data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
+ if (ringtone != null) {
+ preferences.setString(R.string.p_completion_ringtone, ringtone.toString())
+ } else {
+ preferences.setString(R.string.p_completion_ringtone, "")
+ }
+ initializeCompletionSoundPreference()
+ }
REQUEST_QUIET_START -> if (resultCode == RESULT_OK) {
getQuietStartPreference()!!.handleTimePickerActivityIntent(data)
}
diff --git a/app/src/main/res/raw/long_rising_tone.m4a b/app/src/main/res/raw/long_rising_tone.m4a
new file mode 100644
index 000000000..ef870a296
Binary files /dev/null and b/app/src/main/res/raw/long_rising_tone.m4a differ
diff --git a/app/src/main/res/values/keys.xml b/app/src/main/res/values/keys.xml
index bf65d1f65..2f4f24682 100644
--- a/app/src/main/res/values/keys.xml
+++ b/app/src/main/res/values/keys.xml
@@ -72,6 +72,7 @@
notification_ringtone
+ completion_ringtone
notif_default_reminder
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 7f4c8a2ac..841b84b79 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -712,4 +712,5 @@ File %1$s contained %2$s.\n\n
More options
Markdown
Enable Markdown in title and description
+ Play completion sound
diff --git a/app/src/main/res/xml/preferences_notifications.xml b/app/src/main/res/xml/preferences_notifications.xml
index 5e3354f47..2fd77a633 100644
--- a/app/src/main/res/xml/preferences_notifications.xml
+++ b/app/src/main/res/xml/preferences_notifications.xml
@@ -60,6 +60,11 @@
android:key="@string/p_rmd_ringtone"
android:title="@string/sound" />
+
+