Merge pull request #9 from Aga-C/add-custom-notifications

Added custom notifications
This commit is contained in:
Naveen Singh 2024-01-17 19:40:04 +05:30 committed by GitHub
commit e5225de675
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 148 additions and 22 deletions

View file

@ -1,10 +1,20 @@
package org.fossify.smsmessenger.activities package org.fossify.smsmessenger.activities
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Intent
import android.media.AudioAttributes
import android.media.AudioManager
import android.media.RingtoneManager
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.provider.Settings
import androidx.annotation.RequiresApi
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import org.fossify.commons.extensions.* import org.fossify.commons.extensions.*
import org.fossify.commons.helpers.NavigationIcon import org.fossify.commons.helpers.NavigationIcon
import org.fossify.commons.helpers.ensureBackgroundThread import org.fossify.commons.helpers.ensureBackgroundThread
import org.fossify.commons.helpers.isOreoPlus
import org.fossify.commons.models.SimpleContact import org.fossify.commons.models.SimpleContact
import org.fossify.smsmessenger.adapters.ContactsAdapter import org.fossify.smsmessenger.adapters.ContactsAdapter
import org.fossify.smsmessenger.databinding.ActivityConversationDetailsBinding import org.fossify.smsmessenger.databinding.ActivityConversationDetailsBinding
@ -46,6 +56,9 @@ class ConversationDetailsActivity : SimpleActivity() {
runOnUiThread { runOnUiThread {
setupTextViews() setupTextViews()
setupParticipants() setupParticipants()
if (isOreoPlus()) {
setupCustomNotifications()
}
} }
} }
} }
@ -60,6 +73,60 @@ class ConversationDetailsActivity : SimpleActivity() {
binding.membersHeading.setTextColor(primaryColor) binding.membersHeading.setTextColor(primaryColor)
} }
@RequiresApi(Build.VERSION_CODES.O)
private fun setupCustomNotifications() {
binding.apply {
notificationsHeading.beVisible()
customNotificationsHolder.beVisible()
customNotifications.isChecked = config.customNotifications.contains(threadId.toString())
customNotificationsButton.beVisibleIf(customNotifications.isChecked)
customNotificationsHolder.setOnClickListener {
customNotifications.toggle()
if (customNotifications.isChecked) {
customNotificationsButton.beVisible()
config.addCustomNotificationsByThreadId(threadId)
createNotificationChannel()
} else {
customNotificationsButton.beGone()
config.removeCustomNotificationsByThreadId(threadId)
removeNotificationChannel()
}
}
customNotificationsButton.setOnClickListener {
Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS).apply {
putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
putExtra(Settings.EXTRA_CHANNEL_ID, threadId.toString())
startActivity(this)
}
}
}
}
@RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel() {
val name = conversation?.title
val audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setLegacyStreamType(AudioManager.STREAM_NOTIFICATION)
.build()
NotificationChannel(threadId.toString(), name, NotificationManager.IMPORTANCE_HIGH).apply {
setBypassDnd(false)
enableLights(true)
setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION), audioAttributes)
enableVibration(true)
notificationManager.createNotificationChannel(this)
}
}
@RequiresApi(Build.VERSION_CODES.O)
private fun removeNotificationChannel() {
notificationManager.deleteNotificationChannel(threadId.toString())
}
private fun setupTextViews() { private fun setupTextViews() {
binding.conversationName.apply { binding.conversationName.apply {
ResourcesCompat.getDrawable(resources, org.fossify.commons.R.drawable.ic_edit_vector, theme)?.apply { ResourcesCompat.getDrawable(resources, org.fossify.commons.R.drawable.ic_edit_vector, theme)?.apply {

View file

@ -2,6 +2,7 @@ package org.fossify.smsmessenger.extensions
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Application import android.app.Application
import android.app.NotificationManager
import android.content.ContentResolver import android.content.ContentResolver
import android.content.ContentValues import android.content.ContentValues
import android.content.Context import android.content.Context
@ -690,6 +691,11 @@ fun Context.deleteConversation(threadId: Long) {
conversationsDB.deleteThreadId(threadId) conversationsDB.deleteThreadId(threadId)
messagesDB.deleteThreadMessages(threadId) messagesDB.deleteThreadMessages(threadId)
if (config.customNotifications.contains(threadId.toString()) && isOreoPlus()) {
config.removeCustomNotificationsByThreadId(threadId)
notificationManager.deleteNotificationChannel(threadId.toString())
}
} }
fun Context.checkAndDeleteOldRecycleBinMessages(callback: (() -> Unit)? = null) { fun Context.checkAndDeleteOldRecycleBinMessages(callback: (() -> Unit)? = null) {

View file

@ -115,4 +115,16 @@ class Config(context: Context) : BaseConfig(context) {
var isArchiveAvailable: Boolean var isArchiveAvailable: Boolean
get() = prefs.getBoolean(IS_ARCHIVE_AVAILABLE, true) get() = prefs.getBoolean(IS_ARCHIVE_AVAILABLE, true)
set(isArchiveAvailable) = prefs.edit().putBoolean(IS_ARCHIVE_AVAILABLE, isArchiveAvailable).apply() set(isArchiveAvailable) = prefs.edit().putBoolean(IS_ARCHIVE_AVAILABLE, isArchiveAvailable).apply()
var customNotifications: Set<String>
get() = prefs.getStringSet(CUSTOM_NOTIFICATIONS, HashSet<String>())!!
set(customNotifications) = prefs.edit().putStringSet(CUSTOM_NOTIFICATIONS, customNotifications).apply()
fun addCustomNotificationsByThreadId(threadId: Long) {
customNotifications = customNotifications.plus(threadId.toString())
}
fun removeCustomNotificationsByThreadId(threadId: Long) {
customNotifications = customNotifications.minus(threadId.toString())
}
} }

View file

@ -44,6 +44,7 @@ const val USE_RECYCLE_BIN = "use_recycle_bin"
const val LAST_RECYCLE_BIN_CHECK = "last_recycle_bin_check" const val LAST_RECYCLE_BIN_CHECK = "last_recycle_bin_check"
const val IS_RECYCLE_BIN = "is_recycle_bin" const val IS_RECYCLE_BIN = "is_recycle_bin"
const val IS_ARCHIVE_AVAILABLE = "is_archive_available" const val IS_ARCHIVE_AVAILABLE = "is_archive_available"
const val CUSTOM_NOTIFICATIONS = "custom_notifications"
private const val PATH = "org.fossify.smsmessenger.action." private const val PATH = "org.fossify.smsmessenger.action."
const val MARK_AS_READ = PATH + "mark_as_read" const val MARK_AS_READ = PATH + "mark_as_read"

View file

@ -45,7 +45,11 @@ class NotificationHelper(private val context: Context) {
sender: String?, sender: String?,
alertOnlyOnce: Boolean = false alertOnlyOnce: Boolean = false
) { ) {
maybeCreateChannel(name = context.getString(R.string.channel_received_sms)) val hasCustomNotifications = context.config.customNotifications.contains(threadId.toString())
val notificationChannelId = if (hasCustomNotifications) threadId.toString() else NOTIFICATION_CHANNEL
if (!hasCustomNotifications) {
maybeCreateChannel(notificationChannelId, context.getString(R.string.channel_received_sms))
}
val notificationId = threadId.hashCode() val notificationId = threadId.hashCode()
val contentIntent = Intent(context, ThreadActivity::class.java).apply { val contentIntent = Intent(context, ThreadActivity::class.java).apply {
@ -98,7 +102,7 @@ class NotificationHelper(private val context: Context) {
} else { } else {
null null
} }
val builder = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL).apply { val builder = NotificationCompat.Builder(context, notificationChannelId).apply {
when (context.config.lockScreenVisibilitySetting) { when (context.config.lockScreenVisibilitySetting) {
LOCK_SCREEN_SENDER_MESSAGE -> { LOCK_SCREEN_SENDER_MESSAGE -> {
setLargeIcon(largeIcon) setLargeIcon(largeIcon)
@ -129,20 +133,24 @@ class NotificationHelper(private val context: Context) {
} }
builder.addAction(org.fossify.commons.R.drawable.ic_check_vector, context.getString(R.string.mark_as_read), markAsReadPendingIntent) builder.addAction(org.fossify.commons.R.drawable.ic_check_vector, context.getString(R.string.mark_as_read), markAsReadPendingIntent)
.setChannelId(NOTIFICATION_CHANNEL) .setChannelId(notificationChannelId)
if (isNoReplySms) { if (isNoReplySms) {
builder.addAction( builder.addAction(
org.fossify.commons.R.drawable.ic_delete_vector, org.fossify.commons.R.drawable.ic_delete_vector,
context.getString(org.fossify.commons.R.string.delete), context.getString(org.fossify.commons.R.string.delete),
deleteSmsPendingIntent deleteSmsPendingIntent
).setChannelId(NOTIFICATION_CHANNEL) ).setChannelId(notificationChannelId)
} }
notificationManager.notify(notificationId, builder.build()) notificationManager.notify(notificationId, builder.build())
} }
@SuppressLint("NewApi") @SuppressLint("NewApi")
fun showSendingFailedNotification(recipientName: String, threadId: Long) { fun showSendingFailedNotification(recipientName: String, threadId: Long) {
maybeCreateChannel(name = context.getString(R.string.message_not_sent_short)) val hasCustomNotifications = context.config.customNotifications.contains(threadId.toString())
val notificationChannelId = if (hasCustomNotifications) threadId.toString() else NOTIFICATION_CHANNEL
if (!hasCustomNotifications) {
maybeCreateChannel(notificationChannelId, context.getString(R.string.message_not_sent_short))
}
val notificationId = generateRandomId().hashCode() val notificationId = generateRandomId().hashCode()
val intent = Intent(context, ThreadActivity::class.java).apply { val intent = Intent(context, ThreadActivity::class.java).apply {
@ -152,7 +160,7 @@ class NotificationHelper(private val context: Context) {
val summaryText = String.format(context.getString(R.string.message_sending_error), recipientName) val summaryText = String.format(context.getString(R.string.message_sending_error), recipientName)
val largeIcon = SimpleContactsHelper(context).getContactLetterIcon(recipientName) val largeIcon = SimpleContactsHelper(context).getContactLetterIcon(recipientName)
val builder = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL) val builder = NotificationCompat.Builder(context, notificationChannelId)
.setContentTitle(context.getString(R.string.message_not_sent_short)) .setContentTitle(context.getString(R.string.message_not_sent_short))
.setContentText(summaryText) .setContentText(summaryText)
.setColor(context.getProperPrimaryColor()) .setColor(context.getProperPrimaryColor())
@ -164,12 +172,12 @@ class NotificationHelper(private val context: Context) {
.setDefaults(Notification.DEFAULT_LIGHTS) .setDefaults(Notification.DEFAULT_LIGHTS)
.setCategory(Notification.CATEGORY_MESSAGE) .setCategory(Notification.CATEGORY_MESSAGE)
.setAutoCancel(true) .setAutoCancel(true)
.setChannelId(NOTIFICATION_CHANNEL) .setChannelId(notificationChannelId)
notificationManager.notify(notificationId, builder.build()) notificationManager.notify(notificationId, builder.build())
} }
private fun maybeCreateChannel(name: String) { private fun maybeCreateChannel(id: String, name: String) {
if (isOreoPlus()) { if (isOreoPlus()) {
val audioAttributes = AudioAttributes.Builder() val audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION) .setUsage(AudioAttributes.USAGE_NOTIFICATION)
@ -177,7 +185,6 @@ class NotificationHelper(private val context: Context) {
.setLegacyStreamType(AudioManager.STREAM_NOTIFICATION) .setLegacyStreamType(AudioManager.STREAM_NOTIFICATION)
.build() .build()
val id = NOTIFICATION_CHANNEL
val importance = IMPORTANCE_HIGH val importance = IMPORTANCE_HIGH
NotificationChannel(id, name, importance).apply { NotificationChannel(id, name, importance).apply {
setBypassDnd(false) setBypassDnd(false)

View file

@ -22,19 +22,55 @@
android:orientation="vertical"> android:orientation="vertical">
<TextView <TextView
android:id="@+id/conversation_name_heading" android:id="@+id/notifications_heading"
style="@style/MaterialSectionLabelStyle" style="@style/SettingsSectionLabelStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
tools:visibility="visible"
android:text="@string/notifications" />
<RelativeLayout
android:id="@+id/custom_notifications_holder"
style="@style/SettingsHolderCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:visibility="visible"
android:visibility="gone">
<org.fossify.commons.views.MyAppCompatCheckbox
android:id="@+id/custom_notifications"
style="@style/SettingsCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/enable_custom_notifications" />
</RelativeLayout>
<org.fossify.commons.views.MyTextView
android:id="@+id/custom_notifications_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
tools:visibility="visible"
style="@style/SettingsHolderTextViewOneLinerStyle"
android:text="@string/customize_notifications" />
<include
android:id="@+id/settings_conversation_notifications_divider"
layout="@layout/divider" />
<TextView
android:id="@+id/conversation_name_heading"
style="@style/SettingsSectionLabelStyle"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/activity_margin"
android:layout_marginEnd="@dimen/activity_margin"
android:text="@string/conversation_name" /> android:text="@string/conversation_name" />
<org.fossify.commons.views.MyTextView <org.fossify.commons.views.MyTextView
android:id="@+id/conversation_name" android:id="@+id/conversation_name"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/small_margin"
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
android:clickable="true" android:clickable="true"
android:drawableEnd="@drawable/ic_edit_vector" android:drawableEnd="@drawable/ic_edit_vector"
@ -42,10 +78,7 @@
android:ellipsize="end" android:ellipsize="end"
android:focusable="true" android:focusable="true"
android:maxLines="1" android:maxLines="1"
android:paddingStart="@dimen/activity_margin" style="@style/SettingsHolderTextViewOneLinerStyle"
android:paddingTop="@dimen/bigger_margin"
android:paddingEnd="@dimen/activity_margin"
android:paddingBottom="@dimen/bigger_margin"
android:textSize="@dimen/bigger_text_size" android:textSize="@dimen/bigger_text_size"
tools:text="Conversation name" /> tools:text="Conversation name" />
@ -55,18 +88,16 @@
<TextView <TextView
android:id="@+id/members_heading" android:id="@+id/members_heading"
style="@style/MaterialSectionLabelStyle" style="@style/SettingsSectionLabelStyle"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/activity_margin"
android:layout_marginEnd="@dimen/activity_margin"
android:text="@string/members" /> android:text="@string/members" />
<org.fossify.commons.views.MyRecyclerView <org.fossify.commons.views.MyRecyclerView
android:id="@+id/participants_recyclerview" android:id="@+id/participants_recyclerview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginTop="@dimen/normal_margin" android:layout_marginTop="@dimen/medium_margin"
android:clipToPadding="false" android:clipToPadding="false"
app:layoutManager="org.fossify.commons.views.MyLinearLayoutManager" app:layoutManager="org.fossify.commons.views.MyLinearLayoutManager"
tools:itemCount="3" tools:itemCount="3"

View file

@ -52,6 +52,7 @@
<string name="mark_as_read">Oznacz jako przeczytane</string> <string name="mark_as_read">Oznacz jako przeczytane</string>
<string name="mark_as_unread">Oznacz jako nieprzeczytane</string> <string name="mark_as_unread">Oznacz jako nieprzeczytane</string>
<string name="me">Ja</string> <string name="me">Ja</string>
<string name="enable_custom_notifications">Włącz niestandardowe powiadomienia</string>
<string name="unarchive">Cofnij archiwizację</string> <string name="unarchive">Cofnij archiwizację</string>
<string name="empty_archive">Usuń wszystkie zarchiwizowane rozmowy</string> <string name="empty_archive">Usuń wszystkie zarchiwizowane rozmowy</string>
<string name="archived_conversations">Archiwum</string> <string name="archived_conversations">Archiwum</string>

View file

@ -57,6 +57,7 @@
<string name="mark_as_read">Mark as Read</string> <string name="mark_as_read">Mark as Read</string>
<string name="mark_as_unread">Mark as Unread</string> <string name="mark_as_unread">Mark as Unread</string>
<string name="me">Me</string> <string name="me">Me</string>
<string name="enable_custom_notifications">Enable custom notifications</string>
<!-- Archive --> <!-- Archive -->
<string name="unarchive">Unarchive</string> <string name="unarchive">Unarchive</string>
<string name="empty_archive">Delete all archived conversations</string> <string name="empty_archive">Delete all archived conversations</string>