Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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'
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) }
}
}
}
Expand Down