From 1d310e7d8defab999c812983de624bba4b013cd6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 00:53:25 +0000 Subject: [PATCH 1/7] Fix ToastBar empty space above text when positioned at TOP The safe area top padding was unconditionally set to safeArea.getY(), but the layered pane (which wraps the content pane) is already positioned below the toolbar that accounts for the safe area inset. This caused double-counting of the inset, creating empty space above the ToastBar text. Now computes the needed padding as the difference between the safe area top inset and the parent container's absolute Y position, so padding is only added when the ToastBar actually extends into the unsafe area. Agent-Logs-Url: https://github.com/codenameone/CodenameOne/sessions/c374fa90-ccba-426b-b2d3-5e2f430bf1fe Co-authored-by: shai-almog <67850168+shai-almog@users.noreply.github.com> --- .../src/com/codename1/components/ToastBar.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/CodenameOne/src/com/codename1/components/ToastBar.java b/CodenameOne/src/com/codename1/components/ToastBar.java index a86efe9e46..618b2d3d89 100644 --- a/CodenameOne/src/com/codename1/components/ToastBar.java +++ b/CodenameOne/src/com/codename1/components/ToastBar.java @@ -687,9 +687,14 @@ private ToastBarComponent getToastBarComponent(boolean create) { s.setPaddingUnit(Style.UNIT_TYPE_PIXELS); s.setPaddingBottom(safeBottomMargin); } else if (position == Component.TOP && safeArea.getY() > 0) { - Style s = c.getAllStyles(); - s.setPaddingUnit(Style.UNIT_TYPE_PIXELS); - s.setPaddingTop(safeArea.getY()); + Container parent = c.getParent(); + int parentAbsY = parent != null ? parent.getAbsoluteY() : 0; + int neededPadding = safeArea.getY() - parentAbsY; + if (neededPadding > 0) { + Style s = c.getAllStyles(); + s.setPaddingUnit(Style.UNIT_TYPE_PIXELS); + s.setPaddingTop(neededPadding); + } } return c; From 6ef6b8a97081a6af2c0a9584ddfc5e02bf01beae Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 00:53:57 +0000 Subject: [PATCH 2/7] Skip safe area top padding when parent is null to avoid incorrect calculation Agent-Logs-Url: https://github.com/codenameone/CodenameOne/sessions/c374fa90-ccba-426b-b2d3-5e2f430bf1fe Co-authored-by: shai-almog <67850168+shai-almog@users.noreply.github.com> --- .../src/com/codename1/components/ToastBar.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/CodenameOne/src/com/codename1/components/ToastBar.java b/CodenameOne/src/com/codename1/components/ToastBar.java index 618b2d3d89..94647a90be 100644 --- a/CodenameOne/src/com/codename1/components/ToastBar.java +++ b/CodenameOne/src/com/codename1/components/ToastBar.java @@ -688,12 +688,13 @@ private ToastBarComponent getToastBarComponent(boolean create) { s.setPaddingBottom(safeBottomMargin); } else if (position == Component.TOP && safeArea.getY() > 0) { Container parent = c.getParent(); - int parentAbsY = parent != null ? parent.getAbsoluteY() : 0; - int neededPadding = safeArea.getY() - parentAbsY; - if (neededPadding > 0) { - Style s = c.getAllStyles(); - s.setPaddingUnit(Style.UNIT_TYPE_PIXELS); - s.setPaddingTop(neededPadding); + if (parent != null) { + int neededPadding = safeArea.getY() - parent.getAbsoluteY(); + if (neededPadding > 0) { + Style s = c.getAllStyles(); + s.setPaddingUnit(Style.UNIT_TYPE_PIXELS); + s.setPaddingTop(neededPadding); + } } } From 655e9d59fa60f521c22885eb58463eb115f566bc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 01:07:54 +0000 Subject: [PATCH 3/7] Add regression tests for ToastBar TOP position safe area padding Add five regression tests to ToastBarTest covering: - TOP position with safe area inset when parent is below safe area (the original bug: should not double-count the inset) - TOP position without safe area inset (no extra padding) - BOTTOM position with safe area bottom inset (correct padding) - BOTTOM position without safe area inset (no extra padding) - TOP position with parent at Y=0 using FormLayeredPane (full safe area padding needed) Also adds setDisplaySafeArea/getDisplaySafeArea to TestCodenameOneImplementation to simulate devices with notches. Agent-Logs-Url: https://github.com/codenameone/CodenameOne/sessions/d40eb36c-4528-4297-97d4-4f8144175502 Co-authored-by: shai-almog <67850168+shai-almog@users.noreply.github.com> --- .../codename1/components/ToastBarTest.java | 186 ++++++++++++++++++ .../TestCodenameOneImplementation.java | 23 +++ 2 files changed, 209 insertions(+) diff --git a/maven/core-unittests/src/test/java/com/codename1/components/ToastBarTest.java b/maven/core-unittests/src/test/java/com/codename1/components/ToastBarTest.java index 517935f2a4..245fea1381 100644 --- a/maven/core-unittests/src/test/java/com/codename1/components/ToastBarTest.java +++ b/maven/core-unittests/src/test/java/com/codename1/components/ToastBarTest.java @@ -2,6 +2,15 @@ import com.codename1.junit.FormTest; import com.codename1.junit.UITestBase; +import com.codename1.testing.TestCodenameOneImplementation; +import com.codename1.ui.Component; +import com.codename1.ui.Container; +import com.codename1.ui.Display; +import com.codename1.ui.Form; +import com.codename1.ui.geom.Rectangle; +import com.codename1.ui.plaf.Style; + +import java.lang.reflect.Method; import static org.junit.jupiter.api.Assertions.*; @@ -75,4 +84,181 @@ void testShowMessageWithIcon() { ToastBar.Status status = ToastBar.showMessage("Test", '\uE000', 1000); assertNotNull(status); } + + // ---- Regression tests for ToastBar TOP position safe area padding ---- + + /** + * Invokes the private getToastBarComponent(boolean) method via reflection so + * that the component and its padding are set up without triggering animations. + */ + private Container invokeGetToastBarComponent(ToastBar tb) throws Exception { + Method m = ToastBar.class.getDeclaredMethod("getToastBarComponent", boolean.class); + m.setAccessible(true); + return (Container) m.invoke(tb, true); + } + + /** + * Regression test: when position is TOP and the device has a safe area inset + * (e.g. notch), the ToastBar should NOT double-count the inset if its parent + * container is already positioned below the safe area boundary. + */ + @FormTest + void testTopPositionNoPaddingWhenParentBelowSafeArea() throws Exception { + int safeTop = 100; + // Simulate a device with a 100px top safe area inset + implementation.setDisplaySafeArea(new Rectangle(0, safeTop, 1080, 1920 - safeTop)); + + ToastBar tb = ToastBar.getInstance(); + tb.setPosition(Component.TOP); + + Form f = Display.getInstance().getCurrent(); + f.revalidate(); + + Container c = invokeGetToastBarComponent(tb); + assertNotNull(c, "ToastBarComponent should be created"); + + Container parent = c.getParent(); + assertNotNull(parent, "ToastBarComponent should have a parent"); + + // If the parent's absolute Y is at or beyond the safe area top, + // no extra padding should be added (this was the double-counting bug). + if (parent.getAbsoluteY() >= safeTop) { + int paddingTop = c.getStyle().getPaddingTop(); + assertTrue(paddingTop < safeTop, + "Top padding should NOT be the full safe area inset (" + safeTop + + ") when parent is already at or below the safe area, got: " + paddingTop); + } else { + // Parent is above the safe area boundary, padding should be the difference + int expectedPadding = safeTop - parent.getAbsoluteY(); + int paddingTop = c.getStyle().getPaddingTop(); + assertEquals(expectedPadding, paddingTop, + "Top padding should equal safeArea.getY() - parent.getAbsoluteY()"); + } + + c.remove(); + f.putClientProperty("ToastBarComponent", null); + implementation.setDisplaySafeArea(null); + } + + /** + * When position is TOP and the device has NO safe area inset (safeArea.getY() == 0), + * no extra top padding should be applied by the safe area logic. + */ + @FormTest + void testTopPositionNoPaddingWithoutSafeAreaInset() throws Exception { + // Default safe area: full display (y=0) + implementation.setDisplaySafeArea(null); + + ToastBar tb = ToastBar.getInstance(); + tb.setPosition(Component.TOP); + + Form f = Display.getInstance().getCurrent(); + f.revalidate(); + + Container c = invokeGetToastBarComponent(tb); + assertNotNull(c, "ToastBarComponent should be created"); + + // The default UIID may have some small padding, but it should be well below + // any safe area inset value. + int paddingTop = c.getStyle().getPaddingTop(); + assertTrue(paddingTop < 10, + "Top padding should not contain safe area compensation when no inset, got: " + paddingTop); + + c.remove(); + f.putClientProperty("ToastBarComponent", null); + } + + /** + * When position is BOTTOM and the device has a safe area bottom inset, + * the bottom padding should reflect the bottom safe area margin. + */ + @FormTest + void testBottomPositionPaddingWithSafeAreaInset() throws Exception { + int safeTop = 50; + int safeHeight = 1820; // leaves 50px at bottom (1920 - 50 - 1820 = 50) + implementation.setDisplaySafeArea(new Rectangle(0, safeTop, 1080, safeHeight)); + + ToastBar tb = ToastBar.getInstance(); + tb.setPosition(Component.BOTTOM); + + Form f = Display.getInstance().getCurrent(); + f.revalidate(); + + Container c = invokeGetToastBarComponent(tb); + assertNotNull(c, "ToastBarComponent should be created"); + + int expectedBottomPadding = 1920 - safeTop - safeHeight; // 50 + Style s = c.getStyle(); + assertEquals(expectedBottomPadding, s.getPaddingBottom(), + "Bottom padding should equal the safe area bottom margin"); + + c.remove(); + f.putClientProperty("ToastBarComponent", null); + implementation.setDisplaySafeArea(null); + } + + /** + * When position is BOTTOM and the device has no safe area inset, + * no extra bottom padding should be added by the safe area logic. + */ + @FormTest + void testBottomPositionNoPaddingWithoutSafeAreaInset() throws Exception { + implementation.setDisplaySafeArea(null); + + ToastBar tb = ToastBar.getInstance(); + tb.setPosition(Component.BOTTOM); + + Form f = Display.getInstance().getCurrent(); + f.revalidate(); + + Container c = invokeGetToastBarComponent(tb); + assertNotNull(c, "ToastBarComponent should be created"); + + // With full-screen safe area (y=0, height=displayHeight), bottom margin = 0 + // so no extra bottom padding should be applied. + // The default UIID may have some small padding, but it should be well below + // any safe area inset value (typically 30-100+ pixels). + int paddingBottom = c.getStyle().getPaddingBottom(); + assertTrue(paddingBottom < 10, + "Bottom padding should not contain safe area compensation, got: " + paddingBottom); + + c.remove(); + f.putClientProperty("ToastBarComponent", null); + } + + /** + * Verifies that the top padding equals the full safe area Y when the ToastBar's + * parent starts at absolute Y = 0 (e.g. no toolbar, fullscreen layered pane). + */ + @FormTest + void testTopPositionFullPaddingWhenParentAtOrigin() throws Exception { + int safeTop = 80; + implementation.setDisplaySafeArea(new Rectangle(0, safeTop, 1080, 1920 - safeTop)); + + ToastBar tb = ToastBar.getInstance(); + // Use the form layered pane which overlays the full form from Y=0 + tb.useFormLayeredPane(true); + tb.setPosition(Component.TOP); + + Form f = Display.getInstance().getCurrent(); + f.revalidate(); + + Container c = invokeGetToastBarComponent(tb); + assertNotNull(c, "ToastBarComponent should be created"); + + Container parent = c.getParent(); + assertNotNull(parent, "ToastBarComponent should have a parent"); + + // FormLayeredPane starts at absolute Y=0, so full safe area padding is needed + if (parent.getAbsoluteY() == 0) { + int paddingTop = c.getStyle().getPaddingTop(); + assertEquals(safeTop, paddingTop, + "Top padding should equal safeArea.getY() when parent is at Y=0"); + } + + c.remove(); + f.putClientProperty("ToastBarComponent", null); + tb.useFormLayeredPane(false); + implementation.setDisplaySafeArea(null); + } } diff --git a/maven/core-unittests/src/test/java/com/codename1/testing/TestCodenameOneImplementation.java b/maven/core-unittests/src/test/java/com/codename1/testing/TestCodenameOneImplementation.java index b8eafc71f7..ce54977d3e 100644 --- a/maven/core-unittests/src/test/java/com/codename1/testing/TestCodenameOneImplementation.java +++ b/maven/core-unittests/src/test/java/com/codename1/testing/TestCodenameOneImplementation.java @@ -93,6 +93,7 @@ public class TestCodenameOneImplementation extends CodenameOneImplementation { private Dimension desktopSize = new Dimension(displayWidth, displayHeight); private Dimension lastWindowSize; private Rectangle windowBounds = new Rectangle(0, 0, displayWidth, displayHeight); + private Rectangle displaySafeArea = null; private int deviceDensity = Display.DENSITY_MEDIUM; private boolean portrait = true; private boolean tablet = false; @@ -1096,6 +1097,7 @@ public void reset() { displayHeight = 1920; desktopSize = new Dimension(displayWidth, displayHeight); windowBounds = new Rectangle(0, 0, displayWidth, displayHeight); + displaySafeArea = null; lastWindowSize = null; nativeTitle = false; softkeyCount = 2; @@ -1126,6 +1128,27 @@ public void setDisplaySize(int width, int height) { this.displayHeight = height; } + /** + * Sets a custom display safe area to simulate devices with notches or safe area insets. + * Pass {@code null} to revert to the default behavior (full display area). + */ + public void setDisplaySafeArea(Rectangle safeArea) { + this.displaySafeArea = safeArea; + } + + @Override + public Rectangle getDisplaySafeArea(Rectangle rect) { + if (displaySafeArea != null) { + if (rect == null) { + rect = new Rectangle(); + } + rect.setBounds(displaySafeArea.getX(), displaySafeArea.getY(), + displaySafeArea.getWidth(), displaySafeArea.getHeight()); + return rect; + } + return super.getDisplaySafeArea(rect); + } + public void setDeviceDensity(int density) { this.deviceDensity = density; } From a54037f0712b1490bc8ee0b7574bc9aacac422ce Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 01:10:28 +0000 Subject: [PATCH 4/7] Address review feedback: extract cleanup helper and replace magic number with constant Agent-Logs-Url: https://github.com/codenameone/CodenameOne/sessions/d40eb36c-4528-4297-97d4-4f8144175502 Co-authored-by: shai-almog <67850168+shai-almog@users.noreply.github.com> --- .../codename1/components/ToastBarTest.java | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/maven/core-unittests/src/test/java/com/codename1/components/ToastBarTest.java b/maven/core-unittests/src/test/java/com/codename1/components/ToastBarTest.java index 245fea1381..cecc4c3f1a 100644 --- a/maven/core-unittests/src/test/java/com/codename1/components/ToastBarTest.java +++ b/maven/core-unittests/src/test/java/com/codename1/components/ToastBarTest.java @@ -16,6 +16,13 @@ class ToastBarTest extends UITestBase { + /** + * Upper bound on the default UIID padding (in pixels). Safe-area compensation + * values are typically 30-100+ px, so anything below this threshold means the + * safe-area code path did not add extra padding. + */ + private static final int MAX_DEFAULT_STYLE_PADDING = 10; + @FormTest void testGetInstanceReturnsSingleton() { ToastBar tb1 = ToastBar.getInstance(); @@ -97,6 +104,21 @@ private Container invokeGetToastBarComponent(ToastBar tb) throws Exception { return (Container) m.invoke(tb, true); } + /** + * Cleans up the ToastBarComponent from the current form and resets the + * implementation's safe area to the default. + */ + private void cleanupToastBar(Container toastBarComponent) { + if (toastBarComponent != null) { + toastBarComponent.remove(); + } + Form f = Display.getInstance().getCurrent(); + if (f != null) { + f.putClientProperty("ToastBarComponent", null); + } + implementation.setDisplaySafeArea(null); + } + /** * Regression test: when position is TOP and the device has a safe area inset * (e.g. notch), the ToastBar should NOT double-count the inset if its parent @@ -135,9 +157,7 @@ void testTopPositionNoPaddingWhenParentBelowSafeArea() throws Exception { "Top padding should equal safeArea.getY() - parent.getAbsoluteY()"); } - c.remove(); - f.putClientProperty("ToastBarComponent", null); - implementation.setDisplaySafeArea(null); + cleanupToastBar(c); } /** @@ -161,11 +181,10 @@ void testTopPositionNoPaddingWithoutSafeAreaInset() throws Exception { // The default UIID may have some small padding, but it should be well below // any safe area inset value. int paddingTop = c.getStyle().getPaddingTop(); - assertTrue(paddingTop < 10, + assertTrue(paddingTop < MAX_DEFAULT_STYLE_PADDING, "Top padding should not contain safe area compensation when no inset, got: " + paddingTop); - c.remove(); - f.putClientProperty("ToastBarComponent", null); + cleanupToastBar(c); } /** @@ -192,9 +211,7 @@ void testBottomPositionPaddingWithSafeAreaInset() throws Exception { assertEquals(expectedBottomPadding, s.getPaddingBottom(), "Bottom padding should equal the safe area bottom margin"); - c.remove(); - f.putClientProperty("ToastBarComponent", null); - implementation.setDisplaySafeArea(null); + cleanupToastBar(c); } /** @@ -216,14 +233,11 @@ void testBottomPositionNoPaddingWithoutSafeAreaInset() throws Exception { // With full-screen safe area (y=0, height=displayHeight), bottom margin = 0 // so no extra bottom padding should be applied. - // The default UIID may have some small padding, but it should be well below - // any safe area inset value (typically 30-100+ pixels). int paddingBottom = c.getStyle().getPaddingBottom(); - assertTrue(paddingBottom < 10, + assertTrue(paddingBottom < MAX_DEFAULT_STYLE_PADDING, "Bottom padding should not contain safe area compensation, got: " + paddingBottom); - c.remove(); - f.putClientProperty("ToastBarComponent", null); + cleanupToastBar(c); } /** @@ -256,9 +270,7 @@ void testTopPositionFullPaddingWhenParentAtOrigin() throws Exception { "Top padding should equal safeArea.getY() when parent is at Y=0"); } - c.remove(); - f.putClientProperty("ToastBarComponent", null); + cleanupToastBar(c); tb.useFormLayeredPane(false); - implementation.setDisplaySafeArea(null); } } From ba52a647767ba3a475089e5400299bdb9a6ed141 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 03:49:48 +0000 Subject: [PATCH 5/7] Add ToastBar TOP position screenshot test for visual regression Add ToastBarTopPositionScreenshotTest that shows a ToastBar message at the TOP position to visually verify no spurious empty space appears above the toast text. Register it in Cn1ssDeviceRunner. Agent-Logs-Url: https://github.com/codenameone/CodenameOne/sessions/eaa2d20f-fd1b-44cd-b3b8-fda8d8155d7a Co-authored-by: shai-almog <67850168+shai-almog@users.noreply.github.com> --- .../tests/Cn1ssDeviceRunner.java | 1 + .../ToastBarTopPositionScreenshotTest.java | 51 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/ToastBarTopPositionScreenshotTest.java diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java index 6c9a6365be..90f7ddaa72 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java @@ -73,6 +73,7 @@ public final class Cn1ssDeviceRunner extends DeviceRunner { new TabsScreenshotTest(), new TextAreaAlignmentScreenshotTest(), new ValidatorLightweightPickerScreenshotTest(), + new ToastBarTopPositionScreenshotTest(), // Keep this as the last screenshot test; orientation changes can leak into subsequent screenshots. new OrientationLockScreenshotTest(), new InPlaceEditViewTest(), diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/ToastBarTopPositionScreenshotTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/ToastBarTopPositionScreenshotTest.java new file mode 100644 index 0000000000..3318b1470e --- /dev/null +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/ToastBarTopPositionScreenshotTest.java @@ -0,0 +1,51 @@ +package com.codenameone.examples.hellocodenameone.tests; + +import com.codename1.components.ToastBar; +import com.codename1.ui.Component; +import com.codename1.ui.Container; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.util.UITimer; + +/** + * Screenshot test for ToastBar positioned at {@link Component#TOP}. + * + *
This verifies the fix for the issue where {@code ToastBar} with + * {@code setPosition(Component.TOP)} rendered spurious empty space above + * the message text because the safe-area inset was double-counted when + * the layered-pane parent was already below the safe-area boundary.
+ */ +public class ToastBarTopPositionScreenshotTest extends BaseTest { + private Form form; + private int originalPosition; + + @Override + public boolean runTest() { + originalPosition = ToastBar.getInstance().getPosition(); + + form = createForm("ToastBar Top", new BorderLayout(), "ToastBarTopPosition"); + + Container content = new Container(BoxLayout.y()); + content.add(new Label("ToastBar at TOP position")); + content.add(new Label("No empty space should appear above the toast")); + form.add(BorderLayout.CENTER, content); + + form.show(); + return true; + } + + @Override + protected void registerReadyCallback(Form parent, Runnable run) { + ToastBar tb = ToastBar.getInstance(); + tb.setPosition(Component.TOP); + + // Use a long timeout so the toast stays visible for the screenshot + ToastBar.showMessage("Info message at top", FontImage.MATERIAL_INFO, 30000); + + // Wait for the toast animation to complete before taking the screenshot + UITimer.timer(2000, false, parent, run); + } +} From 09f750f6778b0bb172785f173b43c42282b11265 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 8 Apr 2026 07:34:08 +0300 Subject: [PATCH 6/7] Add files via upload Signed-off-by: Shai Almog <67850168+shai-almog@users.noreply.github.com> --- .../android/screenshots/ToastBarTopPosition.png | Bin 0 -> 14843 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 scripts/android/screenshots/ToastBarTopPosition.png diff --git a/scripts/android/screenshots/ToastBarTopPosition.png b/scripts/android/screenshots/ToastBarTopPosition.png new file mode 100644 index 0000000000000000000000000000000000000000..31d21bf6ca76d1333fa8570c10edfa40b1cbc994 GIT binary patch literal 14843 zcmeHuXHZmIw{4pvdJO0h5RVEXSrG(@CR9Ljl6(wElANSTW)wuUB?t&e&NPxUDj*61 zl9h}|Y?_?o9sAtx{do1NUfsIC?ydU#1GL?Huf5isV~#QA>}N^}ml?J)Z6%RN3^G?P zs*p%)&XP!L?`__QSLiRpAL7RvTb0Y_Nh#GVgZN{;?Rgos&G_fC`PNes=`WJZ#dB(R z-_qNi?y5C*ua4K;I(&Hj+AS|1v!Bc265E^;emwEPJ89mhY84R?Wn7+3p1fRYdpR}! zXxMs<@sFYhfBj|L8*5nm;K7&O4xfq)4Za9$$t{+et2RFtcf4n2E12C)q?9H5sY9h10`q?y;xWA#vNL%g 9{4wUV$6Ry-0rSxVI>l-pYKGr4ikny^9isYH+&R&k2rO`kV@G&m{nHj@Iu)
zGO%PtS4BU)`OVWqv8OpU;;1`AQa{v{7x1{lF8B7NK34T0N=qyJpPRWUb0(yk4e)Hf
zlw?p@RiZ7s7#8@wMBPsVD9H%wkd}j*G;T>ORWn`J99C2hH?`F1+pA5%iU+eJS~3w0
zi|M`9KfwP+KKsyA$z^1Srvkn(6gME?j57X0DAuJ-VvhqF=BkN&@>}j79B(F8zzj@_
zUuaF=a;(bxz7R)OW$wQaGG6Iy)Y$%=n%BgL(Ao%oF&aj<+Sd$a(d&3)O0AOmW#7J$
z@LGI%jna?11CIq2G2W}8!^FG`o+jq*TD}kwPKyob$91hB!u5&%g2swN&y|kqW
z1q25q9wWNSTi|1!p8V59d&)a61w=S+;V7Hv#%@6;nvAV{#td|1_RC6goZaO_iJxJl
zcHPx2(gr<
cA4tS!bnEqcfkCE6M!tDR1H{mR|4Rnw#0elYRe-uKa52kw3lV00
z?H7o3SLR%zcdONdWVoO2Piz&dzA3lCvSd7ER|x=yutx?AK{j`|Ko2~_o>H=o-imHN
zdUIGqFpdZDncAX~p7BF;G0uBwG4}3Xq)+89fS`|06B%PMvBLQ5KJ>7TeAcT*?Gf$(
zBa@``VY=9kRE@X;uLp{1H?3554RDuW6asR&w!diEK%a24WwQhlpvs)F71gcKQdClh
z6mf)bC+;jEuuY2MV0`(!Q}Bq{Lo#ZGZ-QZb#LqCvXo;fTGu`T=co0pS2p4(7GT$`N
zp{u~6?qXogvOR^Z72CWa#JaruG;vYRb30!K%kD061K5O~vfsxmG_`oIGE`!LRLK-|
zI=~Pf0mG5}K}Z}bF99a+vDlN57avS@A<$fZL!?!}VshM9FHdoN&w%v^rZ-qvq_k?l
zE?*YvOq1SY8_G^x1fH%xU^$!f`kOw1CRgC-Y>wJ##!c~YWMC<;#J
zts%
w7>5gB+_ik)ZPNgobx57qtrJ)6R#Z0F`&Q)0EhpJXkO&er?b1O(0
zU##|>BA5khz6eF7W<)>m2%Mg7-769t3lx~*;{=(4|Mf(QSIsO_YvuL<_N{VZfHC=P7jA?F2Uv
z8R~Kgfcm@KFyW_+N;Y#duvu5?aysR7XM*noKa6(#K(qrf#r1Q>v(tBIzc8t0!sU?I
z>EbLqESCXE^N1PPQhEEb_M=nz1?H4RTYDX@QLc`Ewp<#