Integrate Should I Answer? blocking support (#977)

* Pulled over SIA code from 2.7.3

* Eliminate unnecessary service, fix service binding

* Flatten logic in isBlocked

* Return single which emits shouldBlock state

* Mark conversation as blocked during sync
This commit is contained in:
Moez Bhatti
2018-04-04 21:23:41 -04:00
committed by GitHub
parent 17017313cc
commit 0c3fc6c8a7
6 changed files with 186 additions and 0 deletions
+1
View File
@@ -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"
@@ -0,0 +1,116 @@
/*
* 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 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<Boolean> which emits whether or not the given [address] should be blocked
*/
override fun shouldBlock(address: String): Single<Boolean> {
return Binder(context, address).shouldBlock()
}
private class Binder(private val context: Context, private val address: String) : ServiceConnection {
private val subject: SingleSubject<Boolean> = SingleSubject.create()
private var serviceMessenger: Messenger? = null
private var isBound: Boolean = false
fun shouldBlock(): Single<Boolean> {
// 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)
}
}
}
}
@@ -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<List<Conversation>> {
@@ -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()
+27
View File
@@ -0,0 +1,27 @@
/*
* 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 util
fun <T> tryOrNull(body: () -> T?): T? {
return try {
body()
} catch (e: Exception) {
null
}
}
@@ -0,0 +1,30 @@
/*
* 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 manager
import io.reactivex.Single
interface ExternalBlockingManager {
/**
* Return a Single<Boolean> which emits whether or not the given [address] should be blocked
*/
fun shouldBlock(address: String): Single<Boolean>
}
@@ -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