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