Loading services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickScrollPanel.java +46 −18 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import com.android.internal.R; import com.android.internal.policy.SystemBarUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; Loading Loading @@ -89,6 +90,8 @@ public class AutoclickScrollPanel { private final ImageButton mRightButton; private final ImageButton mExitButton; private final int mStatusBarHeight; private boolean mInScrollMode = false; // Panel size determined after measuring. Loading Loading @@ -122,6 +125,7 @@ public class AutoclickScrollPanel { mContentView = (AutoclickLinearLayout) LayoutInflater.from(context).inflate( R.layout.accessibility_autoclick_scroll_panel, null); mParams = getDefaultLayoutParams(); mStatusBarHeight = SystemBarUtils.getStatusBarHeight(context); // Initialize buttons. mUpButton = mContentView.findViewById(R.id.scroll_up); Loading Loading @@ -190,8 +194,7 @@ public class AutoclickScrollPanel { /** * Positions the panel at the bottom right of the cursor coordinates, * ensuring it stays within the screen boundaries. * If the panel would go off the right or bottom edge, it's repositioned * to the left or above the cursor, respectively. * If the panel would go off the right or bottom edge, tries other diagonal directions. * The panel's gravity is set to TOP|LEFT for absolute positioning. */ protected void positionPanelAtCursor(float cursorX, float cursorY) { Loading @@ -204,24 +207,34 @@ public class AutoclickScrollPanel { int screenWidth = displayMetrics.widthPixels; int screenHeight = displayMetrics.heightPixels; // Calculate initial position. int panelX = (int) cursorX; int panelY = (int) cursorY; // Check if panel would go off right edge of screen. if (panelX + mPanelWidth > screenWidth - PANEL_EDGE_MARGIN) { // Place to the left of cursor instead if no space left for right edge. panelX = (int) cursorX - mPanelWidth; // Adjust Y for status bar height. float adjustedCursorY = cursorY - mStatusBarHeight; // Offset from cursor point to panel center. int margin = 10; int xOffset = mPanelWidth / 2 + margin; int yOffset = mPanelHeight / 2 + margin; // Try 4 diagonal positions: bottom-right, bottom-left, top-right, top-left. int[][] directions = {{+1, +1}, {-1, +1}, {+1, -1}, {-1, -1}}; for (int[] dir : directions) { // (panelX, panelY) is the top-left point of the panel. int panelX = (int) (cursorX + dir[0] * xOffset - mPanelWidth / 2); int panelY = (int) (adjustedCursorY + dir[1] * yOffset - mPanelHeight / 2); if (isWithinBounds(panelX, panelY, screenWidth, screenHeight)) { mParams.x = panelX; mParams.y = panelY; return; } } // Check if panel would go off bottom edge of screen. if (panelY + mPanelHeight > screenHeight - PANEL_EDGE_MARGIN) { // Place above cursor instead if no space left for bottom edge. panelY = (int) cursorY - mPanelHeight; } mParams.x = panelX; mParams.y = panelY; /** * Returns true if the panel fits on screen with margin. */ private boolean isWithinBounds(int x, int y, int screenWidth, int screenHeight) { return x > PANEL_EDGE_MARGIN && x + mPanelWidth + PANEL_EDGE_MARGIN < screenWidth && y > PANEL_EDGE_MARGIN && y + mPanelHeight + PANEL_EDGE_MARGIN < screenHeight; } /** Loading Loading @@ -336,4 +349,19 @@ public class AutoclickScrollPanel { public WindowManager.LayoutParams getLayoutParamsForTesting() { return mParams; } @VisibleForTesting public int getPanelWidthForTesting() { return mPanelWidth; } @VisibleForTesting public int getPanelHeightForTesting() { return mPanelHeight; } @VisibleForTesting public int getStatusBarHeightForTesting() { return mStatusBarHeight; } } services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickScrollPanelTest.java +40 −8 Original line number Diff line number Diff line Loading @@ -274,17 +274,25 @@ public class AutoclickScrollPanelTest { @Test public void showPanel_normalCase() { // Normal case, position at (10, 10). int cursorX = 10; int cursorY = 10; // Normal case, position at (100, 100). int cursorX = 100; int cursorY = 100; // Capture the current layout params before positioning. WindowManager.LayoutParams params = mScrollPanel.getLayoutParamsForTesting(); mScrollPanel.positionPanelAtCursor(cursorX, cursorY); // Panel should be at cursor position (gravity is LEFT|TOP). assertThat(params.x).isEqualTo(cursorX); assertThat(params.y).isEqualTo(cursorY); // Calculate expected position for bottom-right placement. int margin = 10; int xOffset = mScrollPanel.getPanelWidthForTesting() / 2 + margin; int yOffset = mScrollPanel.getPanelHeightForTesting() / 2 + margin; int expectedX = cursorX + xOffset - mScrollPanel.getPanelWidthForTesting() / 2; int expectedY = (cursorY - mScrollPanel.getStatusBarHeightForTesting()) + yOffset - mScrollPanel.getPanelHeightForTesting() / 2; // Verify panel's position. assertThat(params.x).isEqualTo(expectedX); assertThat(params.y).isEqualTo(expectedY); } @Test Loading Loading @@ -316,7 +324,7 @@ public class AutoclickScrollPanelTest { mScrollPanel.positionPanelAtCursor(cursorX, cursorY); // Panel should be above cursor. assertThat(params.y).isLessThan(cursorY); assertThat(params.y).isLessThan(cursorY - mScrollPanel.getStatusBarHeightForTesting()); } @Test Loading @@ -333,7 +341,31 @@ public class AutoclickScrollPanelTest { // Panel should be left of and above cursor. assertThat(params.x).isLessThan(cursorX); assertThat(params.y).isLessThan(cursorY); assertThat(params.y).isLessThan(cursorY - mScrollPanel.getStatusBarHeightForTesting()); } @Test public void showPanel_closeToEdge_withinBounds() { // Test edge case where cursor is very close to edge, panel should still be positioned // within PANEL_EDGE_MARGIN (15px). int edgeMargin = 15; // Near bottom-right corner case. // 10px from right edge. int cursorX = mScreenWidth - 10; // 10px from bottom edge. int cursorY = mScreenHeight - 10; WindowManager.LayoutParams params = mScrollPanel.getLayoutParamsForTesting(); mScrollPanel.positionPanelAtCursor(cursorX, cursorY); // Verify panel is within bounds with margin. assertThat(params.x).isGreaterThan(edgeMargin); assertThat(params.y).isGreaterThan(edgeMargin); assertThat(params.x + mScrollPanel.getPanelWidthForTesting() + edgeMargin) .isLessThan(mScreenWidth); assertThat(params.y + mScrollPanel.getPanelHeightForTesting() + edgeMargin) .isLessThan(mScreenHeight); } @Test Loading Loading
services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickScrollPanel.java +46 −18 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import com.android.internal.R; import com.android.internal.policy.SystemBarUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; Loading Loading @@ -89,6 +90,8 @@ public class AutoclickScrollPanel { private final ImageButton mRightButton; private final ImageButton mExitButton; private final int mStatusBarHeight; private boolean mInScrollMode = false; // Panel size determined after measuring. Loading Loading @@ -122,6 +125,7 @@ public class AutoclickScrollPanel { mContentView = (AutoclickLinearLayout) LayoutInflater.from(context).inflate( R.layout.accessibility_autoclick_scroll_panel, null); mParams = getDefaultLayoutParams(); mStatusBarHeight = SystemBarUtils.getStatusBarHeight(context); // Initialize buttons. mUpButton = mContentView.findViewById(R.id.scroll_up); Loading Loading @@ -190,8 +194,7 @@ public class AutoclickScrollPanel { /** * Positions the panel at the bottom right of the cursor coordinates, * ensuring it stays within the screen boundaries. * If the panel would go off the right or bottom edge, it's repositioned * to the left or above the cursor, respectively. * If the panel would go off the right or bottom edge, tries other diagonal directions. * The panel's gravity is set to TOP|LEFT for absolute positioning. */ protected void positionPanelAtCursor(float cursorX, float cursorY) { Loading @@ -204,24 +207,34 @@ public class AutoclickScrollPanel { int screenWidth = displayMetrics.widthPixels; int screenHeight = displayMetrics.heightPixels; // Calculate initial position. int panelX = (int) cursorX; int panelY = (int) cursorY; // Check if panel would go off right edge of screen. if (panelX + mPanelWidth > screenWidth - PANEL_EDGE_MARGIN) { // Place to the left of cursor instead if no space left for right edge. panelX = (int) cursorX - mPanelWidth; // Adjust Y for status bar height. float adjustedCursorY = cursorY - mStatusBarHeight; // Offset from cursor point to panel center. int margin = 10; int xOffset = mPanelWidth / 2 + margin; int yOffset = mPanelHeight / 2 + margin; // Try 4 diagonal positions: bottom-right, bottom-left, top-right, top-left. int[][] directions = {{+1, +1}, {-1, +1}, {+1, -1}, {-1, -1}}; for (int[] dir : directions) { // (panelX, panelY) is the top-left point of the panel. int panelX = (int) (cursorX + dir[0] * xOffset - mPanelWidth / 2); int panelY = (int) (adjustedCursorY + dir[1] * yOffset - mPanelHeight / 2); if (isWithinBounds(panelX, panelY, screenWidth, screenHeight)) { mParams.x = panelX; mParams.y = panelY; return; } } // Check if panel would go off bottom edge of screen. if (panelY + mPanelHeight > screenHeight - PANEL_EDGE_MARGIN) { // Place above cursor instead if no space left for bottom edge. panelY = (int) cursorY - mPanelHeight; } mParams.x = panelX; mParams.y = panelY; /** * Returns true if the panel fits on screen with margin. */ private boolean isWithinBounds(int x, int y, int screenWidth, int screenHeight) { return x > PANEL_EDGE_MARGIN && x + mPanelWidth + PANEL_EDGE_MARGIN < screenWidth && y > PANEL_EDGE_MARGIN && y + mPanelHeight + PANEL_EDGE_MARGIN < screenHeight; } /** Loading Loading @@ -336,4 +349,19 @@ public class AutoclickScrollPanel { public WindowManager.LayoutParams getLayoutParamsForTesting() { return mParams; } @VisibleForTesting public int getPanelWidthForTesting() { return mPanelWidth; } @VisibleForTesting public int getPanelHeightForTesting() { return mPanelHeight; } @VisibleForTesting public int getStatusBarHeightForTesting() { return mStatusBarHeight; } }
services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickScrollPanelTest.java +40 −8 Original line number Diff line number Diff line Loading @@ -274,17 +274,25 @@ public class AutoclickScrollPanelTest { @Test public void showPanel_normalCase() { // Normal case, position at (10, 10). int cursorX = 10; int cursorY = 10; // Normal case, position at (100, 100). int cursorX = 100; int cursorY = 100; // Capture the current layout params before positioning. WindowManager.LayoutParams params = mScrollPanel.getLayoutParamsForTesting(); mScrollPanel.positionPanelAtCursor(cursorX, cursorY); // Panel should be at cursor position (gravity is LEFT|TOP). assertThat(params.x).isEqualTo(cursorX); assertThat(params.y).isEqualTo(cursorY); // Calculate expected position for bottom-right placement. int margin = 10; int xOffset = mScrollPanel.getPanelWidthForTesting() / 2 + margin; int yOffset = mScrollPanel.getPanelHeightForTesting() / 2 + margin; int expectedX = cursorX + xOffset - mScrollPanel.getPanelWidthForTesting() / 2; int expectedY = (cursorY - mScrollPanel.getStatusBarHeightForTesting()) + yOffset - mScrollPanel.getPanelHeightForTesting() / 2; // Verify panel's position. assertThat(params.x).isEqualTo(expectedX); assertThat(params.y).isEqualTo(expectedY); } @Test Loading Loading @@ -316,7 +324,7 @@ public class AutoclickScrollPanelTest { mScrollPanel.positionPanelAtCursor(cursorX, cursorY); // Panel should be above cursor. assertThat(params.y).isLessThan(cursorY); assertThat(params.y).isLessThan(cursorY - mScrollPanel.getStatusBarHeightForTesting()); } @Test Loading @@ -333,7 +341,31 @@ public class AutoclickScrollPanelTest { // Panel should be left of and above cursor. assertThat(params.x).isLessThan(cursorX); assertThat(params.y).isLessThan(cursorY); assertThat(params.y).isLessThan(cursorY - mScrollPanel.getStatusBarHeightForTesting()); } @Test public void showPanel_closeToEdge_withinBounds() { // Test edge case where cursor is very close to edge, panel should still be positioned // within PANEL_EDGE_MARGIN (15px). int edgeMargin = 15; // Near bottom-right corner case. // 10px from right edge. int cursorX = mScreenWidth - 10; // 10px from bottom edge. int cursorY = mScreenHeight - 10; WindowManager.LayoutParams params = mScrollPanel.getLayoutParamsForTesting(); mScrollPanel.positionPanelAtCursor(cursorX, cursorY); // Verify panel is within bounds with margin. assertThat(params.x).isGreaterThan(edgeMargin); assertThat(params.y).isGreaterThan(edgeMargin); assertThat(params.x + mScrollPanel.getPanelWidthForTesting() + edgeMargin) .isLessThan(mScreenWidth); assertThat(params.y + mScrollPanel.getPanelHeightForTesting() + edgeMargin) .isLessThan(mScreenHeight); } @Test Loading