From c97c8f4852b117f2919d89a4698e843e3f694ee7 Mon Sep 17 00:00:00 2001 From: darken Date: Wed, 2 Jul 2025 23:13:11 +0200 Subject: [PATCH] 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. --- .../eu/darken/capod/common/uix/Activity2.kt | 3 -- .../eu/darken/capod/common/uix/Fragment2.kt | 13 -------- app/build.gradle.kts | 8 ++--- .../core/client/BillingClientConnection.kt | 9 ++--- .../darken/capod/common/EdgeToEdgeHelper.kt | 33 +++++++++++++++++++ .../eu/darken/capod/main/ui/MainActivity.kt | 5 +-- .../main/ui/onboarding/OnboardingFragment.kt | 4 +++ .../main/ui/overview/OverviewFragment.kt | 14 ++++++-- .../main/ui/settings/SettingsFragment.kt | 9 +++-- .../ui/TroubleShooterFragment.kt | 6 ++++ .../main/res/layout/onboarding_fragment.xml | 2 +- .../res/layout/troubleshooter_fragment.xml | 1 + 12 files changed, 75 insertions(+), 32 deletions(-) create mode 100644 app/src/main/java/eu/darken/capod/common/EdgeToEdgeHelper.kt diff --git a/app-common/src/main/java/eu/darken/capod/common/uix/Activity2.kt b/app-common/src/main/java/eu/darken/capod/common/uix/Activity2.kt index 0f73ed04..3a7b642f 100644 --- a/app-common/src/main/java/eu/darken/capod/common/uix/Activity2.kt +++ b/app-common/src/main/java/eu/darken/capod/common/uix/Activity2.kt @@ -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() { diff --git a/app-common/src/main/java/eu/darken/capod/common/uix/Fragment2.kt b/app-common/src/main/java/eu/darken/capod/common/uix/Fragment2.kt index 8eb977ee..bb4f8f3b 100644 --- a/app-common/src/main/java/eu/darken/capod/common/uix/Fragment2.kt +++ b/app-common/src/main/java/eu/darken/capod/common/uix/Fragment2.kt @@ -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?) { diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 86f0f28b..eb7992a8 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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 { - useJUnitPlatform() - } + } + tasks.withType { + useJUnitPlatform() } } diff --git a/app/src/gplay/java/eu/darken/capod/common/upgrade/core/client/BillingClientConnection.kt b/app/src/gplay/java/eu/darken/capod/common/upgrade/core/client/BillingClientConnection.kt index 6406642c..8321ea57 100644 --- a/app/src/gplay/java/eu/darken/capod/common/upgrade/core/client/BillingClientConnection.kt +++ b/app/src/gplay/java/eu/darken/capod/common/upgrade/core/client/BillingClientConnection.kt @@ -85,9 +85,10 @@ data class BillingClientConnection( val params = QueryProductDetailsParams.newBuilder().setProductList(listOf(productDetails)).build() - val (result, details) = suspendCoroutine?>> { continuation -> - client.queryProductDetailsAsync(params) { result, skuDetails -> - continuation.resume(result to skuDetails) + val (result, details) = suspendCoroutine>> { continuation -> + client.queryProductDetailsAsync(params) { billingResult, queryResult -> + val productDetailsList = queryResult.productDetailsList ?: emptyList() + continuation.resume(billingResult to productDetailsList) } } @@ -97,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) } diff --git a/app/src/main/java/eu/darken/capod/common/EdgeToEdgeHelper.kt b/app/src/main/java/eu/darken/capod/common/EdgeToEdgeHelper.kt new file mode 100644 index 00000000..608fba6d --- /dev/null +++ b/app/src/main/java/eu/darken/capod/common/EdgeToEdgeHelper.kt @@ -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 + } + } +} diff --git a/app/src/main/java/eu/darken/capod/main/ui/MainActivity.kt b/app/src/main/java/eu/darken/capod/main/ui/MainActivity.kt index 9ba57750..606d0a3a 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/MainActivity.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/MainActivity.kt @@ -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) diff --git a/app/src/main/java/eu/darken/capod/main/ui/onboarding/OnboardingFragment.kt b/app/src/main/java/eu/darken/capod/main/ui/onboarding/OnboardingFragment.kt index 4a833311..e039e347 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/onboarding/OnboardingFragment.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/onboarding/OnboardingFragment.kt @@ -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) diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragment.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragment.kt index d4966bad..657335e8 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragment.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragment.kt @@ -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) diff --git a/app/src/main/java/eu/darken/capod/main/ui/settings/SettingsFragment.kt b/app/src/main/java/eu/darken/capod/main/ui/settings/SettingsFragment.kt index 699d5740..7feece76 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/settings/SettingsFragment.kt @@ -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 { diff --git a/app/src/main/java/eu/darken/capod/troubleshooter/ui/TroubleShooterFragment.kt b/app/src/main/java/eu/darken/capod/troubleshooter/ui/TroubleShooterFragment.kt index 7249b1e1..c354850f 100644 --- a/app/src/main/java/eu/darken/capod/troubleshooter/ui/TroubleShooterFragment.kt +++ b/app/src/main/java/eu/darken/capod/troubleshooter/ui/TroubleShooterFragment.kt @@ -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()) } diff --git a/app/src/main/res/layout/onboarding_fragment.xml b/app/src/main/res/layout/onboarding_fragment.xml index 721cfe3e..f1991009 100644 --- a/app/src/main/res/layout/onboarding_fragment.xml +++ b/app/src/main/res/layout/onboarding_fragment.xml @@ -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">