diff --git a/build.gradle b/build.gradle index d3ef5086..c54ce5c2 100644 --- a/build.gradle +++ b/build.gradle @@ -10,6 +10,7 @@ buildscript { ext.androidx_viewpager_version = '1.0.0-alpha01' ext.autodispose_version = '1.3.0' ext.conductor_version = '2.1.5' + ext.coroutines_version = '1.2.2' ext.dagger_version = "2.16" ext.espresso_version = '3.1.0-alpha3' ext.exoplayer_version = "2.8.1" diff --git a/data/src/main/java/com/moez/QKSMS/blocking/BlockingManager.kt b/data/src/main/java/com/moez/QKSMS/blocking/BlockingManager.kt index e134eaef..676a7d20 100644 --- a/data/src/main/java/com/moez/QKSMS/blocking/BlockingManager.kt +++ b/data/src/main/java/com/moez/QKSMS/blocking/BlockingManager.kt @@ -34,14 +34,12 @@ class BlockingManager @Inject constructor( override fun isAvailable(): Boolean = client.isAvailable() + override fun getClientCapability(): BlockingClientCapability = client.getClientCapability() + override fun isBlocked(address: String): Single = client.isBlocked(address) - override fun canBlock(): Boolean = client.canBlock() - override fun block(addresses: List): Completable = client.block(addresses) - override fun canUnblock(): Boolean = client.canUnblock() - override fun unblock(addresses: List): Completable = client.unblock(addresses) override fun openSettings() = client.openSettings() diff --git a/data/src/main/java/com/moez/QKSMS/blocking/CallControlBlockingClient.kt b/data/src/main/java/com/moez/QKSMS/blocking/CallControlBlockingClient.kt index fd845b25..debe6276 100644 --- a/data/src/main/java/com/moez/QKSMS/blocking/CallControlBlockingClient.kt +++ b/data/src/main/java/com/moez/QKSMS/blocking/CallControlBlockingClient.kt @@ -46,6 +46,8 @@ class CallControlBlockingClient @Inject constructor( override fun isAvailable(): Boolean = context.isInstalled("com.flexaspect.android.everycallcontrol") + override fun getClientCapability() = BlockingClientCapability.BLOCK_WITH_PERMISSION + override fun isBlocked(address: String): Single { val uri = Uri.withAppendedPath(CallControl.LOOKUP_TEXT_URI, address) return Single.fromCallable { diff --git a/data/src/main/java/com/moez/QKSMS/blocking/QksmsBlockingClient.kt b/data/src/main/java/com/moez/QKSMS/blocking/QksmsBlockingClient.kt index d3abb272..da35b38c 100644 --- a/data/src/main/java/com/moez/QKSMS/blocking/QksmsBlockingClient.kt +++ b/data/src/main/java/com/moez/QKSMS/blocking/QksmsBlockingClient.kt @@ -29,18 +29,16 @@ class QksmsBlockingClient @Inject constructor( override fun isAvailable(): Boolean = true + override fun getClientCapability() = BlockingClientCapability.BLOCK_WITHOUT_PERMISSION + override fun isBlocked(address: String): Single = Single.fromCallable { blockingRepo.isBlocked(address) } - override fun canBlock(): Boolean = true - override fun block(addresses: List): Completable = Completable.fromCallable { blockingRepo.blockNumber(*addresses.toTypedArray()) } - override fun canUnblock(): Boolean = true - override fun unblock(addresses: List): Completable = Completable.fromCallable { blockingRepo.unblockNumbers(*addresses.toTypedArray()) } diff --git a/data/src/main/java/com/moez/QKSMS/blocking/ShouldIAnswerBlockingClient.kt b/data/src/main/java/com/moez/QKSMS/blocking/ShouldIAnswerBlockingClient.kt index 29dbafe4..ba605495 100644 --- a/data/src/main/java/com/moez/QKSMS/blocking/ShouldIAnswerBlockingClient.kt +++ b/data/src/main/java/com/moez/QKSMS/blocking/ShouldIAnswerBlockingClient.kt @@ -53,6 +53,8 @@ class ShouldIAnswerBlockingClient @Inject constructor( "org.mistergroup.muzutozvednout") .any(context::isInstalled) + override fun getClientCapability() = BlockingClientCapability.CANT_BLOCK + /** * Return a Single which emits whether or not the given [address] should be blocked */ diff --git a/domain/src/main/java/com/moez/QKSMS/blocking/BlockingClient.kt b/domain/src/main/java/com/moez/QKSMS/blocking/BlockingClient.kt index 47d203fd..363ee9a0 100644 --- a/domain/src/main/java/com/moez/QKSMS/blocking/BlockingClient.kt +++ b/domain/src/main/java/com/moez/QKSMS/blocking/BlockingClient.kt @@ -28,28 +28,21 @@ interface BlockingClient { */ fun isAvailable(): Boolean + /** + * Returns the level of access that the given blocking client provides to QKSMS + */ + fun getClientCapability(): BlockingClientCapability + /** * Return a Single which emits whether or not the given [address] should be blocked */ fun isBlocked(address: String): Single - /** - * Returns true if the client is capable of blocking the address in the manager without any - * further user input - */ - fun canBlock(): Boolean = false - /** * Blocks the numbers or opens the manager */ fun block(addresses: List): Completable - /** - * Returns true if the client is capable of unblocking the address in the manager without any - * further user input - */ - fun canUnblock(): Boolean = false - /** * Unblocks the numbers or opens the manager */ @@ -60,4 +53,4 @@ interface BlockingClient { */ fun openSettings() -} \ No newline at end of file +} diff --git a/domain/src/main/java/com/moez/QKSMS/blocking/BlockingClientCapability.kt b/domain/src/main/java/com/moez/QKSMS/blocking/BlockingClientCapability.kt new file mode 100644 index 00000000..7a971faf --- /dev/null +++ b/domain/src/main/java/com/moez/QKSMS/blocking/BlockingClientCapability.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2019 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 com.moez.QKSMS.blocking + +enum class BlockingClientCapability { + BLOCK_WITHOUT_PERMISSION, + BLOCK_WITH_PERMISSION , + CANT_BLOCK +} diff --git a/presentation/build.gradle b/presentation/build.gradle index 9d36e7ca..c05cda31 100644 --- a/presentation/build.gradle +++ b/presentation/build.gradle @@ -169,6 +169,8 @@ dependencies { implementation "com.squareup.moshi:moshi-kotlin:$moshi_version" implementation "me.leolin:ShortcutBadger:1.1.21" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" implementation project(":android-smsmms") implementation project(":common") implementation project(':data') diff --git a/presentation/src/main/java/com/moez/QKSMS/common/base/QkRealmAdapter.kt b/presentation/src/main/java/com/moez/QKSMS/common/base/QkRealmAdapter.kt index 6934ead8..7b1d8997 100644 --- a/presentation/src/main/java/com/moez/QKSMS/common/base/QkRealmAdapter.kt +++ b/presentation/src/main/java/com/moez/QKSMS/common/base/QkRealmAdapter.kt @@ -50,7 +50,7 @@ abstract class QkRealmAdapter : RealmRecyclerViewAdapter> = BehaviorSubject.create() - private val selection = mutableListOf() + private var selection = listOf() /** * Toggles the selected state for a particular view @@ -61,9 +61,9 @@ abstract class QkRealmAdapter : RealmRecyclerViewAdapter selection.remove(id) - false -> selection.add(id) + selection = when (selection.contains(id)) { + true -> selection - id + false -> selection + id } selectionChanges.onNext(selection) @@ -75,7 +75,7 @@ abstract class QkRealmAdapter : RealmRecyclerViewAdapter, block: Boolean) { + fun show(activity: Activity, conversationIds: List, block: Boolean) = GlobalScope.launch { val addresses = conversationIds.toLongArray() - .let(conversationRepo::getConversations) + .let { conversationRepo.getConversations(*it) } .flatMap { conversation -> conversation.recipients } .map { it.address } .distinct() - // If we can block/unblock in the external manager, then just fire that off and exit - if (block) { - markBlocked.execute(conversationIds) - if (blockingManager.canBlock()) { - blockingManager.block(addresses).subscribe() - return - } - } else { - markUnblocked.execute(conversationIds) - if (blockingManager.canUnblock()) { - blockingManager.unblock(addresses).subscribe() - return - } + if (addresses.isEmpty()) { + return@launch } + if (blockingManager.getClientCapability() == BlockingClientCapability.BLOCK_WITHOUT_PERMISSION) { + // If we can block/unblock in the external manager, then just fire that off and exit + if (block) { + markBlocked.execute(conversationIds) + blockingManager.block(addresses).subscribe() + } else { + markUnblocked.execute(conversationIds) + blockingManager.unblock(addresses).subscribe() + } + } else if (addresses.all { address -> block == blockingManager.isBlocked(address).blockingGet() }) { + // If all of the addresses are already in their correct state in the blocking manager, just marked the + // conversations blocked and exit + when (block) { + true -> markBlocked.execute(conversationIds) + false -> markUnblocked.execute(conversationIds) + } + } else { + // Otherwise, show the UI that lets the users know they need to mark the number as blocked in the client + showDialog(activity, conversationIds, addresses, block) + } + } + + private suspend fun showDialog( + activity: Activity, + conversationIds: List, + addresses: List, + block: Boolean + ) = withContext(MainScope().coroutineContext) { val res = when (block) { true -> R.plurals.blocking_block_external false -> R.plurals.blocking_unblock_external @@ -77,13 +99,19 @@ class BlockingDialog @Inject constructor( // Otherwise, show a dialog asking the user if they want to be directed to the external // blocking manager AlertDialog.Builder(activity) - .setTitle(R.string.main_menu_block) + .setTitle(when (block) { + true -> R.string.blocking_block_title + false -> R.string.blocking_unblock_title + }) .setMessage(message) - .setPositiveButton(R.string.button_yes) { _, _ -> - when (block) { - true -> blockingManager.block(addresses) - false -> blockingManager.unblock(addresses) - }.subscribe() + .setPositiveButton(R.string.button_continue) { _, _ -> + if (block) { + markBlocked.execute(conversationIds) + blockingManager.block(addresses).subscribe() + } else { + markUnblocked.execute(conversationIds) + blockingManager.unblock(addresses).subscribe() + } } .setNegativeButton(R.string.button_cancel) { _, _ -> } .create() diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index 42ee26a2..9bc48258 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -274,13 +274,15 @@ Phone number Block + Block + Unblock - Continue to %s to block this number - Continue to %s to block these numbers + Continue to %s and block this number + Continue to %s and block these numbers - Continue to %s to unblock this number - Continue to %s to unblock these numbers + Continue to %s and allow this number + Continue to %s and allow these numbers About @@ -346,6 +348,7 @@ Yes + Continue Cancel Delete Save