diff --git a/data/build.gradle b/data/build.gradle index 92fc5b78..e8c5f296 100644 --- a/data/build.gradle +++ b/data/build.gradle @@ -25,6 +25,7 @@ dependencies { implementation "io.reactivex.rxjava2:rxjava:$rxjava_version" implementation "io.reactivex.rxjava2:rxkotlin:$rxkotlin_version" + implementation "androidx.core:core-ktx:$ktx_version" implementation "com.f2prateek.rx.preferences2:rx-preferences:$rx_preferences_version" implementation "com.jakewharton.timber:timber:$timber_version" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" diff --git a/data/src/main/java/manager/ExternalBlockingManagerImpl.kt b/data/src/main/java/manager/ExternalBlockingManagerImpl.kt new file mode 100644 index 00000000..48c18e1e --- /dev/null +++ b/data/src/main/java/manager/ExternalBlockingManagerImpl.kt @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2017 Moez Bhatti + * + * 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 . + */ +package manager + +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.ServiceConnection +import android.os.Handler +import android.os.IBinder +import android.os.Message +import android.os.Messenger +import androidx.os.bundleOf +import io.reactivex.Single +import io.reactivex.subjects.SingleSubject +import timber.log.Timber +import util.tryOrNull +import javax.inject.Inject + +class ExternalBlockingManagerImpl @Inject constructor(private val context: Context) : ExternalBlockingManager { + + companion object { + const val RATING_UNKNOWN = 0 + const val RATING_POSITIVE = 1 + const val RATING_NEGATIVE = 2 + const val RATING_NEUTRAL = 3 + + const val GET_NUMBER_RATING = 1 + } + + /** + * Return a Single which emits whether or not the given [address] should be blocked + */ + override fun shouldBlock(address: String): Single { + return Binder(context, address).shouldBlock() + } + + private class Binder(private val context: Context, private val address: String) : ServiceConnection { + + private val subject: SingleSubject = SingleSubject.create() + private var serviceMessenger: Messenger? = null + private var isBound: Boolean = false + + fun shouldBlock(): Single { + // If either version of Should I Answer? is installed, build the intent to request a rating + val intent = tryOrNull { + context.packageManager.getApplicationInfo("org.mistergroup.shouldianswerpersonal", 0).enabled + Intent("org.mistergroup.shouldianswerpersonal.PublicService").setPackage("org.mistergroup.shouldianswerpersonal") + } ?: tryOrNull { + context.packageManager.getApplicationInfo("org.mistergroup.muzutozvednout", 0).enabled + Intent("org.mistergroup.muzutozvednout.PublicService").setPackage("org.mistergroup.muzutozvednout") + } + + // If the intent isn't null, bind the service and wait for a result. Otherwise, don't block + if (intent != null) { + context.bindService(intent, this, Context.BIND_AUTO_CREATE) + } else { + subject.onSuccess(false) + } + + return subject + } + + override fun onServiceConnected(className: ComponentName, service: IBinder) { + serviceMessenger = Messenger(service) + isBound = true + + val msg = Message() + msg.what = GET_NUMBER_RATING + msg.data = bundleOf(Pair("number", address)) + msg.replyTo = Messenger(IncomingHandler { rating -> + subject.onSuccess(rating == RATING_NEGATIVE) + Timber.v("Should block: ${rating == RATING_NEGATIVE}") + + // We're done, so unbind the service + if (isBound && serviceMessenger != null) { + context.unbindService(this) + } + }) + + serviceMessenger?.send(msg) + } + + override fun onServiceDisconnected(className: ComponentName) { + serviceMessenger = null + isBound = false + } + } + + private class IncomingHandler(private val callback: (rating: Int) -> Unit) : Handler() { + + override fun handleMessage(msg: Message) { + when (msg.what) { + GET_NUMBER_RATING -> callback(msg.data.getInt("rating")) + else -> super.handleMessage(msg) + } + } + } + +} diff --git a/data/src/main/java/repository/MessageRepositoryImpl.kt b/data/src/main/java/repository/MessageRepositoryImpl.kt index 2a2f365c..a57737cb 100644 --- a/data/src/main/java/repository/MessageRepositoryImpl.kt +++ b/data/src/main/java/repository/MessageRepositoryImpl.kt @@ -38,6 +38,7 @@ import io.reactivex.schedulers.Schedulers import io.realm.Realm import io.realm.RealmResults import io.realm.Sort +import manager.ExternalBlockingManager import manager.KeyManager import mapper.CursorToConversation import mapper.CursorToRecipient @@ -61,6 +62,7 @@ class MessageRepositoryImpl @Inject constructor( private val messageIds: KeyManager, private val cursorToConversation: CursorToConversation, private val cursorToRecipient: CursorToRecipient, + private val externalBlockingManager: ExternalBlockingManager, private val prefs: Preferences) : MessageRepository { override fun getConversations(archived: Boolean): Flowable> { @@ -624,6 +626,11 @@ class MessageRepositoryImpl @Inject constructor( } } + // If any of the addresses here should be blocked, then block the conversation + conversation.blocked = recipients.map { it.address }.any { address -> + externalBlockingManager.shouldBlock(address).blockingGet() + } + conversation.recipients.clear() conversation.recipients.addAll(recipients) conversation.insertOrUpdate() diff --git a/data/src/main/java/util/Utils.kt b/data/src/main/java/util/Utils.kt new file mode 100644 index 00000000..9b624930 --- /dev/null +++ b/data/src/main/java/util/Utils.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2017 Moez Bhatti + * + * 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 . + */ +package util + +fun tryOrNull(body: () -> T?): T? { + return try { + body() + } catch (e: Exception) { + null + } +} \ No newline at end of file diff --git a/domain/src/main/java/manager/ExternalBlockingManager.kt b/domain/src/main/java/manager/ExternalBlockingManager.kt new file mode 100644 index 00000000..386e169b --- /dev/null +++ b/domain/src/main/java/manager/ExternalBlockingManager.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Moez Bhatti + * + * 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 . + */ +package manager + +import io.reactivex.Single + +interface ExternalBlockingManager { + + /** + * Return a Single which emits whether or not the given [address] should be blocked + */ + fun shouldBlock(address: String): Single + +} \ No newline at end of file diff --git a/presentation/src/main/java/injection/AppModule.kt b/presentation/src/main/java/injection/AppModule.kt index 8ba91fa7..807a18e1 100644 --- a/presentation/src/main/java/injection/AppModule.kt +++ b/presentation/src/main/java/injection/AppModule.kt @@ -28,6 +28,8 @@ import common.util.NotificationManagerImpl import dagger.Module import dagger.Provides import manager.AnalyticsManager +import manager.ExternalBlockingManager +import manager.ExternalBlockingManagerImpl import manager.KeyManager import manager.KeyManagerImpl import manager.NotificationManager @@ -79,6 +81,9 @@ class AppModule(private var application: Application) { @Provides fun provideAnalyticsManager(manager: AnalyticsManagerImpl): AnalyticsManager = manager + @Provides + fun externalBlockingManager(manager: ExternalBlockingManagerImpl): ExternalBlockingManager = manager + @Provides fun provideKeyManager(manager: KeyManagerImpl): KeyManager = manager