completely
This commit is contained in:
parent
427b1184f6
commit
a9d33c1ec3
@ -25,6 +25,19 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name=".AppInputMethodService"
|
||||||
|
android:exported="true"
|
||||||
|
android:permission="android.permission.BIND_INPUT_METHOD">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.view.InputMethod" />
|
||||||
|
</intent-filter>
|
||||||
|
<meta-data
|
||||||
|
android:name="android.view.im"
|
||||||
|
android:resource="@xml/key_view" />
|
||||||
|
</service>
|
||||||
|
|
||||||
|
|
||||||
<activity android:name=".activity.DetailsActivity" />
|
<activity android:name=".activity.DetailsActivity" />
|
||||||
<activity android:name=".activity.ApplyActivity" />
|
<activity android:name=".activity.ApplyActivity" />
|
||||||
</application>
|
</application>
|
||||||
|
|||||||
@ -1,37 +1,100 @@
|
|||||||
package com.timber.soft.newkeyboard
|
package com.timber.soft.newkeyboard
|
||||||
|
|
||||||
import android.inputmethodservice.InputMethodService
|
import android.inputmethodservice.InputMethodService
|
||||||
|
import android.inputmethodservice.Keyboard
|
||||||
import android.inputmethodservice.KeyboardView.OnKeyboardActionListener
|
import android.inputmethodservice.KeyboardView.OnKeyboardActionListener
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.SystemClock
|
||||||
|
import android.view.KeyboardShortcutGroup
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.inputmethod.EditorInfo
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import com.timber.soft.newkeyboard.databinding.ViewInputBinding
|
||||||
|
import com.timber.soft.newkeyboard.tools.AppVal
|
||||||
|
|
||||||
class AppInputMethodService : InputMethodService(), OnKeyboardActionListener {
|
class AppInputMethodService : InputMethodService(), OnKeyboardActionListener {
|
||||||
|
|
||||||
|
private lateinit var binding: ViewInputBinding
|
||||||
|
private val views =
|
||||||
|
intArrayOf(R.xml.keyboard_letter, R.xml.keyboard_number, R.xml.keyboard_symbol)
|
||||||
|
private var mouble = false
|
||||||
|
private var laTime = -3L
|
||||||
|
|
||||||
|
private fun keyCase(toBig: Boolean, keyboard: Keyboard) {
|
||||||
|
for (key in keyboard.keys) {
|
||||||
|
if (!key.label.isNullOrEmpty()) {
|
||||||
|
if (key.label.length == 1) {
|
||||||
|
var strin: Char = if (toBig) {
|
||||||
|
key.label.toString()[0].uppercaseChar()
|
||||||
|
} else {
|
||||||
|
key.label.toString()[0].lowercaseChar()
|
||||||
|
}
|
||||||
|
key.run {
|
||||||
|
label = strin.toString()
|
||||||
|
codes[0] = strin.code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun changeXml(mode: Int) {
|
||||||
|
binding.myCustomInput.run {
|
||||||
|
when (mode) {
|
||||||
|
0 -> {
|
||||||
|
xmlMode = AppVal.xml0
|
||||||
|
keyboard = Keyboard(context, views[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
2 -> {
|
||||||
|
xmlMode = AppVal.xml2
|
||||||
|
keyboard = Keyboard(context, views[2])
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
1 -> {
|
||||||
|
xmlMode = AppVal.xml1
|
||||||
|
keyboard = Keyboard(context, views[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建键盘视图
|
* 构建键盘视图
|
||||||
*/
|
*/
|
||||||
override fun onCreateInputView(): View {
|
override fun onCreateInputView(): View {
|
||||||
return super.onCreateInputView()
|
binding = ViewInputBinding.inflate(layoutInflater, null, false)
|
||||||
TODO("Not yet implemented")
|
binding.myCustomInput.setOnKeyboardActionListener(this)
|
||||||
|
binding.myCustomInput.run {
|
||||||
|
keyboard = Keyboard(this@AppInputMethodService, views[0])
|
||||||
|
isEnabled = true
|
||||||
|
}
|
||||||
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 向用户展示键盘时候调用
|
* 向用户展示键盘时候调用
|
||||||
*/
|
*/
|
||||||
|
@RequiresApi(Build.VERSION_CODES.M)
|
||||||
override fun onWindowShown() {
|
override fun onWindowShown() {
|
||||||
super.onWindowShown()
|
super.onWindowShown()
|
||||||
TODO("Not yet implemented")
|
binding.myCustomInput.upUi(this@AppInputMethodService)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the user presses a key.
|
* Called when the user presses a key.
|
||||||
*
|
*
|
||||||
* 监听特定的按钮
|
* 监听特定的按钮
|
||||||
*/
|
*/
|
||||||
override fun onPress(primaryCode: Int) {
|
override fun onPress(primaryCode: Int) {
|
||||||
TODO("Not yet implemented")
|
mouble = false
|
||||||
|
if (primaryCode == Keyboard.KEYCODE_SHIFT) {
|
||||||
|
if (300 > SystemClock.elapsedRealtime() - laTime) {
|
||||||
|
mouble = true
|
||||||
|
}
|
||||||
|
laTime = SystemClock.elapsedRealtime()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,7 +103,80 @@ class AppInputMethodService : InputMethodService(), OnKeyboardActionListener {
|
|||||||
* 监听用户点击的键盘按键
|
* 监听用户点击的键盘按键
|
||||||
*/
|
*/
|
||||||
override fun onKey(primaryCode: Int, keyCodes: IntArray?) {
|
override fun onKey(primaryCode: Int, keyCodes: IntArray?) {
|
||||||
TODO("Not yet implemented")
|
|
||||||
|
when (primaryCode) {
|
||||||
|
Keyboard.KEYCODE_SHIFT -> {
|
||||||
|
binding.myCustomInput.run {
|
||||||
|
val myKeyboard = keyboard
|
||||||
|
when (shiftStatus) {
|
||||||
|
AppVal.Shift_S -> {
|
||||||
|
shiftStatus = if (mouble) {
|
||||||
|
AppVal.Shift_B_lo
|
||||||
|
} else {
|
||||||
|
AppVal.Shift_B
|
||||||
|
}
|
||||||
|
keyCase(true, myKeyboard)
|
||||||
|
keyboard = myKeyboard
|
||||||
|
}
|
||||||
|
|
||||||
|
AppVal.Shift_B_lo -> {
|
||||||
|
shiftStatus = AppVal.Shift_S
|
||||||
|
keyCase(false, myKeyboard)
|
||||||
|
keyboard = myKeyboard
|
||||||
|
}
|
||||||
|
|
||||||
|
AppVal.Shift_B -> {
|
||||||
|
shiftStatus = if (mouble) {
|
||||||
|
AppVal.Shift_B_lo
|
||||||
|
} else {
|
||||||
|
keyCase(false, myKeyboard)
|
||||||
|
AppVal.Shift_S
|
||||||
|
}
|
||||||
|
keyboard = myKeyboard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Keyboard.KEYCODE_DONE -> {
|
||||||
|
currentInputConnection.performEditorAction(EditorInfo.IME_ACTION_DONE)
|
||||||
|
}
|
||||||
|
|
||||||
|
Keyboard.KEYCODE_MODE_CHANGE -> {
|
||||||
|
binding.myCustomInput.run {
|
||||||
|
if (xmlMode == AppVal.xml0) {
|
||||||
|
changeXml(1)
|
||||||
|
} else {
|
||||||
|
changeXml(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AppVal.SHIFT_NUMBER -> {
|
||||||
|
changeXml(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
Keyboard.KEYCODE_DELETE -> {
|
||||||
|
currentInputConnection.deleteSurroundingText(1, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
AppVal.SHIFT_SYMBOL -> {
|
||||||
|
changeXml(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
currentInputConnection.commitText(primaryCode.toChar().toString(), 1)
|
||||||
|
binding.myCustomInput.keyboard = binding.myCustomInput.apply {
|
||||||
|
if (shiftStatus == AppVal.Shift_B) {
|
||||||
|
shiftStatus = AppVal.Shift_S
|
||||||
|
keyCase(false, binding.myCustomInput.keyboard)
|
||||||
|
}
|
||||||
|
}.keyboard
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -67,7 +67,7 @@ class ApplyActivity : AppCompatActivity() {
|
|||||||
private fun isChoose(): Boolean {
|
private fun isChoose(): Boolean {
|
||||||
Settings.Secure.getString(contentResolver, Settings.Secure.DEFAULT_INPUT_METHOD).let { id ->
|
Settings.Secure.getString(contentResolver, Settings.Secure.DEFAULT_INPUT_METHOD).let { id ->
|
||||||
return id.startsWith(packageName)
|
return id.startsWith(packageName)
|
||||||
} ?: return false
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isEnable(): Boolean {
|
private fun isEnable(): Boolean {
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import com.bumptech.glide.request.RequestListener
|
|||||||
import com.bumptech.glide.request.target.Target
|
import com.bumptech.glide.request.target.Target
|
||||||
import com.timber.soft.newkeyboard.R
|
import com.timber.soft.newkeyboard.R
|
||||||
import com.timber.soft.newkeyboard.databinding.ActivityDetailsBinding
|
import com.timber.soft.newkeyboard.databinding.ActivityDetailsBinding
|
||||||
|
import com.timber.soft.newkeyboard.listener.ApplyListener
|
||||||
import com.timber.soft.newkeyboard.model.DataModel
|
import com.timber.soft.newkeyboard.model.DataModel
|
||||||
import com.timber.soft.newkeyboard.tools.AppVal
|
import com.timber.soft.newkeyboard.tools.AppVal
|
||||||
import com.timber.soft.newkeyboard.tools.StatusBarTools
|
import com.timber.soft.newkeyboard.tools.StatusBarTools
|
||||||
@ -36,13 +37,14 @@ import java.io.FileOutputStream
|
|||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.RandomAccessFile
|
import java.io.RandomAccessFile
|
||||||
|
|
||||||
class DetailsActivity : AppCompatActivity() {
|
class DetailsActivity : AppCompatActivity(), ApplyListener {
|
||||||
|
|
||||||
private lateinit var binding: ActivityDetailsBinding
|
private lateinit var binding: ActivityDetailsBinding
|
||||||
private lateinit var inputManager: InputMethodManager
|
private lateinit var inputManager: InputMethodManager
|
||||||
private lateinit var previewUrl: String
|
private lateinit var previewUrl: String
|
||||||
private lateinit var zipPath: String
|
private lateinit var zipPath: String
|
||||||
private lateinit var dataModel: DataModel
|
private lateinit var dataModel: DataModel
|
||||||
|
private lateinit var sp: SharedPreferences
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@ -117,68 +119,59 @@ class DetailsActivity : AppCompatActivity() {
|
|||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 初始化键值对
|
||||||
|
sp = getSharedPreferences(
|
||||||
|
AppVal.SHARE_NAME, Context.MODE_PRIVATE
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun applyTheme() {
|
private fun applyTheme() {
|
||||||
// 检查是否启动键盘并设置
|
// 检查是否启动键盘并设置
|
||||||
if (!isEnable() || !isChoose()) {
|
if (!isEnable() || !isChoose()) {
|
||||||
|
Toast.makeText(this, getString(R.string.text_promote), Toast.LENGTH_SHORT).show()
|
||||||
val intent = Intent(this, ApplyActivity::class.java)
|
val intent = Intent(this, ApplyActivity::class.java)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.themeProgressbar.visibility = View.VISIBLE
|
binding.themeProgressbar.visibility = View.VISIBLE
|
||||||
val file = File(zipPath)
|
val file = File(zipPath)
|
||||||
// 判断缓存中是否存在文件
|
// 判断缓存中是否存在文件
|
||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
val allThemePath: String = getAllThemePath(dataModel.title)
|
val allThemePath: String = getAllThemePath(dataModel.title)
|
||||||
val edit = sp.edit()
|
val edit = sp.edit()
|
||||||
|
// 存放键值对
|
||||||
edit.run {
|
edit.run {
|
||||||
putString(AppVal.KEY_ALL_PATH, allThemePath)
|
putString(AppVal.KEY_ALL_PATH, allThemePath)
|
||||||
apply()
|
apply()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
getZipData(dataModel.title, dataModel.zipUrl, this,this::onResult)
|
getZipData(
|
||||||
|
dataModel.title, dataModel.zipUrl, this@DetailsActivity, this@DetailsActivity
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onResult(isSuccess: Boolean, path: String) {
|
/**
|
||||||
binding.themeProgressbar.visibility = View.GONE
|
* 从指定的URL下载zip文件,并在下载完成后处理该文件。
|
||||||
if (isSuccess) {
|
*
|
||||||
val lastIndexOf: Int = path.lastIndexOf(AppVal.res_path)
|
* @param title 下载文件的标题
|
||||||
val substring = path.subSequence(0, lastIndexOf + AppVal.res_path.length).toString()
|
* @param url 下载文件的URL
|
||||||
|
* @param con 上下文Context
|
||||||
val edit = sp.edit()
|
* @param listener 处理文件完成后的回调接口
|
||||||
|
*/
|
||||||
edit.run {
|
|
||||||
putString(AppVal.KEY_ALL_PATH, substring)
|
|
||||||
apply()
|
|
||||||
}
|
|
||||||
|
|
||||||
edit.run {
|
|
||||||
putString(dataModel.title, substring)
|
|
||||||
apply()
|
|
||||||
}
|
|
||||||
|
|
||||||
Toast.makeText(this, getString(R.string.succ_apply), Toast.LENGTH_LONG).show()
|
|
||||||
finish()
|
|
||||||
} else {
|
|
||||||
Toast.makeText(this, getString(R.string.fail_apply), Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ApplyListener {
|
|
||||||
fun applyListener(isSuccess: Boolean, str: String)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getZipData(title: String, url: String, con: Context, listener: ApplyListener) {
|
private fun getZipData(title: String, url: String, con: Context, listener: ApplyListener) {
|
||||||
Glide.with(con).asFile().load(url).addListener(object : RequestListener<File> {
|
Glide.with(con).asFile().load(url).addListener(object : RequestListener<File> {
|
||||||
|
//失败回调
|
||||||
override fun onLoadFailed(
|
override fun onLoadFailed(
|
||||||
e: GlideException?, model: Any?, target: Target<File>, isFirstResource: Boolean
|
e: GlideException?, model: Any?, target: Target<File>, isFirstResource: Boolean
|
||||||
): Boolean {
|
): Boolean {
|
||||||
|
// 回调失败
|
||||||
listener.applyListener(false, "")
|
listener.applyListener(false, "")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//成功回调
|
||||||
override fun onResourceReady(
|
override fun onResourceReady(
|
||||||
resource: File,
|
resource: File,
|
||||||
model: Any,
|
model: Any,
|
||||||
@ -186,7 +179,6 @@ class DetailsActivity : AppCompatActivity() {
|
|||||||
dataSource: DataSource,
|
dataSource: DataSource,
|
||||||
isFirstResource: Boolean
|
isFirstResource: Boolean
|
||||||
): Boolean {
|
): Boolean {
|
||||||
|
|
||||||
val fileInputStream = FileInputStream(resource)
|
val fileInputStream = FileInputStream(resource)
|
||||||
dealFile(title, url, fileInputStream, listener)
|
dealFile(title, url, fileInputStream, listener)
|
||||||
return false
|
return false
|
||||||
@ -201,9 +193,12 @@ class DetailsActivity : AppCompatActivity() {
|
|||||||
val zipPath = "${cacheDir}/${title}_ZIP"
|
val zipPath = "${cacheDir}/${title}_ZIP"
|
||||||
val unPath = "${cacheDir}/${title}"
|
val unPath = "${cacheDir}/${title}"
|
||||||
|
|
||||||
|
// 将下载的zip文件保存到zipPath路径
|
||||||
val zipBoolean = writeNewFile(input, zipPath)
|
val zipBoolean = writeNewFile(input, zipPath)
|
||||||
|
// 随机读写
|
||||||
val randomAccessFileInStream =
|
val randomAccessFileInStream =
|
||||||
RandomAccessFileInStream(RandomAccessFile(File(zipPath), "r"))
|
RandomAccessFileInStream(RandomAccessFile(File(zipPath), "r"))
|
||||||
|
|
||||||
val openInArchive = SevenZip.openInArchive(
|
val openInArchive = SevenZip.openInArchive(
|
||||||
ArchiveFormat.SEVEN_ZIP, randomAccessFileInStream
|
ArchiveFormat.SEVEN_ZIP, randomAccessFileInStream
|
||||||
)
|
)
|
||||||
@ -213,6 +208,12 @@ class DetailsActivity : AppCompatActivity() {
|
|||||||
if (zipBoolean) {
|
if (zipBoolean) {
|
||||||
try {
|
try {
|
||||||
var filePath: String = ""
|
var filePath: String = ""
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存档文件中的每个文件项。对于每个文件项,判断是否为文件(非文件夹),
|
||||||
|
* 如果是文件,则将其解压缩到目标路径unPath下的相应位置;
|
||||||
|
* 如果是文件夹,则创建对应的文件夹。
|
||||||
|
*/
|
||||||
openInArchive.simpleInterface.archiveItems.forEach { item ->
|
openInArchive.simpleInterface.archiveItems.forEach { item ->
|
||||||
if (!item.isFolder) {
|
if (!item.isFolder) {
|
||||||
val file = File(unPath, item.path)
|
val file = File(unPath, item.path)
|
||||||
@ -223,10 +224,10 @@ class DetailsActivity : AppCompatActivity() {
|
|||||||
File(unPath, item.path).mkdirs()
|
File(unPath, item.path).mkdirs()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//调用接口返回成功
|
||||||
listener.applyListener(true, filePath)
|
listener.applyListener(true, filePath)
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
listener.applyListener(false, "")
|
listener.applyListener(false, "")
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
openInArchive.close()
|
openInArchive.close()
|
||||||
randomAccessFileInStream.close()
|
randomAccessFileInStream.close()
|
||||||
@ -266,9 +267,6 @@ class DetailsActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val sp: SharedPreferences = getSharedPreferences(
|
|
||||||
AppVal.SHARE_NAME, Context.MODE_PRIVATE
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun getAllThemePath(zip: String): String {
|
private fun getAllThemePath(zip: String): String {
|
||||||
val result = sp.getString(zip, "")
|
val result = sp.getString(zip, "")
|
||||||
@ -281,7 +279,7 @@ class DetailsActivity : AppCompatActivity() {
|
|||||||
private fun isChoose(): Boolean {
|
private fun isChoose(): Boolean {
|
||||||
Settings.Secure.getString(contentResolver, Settings.Secure.DEFAULT_INPUT_METHOD).let { id ->
|
Settings.Secure.getString(contentResolver, Settings.Secure.DEFAULT_INPUT_METHOD).let { id ->
|
||||||
return id.startsWith(packageName)
|
return id.startsWith(packageName)
|
||||||
} ?: return false
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -296,4 +294,28 @@ class DetailsActivity : AppCompatActivity() {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点击设置后的回调接口
|
||||||
|
*/
|
||||||
|
override fun applyListener(isSuccess: Boolean, str: String) {
|
||||||
|
binding.themeProgressbar.visibility = View.GONE
|
||||||
|
if (isSuccess) {
|
||||||
|
val lastIndexOf: Int = str.lastIndexOf(AppVal.res_path)
|
||||||
|
val substring = str.subSequence(0, lastIndexOf + AppVal.res_path.length).toString()
|
||||||
|
val edit = sp.edit()
|
||||||
|
edit.run {
|
||||||
|
putString(AppVal.KEY_ALL_PATH, substring)
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
edit.run {
|
||||||
|
putString(dataModel.title, substring)
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
Toast.makeText(this, getString(R.string.succ_apply), Toast.LENGTH_LONG).show()
|
||||||
|
finish()
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, getString(R.string.fail_apply), Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
package com.timber.soft.newkeyboard.listener
|
||||||
|
|
||||||
|
interface ApplyListener {
|
||||||
|
fun applyListener(isSuccess: Boolean, str: String)
|
||||||
|
}
|
||||||
@ -1,5 +0,0 @@
|
|||||||
package com.timber.soft.newkeyboard.tools
|
|
||||||
|
|
||||||
object ImgTools {
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -0,0 +1,317 @@
|
|||||||
|
package com.timber.soft.newkeyboard.view
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.graphics.Rect
|
||||||
|
import android.graphics.drawable.BitmapDrawable
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.graphics.drawable.StateListDrawable
|
||||||
|
import android.inputmethodservice.Keyboard
|
||||||
|
import android.inputmethodservice.KeyboardView
|
||||||
|
import android.os.Build
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.util.Xml
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import com.timber.soft.newkeyboard.R
|
||||||
|
import com.timber.soft.newkeyboard.tools.AppVal
|
||||||
|
import org.xmlpull.v1.XmlPullParser
|
||||||
|
import java.io.File
|
||||||
|
import java.io.StringReader
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
class MyKeyboardView @JvmOverloads constructor(
|
||||||
|
var myContext: Context,
|
||||||
|
attributeSet: AttributeSet? = null,
|
||||||
|
style: Int = 0
|
||||||
|
) : KeyboardView(myContext, attributeSet, style) {
|
||||||
|
|
||||||
|
inner class MyConfig {
|
||||||
|
lateinit var functionBackgroundDraw: Drawable
|
||||||
|
lateinit var spBackgroundDraw: Drawable
|
||||||
|
lateinit var normalBackgroundDraw: Drawable
|
||||||
|
var icShittLock: Drawable? =
|
||||||
|
ContextCompat.getDrawable(context, R.drawable.svg_shift_lit)
|
||||||
|
var icDel: Drawable? =
|
||||||
|
ContextCompat.getDrawable(context, R.drawable.svg_dele)
|
||||||
|
var allBg: Drawable? =
|
||||||
|
ContextCompat.getDrawable(context, R.mipmap.main_bg)
|
||||||
|
var icBshift: Drawable? =
|
||||||
|
ContextCompat.getDrawable(context, R.drawable.svg_shift_lit)
|
||||||
|
var icSshift: Drawable? =
|
||||||
|
ContextCompat.getDrawable(context, R.drawable.svg_shift_lit)
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.M)
|
||||||
|
var keycolor: Int = context.resources.getColor(R.color.white, null)
|
||||||
|
|
||||||
|
private val sp: SharedPreferences = context.getSharedPreferences(
|
||||||
|
AppVal.SHARE_NAME,
|
||||||
|
Context.MODE_PRIVATE
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun getbgic(con: Context, filePath: String): Drawable? {
|
||||||
|
if (!File(filePath).exists()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return BitmapDrawable(con.resources, BitmapFactory.decodeFile(filePath))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getStatus(draw: Drawable, drawPress: Drawable): StateListDrawable {
|
||||||
|
return StateListDrawable().apply {
|
||||||
|
addState(intArrayOf(android.R.attr.state_pressed), drawPress)
|
||||||
|
addState(intArrayOf(), draw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.M)
|
||||||
|
private fun gettextcolor(colorXmlPath: String) {
|
||||||
|
val file = File(colorXmlPath)
|
||||||
|
if (!file.exists()) return
|
||||||
|
val xmlP = Xml.newPullParser()
|
||||||
|
|
||||||
|
xmlP.setInput(StringReader(file.readText()))
|
||||||
|
xmlP.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false)
|
||||||
|
|
||||||
|
var eventT = xmlP.eventType
|
||||||
|
while (eventT != XmlPullParser.END_DOCUMENT) {
|
||||||
|
if (eventT == XmlPullParser.START_TAG && (xmlP.name == "color" || xmlP.name == "item")) {
|
||||||
|
val value = xmlP.getAttributeValue(null, "name")
|
||||||
|
if (value != null && value == AppVal.title_color) {
|
||||||
|
keycolor = Color.parseColor(xmlP.nextText())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eventT = xmlP.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
init {
|
||||||
|
val default =
|
||||||
|
ContextCompat.getDrawable(context, R.drawable.png_keybg)
|
||||||
|
val press = ContextCompat.getDrawable(
|
||||||
|
context,
|
||||||
|
R.drawable.png_keybg_press
|
||||||
|
)
|
||||||
|
if (press != null) {
|
||||||
|
if (default != null) {
|
||||||
|
val listDrawable = StateListDrawable().apply {
|
||||||
|
addState(intArrayOf(android.R.attr.state_pressed), press)
|
||||||
|
addState(intArrayOf(), default)
|
||||||
|
}
|
||||||
|
functionBackgroundDraw = listDrawable
|
||||||
|
normalBackgroundDraw = listDrawable
|
||||||
|
spBackgroundDraw = listDrawable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.M)
|
||||||
|
fun updateConfig(con: Context) {
|
||||||
|
sp.getString(AppVal.KEY_ALL_PATH, "")?.let {
|
||||||
|
getbgic(
|
||||||
|
con,
|
||||||
|
it.plus(AppVal.parent_path).plus(AppVal.title_nor_Bg)
|
||||||
|
)?.let { drawBG ->
|
||||||
|
getbgic(
|
||||||
|
con,
|
||||||
|
it.plus(AppVal.parent_path).plus(AppVal.title_nor_Bg_press)
|
||||||
|
)?.let { drawPressBG ->
|
||||||
|
normalBackgroundDraw = getStatus(drawBG, drawPressBG)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
gettextcolor(it.plus(AppVal.color_path))
|
||||||
|
getbgic(con, it.plus(AppVal.parent_path).plus(AppVal.title_sp_Bg))?.let { drawBG ->
|
||||||
|
getbgic(
|
||||||
|
con,
|
||||||
|
it.plus(AppVal.parent_path).plus(AppVal.title_sp_Bg_press)
|
||||||
|
)?.let { drawPressBG ->
|
||||||
|
spBackgroundDraw = getStatus(drawBG, drawPressBG)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
getbgic(con, it.plus(AppVal.xx_path).plus(AppVal.title_bg))?.run {
|
||||||
|
allBg = this
|
||||||
|
}
|
||||||
|
|
||||||
|
getbgic(
|
||||||
|
con,
|
||||||
|
it.plus(AppVal.parent_path).plus(AppVal.title_fun_Bg)
|
||||||
|
)?.let { drawBG ->
|
||||||
|
getbgic(
|
||||||
|
con,
|
||||||
|
it.plus(AppVal.parent_path).plus(AppVal.title_func_bg_press)
|
||||||
|
)?.let { drawPressBG ->
|
||||||
|
functionBackgroundDraw = getStatus(drawBG, drawPressBG)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getbgic(con, it.plus(AppVal.parent_path).plus(AppVal.title_shitf_ic))?.let {
|
||||||
|
icSshift = it
|
||||||
|
icBshift = it
|
||||||
|
}
|
||||||
|
getbgic(
|
||||||
|
con,
|
||||||
|
it.plus(AppVal.parent_path).plus(AppVal.title_del_ic)
|
||||||
|
)?.let { drawBG ->
|
||||||
|
icDel = getStatus(drawBG, drawBG)
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
getbgic(con, it.plus(AppVal.parent_path).plus(AppVal.title_shitf_ic_lock))?.let {
|
||||||
|
icShittLock = it
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = MyConfig()
|
||||||
|
var shiftStatus = AppVal.Shift_S
|
||||||
|
var xmlMode = AppVal.xml0
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.M)
|
||||||
|
private var myPaint: Paint = Paint().apply {
|
||||||
|
isAntiAlias = true
|
||||||
|
textAlign = Paint.Align.CENTER
|
||||||
|
textSize = myContext.resources.displayMetrics.scaledDensity * 16f
|
||||||
|
color = config.keycolor
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.M)
|
||||||
|
@SuppressLint("SuspiciousIndentation")
|
||||||
|
private fun andDraw(
|
||||||
|
myKey: Keyboard.Key,
|
||||||
|
keyBG: Drawable,
|
||||||
|
icon: Drawable?,
|
||||||
|
canvas: Canvas,
|
||||||
|
) {
|
||||||
|
myKey.run {
|
||||||
|
keyBG.run {
|
||||||
|
bounds = Rect(
|
||||||
|
x.plus(paddingLeft),
|
||||||
|
y.plus(paddingTop),
|
||||||
|
width.plus(x.plus(paddingLeft)),
|
||||||
|
height.plus(y.plus(paddingTop))
|
||||||
|
)
|
||||||
|
state = currentDrawableState
|
||||||
|
draw(canvas)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
myKey.run {
|
||||||
|
icon?.apply {
|
||||||
|
myKey.icon = this
|
||||||
|
|
||||||
|
var icon_w = myKey.icon.intrinsicWidth.toFloat()
|
||||||
|
var icon_wr = icon_w / myKey.width.toFloat()
|
||||||
|
var icon_h = myKey.icon.intrinsicHeight.toFloat()
|
||||||
|
var icon_hr = icon_h / myKey.height.toFloat()
|
||||||
|
|
||||||
|
|
||||||
|
var tep1 = 0f
|
||||||
|
var tep2 = 0f
|
||||||
|
if (icon_wr > icon_hr) {
|
||||||
|
tep2 = icon_wr
|
||||||
|
tep1 = icon_wr.coerceAtLeast(0.5f)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
tep2 = icon_hr
|
||||||
|
tep1 = icon_hr.coerceAtLeast(0.5f)
|
||||||
|
|
||||||
|
}
|
||||||
|
icon_h = (icon_h / tep2) * tep1
|
||||||
|
icon_w = (icon_w / tep2) * tep1
|
||||||
|
myKey.icon.let {
|
||||||
|
it.bounds = Rect().apply {
|
||||||
|
|
||||||
|
top =
|
||||||
|
(myKey.y + paddingTop + (myKey.height - icon_h) / 2f).toInt()
|
||||||
|
left =
|
||||||
|
(myKey.x + paddingLeft + (myKey.width - icon_w) / 2f).toInt()
|
||||||
|
bottom = (top + icon_h).toInt()
|
||||||
|
right = (left + icon_w).toInt()
|
||||||
|
|
||||||
|
}
|
||||||
|
it.draw(canvas)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
myPaint.color = config.keycolor
|
||||||
|
if (!label.isNullOrEmpty()) {
|
||||||
|
val y1 = y.plus(paddingRight).plus((height.div(2f)))
|
||||||
|
.plus((myPaint.textSize.minus(myPaint.descent())).div(2f))
|
||||||
|
val x1 = x.plus(paddingLeft).plus((width.div(2f)))
|
||||||
|
canvas.drawText(label.toString(), x1, y1, myPaint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getCurIc(): Drawable? {
|
||||||
|
return when (shiftStatus) {
|
||||||
|
AppVal.Shift_B_lo -> config.icShittLock
|
||||||
|
AppVal.Shift_B -> config.icBshift
|
||||||
|
AppVal.Shift_S -> config.icSshift
|
||||||
|
else -> config.icSshift
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.M)
|
||||||
|
override fun onDraw(canvas: Canvas) {
|
||||||
|
super.onDraw(canvas)
|
||||||
|
keyboard.keys.forEach {
|
||||||
|
when (it.codes[0]) {
|
||||||
|
Keyboard.KEYCODE_SHIFT -> {
|
||||||
|
andDraw(
|
||||||
|
it,
|
||||||
|
config.functionBackgroundDraw,
|
||||||
|
getCurIc(),
|
||||||
|
canvas
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
AppVal.SHIFT_NUMBER, AppVal.SHIFT_SYMBOL -> {
|
||||||
|
andDraw(it, config.functionBackgroundDraw, null, canvas)
|
||||||
|
}
|
||||||
|
|
||||||
|
Keyboard.KEYCODE_DELETE -> {
|
||||||
|
andDraw(
|
||||||
|
it,
|
||||||
|
config.functionBackgroundDraw,
|
||||||
|
config.icDel,
|
||||||
|
canvas
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Keyboard.KEYCODE_MODE_CHANGE, Keyboard.KEYCODE_DONE -> {
|
||||||
|
andDraw(
|
||||||
|
it,
|
||||||
|
config.functionBackgroundDraw,
|
||||||
|
null,
|
||||||
|
canvas
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
andDraw(it, config.normalBackgroundDraw, null, canvas)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.M)
|
||||||
|
fun upUi(con: Context) {
|
||||||
|
config.updateConfig(con)
|
||||||
|
background = config.allBg
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
BIN
app/src/main/res/drawable/png_keybg.9.png
Normal file
BIN
app/src/main/res/drawable/png_keybg.9.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.8 KiB |
BIN
app/src/main/res/drawable/png_keybg_press.9.png
Normal file
BIN
app/src/main/res/drawable/png_keybg_press.9.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.5 KiB |
16
app/src/main/res/drawable/shape_white_keyboard.xml
Normal file
16
app/src/main/res/drawable/shape_white_keyboard.xml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_pressed="true" >
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<solid android:color="@color/white"/>
|
||||||
|
<corners android:radius="5dp"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item android:state_pressed="false">
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<solid android:color="@color/white"/>
|
||||||
|
<corners android:radius="5dp"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</selector>
|
||||||
9
app/src/main/res/drawable/svg_dele.xml
Normal file
9
app/src/main/res/drawable/svg_dele.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="32dp"
|
||||||
|
android:height="32dp"
|
||||||
|
android:viewportWidth="1024"
|
||||||
|
android:viewportHeight="1024">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFFFF"
|
||||||
|
android:pathData="M830.3,170.3H437.6A178.3,178.3 0,0 0,310.7 223L58.2,475.3a61.5,61.5 0,0 0,0 86.9l252.3,252.3a178.3,178.3 0,0 0,127.1 52.7h392.7A152.9,152.9 0,0 0,983 714.5V323a152.9,152.9 0,0 0,-152.7 -152.7zM760.2,628.8a25.6,25.6 0,0 1,-36.1 36.1L614.1,554.8 504,664.9a25.6,25.6 0,1 1,-36.1 -36.1L578,518.7 467.9,408.5A25.6,25.6 0,1 1,504 372.4l110.1,110.3L724.1,372.4a25.6,25.6 0,0 1,36.1 0,25.6 25.6,0 0,1 0,36.3L650.1,518.7z"/>
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/svg_shift_lit.xml
Normal file
9
app/src/main/res/drawable/svg_shift_lit.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="32dp"
|
||||||
|
android:height="32dp"
|
||||||
|
android:viewportWidth="1024"
|
||||||
|
android:viewportHeight="1024">
|
||||||
|
<path
|
||||||
|
android:pathData="M459.7,155.7l-5.5,4.1 -2.4,2 -3.8,3.5 -2.2,2.2 -3.5,3.8 -258.1,305.9 -4.2,5.4c-28.1,40.2 -20.7,93.3 16,123.6l5.9,4.4 3,2 4.9,2.9 5.6,2.8c13,6.1 26.2,8.9 39.5,8.9l58.6,-0 0,142.6 0.2,6.1c4,48.9 44,85.3 91.9,85.3h212.4l6.1,-0.2 2.7,-0.2 4.8,-0.6 4.7,-0.9c43.4,-9.4 73.7,-46.3 73.7,-89.5l-0,-142.6h58.8l6.1,-0.2c35.6,-2.9 65.4,-24.3 78.7,-55.7a90.9,90.9 0,0 0,-14.2 -94.1L582.4,172.4l-4.6,-5 -1.1,-1.1a92.6,92.6 0,0 0,-117.1 -10.6zM525.3,213.9l2.1,1.8 2.7,2.9 255.9,303.5a21,21 0,0 1,3.4 21.9,22.2 22.2,0 0,1 -18.7,13.3l-3.3,0.1 -127.1,-0v212.4l-0.2,2.6a21.9,21.9 0,0 1,-18.3 18.7l-1.5,0.2 -3.7,0.2L405.6,791.3a22,22 0,0 1,-22.2 -19.9l-0.1,-3.2v-210.9h-128.5l-2.8,-0.2a22.8,22.8 0,0 1,-8 -2.5l-1.7,-1 -3.3,-2.4c-7.4,-6.1 -9.1,-18.4 -2.7,-27.5l2.2,-2.9L494.6,217.4l1.4,-1.5 1.5,-1.3 2.9,-2.2c6.8,-4.6 17.3,-4.1 24.9,1.5z"
|
||||||
|
android:fillColor="#FFFFFF"/>
|
||||||
|
</vector>
|
||||||
19
app/src/main/res/layout/view_input.xml
Normal file
19
app/src/main/res/layout/view_input.xml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<com.timber.soft.newkeyboard.view.MyKeyboardView
|
||||||
|
android:id="@+id/my_custom_input"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:animateLayoutChanges="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:focusableInTouchMode="true"
|
||||||
|
android:keyBackground="@drawable/shape_white_keyboard"
|
||||||
|
android:keyTextSize="15sp"
|
||||||
|
android:labelTextSize="15sp"
|
||||||
|
android:paddingTop="3dp"
|
||||||
|
android:paddingBottom="3dp" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
BIN
app/src/main/res/mipmap-xxxhdpi/main_bg.png
Normal file
BIN
app/src/main/res/mipmap-xxxhdpi/main_bg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 86 KiB |
@ -11,4 +11,5 @@
|
|||||||
<string name="apply_step2_eanble">Step 2: Enable</string>
|
<string name="apply_step2_eanble">Step 2: Enable</string>
|
||||||
<string name="succ_apply">Application successful</string>
|
<string name="succ_apply">Application successful</string>
|
||||||
<string name="fail_apply">Application failed, please try again</string>
|
<string name="fail_apply">Application failed, please try again</string>
|
||||||
|
<string name="text_promote">For normal use, please enter the setting to complete the setting steps.</string>
|
||||||
</resources>
|
</resources>
|
||||||
8
app/src/main/res/xml/key_view.xml
Normal file
8
app/src/main/res/xml/key_view.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<input-method xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<subtype
|
||||||
|
android:icon="@drawable/ic_launcher_background"
|
||||||
|
android:imeSubtypeLocale="en_US"
|
||||||
|
android:imeSubtypeMode="keyboard"
|
||||||
|
android:label="@string/app_name" />
|
||||||
|
</input-method>
|
||||||
151
app/src/main/res/xml/keyboard_letter.xml
Normal file
151
app/src/main/res/xml/keyboard_letter.xml
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:keyWidth="10%p"
|
||||||
|
android:keyHeight="46dp"
|
||||||
|
android:verticalGap="3dp">
|
||||||
|
|
||||||
|
<Row
|
||||||
|
android:horizontalGap="0.5%"
|
||||||
|
android:keyWidth="9.45%"
|
||||||
|
android:keyHeight="46dp"
|
||||||
|
android:rowEdgeFlags="top">
|
||||||
|
<Key
|
||||||
|
android:codes="113"
|
||||||
|
android:keyEdgeFlags="left"
|
||||||
|
android:keyLabel="q" />
|
||||||
|
<Key
|
||||||
|
android:codes="119"
|
||||||
|
android:keyLabel="w" />
|
||||||
|
<Key
|
||||||
|
android:codes="101"
|
||||||
|
android:keyLabel="e" />
|
||||||
|
<Key
|
||||||
|
android:codes="114"
|
||||||
|
android:keyLabel="r" />
|
||||||
|
<Key
|
||||||
|
android:codes="116"
|
||||||
|
android:keyLabel="t" />
|
||||||
|
<Key
|
||||||
|
android:codes="121"
|
||||||
|
android:keyLabel="y" />
|
||||||
|
<Key
|
||||||
|
android:codes="117"
|
||||||
|
android:keyLabel="u" />
|
||||||
|
<Key
|
||||||
|
android:codes="105"
|
||||||
|
android:keyLabel="i" />
|
||||||
|
<Key
|
||||||
|
android:codes="111"
|
||||||
|
android:keyLabel="o" />
|
||||||
|
<Key
|
||||||
|
android:codes="112"
|
||||||
|
android:keyEdgeFlags="right"
|
||||||
|
android:keyLabel="p" />
|
||||||
|
</Row>
|
||||||
|
<Row
|
||||||
|
android:horizontalGap="0.5%"
|
||||||
|
android:keyWidth="9.444444%"
|
||||||
|
android:keyHeight="46dp">
|
||||||
|
<Key
|
||||||
|
android:codes="97"
|
||||||
|
android:horizontalGap="5.5%"
|
||||||
|
android:keyLabel="a" />
|
||||||
|
<Key
|
||||||
|
android:codes="115"
|
||||||
|
android:keyLabel="s" />
|
||||||
|
<Key
|
||||||
|
android:codes="100"
|
||||||
|
android:keyLabel="d" />
|
||||||
|
<Key
|
||||||
|
android:codes="102"
|
||||||
|
android:keyLabel="f" />
|
||||||
|
<Key
|
||||||
|
android:codes="103"
|
||||||
|
android:keyLabel="g" />
|
||||||
|
<Key
|
||||||
|
android:codes="104"
|
||||||
|
android:keyLabel="h" />
|
||||||
|
<Key
|
||||||
|
android:codes="106"
|
||||||
|
android:keyLabel="j" />
|
||||||
|
<Key
|
||||||
|
android:codes="107"
|
||||||
|
android:keyLabel="k" />
|
||||||
|
<Key
|
||||||
|
android:codes="108"
|
||||||
|
android:keyLabel="l" />
|
||||||
|
</Row>
|
||||||
|
<Row
|
||||||
|
android:horizontalGap="0.5%"
|
||||||
|
android:keyWidth="9.5%"
|
||||||
|
android:keyHeight="46dp">
|
||||||
|
<!--shift-->
|
||||||
|
<Key
|
||||||
|
android:codes="-1"
|
||||||
|
android:isModifier="true"
|
||||||
|
android:isSticky="true"
|
||||||
|
android:keyWidth="14.25%"
|
||||||
|
android:keyEdgeFlags="left" />
|
||||||
|
<Key
|
||||||
|
android:codes="122"
|
||||||
|
android:keyLabel="z" />
|
||||||
|
<Key
|
||||||
|
android:codes="120"
|
||||||
|
android:keyLabel="x" />
|
||||||
|
<Key
|
||||||
|
android:codes="99"
|
||||||
|
android:keyLabel="c" />
|
||||||
|
<Key
|
||||||
|
android:codes="118"
|
||||||
|
android:keyLabel="v" />
|
||||||
|
<Key
|
||||||
|
android:codes="98"
|
||||||
|
android:keyLabel="b" />
|
||||||
|
<Key
|
||||||
|
android:codes="110"
|
||||||
|
android:keyLabel="n" />
|
||||||
|
<Key
|
||||||
|
android:codes="109"
|
||||||
|
android:keyLabel="m" />
|
||||||
|
<!--delete-->
|
||||||
|
<Key
|
||||||
|
android:codes="-5"
|
||||||
|
android:isModifier="true"
|
||||||
|
android:isRepeatable="true"
|
||||||
|
android:keyWidth="14.25%"
|
||||||
|
android:keyEdgeFlags="right" />
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<Row
|
||||||
|
android:horizontalGap="0.5%"
|
||||||
|
android:keyWidth="9.5%"
|
||||||
|
android:keyHeight="46dp"
|
||||||
|
android:rowEdgeFlags="bottom">
|
||||||
|
<!--mode change 切到模式2-->
|
||||||
|
<Key
|
||||||
|
android:codes="-2"
|
||||||
|
android:keyWidth="14.25%"
|
||||||
|
android:keyEdgeFlags="left"
|
||||||
|
android:keyLabel="\?123" />
|
||||||
|
|
||||||
|
<Key
|
||||||
|
android:codes="44"
|
||||||
|
android:keyLabel="," />
|
||||||
|
<Key
|
||||||
|
android:codes="32"
|
||||||
|
android:keyWidth="49.5%"
|
||||||
|
android:keyLabel="English" />
|
||||||
|
|
||||||
|
<Key
|
||||||
|
android:codes="46"
|
||||||
|
android:keyLabel="." />
|
||||||
|
|
||||||
|
<!--Done-->
|
||||||
|
<Key
|
||||||
|
android:codes="-4"
|
||||||
|
android:keyWidth="14.25%"
|
||||||
|
android:keyEdgeFlags="right"
|
||||||
|
android:keyLabel="Done" />
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
</Keyboard>
|
||||||
149
app/src/main/res/xml/keyboard_number.xml
Normal file
149
app/src/main/res/xml/keyboard_number.xml
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:verticalGap="3dp">
|
||||||
|
|
||||||
|
<Row
|
||||||
|
android:keyWidth="9.45%"
|
||||||
|
android:keyHeight="46dp"
|
||||||
|
android:horizontalGap="0.5%"
|
||||||
|
android:rowEdgeFlags="top">
|
||||||
|
<Key
|
||||||
|
android:codes="49"
|
||||||
|
android:keyEdgeFlags="left"
|
||||||
|
android:keyLabel="1" />
|
||||||
|
<Key android:keyLabel="2"
|
||||||
|
android:codes="50"/>
|
||||||
|
|
||||||
|
<Key android:keyLabel="3"
|
||||||
|
android:codes="51"/>
|
||||||
|
<Key android:keyLabel="4"
|
||||||
|
android:codes="52"/>
|
||||||
|
<Key android:keyLabel="5"
|
||||||
|
android:codes="53"/>
|
||||||
|
|
||||||
|
<Key android:keyLabel="6"
|
||||||
|
android:codes="54"/>
|
||||||
|
|
||||||
|
<Key android:keyLabel="7"
|
||||||
|
android:codes="55"/>
|
||||||
|
<Key android:keyLabel="8"
|
||||||
|
android:codes="56"/>
|
||||||
|
<Key android:keyLabel="9"
|
||||||
|
android:codes="57"/>
|
||||||
|
<Key
|
||||||
|
android:keyEdgeFlags="right"
|
||||||
|
android:codes="48"
|
||||||
|
android:keyLabel="0" />
|
||||||
|
</Row>
|
||||||
|
<Row
|
||||||
|
android:keyWidth="9.444444%"
|
||||||
|
android:keyHeight="46dp"
|
||||||
|
android:horizontalGap="0.5%">
|
||||||
|
<Key
|
||||||
|
android:keyEdgeFlags="left"
|
||||||
|
android:keyLabel="\@"
|
||||||
|
android:horizontalGap="5.5%"
|
||||||
|
android:codes="64"/>
|
||||||
|
<Key android:keyLabel="#"
|
||||||
|
android:codes="35"/>
|
||||||
|
<Key android:keyLabel="\$"
|
||||||
|
android:codes="36"/>
|
||||||
|
|
||||||
|
<Key android:keyLabel="%"
|
||||||
|
android:codes="37"/>
|
||||||
|
|
||||||
|
<Key android:keyLabel="&"
|
||||||
|
android:codes="38"/>
|
||||||
|
|
||||||
|
<Key android:keyLabel="-"
|
||||||
|
android:codes="45"/>
|
||||||
|
|
||||||
|
<Key android:keyLabel="+"
|
||||||
|
android:codes="43"/>
|
||||||
|
|
||||||
|
<Key android:keyLabel="("
|
||||||
|
android:codes="40"/>
|
||||||
|
<Key
|
||||||
|
android:keyEdgeFlags="right"
|
||||||
|
android:codes="41"
|
||||||
|
android:keyLabel=")" />
|
||||||
|
</Row>
|
||||||
|
<Row
|
||||||
|
android:keyHeight="46dp"
|
||||||
|
android:keyWidth="9.5%"
|
||||||
|
android:horizontalGap="0.5%">
|
||||||
|
<!--more 切换到符号键盘模式-->
|
||||||
|
<Key
|
||||||
|
android:codes="-300"
|
||||||
|
android:isModifier="true"
|
||||||
|
android:keyWidth="14.25%"
|
||||||
|
android:keyLabel="more"
|
||||||
|
android:keyEdgeFlags="left" />
|
||||||
|
|
||||||
|
<Key android:keyLabel="*"
|
||||||
|
android:codes="42" />
|
||||||
|
|
||||||
|
<!-- "-->
|
||||||
|
<Key android:keyLabel="""
|
||||||
|
android:codes="34" />
|
||||||
|
|
||||||
|
<Key android:keyLabel="'"
|
||||||
|
android:codes="39" />
|
||||||
|
|
||||||
|
<Key android:keyLabel=":"
|
||||||
|
android:codes="58" />
|
||||||
|
|
||||||
|
<Key android:keyLabel=";"
|
||||||
|
android:codes="59" />
|
||||||
|
|
||||||
|
<Key android:keyLabel="!"
|
||||||
|
android:codes="33" />
|
||||||
|
|
||||||
|
<Key android:keyLabel="\?"
|
||||||
|
android:codes="63" />
|
||||||
|
<!--delete-->
|
||||||
|
<Key
|
||||||
|
android:codes="-5"
|
||||||
|
android:keyWidth="14.25%"
|
||||||
|
android:isModifier="true"
|
||||||
|
android:isRepeatable="true"
|
||||||
|
android:keyEdgeFlags="right" />
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<Row
|
||||||
|
android:horizontalGap="0.5%"
|
||||||
|
android:keyHeight="46dp"
|
||||||
|
android:rowEdgeFlags="bottom"
|
||||||
|
android:keyWidth="9.5%">
|
||||||
|
<!-- 返回模式1 -->
|
||||||
|
<Key
|
||||||
|
android:codes="-2"
|
||||||
|
android:keyWidth="14.25%"
|
||||||
|
android:keyEdgeFlags="left"
|
||||||
|
android:keyLabel="ABC" />
|
||||||
|
|
||||||
|
<Key android:keyLabel=","
|
||||||
|
android:codes="44" />
|
||||||
|
|
||||||
|
<Key android:keyLabel="_"
|
||||||
|
android:codes="95" />
|
||||||
|
<Key
|
||||||
|
android:codes="32"
|
||||||
|
android:keyWidth="29.5%"
|
||||||
|
android:keyLabel="English" />
|
||||||
|
|
||||||
|
<Key android:keyLabel="/"
|
||||||
|
android:codes="47" />
|
||||||
|
|
||||||
|
<Key android:keyLabel="."
|
||||||
|
android:codes="46" />
|
||||||
|
|
||||||
|
<!--Done-->
|
||||||
|
<Key
|
||||||
|
android:codes="-4"
|
||||||
|
android:keyLabel="Done"
|
||||||
|
android:keyWidth="14.25%"
|
||||||
|
android:keyEdgeFlags="right" />
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
</Keyboard>
|
||||||
157
app/src/main/res/xml/keyboard_symbol.xml
Normal file
157
app/src/main/res/xml/keyboard_symbol.xml
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:verticalGap="3dp">
|
||||||
|
|
||||||
|
<Row
|
||||||
|
android:keyWidth="9.45%"
|
||||||
|
android:keyHeight="46dp"
|
||||||
|
android:horizontalGap="0.5%"
|
||||||
|
android:rowEdgeFlags="top">
|
||||||
|
<Key
|
||||||
|
android:codes="126"
|
||||||
|
android:keyEdgeFlags="left"
|
||||||
|
android:keyLabel="~" />
|
||||||
|
|
||||||
|
<Key android:keyLabel="`"
|
||||||
|
android:codes="96"/>
|
||||||
|
|
||||||
|
<Key android:keyLabel="|"
|
||||||
|
android:codes="124"/>
|
||||||
|
|
||||||
|
<Key android:keyLabel="•"
|
||||||
|
android:codes="149"/>
|
||||||
|
|
||||||
|
<Key android:keyLabel="✔"
|
||||||
|
android:codes="10004"/>
|
||||||
|
|
||||||
|
<Key android:keyLabel="π"
|
||||||
|
android:codes="960"/>
|
||||||
|
|
||||||
|
<Key android:keyLabel="÷"
|
||||||
|
android:codes="247"/>
|
||||||
|
|
||||||
|
<Key android:keyLabel="×"
|
||||||
|
android:codes="215"/>
|
||||||
|
|
||||||
|
<Key android:keyLabel="¶"
|
||||||
|
android:codes="182"/>
|
||||||
|
<Key
|
||||||
|
android:keyEdgeFlags="right"
|
||||||
|
android:codes="8710"
|
||||||
|
android:keyLabel="∆" />
|
||||||
|
</Row>
|
||||||
|
<Row
|
||||||
|
android:keyWidth="9.444444%"
|
||||||
|
android:keyHeight="46dp"
|
||||||
|
android:horizontalGap="0.5%">
|
||||||
|
<Key
|
||||||
|
android:keyEdgeFlags="left"
|
||||||
|
android:keyLabel="£"
|
||||||
|
android:horizontalGap="5.5%"
|
||||||
|
android:codes="163"/>
|
||||||
|
|
||||||
|
<Key android:keyLabel="¢"
|
||||||
|
android:codes="65504"/>
|
||||||
|
|
||||||
|
<Key android:keyLabel="€"
|
||||||
|
android:codes="8364"/>
|
||||||
|
|
||||||
|
<Key android:keyLabel="¥"
|
||||||
|
android:codes="165"/>
|
||||||
|
|
||||||
|
<Key android:keyLabel="^"
|
||||||
|
android:codes="94"/>
|
||||||
|
|
||||||
|
<Key android:keyLabel="°"
|
||||||
|
android:codes="176"/>
|
||||||
|
|
||||||
|
<Key android:keyLabel="="
|
||||||
|
android:codes="61"/>
|
||||||
|
|
||||||
|
<Key android:keyLabel="{"
|
||||||
|
android:codes="123"/>
|
||||||
|
<Key
|
||||||
|
android:keyEdgeFlags="right"
|
||||||
|
android:codes="125"
|
||||||
|
android:keyLabel="}" />
|
||||||
|
</Row>
|
||||||
|
<Row
|
||||||
|
android:keyHeight="46dp"
|
||||||
|
android:keyWidth="9.5%"
|
||||||
|
android:horizontalGap="0.5%">
|
||||||
|
<!-- shift-->
|
||||||
|
<Key
|
||||||
|
android:codes="-301"
|
||||||
|
android:isModifier="true"
|
||||||
|
android:keyWidth="14.25%"
|
||||||
|
android:keyLabel="\?123"
|
||||||
|
android:keyEdgeFlags="left" />
|
||||||
|
|
||||||
|
<Key android:keyLabel="\\"
|
||||||
|
android:codes="92" />
|
||||||
|
|
||||||
|
<Key android:keyLabel="Ⓒ"
|
||||||
|
android:codes="9400" />
|
||||||
|
|
||||||
|
<Key android:keyLabel="®"
|
||||||
|
android:codes="174" />
|
||||||
|
|
||||||
|
<Key android:keyLabel="™"
|
||||||
|
android:codes="8482" />
|
||||||
|
|
||||||
|
<Key android:keyLabel="℅"
|
||||||
|
android:codes="8453" />
|
||||||
|
|
||||||
|
<Key android:keyLabel="["
|
||||||
|
android:codes="91" />
|
||||||
|
|
||||||
|
<Key android:keyLabel="]"
|
||||||
|
android:codes="93" />
|
||||||
|
|
||||||
|
|
||||||
|
<!--delete-->
|
||||||
|
<Key
|
||||||
|
android:codes="-5"
|
||||||
|
android:keyWidth="14.25%"
|
||||||
|
android:isModifier="true"
|
||||||
|
android:isRepeatable="true"
|
||||||
|
android:keyEdgeFlags="right" />
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<Row
|
||||||
|
android:horizontalGap="0.5%"
|
||||||
|
android:keyHeight="46dp"
|
||||||
|
android:rowEdgeFlags="bottom"
|
||||||
|
android:keyWidth="9.5%">
|
||||||
|
<!--mode change 回到模式1-->
|
||||||
|
<Key
|
||||||
|
android:codes="-2"
|
||||||
|
android:keyWidth="14.25%"
|
||||||
|
android:keyEdgeFlags="left"
|
||||||
|
android:keyLabel="ABC" />
|
||||||
|
|
||||||
|
<Key android:keyLabel=","
|
||||||
|
android:codes="46" />
|
||||||
|
|
||||||
|
<Key android:keyLabel="<"
|
||||||
|
android:codes="60" />
|
||||||
|
<Key
|
||||||
|
android:codes="32"
|
||||||
|
android:keyWidth="29.5%"
|
||||||
|
android:keyLabel="English" />
|
||||||
|
|
||||||
|
<Key android:keyLabel=">"
|
||||||
|
android:codes="62" />
|
||||||
|
|
||||||
|
<Key android:keyLabel="."
|
||||||
|
android:codes="46" />
|
||||||
|
|
||||||
|
<!--Done-->
|
||||||
|
<Key
|
||||||
|
android:codes="-4"
|
||||||
|
android:keyLabel="Done"
|
||||||
|
android:keyWidth="14.25%"
|
||||||
|
android:keyEdgeFlags="right" />
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
</Keyboard>
|
||||||
Loading…
Reference in New Issue
Block a user