Completion sound

pull/1718/head
Alex Baker 4 years ago
parent fe48285be8
commit 21db56c4e9

@ -1,16 +1,25 @@
package com.todoroo.astrid.service 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.andlib.utility.DateUtilities
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task import com.todoroo.astrid.data.Task
import dagger.hilt.android.qualifiers.ApplicationContext
import org.tasks.data.GoogleTaskDao import org.tasks.data.GoogleTaskDao
import org.tasks.preferences.Preferences
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class TaskCompleter @Inject internal constructor( class TaskCompleter @Inject internal constructor(
private val taskDao: TaskDao, @ApplicationContext private val context: Context,
private val googleTaskDao: GoogleTaskDao) { private val taskDao: TaskDao,
private val googleTaskDao: GoogleTaskDao,
private val preferences: Preferences,
private val notificationManager: NotificationManager,
) {
suspend fun setComplete(taskId: Long) = suspend fun setComplete(taskId: Long) =
taskDao taskDao
.fetch(taskId) .fetch(taskId)
@ -43,5 +52,15 @@ class TaskCompleter @Inject internal constructor(
} }
taskDao.save(task) 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() }
}
} }
} }

@ -1,11 +1,13 @@
package org.tasks.extensions package org.tasks.extensions
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.ContentResolver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.Intent.ACTION_VIEW import android.content.Intent.ACTION_VIEW
import android.net.Uri import android.net.Uri
import android.widget.Toast import android.widget.Toast
import androidx.annotation.AnyRes
import androidx.browser.customtabs.CustomTabsIntent import androidx.browser.customtabs.CustomTabsIntent
import org.tasks.R import org.tasks.R
@ -46,4 +48,11 @@ object Context {
fun Context.toast(text: String?, duration: Int = Toast.LENGTH_LONG) = fun Context.toast(text: String?, duration: Int = Toast.LENGTH_LONG) =
text?.let { Toast.makeText(this, it, duration).show() } 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()
} }

@ -1,5 +1,6 @@
package org.tasks.injection package org.tasks.injection
import android.app.NotificationManager
import android.content.Context import android.content.Context
import com.todoroo.astrid.dao.Database import com.todoroo.astrid.dao.Database
import dagger.Module import dagger.Module
@ -113,4 +114,8 @@ class ApplicationModule {
fun providesCoroutineScope( fun providesCoroutineScope(
@DefaultDispatcher defaultDispatcher: CoroutineDispatcher @DefaultDispatcher defaultDispatcher: CoroutineDispatcher
): CoroutineScope = CoroutineScope(SupervisorJob() + defaultDispatcher) ): CoroutineScope = CoroutineScope(SupervisorJob() + defaultDispatcher)
@Provides
fun providesNotificationManager(@ApplicationContext context: Context) =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
} }

