diff options
| author | cyBerta <cyberta@riseup.net> | 2025-05-22 01:06:39 +0200 |
|---|---|---|
| committer | cyBerta <cyberta@riseup.net> | 2025-05-22 01:06:39 +0200 |
| commit | cab03bf0149344dc34b6b957027330a6086de5a8 (patch) | |
| tree | 52f26c10c4e027ff846af42bee856fbf3c263dca | |
| parent | 809d6a7dc6d710697729a69ba47560e302b886d6 (diff) | |
adapt UI to API 35 requirements: increase height of action bar, so that it draws below the status bar, status bar icon color depends on underlying background color of action bar. Improve navigation drawer gesture handling by excluding 200 dp next to the status image (mask) from Android's back gesture recognition
8 files changed, 155 insertions, 13 deletions
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/EipFragment.java index 76834332..f618b22b 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/EipFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/EipFragment.java @@ -30,8 +30,10 @@ import static se.leap.bitmaskclient.providersetup.ProviderAPI.UPDATE_INVALID_VPN import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.graphics.Rect; import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.Bundle; import android.text.Spannable; import android.text.SpannableString; @@ -41,6 +43,7 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.ViewTreeObserver; import android.widget.Toast; import androidx.annotation.ColorRes; @@ -59,6 +62,7 @@ import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.util.Collections; import de.blinkt.openvpn.core.ConnectionStatus; import de.blinkt.openvpn.core.VpnStatus; @@ -150,6 +154,8 @@ public class EipFragment extends Fragment implements PropertyChangeListener { mainButton = binding.mainButton; locationButton = binding.gatewayLocationButton; locationButton.setTextColor(R.color.black800); + ViewHelper.applyInsetsToViewMargin(locationButton, false, false, false, true); + excludeBackGesture(binding); mainDescription = binding.mainDescription; subDescription = binding.subDescription; stateView = binding.stateView; @@ -189,6 +195,19 @@ public class EipFragment extends Fragment implements PropertyChangeListener { return binding.getRoot(); } + private void excludeBackGesture(FEipBinding binding) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) return; + ViewTreeObserver treeObserver = binding.gestureSuppressView.getViewTreeObserver(); + treeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + binding.gestureSuppressView.getViewTreeObserver().removeOnGlobalLayoutListener(this); + Rect rect = new Rect(0, 0, binding.gestureSuppressView.getWidth(), binding.gestureSuppressView.getHeight()); + binding.gestureSuppressView.setSystemGestureExclusionRects(Collections.singletonList(rect)); + } + }); + } + @Override public void onStart() { super.onStart(); diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/NavigationDrawerFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/NavigationDrawerFragment.java index 41e102bb..376be389 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/NavigationDrawerFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/NavigationDrawerFragment.java @@ -46,11 +46,9 @@ import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.graphics.drawable.DrawerArrowDrawable; import androidx.appcompat.widget.Toolbar; import androidx.core.content.ContextCompat; -import androidx.core.os.LocaleListCompat; import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.Fragment; @@ -58,7 +56,6 @@ import androidx.fragment.app.Fragment; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Locale; -import java.util.Map; import se.leap.bitmaskclient.BuildConfig; import se.leap.bitmaskclient.R; @@ -67,6 +64,7 @@ import se.leap.bitmaskclient.base.MainActivity; import se.leap.bitmaskclient.base.models.Provider; import se.leap.bitmaskclient.base.models.ProviderObservable; import se.leap.bitmaskclient.base.utils.PreferenceHelper; +import se.leap.bitmaskclient.base.utils.ViewHelper; import se.leap.bitmaskclient.base.views.IconSwitchEntry; import se.leap.bitmaskclient.base.views.IconTextEntry; import se.leap.bitmaskclient.eip.EipStatus; @@ -127,6 +125,7 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen restoreFromSavedInstance(savedInstanceState); TetheringObservable.getInstance().addObserver(this); EipStatus.getInstance().addObserver(this); + ViewHelper.applyInsetsToViewMargin(drawerView, true, true, true, false); return drawerView; } @@ -169,6 +168,7 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen // set a custom shadow that overlays the main content when the drawer opens this.drawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); toolbar = this.drawerLayout.findViewById(R.id.toolbar); + ViewHelper.applyInsetsToViewPadding(toolbar, true, true, true, false); setupEntries(); setupActionBarDrawerToggle(activity); diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/ViewHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/ViewHelper.java index e04ba70d..0c03a16f 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/utils/ViewHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/ViewHelper.java @@ -2,22 +2,28 @@ package se.leap.bitmaskclient.base.utils; import static android.view.View.GONE; import static android.view.View.VISIBLE; +import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; import android.animation.Animator; import android.animation.ValueAnimator; import android.app.Activity; import android.content.Context; import android.graphics.Color; +import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.os.Build; import android.text.Spannable; import android.text.SpannableString; import android.text.style.ForegroundColorSpan; +import android.util.DisplayMetrics; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.view.ViewGroup; import android.view.Window; +import android.view.WindowInsetsController; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; @@ -29,9 +35,16 @@ import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.core.content.ContextCompat; +import androidx.core.graphics.Insets; import androidx.core.graphics.drawable.DrawableCompat; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; import androidx.fragment.app.Fragment; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.base.views.ActionBarTitle; @@ -94,7 +107,7 @@ public class ViewHelper { setActivityBarColor(activity, R.color.colorPrimary, R.color.colorPrimaryDark, R.color.colorActionBarTitleFont); } - public static void setActivityBarColor(Activity activity, @ColorRes int primaryColor, @ColorRes int secondaryColor, @ColorRes int textColor) { + public static void setActivityBarColor(Activity activity, @ColorRes int primaryColorRes, @ColorRes int secondaryColorRes, @ColorRes int textColor) { if (!(activity instanceof AppCompatActivity)) { return; } @@ -103,16 +116,12 @@ public class ViewHelper { if (bar == null) { return; } - int color = ContextCompat.getColor(activity, secondaryColor); - bar.setBackgroundDrawable(new ColorDrawable(color)); - Window window = activity.getWindow(); - window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); - window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); - window.setStatusBarColor(ContextCompat.getColor(activity, primaryColor)); - + int secondaryColor = ContextCompat.getColor(activity, secondaryColorRes); + bar.setBackgroundDrawable(new ColorDrawable(secondaryColor)); + setStatusBarColor(activity, primaryColorRes); int actionBarTextColor; if (textColor == 0) { - actionBarTextColor = isBrightColor(color) ? R.color.actionbar_connectivity_state_text_color_dark : R.color.actionbar_connectivity_state_text_color_light; + actionBarTextColor = isBrightColor(secondaryColor) ? R.color.actionbar_connectivity_state_text_color_dark : R.color.actionbar_connectivity_state_text_color_light; } else { actionBarTextColor = textColor; } @@ -130,6 +139,72 @@ public class ViewHelper { } } + public static void applyInsetsToViewPadding(View view, boolean left, boolean top, boolean right, boolean bottom) { + ViewCompat.setOnApplyWindowInsetsListener(view, (v, windowInsets) -> { + Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); + if (left) { + view.setPadding(insets.left, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom()); + } + if (right) { + view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), insets.right, view.getPaddingBottom()); + } + if (bottom) { + view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), view.getPaddingRight(), insets.bottom); + } + if (top) { + view.setPadding(view.getPaddingLeft(), insets.top, view.getPaddingRight(), view.getPaddingBottom()); + } + + // Return CONSUMED if you don't want the window insets to keep passing + // down to descendant views. + return WindowInsetsCompat.CONSUMED; + }); + } + + public static void applyInsetsToViewMargin(View view, boolean left, boolean top, boolean right, boolean bottom) { + ViewCompat.setOnApplyWindowInsetsListener(view, (v, windowInsets) -> { + Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); + ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) v.getLayoutParams(); + if (left) { + mlp.leftMargin = insets.left; + } + if (right) { + mlp.rightMargin = insets.right; + } + if (bottom) { + mlp.bottomMargin = insets.bottom; + } + if (top) { + mlp.topMargin = insets.top; + } + v.setLayoutParams(mlp); + + // Return CONSUMED if you don't want the window insets to keep passing + // down to descendant views. + return WindowInsetsCompat.CONSUMED; + }); + } + + private static void setStatusBarColor(Activity activity, int colorRes) { + Window window = activity.getWindow(); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) { + window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + window.setStatusBarColor(ContextCompat.getColor(activity, colorRes)); + } else { + // in Androiid 15+, the status bar is transparent. Hence we need to make sure that the status bar icons + // have enough contrast to be visible above the the underlying UI + WindowInsetsController controller = window.getInsetsController(); + if (controller != null) { + int color = ContextCompat.getColor(activity, colorRes); + if (isBrightColor(color)) { + controller.setSystemBarsAppearance(APPEARANCE_LIGHT_STATUS_BARS, APPEARANCE_LIGHT_STATUS_BARS); + } else { + controller.setSystemBarsAppearance(0, APPEARANCE_LIGHT_STATUS_BARS); + } + } + } + } public static boolean isBrightColor(int color) { if (android.R.color.transparent == color) diff --git a/app/src/main/res/layout-port/f_eip.xml b/app/src/main/res/layout-port/f_eip.xml index e14c7a10..457dee38 100644 --- a/app/src/main/res/layout-port/f_eip.xml +++ b/app/src/main/res/layout-port/f_eip.xml @@ -114,6 +114,16 @@ app:layout_constraintVertical_bias="1" app:srcCompat="@drawable/state_disconnected" /> + + <View + android:id="@+id/gesture_suppress_view" + android:layout_width="36dp" + android:layout_height="200dp" + app:layout_constraintTop_toTopOf="@id/state_view" + app:layout_constraintBottom_toBottomOf="@id/state_view" + app:layout_constraintStart_toStartOf="parent" + /> + <se.leap.bitmaskclient.base.views.MainButton android:id="@+id/main_button" android:layout_width="0dp" diff --git a/app/src/main/res/layout-sw600dp-port/f_eip.xml b/app/src/main/res/layout-sw600dp-port/f_eip.xml index 88e51652..bab12d62 100644 --- a/app/src/main/res/layout-sw600dp-port/f_eip.xml +++ b/app/src/main/res/layout-sw600dp-port/f_eip.xml @@ -114,6 +114,16 @@ app:layout_constraintVertical_bias="1" app:srcCompat="@drawable/state_disconnected" /> + + <View + android:id="@+id/gesture_suppress_view" + android:layout_width="36dp" + android:layout_height="200dp" + app:layout_constraintTop_toTopOf="@id/state_view" + app:layout_constraintBottom_toBottomOf="@id/state_view" + app:layout_constraintStart_toStartOf="parent" + /> + <se.leap.bitmaskclient.base.views.MainButton android:id="@+id/main_button" android:layout_width="0dp" diff --git a/app/src/main/res/layout-xlarge-port/f_eip.xml b/app/src/main/res/layout-xlarge-port/f_eip.xml index 590cf1a4..022e0b0e 100644 --- a/app/src/main/res/layout-xlarge-port/f_eip.xml +++ b/app/src/main/res/layout-xlarge-port/f_eip.xml @@ -119,6 +119,14 @@ app:layout_constraintVertical_bias="1" app:srcCompat="@drawable/state_disconnected" /> + <View + android:id="@+id/gesture_suppress_view" + android:layout_width="36dp" + android:layout_height="200dp" + app:layout_constraintTop_toTopOf="@id/state_view" + app:layout_constraintBottom_toBottomOf="@id/state_view" + app:layout_constraintStart_toStartOf="parent" + /> <se.leap.bitmaskclient.base.views.MainButton android:id="@+id/main_button" diff --git a/app/src/main/res/layout-xlarge/f_eip.xml b/app/src/main/res/layout-xlarge/f_eip.xml index e413319a..37441bca 100644 --- a/app/src/main/res/layout-xlarge/f_eip.xml +++ b/app/src/main/res/layout-xlarge/f_eip.xml @@ -119,6 +119,15 @@ app:srcCompat="@drawable/state_disconnected" /> + <View + android:id="@+id/gesture_suppress_view" + android:layout_width="36dp" + android:layout_height="200dp" + app:layout_constraintTop_toTopOf="@id/state_view" + app:layout_constraintBottom_toBottomOf="@id/state_view" + app:layout_constraintStart_toStartOf="parent" + /> + <se.leap.bitmaskclient.base.views.MainButton android:id="@+id/main_button" android:layout_width="0dp" diff --git a/app/src/main/res/layout/f_eip.xml b/app/src/main/res/layout/f_eip.xml index 1e1f8e41..615e1ab7 100644 --- a/app/src/main/res/layout/f_eip.xml +++ b/app/src/main/res/layout/f_eip.xml @@ -66,6 +66,8 @@ app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintGuide_percent="0.7" /> + + <androidx.appcompat.widget.AppCompatImageView android:id="@+id/background" android:layout_width="match_parent" @@ -73,7 +75,6 @@ android:scaleType="fitXY" app:srcCompat="@drawable/bg_disconnected" /> - <androidx.appcompat.widget.AppCompatTextView android:id="@+id/main_description" android:layout_width="wrap_content" @@ -128,6 +129,16 @@ app:srcCompat="@drawable/state_disconnected" /> + <View + android:id="@+id/gesture_suppress_view" + android:layout_width="36dp" + android:layout_height="200dp" + app:layout_constraintTop_toTopOf="@id/state_view" + app:layout_constraintBottom_toBottomOf="@id/state_view" + app:layout_constraintStart_toStartOf="parent" + /> + + <se.leap.bitmaskclient.base.views.MainButton android:id="@+id/main_button" android:layout_width="0dp" |
