Loading res/layout/all_apps_icon_twoline.xml +0 −1 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ android:id="@+id/icon" android:singleLine="false" android:lines="2" android:inputType="textMultiLine" launcher:iconDisplay="all_apps" launcher:centerVertically="true" /> src/com/android/launcher3/BubbleTextView.java +118 −3 Original line number Diff line number Diff line Loading @@ -52,8 +52,10 @@ import android.widget.TextView; import androidx.annotation.Nullable; import androidx.annotation.UiThread; import androidx.annotation.VisibleForTesting; import com.android.launcher3.accessibility.BaseAccessibilityDelegate; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dot.DotInfo; import com.android.launcher3.dragndrop.DragOptions.PreDragCondition; import com.android.launcher3.dragndrop.DraggableView; Loading @@ -71,6 +73,8 @@ import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.util.MultiTranslateDelegate; import com.android.launcher3.search.StringMatcherUtility; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.SafeCloseable; import com.android.launcher3.util.ShortcutUtil; import com.android.launcher3.views.ActivityContext; Loading @@ -97,11 +101,19 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, private static final float MIN_LETTER_SPACING = -0.05f; private static final int MAX_SEARCH_LOOP_COUNT = 20; private static final Character NEW_LINE = '\n'; private static final String EMPTY = ""; private static final StringMatcherUtility.StringMatcher MATCHER = StringMatcherUtility.StringMatcher.getInstance(); private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed}; private float mScaleForReorderBounce = 1f; private IntArray mBreakPointsIntArray; private CharSequence mLastOriginalText; private CharSequence mLastModifiedText; private static final Property<BubbleTextView, Float> DOT_SCALE_PROPERTY = new Property<BubbleTextView, Float>(Float.TYPE, "dotScale") { @Override Loading Loading @@ -134,7 +146,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, private FastBitmapDrawable mIcon; private boolean mCenterVertically; protected final int mDisplay; protected int mDisplay; private final CheckLongPressHelper mLongPressHelper; Loading Loading @@ -255,6 +267,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, mDotParams.scale = 0f; mForceHideDot = false; setBackground(null); setSingleLine(true); setMaxLines(1); setTag(null); if (mIconLoadRequest != null) { Loading Loading @@ -382,8 +396,15 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, } @UiThread private void applyLabel(ItemInfoWithIcon info) { setText(info.title); @VisibleForTesting public void applyLabel(ItemInfoWithIcon info) { CharSequence label = info.title; if (label != null) { mLastOriginalText = label; mLastModifiedText = mLastOriginalText; mBreakPointsIntArray = StringMatcherUtility.getListOfBreakpoints(label, MATCHER); setText(label); } if (info.contentDescription != null) { setContentDescription(info.isDisabled() ? getContext().getString(R.string.disabled_app_label, info.contentDescription) Loading @@ -391,6 +412,12 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, } } /** This is used for testing to forcefully set the display to ALL_APPS */ @VisibleForTesting public void setDisplayAllApps() { mDisplay = DISPLAY_ALL_APPS; } /** * Overrides the default long press timeout. */ Loading Loading @@ -637,6 +664,27 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, setPadding(getPaddingLeft(), (height - cellHeightPx) / 2, getPaddingRight(), getPaddingBottom()); } // only apply two line for all_apps if (FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get() && (mLastOriginalText != null) && (mDisplay == DISPLAY_ALL_APPS)) { CharSequence modifiedString = modifyTitleToSupportMultiLine( MeasureSpec.getSize(widthMeasureSpec) - getCompoundPaddingLeft() - getCompoundPaddingRight(), mLastOriginalText, getPaint(), mBreakPointsIntArray); if (!TextUtils.equals(modifiedString, mLastModifiedText)) { mLastModifiedText = modifiedString; setText(modifiedString); // if text contains NEW_LINE, set max lines to 2 if (TextUtils.indexOf(modifiedString, NEW_LINE) != -1) { setSingleLine(false); setMaxLines(2); } else { setSingleLine(true); setMaxLines(1); } } } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } Loading Loading @@ -697,6 +745,73 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, return ObjectAnimator.ofFloat(this, TEXT_ALPHA_PROPERTY, toAlpha); } /** * Generate a new string that will support two line text depending on the current string. * This method calculates the limited width of a text view and creates a string to fit as * many words as it can until the limit is reached. Once the limit is reached, we decide to * either return the original title or continue on a new line. How to get the new string is by * iterating through the list of break points and determining if the strings between the break * points can fit within the line it is in. * Example assuming each character takes up one spot: * title = "Battery Stats", breakpoint = [6], stringPtr = 0, limitedWidth = 7 * We get the current word -> from sublist(0, breakpoint[i]+1) so sublist (0,7) -> Battery, * now stringPtr = 7 then from sublist(7) the current string is " Stats" and the runningWidth * at this point exceeds limitedWidth and so we put " Stats" onto the next line (after checking * if the first char is a SPACE, we trim to append "Stats". So resulting string would be * "Battery\nStats" */ public static CharSequence modifyTitleToSupportMultiLine(int limitedWidth, CharSequence title, TextPaint paint, IntArray breakPoints) { // current title is less than the width allowed so we can just skip if (title == null || paint.measureText(title, 0, title.length()) <= limitedWidth) { return title; } float currentWordWidth, runningWidth = 0; CharSequence currentWord; StringBuilder newString = new StringBuilder(); int stringPtr = 0; for (int i = 0; i < breakPoints.size()+1; i++) { if (i < breakPoints.size()) { currentWord = title.subSequence(stringPtr, breakPoints.get(i)+1); } else { // last word from recent breakpoint until the end of the string currentWord = title.subSequence(stringPtr, title.length()); } currentWordWidth = paint.measureText(currentWord,0, currentWord.length()); runningWidth += currentWordWidth; if (runningWidth <= limitedWidth) { newString.append(currentWord); } else { // there is no more space if (i == 0) { // if the first words exceeds width, just return as the first line will ellipse return title; } else { // If putting word onto a new line, make sure there is no space or new line // character in the beginning of the current word and just put in the rest of // the characters. CharSequence lastCharacters = title.subSequence(stringPtr, title.length()); int beginningLetterType = Character.getType(Character.codePointAt(lastCharacters,0)); if (beginningLetterType == Character.SPACE_SEPARATOR || beginningLetterType == Character.LINE_SEPARATOR) { lastCharacters = lastCharacters.length() > 1 ? lastCharacters.subSequence(1, lastCharacters.length()) : EMPTY; } newString.append(NEW_LINE).append(lastCharacters); return newString.toString(); } } if (i >= breakPoints.size()) { // no need to look forward into the string if we've already finished processing break; } stringPtr = breakPoints.get(i)+1; } return newString.toString(); } @Override public void cancelLongPress() { super.cancelLongPress(); Loading src/com/android/launcher3/allapps/BaseAllAppsAdapter.java +5 −3 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.android.launcher3.BubbleTextView; import com.android.launcher3.R; import com.android.launcher3.allapps.search.SearchAdapterProvider; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.views.ActivityContext; Loading Loading @@ -140,7 +141,7 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex protected final OnClickListener mOnIconClickListener; protected OnLongClickListener mOnIconLongClickListener = INSTANCE_ALL_APPS; protected OnFocusChangeListener mIconFocusListener; private final int mExtraHeight; private final int mExtraTextHeight; public BaseAllAppsAdapter(T activityContext, LayoutInflater inflater, AlphabeticalAppsList<T> apps, SearchAdapterProvider<?> adapterProvider) { Loading @@ -152,7 +153,8 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex mOnIconClickListener = mActivityContext.getItemOnClickListener(); mAdapterProvider = adapterProvider; mExtraHeight = res.getDimensionPixelSize(R.dimen.all_apps_height_extra); mExtraTextHeight = Utilities.calculateTextHeight( mActivityContext.getDeviceProfile().allAppsIconTextSizePx); } /** Loading Loading @@ -197,7 +199,7 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex icon.getLayoutParams().height = mActivityContext.getDeviceProfile().allAppsCellHeightPx; if (FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get()) { icon.getLayoutParams().height += mExtraHeight; icon.getLayoutParams().height += mExtraTextHeight; } return new ViewHolder(icon); case VIEW_TYPE_EMPTY_SEARCH: Loading src/com/android/launcher3/config/FeatureFlags.java +4 −0 Original line number Diff line number Diff line Loading @@ -113,6 +113,10 @@ public final class FeatureFlags { public static final BooleanFlag ENABLE_TWOLINE_ALLAPPS = getDebugFlag(270390937, "ENABLE_TWOLINE_ALLAPPS", false, "Enables two line label inside all apps."); public static final BooleanFlag ENABLE_TWOLINE_DEVICESEARCH = getDebugFlag(201388851, "ENABLE_TWOLINE_DEVICESEARCH", false, "Enable two line label for icons with labels on device search."); public static final BooleanFlag ENABLE_DEVICE_SEARCH_PERFORMANCE_LOGGING = getReleaseFlag( 270391397, "ENABLE_DEVICE_SEARCH_PERFORMANCE_LOGGING", false, "Allows on device search in all apps logging"); Loading src/com/android/launcher3/search/StringMatcherUtility.java +47 −1 Original line number Diff line number Diff line Loading @@ -16,13 +16,20 @@ package com.android.launcher3.search; import android.text.TextUtils; import com.android.launcher3.util.IntArray; import java.text.Collator; import java.util.stream.IntStream; /** * Utilities for matching query string to target string. */ public class StringMatcherUtility { private static final Character SPACE = ' '; /** * Returns {@code true} if {@code query} is a prefix of a substring in {@code target}. How to * break target to valid substring is defined in the given {@code matcher}. Loading Loading @@ -58,6 +65,41 @@ public class StringMatcherUtility { return false; } /** * Returns a list of breakpoints wherever the string contains a break. For example: * "t-mobile" would have breakpoints at [0, 1] * "Agar.io" would have breakpoints at [3, 4] * "LEGO®Builder" would have a breakpoint at [4] */ public static IntArray getListOfBreakpoints(CharSequence input, StringMatcher matcher) { int inputLength = input.length(); if ((inputLength <= 2) || TextUtils.indexOf(input, SPACE) != -1) { // when there is a space in the string, return a list where the elements are the // position of the spaces - 1. This is to make the logic consistent where breakpoints // are placed return IntArray.wrap(IntStream.range(0, inputLength) .filter(i -> input.charAt(i) == SPACE) .map(i -> i - 1) .toArray()); } IntArray listOfBreakPoints = new IntArray(); int prevType; int thisType = Character.getType(Character.codePointAt(input, 0)); int nextType = Character.getType(Character.codePointAt(input, 1)); for (int i = 1; i < inputLength; i++) { prevType = thisType; thisType = nextType; nextType = i < (inputLength - 1) ? Character.getType(Character.codePointAt(input, i + 1)) : Character.UNASSIGNED; if (matcher.isBreak(thisType, prevType, nextType)) { // breakpoint is at previous listOfBreakPoints.add(i-1); } } return listOfBreakPoints; } /** * Performs locale sensitive string comparison using {@link Collator}. */ Loading Loading @@ -118,7 +160,11 @@ public class StringMatcherUtility { } switch (thisType) { case Character.UPPERCASE_LETTER: if (nextType == Character.UPPERCASE_LETTER) { // takes care of the case where there are consistent uppercase letters as well // as a special symbol following the capitalize letters for example: LEGO® if (nextType != Character.UPPERCASE_LETTER && nextType != Character.OTHER_SYMBOL && nextType != Character.DECIMAL_DIGIT_NUMBER && nextType != Character.UNASSIGNED) { return true; } // Follow through Loading Loading
res/layout/all_apps_icon_twoline.xml +0 −1 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ android:id="@+id/icon" android:singleLine="false" android:lines="2" android:inputType="textMultiLine" launcher:iconDisplay="all_apps" launcher:centerVertically="true" />
src/com/android/launcher3/BubbleTextView.java +118 −3 Original line number Diff line number Diff line Loading @@ -52,8 +52,10 @@ import android.widget.TextView; import androidx.annotation.Nullable; import androidx.annotation.UiThread; import androidx.annotation.VisibleForTesting; import com.android.launcher3.accessibility.BaseAccessibilityDelegate; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dot.DotInfo; import com.android.launcher3.dragndrop.DragOptions.PreDragCondition; import com.android.launcher3.dragndrop.DraggableView; Loading @@ -71,6 +73,8 @@ import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.util.MultiTranslateDelegate; import com.android.launcher3.search.StringMatcherUtility; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.SafeCloseable; import com.android.launcher3.util.ShortcutUtil; import com.android.launcher3.views.ActivityContext; Loading @@ -97,11 +101,19 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, private static final float MIN_LETTER_SPACING = -0.05f; private static final int MAX_SEARCH_LOOP_COUNT = 20; private static final Character NEW_LINE = '\n'; private static final String EMPTY = ""; private static final StringMatcherUtility.StringMatcher MATCHER = StringMatcherUtility.StringMatcher.getInstance(); private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed}; private float mScaleForReorderBounce = 1f; private IntArray mBreakPointsIntArray; private CharSequence mLastOriginalText; private CharSequence mLastModifiedText; private static final Property<BubbleTextView, Float> DOT_SCALE_PROPERTY = new Property<BubbleTextView, Float>(Float.TYPE, "dotScale") { @Override Loading Loading @@ -134,7 +146,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, private FastBitmapDrawable mIcon; private boolean mCenterVertically; protected final int mDisplay; protected int mDisplay; private final CheckLongPressHelper mLongPressHelper; Loading Loading @@ -255,6 +267,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, mDotParams.scale = 0f; mForceHideDot = false; setBackground(null); setSingleLine(true); setMaxLines(1); setTag(null); if (mIconLoadRequest != null) { Loading Loading @@ -382,8 +396,15 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, } @UiThread private void applyLabel(ItemInfoWithIcon info) { setText(info.title); @VisibleForTesting public void applyLabel(ItemInfoWithIcon info) { CharSequence label = info.title; if (label != null) { mLastOriginalText = label; mLastModifiedText = mLastOriginalText; mBreakPointsIntArray = StringMatcherUtility.getListOfBreakpoints(label, MATCHER); setText(label); } if (info.contentDescription != null) { setContentDescription(info.isDisabled() ? getContext().getString(R.string.disabled_app_label, info.contentDescription) Loading @@ -391,6 +412,12 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, } } /** This is used for testing to forcefully set the display to ALL_APPS */ @VisibleForTesting public void setDisplayAllApps() { mDisplay = DISPLAY_ALL_APPS; } /** * Overrides the default long press timeout. */ Loading Loading @@ -637,6 +664,27 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, setPadding(getPaddingLeft(), (height - cellHeightPx) / 2, getPaddingRight(), getPaddingBottom()); } // only apply two line for all_apps if (FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get() && (mLastOriginalText != null) && (mDisplay == DISPLAY_ALL_APPS)) { CharSequence modifiedString = modifyTitleToSupportMultiLine( MeasureSpec.getSize(widthMeasureSpec) - getCompoundPaddingLeft() - getCompoundPaddingRight(), mLastOriginalText, getPaint(), mBreakPointsIntArray); if (!TextUtils.equals(modifiedString, mLastModifiedText)) { mLastModifiedText = modifiedString; setText(modifiedString); // if text contains NEW_LINE, set max lines to 2 if (TextUtils.indexOf(modifiedString, NEW_LINE) != -1) { setSingleLine(false); setMaxLines(2); } else { setSingleLine(true); setMaxLines(1); } } } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } Loading Loading @@ -697,6 +745,73 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, return ObjectAnimator.ofFloat(this, TEXT_ALPHA_PROPERTY, toAlpha); } /** * Generate a new string that will support two line text depending on the current string. * This method calculates the limited width of a text view and creates a string to fit as * many words as it can until the limit is reached. Once the limit is reached, we decide to * either return the original title or continue on a new line. How to get the new string is by * iterating through the list of break points and determining if the strings between the break * points can fit within the line it is in. * Example assuming each character takes up one spot: * title = "Battery Stats", breakpoint = [6], stringPtr = 0, limitedWidth = 7 * We get the current word -> from sublist(0, breakpoint[i]+1) so sublist (0,7) -> Battery, * now stringPtr = 7 then from sublist(7) the current string is " Stats" and the runningWidth * at this point exceeds limitedWidth and so we put " Stats" onto the next line (after checking * if the first char is a SPACE, we trim to append "Stats". So resulting string would be * "Battery\nStats" */ public static CharSequence modifyTitleToSupportMultiLine(int limitedWidth, CharSequence title, TextPaint paint, IntArray breakPoints) { // current title is less than the width allowed so we can just skip if (title == null || paint.measureText(title, 0, title.length()) <= limitedWidth) { return title; } float currentWordWidth, runningWidth = 0; CharSequence currentWord; StringBuilder newString = new StringBuilder(); int stringPtr = 0; for (int i = 0; i < breakPoints.size()+1; i++) { if (i < breakPoints.size()) { currentWord = title.subSequence(stringPtr, breakPoints.get(i)+1); } else { // last word from recent breakpoint until the end of the string currentWord = title.subSequence(stringPtr, title.length()); } currentWordWidth = paint.measureText(currentWord,0, currentWord.length()); runningWidth += currentWordWidth; if (runningWidth <= limitedWidth) { newString.append(currentWord); } else { // there is no more space if (i == 0) { // if the first words exceeds width, just return as the first line will ellipse return title; } else { // If putting word onto a new line, make sure there is no space or new line // character in the beginning of the current word and just put in the rest of // the characters. CharSequence lastCharacters = title.subSequence(stringPtr, title.length()); int beginningLetterType = Character.getType(Character.codePointAt(lastCharacters,0)); if (beginningLetterType == Character.SPACE_SEPARATOR || beginningLetterType == Character.LINE_SEPARATOR) { lastCharacters = lastCharacters.length() > 1 ? lastCharacters.subSequence(1, lastCharacters.length()) : EMPTY; } newString.append(NEW_LINE).append(lastCharacters); return newString.toString(); } } if (i >= breakPoints.size()) { // no need to look forward into the string if we've already finished processing break; } stringPtr = breakPoints.get(i)+1; } return newString.toString(); } @Override public void cancelLongPress() { super.cancelLongPress(); Loading
src/com/android/launcher3/allapps/BaseAllAppsAdapter.java +5 −3 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.android.launcher3.BubbleTextView; import com.android.launcher3.R; import com.android.launcher3.allapps.search.SearchAdapterProvider; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.views.ActivityContext; Loading Loading @@ -140,7 +141,7 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex protected final OnClickListener mOnIconClickListener; protected OnLongClickListener mOnIconLongClickListener = INSTANCE_ALL_APPS; protected OnFocusChangeListener mIconFocusListener; private final int mExtraHeight; private final int mExtraTextHeight; public BaseAllAppsAdapter(T activityContext, LayoutInflater inflater, AlphabeticalAppsList<T> apps, SearchAdapterProvider<?> adapterProvider) { Loading @@ -152,7 +153,8 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex mOnIconClickListener = mActivityContext.getItemOnClickListener(); mAdapterProvider = adapterProvider; mExtraHeight = res.getDimensionPixelSize(R.dimen.all_apps_height_extra); mExtraTextHeight = Utilities.calculateTextHeight( mActivityContext.getDeviceProfile().allAppsIconTextSizePx); } /** Loading Loading @@ -197,7 +199,7 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex icon.getLayoutParams().height = mActivityContext.getDeviceProfile().allAppsCellHeightPx; if (FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get()) { icon.getLayoutParams().height += mExtraHeight; icon.getLayoutParams().height += mExtraTextHeight; } return new ViewHolder(icon); case VIEW_TYPE_EMPTY_SEARCH: Loading
src/com/android/launcher3/config/FeatureFlags.java +4 −0 Original line number Diff line number Diff line Loading @@ -113,6 +113,10 @@ public final class FeatureFlags { public static final BooleanFlag ENABLE_TWOLINE_ALLAPPS = getDebugFlag(270390937, "ENABLE_TWOLINE_ALLAPPS", false, "Enables two line label inside all apps."); public static final BooleanFlag ENABLE_TWOLINE_DEVICESEARCH = getDebugFlag(201388851, "ENABLE_TWOLINE_DEVICESEARCH", false, "Enable two line label for icons with labels on device search."); public static final BooleanFlag ENABLE_DEVICE_SEARCH_PERFORMANCE_LOGGING = getReleaseFlag( 270391397, "ENABLE_DEVICE_SEARCH_PERFORMANCE_LOGGING", false, "Allows on device search in all apps logging"); Loading
src/com/android/launcher3/search/StringMatcherUtility.java +47 −1 Original line number Diff line number Diff line Loading @@ -16,13 +16,20 @@ package com.android.launcher3.search; import android.text.TextUtils; import com.android.launcher3.util.IntArray; import java.text.Collator; import java.util.stream.IntStream; /** * Utilities for matching query string to target string. */ public class StringMatcherUtility { private static final Character SPACE = ' '; /** * Returns {@code true} if {@code query} is a prefix of a substring in {@code target}. How to * break target to valid substring is defined in the given {@code matcher}. Loading Loading @@ -58,6 +65,41 @@ public class StringMatcherUtility { return false; } /** * Returns a list of breakpoints wherever the string contains a break. For example: * "t-mobile" would have breakpoints at [0, 1] * "Agar.io" would have breakpoints at [3, 4] * "LEGO®Builder" would have a breakpoint at [4] */ public static IntArray getListOfBreakpoints(CharSequence input, StringMatcher matcher) { int inputLength = input.length(); if ((inputLength <= 2) || TextUtils.indexOf(input, SPACE) != -1) { // when there is a space in the string, return a list where the elements are the // position of the spaces - 1. This is to make the logic consistent where breakpoints // are placed return IntArray.wrap(IntStream.range(0, inputLength) .filter(i -> input.charAt(i) == SPACE) .map(i -> i - 1) .toArray()); } IntArray listOfBreakPoints = new IntArray(); int prevType; int thisType = Character.getType(Character.codePointAt(input, 0)); int nextType = Character.getType(Character.codePointAt(input, 1)); for (int i = 1; i < inputLength; i++) { prevType = thisType; thisType = nextType; nextType = i < (inputLength - 1) ? Character.getType(Character.codePointAt(input, i + 1)) : Character.UNASSIGNED; if (matcher.isBreak(thisType, prevType, nextType)) { // breakpoint is at previous listOfBreakPoints.add(i-1); } } return listOfBreakPoints; } /** * Performs locale sensitive string comparison using {@link Collator}. */ Loading Loading @@ -118,7 +160,11 @@ public class StringMatcherUtility { } switch (thisType) { case Character.UPPERCASE_LETTER: if (nextType == Character.UPPERCASE_LETTER) { // takes care of the case where there are consistent uppercase letters as well // as a special symbol following the capitalize letters for example: LEGO® if (nextType != Character.UPPERCASE_LETTER && nextType != Character.OTHER_SYMBOL && nextType != Character.DECIMAL_DIGIT_NUMBER && nextType != Character.UNASSIGNED) { return true; } // Follow through Loading