Loading common/src/main/java/com/moez/QKSMS/common/util/extensions/ContextExtensions.kt +5 −0 Original line number Original line Diff line number Diff line Loading @@ -18,6 +18,7 @@ */ */ package com.moez.QKSMS.common.util.extensions package com.moez.QKSMS.common.util.extensions import android.app.job.JobScheduler import android.content.Context import android.content.Context import android.content.res.ColorStateList import android.content.res.ColorStateList import android.graphics.Color import android.graphics.Color Loading @@ -25,6 +26,7 @@ import android.util.TypedValue import android.widget.Toast import android.widget.Toast import androidx.annotation.StringRes import androidx.annotation.StringRes import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat import androidx.core.content.getSystemService import com.moez.QKSMS.util.tryOrNull import com.moez.QKSMS.util.tryOrNull fun Context.getColorCompat(colorRes: Int): Int { fun Context.getColorCompat(colorRes: Int): Int { Loading Loading @@ -84,3 +86,6 @@ fun Context.isInstalled(packageName: String): Boolean { val Context.versionCode: Int val Context.versionCode: Int get() = packageManager.getPackageInfo(packageName, 0).versionCode get() = packageManager.getPackageInfo(packageName, 0).versionCode val Context.jobScheduler: JobScheduler get() = getSystemService()!! data/src/main/java/com/moez/QKSMS/repository/MessageRepositoryImpl.kt +27 −1 Original line number Original line Diff line number Diff line Loading @@ -66,8 +66,11 @@ import java.io.File import java.io.FileNotFoundException import java.io.FileNotFoundException import java.io.FileOutputStream import java.io.FileOutputStream import java.io.IOException import java.io.IOException import java.util.* import java.util.concurrent.TimeUnit import javax.inject.Inject import javax.inject.Inject import javax.inject.Singleton import javax.inject.Singleton import kotlin.collections.ArrayList import kotlin.math.sqrt import kotlin.math.sqrt @Singleton @Singleton Loading Loading @@ -724,4 +727,27 @@ class MessageRepositoryImpl @Inject constructor( } } } } override fun getOldMessageCounts(maxAgeDays: Int): Map<Long, Int> { return Realm.getDefaultInstance().use { realm -> realm.where(Message::class.java) .lessThan("date", now() - TimeUnit.DAYS.toMillis(maxAgeDays.toLong())) .findAll() .groupingBy { message -> message.threadId } .eachCount() } } override fun deleteOldMessages(maxAgeDays: Int) { return Realm.getDefaultInstance().use { realm -> val messages = realm.where(Message::class.java) .lessThan("date", now() - TimeUnit.DAYS.toMillis(maxAgeDays.toLong())) .findAll() val uris = messages.map { it.getUri() } realm.executeTransaction { messages.deleteAllFromRealm() } uris.forEach { uri -> context.contentResolver.delete(uri, null, null) } } } } } data/src/main/java/com/moez/QKSMS/service/AutoDeleteService.kt 0 → 100644 +62 −0 Original line number Original line Diff line number Diff line package com.moez.QKSMS.service import android.annotation.SuppressLint import android.app.job.JobInfo import android.app.job.JobParameters import android.app.job.JobService import android.content.ComponentName import android.content.Context import androidx.core.content.getSystemService import com.moez.QKSMS.common.util.extensions.jobScheduler import com.moez.QKSMS.interactor.DeleteOldMessages import dagger.android.AndroidInjection import io.reactivex.disposables.CompositeDisposable import io.reactivex.rxkotlin.plusAssign import kotlinx.coroutines.Job import timber.log.Timber import java.util.concurrent.TimeUnit import javax.inject.Inject class AutoDeleteService : JobService() { companion object { private const val JobId = 8120235 @SuppressLint("MissingPermission") // Added in [presentation]'s AndroidManifest.xml fun scheduleJob(context: Context) { Timber.i("Scheduling job") val serviceComponent = ComponentName(context, AutoDeleteService::class.java) val periodicJob = JobInfo.Builder(JobId, serviceComponent) .setPeriodic(TimeUnit.DAYS.toMillis(1)) .setPersisted(true) .build() context.jobScheduler.schedule(periodicJob) } fun cancelJob(context: Context) { Timber.i("Canceling job") context.jobScheduler.cancel(JobId) } } @Inject lateinit var deleteOldMessages: DeleteOldMessages private val disposables = CompositeDisposable() override fun onStartJob(params: JobParameters?): Boolean { Timber.i("onStartJob") AndroidInjection.inject(this) disposables += deleteOldMessages deleteOldMessages.execute(Unit) { jobFinished(params, false) } return true } override fun onStopJob(params: JobParameters?): Boolean { Timber.i("onStopJob") disposables.dispose() return true } } domain/src/main/java/com/moez/QKSMS/interactor/DeleteOldMessages.kt 0 → 100644 +44 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com> * * This file is part of QKSMS. * * QKSMS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * QKSMS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with QKSMS. If not, see <http://www.gnu.org/licenses/>. */ package com.moez.QKSMS.interactor import com.moez.QKSMS.repository.ConversationRepository import com.moez.QKSMS.repository.MessageRepository import com.moez.QKSMS.util.Preferences import io.reactivex.Flowable import timber.log.Timber import javax.inject.Inject class DeleteOldMessages @Inject constructor( private val conversationRepo: ConversationRepository, private val messageRepo: MessageRepository, private val prefs: Preferences ) : Interactor<Unit>() { override fun buildObservable(params: Unit): Flowable<*> = Flowable.fromCallable { val maxAge = prefs.autoDelete.get().takeIf { it > 0 } ?: return@fromCallable val counts = messageRepo.getOldMessageCounts(maxAge) val threadIds = counts.keys.toLongArray() Timber.d("Deleting ${counts.values.sum()} old messages from ${threadIds.size} conversations") messageRepo.deleteOldMessages(maxAge) conversationRepo.updateConversations(*threadIds) } } domain/src/main/java/com/moez/QKSMS/repository/MessageRepository.kt +10 −0 Original line number Original line Diff line number Diff line Loading @@ -102,4 +102,14 @@ interface MessageRepository { fun deleteMessages(vararg messageIds: Long) fun deleteMessages(vararg messageIds: Long) /** * Returns the number of messages older than [maxAgeDays] per conversation */ fun getOldMessageCounts(maxAgeDays: Int): Map<Long, Int> /** * Deletes all messages older than [maxAgeDays] */ fun deleteOldMessages(maxAgeDays: Int) } } Loading
common/src/main/java/com/moez/QKSMS/common/util/extensions/ContextExtensions.kt +5 −0 Original line number Original line Diff line number Diff line Loading @@ -18,6 +18,7 @@ */ */ package com.moez.QKSMS.common.util.extensions package com.moez.QKSMS.common.util.extensions import android.app.job.JobScheduler import android.content.Context import android.content.Context import android.content.res.ColorStateList import android.content.res.ColorStateList import android.graphics.Color import android.graphics.Color Loading @@ -25,6 +26,7 @@ import android.util.TypedValue import android.widget.Toast import android.widget.Toast import androidx.annotation.StringRes import androidx.annotation.StringRes import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat import androidx.core.content.getSystemService import com.moez.QKSMS.util.tryOrNull import com.moez.QKSMS.util.tryOrNull fun Context.getColorCompat(colorRes: Int): Int { fun Context.getColorCompat(colorRes: Int): Int { Loading Loading @@ -84,3 +86,6 @@ fun Context.isInstalled(packageName: String): Boolean { val Context.versionCode: Int val Context.versionCode: Int get() = packageManager.getPackageInfo(packageName, 0).versionCode get() = packageManager.getPackageInfo(packageName, 0).versionCode val Context.jobScheduler: JobScheduler get() = getSystemService()!!
data/src/main/java/com/moez/QKSMS/repository/MessageRepositoryImpl.kt +27 −1 Original line number Original line Diff line number Diff line Loading @@ -66,8 +66,11 @@ import java.io.File import java.io.FileNotFoundException import java.io.FileNotFoundException import java.io.FileOutputStream import java.io.FileOutputStream import java.io.IOException import java.io.IOException import java.util.* import java.util.concurrent.TimeUnit import javax.inject.Inject import javax.inject.Inject import javax.inject.Singleton import javax.inject.Singleton import kotlin.collections.ArrayList import kotlin.math.sqrt import kotlin.math.sqrt @Singleton @Singleton Loading Loading @@ -724,4 +727,27 @@ class MessageRepositoryImpl @Inject constructor( } } } } override fun getOldMessageCounts(maxAgeDays: Int): Map<Long, Int> { return Realm.getDefaultInstance().use { realm -> realm.where(Message::class.java) .lessThan("date", now() - TimeUnit.DAYS.toMillis(maxAgeDays.toLong())) .findAll() .groupingBy { message -> message.threadId } .eachCount() } } override fun deleteOldMessages(maxAgeDays: Int) { return Realm.getDefaultInstance().use { realm -> val messages = realm.where(Message::class.java) .lessThan("date", now() - TimeUnit.DAYS.toMillis(maxAgeDays.toLong())) .findAll() val uris = messages.map { it.getUri() } realm.executeTransaction { messages.deleteAllFromRealm() } uris.forEach { uri -> context.contentResolver.delete(uri, null, null) } } } } }
data/src/main/java/com/moez/QKSMS/service/AutoDeleteService.kt 0 → 100644 +62 −0 Original line number Original line Diff line number Diff line package com.moez.QKSMS.service import android.annotation.SuppressLint import android.app.job.JobInfo import android.app.job.JobParameters import android.app.job.JobService import android.content.ComponentName import android.content.Context import androidx.core.content.getSystemService import com.moez.QKSMS.common.util.extensions.jobScheduler import com.moez.QKSMS.interactor.DeleteOldMessages import dagger.android.AndroidInjection import io.reactivex.disposables.CompositeDisposable import io.reactivex.rxkotlin.plusAssign import kotlinx.coroutines.Job import timber.log.Timber import java.util.concurrent.TimeUnit import javax.inject.Inject class AutoDeleteService : JobService() { companion object { private const val JobId = 8120235 @SuppressLint("MissingPermission") // Added in [presentation]'s AndroidManifest.xml fun scheduleJob(context: Context) { Timber.i("Scheduling job") val serviceComponent = ComponentName(context, AutoDeleteService::class.java) val periodicJob = JobInfo.Builder(JobId, serviceComponent) .setPeriodic(TimeUnit.DAYS.toMillis(1)) .setPersisted(true) .build() context.jobScheduler.schedule(periodicJob) } fun cancelJob(context: Context) { Timber.i("Canceling job") context.jobScheduler.cancel(JobId) } } @Inject lateinit var deleteOldMessages: DeleteOldMessages private val disposables = CompositeDisposable() override fun onStartJob(params: JobParameters?): Boolean { Timber.i("onStartJob") AndroidInjection.inject(this) disposables += deleteOldMessages deleteOldMessages.execute(Unit) { jobFinished(params, false) } return true } override fun onStopJob(params: JobParameters?): Boolean { Timber.i("onStopJob") disposables.dispose() return true } }
domain/src/main/java/com/moez/QKSMS/interactor/DeleteOldMessages.kt 0 → 100644 +44 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com> * * This file is part of QKSMS. * * QKSMS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * QKSMS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with QKSMS. If not, see <http://www.gnu.org/licenses/>. */ package com.moez.QKSMS.interactor import com.moez.QKSMS.repository.ConversationRepository import com.moez.QKSMS.repository.MessageRepository import com.moez.QKSMS.util.Preferences import io.reactivex.Flowable import timber.log.Timber import javax.inject.Inject class DeleteOldMessages @Inject constructor( private val conversationRepo: ConversationRepository, private val messageRepo: MessageRepository, private val prefs: Preferences ) : Interactor<Unit>() { override fun buildObservable(params: Unit): Flowable<*> = Flowable.fromCallable { val maxAge = prefs.autoDelete.get().takeIf { it > 0 } ?: return@fromCallable val counts = messageRepo.getOldMessageCounts(maxAge) val threadIds = counts.keys.toLongArray() Timber.d("Deleting ${counts.values.sum()} old messages from ${threadIds.size} conversations") messageRepo.deleteOldMessages(maxAge) conversationRepo.updateConversations(*threadIds) } }
domain/src/main/java/com/moez/QKSMS/repository/MessageRepository.kt +10 −0 Original line number Original line Diff line number Diff line Loading @@ -102,4 +102,14 @@ interface MessageRepository { fun deleteMessages(vararg messageIds: Long) fun deleteMessages(vararg messageIds: Long) /** * Returns the number of messages older than [maxAgeDays] per conversation */ fun getOldMessageCounts(maxAgeDays: Int): Map<Long, Int> /** * Deletes all messages older than [maxAgeDays] */ fun deleteOldMessages(maxAgeDays: Int) } }