Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit 0c3fc6c8 authored by Moez Bhatti's avatar Moez Bhatti Committed by GitHub
Browse files

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
parent 17017313
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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"
+116 −0
Original line number 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 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)
            }
        }
    }

}
+7 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number 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 util

fun <T> tryOrNull(body: () -> T?): T? {
    return try {
        body()
    } catch (e: Exception) {
        null
    }
}
 No newline at end of file
+30 −0
Original line number 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 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>

}
 No newline at end of file
Loading