From 00e5b1f512cffd6b551f99d2412a46d7a66d90b9 Mon Sep 17 00:00:00 2001 From: Seigo Nonaka Date: Wed, 22 Feb 2023 15:04:13 +0900 Subject: [PATCH] Adjust icon spacing for text alignment Bug: 240385199 Test: atest TextViewContextMenuTest Test: manually verified Change-Id: I375d4069fb0e5c6da0115574cd839045652af22c --- core/java/android/widget/Editor.java | 39 ++++++++++ .../widget/TextViewContextMenuTest.java | 73 +++++++++++++++++++ 2 files changed, 112 insertions(+) diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index b89dd31725b7b..9f9a7815932cd 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -52,6 +52,7 @@ import android.graphics.RectF; import android.graphics.RenderNode; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; import android.os.Build; import android.os.Bundle; import android.os.LocaleList; @@ -3238,6 +3239,44 @@ public class Editor { .setOnMenuItemClickListener(mOnContextMenuItemClickListener); mPreserveSelection = true; + + // No-op for the old context menu because it doesn't have icons. + adjustIconSpacing(menu); + } + + /** + * Adjust icon spacing to align the texts. + * @hide + */ + @VisibleForTesting + public void adjustIconSpacing(ContextMenu menu) { + int width = -1; + int height = -1; + for (int i = 0; i < menu.size(); ++i) { + final MenuItem item = menu.getItem(i); + final Drawable d = item.getIcon(); + if (d == null) { + continue; + } + + width = Math.max(width, d.getIntrinsicWidth()); + height = Math.max(height, d.getIntrinsicHeight()); + } + + if (width < 0 || height < 0) { + return; // No menu has icon drawable. + } + + GradientDrawable paddingDrawable = new GradientDrawable(); + paddingDrawable.setSize(width, height); + + for (int i = 0; i < menu.size(); ++i) { + final MenuItem item = menu.getItem(i); + final Drawable d = item.getIcon(); + if (d == null) { + item.setIcon(paddingDrawable); + } + } } @Nullable diff --git a/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java b/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java index 0c7550e79cfa5..777246b83a577 100644 --- a/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java +++ b/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java @@ -33,6 +33,8 @@ import android.app.Activity; import android.app.PendingIntent; import android.app.RemoteAction; import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.Icon; import android.view.ContextMenu; import android.view.MenuItem; @@ -167,4 +169,75 @@ public class TextViewContextMenuTest { assertThat(idCaptor.getValue()).isEqualTo(TextView.ID_ASSIST); assertThat(titleCaptor.getValue().toString()).isEqualTo(ACTION_TITLE); } + + @UiThreadTest + @Test + public void testAdjustIconSpaces() { + GradientDrawable gd = new GradientDrawable(); + gd.setSize(128, 256); + + // Setup mocks + ContextMenu menu = mock(ContextMenu.class); + + MenuItem mockIconMenu = newMockMenuItem(); + when(mockIconMenu.getIcon()).thenReturn(gd); + + MenuItem mockNoIconMenu = newMockMenuItem(); + when(mockNoIconMenu.getIcon()).thenReturn(null); + + MenuItem mockNoIconMenu2 = newMockMenuItem(); + when(mockNoIconMenu2.getIcon()).thenReturn(null); + + when(menu.size()).thenReturn(3); + when(menu.getItem(0)).thenReturn(mockIconMenu); + when(menu.getItem(1)).thenReturn(mockNoIconMenu); + when(menu.getItem(2)).thenReturn(mockNoIconMenu2); + + + // Execute the test method + EditText et = mActivity.findViewById(R.id.editText); + Editor editor = et.getEditorForTesting(); + editor.adjustIconSpacing(menu); + + // Verify + ArgumentCaptor drawableCaptor = ArgumentCaptor.forClass(Drawable.class); + verify(mockNoIconMenu).setIcon(drawableCaptor.capture()); + + Drawable paddingDrawable = drawableCaptor.getValue(); + assertThat(paddingDrawable).isNotNull(); + assertThat(paddingDrawable.getIntrinsicWidth()).isEqualTo(128); + assertThat(paddingDrawable.getIntrinsicHeight()).isEqualTo(256); + + ArgumentCaptor drawableCaptor2 = ArgumentCaptor.forClass(Drawable.class); + verify(mockNoIconMenu2).setIcon(drawableCaptor2.capture()); + + Drawable paddingDrawable2 = drawableCaptor2.getValue(); + assertThat(paddingDrawable2).isSameInstanceAs(paddingDrawable); + } + + @UiThreadTest + @Test + public void testAdjustIconSpacesNoIconCase() { + // Setup mocks + ContextMenu menu = mock(ContextMenu.class); + + MenuItem mockNoIconMenu = newMockMenuItem(); + when(mockNoIconMenu.getIcon()).thenReturn(null); + + MenuItem mockNoIconMenu2 = newMockMenuItem(); + when(mockNoIconMenu2.getIcon()).thenReturn(null); + + when(menu.size()).thenReturn(2); + when(menu.getItem(0)).thenReturn(mockNoIconMenu); + when(menu.getItem(1)).thenReturn(mockNoIconMenu2); + + // Execute the test method + EditText et = mActivity.findViewById(R.id.editText); + Editor editor = et.getEditorForTesting(); + editor.adjustIconSpacing(menu); + + // Verify + verify(mockNoIconMenu, times(0)).setIcon(any()); + verify(mockNoIconMenu2, times(0)).setIcon(any()); + } }