@ -10,6 +10,7 @@ import android.media.RingtoneManager
import android.net.Uri import android.net.Uri
import android.os.Binder import android.os.Binder
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.net.toUri
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.todoroo.andlib.utility.DateUtilities import com.todoroo.andlib.utility.DateUtilities
@ -25,6 +26,7 @@ import org.tasks.R
import org.tasks.Strings.isNullOrEmpty import org.tasks.Strings.isNullOrEmpty
import org.tasks.billing.Purchase import org.tasks.billing.Purchase
import org.tasks.data.TaskAttachment import org.tasks.data.TaskAttachment
import org.tasks.extensions.Context.getResourceUri
import org.tasks.themes.ColorProvider import org.tasks.themes.ColorProvider
import org.tasks.themes.ThemeBase import org.tasks.themes.ThemeBase
import org.tasks.time.DateTime import org.tasks.time.DateTime
@ -141,13 +143,25 @@ class Preferences @JvmOverloads constructor(
} }
val ringtone: Uri? val ringtone: Uri?
get() { get() = getRingtone(
val ringtone = getStringValue(R.string.p_rmd_ringtone) R.string.p_rmd_ringtone,
?: return RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
return if ("" == ringtone) { )
null
} else Uri.parse(ringtone) 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 val isTrackingEnabled: Boolean
get() = getBoolean(R.string.p_collect_statistics, true) get() = getBoolean(R.string.p_collect_statistics, true)

@ -9,6 +9,7 @@ import android.os.Bundle
import android.os.PowerManager import android.os.PowerManager
import android.provider.Settings import android.provider.Settings
import android.speech.tts.TextToSpeech import android.speech.tts.TextToSpeech
import androidx.core.net.toUri
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.SwitchPreferenceCompat import androidx.preference.SwitchPreferenceCompat
@ -21,6 +22,7 @@ import org.tasks.LocalBroadcastManager
import org.tasks.R import org.tasks.R
import org.tasks.activities.FilterSelectionActivity import org.tasks.activities.FilterSelectionActivity
import org.tasks.dialogs.MyTimePickerDialog.Companion.newTimePicker import org.tasks.dialogs.MyTimePickerDialog.Companion.newTimePicker
import org.tasks.extensions.Context.getResourceUri
import org.tasks.injection.InjectingPreferenceFragment import org.tasks.injection.InjectingPreferenceFragment
import org.tasks.preferences.DefaultFilterProvider import org.tasks.preferences.DefaultFilterProvider
import org.tasks.preferences.Preferences 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_BADGE_LIST = 10004
private const val REQUEST_CODE_ALERT_RINGTONE = 10005 private const val REQUEST_CODE_ALERT_RINGTONE = 10005
private const val REQUEST_CODE_TTS_CHECK = 10006 private const val REQUEST_CODE_TTS_CHECK = 10006
private const val REQUEST_CODE_COMPLETION_SOUND = 10007
@AndroidEntryPoint @AndroidEntryPoint
class Notifications : InjectingPreferenceFragment() { class Notifications : InjectingPreferenceFragment() {
@ -59,6 +62,7 @@ class Notifications : InjectingPreferenceFragment() {
rescheduleNotificationsOnChange(true, R.string.p_bundle_notifications) rescheduleNotificationsOnChange(true, R.string.p_bundle_notifications)
initializeRingtonePreference() initializeRingtonePreference()
initializeCompletionSoundPreference()
initializeTimePreference(getDefaultRemindTimePreference()!!, REQUEST_DEFAULT_REMIND) initializeTimePreference(getDefaultRemindTimePreference()!!, REQUEST_DEFAULT_REMIND)
initializeTimePreference(getQuietStartPreference()!!, REQUEST_QUIET_START) initializeTimePreference(getQuietStartPreference()!!, REQUEST_QUIET_START)
initializeTimePreference(getQuietEndPreference()!!, REQUEST_QUIET_END) initializeTimePreference(getQuietEndPreference()!!, REQUEST_QUIET_END)
@ -175,19 +179,27 @@ class Notifications : InjectingPreferenceFragment() {
} }
override fun onPreferenceTreeClick(preference: Preference?): Boolean = 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) 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_DEFAULT, true)
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true) intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true)
intent.putExtra( intent.putExtra(
RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI,
Settings.System.DEFAULT_NOTIFICATION_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 != null) {
if (existingValue.isEmpty()) { if (existingValue.isEmpty()) {
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, null as Uri?) intent.putExtra(
RingtoneManager.EXTRA_RINGTONE_EXISTING_URI,
null as Uri?
)
} else { } else {
intent.putExtra( intent.putExtra(
RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, RingtoneManager.EXTRA_RINGTONE_EXISTING_URI,
@ -200,11 +212,20 @@ class Notifications : InjectingPreferenceFragment() {
Settings.System.DEFAULT_NOTIFICATION_URI 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 true
} else { }
else -> {
super.onPreferenceTreeClick(preference) super.onPreferenceTreeClick(preference)
} }
}
private fun getQuietStartPreference(): TimePreference? = private fun getQuietStartPreference(): TimePreference? =
getTimePreference(R.string.p_rmd_quietStart) 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 = val ringtoneChangedListener =
Preference.OnPreferenceChangeListener { preference: Preference, value: Any? -> Preference.OnPreferenceChangeListener { preference: Preference, value: Any? ->
if ("" == value) { if ("" == value) {
preference.setSummary(R.string.silent) preference.setSummary(noneRes)
} else { } else {
val ringtone = RingtoneManager.getRingtone( val uri =
context, (value as? String?)?.toUri()
if (value == null) Settings.System.DEFAULT_NOTIFICATION_URI else Uri.parse( ?: default
value as String? ?: Settings.System.DEFAULT_RINGTONE_URI
) preference.summary = if (uri == default) {
) getString(R.string.settings_default)
preference.summary = if (ringtone == null) "" else ringtone.getTitle(context) } else {
RingtoneManager.getRingtone(context, uri).getTitle(context)
}
} }
true true
} }
val ringtoneKey = R.string.p_rmd_ringtone val ringtonePreference = findPreference(pref)
val ringtonePreference: Preference = findPreference(ringtoneKey)
ringtonePreference.onPreferenceChangeListener = ringtoneChangedListener ringtonePreference.onPreferenceChangeListener = ringtoneChangedListener
ringtoneChangedListener.onPreferenceChange( ringtoneChangedListener
ringtonePreference, .onPreferenceChange(ringtonePreference, preferences.getStringValue(pref))
preferences.getStringValue(ringtoneKey)
)
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
@ -264,6 +297,16 @@ class Notifications : InjectingPreferenceFragment() {
} }
initializeRingtonePreference() 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) { REQUEST_QUIET_START -> if (resultCode == RESULT_OK) {
getQuietStartPreference()!!.handleTimePickerActivityIntent(data) getQuietStartPreference()!!.handleTimePickerActivityIntent(data)
} }

@ -72,6 +72,7 @@
<!-- ringtone to use for notifications --> <!-- ringtone to use for notifications -->
<string name="p_rmd_ringtone">notification_ringtone</string> <string name="p_rmd_ringtone">notification_ringtone</string>
<string name="p_completion_ringtone">completion_ringtone</string>
<!-- default random reminder setting (in hours) --> <!-- default random reminder setting (in hours) -->
<string name="p_rmd_default_random_hours">notif_default_reminder</string> <string name="p_rmd_default_random_hours">notif_default_reminder</string>

@ -712,4 +712,5 @@ File %1$s contained %2$s.\n\n
<string name="more_options">More options</string> <string name="more_options">More options</string>
<string name="markdown">Markdown</string> <string name="markdown">Markdown</string>
<string name="markdown_description">Enable Markdown in title and description</string> <string name="markdown_description">Enable Markdown in title and description</string>
<string name="completion_sound">Play completion sound</string>
</resources> </resources>

@ -60,6 +60,11 @@
android:key="@string/p_rmd_ringtone" android:key="@string/p_rmd_ringtone"
android:title="@string/sound" /> android:title="@string/sound" />
<Preference
android:defaultValue="content://settings/system/notification_sound"
android:key="@string/p_completion_ringtone"
android:title="@string/completion_sound" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
android:defaultValue="true" android:defaultValue="true"
android:dependency="@string/p_rmd_enabled" android:dependency="@string/p_rmd_enabled"

Loading…
Cancel
Save