@@ -60,6 +60,7 @@ import helium314.keyboard.latin.utils.prefs
6060import helium314.keyboard.latin.utils.removeFirst
6161import helium314.keyboard.latin.utils.removePinnedKey
6262import helium314.keyboard.latin.utils.setToolbarButtonsActivatedStateOnPrefChange
63+ import helium314.keyboard.settings.SettingsWithoutKey
6364import java.util.concurrent.atomic.AtomicBoolean
6465import 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()
0 commit comments