I have a background worker that I toggle in one spot, but can add a filter to after the fact elsewhere. When I add a filter, I try to update and/or remove the original worker if one exists, but this seems to not be updating. I've even tried removing the update (which adds the worker back) completely and replaced it with the prune and/or cancelAllWork stuff, which does cancel the work. Then I uncomment the update logic, and it once again results in just adding another without cancelling the original. This can leave stacks of workers if left alone. It seems that on a relaunch of the app, the extra workers aren't there. That doesn't really solve the problem though.
Why is this code not working properly?
Fair warning, this code is a mess I know.
NotificationManager
class NotificationHandler : ComponentActivity() {
private val CHANNEL_ID = "posts"
private val SHARED_PREF_NAME = "notification_prefs"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
NotificationContent()
}
}
@Composable
fun NotificationContent() {
val context = LocalContext.current
var hasNotificationPermission by rememberSaveable {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
mutableStateOf(
ContextCompat.checkSelfPermission(
context,
Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED
)
} else {mutableStateOf(true)}
}
val workManager = WorkManager.getInstance(this)
var notificationToggle by rememberSaveable{
mutableStateOf(getNotificationToggleState(context))
}
val launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestPermission(),
onResult = { isGranted ->
hasNotificationPermission = isGranted
}
)
if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) && !hasNotificationPermission) {
Button(
onClick = {
launcher.launch(Manifest.permission.POST_NOTIFICATIONS)
},
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
contentColor = MaterialTheme.colorScheme.onPrimary
)
) {
Text(text = "Request Notification Permission")
}
}
if (hasNotificationPermission) {
Row(verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(horizontal = 16.dp)
.fillMaxWidth()
) {
Text("Toggle Notifications", color = MaterialTheme.colorScheme.onSurface, fontWeight = FontWeight.Bold)
Spacer(Modifier.weight(1f))
Switch(
modifier = Modifier.scale(1.3f),
checked = notificationToggle,
onCheckedChange = { newToggleState ->
notificationToggle = newToggleState
if (newToggleState) {
workManager.enqueueUniquePeriodicWork(
"updateCheckRequest",
ExistingPeriodicWorkPolicy.UPDATE,
updateCheckRequest
)
} else {
workManager.cancelAllWork()
workManager.pruneWork()
}
saveNotificationToggleState(context, newToggleState)
}
)
}
}
}
private fun getNotificationToggleState(context: Context): Boolean {
val sharedPreferences: SharedPreferences = context.getSharedPreferences(SHARED_PREF_NAME, MODE_PRIVATE)
return sharedPreferences.getBoolean("notification_toggle", false)
}
private fun saveNotificationToggleState(context: Context, state: Boolean) {
val sharedPreferences: SharedPreferences = context.getSharedPreferences(SHARED_PREF_NAME, MODE_PRIVATE)
with(sharedPreferences.edit()) {
putBoolean("notification_toggle", state)
apply()
}
}
}
Filter
class Filter(private val context: Context) {
private val sharedPreferences = context.getSharedPreferences("ExclusionPrefs", Context.MODE_PRIVATE)
private var filter = sharedPreferences.getStringSet("filter", HashSet()) ?: HashSet()
private var filterText = sharedPreferences.getString("filterText", "") ?: ""
private var textToSave by mutableStateOf("")
private var editingFilters by mutableStateOf(false)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun FilterContent(){
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(horizontal = 16.dp)
.fillMaxWidth()
) {
Text(
"Add/Edit Filters",
color = MaterialTheme.colorScheme.onSurface,
fontWeight = FontWeight.Bold
)
Spacer(Modifier.weight(1f))
Button(
onClick = {editingFilters = true},
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
contentColor = MaterialTheme.colorScheme.onPrimary
)
) {
Icon(
painter = painterResource(id = R.drawable.filter_icon),
contentDescription = "Filter",
tint = MaterialTheme.colorScheme.onPrimary
)
}
}
if (editingFilters) {
if (filterText != "") {
Text("Current Filter: $filterText", fontStyle = FontStyle.Italic, modifier = Modifier.padding(bottom = 8.dp))
}
TextField(
value = textToSave,
onValueChange = { textToSave = it },
label = { Text("Separate By Comma(s)") },
placeholder = { Text("Epic, (DLC), [PSA], etc.") },
singleLine = true,
keyboardActions = KeyboardActions(
onDone = {
saveTextToSharedPreferences(textToSave)
editingFilters = false
}
)
)
if (filterText != "") {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(horizontal = 16.dp)
.fillMaxWidth()
) {
Text(
"Delete All Filters?",
color = MaterialTheme.colorScheme.onSurface,
fontWeight = FontWeight.Bold
)
Spacer(Modifier.weight(1f))
Button(
onClick = {
saveTextToSharedPreferences("")
editingFilters = false},
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
contentColor = MaterialTheme.colorScheme.onPrimary
)
) {
Icon(
imageVector = Icons.Default.Delete,
contentDescription = "Delete Filters",
tint = MaterialTheme.colorScheme.onPrimary
)
}
}
}
}
}
private fun saveTextToSharedPreferences(text: String) {
if (text != "") {
val keywords = text.split(",").map { it.trim() }.toSet()
filter = HashSet() // Clearing the old
filterText = "" // Clearing the old
filter = HashSet(filter + keywords) // Update the filter set with the new keywords
sharedPreferences.edit {
putString("filterText", text)
putStringSet("filter", filter)
}
// Only runs if notifications are enabled
val workManager = WorkManager.getInstance(context)
val workInfos = workManager.getWorkInfosForUniqueWork("updateCheckRequest").get()
if (workInfos.isNotEmpty()) {
workManager.cancelAllWork() // Added during testing
workManager.pruneWork() // Added during testing
// Specifically where the problem is
workManager.enqueueUniquePeriodicWork(
"updateCheckRequest",
ExistingPeriodicWorkPolicy.UPDATE,
updateCheckRequest
)
}
}
else {
filter = HashSet() // Clearing the old
filterText = "" // Clearing the old
sharedPreferences.edit{
putStringSet("filter", HashSet())
putString("filterText", "")
}
// Only runs if notifications are enabled
val workManager = WorkManager.getInstance(context)
val workInfos = workManager.getWorkInfosForUniqueWork("updateCheckRequest").get()
if (workInfos.isNotEmpty()) {
workManager.cancelAllWork() // Added during testing
workManager.pruneWork() // Added during testing
// Specifically where the problem is
workManager.enqueueUniquePeriodicWork(
"updateCheckRequest",
ExistingPeriodicWorkPolicy.UPDATE,
updateCheckRequest
)
}
}
}
}
UpdateWorkerCheck
class UpdateCheckWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
override fun doWork(): Result {
try {
val appContext = applicationContext
val sharedPreferences = appContext.getSharedPreferences("ExclusionPrefs", Context.MODE_PRIVATE)
val filter = sharedPreferences.getStringSet("filter", HashSet()) ?: HashSet()
val intent = Intent(appContext, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
val pendingIntent: PendingIntent = PendingIntent.getActivity(
appContext,
1,
intent,
PendingIntent.FLAG_IMMUTABLE
)
data class CheckItem(val title: String, val id: String)
val retrofit = Retrofit.Builder()
.baseUrl("https://www.reddit.com/r/")
.addConverterFactory(GsonConverterFactory.create())
.build()
val retrofitAPI = retrofit.create(RetroFitAPI::class.java)
val response = retrofitAPI.getData().execute()
if (response.isSuccessful) {
val db_li = LatestDatabase.getInstance(appContext)
if (db_li.latestItemDao().getAll() == null) { return Result.failure()}
val data = response.body()
data?.let {
val check = it.data.children.map { child ->
CheckItem(title = child.data.title, id = child.data.id)
}.filterNot { check -> filter.any { keyword -> check.title.contains(keyword, ignoreCase = true) } }
var notificationId = (0..1000).random() // This should prob be a static int but I don't want it to be overwritten
GlobalScope.launch(Dispatchers.IO) {
// Added precaution
delay(20000)
check.take(3).forEach { check ->
if (db_li.latestItemDao().getLatestItemById(check.id) == null) {
db_li.latestItemDao().insert(LatestItem(check.id))
val notification =
NotificationCompat.Builder(appContext, "posts")
.setSmallIcon(R.drawable.logo_whiteout)
.setContentTitle("New Post Is Live!")
.setContentText(check.title)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.build()
val notificationManager = ContextCompat.getSystemService(
appContext,
NotificationManager::class.java
)
notificationManager?.notify(notificationId, notification)
notificationId++
}
}
}
}
}
return Result.success()
} catch (e: Exception) {
return Result.failure()
}
}
}
// Schedule the worker to run periodically
val updateCheckRequest = PeriodicWorkRequestBuilder<UpdateCheckWorker>(5, TimeUnit.MINUTES)
.setConstraints(Constraints(NetworkType.CONNECTED))
.setInitialDelay(1, TimeUnit.MINUTES)
.build()
Thanks for any insight.