Compare commits

...
Author SHA1 Message Date
darken 65f9e1d9db Release: 2.17.6-rc0 2025-07-03 00:19:40 +02:00
darken c97c8f4852 Refactor: Introduce EdgeToEdgeHelper for consistent inset handling
This commit introduces an `EdgeToEdgeHelper` class to centralize and simplify the application of window insets for edge-to-edge display.

Key changes:
- Created `EdgeToEdgeHelper` to manage padding based on system bar insets.
- Migrated various Fragments (`OverviewFragment`, `OnboardingFragment`, `SettingsFragment`, `TroubleShooterFragment`) to use `EdgeToEdgeHelper` for consistent padding.
- Removed manual inset handling from `Activity2` and `Fragment2` as `EdgeToEdgeHelper` now manages this.
- Enabled edge-to-edge display in `MainActivity`.
- Minor updates to `BillingClientConnection` for product details fetching and `build.gradle.kts` for configuration.
2025-07-03 00:14:58 +02:00
darken 6b2073eff2 Update AGP, SDK, and BillingClient
This commit updates:
- Android Gradle Plugin to 8.11.0
- Compile/Target SDK to 36
- Google Play Billing Library to 8.0.0

The `BillingClient` is updated to use the new `PendingPurchasesParams` and `QueryPurchasesParams` APIs.
2025-07-03 00:14:58 +02:00
19 changed files with 100 additions and 42 deletions
+1 -1
View File
@@ -1 +1 @@
2.17.5-rc0 21705000
2.17.6-rc0 21706000
@@ -3,7 +3,6 @@ package eu.darken.capod.common.uix
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
import androidx.lifecycle.LiveData
import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE
import eu.darken.capod.common.debug.logging.log
@@ -16,8 +15,6 @@ abstract class Activity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
log(tag, VERBOSE) { "onCreate(savedInstanceState=$savedInstanceState)" }
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows( window, false )
}
override fun onResume() {
@@ -7,8 +7,6 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.LayoutRes
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.Fragment
import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE
import eu.darken.capod.common.debug.logging.log
@@ -42,17 +40,6 @@ abstract class Fragment2(@LayoutRes val layoutRes: Int?) : Fragment(layoutRes ?:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
log(tag, VERBOSE) { "onViewCreated(view=$view, savedInstanceState=$savedInstanceState)" }
super.onViewCreated(view, savedInstanceState)
ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->
val systemWindowInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(
v.paddingLeft,
systemWindowInsets.top,
v.paddingRight,
0
)
insets
}
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
+6 -6
View File
@@ -10,7 +10,7 @@ apply(plugin = "androidx.navigation.safeargs.kotlin")
android {
compileSdk = ProjectConfig.compileSdk
namespace = "${ProjectConfig.packageName}"
namespace = ProjectConfig.packageName
defaultConfig {
applicationId = ProjectConfig.packageName
@@ -122,9 +122,9 @@ android {
unitTests {
isIncludeAndroidResources = true
}
tasks.withType<Test> {
useJUnitPlatform()
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
}
@@ -151,6 +151,6 @@ dependencies {
addTesting()
"gplayImplementation"("com.android.billingclient:billing:7.1.1")
"gplayImplementation"("com.android.billingclient:billing-ktx:7.1.1")
"gplayImplementation"("com.android.billingclient:billing:8.0.0")
"gplayImplementation"("com.android.billingclient:billing-ktx:8.0.0")
}
@@ -8,6 +8,7 @@ import com.android.billingclient.api.BillingResult
import com.android.billingclient.api.ProductDetails
import com.android.billingclient.api.Purchase
import com.android.billingclient.api.QueryProductDetailsParams
import com.android.billingclient.api.QueryPurchasesParams
import eu.darken.capod.common.debug.logging.Logging.Priority.INFO
import eu.darken.capod.common.debug.logging.Logging.Priority.WARN
import eu.darken.capod.common.debug.logging.log
@@ -39,8 +40,12 @@ data class BillingClientConnection(
.setupCommonEventHandlers(TAG) { "purchases" }
suspend fun queryPurchases(): Collection<Purchase> {
val params = QueryPurchasesParams.newBuilder()
.setProductType(BillingClient.ProductType.INAPP)
.build()
val (result: BillingResult, purchases) = suspendCoroutine<Pair<BillingResult, Collection<Purchase>?>> { continuation ->
client.queryPurchasesAsync(BillingClient.SkuType.INAPP) { result, purchases ->
client.queryPurchasesAsync(params) { result, purchases ->
continuation.resume(result to purchases)
}
}
@@ -80,9 +85,10 @@ data class BillingClientConnection(
val params = QueryProductDetailsParams.newBuilder().setProductList(listOf(productDetails)).build()
val (result, details) = suspendCoroutine<Pair<BillingResult, Collection<ProductDetails>?>> { continuation ->
client.queryProductDetailsAsync(params) { result, skuDetails ->
continuation.resume(result to skuDetails)
val (result, details) = suspendCoroutine<Pair<BillingResult, List<ProductDetails>>> { continuation ->
client.queryProductDetailsAsync(params) { billingResult, queryResult ->
val productDetailsList = queryResult.productDetailsList ?: emptyList()
continuation.resume(billingResult to productDetailsList)
}
}
@@ -92,7 +98,7 @@ data class BillingClientConnection(
if (!result.isSuccess) throw BillingResultException(result)
if (details.isNullOrEmpty()) throw IllegalStateException("Unknown SKU, no details available.")
if (details.isEmpty()) throw IllegalStateException("Unknown SKU, no details available.")
return Sku.Details(sku, details)
}
@@ -5,6 +5,7 @@ import com.android.billingclient.api.BillingClient.BillingResponseCode
import com.android.billingclient.api.BillingClient.newBuilder
import com.android.billingclient.api.BillingClientStateListener
import com.android.billingclient.api.BillingResult
import com.android.billingclient.api.PendingPurchasesParams
import com.android.billingclient.api.Purchase
import dagger.hilt.android.qualifiers.ApplicationContext
import eu.darken.capod.common.debug.logging.Logging.Priority.*
@@ -33,7 +34,11 @@ class BillingClientConnectionProvider @Inject constructor(
val purchasePublisher = MutableStateFlow<Collection<Purchase>>(emptySet())
val client = newBuilder(context).apply {
enablePendingPurchases()
enablePendingPurchases(
PendingPurchasesParams.newBuilder()
.enableOneTimeProducts()
.build()
)
setListener { result, purchases ->
if (result.isSuccess) {
log(TAG) {
@@ -0,0 +1,33 @@
package eu.darken.capod.common
import android.app.Activity
import android.view.View
import androidx.core.graphics.Insets
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import eu.darken.capod.common.debug.logging.logTag
class EdgeToEdgeHelper(activity: Activity) {
private val tag = logTag("EdgeToEdge", "$activity")
fun insetsPadding(
view: View,
left: Boolean = false,
top: Boolean = false,
right: Boolean = false,
bottom: Boolean = false,
) {
ViewCompat.setOnApplyWindowInsetsListener(view) { v: View, insets: WindowInsetsCompat ->
val systemBars: Insets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(
if (left) systemBars.left else v.paddingLeft,
if (top) systemBars.top else v.paddingTop,
if (right) systemBars.right else v.paddingRight,
if (bottom) systemBars.bottom else v.paddingBottom,
)
insets
}
}
}
@@ -1,6 +1,7 @@
package eu.darken.capod.main.ui
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import dagger.hilt.android.AndroidEntryPoint
@@ -17,9 +18,9 @@ class MainActivity : Activity2() {
private val navController by lazy { supportFragmentManager.findNavController(R.id.nav_host) }
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
super.onCreate(savedInstanceState)
installSplashScreen()
enableEdgeToEdge()
ui = MainActivityBinding.inflate(layoutInflater)
setContentView(ui.root)
@@ -5,6 +5,7 @@ import android.view.View
import androidx.fragment.app.viewModels
import dagger.hilt.android.AndroidEntryPoint
import eu.darken.capod.R
import eu.darken.capod.common.EdgeToEdgeHelper
import eu.darken.capod.common.PrivacyPolicy
import eu.darken.capod.common.WebpageTool
import eu.darken.capod.common.uix.Fragment3
@@ -22,6 +23,9 @@ class OnboardingFragment : Fragment3(R.layout.onboarding_fragment) {
@Inject lateinit var webpageTool: WebpageTool
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
EdgeToEdgeHelper(requireActivity()).apply {
insetsPadding(ui.root, left = true, right = true, top = true, bottom = true)
}
ui.goPrivacyPolicy.setOnClickListener { webpageTool.open(PrivacyPolicy.URL) }
ui.continueAction.setOnClickListener { vm.finishOnboarding() }
super.onViewCreated(view, savedInstanceState)
@@ -8,12 +8,11 @@ import android.text.SpannableStringBuilder
import android.view.View
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.viewModels
import dagger.hilt.android.AndroidEntryPoint
import eu.darken.capod.BuildConfig
import eu.darken.capod.R
import eu.darken.capod.common.EdgeToEdgeHelper
import eu.darken.capod.common.colorString
import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.lists.differ.update
@@ -49,6 +48,11 @@ class OverviewFragment : Fragment3(R.layout.main_fragment) {
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
EdgeToEdgeHelper(requireActivity()).apply {
insetsPadding(ui.root, left = true, right = true)
insetsPadding(ui.toolbar, top = true)
insetsPadding(ui.list, bottom = false)
}
ui.apply {
list.setupDefaults(adapter, dividers = false)
}
@@ -60,14 +64,17 @@ class OverviewFragment : Fragment3(R.layout.main_fragment) {
vm.goToSettings()
true
}
R.id.menu_item_donate -> {
vm.onUpgrade()
true
}
R.id.menu_item_upgrade -> {
vm.onUpgrade()
true
}
else -> false
}
}
@@ -93,6 +100,7 @@ class OverviewFragment : Fragment3(R.layout.main_fragment) {
)
)
}
Permission.SYSTEM_ALERT_WINDOW -> {
awaitingPermission = true
startActivity(
@@ -102,6 +110,7 @@ class OverviewFragment : Fragment3(R.layout.main_fragment) {
)
)
}
else -> {
permissionLauncher.launch(it.permissionId)
}
@@ -123,6 +132,7 @@ class OverviewFragment : Fragment3(R.layout.main_fragment) {
getString(eu.darken.capod.common.R.string.app_name)
}
}
UpgradeRepo.Type.FOSS -> {
if (info.isPro) {
getString(eu.darken.capod.common.R.string.app_name_foss)
@@ -2,10 +2,7 @@ package eu.darken.capod.main.ui.settings
import android.os.Bundle
import android.os.Parcelable
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ListView
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.viewModels
import androidx.preference.Preference
@@ -13,6 +10,7 @@ import androidx.preference.PreferenceFragmentCompat
import dagger.hilt.android.AndroidEntryPoint
import eu.darken.capod.R
import eu.darken.capod.common.BuildConfigWrap
import eu.darken.capod.common.EdgeToEdgeHelper
import eu.darken.capod.common.uix.Fragment2
import eu.darken.capod.common.viewbinding.viewBinding
import eu.darken.capod.databinding.SettingsFragmentBinding
@@ -38,6 +36,11 @@ class SettingsFragment : Fragment2(R.layout.settings_fragment),
) : Parcelable
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
EdgeToEdgeHelper(requireActivity()).apply {
insetsPadding(ui.root, left = true, right = true)
insetsPadding(ui.toolbar, top = true)
insetsPadding(ui.contentFrame, bottom = true)
}
childFragmentManager.addOnBackStackChangedListener {
val backStackCnt = childFragmentManager.backStackEntryCount
val newScreenInfo = when {
@@ -8,6 +8,7 @@ import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.setupWithNavController
import dagger.hilt.android.AndroidEntryPoint
import eu.darken.capod.R
import eu.darken.capod.common.EdgeToEdgeHelper
import eu.darken.capod.common.WebpageTool
import eu.darken.capod.common.navigation.popBackStack
import eu.darken.capod.common.uix.Fragment3
@@ -26,6 +27,11 @@ class TroubleShooterFragment : Fragment3(R.layout.troubleshooter_fragment) {
@Inject lateinit var webpageTool: WebpageTool
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
EdgeToEdgeHelper(requireActivity()).apply {
insetsPadding(ui.root, left = true, right = true)
insetsPadding(ui.toolbar, top = true)
insetsPadding(ui.scrollView, bottom = true)
}
ui.toolbar.apply {
setupWithNavController(findNavController())
}
@@ -3,7 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="32dp">
android:layout_margin="32dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
@@ -18,6 +18,7 @@
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="@+id/scrollView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
+1 -1
View File
@@ -8,7 +8,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath("com.android.tools.build:gradle:8.10.1")
classpath("com.android.tools.build:gradle:8.11.0")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.Kotlin.core}")
classpath("com.google.dagger:hilt-android-gradle-plugin:${Versions.Dagger.core}")
classpath("androidx.navigation:navigation-safe-args-gradle-plugin:${Versions.AndroidX.Navigation.core}")
+1 -1
View File
@@ -8,7 +8,7 @@ repositories {
mavenCentral()
}
dependencies {
implementation("com.android.tools.build:gradle:8.10.1")
implementation("com.android.tools.build:gradle:8.11.0")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:2.1.21")
implementation("com.squareup:javapoet:1.13.0")
}
+2 -2
View File
@@ -11,8 +11,8 @@ object ProjectConfig {
const val packageName = "eu.darken.capod"
const val minSdk = 26
const val compileSdk = 34
const val targetSdk = 34
const val compileSdk = 36
const val targetSdk = 36
object Version {
val versionProperties = Properties().apply {
@@ -0,0 +1,5 @@
🐛 Bug fixes, 🚀 performance boosts, maybe even ✨ new features.
Changelog: https://capod.darken.eu/changelog
FYI: Its just me here — thanks for understanding if replies take a bit. ¯\_(ツ)_/¯
+1 -1
View File
@@ -1,6 +1,6 @@
### Updated by release.sh ###
project.versioning.major=2
project.versioning.minor=17
project.versioning.patch=5
project.versioning.patch=6
project.versioning.build=0
#############################