Skip to content

Commit 15dfe0d

Browse files
committed
Add toolbar state remember, auto-hide pinned keys, and translate language selector
- New: Auto-hide pinned keys when toolbar is expanded (Settings > Toolbar) - New: Remember toolbar expanded/collapsed state (Settings > Toolbar) - New: Long-press TRANSLATE key shows language selector panel - Fix: HeliBoard -> LeanType branding in all translations - Fix: Animators may only be run on Looper threads (ToolbarUtils) - Update: release.py to auto-replace HeliBoard with LeanType in translations
1 parent a1d5f8b commit 15dfe0d

70 files changed

Lines changed: 416 additions & 200 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
# Ignore all files in the app/src/main/java directory
22
.aiderignore
3+
4+
# Qwen AI context files
5+
QWEN.md
6+
**/QWEN.md

app/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,9 @@ android {
168168
namespace = "helium314.keyboard.latin"
169169
lint {
170170
abortOnError = true
171+
// Upstream Heliboard translations reference strings not in LeanType's base strings.xml;
172+
// these orphaned strings are harmlessly stripped by R8 during minification.
173+
disable += "ExtraTranslation"
171174
}
172175
}
173176

app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/KeyCode.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ object KeyCode {
179179
const val INLINE_EMOJI_SEARCH_DONE = -10051
180180
const val PROOFREAD = -10052 // Gemini AI proofreading
181181
const val TRANSLATE = -10053 // Gemini AI translation
182+
const val SHOW_TRANSLATE_LANGUAGES = -10054 // Show translate language selector
182183
const val CUSTOM_AI_1 = -10061
183184
const val CUSTOM_AI_2 = -10062
184185
const val CUSTOM_AI_3 = -10063
@@ -213,7 +214,7 @@ object KeyCode {
213214
PAGE_DOWN, META, TAB, ESCAPE, INSERT, SLEEP, MEDIA_PLAY, MEDIA_PAUSE, MEDIA_PLAY_PAUSE, MEDIA_NEXT,
214215
MEDIA_PREVIOUS, VOL_UP, VOL_DOWN, MUTE, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, BACK,
215216
TIMESTAMP, CTRL_LEFT, CTRL_RIGHT, ALT_LEFT, ALT_RIGHT, META_LEFT, META_RIGHT, SEND_INTENT_ONE, SEND_INTENT_TWO,
216-
SEND_INTENT_THREE, INLINE_EMOJI_SEARCH_DONE, META_LOCK, PROOFREAD, TRANSLATE,
217+
SEND_INTENT_THREE, INLINE_EMOJI_SEARCH_DONE, META_LOCK, PROOFREAD, TRANSLATE, SHOW_TRANSLATE_LANGUAGES,
217218
CUSTOM_AI_1, CUSTOM_AI_2, CUSTOM_AI_3, CUSTOM_AI_4, CUSTOM_AI_5,
218219
CUSTOM_AI_6, CUSTOM_AI_7, CUSTOM_AI_8, CUSTOM_AI_9, CUSTOM_AI_10, CLIPBOARD_SEARCH
219220
-> this

app/src/main/java/helium314/keyboard/latin/LatinIME.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1695,6 +1695,12 @@ public void setNeutralSuggestionStrip() {
16951695
}
16961696
}
16971697

1698+
public void showTranslateLanguageSelector() {
1699+
if (mSuggestionStripView != null) {
1700+
mSuggestionStripView.showTranslateLanguageSelector();
1701+
}
1702+
}
1703+
16981704
@Override
16991705
public void removeSuggestion(final String word) {
17001706
mDictionaryFacilitator.removeWord(word);

app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -907,7 +907,6 @@ public void onSuccess(String translatedText) {
907907
mConnection.commitText(translatedText, 1);
908908

909909
} else {
910-
911910
if (!hasSelection) {
912911
int len = mTextBeforeTranslate != null ? mTextBeforeTranslate.length() : 0;
913912
mConnection.setSelection(len, len);
@@ -922,6 +921,11 @@ public void onError(String errorMessage) {
922921
});
923922
}
924923

924+
private void handleShowTranslateLanguages() {
925+
// Show language selector panel in SuggestionStripView
926+
mLatinIME.showTranslateLanguageSelector();
927+
}
928+
925929
/**
926930
* Handle a functional key event.
927931
* <p>
@@ -1112,6 +1116,9 @@ private void handleFunctionalEvent(final Event event, final InputTransaction inp
11121116
case KeyCode.TRANSLATE:
11131117
handleTranslate();
11141118
break;
1119+
case KeyCode.SHOW_TRANSLATE_LANGUAGES:
1120+
handleShowTranslateLanguages();
1121+
break;
11151122
case KeyCode.SPLIT_LAYOUT:
11161123
KeyboardSwitcher.getInstance().toggleSplitKeyboardMode();
11171124
break;

app/src/main/java/helium314/keyboard/latin/settings/Defaults.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,9 @@ object Defaults {
172172
const val PREF_AUTO_SHOW_TOOLBAR = false
173173
const val PREF_AUTO_SHOW_TOOLBAR_ON_SELECT = false
174174
const val PREF_AUTO_HIDE_TOOLBAR = true
175+
const val PREF_AUTO_HIDE_PINNED_KEYS = false
176+
const val PREF_REMEMBER_TOOLBAR_STATE = true
177+
const val PREF_TOOLBAR_EXPANDED = false
175178
val PREF_CLIPBOARD_TOOLBAR_KEYS = defaultClipboardToolbarPref
176179
const val PREF_ABC_AFTER_EMOJI = false
177180
const val PREF_ABC_AFTER_CLIP = false

app/src/main/java/helium314/keyboard/latin/settings/Settings.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,9 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
178178
public static final String PREF_AUTO_SHOW_TOOLBAR = "auto_show_toolbar";
179179
public static final String PREF_AUTO_SHOW_TOOLBAR_ON_SELECT = "auto_show_toolbar_on_select";
180180
public static final String PREF_AUTO_HIDE_TOOLBAR = "auto_hide_toolbar";
181+
public static final String PREF_AUTO_HIDE_PINNED_KEYS = "auto_hide_pinned_keys";
182+
public static final String PREF_REMEMBER_TOOLBAR_STATE = "remember_toolbar_state";
183+
public static final String PREF_TOOLBAR_EXPANDED = "toolbar_expanded";
181184
public static final String PREF_CLIPBOARD_TOOLBAR_KEYS = "clipboard_toolbar_keys";
182185
public static final String PREF_ABC_AFTER_EMOJI = "abc_after_emoji";
183186
public static final String PREF_ABC_AFTER_CLIP = "abc_after_clip";

app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ public class SettingsValues {
130130
public final boolean mAutoShowToolbar;
131131
public final boolean mAutoShowToolbarOnSelect;
132132
public final boolean mAutoHideToolbar;
133+
public final boolean mAutoHidePinnedKeys;
134+
public final boolean mRememberToolbarState;
133135
public final boolean mAlphaAfterEmojiInEmojiView;
134136
public final boolean mAlphaAfterClipHistoryEntry;
135137
public final boolean mAlphaAfterSymbolAndSpace;
@@ -378,6 +380,10 @@ public SettingsValues(final Context context, final SharedPreferences prefs, fina
378380
&& prefs.getBoolean(Settings.PREF_AUTO_SHOW_TOOLBAR, Defaults.PREF_AUTO_SHOW_TOOLBAR);
379381
mAutoHideToolbar = mSuggestionsEnabledPerUserSettings
380382
&& prefs.getBoolean(Settings.PREF_AUTO_HIDE_TOOLBAR, Defaults.PREF_AUTO_HIDE_TOOLBAR);
383+
mAutoHidePinnedKeys = mToolbarMode == ToolbarMode.EXPANDABLE
384+
&& !mSplitToolbar
385+
&& prefs.getBoolean(Settings.PREF_AUTO_HIDE_PINNED_KEYS, Defaults.PREF_AUTO_HIDE_PINNED_KEYS);
386+
mRememberToolbarState = prefs.getBoolean(Settings.PREF_REMEMBER_TOOLBAR_STATE, Defaults.PREF_REMEMBER_TOOLBAR_STATE);
381387
mAlphaAfterEmojiInEmojiView = prefs.getBoolean(Settings.PREF_ABC_AFTER_EMOJI,
382388
Defaults.PREF_ABC_AFTER_EMOJI);
383389
mAlphaAfterClipHistoryEntry = prefs.getBoolean(Settings.PREF_ABC_AFTER_CLIP,

app/src/main/java/helium314/keyboard/latin/suggestions/SuggestionStripView.kt

Lines changed: 129 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ import helium314.keyboard.latin.utils.prefs
6060
import helium314.keyboard.latin.utils.removeFirst
6161
import helium314.keyboard.latin.utils.removePinnedKey
6262
import helium314.keyboard.latin.utils.setToolbarButtonsActivatedStateOnPrefChange
63+
import helium314.keyboard.settings.SettingsWithoutKey
6364
import java.util.concurrent.atomic.AtomicBoolean
6465
import kotlin.math.min
6566

@@ -122,6 +123,16 @@ class SuggestionStripView(context: Context, attrs: AttributeSet?, defStyle: Int)
122123
private val enabledToolKeyBackground = GradientDrawable()
123124
private var direction = 1 // 1 if LTR, -1 if RTL
124125

126+
// Track whether the user manually toggles the toolbar open/close
127+
var isToolbarManuallyOpen: Boolean = false
128+
129+
// Translate language selector
130+
private var isTranslateLanguageSelectorVisible = false
131+
private val translateLanguageSelector: ViewGroup = findViewById(R.id.translate_language_selector)
132+
private val translateLanguageCloseButton: ImageButton by lazy {
133+
findViewById(R.id.translate_language_close_button)
134+
}
135+
125136
// Loading animation for proofreading/translation
126137
private var loadingAnimator: ValueAnimator? = null
127138
private val loadingBorderDrawable = GradientDrawable().apply {
@@ -138,6 +149,8 @@ class SuggestionStripView(context: Context, attrs: AttributeSet?, defStyle: Int)
138149
).apply { gravity = android.view.Gravity.CENTER_VERTICAL }
139150

140151
init {
152+
isToolbarManuallyOpen = Settings.getValues().mAutoShowToolbar
153+
141154
val colors = Settings.getValues().mColors
142155

143156
// expand key
@@ -158,7 +171,15 @@ class SuggestionStripView(context: Context, attrs: AttributeSet?, defStyle: Int)
158171

159172
val mToolbarMode = Settings.getValues().mToolbarMode
160173
if (mToolbarMode == ToolbarMode.TOOLBAR_KEYS) {
161-
setToolbarVisibility(true)
174+
setToolbarVisibility(true, saveState = false)
175+
} else if (mToolbarMode == ToolbarMode.EXPANDABLE) {
176+
// Restore saved toolbar state if enabled
177+
val settingsValues = Settings.getValues()
178+
if (settingsValues.mRememberToolbarState) {
179+
val savedExpanded = context.prefs().getBoolean(Settings.PREF_TOOLBAR_EXPANDED, false)
180+
isToolbarManuallyOpen = savedExpanded || settingsValues.mAutoShowToolbar
181+
setToolbarVisibility(isToolbarManuallyOpen, saveState = false)
182+
}
162183
}
163184

164185
// toolbar keys setup
@@ -296,14 +317,20 @@ class SuggestionStripView(context: Context, attrs: AttributeSet?, defStyle: Int)
296317
suggestionsStrip.layoutDirection = newLayoutDirection
297318
}
298319

299-
// Track whether the user manually toggles the toolbar open/close
300-
var isToolbarManuallyOpen: Boolean = Settings.getValues().mAutoShowToolbar
301-
302-
fun setToolbarVisibility(toolbarVisible: Boolean) {
320+
// Overload for Java compatibility (default saveState = true)
321+
@JvmOverloads
322+
fun setToolbarVisibility(toolbarVisible: Boolean, saveState: Boolean = true) {
303323
// avoid showing toolbar keys when locked
304324
val locked = isDeviceLocked(context)
305325
val split = Settings.getValues().mSplitToolbar
306-
326+
val settingsValues = Settings.getValues()
327+
val autoHidePinnedKeys = settingsValues.mAutoHidePinnedKeys
328+
329+
// Save toolbar state if requested
330+
if (saveState && settingsValues.mRememberToolbarState) {
331+
context.prefs().edit().putBoolean(Settings.PREF_TOOLBAR_EXPANDED, toolbarVisible).apply()
332+
}
333+
307334
// In split mode, show only full toolbar, hide pinned keys
308335
if (split) {
309336
// suggestionsStrip visibility is handled dynamically in updateSplitToolbarState
@@ -313,7 +340,9 @@ class SuggestionStripView(context: Context, attrs: AttributeSet?, defStyle: Int)
313340
toolbarExpandKey.isVisible = false // Hide expand key
314341
updateSplitToolbarState()
315342
} else {
316-
pinnedKeys.isVisible = !locked && !toolbarVisible
343+
// Auto-hide pinned keys when toolbar is expanded
344+
val shouldHidePinnedKeys = autoHidePinnedKeys && toolbarVisible && !locked
345+
pinnedKeys.isVisible = !locked && !toolbarVisible && !shouldHidePinnedKeys
317346
suggestionsStrip.isVisible = locked || !toolbarVisible
318347
toolbarContainer.isVisible = !locked && toolbarVisible
319348
}
@@ -369,7 +398,7 @@ class SuggestionStripView(context: Context, attrs: AttributeSet?, defStyle: Int)
369398
suggestionsStrip.addView(view)
370399
}
371400

372-
if (Settings.getValues().mAutoHideToolbar) setToolbarVisibility(false)
401+
if (Settings.getValues().mAutoHideToolbar) setToolbarVisibility(false, saveState = false)
373402
updateSplitToolbarState()
374403
}
375404

@@ -461,7 +490,7 @@ class SuggestionStripView(context: Context, attrs: AttributeSet?, defStyle: Int)
461490
// workaround for a bug with inline suggestions views that just keep showing up otherwise, https://github.com/Helium314/HeliBoard/pull/386
462491
if (view === this) {
463492
if (visibility == View.VISIBLE) {
464-
setToolbarVisibility(isToolbarManuallyOpen)
493+
setToolbarVisibility(isToolbarManuallyOpen, saveState = false)
465494
} else {
466495
suggestionsStrip.visibility = visibility
467496
}
@@ -544,10 +573,19 @@ class SuggestionStripView(context: Context, attrs: AttributeSet?, defStyle: Int)
544573

545574
private fun onLongClickToolbarKey(view: View) {
546575
val tag = view.tag as? ToolbarKey ?: return
547-
576+
577+
// Special handling for TRANSLATE key - always allow language selector
578+
if (tag === ToolbarKey.TRANSLATE) {
579+
val longClickCode = getCodeForToolbarKeyLongClick(tag)
580+
if (longClickCode != KeyCode.UNSPECIFIED) {
581+
listener.onCodeInput(longClickCode, Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE, false)
582+
}
583+
return
584+
}
585+
548586
// Disable pinning and long press actions when split toolbar is enabled
549587
if (Settings.getValues().mSplitToolbar) return
550-
588+
551589
if (Settings.getValues().mQuickPinToolbarKeys) {
552590
if (view.parent === toolbar) {
553591
// Pin: Move from toolbar to pinned keys
@@ -657,7 +695,7 @@ class SuggestionStripView(context: Context, attrs: AttributeSet?, defStyle: Int)
657695

658696
// Show the toolbar if no suggestions are left and the "Auto show toolbar" setting is enabled
659697
if (this.suggestedWords.isEmpty && Settings.getValues().mAutoShowToolbar) {
660-
setToolbarVisibility(true)
698+
setToolbarVisibility(true, saveState = false)
661699
}
662700
}
663701

@@ -688,6 +726,85 @@ class SuggestionStripView(context: Context, attrs: AttributeSet?, defStyle: Int)
688726
pinnedKeys.findViewWithTag<View>(ToolbarKey.VOICE)?.isVisible = show
689727
}
690728

729+
fun showTranslateLanguageSelector() {
730+
// Hide other views
731+
suggestionsStrip.isVisible = false
732+
toolbarContainer.isVisible = false
733+
pinnedKeys.isVisible = false
734+
toolbarExpandKey.isVisible = false
735+
736+
// Populate language buttons
737+
val languageList = findViewById<LinearLayout>(R.id.translate_language_list)
738+
languageList.removeAllViews()
739+
740+
val languageNames = resources.getStringArray(R.array.translate_language_names)
741+
val prefs = context.prefs()
742+
743+
// Create a button for each language
744+
for (languageName in languageNames) {
745+
val button = android.widget.TextView(context, null, R.attr.suggestionWordStyle).apply {
746+
text = languageName
747+
gravity = android.view.Gravity.CENTER
748+
setPadding(8.dpToPx(resources), 0, 8.dpToPx(resources), 0)
749+
setTextSize(TypedValue.COMPLEX_UNIT_SP, 11f)
750+
setSingleLine()
751+
ellipsize = android.text.TextUtils.TruncateAt.END
752+
// Set minimum width for consistent appearance
753+
minimumWidth = 100.dpToPx(resources)
754+
}
755+
button.layoutParams = LinearLayout.LayoutParams(
756+
LinearLayout.LayoutParams.WRAP_CONTENT,
757+
LinearLayout.LayoutParams.MATCH_PARENT
758+
).apply { gravity = android.view.Gravity.CENTER_VERTICAL }
759+
760+
button.setOnClickListener {
761+
// Set the selected language and start translation
762+
context.prefs().edit().apply {
763+
putString(Settings.PREF_OFFLINE_TRANSLATE_TARGET_LANGUAGE, languageName)
764+
// Also update Gemini target language
765+
putString(SettingsWithoutKey.GEMINI_TARGET_LANGUAGE, languageName)
766+
}.apply()
767+
// Hide selector and trigger translation
768+
hideTranslateLanguageSelector()
769+
// Trigger translation with new language
770+
listener.onCodeInput(KeyCode.TRANSLATE, Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE, false)
771+
}
772+
Settings.getValues().mColors.setColor(button.background, ColorType.TOOL_BAR_KEY)
773+
button.setBackgroundResource(R.drawable.toolbar_key_background)
774+
languageList.addView(button)
775+
}
776+
777+
// Setup close button
778+
translateLanguageCloseButton.isVisible = true
779+
translateLanguageCloseButton.setOnClickListener {
780+
hideTranslateLanguageSelector()
781+
}
782+
783+
// Show the selector
784+
translateLanguageSelector.isVisible = true
785+
isTranslateLanguageSelectorVisible = true
786+
}
787+
788+
fun hideTranslateLanguageSelector() {
789+
translateLanguageSelector.isVisible = false
790+
translateLanguageCloseButton.isVisible = false
791+
792+
// Restore normal view
793+
val settingsValues = Settings.getValues()
794+
if (!settingsValues.mSplitToolbar) {
795+
toolbarExpandKey.isVisible = settingsValues.mToolbarMode == ToolbarMode.EXPANDABLE
796+
pinnedKeys.isVisible = !isDeviceLocked(context) && !isToolbarManuallyOpen
797+
toolbarContainer.isVisible = !isDeviceLocked(context) && isToolbarManuallyOpen
798+
suggestionsStrip.isVisible = isDeviceLocked(context) || !isToolbarManuallyOpen
799+
} else {
800+
toolbarContainer.isVisible = !isDeviceLocked(context)
801+
toolbar.visibility = VISIBLE
802+
updateSplitToolbarState()
803+
}
804+
805+
isTranslateLanguageSelectorVisible = false
806+
}
807+
691808
private fun updateKeys() {
692809
updateVoiceKey()
693810
val settingsValues = Settings.getValues()

app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ import helium314.keyboard.latin.common.Constants.Separators
1616
import helium314.keyboard.latin.settings.Defaults
1717
import helium314.keyboard.latin.settings.Settings
1818
import helium314.keyboard.latin.utils.ToolbarKey.*
19+
import kotlinx.coroutines.Dispatchers
1920
import kotlinx.coroutines.GlobalScope
2021
import kotlinx.coroutines.delay
2122
import kotlinx.coroutines.launch
23+
import kotlinx.coroutines.withContext
2224
import java.util.EnumMap
2325
import java.util.Locale
2426

@@ -43,7 +45,9 @@ fun setToolbarButtonsActivatedStateOnPrefChange(buttonsGroup: ViewGroup, key: St
4345

4446
GlobalScope.launch {
4547
delay(10) // need to wait until SettingsValues are reloaded
46-
buttonsGroup.forEach { if (it is ImageButton) setToolbarButtonActivatedState(it) }
48+
withContext(Dispatchers.Main) {
49+
buttonsGroup.forEach { if (it is ImageButton) setToolbarButtonActivatedState(it) }
50+
}
4751
}
4852
}
4953

@@ -119,6 +123,7 @@ fun getCodeForToolbarKeyLongClick(key: ToolbarKey) = Settings.getInstance().getC
119123
WORD_RIGHT -> KeyCode.MOVE_END_OF_LINE
120124
PAGE_UP -> KeyCode.MOVE_START_OF_PAGE
121125
PAGE_DOWN -> KeyCode.MOVE_END_OF_PAGE
126+
TRANSLATE -> KeyCode.SHOW_TRANSLATE_LANGUAGES
122127
else -> KeyCode.UNSPECIFIED
123128
}
124129

0 commit comments

Comments
 (0)