From 163f4f2014271823da36f25ad091263270674f17 Mon Sep 17 00:00:00 2001 From: lynxnb Date: Thu, 10 Nov 2022 16:48:25 +0100 Subject: [PATCH] Fix window insets handling when in landscape mode To avoid code duplication, insets handling has been moved to a separate interface. --- app/src/main/java/emu/skyline/MainActivity.kt | 12 ++- .../main/java/emu/skyline/SettingsActivity.kt | 12 +-- .../emu/skyline/input/ControllerActivity.kt | 11 +-- .../skyline/preference/GpuDriverActivity.kt | 12 +-- .../emu/skyline/utils/WindowInsetsHelper.kt | 85 +++++++++++++++++++ 5 files changed, 101 insertions(+), 31 deletions(-) create mode 100644 app/src/main/java/emu/skyline/utils/WindowInsetsHelper.kt diff --git a/app/src/main/java/emu/skyline/MainActivity.kt b/app/src/main/java/emu/skyline/MainActivity.kt index 37472d47..93237ce8 100644 --- a/app/src/main/java/emu/skyline/MainActivity.kt +++ b/app/src/main/java/emu/skyline/MainActivity.kt @@ -18,7 +18,9 @@ import androidx.appcompat.app.AppCompatDelegate import androidx.core.content.ContextCompat import androidx.core.content.res.use import androidx.core.graphics.drawable.toBitmap -import androidx.core.view.* +import androidx.core.view.WindowCompat +import androidx.core.view.isInvisible +import androidx.core.view.isVisible import androidx.documentfile.provider.DocumentFile import androidx.preference.PreferenceManager import androidx.recyclerview.widget.GridLayoutManager @@ -35,6 +37,7 @@ import emu.skyline.loader.LoaderResult import emu.skyline.loader.RomFormat import emu.skyline.provider.DocumentsProvider import emu.skyline.utils.PreferenceSettings +import emu.skyline.utils.WindowInsetsHelper import javax.inject.Inject import kotlin.math.ceil @@ -100,12 +103,7 @@ class MainActivity : AppCompatActivity() { setContentView(binding.root) WindowCompat.setDecorFitsSystemWindows(window, false) - // Apply inset padding to the app list recycler view to avoid navigation bar overlap - ViewCompat.setOnApplyWindowInsetsListener(binding.appList) { view, windowInsets -> - val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) - view.updatePadding(bottom = insets.bottom) - WindowInsetsCompat.CONSUMED - } + WindowInsetsHelper.applyToActivity(binding.root, binding.appList) PreferenceManager.setDefaultValues(this, R.xml.preferences, false) diff --git a/app/src/main/java/emu/skyline/SettingsActivity.kt b/app/src/main/java/emu/skyline/SettingsActivity.kt index 525e615d..a3f789ab 100644 --- a/app/src/main/java/emu/skyline/SettingsActivity.kt +++ b/app/src/main/java/emu/skyline/SettingsActivity.kt @@ -16,6 +16,7 @@ import androidx.preference.PreferenceCategory import androidx.preference.PreferenceFragmentCompat import emu.skyline.databinding.SettingsActivityBinding import emu.skyline.preference.IntegerListPreference +import emu.skyline.utils.WindowInsetsHelper class SettingsActivity : AppCompatActivity() { val binding by lazy { SettingsActivityBinding.inflate(layoutInflater) } @@ -33,6 +34,7 @@ class SettingsActivity : AppCompatActivity() { setContentView(binding.root) WindowCompat.setDecorFitsSystemWindows(window, false) + WindowInsetsHelper.applyToActivity(binding.root) setSupportActionBar(binding.titlebar.toolbar) supportActionBar?.setDisplayHomeAsUpEnabled(true) @@ -81,15 +83,9 @@ class SettingsActivity : AppCompatActivity() { } override fun onViewCreated(view : View, savedInstanceState : Bundle?) { - val recyclerView = view.findViewById(R.id.recycler_view) - // Apply inset padding to the settings recycler view to avoid navigation bar overlap - ViewCompat.setOnApplyWindowInsetsListener(recyclerView) { v, windowInsets -> - val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) - v.updatePadding(bottom = insets.bottom) - WindowInsetsCompat.CONSUMED - } - super.onViewCreated(view, savedInstanceState) + val recyclerView = view.findViewById(R.id.recycler_view) + WindowInsetsHelper.setPadding(recyclerView, bottom = true) } /** diff --git a/app/src/main/java/emu/skyline/input/ControllerActivity.kt b/app/src/main/java/emu/skyline/input/ControllerActivity.kt index ad24f9ff..4d224ad6 100644 --- a/app/src/main/java/emu/skyline/input/ControllerActivity.kt +++ b/app/src/main/java/emu/skyline/input/ControllerActivity.kt @@ -12,7 +12,8 @@ import android.view.KeyEvent import android.view.ViewTreeObserver import androidx.appcompat.app.AppCompatActivity import androidx.coordinatorlayout.widget.CoordinatorLayout -import androidx.core.view.* +import androidx.core.view.WindowCompat +import androidx.core.view.marginTop import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -29,6 +30,7 @@ import emu.skyline.input.dialog.RumbleDialog import emu.skyline.input.dialog.StickDialog import emu.skyline.input.onscreen.OnScreenEditActivity import emu.skyline.utils.PreferenceSettings +import emu.skyline.utils.WindowInsetsHelper import javax.inject.Inject /** @@ -173,12 +175,7 @@ class ControllerActivity : AppCompatActivity() { setContentView(binding.root) WindowCompat.setDecorFitsSystemWindows(window, false) - // Apply inset padding to the controller list recycler view to avoid navigation bar overlap - ViewCompat.setOnApplyWindowInsetsListener(binding.controllerList) { view, windowInsets -> - val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) - view.updatePadding(bottom = insets.bottom) - WindowInsetsCompat.CONSUMED - } + WindowInsetsHelper.applyToActivity(binding.root, binding.controllerList) setSupportActionBar(binding.titlebar.toolbar) supportActionBar?.setDisplayHomeAsUpEnabled(true) diff --git a/app/src/main/java/emu/skyline/preference/GpuDriverActivity.kt b/app/src/main/java/emu/skyline/preference/GpuDriverActivity.kt index 5d1741e7..cd1a9858 100644 --- a/app/src/main/java/emu/skyline/preference/GpuDriverActivity.kt +++ b/app/src/main/java/emu/skyline/preference/GpuDriverActivity.kt @@ -12,10 +12,7 @@ import android.view.ViewTreeObserver import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.coordinatorlayout.widget.CoordinatorLayout -import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat -import androidx.core.view.WindowInsetsCompat -import androidx.core.view.updatePadding import androidx.recyclerview.widget.LinearLayoutManager import androidx.viewbinding.ViewBinding import com.google.android.material.snackbar.Snackbar @@ -29,6 +26,7 @@ import emu.skyline.databinding.GpuDriverActivityBinding import emu.skyline.utils.GpuDriverHelper import emu.skyline.utils.GpuDriverInstallResult import emu.skyline.utils.PreferenceSettings +import emu.skyline.utils.WindowInsetsHelper import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -127,12 +125,8 @@ class GpuDriverActivity : AppCompatActivity() { setContentView(binding.root) WindowCompat.setDecorFitsSystemWindows(window, false) - // Apply inset padding to the driver list recycler view to avoid navigation bar overlap - ViewCompat.setOnApplyWindowInsetsListener(binding.driverList) { view, windowInsets -> - val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) - view.updatePadding(bottom = insets.bottom) - WindowInsetsCompat.CONSUMED - } + WindowInsetsHelper.applyToActivity(binding.root, binding.driverList) + WindowInsetsHelper.addMargin(binding.addDriverButton, bottom = true) setSupportActionBar(binding.titlebar.toolbar) supportActionBar?.setDisplayHomeAsUpEnabled(true) diff --git a/app/src/main/java/emu/skyline/utils/WindowInsetsHelper.kt b/app/src/main/java/emu/skyline/utils/WindowInsetsHelper.kt new file mode 100644 index 00000000..322c4bde --- /dev/null +++ b/app/src/main/java/emu/skyline/utils/WindowInsetsHelper.kt @@ -0,0 +1,85 @@ +/* + * SPDX-License-Identifier: MPL-2.0 + * Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/) + */ + +package emu.skyline.utils + +import android.view.View +import android.view.ViewGroup +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updateLayoutParams + +/** + * An interface to easily add window insets handling to any layout + */ +interface WindowInsetsHelper { + companion object { + /** + * Convenience method to setup insets for most Activities with a single call + * @param rootView The root view of the layout + * @param listView The list view of the layout + */ + fun applyToActivity(rootView : View, listView : View? = null) { + // Apply margin to the root view to avoid overlapping system bar in landscape mode + // Don't consume insets in the root view so that child views can apply them + setMargin(rootView, consume = false, left = true, right = true) + + // Apply padding to the list view to avoid navigation bar overlap at the bottom + listView?.let { addPadding(it, bottom = true) } + } + + fun setMargin(view : View, consume : Boolean = true, left : Boolean = false, top : Boolean = false, right : Boolean = false, bottom : Boolean = false) { + ViewCompat.setOnApplyWindowInsetsListener(view) { v, windowInsets -> + val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) + v.updateLayoutParams { + if (left) leftMargin = insets.left + if (top) topMargin = insets.top + if (right) rightMargin = insets.right + if (bottom) bottomMargin = insets.bottom + } + if (consume) WindowInsetsCompat.CONSUMED else windowInsets + } + } + + fun addMargin(view : View, consume : Boolean = true, left : Boolean = false, top : Boolean = false, right : Boolean = false, bottom : Boolean = false) { + ViewCompat.setOnApplyWindowInsetsListener(view) { v, windowInsets -> + val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) + v.updateLayoutParams { + if (left) leftMargin += insets.left + if (top) topMargin += insets.top + if (right) rightMargin += insets.right + if (bottom) bottomMargin += insets.bottom + } + if (consume) WindowInsetsCompat.CONSUMED else windowInsets + } + } + + fun setPadding(view : View, consume : Boolean = true, left : Boolean = false, top : Boolean = false, right : Boolean = false, bottom : Boolean = false) { + ViewCompat.setOnApplyWindowInsetsListener(view) { v, windowInsets -> + val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) + v.setPadding( + if (left) insets.left else v.paddingLeft, + if (top) insets.top else v.paddingTop, + if (right) insets.right else v.paddingRight, + if (bottom) insets.bottom else v.paddingBottom + ) + if (consume) WindowInsetsCompat.CONSUMED else windowInsets + } + } + + fun addPadding(view : View, consume : Boolean = true, left : Boolean = false, top : Boolean = false, right : Boolean = false, bottom : Boolean = false) { + ViewCompat.setOnApplyWindowInsetsListener(view) { v, windowInsets -> + val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) + v.setPadding( + if (left) insets.left + v.paddingLeft else v.paddingLeft, + if (top) insets.top + v.paddingTop else v.paddingTop, + if (right) insets.right + v.paddingRight else v.paddingRight, + if (bottom) insets.bottom + v.paddingBottom else v.paddingBottom + ) + if (consume) WindowInsetsCompat.CONSUMED else windowInsets + } + } + } +}