From 5c77892fac2a2a58c6fc0e5f17d111ba828c0fee Mon Sep 17 00:00:00 2001 From: Eugene Date: Mon, 20 Jul 2026 19:56:24 +0300 Subject: [PATCH] up versions, remove legacy callblocker app --- apps/call-blocker/.gitignore | 2 - apps/call-blocker/build.gradle.kts | 46 ----- .../call-blocker/src/main/AndroidManifest.xml | 50 ------ .../src/main/ic_launcher-playstore.png | Bin 11545 -> 0 bytes .../ru/shadowsparky/callblocker/AppModule.kt | 24 --- .../data/ContentProviderContactReader.kt | 51 ------ .../callblocker/data/LRejectCallDelegate.kt | 28 --- .../data/NonContactsRejectCallDelegate.kt | 39 ---- .../callblocker/data/PRejectCallDelegate.kt | 20 --- .../data/PolicyBasedRejectDelegate.kt | 44 ----- .../data/PreferenceRejectPolicyManager.kt | 27 --- .../callblocker/domain/ContactReader.kt | 11 -- .../callblocker/domain/RejectCallDelegate.kt | 5 - .../callblocker/domain/RejectPolicyManager.kt | 24 --- .../callblocker/presentation/App.kt | 19 -- .../presentation/RejectCallReceiver.kt | 32 ---- .../presentation/RejectCallWorker.kt | 36 ---- .../presentation/ui/MainActivity.kt | 42 ----- .../presentation/ui/PermissionRequest.kt | 147 --------------- .../presentation/ui/PolicyScreen.kt | 159 ---------------- .../res/drawable/ic_launcher_background.xml | 170 ------------------ .../res/drawable/ic_launcher_foreground.xml | 16 -- .../res/drawable/ic_launcher_monochrome.xml | 17 -- .../res/mipmap-anydpi-v26/ic_launcher.xml | 6 - .../mipmap-anydpi-v26/ic_launcher_round.xml | 6 - .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 1557 -> 0 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 3323 -> 0 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 1088 -> 0 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 2148 -> 0 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 2006 -> 0 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 4842 -> 0 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 3109 -> 0 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 7680 -> 0 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 4452 -> 0 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 11293 -> 0 bytes .../src/main/res/values/colors.xml | 10 -- .../res/values/ic_launcher_background.xml | 4 - .../src/main/res/values/strings.xml | 22 --- .../src/main/res/values/themes.xml | 7 - .../src/main/res/xml/backup_rules.xml | 13 -- .../main/res/xml/data_extraction_rules.xml | 19 -- .../shadowsparky/plugin/AndroidApplication.kt | 5 + gradle.properties | 11 +- gradle/libs.versions.toml | 28 +-- gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle.kts | 2 - 46 files changed, 26 insertions(+), 1118 deletions(-) delete mode 100644 apps/call-blocker/.gitignore delete mode 100644 apps/call-blocker/build.gradle.kts delete mode 100644 apps/call-blocker/src/main/AndroidManifest.xml delete mode 100644 apps/call-blocker/src/main/ic_launcher-playstore.png delete mode 100644 apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/AppModule.kt delete mode 100644 apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/data/ContentProviderContactReader.kt delete mode 100644 apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/data/LRejectCallDelegate.kt delete mode 100644 apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/data/NonContactsRejectCallDelegate.kt delete mode 100644 apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/data/PRejectCallDelegate.kt delete mode 100644 apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/data/PolicyBasedRejectDelegate.kt delete mode 100644 apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/data/PreferenceRejectPolicyManager.kt delete mode 100644 apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/domain/ContactReader.kt delete mode 100644 apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/domain/RejectCallDelegate.kt delete mode 100644 apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/domain/RejectPolicyManager.kt delete mode 100644 apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/presentation/App.kt delete mode 100644 apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/presentation/RejectCallReceiver.kt delete mode 100644 apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/presentation/RejectCallWorker.kt delete mode 100644 apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/presentation/ui/MainActivity.kt delete mode 100644 apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/presentation/ui/PermissionRequest.kt delete mode 100644 apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/presentation/ui/PolicyScreen.kt delete mode 100644 apps/call-blocker/src/main/res/drawable/ic_launcher_background.xml delete mode 100644 apps/call-blocker/src/main/res/drawable/ic_launcher_foreground.xml delete mode 100644 apps/call-blocker/src/main/res/drawable/ic_launcher_monochrome.xml delete mode 100644 apps/call-blocker/src/main/res/mipmap-anydpi-v26/ic_launcher.xml delete mode 100644 apps/call-blocker/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml delete mode 100644 apps/call-blocker/src/main/res/mipmap-hdpi/ic_launcher.png delete mode 100644 apps/call-blocker/src/main/res/mipmap-hdpi/ic_launcher_round.png delete mode 100644 apps/call-blocker/src/main/res/mipmap-mdpi/ic_launcher.png delete mode 100644 apps/call-blocker/src/main/res/mipmap-mdpi/ic_launcher_round.png delete mode 100644 apps/call-blocker/src/main/res/mipmap-xhdpi/ic_launcher.png delete mode 100644 apps/call-blocker/src/main/res/mipmap-xhdpi/ic_launcher_round.png delete mode 100644 apps/call-blocker/src/main/res/mipmap-xxhdpi/ic_launcher.png delete mode 100644 apps/call-blocker/src/main/res/mipmap-xxhdpi/ic_launcher_round.png delete mode 100644 apps/call-blocker/src/main/res/mipmap-xxxhdpi/ic_launcher.png delete mode 100644 apps/call-blocker/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png delete mode 100644 apps/call-blocker/src/main/res/values/colors.xml delete mode 100644 apps/call-blocker/src/main/res/values/ic_launcher_background.xml delete mode 100644 apps/call-blocker/src/main/res/values/strings.xml delete mode 100644 apps/call-blocker/src/main/res/values/themes.xml delete mode 100644 apps/call-blocker/src/main/res/xml/backup_rules.xml delete mode 100644 apps/call-blocker/src/main/res/xml/data_extraction_rules.xml diff --git a/apps/call-blocker/.gitignore b/apps/call-blocker/.gitignore deleted file mode 100644 index 348c102..0000000 --- a/apps/call-blocker/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/build -/bin diff --git a/apps/call-blocker/build.gradle.kts b/apps/call-blocker/build.gradle.kts deleted file mode 100644 index 05d0cac..0000000 --- a/apps/call-blocker/build.gradle.kts +++ /dev/null @@ -1,46 +0,0 @@ - -import com.android.build.api.dsl.ApplicationExtension - -plugins { - alias(libs.plugins.convention.android.app) - alias(libs.plugins.convention.compose) - alias(libs.plugins.convention.koin) -} - -extensions.getByType().apply { - namespace = "ru.shadowsparky.callblocker" - defaultConfig { - applicationId = namespace - versionCode = 1 - versionName = "1.0" - } - - buildTypes { - release { - isMinifyEnabled = true - } - } - buildFeatures { - buildConfig = true - } -} - -dependencies { - implementation(libs.androidx.core.ktx) - implementation(libs.androidx.datastore.preferences) - implementation(libs.koin.android) - implementation(libs.koin.androidx.compose) - implementation(libs.activity.compose) - implementation(libs.androidx.work.runtime.ktx) - implementation(libs.androidx.appcompat) - - implementation(project(":libs:base")) - implementation(project(":libs:ui")) - - debugImplementation(libs.ui.tooling) - compileOnly(libs.ui.tooling.preview.android) - compileOnly(project(":libs:android-stub")) - testImplementation(libs.koin.test) - testImplementation(libs.mockk) - testImplementation(kotlin("test-junit")) -} diff --git a/apps/call-blocker/src/main/AndroidManifest.xml b/apps/call-blocker/src/main/AndroidManifest.xml deleted file mode 100644 index da8dff8..0000000 --- a/apps/call-blocker/src/main/AndroidManifest.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/call-blocker/src/main/ic_launcher-playstore.png b/apps/call-blocker/src/main/ic_launcher-playstore.png deleted file mode 100644 index 01f49b3674061db8afdecda03d4f75dbb6d8689a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11545 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 C%8@?+ diff --git a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/AppModule.kt b/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/AppModule.kt deleted file mode 100644 index 6693d7a..0000000 --- a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/AppModule.kt +++ /dev/null @@ -1,24 +0,0 @@ -package ru.shadowsparky.callblocker - -import android.app.Application -import android.os.Build -import org.koin.core.annotation.ComponentScan -import org.koin.core.annotation.Factory -import org.koin.core.annotation.Module -import ru.shadowsparky.callblocker.data.LRejectCallDelegate -import ru.shadowsparky.callblocker.data.PRejectCallDelegate -import ru.shadowsparky.callblocker.domain.RejectCallDelegate - -@Module -@ComponentScan -class AppModule { - - @Factory - fun provideAlwaysRejectCallDelegate(app: Application): RejectCallDelegate { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - PRejectCallDelegate(app) - } else { - LRejectCallDelegate(app) - } - } -} diff --git a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/data/ContentProviderContactReader.kt b/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/data/ContentProviderContactReader.kt deleted file mode 100644 index 736700c..0000000 --- a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/data/ContentProviderContactReader.kt +++ /dev/null @@ -1,51 +0,0 @@ -package ru.shadowsparky.callblocker.data - -import android.content.Context -import android.database.Cursor -import android.provider.ContactsContract -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import org.koin.core.annotation.Factory -import ru.shadowsparky.callblocker.domain.ContactEntry -import ru.shadowsparky.callblocker.domain.ContactReader - -@Factory -class ContentProviderContactReader( - private val context: Context -) : ContactReader { - - override suspend fun read(): Set { - val projection = arrayOf( - ContactsContract.Contacts.DISPLAY_NAME, - ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER - ) - return withContext(Dispatchers.IO) { - context.contentResolver.query( - ContactsContract.Data.CONTENT_URI, - projection, - null, - null, - null - ).use { queryContacts(it ?: return@withContext emptySet()) } - } - } - - private fun queryContacts(cursor: Cursor): Set { - if (!cursor.moveToFirst()) return emptySet() - val result = mutableSetOf() - do { - val name = cursor.getString(ContactsContract.Contacts.DISPLAY_NAME) - val phone = cursor.getString(ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER) - if (phone != null && name != null) { - result.add(ContactEntry(name, phone)) - } - } while (cursor.moveToNext()) - return result - } - - private fun Cursor.getString(column: String): String? { - val idx = getColumnIndex(column) - if (idx < 0) return null - return getString(idx) - } -} diff --git a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/data/LRejectCallDelegate.kt b/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/data/LRejectCallDelegate.kt deleted file mode 100644 index 3b91d02..0000000 --- a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/data/LRejectCallDelegate.kt +++ /dev/null @@ -1,28 +0,0 @@ -package ru.shadowsparky.callblocker.data - -import android.app.Application -import android.content.Context -import android.telephony.TelephonyManager -import android.util.Log -import com.android.internal.telephony.ITelephony -import ru.shadowsparky.callblocker.domain.RejectCallDelegate - -class LRejectCallDelegate(private val app: Application) : RejectCallDelegate { - - override suspend fun reject() { - getTeleService()?.endCall() - } - - private fun getTeleService(): ITelephony? { - val tm = app.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager - try { - val c = Class.forName(tm.javaClass.name) - val m = c.getDeclaredMethod("getITelephony") - m.isAccessible = true - return m.invoke(tm) as ITelephony - } catch (e: Exception) { - Log.e("LRejectCallDelegate", "err", e) - } - return null - } -} diff --git a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/data/NonContactsRejectCallDelegate.kt b/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/data/NonContactsRejectCallDelegate.kt deleted file mode 100644 index b7848a3..0000000 --- a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/data/NonContactsRejectCallDelegate.kt +++ /dev/null @@ -1,39 +0,0 @@ -package ru.shadowsparky.callblocker.data - -import android.util.Log -import ru.shadowsparky.callblocker.BuildConfig -import ru.shadowsparky.callblocker.domain.ContactReader -import ru.shadowsparky.callblocker.domain.RejectCallDelegate - -class NonContactsRejectCallDelegate( - private val contactReader: ContactReader, - private val number: String?, - private val wrapper: RejectCallDelegate -) : RejectCallDelegate { - - override suspend fun reject() { - log("reject called with number $number") - if (number == null) { - log("number is null!!! exiting...") - return - } - val contacts = contactReader.read() - log("contacts found ${contacts.joinToString()}") - if (!contacts.any { it.phoneNumber == number }) { - log("calling number not found in contacts. rejecting") - wrapper.reject() - } else { - log("calling number found in contacts!") - } - } - - private fun log(msg: String) { - if (BuildConfig.DEBUG) { - Log.d(TAG, msg) - } - } - - companion object { - private const val TAG = "NonContactsCallDelegate" - } -} diff --git a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/data/PRejectCallDelegate.kt b/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/data/PRejectCallDelegate.kt deleted file mode 100644 index ab5bc51..0000000 --- a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/data/PRejectCallDelegate.kt +++ /dev/null @@ -1,20 +0,0 @@ -package ru.shadowsparky.callblocker.data - -import android.annotation.SuppressLint -import android.content.Context -import android.os.Build -import android.telecom.TelecomManager -import android.util.Log -import androidx.annotation.RequiresApi -import androidx.core.content.getSystemService -import ru.shadowsparky.callblocker.domain.RejectCallDelegate - -@RequiresApi(Build.VERSION_CODES.P) -class PRejectCallDelegate(private val context: Context) : RejectCallDelegate { - @SuppressLint("MissingPermission") // пофиг - @Suppress("DEPRECATION") // еще больше пофиг, пока работает будем использовать! - override suspend fun reject() { - Log.e("PRejectCallDelegate", "reject called", RuntimeException()) - context.getSystemService()?.endCall() - } -} diff --git a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/data/PolicyBasedRejectDelegate.kt b/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/data/PolicyBasedRejectDelegate.kt deleted file mode 100644 index e070554..0000000 --- a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/data/PolicyBasedRejectDelegate.kt +++ /dev/null @@ -1,44 +0,0 @@ -package ru.shadowsparky.callblocker.data - -import org.koin.core.annotation.Factory -import ru.shadowsparky.callblocker.domain.ContactReader -import ru.shadowsparky.callblocker.domain.RejectCallDelegate -import ru.shadowsparky.callblocker.domain.RejectPolicy -import ru.shadowsparky.callblocker.domain.RejectPolicyManager - -@Factory -class PolicyBasedRejectDelegateFactory( - private val rejectPolicyManager: RejectPolicyManager, - private val contactReader: ContactReader, - private val rejectDelegate: RejectCallDelegate -) { - fun create(callingNumber: String?): PolicyBasedRejectDelegate { - return PolicyBasedRejectDelegate( - rejectPolicyManager, - callingNumber, - contactReader, - rejectDelegate - ) - } -} - -class PolicyBasedRejectDelegate( - private val rejectPolicyManager: RejectPolicyManager, - private val callingNumber: String?, - private val contactReader: ContactReader, - private val rejectDelegate: RejectCallDelegate -) : RejectCallDelegate { - override suspend fun reject() { - val wrapper = when (rejectPolicyManager.get()) { - RejectPolicy.All -> rejectDelegate - RejectPolicy.NonContacts -> NonContactsRejectCallDelegate( - contactReader, - callingNumber, - rejectDelegate - ) - - RejectPolicy.None -> null - } - wrapper?.reject() - } -} diff --git a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/data/PreferenceRejectPolicyManager.kt b/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/data/PreferenceRejectPolicyManager.kt deleted file mode 100644 index bf54cf4..0000000 --- a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/data/PreferenceRejectPolicyManager.kt +++ /dev/null @@ -1,27 +0,0 @@ -package ru.shadowsparky.callblocker.data - -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.map -import org.koin.core.annotation.Factory -import ru.shadowsparky.callblocker.domain.RejectPolicy -import ru.shadowsparky.callblocker.domain.RejectPolicyManager -import ru.shadowsparky.domain.KeyValueStorage - -@Factory -class PreferenceRejectPolicyManager( - private val keyValueStorage: KeyValueStorage -) : RejectPolicyManager { - override val flow = keyValueStorage.getInt(POLICY_KEY).map { - RejectPolicy.findByCode(it ?: RejectPolicy.None.code) - } - - override suspend fun set(newPolicy: RejectPolicy) { - keyValueStorage.putInt(POLICY_KEY, newPolicy.code) - } - - override suspend fun get(): RejectPolicy = flow.first() - - companion object { - private const val POLICY_KEY = "policy" - } -} diff --git a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/domain/ContactReader.kt b/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/domain/ContactReader.kt deleted file mode 100644 index c0374c2..0000000 --- a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/domain/ContactReader.kt +++ /dev/null @@ -1,11 +0,0 @@ -package ru.shadowsparky.callblocker.domain - -interface ContactReader { - - suspend fun read(): Set -} - -data class ContactEntry( - val name: String, - val phoneNumber: String -) diff --git a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/domain/RejectCallDelegate.kt b/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/domain/RejectCallDelegate.kt deleted file mode 100644 index 68d0cc6..0000000 --- a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/domain/RejectCallDelegate.kt +++ /dev/null @@ -1,5 +0,0 @@ -package ru.shadowsparky.callblocker.domain - -interface RejectCallDelegate { - suspend fun reject() -} diff --git a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/domain/RejectPolicyManager.kt b/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/domain/RejectPolicyManager.kt deleted file mode 100644 index cd2d546..0000000 --- a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/domain/RejectPolicyManager.kt +++ /dev/null @@ -1,24 +0,0 @@ -package ru.shadowsparky.callblocker.domain - -import kotlinx.coroutines.flow.Flow - -interface RejectPolicyManager { - val flow: Flow - - suspend fun set(newPolicy: RejectPolicy) - suspend fun get(): RejectPolicy -} - -sealed class RejectPolicy(val code: Int) { - data object None : RejectPolicy(0) - data object All : RejectPolicy(1) - data object NonContacts : RejectPolicy(2) - - companion object { - val values: Set get() = setOf(None, All, NonContacts) - - fun findByCode(code: Int): RejectPolicy { - return values.first { it.code == code } - } - } -} diff --git a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/presentation/App.kt b/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/presentation/App.kt deleted file mode 100644 index 1e14e72..0000000 --- a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/presentation/App.kt +++ /dev/null @@ -1,19 +0,0 @@ -package ru.shadowsparky.callblocker.presentation - -import android.app.Application -import org.koin.android.ext.koin.androidContext -import org.koin.core.annotation.KoinApplication -import org.koin.plugin.module.dsl.startKoin -import ru.shadowsparky.callblocker.AppModule - -@KoinApplication(modules = [AppModule::class]) -object CallBlocker - -class App : Application() { - override fun onCreate() { - super.onCreate() - startKoin { - androidContext(this@App) - } - } -} diff --git a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/presentation/RejectCallReceiver.kt b/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/presentation/RejectCallReceiver.kt deleted file mode 100644 index c0b05fc..0000000 --- a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/presentation/RejectCallReceiver.kt +++ /dev/null @@ -1,32 +0,0 @@ -package ru.shadowsparky.callblocker.presentation - -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.telephony.TelephonyManager -import android.util.Log -import ru.shadowsparky.callblocker.BuildConfig - -class RejectCallReceiver : BroadcastReceiver() { - override fun onReceive(context: Context?, intent: Intent?) { - val keys = intent?.extras?.keySet() ?: return - val values = keys.map { it to intent.extras?.get(it).toString() } - - if (BuildConfig.DEBUG) { - Log.d("RejectCallReceiver", "start") - values.forEach { - Log.d("RejectCallReceiver", "key ${it.first}; value: ${it.second}") - } - Log.d("RejectCallReceiver", "end") - } - - if (intent.action == TelephonyManager.ACTION_PHONE_STATE_CHANGED && context != null) { - val state = intent.getStringExtra(TelephonyManager.EXTRA_STATE) - if (state == TelephonyManager.EXTRA_STATE_RINGING) { - @Suppress("DEPRECATION") - val number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER) - RejectCallWorker.start(context, number) - } - } - } -} diff --git a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/presentation/RejectCallWorker.kt b/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/presentation/RejectCallWorker.kt deleted file mode 100644 index c9063f7..0000000 --- a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/presentation/RejectCallWorker.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ru.shadowsparky.callblocker.presentation - -import android.content.Context -import androidx.work.CoroutineWorker -import androidx.work.OneTimeWorkRequestBuilder -import androidx.work.WorkManager -import androidx.work.WorkerParameters -import androidx.work.workDataOf -import ru.shadowsparky.callblocker.data.PolicyBasedRejectDelegateFactory -import ru.shadowsparky.koin - -class RejectCallWorker( - appContext: Context, - params: WorkerParameters -) : CoroutineWorker(appContext, params) { - private val delegateFactory by koin.inject() - - override suspend fun doWork(): Result { - val callingNumber = inputData.getString(NUMBER_KEY) - delegateFactory.create(callingNumber).reject() - return Result.success() - } - - companion object { - private const val NUMBER_KEY = "number" - - fun start(context: Context?, number: String?) { - context ?: return - val wm = WorkManager.getInstance(context) - val request = OneTimeWorkRequestBuilder() - .setInputData(workDataOf(NUMBER_KEY to number)) - .build() - wm.enqueue(request) - } - } -} diff --git a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/presentation/ui/MainActivity.kt b/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/presentation/ui/MainActivity.kt deleted file mode 100644 index a3100a8..0000000 --- a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/presentation/ui/MainActivity.kt +++ /dev/null @@ -1,42 +0,0 @@ -package ru.shadowsparky.callblocker.presentation.ui - -import android.os.Bundle -import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent -import androidx.activity.enableEdgeToEdge -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import ru.shadowsparky.callblocker.R -import ru.shadowsparky.callblocker.domain.RejectPolicyManager -import ru.shadowsparky.koin -import ru.shadowsparky.ui.AppScaffold - -class MainActivity : ComponentActivity() { - private val rejectPolicyManager by koin.inject() - - @OptIn(ExperimentalMaterial3Api::class) - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - enableEdgeToEdge() - val permissionRequest = newPermissionRequest() - setContent { - AppScaffold( - appBar = { - TopAppBar(title = { Text(stringResource(R.string.app_name)) }) - } - ) { - CompositionLocalProvider(LocalPermissionRequest provides permissionRequest) { - Box(modifier = Modifier.padding(it)) { - PolicyContent(rejectPolicyManager) - } - } - } - } - } -} diff --git a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/presentation/ui/PermissionRequest.kt b/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/presentation/ui/PermissionRequest.kt deleted file mode 100644 index 0ce66db..0000000 --- a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/presentation/ui/PermissionRequest.kt +++ /dev/null @@ -1,147 +0,0 @@ -package ru.shadowsparky.callblocker.presentation.ui - -import android.Manifest -import android.content.pm.PackageManager -import android.os.Build -import androidx.activity.ComponentActivity -import androidx.activity.result.contract.ActivityResultContracts -import androidx.annotation.RequiresApi -import androidx.compose.runtime.ProvidableCompositionLocal -import androidx.compose.runtime.staticCompositionLocalOf -import androidx.core.content.ContextCompat -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import ru.shadowsparky.callblocker.R - -interface PermissionRequest { - val hasPermission: StateFlow - - fun getTitle(): String - - fun getDescription(): String - - fun requestPermission() -} - -abstract class MutablePermissionRequest(private val hasPermissionCallback: () -> Boolean) : - PermissionRequest { - override val hasPermission = MutableStateFlow(hasPermissionCallback()) - - init { - initState() - } - - override fun requestPermission() { - if (!hasPermission.value) requestPermissionInternal() - } - - private fun initState() { - if (!hasPermission.value) { - this.hasPermission.tryEmit(hasPermissionCallback()) - } - } - - protected abstract fun requestPermissionInternal() -} - -class MultiplePermissionRequest(private val requests: List) : PermissionRequest { - override val hasPermission - get() = getLatestDeclinedRequest()?.hasPermission ?: MutableStateFlow(true) - - override fun getTitle(): String { - return getLatestDeclinedRequest()?.getTitle() ?: "null" - } - - override fun getDescription(): String { - return getLatestDeclinedRequest()?.getDescription() ?: "null" - } - - private fun getLatestDeclinedRequest(): PermissionRequest? { - return requests.firstOrNull { !it.hasPermission.value } - } - - override fun requestPermission() { - getLatestDeclinedRequest()?.requestPermission() - } -} - -val LocalPermissionRequest: ProvidableCompositionLocal = - staticCompositionLocalOf { throw IllegalStateException("request must be initialized here") } - -fun ComponentActivity.newPermissionRequest(): PermissionRequest { - val list = mutableListOf( - ReadContactsPermissionRequest(this), - ReadCallLogPermissionRequest(this), - newAnswerPhonePermissionRequest(), - ReadPhoneStatePermissionRequest(this), - ) - return MultiplePermissionRequest(list) -} - -abstract class AndroidPermissionRequest( - private val activity: ComponentActivity, - private val permission: String, - hasPermissionCallback: () -> Boolean = { - ContextCompat.checkSelfPermission( - activity, - permission - ) == PackageManager.PERMISSION_GRANTED - } -) : MutablePermissionRequest(hasPermissionCallback) { - private val launcher = - activity.registerForActivityResult(ActivityResultContracts.RequestPermission()) { - hasPermission.tryEmit(it) - } - - override fun requestPermissionInternal() { - launcher.launch(permission) - } - - protected fun getString(code: Int, vararg args: Any): String { - return activity.getString(code, args) - } -} - -class ReadContactsPermissionRequest( - activity: ComponentActivity -) : AndroidPermissionRequest(activity, Manifest.permission.READ_CONTACTS) { - override fun getDescription(): String = getString(R.string.read_contacts_desc) - override fun getTitle(): String = getString(R.string.read_contacts) -} - -class ReadCallLogPermissionRequest( - activity: ComponentActivity -) : AndroidPermissionRequest(activity, Manifest.permission.READ_CALL_LOG) { - override fun getDescription(): String = getString(R.string.read_call_log_desc) - override fun getTitle(): String = getString(R.string.read_call_log) -} - -class ReadPhoneStatePermissionRequest( - activity: ComponentActivity -) : AndroidPermissionRequest(activity, Manifest.permission.READ_PHONE_STATE) { - override fun getDescription(): String = getString(R.string.read_phone_state_desc) - override fun getTitle(): String = getString(R.string.read_phone_state) -} - -private fun ComponentActivity.newAnswerPhonePermissionRequest(): PermissionRequest { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - AnswerPhoneCallPermissionRequest(this) - } else { - CallPhonePermissionRequest(this) - } -} - -class CallPhonePermissionRequest( - activity: ComponentActivity -) : AndroidPermissionRequest(activity, Manifest.permission.CALL_PHONE) { - override fun getDescription(): String = getString(R.string.answer_phone_calls_desc) - override fun getTitle(): String = getString(R.string.answer_phone_calls) -} - -@RequiresApi(Build.VERSION_CODES.O) -class AnswerPhoneCallPermissionRequest( - activity: ComponentActivity -) : AndroidPermissionRequest(activity, Manifest.permission.ANSWER_PHONE_CALLS) { - override fun getDescription(): String = getString(R.string.answer_phone_calls_desc) - override fun getTitle(): String = getString(R.string.answer_phone_calls) -} diff --git a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/presentation/ui/PolicyScreen.kt b/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/presentation/ui/PolicyScreen.kt deleted file mode 100644 index dbf3d0d..0000000 --- a/apps/call-blocker/src/main/kotlin/ru/shadowsparky/callblocker/presentation/ui/PolicyScreen.kt +++ /dev/null @@ -1,159 +0,0 @@ -package ru.shadowsparky.callblocker.presentation.ui - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Card -import androidx.compose.material3.CardDefaults -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedButton -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.State -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.launch -import ru.shadowsparky.callblocker.R -import ru.shadowsparky.callblocker.domain.RejectPolicy -import ru.shadowsparky.callblocker.domain.RejectPolicyManager -import ru.shadowsparky.ui.components.ListPreference - -@Composable -fun PolicyContent(policyManager: RejectPolicyManager) { - val state = policyManager.flow.collectAsState(null) - if (state.value != null) { - InitializedPolicyContent(state, policyManager, LocalPermissionRequest.current) - } -} - -@Preview(showBackground = true) -@Composable -fun InitializedPolicyScreenPreview() { - val state = remember { mutableStateOf(RejectPolicy.None) } - InitializedPolicyContent( - state = state, - manager = object : RejectPolicyManager { - override val flow = MutableStateFlow(RejectPolicy.None) - override suspend fun set(newPolicy: RejectPolicy) = flow.emit(newPolicy) - override suspend fun get(): RejectPolicy = flow.value - }, - MultiplePermissionRequest( - listOf( - NoOpPermissionRequest("1"), - NoOpPermissionRequest("2"), - NoOpPermissionRequest("3") - ) - ) - ) -} - -private class NoOpPermissionRequest(private val prefix: String) : PermissionRequest { - override val hasPermission = MutableStateFlow(false) - - override fun getTitle(): String { - return "$prefix noop" - } - - override fun getDescription(): String { - return "$prefix noop" - } - - override fun requestPermission() { - hasPermission.tryEmit(true) - } -} - -@Composable -private fun InitializedPolicyContent( - state: State, - manager: RejectPolicyManager, - permissionRequest: PermissionRequest -) { - val coroutineScope = rememberCoroutineScope() - Column { - val items = RejectPolicy.values.associateWith { it.getTitle() } - val item = remember { mutableStateOf(state.value!!) } - val hasPermission by permissionRequest.hasPermission.collectAsState() - if (!hasPermission) { - MissingPermissionCard( - permissionRequest, - modifier = Modifier.padding(4.dp), - ) - } - ListPreference( - title = stringResource(id = R.string.selected_policy), - items = items, - selectedItem = item.value, - onItemSelected = { - item.value = it - coroutineScope.launch { - manager.set(it) - } - } - ) - } -} - -@Composable -private fun RejectPolicy.getTitle(): String { - val code = when (this) { - RejectPolicy.All -> R.string.reject_all - RejectPolicy.NonContacts -> R.string.reject_non_contacts - RejectPolicy.None -> R.string.reject_none - } - return stringResource(code) -} - -@Preview -@Composable -fun MissingPermissionCardPreview() { - MissingPermissionCard(NoOpPermissionRequest("1")) -} - -@Composable -fun MissingPermissionCard( - request: PermissionRequest, - modifier: Modifier = Modifier -) { - Card( - modifier = modifier.fillMaxWidth(), - colors = CardDefaults.cardColors( - containerColor = MaterialTheme.colorScheme.errorContainer, - contentColor = MaterialTheme.colorScheme.onErrorContainer, - ) - ) { - Column(modifier = Modifier.padding(8.dp)) { - Text( - stringResource(id = R.string.warning), - style = MaterialTheme.typography.titleMedium - ) - Text( - stringResource( - id = R.string.mission_permission_text, - request.getTitle(), - request.getDescription() - ) - ) - Box( - contentAlignment = Alignment.CenterEnd, - modifier = Modifier.fillMaxWidth() - ) { - OutlinedButton( - onClick = { request.requestPermission() }, - ) { - Text(stringResource(id = R.string.grant_permission)) - } - } - } - } -} diff --git a/apps/call-blocker/src/main/res/drawable/ic_launcher_background.xml b/apps/call-blocker/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 07d5da9..0000000 --- a/apps/call-blocker/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/call-blocker/src/main/res/drawable/ic_launcher_foreground.xml b/apps/call-blocker/src/main/res/drawable/ic_launcher_foreground.xml deleted file mode 100644 index cb40282..0000000 --- a/apps/call-blocker/src/main/res/drawable/ic_launcher_foreground.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - diff --git a/apps/call-blocker/src/main/res/drawable/ic_launcher_monochrome.xml b/apps/call-blocker/src/main/res/drawable/ic_launcher_monochrome.xml deleted file mode 100644 index 1305eda..0000000 --- a/apps/call-blocker/src/main/res/drawable/ic_launcher_monochrome.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - diff --git a/apps/call-blocker/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/apps/call-blocker/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index c78bee3..0000000 --- a/apps/call-blocker/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/apps/call-blocker/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/apps/call-blocker/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index c78bee3..0000000 --- a/apps/call-blocker/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/apps/call-blocker/src/main/res/mipmap-hdpi/ic_launcher.png b/apps/call-blocker/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 26260d87857c8b1586704d9d5977f1d4d32168dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1557 zcmV+w2I~2VP)-Y=);Kg|r28BHeH-Apn&AN-iv$vG$U{X8>s&Y8`N z7iy@Xh8k*wb`U}|6s^M02=nwSXhUJbV@in+&W+yy-G4>-h4O~-8|8I~fpf;WBZpT{ z<>*te73o$-8Gn3yB;euUA>?u8GVX9Vbo^W;2jN|xw4n(!cz=I?rzDUQ74#2Hk?jc5 z?@6?<7TfFV>mOJ-!oNbOL-V@3yu2h!OG`iUbCwjY!WyU3`KwX|QSNSUZ^_Ea%J0b4 zA3B3QF(nE@PBb8>6}kE&NNcy-|705zet`mD1X-=tHstEBpk_3ta6#{ot6zc+ z4i4U;F(nIHTwMGMxvGN1sDiwp3i5&~$P4lcqGKqsySppm=;#RTiOS1>e1e*qnn+4Y z3W%z{n@4xVTs(L`Zpgxu{-%kxx)< zZLLU%ki5J+a&~qmsvAJ$6V%bsLBhksMN;_0#>QebPgFO6$R}uSZjKRvrk*1vCWb65 zEQsm`5cvc-91fD1nJJRO5)lzW>g($X)`VT{lkM$ovb?;^fPHqfeRFd|*4Nj$&yJ6e zx$nVfojty`w#Jkdo{IE-r}4Wa7$U zAII?7*47qDPfur#hlPcawzf8|UQbUCR|nG=$P}K32jmrm?w^~R%O#-EXh=mx1-ZJq z5|xFFl9Cb<85zldeZC;QUe6rI6s5hrovYW|+soBSN=gbXL7=s@mHDfqNo?_S6P5uu z(dp@F(%09=fPEapXXtGsBO}c5{{DWlxw*;JgAlgP@bEAvRU+~WIy^iiSy@?JFG@{K zb-VY-jQoN?XJ@C|n5eqC+SjMery@86VYFOSRKy9w{UQh^svtN70gJ^#bUGa;DLFY= z?m;359zi&fmX;Roh7Gc_vzfu2uxtPc9zg*2owBkr=5dd&07j!xk|Yco(c^43+sB;X z2A3d!X+mLPp{OT$y4>*8!?Xl*H(aJz1;A1X){_PX2AFvtmT&!7;|@MSU}tBCnL6+# z;huysj_~P*F%YIDn4Yj@p8^9&ya-_DZxRVkK>(7_llYe#o^BWdu;V0*xG^p9CWi%Y zOYllb_+1_dUO@nt8}3Q`o&?aFFyh9v1amk3XPyYTxtJ7iN#W_n9|8bK zNn)IY!KAlUNKYtM5DOmP-QC^H+)a4dNkQ>~STMoGDgc&BgzHJ3_-q8RU_A+oZdjSb z@-5c5JzaunX=%QWg+f~pA3XXoAjH!RuaxlK4KFA01{ce)kmJXTNaYCf1bU5+J%{D< z5cC14g1n##@`5VJ3#uS5sDixkQ3NUew;EK?JLKw@AiLeJ*dBzOAjoF3`QL-~_xBr> z{)4)inVGl9)h|I)Q&T^&jotgUoEg+07yKj>Q<$!+e9%i!{f?`Z2Q@+(|UBrJ7#|j`C4rOpzVWq%g zu~>~paT zT2BR)6hV7a4BhHzqp_h3a&YA${if_gz_t=lLk%_52#NR)yM3l4>j@;z00000NkvXX Hu0mjf%(>v| diff --git a/apps/call-blocker/src/main/res/mipmap-hdpi/ic_launcher_round.png b/apps/call-blocker/src/main/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index 4e3e8be2f87ea2914aabc6150af816205edead15..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3323 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 FV1gG(H6H)~ diff --git a/apps/call-blocker/src/main/res/mipmap-mdpi/ic_launcher.png b/apps/call-blocker/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 06aeb537ca5eb0d0c769a46fb9b984b9f449c11f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1088 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 SjYET{Cje5%NV`T86aGJxE*}E` diff --git a/apps/call-blocker/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/apps/call-blocker/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index f631722b0bd0859e797364781611267cc6db4034..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11293 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 - - #FFBB86FC - #FF6200EE - #FF3700B3 - #FF03DAC5 - #FF018786 - #FF000000 - #FFFFFFFF - \ No newline at end of file diff --git a/apps/call-blocker/src/main/res/values/ic_launcher_background.xml b/apps/call-blocker/src/main/res/values/ic_launcher_background.xml deleted file mode 100644 index c5d5899..0000000 --- a/apps/call-blocker/src/main/res/values/ic_launcher_background.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - #FFFFFF - \ No newline at end of file diff --git a/apps/call-blocker/src/main/res/values/strings.xml b/apps/call-blocker/src/main/res/values/strings.xml deleted file mode 100644 index 03dd6e0..0000000 --- a/apps/call-blocker/src/main/res/values/strings.xml +++ /dev/null @@ -1,22 +0,0 @@ - - Блокировка звонков - Права не выданы - Выключено - Отменять любые звонки - Отменять звонки не из контактов - Режим работы - чтения контактов - распознавания находится ли номер в списке контактов или нет - Внимание! - Не выданы права для %1$s. Приложению эти права необходимы для %2$s. Пожалуйста, выдайте права, иначе приложение может работать некорректно" - просмотра номера звонящего - просмотра информации о звонке - получения уведомлений о звонках - чтение состояния телефона - сброса входящего звонка - ответа на звонок - Выдать права - Настройки - О приложении - Версия - \ No newline at end of file diff --git a/apps/call-blocker/src/main/res/values/themes.xml b/apps/call-blocker/src/main/res/values/themes.xml deleted file mode 100644 index 1de0704..0000000 --- a/apps/call-blocker/src/main/res/values/themes.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/apps/call-blocker/src/main/res/xml/backup_rules.xml b/apps/call-blocker/src/main/res/xml/backup_rules.xml deleted file mode 100644 index fa0f996..0000000 --- a/apps/call-blocker/src/main/res/xml/backup_rules.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - \ No newline at end of file diff --git a/apps/call-blocker/src/main/res/xml/data_extraction_rules.xml b/apps/call-blocker/src/main/res/xml/data_extraction_rules.xml deleted file mode 100644 index 9ee9997..0000000 --- a/apps/call-blocker/src/main/res/xml/data_extraction_rules.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/ru/shadowsparky/plugin/AndroidApplication.kt b/build-logic/convention/src/main/kotlin/ru/shadowsparky/plugin/AndroidApplication.kt index 377775f..4399a15 100644 --- a/build-logic/convention/src/main/kotlin/ru/shadowsparky/plugin/AndroidApplication.kt +++ b/build-logic/convention/src/main/kotlin/ru/shadowsparky/plugin/AndroidApplication.kt @@ -30,9 +30,14 @@ class AndroidApplication : Plugin { } } } + lint { + checkReleaseBuilds = false + abortOnError = false + } buildTypes { getByName("release") { isMinifyEnabled = true + isShrinkResources = true if (keystorePropertiesFile.exists()) { signingConfig = signingConfigs.getByName("release") } diff --git a/gradle.properties b/gradle.properties index c157c29..de4512b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,13 +1,14 @@ kotlin.code.style=official -xcodeproj=./iosApp android.useAndroidX=true -org.gradle.jvmargs=-Xms512m -Xmx4096m -XX:+HeapDumpOnOutOfMemoryError + +org.gradle.jvmargs=-Xms1g -Xmx3584m -XX:+UseParallelGC -Dfile.encoding=UTF-8 +org.gradle.daemon.idletimeout=60000 + org.jetbrains.compose.experimental.jscanvas.enabled=true org.jetbrains.compose.experimental.uikit.enabled=true android.nonTransitiveRClass=false +android.r8.strictFullModeForKeepRules=true + org.gradle.caching=true org.gradle.parallel=true org.gradle.configuration-cache=true -android.uniquePackageNames=false -android.dependency.useConstraints=false -android.r8.strictFullModeForKeepRules=false diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e991604..392c16e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,21 +2,21 @@ activity-compose = "1.13.0" appcompat = "1.7.1" -coil-compose = "3.4.0" -compose-gradle-plugin = "1.11.1" -compose-version = "1.11.0-alpha03" +coil-compose = "3.5.0" +compose-gradle-plugin = "1.12.0-beta01" +compose-version = "1.12.0-alpha03" desugar_jdk_libs = "2.1.5" haze = "1.7.2" -java-jwt = "4.5.2" -kotlin-version = "2.4.0" -agp = "9.2.1" +java-jwt = "4.6.0" +kotlin-version = "2.4.10" +agp = "9.3.0" coroutines-extensions = "1.5.5" decompose-version = "3.5.0" image-loader = "1.10.0" junit = "4.13.2" lifecycleCoroutines = "2.5.0" -material = "1.11.3" +material = "1.11.4" materialVersion = "1.14.0" media3 = "1.10.1" mockk = "1.14.11" @@ -24,15 +24,15 @@ jsoup = "1.22.2" gradle-plugin = "1.5.5" kotlinx-coroutines-core = "1.11.0" kotlinx-serialization-json = "1.11.0" -ktor-plugin = "3.5.0" +ktor-plugin = "3.5.1" kxml2 = "2.4.1" logback-classic = "1.5.32" multiplatform-settings = "1.3.0" -postgresql = "42.7.11" +postgresql = "42.7.13" runner = "1.7.0" -sqlite = "2.6.2" +sqlite = "2.7.0" sqlite-driver = "2.3.2" -ui-tooling = "1.11.1" +ui-tooling = "1.11.4" essenty = "1.3.0" stately-common = "2.0.6" proj-target = "37" @@ -43,11 +43,11 @@ proj-server-java = "21" datastore-preferences-core = "1.2.1" ksp-version = "2.3.7" koin-core = "4.2.2" -koin-compiler = "1.0.1" +koin-compiler = "1.0.2" kotlin-dsl-version = "5.1.2" -coreKtx = "1.18.0" +coreKtx = "1.19.0" workRuntimeKtx = "2.11.2" -krpc = "0.10.2" +krpc = "0.10.3" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c3dd19e..a374128 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Tue Jun 23 20:20:20 MSK 2026 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle.kts b/settings.gradle.kts index 95a2aa9..21914db 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -34,8 +34,6 @@ include(":apps:vbox:client:shared") include(":apps:vbox:client:web") include(":apps:vbox:backend") -include(":apps:call-blocker") - include(":apps:experimentarium:androidApp") include(":apps:experimentarium:shared") include(":apps:experimentarium:web")