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
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,16 @@ class KeyboardTest {
every { mockIME.findWhetherWordIsPlural(any(), "in") } returns false
every { mockIME.getCaseAnnotationForPreposition(any(), "in") } returns null

every { mockIME.updateAutoSuggestText(any(), any(), any(), any()) } answers {
every { mockIME.updateAutoSuggestText(any(), any(), any()) } answers {
conjugateBtn.text = "der"
pluralBtn.text = "den"
translateBtn.text = "die"
}

suggestionHandler.processLinguisticSuggestions("in")

Thread.sleep(100)

verify { conjugateBtn.text = match { it.isNotEmpty() } }
verify { pluralBtn.text = match { it.isNotEmpty() } }
verify { translateBtn.text = match { it.isNotEmpty() } }
Expand Down
8 changes: 8 additions & 0 deletions app/src/keyboards/java/be/scri/helpers/KeyHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ class KeyHandler(

resetShiftIfNeeded(code)

if (code != KeyboardBase.KEYCODE_SHIFT && code != KeyboardBase.KEYCODE_MODE_CHANGE) {
ime.hideClipboardSuggestionChip()
}

val previousWasLastKeySpace = wasLastKeySpace
if (code != KeyboardBase.KEYCODE_SPACE && code != KeyboardBase.KEYCODE_ENTER) {
suggestionHandler.clearLinguisticSuggestions()
Expand Down Expand Up @@ -113,6 +117,10 @@ class KeyHandler(
handleCurrencyKey(language)
true
}
KeyboardBase.KEYCODE_CLIPBOARD -> {
ime.openClipboardPanel()
true
}
else -> {
handleDefaultKey(code)
true
Expand Down
63 changes: 61 additions & 2 deletions app/src/keyboards/java/be/scri/helpers/ui/KeyboardUIManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ class KeyboardUIManager(
fun processLinguisticSuggestions(word: String)

fun isNumericKeyboardActive(): Boolean

fun onClipboardSuggestionClicked()
}

var keyboardView: KeyboardView = binding.keyboardView
Expand Down Expand Up @@ -106,15 +108,23 @@ class KeyboardUIManager(
}

private fun setupClickListeners() {
binding.scribeKeyOptions.setOnClickListener { listener.onScribeKeyOptionsClicked() }
binding.scribeKeyToolbar.setOnClickListener { listener.onScribeKeyToolbarClicked() }
binding.scribeKeyOptions.setOnClickListener {
hideClipboardSuggestionChip()
listener.onScribeKeyOptionsClicked()
}
binding.scribeKeyToolbar.setOnClickListener {
hideClipboardSuggestionChip()
listener.onScribeKeyToolbarClicked()
}

binding.translateBtn.setOnClickListener { listener.onTranslateClicked() }
binding.conjugateBtn.setOnClickListener { listener.onConjugateClicked() }
binding.pluralBtn.setOnClickListener { listener.onPluralClicked() }

binding.scribeKeyClose.setOnClickListener { listener.onCloseClicked() }

binding.clipboardSuggestionChip.setOnClickListener { listener.onClipboardSuggestionClicked() }

// Info button listener for INVALID state.
binding.ivInfo.setOnClickListener { showInvalidInfo() }
}
Expand Down Expand Up @@ -940,4 +950,53 @@ class KeyboardUIManager(
)
}
}

fun showClipboardSuggestionChip(clipText: String) {
val truncatedText =
if (clipText.length > 25) {
clipText.take(22) + "..."
} else {
clipText
}
binding.clipboardSuggestionChip.text = truncatedText
binding.clipboardSuggestionChip.visibility = View.VISIBLE

binding.translateBtn.visibility = View.GONE
binding.conjugateBtn.visibility = View.GONE
binding.pluralBtn.visibility = View.GONE
binding.separator2.visibility = View.GONE
binding.separator3.visibility = View.GONE

binding.emojiBtnPhone1?.visibility = View.GONE
binding.emojiBtnPhone2?.visibility = View.GONE
binding.emojiBtnTablet1?.visibility = View.GONE
binding.emojiBtnTablet2?.visibility = View.GONE
binding.emojiBtnTablet3?.visibility = View.GONE
binding.separator4.visibility = View.GONE
binding.separator5.visibility = View.GONE
binding.separator6.visibility = View.GONE
}

fun hideClipboardSuggestionChip() {
if (binding.clipboardSuggestionChip.visibility == View.VISIBLE) {
binding.clipboardSuggestionChip.visibility = View.GONE
binding.translateBtn.visibility = View.VISIBLE
binding.conjugateBtn.visibility = View.VISIBLE
binding.pluralBtn.visibility = View.VISIBLE
binding.separator2.visibility = View.VISIBLE
binding.separator3.visibility = View.VISIBLE
}
}

fun showClipboardPanel() {
binding.clipboardPanelHolder.visibility = View.VISIBLE
keyboardView.visibility = View.INVISIBLE
binding.commandOptionsBar.visibility = View.INVISIBLE
}

fun hideClipboardPanel() {
binding.clipboardPanelHolder.visibility = View.GONE
keyboardView.visibility = View.VISIBLE
binding.commandOptionsBar.visibility = View.VISIBLE
}
}
103 changes: 100 additions & 3 deletions app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,13 @@ import be.scri.helpers.SHIFT_OFF
import be.scri.helpers.SHIFT_ON_ONE_CHAR
import be.scri.helpers.SHIFT_ON_PERMANENT
import be.scri.helpers.SuggestionHandler
import be.scri.helpers.clipboard.ClipboardMonitor
import be.scri.helpers.data.AutocompletionDataManager
import be.scri.helpers.english.ENInterfaceVariables.ALREADY_PLURAL_MSG
import be.scri.helpers.ui.KeyboardUIManager
import be.scri.models.ScribeState
import be.scri.views.KeyboardView
import kotlinx.coroutines.launch
import java.util.Locale

private const val DATA_SIZE_2 = 2
Expand Down Expand Up @@ -115,6 +117,10 @@ abstract class GeneralKeyboardIME(
internal val binding: InputMethodViewBinding
get() = uiManager.binding

internal var hasNewClip: Boolean = false
internal var latestClipText: String? = null
private lateinit var clipboardMonitor: ClipboardMonitor

// MARK: State Variables

internal var isSingularAndPlural: Boolean = false
Expand Down Expand Up @@ -211,6 +217,14 @@ abstract class GeneralKeyboardIME(
suggestionHandler = SuggestionHandler(this)
autocompletionManager = dbManagers.autocompletionManager
autocompletionHandler = AutocompletionHandler(this)
clipboardMonitor =
ClipboardMonitor(this) { text ->
latestClipText = text
hasNewClip = true
if (currentState == ScribeState.IDLE && this::uiManager.isInitialized) {
uiManager.showClipboardSuggestionChip(text)
}
}
}

override fun onDestroy() {
Expand Down Expand Up @@ -339,6 +353,9 @@ abstract class GeneralKeyboardIME(
restarting: Boolean,
) {
super.onStartInputView(editorInfo, restarting)
if (this::clipboardMonitor.isInitialized) {
clipboardMonitor.startMonitoring()
}
emojiAutoSuggestionEnabled = getIsEmojiSuggestionsEnabled(applicationContext, language)
autoSuggestEmojis = null
suggestionHandler.clearAllSuggestionsAndHideButtonUI()
Expand Down Expand Up @@ -416,6 +433,9 @@ abstract class GeneralKeyboardIME(
*/
override fun onFinishInputView(finishingInput: Boolean) {
super.onFinishInputView(finishingInput)
if (this::clipboardMonitor.isInitialized) {
clipboardMonitor.stopMonitoring()
}
moveToIdleState()
}

Expand Down Expand Up @@ -510,6 +530,7 @@ abstract class GeneralKeyboardIME(

KeyboardBase.KEYCODE_ENTER -> handleKeycodeEnter()
KeyboardBase.KEYCODE_MODE_CHANGE -> handleModeChange(keyboardMode, keyboardView, this)
KeyboardBase.KEYCODE_CLIPBOARD -> openClipboardPanel()
else -> {
if (KeyboardBase.SCRIBE_VIEW_KEYS.contains(code)) {
val keyLabel = keyboardView?.getKeyLabel(code)
Expand Down Expand Up @@ -1950,8 +1971,84 @@ abstract class GeneralKeyboardIME(
emojis: MutableList<String>?,
) = uiManager.updateEmojiSuggestion(currentState, enabled, emojis)

/**
* Disables all auto-suggestions and resets the suggestion buttons to their default, inactive state.
*/
fun disableAutoSuggest() = uiManager.disableAutoSuggest(language)

override fun onClipboardSuggestionClicked() {
latestClipText?.let { text ->
currentInputConnection?.commitText(text, 1)
}
hideClipboardSuggestionChip()
}

fun hideClipboardSuggestionChip() {
hasNewClip = false
latestClipText = null
if (this::uiManager.isInitialized) {
uiManager.hideClipboardSuggestionChip()
}
}

private var clipboardAdapter: be.scri.helpers.clipboard.ClipboardAdapter? = null
private val clipboardRepository by lazy {
be.scri.helpers.clipboard
.ClipboardRepository(this)
}

fun openClipboardPanel() {
if (!this::uiManager.isInitialized) return
uiManager.showClipboardPanel()

val recyclerView = binding.clipboardItemsList
val emptyText = binding.clipboardEmptyText

clipboardAdapter =
be.scri.helpers.clipboard.ClipboardAdapter(
items = emptyList(),
onItemClick = { item ->
currentInputConnection?.commitText(item.text, 1)
closeClipboardPanel()
},
onItemDelete = { item ->
kotlinx.coroutines.CoroutineScope(kotlinx.coroutines.Dispatchers.Main).launch {
clipboardRepository.deleteItem(item.id)
refreshClipboardPanel()
}
},
onItemPinToggle = { item ->
kotlinx.coroutines.CoroutineScope(kotlinx.coroutines.Dispatchers.Main).launch {
clipboardRepository.togglePin(item.id, item.isPinned)
refreshClipboardPanel()
}
},
)
recyclerView.adapter = clipboardAdapter
recyclerView.layoutManager = androidx.recyclerview.widget.GridLayoutManager(this, 2)

binding.clipboardPanelClose.setOnClickListener { closeClipboardPanel() }
binding.clipboardClearAll.setOnClickListener {
kotlinx.coroutines.CoroutineScope(kotlinx.coroutines.Dispatchers.Main).launch {
clipboardRepository.clearAll()
refreshClipboardPanel()
}
}

kotlinx.coroutines.CoroutineScope(kotlinx.coroutines.Dispatchers.Main).launch {
val items = clipboardRepository.getAllItems()
clipboardAdapter?.updateItems(items)
emptyText.visibility = if (items.isEmpty()) android.view.View.VISIBLE else android.view.View.GONE
recyclerView.visibility = if (items.isEmpty()) android.view.View.GONE else android.view.View.VISIBLE
}
}

fun closeClipboardPanel() {
if (!this::uiManager.isInitialized) return
uiManager.hideClipboardPanel()
}

private suspend fun refreshClipboardPanel() {
val items = clipboardRepository.getAllItems()
clipboardAdapter?.updateItems(items)
binding.clipboardEmptyText.visibility = if (items.isEmpty()) android.view.View.VISIBLE else android.view.View.GONE
binding.clipboardItemsList.visibility = if (items.isEmpty()) android.view.View.GONE else android.view.View.VISIBLE
}
}
33 changes: 33 additions & 0 deletions app/src/main/java/be/scri/helpers/KeyboardBase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ class KeyboardBase {
const val KEYCODE_CAPS_LOCK = -50
const val KEYCODE_LEFT_ARROW = -55
const val KEYCODE_RIGHT_ARROW = -56
const val KEYCODE_CLIPBOARD = -60
const val SHIFT_OFF = 0
const val SHIFT_ON = 1
const val SHIFT_ON_PERMANENT = 2
Expand Down Expand Up @@ -658,6 +659,38 @@ class KeyboardBase {
}
}

if (currentKeyboardMode == keyboardLettersMode && mKeys != null) {
val spaceKey = mKeys!!.find { it?.code == 32 }
val commaKey = mKeys!!.find { it?.code == ','.code }
val row = mRows.lastOrNull()
if (spaceKey != null && commaKey != null && row != null) {
val clipWidth = (mDisplayWidth * 0.10).toInt()

val clipKey = Key(row)
clipKey.code = KEYCODE_CLIPBOARD
clipKey.width = clipWidth
clipKey.height = spaceKey.height
clipKey.gap = spaceKey.gap
clipKey.icon = context.resources.getDrawable(R.drawable.ic_clipboard_vector, context.theme)
clipKey.icon?.setBounds(0, 0, clipKey.icon!!.intrinsicWidth, clipKey.icon!!.intrinsicHeight)

spaceKey.width = spaceKey.width - clipWidth - clipKey.gap

val commaIdxInList = mKeys!!.indexOf(commaKey)
val commaIdxInRow = row.mKeys.indexOf(commaKey)

if (commaIdxInList != -1 && commaIdxInRow != -1) {
clipKey.x = commaKey.x + commaKey.width + commaKey.gap
clipKey.y = commaKey.y

spaceKey.x = clipKey.x + clipKey.width + clipKey.gap

mKeys!!.add(commaIdxInList + 1, clipKey)
row.mKeys.add(commaIdxInRow + 1, clipKey)
}
}
}

mHeight = y
}

Expand Down
Loading
Loading