!!
This commit is contained in:
parent
de9592764a
commit
c06aff7133
@ -3,7 +3,20 @@
|
||||
<component name="deploymentTargetDropDown">
|
||||
<value>
|
||||
<entry key="app">
|
||||
<State />
|
||||
<State>
|
||||
<runningDeviceTargetSelectedWithDropDown>
|
||||
<Target>
|
||||
<type value="RUNNING_DEVICE_TARGET" />
|
||||
<deviceKey>
|
||||
<Key>
|
||||
<type value="SERIAL_NUMBER" />
|
||||
<value value="8cb784d4" />
|
||||
</Key>
|
||||
</deviceKey>
|
||||
</Target>
|
||||
</runningDeviceTargetSelectedWithDropDown>
|
||||
<timeTargetWasSelectedWithDropDown value="2024-11-24T11:06:07.206200396Z" />
|
||||
</State>
|
||||
</entry>
|
||||
</value>
|
||||
</component>
|
||||
|
@ -1,8 +1,14 @@
|
||||
package com.sortug.thaiinput
|
||||
|
||||
import android.util.Log
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
typealias InputToolsResponse = List<Any>
|
||||
sealed interface InputToolsResponse{
|
||||
data class Success(
|
||||
val results: List<TransliterationResult>
|
||||
): InputToolsResponse
|
||||
data object FailedToParse: InputToolsResponse
|
||||
}
|
||||
|
||||
data class ParsedResponse(
|
||||
val status: String,
|
||||
@ -13,4 +19,23 @@ data class TransliterationResult(
|
||||
val wholeString: String,
|
||||
val options: List<String>,
|
||||
val metadata: Map<String, Any>?
|
||||
)
|
||||
)
|
||||
public fun parseResponse(response: List<Any>): 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<String>(),
|
||||
metadata = if (itemList.size > 3) itemList[3] as? Map<String, Any> else null
|
||||
)
|
||||
}
|
||||
InputToolsResponse.Success(results)
|
||||
}
|
||||
"FAILED_TO_PARSE_REQUEST_BODY" -> InputToolsResponse.FailedToParse
|
||||
else -> throw IllegalStateException("Unknown response status: ${response[0]}")
|
||||
}
|
||||
}
|
@ -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<Any>
|
||||
@GET("request")
|
||||
suspend fun getRawInputSuggestions(
|
||||
@Query("text") text: String,
|
||||
|
@ -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<List<Char>>) {
|
||||
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<String>(),
|
||||
metadata = if (itemList.size > 3) itemList[3] as? Map<String, Any> else null
|
||||
)
|
||||
}
|
||||
return ParsedResponse(status, results)
|
||||
}
|
||||
|
||||
|
||||
private fun displaySuggestions(options: List<String>){
|
||||
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<String>()
|
||||
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()
|
||||
}
|
||||
}
|
@ -1,8 +1,54 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/keyboard_layout"
|
||||
android:id="@+id/keyboard_main_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
android:background="@android:color/darker_gray">
|
||||
|
||||
<!-- Top Bar -->
|
||||
<TextView
|
||||
android:id="@+id/keyboard_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:text="SORKB"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:gravity="center"
|
||||
android:textColor="@android:color/white"
|
||||
android:background="@android:color/black"
|
||||
android:elevation="4dp" />
|
||||
|
||||
<!-- Current Input Display -->
|
||||
<TextView
|
||||
android:id="@+id/current_input_display"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:layout_margin="4dp"
|
||||
android:padding="4dp"
|
||||
android:textSize="18sp"
|
||||
android:textColor="@android:color/darker_gray"
|
||||
android:background="@android:color/black" />
|
||||
|
||||
<!-- Suggestions Scroll View -->
|
||||
<HorizontalScrollView
|
||||
android:id="@+id/suggestions_scroll_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/suggestions_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal" />
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
||||
<!-- Keyboard Container -->
|
||||
<LinearLayout
|
||||
android:id="@+id/keyboard_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" />
|
||||
|
||||
</LinearLayout>
|
8
app/src/main/res/layout/old_keyboard_layout.xml
Normal file
8
app/src/main/res/layout/old_keyboard_layout.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/keyboard_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
</LinearLayout>
|
Loading…
Reference in New Issue
Block a user