From 852378e4841eb69cff03485fb3a166bd406529ec Mon Sep 17 00:00:00 2001 From: Andrew Gunnerson Date: Sat, 4 Oct 2025 15:37:45 -0400 Subject: [PATCH 1/2] Simplify inset logic, fix landscape mode, fix cutout overlapping The commit primarily fixes a few overlapping issues caused by the window inset handling. Previously, there were two main issues: * Because setTransparentNavigationBar() checked for portrait mode, the inset logic never executed in landscape mode. This caused the app to overlap the status bar and navigation bar. * The inset logic did not have handling for displayCutout insets. In landscape mode, this would cause the app to overlap the notch or camera hole punch area on phones. In addition to fixing those issues, this commit simplifies the inset logic a bit: * applyWindowInsets() now accepts an EnumSet of WindowInsetPosition to avoid needing to duplicate logic for the various position combinations. * Insets are now applied to the main container in the layout instead of individual elements where possible. This eliminates the need for the previous manual IME height handling logic in BOTTOM_IME vs TOP_BOTTOM_IME (for avoiding the insets being applied twice). * Since insets are now applied to the main layout container, applyWindowInsets() now takes systemBars, displayCutout, and ime all into consideration. This should avoid all possible overlapping. * Add support for using padding instead of margins for insets. This is used for GroupActivity's navigation drawer, where Material design intends for the drawer background to be drawn behind system bars. Signed-off-by: Andrew Gunnerson --- .../keepass/activities/EntryActivity.kt | 15 ++- .../keepass/activities/EntryEditActivity.kt | 9 +- .../keepass/activities/GroupActivity.kt | 19 +++- .../keepass/settings/SettingsActivity.kt | 8 +- .../com/kunzisoft/keepass/view/ViewUtil.kt | 107 +++++++++++------- app/src/main/res/layout/activity_entry.xml | 1 + 6 files changed, 105 insertions(+), 54 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt index 5da1544b8..186288919 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt @@ -23,7 +23,6 @@ import android.content.Intent import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.net.Uri -import android.os.Build import android.os.Bundle import android.os.Handler import android.os.Looper @@ -38,13 +37,10 @@ import androidx.activity.result.ActivityResultLauncher import androidx.activity.viewModels import androidx.appcompat.widget.Toolbar import androidx.coordinatorlayout.widget.CoordinatorLayout -import androidx.core.content.ContextCompat -import androidx.core.content.res.ResourcesCompat import androidx.core.graphics.BlendModeColorFilterCompat import androidx.core.graphics.BlendModeCompat import androidx.core.graphics.ColorUtils import androidx.core.view.ViewCompat -import androidx.core.view.WindowCompat import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.google.android.material.appbar.AppBarLayout @@ -83,11 +79,13 @@ import com.kunzisoft.keepass.view.hideByFading import com.kunzisoft.keepass.view.setTransparentNavigationBar import com.kunzisoft.keepass.view.showActionErrorIfNeeded import com.kunzisoft.keepass.viewmodels.EntryViewModel +import java.util.EnumSet import java.util.UUID class EntryActivity : DatabaseLockActivity() { private var footer: ViewGroup? = null + private var container: View? = null private var coordinatorLayout: CoordinatorLayout? = null private var collapsingToolbarLayout: CollapsingToolbarLayout? = null private var appBarLayout: AppBarLayout? = null @@ -139,6 +137,7 @@ class EntryActivity : DatabaseLockActivity() { // Get views footer = findViewById(R.id.activity_entry_footer) + container = findViewById(R.id.activity_entry_container) coordinatorLayout = findViewById(R.id.toolbar_coordinator) collapsingToolbarLayout = findViewById(R.id.toolbar_layout) appBarLayout = findViewById(R.id.app_bar) @@ -154,8 +153,12 @@ class EntryActivity : DatabaseLockActivity() { setTransparentNavigationBar { // To fix margin with API 27 ViewCompat.setOnApplyWindowInsetsListener(collapsingToolbarLayout!!, null) - coordinatorLayout?.applyWindowInsets(WindowInsetPosition.TOP) - footer?.applyWindowInsets(WindowInsetPosition.BOTTOM) + container?.applyWindowInsets(EnumSet.of( + WindowInsetPosition.TOP_MARGINS, + WindowInsetPosition.BOTTOM_MARGINS, + WindowInsetPosition.START_MARGINS, + WindowInsetPosition.END_MARGINS, + )) } // Empty title diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt index 49f6544c6..f5b2d6bcb 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt @@ -100,6 +100,7 @@ import com.kunzisoft.keepass.view.showActionErrorIfNeeded import com.kunzisoft.keepass.view.updateLockPaddingStart import com.kunzisoft.keepass.viewmodels.ColorPickerViewModel import com.kunzisoft.keepass.viewmodels.EntryEditViewModel +import java.util.EnumSet import java.util.UUID class EntryEditActivity : DatabaseLockActivity(), @@ -180,8 +181,12 @@ class EntryEditActivity : DatabaseLockActivity(), // To apply fit window with transparency setTransparentNavigationBar(applyToStatusBar = true) { - container?.applyWindowInsets(WindowInsetPosition.TOP_BOTTOM_IME) - footer?.applyWindowInsets(WindowInsetPosition.BOTTOM_IME) + container?.applyWindowInsets(EnumSet.of( + WindowInsetPosition.TOP_MARGINS, + WindowInsetPosition.BOTTOM_MARGINS, + WindowInsetPosition.START_MARGINS, + WindowInsetPosition.END_MARGINS, + )) } stopService(Intent(this, ClipboardEntryNotificationService::class.java)) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt index c0704dc0a..c82207b3e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt @@ -48,6 +48,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.view.ActionMode import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.Toolbar +import androidx.constraintlayout.widget.ConstraintLayout import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.view.GravityCompat import androidx.core.view.WindowInsetsCompat @@ -117,6 +118,7 @@ import com.kunzisoft.keepass.view.updateLockPaddingStart import com.kunzisoft.keepass.viewmodels.GroupEditViewModel import com.kunzisoft.keepass.viewmodels.GroupViewModel import org.joda.time.LocalDateTime +import java.util.EnumSet class GroupActivity : DatabaseLockActivity(), @@ -131,6 +133,7 @@ class GroupActivity : DatabaseLockActivity(), private var header: ViewGroup? = null private var footer: ViewGroup? = null private var drawerLayout: DrawerLayout? = null + private var constraintLayout: ConstraintLayout? = null private var databaseNavView: NavigationDatabaseView? = null private var coordinatorLayout: CoordinatorLayout? = null private var coordinatorError: CoordinatorLayout? = null @@ -279,6 +282,7 @@ class GroupActivity : DatabaseLockActivity(), header = findViewById(R.id.activity_group_header) footer = findViewById(R.id.activity_group_footer) drawerLayout = findViewById(R.id.drawer_layout) + constraintLayout = findViewById(R.id.activity_group_container_view) databaseNavView = findViewById(R.id.database_nav_view) coordinatorLayout = findViewById(R.id.group_coordinator) coordinatorError = findViewById(R.id.error_coordinator) @@ -296,8 +300,19 @@ class GroupActivity : DatabaseLockActivity(), // To apply fit window with transparency setTransparentNavigationBar(applyToStatusBar = true) { - drawerLayout?.applyWindowInsets(WindowInsetPosition.TOP_BOTTOM_IME) - footer?.applyWindowInsets(WindowInsetPosition.BOTTOM_IME) + constraintLayout?.applyWindowInsets(EnumSet.of( + WindowInsetPosition.TOP_MARGINS, + WindowInsetPosition.BOTTOM_MARGINS, + WindowInsetPosition.START_MARGINS, + WindowInsetPosition.END_MARGINS, + )) + // The background of the drawer is meant to overlap system bars, so use padding + databaseNavView?.applyWindowInsets(EnumSet.of( + WindowInsetPosition.TOP_PADDING, + WindowInsetPosition.BOTTOM_PADDING, + // Only on the start side, since the drawer is anchored to one side of the screen + WindowInsetPosition.START_PADDING, + )) } lockView?.setOnClickListener { diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt index 6bf068bbf..7c996aac3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt @@ -70,8 +70,12 @@ open class SettingsActivity // To apply navigation bar with background color /* TODO Settings nav bar setTransparentNavigationBar { - coordinatorLayout?.applyWindowInsets(WindowInsetPosition.TOP) - footer?.applyWindowInsets(WindowInsetPosition.BOTTOM) + coordinatorLayout?.applyWindowInsets(EnumSet.of( + WindowInsetPosition.TOP_MARGINS, + WindowInsetPosition.BOTTOM_MARGINS, + WindowInsetPosition.START_MARGINS, + WindowInsetPosition.END_MARGINS, + )) }*/ mExternalFileHelper = ExternalFileHelper(this) diff --git a/app/src/main/java/com/kunzisoft/keepass/view/ViewUtil.kt b/app/src/main/java/com/kunzisoft/keepass/view/ViewUtil.kt index 1768c3c49..2437e070d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/ViewUtil.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/ViewUtil.kt @@ -24,7 +24,6 @@ import android.animation.AnimatorSet import android.animation.ValueAnimator import android.app.Activity import android.content.Context -import android.content.res.Configuration import android.graphics.Color import android.graphics.Paint import android.graphics.PorterDuff @@ -58,7 +57,6 @@ import androidx.core.view.WindowInsetsCompat import androidx.core.view.forEach import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams -import androidx.core.view.updatePadding import androidx.core.view.updatePaddingRelative import com.google.android.material.appbar.CollapsingToolbarLayout import com.google.android.material.snackbar.Snackbar @@ -66,6 +64,7 @@ import com.kunzisoft.keepass.R import com.kunzisoft.keepass.database.helper.getLocalizedMessage import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.tasks.ActionRunnable +import java.util.EnumSet /** @@ -306,9 +305,7 @@ fun CollapsingToolbarLayout.changeTitleColor(color: Int) { @Suppress("DEPRECATION") fun Activity.setTransparentNavigationBar(applyToStatusBar: Boolean = false, applyWindowInsets: () -> Unit) { - // Only in portrait - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 - && resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { WindowCompat.setDecorFitsSystemWindows(window, false) window.navigationBarColor = ContextCompat.getColor(this, R.color.surface_selector) if (applyToStatusBar) { @@ -324,7 +321,7 @@ fun Activity.setTransparentNavigationBar(applyToStatusBar: Boolean = false, appl /** * Apply a margin to a view to fix the window inset */ -fun View.applyWindowInsets(position: WindowInsetPosition = WindowInsetPosition.BOTTOM) { +fun View.applyWindowInsets(positions: EnumSet) { ViewCompat.setOnApplyWindowInsetsListener(this) { view, windowInsets -> var consumed = false @@ -340,52 +337,78 @@ fun View.applyWindowInsets(position: WindowInsetPosition = WindowInsetPosition.B } } - val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) - when (position) { - WindowInsetPosition.TOP -> { - if (view.layoutParams is ViewGroup.MarginLayoutParams) { - view.updateLayoutParams { - topMargin = insets.top + val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars() + or WindowInsetsCompat.Type.displayCutout() + or WindowInsetsCompat.Type.ime()) + + val isRtl = layoutDirection == View.LAYOUT_DIRECTION_RTL + + val wantTopMargins = positions.contains(WindowInsetPosition.TOP_MARGINS) + val wantBottomMargins = positions.contains(WindowInsetPosition.BOTTOM_MARGINS) + val wantStartMargins = positions.contains(WindowInsetPosition.START_MARGINS) + val wantEndMargins = positions.contains(WindowInsetPosition.END_MARGINS) + + if (view.layoutParams is ViewGroup.MarginLayoutParams + && (wantTopMargins || wantBottomMargins || wantStartMargins || wantEndMargins)) { + view.updateLayoutParams { + if (wantTopMargins) { + topMargin = insets.top + } + if (wantBottomMargins) { + bottomMargin = insets.bottom + } + if (wantStartMargins) { + if (isRtl) { + rightMargin = insets.right + } else { + leftMargin = insets.left } } - } - WindowInsetPosition.LEGIT_TOP -> { - if (view.layoutParams is ViewGroup.MarginLayoutParams) { - view.updateLayoutParams { - topMargin = 0 - } - } - } - WindowInsetPosition.BOTTOM -> { - if (view.layoutParams is ViewGroup.MarginLayoutParams) { - view.updateLayoutParams { - bottomMargin = insets.bottom - } - } - } - WindowInsetPosition.BOTTOM_IME -> { - val imeHeight = windowInsets.getInsets(WindowInsetsCompat.Type.ime()).bottom - if (view.layoutParams is ViewGroup.MarginLayoutParams) { - view.updateLayoutParams { - bottomMargin = if (imeHeight > 1) 0 else insets.bottom - } - } - } - WindowInsetPosition.TOP_BOTTOM_IME -> { - val imeHeight = windowInsets.getInsets(WindowInsetsCompat.Type.ime()).bottom - if (view.layoutParams is ViewGroup.MarginLayoutParams) { - view.updateLayoutParams { - topMargin = insets.top - bottomMargin = if (imeHeight > 1) imeHeight else 0 + if (wantEndMargins) { + if (isRtl) { + leftMargin = insets.left + } else { + rightMargin = insets.right } } } } + + val wantTopPadding = positions.contains(WindowInsetPosition.TOP_PADDING) + val wantBottomPadding = positions.contains(WindowInsetPosition.BOTTOM_PADDING) + val wantStartPadding = positions.contains(WindowInsetPosition.START_PADDING) + val wantEndPadding = positions.contains(WindowInsetPosition.END_PADDING) + + if (wantTopPadding || wantBottomPadding || wantStartPadding || wantEndPadding) { + val topPadding = if (wantTopPadding) insets.top else 0 + val bottomPadding = if (wantBottomPadding) insets.bottom else 0 + var leftPadding = 0 + var rightPadding = 0 + + if (wantStartPadding) { + if (isRtl) { + rightPadding = insets.right + } else { + leftPadding = insets.left + } + } + if (wantEndPadding) { + if (isRtl) { + leftPadding = insets.left + } else { + rightPadding = insets.right + } + } + + setPadding(leftPadding, topPadding, rightPadding, bottomPadding) + } + // If any of the children consumed the insets, return an appropriate value if (consumed) WindowInsetsCompat.CONSUMED else windowInsets } } enum class WindowInsetPosition { - TOP, BOTTOM, LEGIT_TOP, BOTTOM_IME, TOP_BOTTOM_IME + TOP_MARGINS, BOTTOM_MARGINS, START_MARGINS, END_MARGINS, + TOP_PADDING, BOTTOM_PADDING, START_PADDING, END_PADDING, } diff --git a/app/src/main/res/layout/activity_entry.xml b/app/src/main/res/layout/activity_entry.xml index b93a66a3b..63d4c696c 100644 --- a/app/src/main/res/layout/activity_entry.xml +++ b/app/src/main/res/layout/activity_entry.xml @@ -20,6 +20,7 @@ From 99cb50d03160135b8bf26003dde6d7a51491e143 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Mon, 6 Oct 2025 12:47:30 +0200 Subject: [PATCH 2/2] fix: Bug report title --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index d08d4ae39..31addac9a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,4 +1,4 @@ -name: Bug Report +name: Bug report description: Report a bug. labels: ["bug"] body: