diff --git a/app/src/main/java/com/github/codeworkscreativehub/mlauncher/MainViewModel.kt b/app/src/main/java/com/github/codeworkscreativehub/mlauncher/MainViewModel.kt index 6141a7e13..a5bd0df35 100644 --- a/app/src/main/java/com/github/codeworkscreativehub/mlauncher/MainViewModel.kt +++ b/app/src/main/java/com/github/codeworkscreativehub/mlauncher/MainViewModel.kt @@ -32,6 +32,7 @@ import com.github.codeworkscreativehub.mlauncher.data.Constants.AppDrawerFlag import com.github.codeworkscreativehub.mlauncher.data.ContactCategory import com.github.codeworkscreativehub.mlauncher.data.ContactListItem import com.github.codeworkscreativehub.mlauncher.data.Prefs +import com.github.codeworkscreativehub.mlauncher.helper.ChineseSortHelper import com.github.codeworkscreativehub.mlauncher.helper.analytics.AppUsageMonitor import com.github.codeworkscreativehub.mlauncher.helper.ismlauncherDefault import com.github.codeworkscreativehub.mlauncher.helper.logActivitiesFromPackage @@ -497,7 +498,13 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { list.forEachIndexed { index, item -> val label = getLabel(item) val pinned = pinnedStatusMap[item] == true - val key = if (pinned) "★" else label.firstOrNull()?.uppercaseChar()?.toString() ?: "#" + val key = if (pinned) { + "★" + } else { + ChineseSortHelper.sectionKey(label, prefs.appLanguage) + ?: label.firstOrNull()?.uppercaseChar()?.toString() + ?: "#" + } scrollMap.putIfAbsent(key, index) } @@ -797,10 +804,12 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { // Helper: cheap normalization for sorting // Removes diacritics/unsupported characters cheaply and collapses whitespace. private fun normalizeForSort(s: String): String { + val source = ChineseSortHelper.sortKey(s, prefs.appLanguage) ?: s + // Keep letters, digits and spaces. Collapse multiple spaces. Lowercase using default locale. - val sb = StringBuilder(s.length) + val sb = StringBuilder(source.length) var lastWasSpace = false - for (ch in s) { + for (ch in source) { if (ch.isLetterOrDigit()) { sb.append(ch.lowercaseChar()) lastWasSpace = false diff --git a/app/src/main/java/com/github/codeworkscreativehub/mlauncher/helper/ChineseSortHelper.kt b/app/src/main/java/com/github/codeworkscreativehub/mlauncher/helper/ChineseSortHelper.kt new file mode 100644 index 000000000..9507a7424 --- /dev/null +++ b/app/src/main/java/com/github/codeworkscreativehub/mlauncher/helper/ChineseSortHelper.kt @@ -0,0 +1,63 @@ +package com.github.codeworkscreativehub.mlauncher.helper + +import android.icu.text.Transliterator +import android.os.Build +import androidx.annotation.ChecksSdkIntAtLeast +import com.github.codeworkscreativehub.mlauncher.data.Constants +import java.util.Locale + +object ChineseSortHelper { + @get:ChecksSdkIntAtLeast(api = Build.VERSION_CODES.Q) + private val platformSupported: Boolean + get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q + + /** + * Returns a pinyin-based sort key only when the app language is Chinese, or follows a + * Chinese system language, and the platform supports ICU transliteration. + * Callers should fall back to the original label. + */ + fun sortKey(label: String, language: Constants.Language): String? { + if (!shouldUseChineseSort(language) || !label.startsWithChinese()) return null + + return transliterate(label) + ?.takeIf { it.isNotBlank() } + ?.lowercase(Locale.ROOT) + } + + /** + * Returns a pinyin-based section letter only when the app language is Chinese, or follows a + * Chinese system language, and the platform supports ICU transliteration. + * Callers should fall back to the label's first char. + */ + fun sectionKey(label: String, language: Constants.Language): String? { + if (!shouldUseChineseSort(language)) return null + val firstChar = label.firstOrNull()?.takeIf(::isChinese) ?: return null + + return transliterate(firstChar.toString()) + ?.firstOrNull { it.isLetter() } + ?.uppercaseChar() + ?.takeIf { it in 'A'..'Z' } + ?.toString() + } + + private fun transliterate(label: String): String? { + if (!platformSupported) return null + + return runCatching { + Transliterator.getInstance("Han-Latin; Latin-ASCII").transliterate(label) + }.getOrNull() + } + + private fun shouldUseChineseSort(language: Constants.Language): Boolean = + platformSupported && when (language) { + Constants.Language.Chinese -> true + Constants.Language.System -> Locale.getDefault().language == Locale.CHINESE.language + else -> false + } + + private fun String.startsWithChinese(): Boolean = + firstOrNull()?.let(::isChinese) == true + + private fun isChinese(ch: Char): Boolean = + ch in '\u4E00'..'\u9FFF' +} diff --git a/app/src/main/java/com/github/codeworkscreativehub/mlauncher/ui/AppDrawerFragment.kt b/app/src/main/java/com/github/codeworkscreativehub/mlauncher/ui/AppDrawerFragment.kt index ec5ed16f5..aca13eb2c 100644 --- a/app/src/main/java/com/github/codeworkscreativehub/mlauncher/ui/AppDrawerFragment.kt +++ b/app/src/main/java/com/github/codeworkscreativehub/mlauncher/ui/AppDrawerFragment.kt @@ -52,6 +52,7 @@ import com.github.codeworkscreativehub.mlauncher.data.Constants.AppDrawerFlag import com.github.codeworkscreativehub.mlauncher.data.ContactListItem import com.github.codeworkscreativehub.mlauncher.data.Prefs import com.github.codeworkscreativehub.mlauncher.databinding.FragmentAppDrawerBinding +import com.github.codeworkscreativehub.mlauncher.helper.ChineseSortHelper import com.github.codeworkscreativehub.mlauncher.helper.emptyString import com.github.codeworkscreativehub.mlauncher.helper.getHexForOpacity import com.github.codeworkscreativehub.mlauncher.helper.hasContactsPermission @@ -304,7 +305,11 @@ class AppDrawerFragment : BaseFragment() { val sectionLetter = when (item.category) { AppCategory.PINNED -> "★" - else -> item.activityLabel.firstOrNull()?.uppercaseChar()?.toString() ?: return + else -> { + ChineseSortHelper.sectionKey(item.activityLabel, prefs.appLanguage) + ?: item.activityLabel.firstOrNull()?.uppercaseChar()?.toString() + ?: return + } } // Skip redundant updates @@ -858,10 +863,9 @@ class AppDrawerFragment : BaseFragment() { when (item.category) { AppCategory.PINNED -> letters.add("★") else -> { - item.activityLabel.firstOrNull() - ?.uppercaseChar() - ?.toString() - ?.let { letters.add(it) } + val sectionLetter = ChineseSortHelper.sectionKey(item.activityLabel, prefs.appLanguage) + ?: item.activityLabel.firstOrNull()?.uppercaseChar()?.toString() + sectionLetter?.let { letters.add(it) } } } }