Skip to content
Open
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
25 changes: 24 additions & 1 deletion java/src/org/futo/inputmethod/keyboard/MainKeyboardView.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import org.futo.inputmethod.keyboard.internal.SlidingKeyInputDrawingPreview;
import org.futo.inputmethod.keyboard.internal.TimerHandler;
import org.futo.inputmethod.latin.AudioAndHapticFeedbackManager;
import org.futo.inputmethod.latin.RichInputMethodManager;
import org.futo.inputmethod.latin.uix.DynamicThemeProvider;
import org.futo.inputmethod.latin.R;
import org.futo.inputmethod.latin.SuggestedWords;
Expand Down Expand Up @@ -875,6 +876,25 @@ private String layoutLanguageOnSpacebar(final Paint paint,
return "";
}

private String layoutMultilingualLanguagesOnSpacebar(final List<Locale> locales) {
final StringBuilder builder = new StringBuilder();

for (final Locale locale : locales) {
final String language = locale.getLanguage();
if (language == null || language.isEmpty()) {
continue;
}

if (builder.length() > 0) {
builder.append('/');
}

builder.append(language.toUpperCase(Locale.ROOT));
}

return builder.toString();
}

private void drawLanguageOnSpacebar(final Key key, final Canvas canvas, final Paint paint, final int color) {
final Keyboard keyboard = getKeyboard();
if (keyboard == null) {
Expand All @@ -885,7 +905,10 @@ private void drawLanguageOnSpacebar(final Key key, final Canvas canvas, final Pa
paint.setTextAlign(Align.CENTER);
paint.setTypeface(mDrawableProvider.selectKeyTypeface(Typeface.DEFAULT));
paint.setTextSize(mLanguageOnSpacebarTextSize);
final String language = layoutLanguageOnSpacebar(paint, keyboard.mId.mLocale, width);
final List<Locale> currentSubtypeLocales = RichInputMethodManager.getInstance().getCurrentSubtypeLocales();
final String language = currentSubtypeLocales.size() > 1
? layoutMultilingualLanguagesOnSpacebar(currentSubtypeLocales)
: layoutLanguageOnSpacebar(paint, keyboard.mId.mLocale, width);
// Draw language text with shadow
final float descent = paint.descent();
final float textHeight = -paint.ascent() + descent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ public String getKeyboardLayoutSetName() {

@Nonnull
public List<Locale> getMultilingualTypingLanguages(Context context) {
return Subtypes.INSTANCE.getMultilingualBucket(context, mLocale);
return Subtypes.INSTANCE.getMultilingualBucket(context, mLocale, getKeyboardLayoutSetName());
}

public static RichInputMethodSubtype getRichInputMethodSubtype(
Expand Down
198 changes: 157 additions & 41 deletions java/src/org/futo/inputmethod/latin/Subtypes.kt
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ object Subtypes {
}

fun hasMultipleEnabledSubtypes(context: Context): Boolean {
return context.getSettingBlocking(SubtypesSetting).size > 1
return getSwitchableSubtypes(context).size > 1
}

fun subtypeToString(subtype: InputMethodSubtype): String {
Expand Down Expand Up @@ -255,6 +255,83 @@ object Subtypes {
}
}

private fun InputMethodSubtype.layoutSetName(): String =
SubtypeLocaleUtils.getKeyboardLayoutSetName(this)

private fun multilingualBucketLocales(multilingualBucket: Set<String>): Set<Locale> =
multilingualBucket.map { getLocale(it) }.toSet()

private fun multilingualGroupKey(
subtype: InputMethodSubtype,
enabledSubtypes: List<InputMethodSubtype>,
multilingualBucket: Set<Locale>
): String {
val locale = getLocale(subtype)
val layout = subtype.layoutSetName()

val localesInSameLayoutBucket = enabledSubtypes.asSequence()
.filter { it.layoutSetName() == layout }
.map { getLocale(it) }
.filter { multilingualBucket.contains(it) }
.distinct()
.sortedBy { it.toLanguageTag() }
.toList()

return if(multilingualBucket.contains(locale) && localesInSameLayoutBucket.size > 1) {
"multilingual:$layout:${localesInSameLayoutBucket.joinToString(";") { it.toLanguageTag() }}"
} else {
"single:${subtypeToString(subtype)}"
}
}

fun getSwitchableSubtypes(context: Context): List<InputMethodSubtype> =
getSwitchableSubtypes(
context.getSettingBlocking(SubtypesSetting),
context.getSettingBlocking(MultilingualBucketSetting)
)

fun getSwitchableSubtypes(
subtypeSet: Set<String>,
multilingualBucketSet: Set<String>
): List<InputMethodSubtype> {
val enabledSubtypes = subtypeSet.map { convertToSubtype(it) }
val multilingualBucket = multilingualBucketLocales(multilingualBucketSet)
val seen = mutableSetOf<String>()

return enabledSubtypes.filter { subtype ->
seen.add(multilingualGroupKey(subtype, enabledSubtypes, multilingualBucket))
}
}

fun isSameSwitchableSubtype(
subtypeSet: Set<String>,
multilingualBucketSet: Set<String>,
firstSubtype: String,
secondSubtype: InputMethodSubtype
): Boolean {
if(firstSubtype.isEmpty()) return false
if(firstSubtype == subtypeToString(secondSubtype)) return true

val enabledSubtypes = subtypeSet.map { convertToSubtype(it) }
val multilingualBucket = multilingualBucketLocales(multilingualBucketSet)
val first = convertToSubtype(firstSubtype)

return multilingualGroupKey(first, enabledSubtypes, multilingualBucket) ==
multilingualGroupKey(secondSubtype, enabledSubtypes, multilingualBucket)
}

fun getSwitchableSubtypeDisplayName(
context: Context,
subtype: InputMethodSubtype
): String {
val locale = getLocale(subtype)
val locales = listOf(locale) + getMultilingualBucket(context, locale, subtype.layoutSetName())

return locales.distinct().joinToString(" / ") {
getLocaleDisplayName(it, it)
}
}

fun getDirectBootInitialLayouts(context: Context): Set<String> {
val layouts = mutableSetOf("en_US:")

Expand Down Expand Up @@ -284,7 +361,7 @@ object Subtypes {
): Boolean {
if(direction == 0) return true

val enabledSubtypes = context.getSettingBlocking(SubtypesSetting).toList()
val enabledSubtypes = getSwitchableSubtypes(context).map { subtypeToString(it) }
val currentSubtype = context.getSettingBlocking(ActiveSubtype)

if(enabledSubtypes.isEmpty()) return false
Expand All @@ -293,7 +370,23 @@ object Subtypes {
return false
}

val index = enabledSubtypes.indexOf(currentSubtype)
val allSubtypes = context.getSettingBlocking(SubtypesSetting)
val multilingualBucket = context.getSettingBlocking(MultilingualBucketSetting)

val index = enabledSubtypes.indexOf(currentSubtype).let { directIndex ->
if(directIndex != -1) {
directIndex
} else {
enabledSubtypes.indexOfFirst {
isSameSwitchableSubtype(
allSubtypes,
multilingualBucket,
currentSubtype,
convertToSubtype(it)
)
}
}
}
val nextIndex = if(index == -1) {
0
} else {
Expand All @@ -304,14 +397,30 @@ object Subtypes {
return true
}

fun getMultilingualBucket(context: Context, locale: Locale): List<Locale> {
val set = context.getSetting(MultilingualBucketSetting).map {
getLocale(it)
}
@JvmOverloads
fun getMultilingualBucket(
context: Context,
locale: Locale,
keyboardLayoutSet: String? = null
): List<Locale> {
val set = multilingualBucketLocales(context.getSetting(MultilingualBucketSetting))
if(!set.contains(locale)) {
return emptyList()
} else {
return set.filter { it != locale }
val enabledLocales = if(keyboardLayoutSet != null) {
context.getSettingBlocking(SubtypesSetting)
.asSequence()
.map { convertToSubtype(it) }
.filter { it.layoutSetName() == keyboardLayoutSet }
.map { getLocale(it) }
.filter { set.contains(it) }
.distinct()
.toList()
} else {
set.toList()
}

return enabledLocales.filter { it != locale }
}
}
}
Expand All @@ -331,11 +440,15 @@ fun LanguageSwitcherDialog(
useDataStoreValue(SubtypesSetting)
}

val subtypes = remember(subtypeSet) {
Subtypes.layoutsMappedByLanguage(subtypeSet)
val multilingualBucketSet = if(inspection) {
setOf("en_US", "pt_PT")
} else {
useDataStoreValue(MultilingualBucketSetting)
}

val keys = remember(subtypes) { subtypes.keys.toList().sorted() }
val subtypes = remember(subtypeSet, multilingualBucketSet) {
Subtypes.getSwitchableSubtypes(subtypeSet, multilingualBucketSet)
}

val activeSubtype = if(inspection) {
"pt_PT:KeyboardLayoutSet=portugues:"
Expand Down Expand Up @@ -372,42 +485,45 @@ fun LanguageSwitcherDialog(
)

LazyColumn(modifier = Modifier.weight(1.0f)) {
items(keys) { locale ->
subtypes[locale]!!.forEach { subtype ->
val layoutSetName = subtype.getExtraValueOf(Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET) ?: ""

val layout = if(inspection) { layoutSetName } else { Subtypes.getLayoutName(context, layoutSetName) }
val title = if(inspection) { subtype.locale } else { Subtypes.getName(subtype) }

val selected = activeSubtype == Subtypes.subtypeToString(subtype)

val item = @Composable {
NavigationItem(
title = title,
subtitle = layout.ifBlank { null },
style = NavigationItemStyle.MiscNoArrow,
navigate = {
context.setSettingBlocking(ActiveSubtype.key, Subtypes.subtypeToString(subtype))
onDismiss()
},
icon = if(selected) painterResource(R.drawable.check_circle) else painterResource(R.drawable.circle)
)
}
items(subtypes) { subtype ->
val layoutSetName = subtype.getExtraValueOf(Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET) ?: ""

val layout = if(inspection) { layoutSetName } else { Subtypes.getLayoutName(context, layoutSetName) }
val title = if(inspection) { subtype.locale } else { Subtypes.getSwitchableSubtypeDisplayName(context, subtype) }

if (selected) {
Surface(color = MaterialTheme.colorScheme.primary) {
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onPrimary) {
item()
}
val selected = Subtypes.isSameSwitchableSubtype(
subtypeSet,
multilingualBucketSet,
activeSubtype,
subtype
)

val item = @Composable {
NavigationItem(
title = title,
subtitle = layout.ifBlank { null },
style = NavigationItemStyle.MiscNoArrow,
navigate = {
context.setSettingBlocking(ActiveSubtype.key, Subtypes.subtypeToString(subtype))
onDismiss()
},
icon = if(selected) painterResource(R.drawable.check_circle) else painterResource(R.drawable.circle)
)
}

if (selected) {
Surface(color = MaterialTheme.colorScheme.primary) {
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onPrimary) {
item()
}
} else {
item()
}
} else {
item()
}
}

item {
if(activeIMEs.isNotEmpty() && keys.isNotEmpty()) {
if(activeIMEs.isNotEmpty() && subtypes.isNotEmpty()) {
Spacer(Modifier.height(16.dp))
HorizontalDivider()
Spacer(Modifier.height(16.dp))
Expand Down Expand Up @@ -461,4 +577,4 @@ fun LanguageSwitcherDialog(
}
}
}
}
}
Loading