From c06aff7133a62e89bfbe7b4dbe2219d31df0405d Mon Sep 17 00:00:00 2001 From: polwex Date: Sun, 24 Nov 2024 19:19:17 +0700 Subject: [PATCH] !! --- .idea/deploymentTargetDropDown.xml | 15 +- .../java/com/sortug/thaiinput/ApiModels.kt | 29 +- .../thaiinput/GoogleInputToolsService.kt | 2 +- .../com/sortug/thaiinput/MyKeyboardService.kt | 322 ++++++++++-------- app/src/main/res/layout/keyboard_layout.xml | 50 ++- .../main/res/layout/old_keyboard_layout.xml | 8 + 6 files changed, 286 insertions(+), 140 deletions(-) create mode 100644 app/src/main/res/layout/old_keyboard_layout.xml diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml index 0c0c338..4c794d3 100644 --- a/.idea/deploymentTargetDropDown.xml +++ b/.idea/deploymentTargetDropDown.xml @@ -3,7 +3,20 @@ - + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/sortug/thaiinput/ApiModels.kt b/app/src/main/java/com/sortug/thaiinput/ApiModels.kt index 6e4c528..d61e8f0 100644 --- a/app/src/main/java/com/sortug/thaiinput/ApiModels.kt +++ b/app/src/main/java/com/sortug/thaiinput/ApiModels.kt @@ -1,8 +1,14 @@ package com.sortug.thaiinput +import android.util.Log import com.google.gson.annotations.SerializedName -typealias InputToolsResponse = List +sealed interface InputToolsResponse{ + data class Success( + val results: List + ): InputToolsResponse + data object FailedToParse: InputToolsResponse +} data class ParsedResponse( val status: String, @@ -13,4 +19,23 @@ data class TransliterationResult( val wholeString: String, val options: List, val metadata: Map? -) \ No newline at end of file +) +public fun parseResponse(response: List): InputToolsResponse { + Log.d("THAI_IME", "parsing API response: $response") + return when (response[0] as String) { + "SUCCESS" -> { + val dataList = response[1] as List<*> + val results = dataList.map { item -> + val itemList = item as List<*> + TransliterationResult( + wholeString = itemList[0] as String, + options = (itemList[1] as List<*>).filterIsInstance(), + metadata = if (itemList.size > 3) itemList[3] as? Map else null + ) + } + InputToolsResponse.Success(results) + } + "FAILED_TO_PARSE_REQUEST_BODY" -> InputToolsResponse.FailedToParse + else -> throw IllegalStateException("Unknown response status: ${response[0]}") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/sortug/thaiinput/GoogleInputToolsService.kt b/app/src/main/java/com/sortug/thaiinput/GoogleInputToolsService.kt index d1ef5f7..5d87c88 100644 --- a/app/src/main/java/com/sortug/thaiinput/GoogleInputToolsService.kt +++ b/app/src/main/java/com/sortug/thaiinput/GoogleInputToolsService.kt @@ -19,7 +19,7 @@ interface GoogleInputToolsApiService { @Query("ie") ie: String = "utf-8", @Query("oe") oe: String = "utf-8", @Query("app") app: String = "demopage" - ): InputToolsResponse + ): List @GET("request") suspend fun getRawInputSuggestions( @Query("text") text: String, diff --git a/app/src/main/java/com/sortug/thaiinput/MyKeyboardService.kt b/app/src/main/java/com/sortug/thaiinput/MyKeyboardService.kt index fff60df..dff683e 100644 --- a/app/src/main/java/com/sortug/thaiinput/MyKeyboardService.kt +++ b/app/src/main/java/com/sortug/thaiinput/MyKeyboardService.kt @@ -1,8 +1,9 @@ package com.sortug.thaiinput - import android.graphics.Color +import android.graphics.Typeface import android.inputmethodservice.InputMethodService import android.view.View +import android.view.ViewGroup import android.view.inputmethod.InputConnection import kotlinx.coroutines.* import android.util.Log @@ -21,6 +22,7 @@ class MyKeyboardService : InputMethodService() { updateCurrentInputDisplay(newValue) } private val gson = Gson() + private lateinit var keyboardContainer: LinearLayout private lateinit var suggestionsContainer: LinearLayout private lateinit var currentInputDisplay: TextView @@ -28,87 +30,97 @@ class MyKeyboardService : InputMethodService() { private fun updateCurrentInputDisplay(text: String) { currentInputDisplay.text = text } + private val syms = listOf( + "1234567890".toList(), + "!@#$%^&*()".toList(), + "\".:?/-=+".toList() + ) + private val letters = listOf( + "QWERTYUIOP".toList(), + "ASDFGHJKL".toList(), + "ZXCVBNM".toList() + ) + private val fonts = listOf( + Typeface.NORMAL, +// Typeface.createFromAsset(assets, "fonts/round.ttf") + ) + + private var currentFontIndex = 0 override fun onCreateInputView(): View { - val inputView = layoutInflater.inflate(R.layout.keyboard_layout, null) as LinearLayout - // options box - val scrollView = HorizontalScrollView(this).apply { - layoutParams = LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.WRAP_CONTENT - ) - } + Log.d("THAI_IME", "onCreateInputView called") // Add this debug log + val inputView = layoutInflater.inflate(R.layout.keyboard_layout, null) - // Create suggestions container - suggestionsContainer = LinearLayout(this).apply { - orientation = LinearLayout.HORIZONTAL - layoutParams = LinearLayout.LayoutParams( - LinearLayout.LayoutParams.WRAP_CONTENT, - LinearLayout.LayoutParams.WRAP_CONTENT - ) - } - currentInputDisplay = TextView(this).apply{ - layoutParams = LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.WRAP_CONTENT - ).apply { - setMargins(10, 10, 10, 10) - } - setTextColor(Color.BLACK) - setBackgroundColor(Color.LTGRAY) - textSize = 18f - setPadding(10, 10, 10, 10) - } + // Initialize views using findViewById + currentInputDisplay = inputView.findViewById(R.id.current_input_display) + suggestionsContainer = inputView.findViewById(R.id.suggestions_container) + keyboardContainer = inputView.findViewById(R.id.keyboard_container) - inputView.addView(currentInputDisplay) - scrollView.addView(suggestionsContainer) - inputView.addView(scrollView) - setupKeyboardKeys(inputView) + // Set up the keyboard + setKeyboard(letters) + + Log.d("THAI_IME", "Keyboard container child count: ${keyboardContainer.childCount}") return inputView } - private fun setupKeyboardKeys(keyboardView: LinearLayout){ - val keys = listOf( - "qwertyuiop".toList(), - "asdfghjkl".toList(), - "zkcvbnm".toList() - ) - var rowLayout: LinearLayout? = null - keys.forEachIndexed { rowIndex, row -> - val rowLayout = LinearLayout(this).apply { - orientation = LinearLayout.HORIZONTAL - layoutParams = LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.WRAP_CONTENT - ) - } - if (rowIndex == 2) { // Third row - // Add Shift key - val shiftButton = createSpecialButton("⇧") { onShiftPressed() } - rowLayout.addView(shiftButton) - } + + private fun setSpecialKey(row: LinearLayout, txt: String, onClick: (String) -> Unit, width: Float = 1.5f){ + val button = Button(this).apply{ + text = txt + layoutParams = LinearLayout.LayoutParams( + 0, + LinearLayout.LayoutParams.WRAP_CONTENT, + width + ).apply { + setMargins(4, 4, 4, 4) + } + setTextColor(Color.LTGRAY) + setBackgroundColor(Color.BLACK) + setOnClickListener {onClick(txt)} + } + row.addView(button) + } + private fun setKeyboard(rows: List>) { + rows.forEachIndexed { rowIndex, row -> + Log.d("THAI_IME", "setting row :$row $rowIndex") + val rowLayout = LinearLayout(this).apply { + orientation = LinearLayout.HORIZONTAL + layoutParams = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ) + } + if (rowIndex == 2){setSpecialKey(rowLayout, "1!", ::handleShift)} + // set keys proper row.forEach { char -> + Log.d("THAI_IME", "setting char :$char") val button = Button(this).apply { text = char.toString() layoutParams = LinearLayout.LayoutParams( 0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f - ) - setOnClickListener { onKeyPressed(if (isShiftActive) char.toString() else char.lowercaseChar().toString()) } + ).apply{ + setMargins(4, 4, 4, 4) + } + setTextColor(Color.LTGRAY) + textSize = 18f + setBackgroundColor(Color.BLACK) + setOnClickListener { + if (isShiftActive) { + noApiCall(char.toString()) + }else{ + onKeyPressed(char.lowercaseChar().toString()) + } + } } rowLayout.addView(button) } - if (rowIndex == 2) { // Third row - // Add Backspace key - val backspaceButton = createSpecialButton("⌫") { onBackspace() } - rowLayout.addView(backspaceButton) - } - keyboardView.addView(rowLayout) + if (rowIndex == 2) {setSpecialKey(rowLayout,"⌫", ::onBackspace)} + keyboardContainer.addView(rowLayout) } - // Add a row for space val spaceRow = LinearLayout(this).apply { orientation = LinearLayout.HORIZONTAL layoutParams = LinearLayout.LayoutParams( @@ -116,21 +128,10 @@ class MyKeyboardService : InputMethodService() { LinearLayout.LayoutParams.WRAP_CONTENT ) } - - val spaceButton = Button(this).apply { - text = "Space" - layoutParams = LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.WRAP_CONTENT - ) - setOnClickListener { onSpace() } - } - spaceRow.addView(spaceButton) - - keyboardView.addView(spaceRow) - - Log.d("MyKeyboardService", "Keyboard keys set up") - + setSpecialKey(spaceRow, "F", ::changeFont, 0.4f) + setSpecialKey(spaceRow, "␣", {_ -> noApiCall(" ")}, 1f) + setSpecialKey(spaceRow, "↵", {_ -> noApiCall("\n")}, 0.4f) + keyboardContainer.addView(spaceRow) } private fun createSpecialButton(text: String, onClickListener: () -> Unit): Button { @@ -145,35 +146,15 @@ class MyKeyboardService : InputMethodService() { } } - private fun onShiftPressed() { - isShiftActive = !isShiftActive - Log.d("MyKeyboardService", "Shift pressed. Active: $isShiftActive") - // You might want to update the appearance of the Shift key here - } - private fun onBackspace() { + private fun onBackspace(_thing: String) { Log.d("MyKeyboardService", "onBackspace called") val inputConnection = currentInputConnection ?: return // Only make API request if we still have text if (currentInput.isNotEmpty()) { currentInput = currentInput.substring(0, currentInput.length - 1) - coroutineScope.launch { - try { - val response = withContext(Dispatchers.IO) { - RetrofitClient.googleInputToolsApiService.getInputSuggestions(currentInput) - } - - val parsedResponse = parseResponse(response) - val options = parsedResponse.results.flatMap { it.options } - - Log.d("MyKeyboardService", "Parsed options after backspace: $options") - displaySuggestions(options) - } catch (e: Exception) { - Log.e("MyKeyboardService", "Error in API call after backspace", e) - displaySuggestions(listOf("Error: ${e.message}")) - } - } + apiCall() } else { inputConnection.deleteSurroundingText(1, 0) // Clear suggestions if we've deleted all text @@ -181,64 +162,56 @@ class MyKeyboardService : InputMethodService() { } Log.d("MyKeyboardService", "Current input after backspace: $currentInput") } - private fun onSpace(){ - val inputConnection = currentInputConnection ?: return - inputConnection.commitText(" ", 1) - } - - override fun onDestroy() { super.onDestroy() coroutineScope.cancel() } private fun onKeyPressed(text: String) { Log.d("MyKeyboardService", "onKeyPressed called with text: $text") - val inputConnection: InputConnection = currentInputConnection ?: return currentInput += text Log.d("MyKeyboardService", "Current input: $currentInput") - Log.d("MyKeyboardService", "Key pressed: $text") - + apiCall() + } + private fun noApiCall(text: String){ + val inputConnection: InputConnection = currentInputConnection ?: return + inputConnection.commitText(text, 1) + } + private fun apiCall(){ + if (currentInput.isNotEmpty()){ coroutineScope.launch { try { -// val rawResponse = withContext(Dispatchers.IO) { -// RetrofitClient.googleInputToolsApiService.getRawInputSuggestions(currentInput.toString()) -// } -// val rawJson = rawResponse.body()?.string() ?: "Empty response" -// Log.d("MyKeyboardService", "Raw API Response: $rawJson") - - val result = withContext(Dispatchers.IO) { - RetrofitClient.googleInputToolsApiService.getInputSuggestions(currentInput.toString()) + Log.d("THAI_IME", "API request input: $currentInput") + val response = withContext(Dispatchers.IO) { + RetrofitClient.googleInputToolsApiService.getInputSuggestions(currentInput) + } + Log.d("THAI_IME", "raw API response: $response") + when (val apiResponse = parseResponse(response)){ + is InputToolsResponse.Success -> { + val options = apiResponse.results.flatMap{it.options} + Log.d("MyKeyboardService", "Parsed options after backspace: $options") + displaySuggestions((options)) + } + is InputToolsResponse.FailedToParse -> { + Log.e("THAI_IME", "Failed to parse request body: $currentInput") + } } - val parsedResponse = parseResponse(result) - val options = parsedResponse.results.flatMap { it.options } - displaySuggestions(options) } catch (e: Exception) { - Log.e("MyKeyboardService", "Unhandled error in HTTP request", e) - - // Handle exceptions + Log.e("MyKeyboardService", "Error in API call after backspace", e) + displaySuggestions(listOf("Error: ${e.message}")) } } - } - private fun parseResponse(response: InputToolsResponse): ParsedResponse { - val status = response[0] as String - val dataList = response[1] as List<*> - val results = dataList.map { item -> - val itemList = item as List<*> - TransliterationResult( - wholeString = itemList[0] as String, - options = (itemList[1] as List<*>).filterIsInstance(), - metadata = if (itemList.size > 3) itemList[3] as? Map else null - ) } - return ParsedResponse(status, results) } + private fun displaySuggestions(options: List){ suggestionsContainer.removeAllViews() options.forEach{ option -> val button = Button(this).apply{ text = option + textSize = 20f +// typeface = fonts[currentFontIndex] // Set the current font setOnClickListener { onSuggestionClicked(option) } layoutParams = LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, @@ -261,7 +234,88 @@ class MyKeyboardService : InputMethodService() { suggestionsContainer.removeAllViews() } + private fun handleShift(_thing: String){ + Log.d("THAI_IME", "shift pressed") + isShiftActive = !isShiftActive + val keys = if (isShiftActive) syms else letters + keyboardContainer.removeAllViews() + setKeyboard(keys) + } + private fun changeFont(_thing: String){ + currentFontIndex = (currentFontIndex + 1) % fonts.size + Log.d("THAI_IME", "🔤 Changed font to index: $currentFontIndex") + // Refresh suggestions with new font + redrawCurrentSuggestions() + } + private fun redrawCurrentSuggestions() { + val currentButtons = mutableListOf() + for (i in 0 until suggestionsContainer.childCount) { + val button = suggestionsContainer.getChildAt(i) as? Button + button?.text?.toString()?.let { currentButtons.add(it) } + } + displaySuggestions(currentButtons) + } + // TODO: Add methods for handling shift and number toggle +} +object ViewDebugUtils { + fun getViewHierarchy(view: View?, level: Int = 0): String { + if (view == null) return "null" + + val indent = " ".repeat(level) + val builder = StringBuilder() + + // Basic view information + builder.append("$indent${view.javaClass.simpleName} {") + builder.append("\n${indent} id: ${getViewId(view)}") + builder.append("\n${indent} width: ${view.width}, height: ${view.height}") + builder.append("\n${indent} visibility: ${getVisibilityString(view.visibility)}") + + // Layout parameters + val params = view.layoutParams + if (params != null) { + builder.append("\n${indent} layout_width: ${getLayoutParamSize(params.width)}") + builder.append("\n${indent} layout_height: ${getLayoutParamSize(params.height)}") + } + + // For ViewGroups, recursively print children + if (view is ViewGroup) { + builder.append("\n${indent} children: [") + for (i in 0 until view.childCount) { + builder.append("\n${getViewHierarchy(view.getChildAt(i), level + 2)}") + } + if (view.childCount > 0) builder.append("\n$indent ]") + else builder.append("]") + } + + builder.append("\n$indent}") + return builder.toString() + } + + private fun getViewId(view: View): String { + val id = view.id + return when { + id == View.NO_ID -> "NO_ID" + else -> try { + view.resources.getResourceEntryName(id) + } catch (e: Exception) { + id.toString() + } + } + } + + private fun getVisibilityString(visibility: Int): String = when (visibility) { + View.VISIBLE -> "VISIBLE" + View.INVISIBLE -> "INVISIBLE" + View.GONE -> "GONE" + else -> visibility.toString() + } + + private fun getLayoutParamSize(size: Int): String = when (size) { + ViewGroup.LayoutParams.MATCH_PARENT -> "MATCH_PARENT" + ViewGroup.LayoutParams.WRAP_CONTENT -> "WRAP_CONTENT" + else -> size.toString() + } } \ No newline at end of file diff --git a/app/src/main/res/layout/keyboard_layout.xml b/app/src/main/res/layout/keyboard_layout.xml index 22ea1f2..1b49b45 100644 --- a/app/src/main/res/layout/keyboard_layout.xml +++ b/app/src/main/res/layout/keyboard_layout.xml @@ -1,8 +1,54 @@ + android:background="@android:color/darker_gray"> + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/old_keyboard_layout.xml b/app/src/main/res/layout/old_keyboard_layout.xml new file mode 100644 index 0000000..22ea1f2 --- /dev/null +++ b/app/src/main/res/layout/old_keyboard_layout.xml @@ -0,0 +1,8 @@ + + + \ No newline at end of file