Use ICU to format plural strings
Test: manual
Fix: 199230208
Change-Id: I0b6fe9f8bb134a1479117c832575c63da2a07794
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a6105eb..323c5ca 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -67,17 +67,15 @@
button in a dialog. [CHAR_LIMIT=none] -->
<string name="added_to_home_screen_accessibility_text"><xliff:g id="widget_name" example="Calendar month view">%1$s</xliff:g> widget added to home screen</string>
<!-- Label for showing the number of widgets an app has in the full widgets picker.
- [CHAR_LIMIT=25] -->
- <plurals name="widgets_count">
- <item quantity="one"><xliff:g id="widgets_count" example="1">%1$d</xliff:g> widget</item>
- <item quantity="other"><xliff:g id="widgets_count" example="2">%1$d</xliff:g> widgets</item>
- </plurals>
+ [CHAR_LIMIT=25][ICU SYNTAX] -->
+ <string name="widgets_count">
+ {count, plural, =1{# widget} other{# widgets}}
+ </string>
<!-- Label for showing the number of shortcut an app has in the full widgets picker.
- [CHAR_LIMIT=25] -->
- <plurals name="shortcuts_count">
- <item quantity="one"><xliff:g id="shortcuts_count" example="1">%1$d</xliff:g> shortcut</item>
- <item quantity="other"><xliff:g id="shortcuts_count" example="2">%1$d</xliff:g> shortcuts</item>
- </plurals>
+ [CHAR_LIMIT=25][ICU SYNTAX] -->
+ <string name="shortcuts_count">
+ {count, plural, =1{# shortcut} other{# shortcuts}}
+ </string>
<!-- Label for showing both the number of widgets and shortcuts an app has in the full widgets
picker. [CHAR_LIMIT=50] -->
<string name="widgets_and_shortcuts_count"><xliff:g id="widgets_count" example="5 widgets">%1$s</xliff:g>, <xliff:g id="shortcuts_count" example="1 shortcut">%2$s</xliff:g></string>
@@ -212,11 +210,13 @@
<!-- Accessibility -->
<!-- The format string for when an app is temporarily disabled. -->
<string name="disabled_app_label">Disabled <xliff:g id="app_name" example="Messenger">%1$s</xliff:g></string>
- <!-- The format string for when an app has a notification dot (meaning it has associated notifications). -->
- <plurals name="dotted_app_label">
- <item quantity="one"><xliff:g id="app_name" example="Messenger">%1$s</xliff:g>, has <xliff:g id="notification_count" example="1">%2$d</xliff:g> notification</item>
- <item quantity="other"><xliff:g id="app_name" example="Messenger">%1$s</xliff:g>, has <xliff:g id="notification_count" example="3">%2$d</xliff:g> notifications</item>
- </plurals>
+ <!-- The format string for when an app has a notification dot (meaning it has associated notifications). [ICU_FORMAT]-->
+ <string name="dotted_app_label">
+ {count, plural, offset:1
+ =1 {{app_name} has # notification}
+ other {{app_name} has # notifications}
+ }
+ </string>
<skip />
<!-- The format string for default page scroll text [CHAR_LIMIT=none] -->
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 54920e1..f6c58c4 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -32,6 +32,7 @@
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.icu.text.MessageFormat;
import android.text.TextUtils.TruncateAt;
import android.util.AttributeSet;
import android.util.Property;
@@ -68,6 +69,8 @@
import com.android.launcher3.views.IconLabelDotView;
import java.text.NumberFormat;
+import java.util.HashMap;
+import java.util.Locale;
/**
* TextView that draws a bubble behind the text. We cannot use a LineBackgroundSpan
@@ -695,8 +698,8 @@
itemInfo.contentDescription));
} else if (hasDot()) {
int count = mDotInfo.getNotificationCount();
- setContentDescription(getContext().getResources().getQuantityString(
- R.plurals.dotted_app_label, count, itemInfo.contentDescription, count));
+ setContentDescription(
+ getAppLabelPluralString(itemInfo.contentDescription.toString(), count));
} else {
setContentDescription(itemInfo.contentDescription);
}
@@ -938,4 +941,14 @@
setCompoundDrawables(null, newIcon, null, null);
}
}
+
+ private String getAppLabelPluralString(String appName, int notificationCount) {
+ MessageFormat icuCountFormat = new MessageFormat(
+ getResources().getString(R.string.dotted_app_label),
+ Locale.getDefault());
+ HashMap<String, Object> args = new HashMap();
+ args.put("app_name", appName);
+ args.put("count", notificationCount);
+ return icuCountFormat.format(args);
+ }
}
diff --git a/src/com/android/launcher3/util/PluralMessageFormat.java b/src/com/android/launcher3/util/PluralMessageFormat.java
new file mode 100644
index 0000000..5e4ce8d
--- /dev/null
+++ b/src/com/android/launcher3/util/PluralMessageFormat.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import android.content.Context;
+import android.icu.text.MessageFormat;
+
+import androidx.annotation.StringRes;
+
+import java.util.HashMap;
+import java.util.Locale;
+
+/** A helper class to format common ICU plural strings. */
+public class PluralMessageFormat {
+
+ /**
+ * Returns a plural string from a ICU format message template, which takes "count" as an
+ * argument.
+ *
+ * <p>An example of ICU format message template provided by {@code stringId}:
+ * {count, plural, =1{# widget} other{# widgets}}
+ */
+ public static final String getIcuPluralString(Context context, @StringRes int stringId,
+ int count) {
+ MessageFormat icuCountFormat = new MessageFormat(
+ context.getResources().getString(stringId),
+ Locale.getDefault());
+ HashMap<String, Object> args = new HashMap();
+ args.put("count", count);
+ return icuCountFormat.format(args);
+ }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
index ef2adbb..ebd2d10 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
@@ -39,6 +39,7 @@
import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.util.PluralMessageFormat;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
@@ -217,18 +218,18 @@
String subtitle;
if (entry.widgetsCount > 0 && entry.shortcutsCount > 0) {
- String widgetsCount = resources.getQuantityString(R.plurals.widgets_count,
- entry.widgetsCount, entry.widgetsCount);
- String shortcutsCount = resources.getQuantityString(R.plurals.shortcuts_count,
- entry.shortcutsCount, entry.shortcutsCount);
+ String widgetsCount = PluralMessageFormat.getIcuPluralString(getContext(),
+ R.string.widgets_count, entry.widgetsCount);
+ String shortcutsCount = PluralMessageFormat.getIcuPluralString(getContext(),
+ R.string.shortcuts_count, entry.shortcutsCount);
subtitle = resources.getString(R.string.widgets_and_shortcuts_count, widgetsCount,
shortcutsCount);
} else if (entry.widgetsCount > 0) {
- subtitle = resources.getQuantityString(R.plurals.widgets_count,
- entry.widgetsCount, entry.widgetsCount);
+ subtitle = PluralMessageFormat.getIcuPluralString(getContext(),
+ R.string.widgets_count, entry.widgetsCount);
} else {
- subtitle = resources.getQuantityString(R.plurals.shortcuts_count,
- entry.shortcutsCount, entry.shortcutsCount);
+ subtitle = PluralMessageFormat.getIcuPluralString(getContext(),
+ R.string.shortcuts_count, entry.shortcutsCount);
}
mSubtitle.setText(subtitle);
mSubtitle.setVisibility(VISIBLE);