mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-28 07:50:31 +05:30
Merge pull request #1775 from Bnyro/master
Notification bell to configurate notifications by certain channels
This commit is contained in:
commit
240915a6dc
@ -52,7 +52,8 @@ class LibreTubeApp : Application() {
|
||||
/**
|
||||
* Initialize the notification listener in the background
|
||||
*/
|
||||
NotificationHelper(this).enqueueWork(
|
||||
NotificationHelper.enqueueWork(
|
||||
context = this,
|
||||
existingPeriodicWorkPolicy = ExistingPeriodicWorkPolicy.KEEP
|
||||
)
|
||||
|
||||
|
@ -93,6 +93,7 @@ object PreferenceKeys {
|
||||
const val CHECKING_FREQUENCY = "checking_frequency"
|
||||
const val REQUIRED_NETWORK = "required_network"
|
||||
const val LAST_STREAM_VIDEO_ID = "last_stream_video_id"
|
||||
const val IGNORED_NOTIFICATION_CHANNELS = "ignored_notification_channels"
|
||||
|
||||
/**
|
||||
* Advanced
|
||||
|
@ -0,0 +1,18 @@
|
||||
package com.github.libretube.extensions
|
||||
|
||||
import android.util.Log
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.util.PreferenceHelper
|
||||
import com.google.android.material.button.MaterialButton
|
||||
|
||||
fun MaterialButton.setupNotificationBell(channelId: String) {
|
||||
var isIgnorable = PreferenceHelper.isChannelNotificationIgnorable(channelId)
|
||||
Log.e(channelId, isIgnorable.toString())
|
||||
setIconResource(if (isIgnorable) R.drawable.ic_bell else R.drawable.ic_notification)
|
||||
|
||||
setOnClickListener {
|
||||
isIgnorable = !isIgnorable
|
||||
PreferenceHelper.toggleIgnorableNotificationChannel(channelId)
|
||||
setIconResource(if (isIgnorable) R.drawable.ic_bell else R.drawable.ic_notification)
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.Toast
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
@ -71,8 +72,7 @@ class CommentsAdapter(
|
||||
if (comment.hearted == true) heartedImageView.visibility = View.VISIBLE
|
||||
if (comment.repliesPage != null) repliesAvailable.visibility = View.VISIBLE
|
||||
if ((comment.replyCount ?: -1L) > 0L) {
|
||||
repliesCount.text =
|
||||
comment.replyCount?.formatShort()
|
||||
repliesCount.text = comment.replyCount?.formatShort()
|
||||
}
|
||||
|
||||
commentorImage.setOnClickListener {
|
||||
@ -89,33 +89,7 @@ class CommentsAdapter(
|
||||
repliesRecView.adapter = repliesAdapter
|
||||
if (!isRepliesAdapter && comment.repliesPage != null) {
|
||||
root.setOnClickListener {
|
||||
when {
|
||||
repliesAdapter.itemCount.equals(0) -> {
|
||||
fetchReplies(comment.repliesPage) {
|
||||
repliesAdapter.updateItems(it.comments)
|
||||
if (repliesPage.nextpage == null) {
|
||||
showMore.visibility = View.GONE
|
||||
return@fetchReplies
|
||||
}
|
||||
showMore.visibility = View.VISIBLE
|
||||
showMore.setOnClickListener {
|
||||
if (repliesPage.nextpage == null) {
|
||||
it.visibility = View.GONE
|
||||
return@setOnClickListener
|
||||
}
|
||||
fetchReplies(
|
||||
repliesPage.nextpage!!
|
||||
) {
|
||||
repliesAdapter.updateItems(repliesPage.comments)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
repliesAdapter.clear()
|
||||
showMore.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
showMoreReplies(comment.repliesPage, showMore, repliesAdapter)
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,6 +101,36 @@ class CommentsAdapter(
|
||||
}
|
||||
}
|
||||
|
||||
private fun showMoreReplies(nextPage: String, showMoreBtn: Button, repliesAdapter: CommentsAdapter) {
|
||||
when {
|
||||
repliesAdapter.itemCount.equals(0) -> {
|
||||
fetchReplies(nextPage) {
|
||||
repliesAdapter.updateItems(it.comments)
|
||||
if (repliesPage.nextpage == null) {
|
||||
showMoreBtn.visibility = View.GONE
|
||||
return@fetchReplies
|
||||
}
|
||||
showMoreBtn.visibility = View.VISIBLE
|
||||
showMoreBtn.setOnClickListener {
|
||||
if (repliesPage.nextpage == null) {
|
||||
it.visibility = View.GONE
|
||||
return@setOnClickListener
|
||||
}
|
||||
fetchReplies(
|
||||
repliesPage.nextpage!!
|
||||
) {
|
||||
repliesAdapter.updateItems(repliesPage.comments)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
repliesAdapter.clear()
|
||||
showMoreBtn.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return comments.size
|
||||
}
|
||||
|
@ -1,18 +1,22 @@
|
||||
package com.github.libretube.ui.adapters
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.api.SubscriptionHelper
|
||||
import com.github.libretube.api.obj.Subscription
|
||||
import com.github.libretube.databinding.ChannelSubscriptionRowBinding
|
||||
import com.github.libretube.extensions.setupNotificationBell
|
||||
import com.github.libretube.extensions.toID
|
||||
import com.github.libretube.ui.viewholders.SubscriptionChannelViewHolder
|
||||
import com.github.libretube.util.ImageHelper
|
||||
import com.github.libretube.util.NavigationHelper
|
||||
|
||||
class SubscriptionChannelAdapter(private val subscriptions: MutableList<com.github.libretube.api.obj.Subscription>) :
|
||||
RecyclerView.Adapter<SubscriptionChannelViewHolder>() {
|
||||
class SubscriptionChannelAdapter(
|
||||
private val subscriptions: MutableList<Subscription>
|
||||
) : RecyclerView.Adapter<SubscriptionChannelViewHolder>() {
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return subscriptions.size
|
||||
@ -27,25 +31,30 @@ class SubscriptionChannelAdapter(private val subscriptions: MutableList<com.gith
|
||||
|
||||
override fun onBindViewHolder(holder: SubscriptionChannelViewHolder, position: Int) {
|
||||
val subscription = subscriptions[position]
|
||||
var subscribed = true
|
||||
var isSubscribed = true
|
||||
|
||||
holder.binding.apply {
|
||||
subscriptionChannelName.text = subscription.name
|
||||
ImageHelper.loadImage(subscription.avatar, subscriptionChannelImage)
|
||||
|
||||
subscription.url?.toID()?.let { notificationBell.setupNotificationBell(it) }
|
||||
|
||||
root.setOnClickListener {
|
||||
NavigationHelper.navigateChannel(root.context, subscription.url)
|
||||
}
|
||||
subscriptionSubscribe.setOnClickListener {
|
||||
val channelId = subscription.url!!.toID()
|
||||
if (subscribed) {
|
||||
if (isSubscribed) {
|
||||
SubscriptionHelper.handleUnsubscribe(root.context, channelId, subscription.name ?: "") {
|
||||
subscriptionSubscribe.text = root.context.getString(R.string.subscribe)
|
||||
subscribed = false
|
||||
notificationBell.visibility = View.GONE
|
||||
isSubscribed = false
|
||||
}
|
||||
} else {
|
||||
SubscriptionHelper.subscribe(channelId)
|
||||
subscriptionSubscribe.text = root.context.getString(R.string.unsubscribe)
|
||||
subscribed = true
|
||||
notificationBell.visibility = View.VISIBLE
|
||||
isSubscribed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import com.github.libretube.databinding.FragmentChannelBinding
|
||||
import com.github.libretube.enums.ShareObjectType
|
||||
import com.github.libretube.extensions.TAG
|
||||
import com.github.libretube.extensions.formatShort
|
||||
import com.github.libretube.extensions.setupNotificationBell
|
||||
import com.github.libretube.extensions.toID
|
||||
import com.github.libretube.obj.ShareData
|
||||
import com.github.libretube.ui.adapters.SearchAdapter
|
||||
@ -128,16 +129,21 @@ class ChannelFragment : BaseFragment() {
|
||||
binding.channelSubscribe.text = getString(R.string.unsubscribe)
|
||||
}
|
||||
|
||||
channelId?.let { binding.notificationBell.setupNotificationBell(it) }
|
||||
if (isSubscribed == false) binding.notificationBell.visibility = View.GONE
|
||||
|
||||
binding.channelSubscribe.setOnClickListener {
|
||||
if (isSubscribed == true) {
|
||||
SubscriptionHelper.handleUnsubscribe(requireContext(), channelId!!, channelName) {
|
||||
isSubscribed = false
|
||||
binding.channelSubscribe.text = getString(R.string.subscribe)
|
||||
binding.notificationBell.visibility = View.GONE
|
||||
}
|
||||
} else {
|
||||
SubscriptionHelper.subscribe(channelId!!)
|
||||
isSubscribed = true
|
||||
binding.channelSubscribe.text = getString(R.string.unsubscribe)
|
||||
binding.notificationBell.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,8 +45,9 @@ class NotificationSettings : BasePreferenceFragment() {
|
||||
|
||||
private fun updateNotificationPrefs() {
|
||||
// replace the previous queued work request
|
||||
NotificationHelper(requireContext())
|
||||
NotificationHelper
|
||||
.enqueueWork(
|
||||
context = requireContext(),
|
||||
existingPeriodicWorkPolicy = ExistingPeriodicWorkPolicy.REPLACE
|
||||
)
|
||||
}
|
||||
|
@ -30,11 +30,11 @@ class PlaybackSpeedSheet(
|
||||
binding.speed.value = player.playbackParameters.speed
|
||||
binding.pitch.value = player.playbackParameters.pitch
|
||||
|
||||
binding.speed.addOnChangeListener { _, value, _ ->
|
||||
binding.speed.addOnChangeListener { _, _, _ ->
|
||||
onChange()
|
||||
}
|
||||
|
||||
binding.pitch.addOnChangeListener { _, value, _ ->
|
||||
binding.pitch.addOnChangeListener { _, _, _ ->
|
||||
onChange()
|
||||
}
|
||||
|
||||
|
@ -1,46 +1,21 @@
|
||||
package com.github.libretube.util
|
||||
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.work.Constraints
|
||||
import androidx.work.ExistingPeriodicWorkPolicy
|
||||
import androidx.work.NetworkType
|
||||
import androidx.work.PeriodicWorkRequest
|
||||
import androidx.work.WorkManager
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.api.RetrofitInstance
|
||||
import com.github.libretube.api.SubscriptionHelper
|
||||
import com.github.libretube.constants.NOTIFICATION_WORK_NAME
|
||||
import com.github.libretube.constants.PUSH_CHANNEL_ID
|
||||
import com.github.libretube.constants.PreferenceKeys
|
||||
import com.github.libretube.extensions.toID
|
||||
import com.github.libretube.ui.activities.MainActivity
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class NotificationHelper(
|
||||
private val context: Context
|
||||
) {
|
||||
val NotificationManager =
|
||||
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
|
||||
// the id where notification channels start
|
||||
private var notificationId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
NotificationManager.activeNotifications.size + 5
|
||||
} else {
|
||||
5
|
||||
}
|
||||
|
||||
object NotificationHelper {
|
||||
/**
|
||||
* Enqueue the work manager task
|
||||
*/
|
||||
fun enqueueWork(
|
||||
context: Context,
|
||||
existingPeriodicWorkPolicy: ExistingPeriodicWorkPolicy
|
||||
) {
|
||||
// get the notification preferences
|
||||
@ -95,115 +70,4 @@ class NotificationHelper(
|
||||
.cancelUniqueWork(NOTIFICATION_WORK_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* check whether new streams are available in subscriptions
|
||||
*/
|
||||
fun checkForNewStreams(): Boolean {
|
||||
var success = true
|
||||
|
||||
val token = PreferenceHelper.getToken()
|
||||
runBlocking {
|
||||
val task = async {
|
||||
if (token != "") {
|
||||
RetrofitInstance.authApi.getFeed(token)
|
||||
} else {
|
||||
RetrofitInstance.authApi.getUnauthenticatedFeed(
|
||||
SubscriptionHelper.getFormattedLocalSubscriptions()
|
||||
)
|
||||
}
|
||||
}
|
||||
// fetch the users feed
|
||||
val videoFeed = try {
|
||||
task.await()
|
||||
} catch (e: Exception) {
|
||||
success = false
|
||||
return@runBlocking
|
||||
}
|
||||
|
||||
val lastSeenStreamId = PreferenceHelper.getLastSeenVideoId()
|
||||
val latestFeedStreamId = videoFeed[0].url!!.toID()
|
||||
|
||||
// first time notifications enabled or no new video available
|
||||
if (lastSeenStreamId == "" || lastSeenStreamId == latestFeedStreamId) {
|
||||
PreferenceHelper.setLatestVideoId(lastSeenStreamId)
|
||||
return@runBlocking
|
||||
}
|
||||
|
||||
// filter the new videos out
|
||||
val lastSeenStreamItem = videoFeed.filter { it.url!!.toID() == lastSeenStreamId }
|
||||
|
||||
// previous video not found
|
||||
if (lastSeenStreamItem.isEmpty()) return@runBlocking
|
||||
|
||||
val lastStreamIndex = videoFeed.indexOf(lastSeenStreamItem[0])
|
||||
val newVideos = videoFeed.filterIndexed { index, _ ->
|
||||
index < lastStreamIndex
|
||||
}
|
||||
|
||||
// group the new streams by the uploader
|
||||
val channelGroups = newVideos.groupBy { it.uploaderUrl }
|
||||
// create a notification for each new stream
|
||||
channelGroups.forEach { (_, streams) ->
|
||||
createNotification(
|
||||
group = streams[0].uploaderUrl!!.toID(),
|
||||
title = streams[0].uploaderName.toString(),
|
||||
isSummary = true
|
||||
)
|
||||
|
||||
streams.forEach { streamItem ->
|
||||
notificationId += 1
|
||||
createNotification(
|
||||
title = streamItem.title.toString(),
|
||||
description = streamItem.uploaderName.toString(),
|
||||
group = streamItem.uploaderUrl!!.toID()
|
||||
)
|
||||
}
|
||||
}
|
||||
// save the latest streams that got notified about
|
||||
PreferenceHelper.setLatestVideoId(videoFeed[0].url!!.toID())
|
||||
}
|
||||
// return whether the work succeeded
|
||||
return success
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification that is created when new streams are found
|
||||
*/
|
||||
private fun createNotification(
|
||||
title: String,
|
||||
group: String,
|
||||
description: String? = null,
|
||||
isSummary: Boolean = false
|
||||
) {
|
||||
val intent = Intent(context, MainActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
}
|
||||
val pendingIntent: PendingIntent = PendingIntent.getActivity(
|
||||
context,
|
||||
0,
|
||||
intent,
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
|
||||
val builder = NotificationCompat.Builder(context, PUSH_CHANNEL_ID)
|
||||
.setContentTitle(title)
|
||||
.setGroup(group)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||
// Set the intent that will fire when the user taps the notification
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true)
|
||||
|
||||
if (isSummary) {
|
||||
builder.setGroupSummary(true)
|
||||
} else {
|
||||
builder.setContentText(description)
|
||||
}
|
||||
|
||||
with(NotificationManagerCompat.from(context)) {
|
||||
// notificationId is a unique int for each notification that you must define
|
||||
notify(notificationId, builder.build())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,22 @@
|
||||
package com.github.libretube.util
|
||||
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.api.RetrofitInstance
|
||||
import com.github.libretube.api.SubscriptionHelper
|
||||
import com.github.libretube.constants.PUSH_CHANNEL_ID
|
||||
import com.github.libretube.extensions.toID
|
||||
import com.github.libretube.ui.activities.MainActivity
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
/**
|
||||
* The notification worker which checks for new streams in a certain frequency
|
||||
@ -10,11 +24,139 @@ import androidx.work.WorkerParameters
|
||||
class NotificationWorker(appContext: Context, parameters: WorkerParameters) :
|
||||
Worker(appContext, parameters) {
|
||||
|
||||
private val notificationManager =
|
||||
appContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
|
||||
// the id where notification channels start
|
||||
private var notificationId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
notificationManager.activeNotifications.size + 5
|
||||
} else {
|
||||
5
|
||||
}
|
||||
|
||||
override fun doWork(): Result {
|
||||
// check whether there are new streams and notify if there are some
|
||||
val result = NotificationHelper(applicationContext)
|
||||
.checkForNewStreams()
|
||||
val result = checkForNewStreams()
|
||||
// return success if the API request succeeded
|
||||
return if (result) Result.success() else Result.retry()
|
||||
}
|
||||
|
||||
/**
|
||||
* check whether new streams are available in subscriptions
|
||||
*/
|
||||
private fun checkForNewStreams(): Boolean {
|
||||
var success = true
|
||||
|
||||
val token = PreferenceHelper.getToken()
|
||||
runBlocking {
|
||||
val task = async {
|
||||
if (token != "") {
|
||||
RetrofitInstance.authApi.getFeed(token)
|
||||
} else {
|
||||
RetrofitInstance.authApi.getUnauthenticatedFeed(
|
||||
SubscriptionHelper.getFormattedLocalSubscriptions()
|
||||
)
|
||||
}
|
||||
}
|
||||
// fetch the users feed
|
||||
val videoFeed = try {
|
||||
task.await()
|
||||
} catch (e: Exception) {
|
||||
success = false
|
||||
return@runBlocking
|
||||
}
|
||||
|
||||
val lastSeenStreamId = PreferenceHelper.getLastSeenVideoId()
|
||||
val latestFeedStreamId = videoFeed[0].url!!.toID()
|
||||
|
||||
// first time notifications enabled or no new video available
|
||||
if (lastSeenStreamId == "" || lastSeenStreamId == latestFeedStreamId) {
|
||||
PreferenceHelper.setLatestVideoId(lastSeenStreamId)
|
||||
return@runBlocking
|
||||
}
|
||||
|
||||
// filter the new videos out
|
||||
val lastSeenStreamItem = videoFeed.filter { it.url!!.toID() == lastSeenStreamId }
|
||||
|
||||
// previous video not found
|
||||
if (lastSeenStreamItem.isEmpty()) return@runBlocking
|
||||
|
||||
val lastStreamIndex = videoFeed.indexOf(lastSeenStreamItem[0])
|
||||
val newVideos = videoFeed.filterIndexed { index, _ ->
|
||||
index < lastStreamIndex
|
||||
}
|
||||
|
||||
// hide for notifications unsubscribed channels
|
||||
val channelsToIgnore = PreferenceHelper.getIgnorableNotificationChannels()
|
||||
val filteredVideos = newVideos.filter {
|
||||
channelsToIgnore.none { channelId ->
|
||||
channelId == it.uploaderUrl?.toID()
|
||||
}
|
||||
}
|
||||
|
||||
// group the new streams by the uploader
|
||||
val channelGroups = filteredVideos.groupBy { it.uploaderUrl }
|
||||
// create a notification for each new stream
|
||||
channelGroups.forEach { (_, streams) ->
|
||||
createNotification(
|
||||
group = streams[0].uploaderUrl!!.toID(),
|
||||
title = streams[0].uploaderName.toString(),
|
||||
isSummary = true
|
||||
)
|
||||
|
||||
streams.forEach { streamItem ->
|
||||
notificationId += 1
|
||||
createNotification(
|
||||
title = streamItem.title.toString(),
|
||||
description = streamItem.uploaderName.toString(),
|
||||
group = streamItem.uploaderUrl!!.toID()
|
||||
)
|
||||
}
|
||||
}
|
||||
// save the latest streams that got notified about
|
||||
PreferenceHelper.setLatestVideoId(videoFeed[0].url!!.toID())
|
||||
}
|
||||
// return whether the work succeeded
|
||||
return success
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification that is created when new streams are found
|
||||
*/
|
||||
private fun createNotification(
|
||||
title: String,
|
||||
group: String,
|
||||
description: String? = null,
|
||||
isSummary: Boolean = false
|
||||
) {
|
||||
val intent = Intent(applicationContext, MainActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
}
|
||||
val pendingIntent: PendingIntent = PendingIntent.getActivity(
|
||||
applicationContext,
|
||||
0,
|
||||
intent,
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
|
||||
val builder = NotificationCompat.Builder(applicationContext, PUSH_CHANNEL_ID)
|
||||
.setContentTitle(title)
|
||||
.setGroup(group)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||
// Set the intent that will fire when the user taps the notification
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true)
|
||||
|
||||
if (isSummary) {
|
||||
builder.setGroupSummary(true)
|
||||
} else {
|
||||
builder.setContentText(description)
|
||||
}
|
||||
|
||||
with(NotificationManagerCompat.from(applicationContext)) {
|
||||
// notificationId is a unique int for each notification that you must define
|
||||
notify(notificationId, builder.build())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -85,6 +85,23 @@ object PreferenceHelper {
|
||||
return getString(PreferenceKeys.ERROR_LOG, "")
|
||||
}
|
||||
|
||||
fun getIgnorableNotificationChannels(): List<String> {
|
||||
return getString(PreferenceKeys.IGNORED_NOTIFICATION_CHANNELS, "").split(",")
|
||||
}
|
||||
|
||||
fun isChannelNotificationIgnorable(channelId: String): Boolean {
|
||||
return getIgnorableNotificationChannels().any { it == channelId }
|
||||
}
|
||||
|
||||
fun toggleIgnorableNotificationChannel(channelId: String) {
|
||||
val ignorableChannels = getIgnorableNotificationChannels().toMutableList()
|
||||
if (ignorableChannels.contains(channelId)) ignorableChannels.remove(channelId) else ignorableChannels.add(channelId)
|
||||
editor.putString(
|
||||
PreferenceKeys.IGNORED_NOTIFICATION_CHANNELS,
|
||||
ignorableChannels.joinToString(",")
|
||||
).apply()
|
||||
}
|
||||
|
||||
private fun getDefaultSharedPreferences(context: Context): SharedPreferences {
|
||||
return PreferenceManager.getDefaultSharedPreferences(context)
|
||||
}
|
||||
|
10
app/src/main/res/drawable/ic_bell.xml
Normal file
10
app/src/main/res/drawable/ic_bell.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M8,38v-3h4.2L12.2,19.7q0,-4.2 2.475,-7.475Q17.15,8.95 21.2,8.1L21.2,6.65q0,-1.15 0.825,-1.9T24,4q1.15,0 1.975,0.75 0.825,0.75 0.825,1.9L26.8,8.1q4.05,0.85 6.55,4.125t2.5,7.475L35.85,35L40,35v3ZM24,23.25ZM24,44q-1.6,0 -2.8,-1.175Q20,41.65 20,40h8q0,1.65 -1.175,2.825Q25.65,44 24,44ZM15.2,35h17.65L32.85,19.7q0,-3.7 -2.55,-6.3 -2.55,-2.6 -6.25,-2.6t-6.275,2.6Q15.2,16 15.2,19.7Z" />
|
||||
</vector>
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
@ -12,32 +12,38 @@
|
||||
android:id="@+id/subscription_channel_image"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginStart="8dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subscription_channel_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_toEndOf="@id/subscription_channel_image"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textSize="16sp"
|
||||
tools:text="Channel Name" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/notification_bell"
|
||||
style="@style/ElevatedIconButton"
|
||||
android:layout_gravity="center"
|
||||
app:icon="@drawable/ic_notification"
|
||||
tools:targetApi="m" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/subscription_subscribe"
|
||||
style="@style/Widget.Material3.Button.ElevatedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:stateListAnimator="@null"
|
||||
android:text="@string/unsubscribe"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="12sp"
|
||||
app:cornerRadius="20dp" />
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
@ -45,30 +45,25 @@
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
<TextView
|
||||
android:id="@+id/channel_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="3.5dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/channel_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:drawablePadding="3dip"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="Channel Name" />
|
||||
|
||||
</LinearLayout>
|
||||
android:layout_gravity="start"
|
||||
android:layout_marginTop="3.5dp"
|
||||
android:drawablePadding="3dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="Channel Name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/channel_subs"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:maxLines="1"
|
||||
android:text="@string/app_name"
|
||||
android:textSize="12sp" />
|
||||
|
||||
@ -76,18 +71,16 @@
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/channel_share"
|
||||
style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:drawableTint="?android:attr/textColorPrimary"
|
||||
android:paddingHorizontal="15dp"
|
||||
android:stateListAnimator="@null"
|
||||
app:cornerRadius="20dp"
|
||||
app:elevation="20dp"
|
||||
style="@style/ElevatedIconButton"
|
||||
app:icon="@drawable/ic_share"
|
||||
tools:targetApi="m" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/notification_bell"
|
||||
style="@style/ElevatedIconButton"
|
||||
app:icon="@drawable/ic_notification"
|
||||
tools:targetApi="m" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/channel_subscribe"
|
||||
style="@style/Widget.Material3.Button.ElevatedButton"
|
||||
|
@ -195,4 +195,15 @@
|
||||
|
||||
</style>
|
||||
|
||||
<style name="ElevatedIconButton" parent="@style/Widget.Material3.Button.IconButton.Filled.Tonal">
|
||||
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:paddingStart">15dp</item>
|
||||
<item name="android:paddingEnd">15dp</item>
|
||||
<item name="android:stateListAnimator">@null</item>
|
||||
<item name="cardCornerRadius">25dp</item>
|
||||
<item name="android:drawableTint" tools:targetApi="m">?android:attr/textColorPrimary</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
Loading…
x
Reference in New Issue
Block a user