Background worker updates aren't deleting original worker. (Android Studio - Jetpack Compose/Kotlin)

69 Views Asked by At

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.

0

There are 0 best solutions below