接入tradplus
18
.gitignore
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
/local.properties
|
||||||
|
/.idea/caches
|
||||||
|
/.idea/libraries
|
||||||
|
/.idea/modules.xml
|
||||||
|
/.idea/workspace.xml
|
||||||
|
/.idea/navEditor.xml
|
||||||
|
/.idea/assetWizardSettings.xml
|
||||||
|
.DS_Store
|
||||||
|
/build
|
||||||
|
/captures
|
||||||
|
.externalNativeBuild
|
||||||
|
.cxx
|
||||||
|
local.properties
|
||||||
|
.idea/
|
||||||
|
.safedk/
|
||||||
|
app/debug/
|
||||||
1
app/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
BIN
app/RainbowBoard
Normal file
175
app/build.gradle.kts
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
|
||||||
|
import java.util.Date
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("com.android.application")
|
||||||
|
id("org.jetbrains.kotlin.android")
|
||||||
|
kotlin("kapt")
|
||||||
|
id ("kotlin-android")
|
||||||
|
id("com.google.gms.google-services")
|
||||||
|
id("com.google.firebase.crashlytics")
|
||||||
|
}
|
||||||
|
|
||||||
|
val timestamp = SimpleDateFormat("MM_dd_HH_mm").format(Date())
|
||||||
|
android {
|
||||||
|
namespace = "com.rainbow.app.keyboard"
|
||||||
|
compileSdk = 36
|
||||||
|
defaultConfig {
|
||||||
|
applicationId = "com.rainbow.app.keyboard"
|
||||||
|
minSdk = 24
|
||||||
|
targetSdk = 36
|
||||||
|
versionCode = 2
|
||||||
|
versionName = "1.1"
|
||||||
|
setProperty(
|
||||||
|
"archivesBaseName",
|
||||||
|
"RainbowBoard_V" + versionName + "(${versionCode})_$timestamp"
|
||||||
|
)
|
||||||
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
isMinifyEnabled = true
|
||||||
|
proguardFiles(
|
||||||
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
|
"proguard-rules.pro"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "17"
|
||||||
|
}
|
||||||
|
buildFeatures {
|
||||||
|
buildConfig = true
|
||||||
|
viewBinding = true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
implementation("androidx.core:core-ktx:1.15.0")
|
||||||
|
implementation("androidx.appcompat:appcompat:1.7.1")
|
||||||
|
implementation("com.google.android.material:material:1.13.0")
|
||||||
|
implementation("androidx.constraintlayout:constraintlayout:2.2.1")
|
||||||
|
implementation("androidx.activity:activity:1.12.1")
|
||||||
|
testImplementation("junit:junit:4.13.2")
|
||||||
|
androidTestImplementation("androidx.test.ext:junit:1.3.0")
|
||||||
|
androidTestImplementation("androidx.test.espresso:espresso-core:3.7.0")
|
||||||
|
|
||||||
|
|
||||||
|
implementation("com.squareup.okhttp3:okhttp:5.3.2")
|
||||||
|
implementation("com.github.bumptech.glide:glide:5.0.5")
|
||||||
|
implementation ("jp.wasabeef:glide-transformations:4.3.0")
|
||||||
|
//Glide支持webp动图的库
|
||||||
|
implementation("com.github.zjupure:webpdecoder:2.7.4.16.0")
|
||||||
|
implementation("com.github.omicronapps:7-Zip-JBinding-4Android:Release-16.02-2.03")
|
||||||
|
|
||||||
|
val room_version = "2.8.4"
|
||||||
|
implementation ("androidx.room:room-runtime:$room_version")
|
||||||
|
kapt("androidx.room:room-compiler:$room_version")
|
||||||
|
implementation ("androidx.room:room-ktx:$room_version")
|
||||||
|
implementation ("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
|
||||||
|
implementation ("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2")
|
||||||
|
|
||||||
|
implementation("com.google.android.material:material:1.8.0")
|
||||||
|
|
||||||
|
|
||||||
|
implementation(files("libs/TradPlusLibrary_01_04_12_20-release.aar"))
|
||||||
|
implementation(files("libs/UpLoadLibrary_12_03_15_13-release.aar"))
|
||||||
|
|
||||||
|
implementation ("com.squareup.okhttp3:okhttp:4.12.0")
|
||||||
|
implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
|
||||||
|
|
||||||
|
implementation ("com.google.android.gms:play-services-ads-identifier:18.0.1")
|
||||||
|
// TradPlus
|
||||||
|
implementation("com.tradplusad:tradplus:15.2.0.1")
|
||||||
|
implementation("androidx.legacy:legacy-support-v4:1.0.0")
|
||||||
|
implementation("androidx.appcompat:appcompat:1.3.0-alpha02")
|
||||||
|
|
||||||
|
// IronSource
|
||||||
|
implementation("com.ironsource.sdk:mediationsdk:9.0.0")
|
||||||
|
implementation("com.tradplusad:tradplus-ironsource:10.15.2.0.1")
|
||||||
|
|
||||||
|
// Pangle
|
||||||
|
implementation("com.tradplusad:tradplus-pangle:19.15.2.0.1")
|
||||||
|
implementation("com.pangle.global:pag-sdk:7.8.0.7")
|
||||||
|
|
||||||
|
// UnityAds
|
||||||
|
implementation("com.tradplusad:tradplus-unity:5.15.2.0.1")
|
||||||
|
implementation("com.unity3d.ads:unity-ads:4.16.3")
|
||||||
|
|
||||||
|
// Chartboost
|
||||||
|
// implementation("com.tradplusad:tradplus-chartboostx:15.15.2.0.1")
|
||||||
|
// implementation("com.chartboost:chartboost-sdk:9.10.0")
|
||||||
|
// implementation("com.google.android.gms:play-services-ads-identifier:17.0.0")
|
||||||
|
// implementation("com.google.android.gms:play-services-base:17.4.0")
|
||||||
|
|
||||||
|
|
||||||
|
//上面新版本下载失败用旧版本
|
||||||
|
implementation("com.tradplusad:tradplus-chartboostx:15.14.5.0.1")
|
||||||
|
implementation("com.chartboost:chartboost-sdk:9.8.3")
|
||||||
|
implementation("com.google.android.gms:play-services-ads-identifier:17.0.0")
|
||||||
|
implementation("com.google.android.gms:play-services-base:17.4.0")
|
||||||
|
|
||||||
|
// InMobi
|
||||||
|
implementation("com.tradplusad:tradplus-inmobix:23.15.2.0.1")
|
||||||
|
implementation("com.inmobi.monetization:inmobi-ads-kotlin:11.0.0")
|
||||||
|
implementation("com.squareup.okhttp3:okhttp:3.14.9")
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4")
|
||||||
|
implementation("androidx.core:core-ktx:1.5.0")
|
||||||
|
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.0")
|
||||||
|
|
||||||
|
implementation("com.google.android.gms:play-services-ads-identifier:18.0.1")
|
||||||
|
implementation("com.google.android.gms:play-services-location:21.0.1") // optional
|
||||||
|
implementation("androidx.browser:browser:1.8.0")
|
||||||
|
implementation("com.squareup.picasso:picasso:2.8")
|
||||||
|
implementation("androidx.viewpager:viewpager:1.0.0")
|
||||||
|
implementation("androidx.recyclerview:recyclerview:1.2.1")
|
||||||
|
|
||||||
|
// Fyber
|
||||||
|
implementation("com.fyber:marketplace-sdk:8.4.0")
|
||||||
|
implementation("com.tradplusad:tradplus-fyber:24.15.2.0.1")
|
||||||
|
implementation("com.google.android.gms:play-services-ads-identifier:17.0.0")
|
||||||
|
implementation("com.google.android.gms:play-services-base:17.4.0")
|
||||||
|
|
||||||
|
// Mintegral
|
||||||
|
implementation("com.tradplusad:tradplus-mintegralx_overseas:18.15.2.0.1")
|
||||||
|
implementation("androidx.recyclerview:recyclerview:1.1.0")
|
||||||
|
implementation("com.mbridge.msdk.oversea:mbridge_android_sdk:16.10.11")
|
||||||
|
|
||||||
|
// Liftoff (Vungle)
|
||||||
|
implementation("com.tradplusad:tradplus-vunglex:7.15.2.0.1")
|
||||||
|
implementation("com.vungle:vungle-ads:7.6.0")
|
||||||
|
|
||||||
|
// Bigo
|
||||||
|
implementation("com.bigossp:bigo-ads:5.5.2")
|
||||||
|
implementation("com.tradplusad:tradplus-bigo:57.15.2.0.1")
|
||||||
|
|
||||||
|
// Cross Promotion
|
||||||
|
implementation("com.tradplusad:tradplus-crosspromotion:27.15.2.0.1")
|
||||||
|
|
||||||
|
// TP Exchange(注意与主包版本同步)
|
||||||
|
implementation("com.google.code.gson:gson:2.8.6")
|
||||||
|
implementation("com.tradplusad:tp_exchange:40.15.2.0.1")
|
||||||
|
|
||||||
|
// Google UMP
|
||||||
|
implementation ("com.google.android.ump:user-messaging-platform:3.2.0")
|
||||||
|
|
||||||
|
// TradPlus Tools
|
||||||
|
// implementation 'com.tradplusad:tradplus-tool:1.1.4'
|
||||||
|
|
||||||
|
|
||||||
|
implementation(platform("com.google.firebase:firebase-bom:33.7.0"))
|
||||||
|
implementation("com.google.firebase:firebase-crashlytics")
|
||||||
|
implementation("com.google.firebase:firebase-analytics")
|
||||||
|
implementation("com.google.firebase:firebase-config")
|
||||||
|
|
||||||
|
}
|
||||||
29
app/google-services.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"project_info": {
|
||||||
|
"project_number": "772081232638",
|
||||||
|
"project_id": "rainbowboard-96ae6",
|
||||||
|
"storage_bucket": "rainbowboard-96ae6.firebasestorage.app"
|
||||||
|
},
|
||||||
|
"client": [
|
||||||
|
{
|
||||||
|
"client_info": {
|
||||||
|
"mobilesdk_app_id": "1:772081232638:android:491f6c55ffdfbf98e913af",
|
||||||
|
"android_client_info": {
|
||||||
|
"package_name": "com.rainbow.app.keyboard"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"oauth_client": [],
|
||||||
|
"api_key": [
|
||||||
|
{
|
||||||
|
"current_key": "AIzaSyCB9XOD2_jWWAG5gLK_Ob9AgQHbFMU-V9M"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"services": {
|
||||||
|
"appinvite_service": {
|
||||||
|
"other_platform_oauth_client": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"configuration_version": "1"
|
||||||
|
}
|
||||||
BIN
app/libs/TradPlusLibrary_01_04_12_20-release.aar
Normal file
BIN
app/libs/UpLoadLibrary_12_03_15_13-release.aar
Normal file
24
app/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
|
||||||
|
# 保持 Room 的核心类不被混淆
|
||||||
|
-keep class androidx.room.** { *; }
|
||||||
|
-keep class androidx.sqlite.db.** { *; }
|
||||||
|
|
||||||
|
# 保持 Room 数据库类的基本结构
|
||||||
|
-keep class * extends androidx.room.RoomDatabase { *; }
|
||||||
|
|
||||||
|
# 保持 Room DAO 接口
|
||||||
|
-keep @androidx.room.Dao interface * { *; }
|
||||||
|
-keep @androidx.room.Dao class * { *; }
|
||||||
|
|
||||||
|
# 保持 Room 实体类
|
||||||
|
-keep @androidx.room.Entity class * { *; }
|
||||||
|
|
||||||
|
# 保持 Room 的注解类
|
||||||
|
-keep @androidx.room.Database class * { *; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-keep class com.omicronapplications.** { *; }
|
||||||
|
-keep class net.sf.sevenzipjbinding.** { *; }
|
||||||
|
|
||||||
|
|
||||||
37
app/release/output-metadata.json
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"version": 3,
|
||||||
|
"artifactType": {
|
||||||
|
"type": "APK",
|
||||||
|
"kind": "Directory"
|
||||||
|
},
|
||||||
|
"applicationId": "com.rainbow.app.keyboard",
|
||||||
|
"variantName": "release",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"type": "SINGLE",
|
||||||
|
"filters": [],
|
||||||
|
"attributes": [],
|
||||||
|
"versionCode": 2,
|
||||||
|
"versionName": "1.1",
|
||||||
|
"outputFile": "RainbowBoard_V1.1(2)_01_12_17_17-release.apk"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"elementType": "File",
|
||||||
|
"baselineProfiles": [
|
||||||
|
{
|
||||||
|
"minApi": 28,
|
||||||
|
"maxApi": 30,
|
||||||
|
"baselineProfiles": [
|
||||||
|
"baselineProfiles/1/RainbowBoard_V1.1(2)_01_12_17_17-release.dm"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"minApi": 31,
|
||||||
|
"maxApi": 2147483647,
|
||||||
|
"baselineProfiles": [
|
||||||
|
"baselineProfiles/0/RainbowBoard_V1.1(2)_01_12_17_17-release.dm"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"minSdkVersionForDexing": 24
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package com.keyboard.skinning.cool
|
||||||
|
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
import org.junit.Assert.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instrumented test, which will execute on an Android device.
|
||||||
|
*
|
||||||
|
* See [testing documentation](http://d.android.com/tools/testing).
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class ExampleInstrumentedTest {
|
||||||
|
@Test
|
||||||
|
fun useAppContext() {
|
||||||
|
// Context of the app under test.
|
||||||
|
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
|
assertEquals("com.ba.ali.apps.keyboard", appContext.packageName)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
68
app/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.AD_ID" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
|
<application
|
||||||
|
android:name=".App"
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:icon="@mipmap/logo"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:roundIcon="@mipmap/logo"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:networkSecurityConfig="@xml/net"
|
||||||
|
tools:replace="android:networkSecurityConfig"
|
||||||
|
android:theme="@style/MyKeyBoard"
|
||||||
|
tools:targetApi="31">
|
||||||
|
<activity
|
||||||
|
android:name=".uiactivity.SettingsActivity"
|
||||||
|
android:screenOrientation="portrait"
|
||||||
|
android:theme="@style/Theme.AppCompat.Dialog"
|
||||||
|
android:windowSoftInputMode="adjustResize"
|
||||||
|
android:exported="false" />
|
||||||
|
<activity
|
||||||
|
android:name=".uiactivity.LaunchActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:screenOrientation="portrait">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:name=".uiactivity.DetailkbActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:screenOrientation="portrait" />
|
||||||
|
<activity
|
||||||
|
android:name=".uiactivity.EditVActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:screenOrientation="portrait"
|
||||||
|
android:windowSoftInputMode="stateAlwaysVisible|adjustResize|adjustPan" />
|
||||||
|
<activity
|
||||||
|
android:name=".uiactivity.MainActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:launchMode="singleTask"
|
||||||
|
android:screenOrientation="portrait" />
|
||||||
|
<activity
|
||||||
|
android:name=".uiactivity.CollectionActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:screenOrientation="portrait" />
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name=".helper.KeybinputService"
|
||||||
|
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/keyborad_xml" />
|
||||||
|
</service>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
21607
app/src/main/assets/kbres.json
Normal file
123
app/src/main/java/com/rainbow/app/keyboard/App.kt
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
package com.rainbow.app.keyboard
|
||||||
|
|
||||||
|
//import com.pretty.keyboard.theme.keyboard.helper.ObjectBox
|
||||||
|
import android.app.Application
|
||||||
|
import android.graphics.Typeface
|
||||||
|
import com.rainbow.app.keyboard.javabean.DetailsjavaBean
|
||||||
|
import com.rainbow.app.keyboard.javabean.WrapperjavaBean
|
||||||
|
import com.up.uploadlibrary.UpLoadManager
|
||||||
|
import org.json.JSONArray
|
||||||
|
import java.io.BufferedReader
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.InputStreamReader
|
||||||
|
import java.io.StringWriter
|
||||||
|
|
||||||
|
|
||||||
|
class App : Application() {
|
||||||
|
companion object {
|
||||||
|
lateinit var appInstance: App
|
||||||
|
|
||||||
|
lateinit var list: MutableList<WrapperjavaBean>
|
||||||
|
|
||||||
|
const val TAG = "-----------------"
|
||||||
|
var defaultFont: Typeface? = null
|
||||||
|
const val DB_VERSION = 3
|
||||||
|
const val DB_NAME = "db_name"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
appInstance = this
|
||||||
|
UpLoadManager.init(this, TAG) { s: String?, s2: String? -> null }
|
||||||
|
|
||||||
|
dealFile()
|
||||||
|
}
|
||||||
|
private fun dealFile() {
|
||||||
|
val openFile = appInstance.assets.open("kbres.json")
|
||||||
|
val jsonString = getJsonString(openFile)
|
||||||
|
if (jsonString != null) {
|
||||||
|
resolveJsonString(jsonString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun resolveJsonString(string: String) {
|
||||||
|
val jsonData = JSONArray(string)
|
||||||
|
var dataList: MutableList<WrapperjavaBean> = mutableListOf()
|
||||||
|
for (i in 0 until jsonData.length()) {
|
||||||
|
jsonData.getJSONObject(i).run {
|
||||||
|
val pName = getString("parent_name")
|
||||||
|
val listArray = getJSONArray("keyboard_list")
|
||||||
|
var detailsjavaBeanList: MutableList<DetailsjavaBean> = mutableListOf()
|
||||||
|
for (listIndex in 0 until listArray.length()) {
|
||||||
|
listArray.getJSONObject(listIndex).also {
|
||||||
|
val title = it.getString("title")
|
||||||
|
val thUrl = it.getString("thumbUrl")
|
||||||
|
val thGif = it.getString("thumbUrlGif")
|
||||||
|
var zipPath = ""
|
||||||
|
var imgPath = ""
|
||||||
|
var imgGif = ""
|
||||||
|
var imgPreviewGif = ""
|
||||||
|
if (it.has("detail")) {
|
||||||
|
val contentObject =
|
||||||
|
it.getJSONObject("detail").getJSONObject("themeContent")
|
||||||
|
zipPath = contentObject.getString("androidRawZipUrl")
|
||||||
|
imgPath = contentObject.getString("img")
|
||||||
|
imgGif = contentObject.getString("imgGif")
|
||||||
|
imgPreviewGif = contentObject.getString("imgPreviewGif")
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
detailsjavaBeanList.add(
|
||||||
|
DetailsjavaBean().apply {
|
||||||
|
setImgPath(imgPath)
|
||||||
|
setZipPath(zipPath)
|
||||||
|
setTitleName(title)
|
||||||
|
setImgGif(imgGif)
|
||||||
|
thumbUrl = thUrl
|
||||||
|
thumbGif = thGif
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
val shuffled = detailsjavaBeanList.shuffled()
|
||||||
|
val dataWrapperjavaBean = WrapperjavaBean()
|
||||||
|
.apply {
|
||||||
|
parentName = pName
|
||||||
|
keyboardList = shuffled
|
||||||
|
}
|
||||||
|
dataList.add(dataWrapperjavaBean)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDataList(dataList)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateDataList(mainList: MutableList<WrapperjavaBean>) {
|
||||||
|
list = mainList
|
||||||
|
|
||||||
|
}
|
||||||
|
private fun getJsonString(fileInputStream: InputStream): String? {
|
||||||
|
return try {
|
||||||
|
// FileInputStream fileInputStream = new FileInputStream(path);
|
||||||
|
val charArray = CharArray(fileInputStream.available())
|
||||||
|
var readCount = 0
|
||||||
|
val streamReader = InputStreamReader(fileInputStream)
|
||||||
|
val bufferedReader = BufferedReader(streamReader)
|
||||||
|
val stringWriter = StringWriter()
|
||||||
|
while (bufferedReader.read(charArray).also { readCount = it } != -1) {
|
||||||
|
stringWriter.write(charArray, 0, readCount)
|
||||||
|
}
|
||||||
|
stringWriter.toString()
|
||||||
|
} catch (e: IOException) {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
100
app/src/main/java/com/rainbow/app/keyboard/helper/KeybFuncs.kt
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package com.rainbow.app.keyboard.helper
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.graphics.drawable.BitmapDrawable
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.util.Log
|
||||||
|
import com.rainbow.app.keyboard.App
|
||||||
|
import com.rainbow.app.keyboard.mysourcecode.KeybCode
|
||||||
|
import com.rainbow.app.keyboard.utils.UtilsKeyname
|
||||||
|
import com.rainbow.app.keyboard.utils.UtilsSavecurrentkb
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
object KeybFuncs {
|
||||||
|
fun keyToUpper(mKeyBoard: KeybCode) {
|
||||||
|
for (key in mKeyBoard.keys) {
|
||||||
|
if (key.label != null) {
|
||||||
|
if (key.label.length == 1) {
|
||||||
|
val charLabel = key.label.toString()[0]
|
||||||
|
val toUpperCase = charLabel.uppercaseChar()
|
||||||
|
key.codes[0] = toUpperCase.toInt()
|
||||||
|
key.label = toUpperCase.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun keyToLowerCase(mKeyBoard: KeybCode) {
|
||||||
|
for (key in mKeyBoard.keys) {
|
||||||
|
if (key.label != null) {
|
||||||
|
if (key.label.length == 1) {
|
||||||
|
val charLabel = key.label.toString()[0]
|
||||||
|
val toLowerCase = charLabel.lowercaseChar()
|
||||||
|
key.codes[0] = toLowerCase.toInt()
|
||||||
|
key.label = toLowerCase.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun primaryCodeToChar(primCode: Int): String {
|
||||||
|
val toString = primCode.toChar().toString()
|
||||||
|
|
||||||
|
return toString
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SuspiciousIndentation")
|
||||||
|
fun readBgOrVideo(
|
||||||
|
context: Context,
|
||||||
|
playAction: (gif: String?, bgDraw: Drawable?) -> Unit
|
||||||
|
) {
|
||||||
|
|
||||||
|
UtilsSavecurrentkb.getSkinPath()?.let { resPath ->
|
||||||
|
val videoPath = "${resPath}res/raw/${UtilsKeyname.videoName}"
|
||||||
|
val videoPath2 = "${resPath}res/raw/${UtilsKeyname.video}"
|
||||||
|
val backgroundPath = "${resPath}res/drawable-xxhdpi-v4/${UtilsKeyname.bgName}"
|
||||||
|
val backgroundPath_png = "${resPath}res/drawable-xxhdpi-v4/${UtilsKeyname.bgName_png}"
|
||||||
|
val file = File(videoPath)
|
||||||
|
val file2 = File(videoPath2)
|
||||||
|
val file3 = File(backgroundPath)
|
||||||
|
val file4 = File(backgroundPath_png)
|
||||||
|
if (file.exists() || file2.exists()) {
|
||||||
|
Log.d(App.Companion.TAG, "--------11111111= resPath=${resPath}")
|
||||||
|
val path: String = if (file.exists()) {
|
||||||
|
videoPath
|
||||||
|
} else {
|
||||||
|
videoPath2
|
||||||
|
}
|
||||||
|
val bitmapDrawable =
|
||||||
|
BitmapDrawable(context.resources, BitmapFactory.decodeFile(path))
|
||||||
|
|
||||||
|
playAction.invoke(path, null)
|
||||||
|
|
||||||
|
// playAction.invoke(mediaPlayer,null);
|
||||||
|
} else {
|
||||||
|
Log.d(
|
||||||
|
App.Companion.TAG,
|
||||||
|
"--------11111111= file3.exists()" + file3.exists() + "---resPath=" + resPath
|
||||||
|
)
|
||||||
|
if (file3.exists()) {
|
||||||
|
val bitmapDrawable =
|
||||||
|
BitmapDrawable(context.resources, BitmapFactory.decodeFile(backgroundPath))
|
||||||
|
playAction.invoke(null, bitmapDrawable)
|
||||||
|
} else if (file4.exists()) {
|
||||||
|
val bitmapDrawable =
|
||||||
|
BitmapDrawable(context.resources, BitmapFactory.decodeFile(backgroundPath_png))
|
||||||
|
playAction.invoke(null, bitmapDrawable)
|
||||||
|
} else {
|
||||||
|
playAction.invoke(null, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
package com.rainbow.app.keyboard.helper;
|
||||||
|
|
||||||
|
// 按键对象模型
|
||||||
|
public class KeybModel {
|
||||||
|
private String name;
|
||||||
|
private String background;
|
||||||
|
private String label;
|
||||||
|
|
||||||
|
public KeybModel(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBackground() {
|
||||||
|
return background;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBackground(String background) {
|
||||||
|
this.background = background;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLabel() {
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLabel(String label) {
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
}
|
||||||
324
app/src/main/java/com/rainbow/app/keyboard/helper/KeybView.java
Normal file
@ -0,0 +1,324 @@
|
|||||||
|
package com.rainbow.app.keyboard.helper;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
|
||||||
|
import androidx.core.graphics.drawable.DrawableCompat;
|
||||||
|
|
||||||
|
import com.rainbow.app.keyboard.App;
|
||||||
|
import com.rainbow.app.keyboard.R;
|
||||||
|
import com.rainbow.app.keyboard.mysourcecode.KeybCode;
|
||||||
|
import com.rainbow.app.keyboard.mysourcecode.KeybCodeView;
|
||||||
|
import com.rainbow.app.keyboard.utils.UtilsKeyname;
|
||||||
|
import com.rainbow.app.keyboard.utils.UtilsKbmanager;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
public class KeybView extends KeybCodeView {
|
||||||
|
|
||||||
|
private Paint mPaint;
|
||||||
|
private Context mContext;
|
||||||
|
|
||||||
|
private float mRation = 0.5f;
|
||||||
|
|
||||||
|
|
||||||
|
//0 小写 1 大写 2 大写锁定
|
||||||
|
private int isLowerCase = 0;
|
||||||
|
//0 默认键盘 1 字母键盘 2 符号键盘
|
||||||
|
private int mMode = 0;
|
||||||
|
|
||||||
|
private UtilsKbmanager UtilsKbmanager;
|
||||||
|
private int curImeAction = EditorInfo.IME_ACTION_UNSPECIFIED;
|
||||||
|
|
||||||
|
public KeybView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
mContext = context;
|
||||||
|
setAttribute(attrs, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeybView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
mContext = context;
|
||||||
|
setAttribute(attrs, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeybView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
mContext = context;
|
||||||
|
setAttribute(attrs, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setMode(int mType) {
|
||||||
|
this.mMode = mType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMode() {
|
||||||
|
return mMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int isLowerCase() {
|
||||||
|
return isLowerCase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLowerCase(int lowerCase) {
|
||||||
|
isLowerCase = lowerCase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateUi(int ime) {
|
||||||
|
Log.d(App.TAG, "----------ime=" + ime);
|
||||||
|
curImeAction = ime;
|
||||||
|
UtilsKbmanager.updateSkinConfig();
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initPaint() {
|
||||||
|
mPaint = new Paint();
|
||||||
|
mPaint.setAntiAlias(true);
|
||||||
|
mPaint.setTextAlign(Paint.Align.CENTER);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setAttribute(AttributeSet attrs, Context con) {
|
||||||
|
UtilsKbmanager = new UtilsKbmanager(con);
|
||||||
|
initPaint();
|
||||||
|
TypedArray mTypedArray = con.obtainStyledAttributes(attrs, R.styleable.CustomInputView);
|
||||||
|
|
||||||
|
// int color = mTypedArray.getColor(R.styleable.CustomInputView_text_color_done, 1);
|
||||||
|
//
|
||||||
|
// Drawable drawable = mTypedArray.getDrawable(R.styleable.CustomInputView_drawable_cancel);
|
||||||
|
//
|
||||||
|
// int textSize = mTypedArray.getInt(R.styleable.CustomInputView_text_size_key, 12);
|
||||||
|
|
||||||
|
mTypedArray.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDraw(Canvas canvas) {
|
||||||
|
super.onDraw(canvas);
|
||||||
|
|
||||||
|
|
||||||
|
ResConfig config = UtilsKbmanager.getConfig();
|
||||||
|
List<KeybModel> keybModels = new ArrayList<>();
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (KeybCode.Key curKey : getKeyboard().getKeys()) {
|
||||||
|
int code = curKey.codes[0];
|
||||||
|
if (config != null&& !config.getLayouts().isEmpty()) {
|
||||||
|
if (code == 113 ||code == 81 || code == 49||code == 91) {
|
||||||
|
i = 0;
|
||||||
|
ResLayout resLayout = config.getLayouts().get(0);
|
||||||
|
keybModels = resLayout.getKeys();
|
||||||
|
} else if (code == 97||code == 65 || code == 33||code == 126) {
|
||||||
|
i = 0;
|
||||||
|
ResLayout resLayout = config.getLayouts().get(1);
|
||||||
|
keybModels = resLayout.getKeys();
|
||||||
|
}else if (code == -1 || code == -103||code==-101) {
|
||||||
|
i = 0;
|
||||||
|
ResLayout resLayout = config.getLayouts().get(2);
|
||||||
|
keybModels = resLayout.getKeys();
|
||||||
|
}else if (code == -2 || code == -102) {
|
||||||
|
i = 0;
|
||||||
|
ResLayout resLayout = config.getLayouts().get(3);
|
||||||
|
keybModels = resLayout.getKeys();
|
||||||
|
}
|
||||||
|
String background = keybModels.get(i).getBackground()+".9.png";
|
||||||
|
i++;
|
||||||
|
Drawable configBg = UtilsKbmanager.getConfigBg(background);
|
||||||
|
realNewDraw(configBg, curKey, canvas, code);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
realDraw(curKey, canvas, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void realNewDraw(Drawable configBg, KeybCode.Key curKey, Canvas canvas, int code) {
|
||||||
|
switch (code) {
|
||||||
|
case UtilsKeyname.KEY_CODE_SHIFT:
|
||||||
|
// drawAllShift(curKey, canvas);
|
||||||
|
onDrawCurKey(curKey, canvas, "Shift", configBg, null);
|
||||||
|
break;
|
||||||
|
case UtilsKeyname.KEY_CODE_NUMBER_SHIFT:
|
||||||
|
onDrawCurKey(curKey, canvas, "More", configBg, null);
|
||||||
|
break;
|
||||||
|
case UtilsKeyname.KEY_CODE_DELETE:
|
||||||
|
onDrawCurKey(curKey, canvas, "Delete", configBg, null);
|
||||||
|
break;
|
||||||
|
case UtilsKeyname.KEY_CODE_SYMBOL_SHIFT:
|
||||||
|
onDrawCurKey(curKey, canvas, "123", configBg, null);
|
||||||
|
break;
|
||||||
|
case UtilsKeyname.KEY_CODE_CHANGE_NUMBER:
|
||||||
|
onDrawCurKey(curKey, canvas, null, configBg, null);
|
||||||
|
break;
|
||||||
|
case UtilsKeyname.KEY_CODE_BACK:
|
||||||
|
onDrawCurKey(curKey, canvas, "Back", configBg, null);
|
||||||
|
break;
|
||||||
|
case UtilsKeyname.KEY_CODE_SPACE:
|
||||||
|
onDrawCurKey(curKey, canvas, null, configBg, null);
|
||||||
|
break;
|
||||||
|
case UtilsKeyname.KEY_CODE_COMPLETE, UtilsKeyname.KEY_CODE_CANCEL:
|
||||||
|
Log.d(App.TAG, "-11111111111---------curImeAction=" + curImeAction);
|
||||||
|
if (curImeAction == EditorInfo.IME_ACTION_SEARCH) {
|
||||||
|
onDrawCurKey(curKey, canvas, "Search", configBg, null);
|
||||||
|
} else {
|
||||||
|
onDrawCurKey(curKey, canvas, "Done", configBg, null);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
|
||||||
|
onDrawCurKey(curKey, canvas, null, configBg, null);
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void realDraw(KeybCode.Key curKey, Canvas canvas, int code) {
|
||||||
|
switch (code) {
|
||||||
|
case UtilsKeyname.KEY_CODE_SHIFT:
|
||||||
|
drawAllShift(curKey, canvas);
|
||||||
|
break;
|
||||||
|
case UtilsKeyname.KEY_CODE_NUMBER_SHIFT:
|
||||||
|
onDrawCurKey(curKey, canvas, "More", UtilsKbmanager.getFunctionDraw(), null);
|
||||||
|
break;
|
||||||
|
case UtilsKeyname.KEY_CODE_DELETE:
|
||||||
|
onDrawCurKey(curKey, canvas, "Delete", UtilsKbmanager.getFunctionDraw(), null);
|
||||||
|
break;
|
||||||
|
case UtilsKeyname.KEY_CODE_SYMBOL_SHIFT:
|
||||||
|
onDrawCurKey(curKey, canvas, "123", UtilsKbmanager.getFunctionDraw(), null);
|
||||||
|
break;
|
||||||
|
case UtilsKeyname.KEY_CODE_CHANGE_NUMBER:
|
||||||
|
onDrawCurKey(curKey, canvas, null, UtilsKbmanager.getToDraw(), null);
|
||||||
|
break;
|
||||||
|
case UtilsKeyname.KEY_CODE_BACK:
|
||||||
|
onDrawCurKey(curKey, canvas, "Back", UtilsKbmanager.getToDraw(), null);
|
||||||
|
break;
|
||||||
|
case UtilsKeyname.KEY_CODE_SPACE:
|
||||||
|
onDrawCurKey(curKey, canvas, null, UtilsKbmanager.getSpaceDraw(), null);
|
||||||
|
break;
|
||||||
|
case UtilsKeyname.KEY_CODE_COMPLETE, UtilsKeyname.KEY_CODE_CANCEL:
|
||||||
|
Log.d(App.TAG, "-11111111111---------curImeAction=" + curImeAction);
|
||||||
|
if (curImeAction == EditorInfo.IME_ACTION_SEARCH) {
|
||||||
|
onDrawCurKey(curKey, canvas, "Search", UtilsKbmanager.getFunctionDraw(), null);
|
||||||
|
} else {
|
||||||
|
onDrawCurKey(curKey, canvas, "Done", UtilsKbmanager.getFunctionDraw(), null);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
|
||||||
|
onDrawCurKey(curKey, canvas, null, UtilsKbmanager.getGeneralDraw(), null);
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawAllShift(KeybCode.Key curKey, Canvas canvas) {
|
||||||
|
if (isLowerCase == 0) {
|
||||||
|
onDrawCurKey(curKey, canvas, "Shift", UtilsKbmanager.getFunctionDraw(), null);
|
||||||
|
} else if (isLowerCase == 1) {
|
||||||
|
onDrawCurKey(curKey, canvas, "Shift", UtilsKbmanager.getFunctionDraw(), null);
|
||||||
|
} else if (isLowerCase == 2) {
|
||||||
|
onDrawCurKey(curKey, canvas, "Shift", UtilsKbmanager.getFunctionDraw(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onDrawCurKey(KeybCode.Key curKey, Canvas curCanvas, String label, Drawable bgDrawable, Drawable iconDraw) {
|
||||||
|
|
||||||
|
if (bgDrawable != null) {
|
||||||
|
onDrawKeyBackground(curKey, curCanvas, bgDrawable);
|
||||||
|
}
|
||||||
|
if (iconDraw != null) {
|
||||||
|
onDrawKeyIcon(curKey, curCanvas, iconDraw);
|
||||||
|
}
|
||||||
|
onDrawKeyText(curKey, curCanvas, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onDrawKeyText(KeybCode.Key curKey, Canvas curCanvas, String label) {
|
||||||
|
mPaint.setColor(UtilsKbmanager.getKeyTextColor());
|
||||||
|
mPaint.setTextSize(mContext.getResources().getDimension(R.dimen.text_size));
|
||||||
|
float v = curKey.width / 2f;
|
||||||
|
float v1 = curKey.height / 2f;
|
||||||
|
float v2 = (mPaint.getTextSize() - mPaint.descent()) / 2f;
|
||||||
|
|
||||||
|
|
||||||
|
if (curKey.label != null) {
|
||||||
|
curCanvas.drawText((String) curKey.label, curKey.x + getPaddingLeft() + v, curKey.y + getPaddingRight() + v1 + v2, mPaint);
|
||||||
|
} else if (label != null) {
|
||||||
|
curCanvas.drawText(label, curKey.x + getPaddingLeft() + v, curKey.y + getPaddingRight() + v1 + v2, mPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onDrawKeyBackground(KeybCode.Key curKey, Canvas curCanvas, Drawable curDrawable) {
|
||||||
|
if (curKey.codes[0] != 0) {
|
||||||
|
curDrawable.setState(curKey.getCurrentDrawableState());
|
||||||
|
}
|
||||||
|
Rect rect = new Rect((curKey.x + this.getPaddingLeft()), (curKey.y + this.getPaddingTop()), (curKey.x + this.getPaddingLeft() + curKey.width), (curKey.y + this.getPaddingTop() + curKey.height));
|
||||||
|
curDrawable.setBounds(rect);
|
||||||
|
|
||||||
|
curDrawable.draw(curCanvas);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onDrawKeyIcon(KeybCode.Key curKey, Canvas curCanvas, Drawable curDrawable) {
|
||||||
|
|
||||||
|
Drawable wrap = DrawableCompat.wrap(curDrawable);
|
||||||
|
|
||||||
|
curKey.icon = curDrawable;
|
||||||
|
float iconW = (float) curKey.icon.getIntrinsicWidth();
|
||||||
|
float iconH = (float) curKey.icon.getIntrinsicHeight();
|
||||||
|
|
||||||
|
float wDivRation = iconW / curKey.width;
|
||||||
|
float hDivRation = iconH / curKey.height;
|
||||||
|
|
||||||
|
curKey.icon.draw(curCanvas);
|
||||||
|
|
||||||
|
if (wDivRation > hDivRation) {
|
||||||
|
float minRatio = 0;
|
||||||
|
if (wDivRation <= mRation) {
|
||||||
|
minRatio = wDivRation;
|
||||||
|
} else {
|
||||||
|
minRatio = mRation;
|
||||||
|
}
|
||||||
|
iconH = (iconH / wDivRation) * minRatio;
|
||||||
|
iconW = (iconW / wDivRation) * minRatio;
|
||||||
|
} else {
|
||||||
|
float minRatio = 0;
|
||||||
|
if (hDivRation <= mRation) {
|
||||||
|
minRatio = hDivRation;
|
||||||
|
} else {
|
||||||
|
minRatio = mRation;
|
||||||
|
}
|
||||||
|
iconH = (iconH / hDivRation) * minRatio;
|
||||||
|
iconW = (iconW / hDivRation) * minRatio;
|
||||||
|
}
|
||||||
|
float subW = (curKey.width - iconW) / 2f;
|
||||||
|
float subH = (curKey.height - iconH) / 2f;
|
||||||
|
int xLeft = (int) (curKey.x + getPaddingLeft() + subW);
|
||||||
|
int yTop = (int) (curKey.y + getPaddingTop() + subH);
|
||||||
|
int xRight = (int) (xLeft + iconW);
|
||||||
|
int yBottom = (int) (yTop + iconH);
|
||||||
|
curKey.icon.setBounds(xLeft, yTop, xRight, yBottom);
|
||||||
|
curKey.icon.draw(curCanvas);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,366 @@
|
|||||||
|
package com.rainbow.app.keyboard.helper;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.inputmethodservice.InputMethodService;
|
||||||
|
import android.media.MediaPlayer;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
import android.view.inputmethod.InputConnection;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.VideoView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.bumptech.glide.integration.webp.decoder.WebpDrawable;
|
||||||
|
import com.bumptech.glide.load.DataSource;
|
||||||
|
import com.bumptech.glide.load.engine.GlideException;
|
||||||
|
import com.bumptech.glide.request.RequestListener;
|
||||||
|
import com.bumptech.glide.request.target.Target;
|
||||||
|
import com.rainbow.app.keyboard.App;
|
||||||
|
import com.rainbow.app.keyboard.R;
|
||||||
|
import com.rainbow.app.keyboard.utils.UtilsKeyname;
|
||||||
|
import com.rainbow.app.keyboard.utils.UtilsCommon;
|
||||||
|
import com.rainbow.app.keyboard.utils.UtilsSavecurrentkb;
|
||||||
|
import com.rainbow.app.keyboard.mysourcecode.KeybCode;
|
||||||
|
import com.rainbow.app.keyboard.mysourcecode.KeybCodeView;
|
||||||
|
|
||||||
|
import kotlin.Unit;
|
||||||
|
import kotlin.jvm.functions.Function2;
|
||||||
|
|
||||||
|
public class KeybinputService extends InputMethodService implements KeybCodeView.OnKeyboardActionListener {
|
||||||
|
|
||||||
|
private KeybView myKeyBoardView;
|
||||||
|
private KeybCode mKeyBoard;
|
||||||
|
private View parentView;
|
||||||
|
private ImageView imBG;
|
||||||
|
private VideoView videoView;
|
||||||
|
|
||||||
|
private int a = R.xml.xml_one;
|
||||||
|
private int b = R.xml.xml_two;
|
||||||
|
private int c = R.xml.xml_three;
|
||||||
|
|
||||||
|
private int curImeAction = EditorInfo.IME_ACTION_UNSPECIFIED;
|
||||||
|
|
||||||
|
@SuppressLint("InflateParams")
|
||||||
|
@Override
|
||||||
|
public View onCreateInputView() {
|
||||||
|
parentView = getLayoutInflater().inflate(R.layout.item_editv, null);
|
||||||
|
|
||||||
|
// API 30+ 的特殊处理
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
// 设置窗口标志,确保键盘正确显示
|
||||||
|
try {
|
||||||
|
if (getWindow() != null && getWindow().getWindow() != null) {
|
||||||
|
getWindow().getWindow().addFlags(
|
||||||
|
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
|
||||||
|
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
|
||||||
|
);
|
||||||
|
|
||||||
|
// 对于 API 31+
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
getWindow().getWindow().setDecorFitsSystemWindows(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(App.TAG, "Error setting window flags: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
findView();
|
||||||
|
return parentView;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void findView() {
|
||||||
|
imBG = parentView.findViewById(R.id.gif_bg);
|
||||||
|
videoView = parentView.findViewById(R.id.video_view);
|
||||||
|
mKeyBoard = new KeybCode(this, a);
|
||||||
|
myKeyBoardView = parentView.findViewById(R.id.custom_input_view);
|
||||||
|
myKeyBoardView.setEnabled(true);
|
||||||
|
myKeyBoardView.setPreviewEnabled(false);
|
||||||
|
myKeyBoardView.setKeyboard(mKeyBoard);
|
||||||
|
myKeyBoardView.setOnKeyboardActionListener(this);
|
||||||
|
|
||||||
|
// API 31+ 的特殊处理
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
myKeyBoardView.setFocusable(true);
|
||||||
|
myKeyBoardView.setFocusableInTouchMode(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStartInputView(EditorInfo info, boolean restarting) {
|
||||||
|
super.onStartInputView(info, restarting);
|
||||||
|
|
||||||
|
// API 31+ 可能需要额外的初始化
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
// 确保键盘视图正确初始化
|
||||||
|
if (myKeyBoardView != null) {
|
||||||
|
myKeyBoardView.requestFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新当前输入法动作
|
||||||
|
if (info != null) {
|
||||||
|
curImeAction = UtilsCommon.INSTANCE.getTextForImeAction(info.imeOptions);
|
||||||
|
Log.d(App.TAG, "onStartInputView: curImeAction = " + curImeAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onWindowHidden() {
|
||||||
|
super.onWindowHidden();
|
||||||
|
if (videoView != null && videoView.isPlaying()) {
|
||||||
|
videoView.pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onWindowShown() {
|
||||||
|
super.onWindowShown();
|
||||||
|
|
||||||
|
// 获取当前编辑信息
|
||||||
|
EditorInfo currentInputEditorInfo = getCurrentInputEditorInfo();
|
||||||
|
if (currentInputEditorInfo != null) {
|
||||||
|
curImeAction = UtilsCommon.INSTANCE.getTextForImeAction(currentInputEditorInfo.imeOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
String skinPath = UtilsSavecurrentkb.INSTANCE.getSkinPath();
|
||||||
|
|
||||||
|
if (skinPath == null || skinPath.isEmpty()) {
|
||||||
|
Log.d(App.TAG, "---------skinPath= null");
|
||||||
|
if (myKeyBoardView != null) {
|
||||||
|
myKeyBoardView.updateUi(curImeAction);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.d(App.TAG, "---------skinPath= " + skinPath);
|
||||||
|
KeybFuncs.INSTANCE.readBgOrVideo(this, new Function2<String, Drawable, Unit>() {
|
||||||
|
@Override
|
||||||
|
public Unit invoke(String s, Drawable drawable) {
|
||||||
|
Log.d(App.TAG, "---------s= " + s + "---------drawable=" + drawable);
|
||||||
|
|
||||||
|
// 检查视图是否仍然有效
|
||||||
|
if (myKeyBoardView == null || imBG == null || videoView == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s != null) {
|
||||||
|
myKeyBoardView.setBackground(null);
|
||||||
|
if (s.endsWith(".gif")) {
|
||||||
|
imBG.setVisibility(View.VISIBLE);
|
||||||
|
videoView.setVisibility(View.GONE);
|
||||||
|
try {
|
||||||
|
Glide.with(KeybinputService.this)
|
||||||
|
.load(s)
|
||||||
|
.addListener(new RequestListener<Drawable>() {
|
||||||
|
@Override
|
||||||
|
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> target, boolean isFirstResource) {
|
||||||
|
Log.e(App.TAG, "Failed to load GIF: " + s);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target<Drawable> target, @NonNull DataSource dataSource, boolean isFirstResource) {
|
||||||
|
if (resource instanceof WebpDrawable) {
|
||||||
|
((WebpDrawable) resource).setLoopCount(WebpDrawable.LOOP_FOREVER);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}).into(imBG);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(App.TAG, "Error loading GIF: " + e.getMessage());
|
||||||
|
}
|
||||||
|
} else if (s.endsWith(".mp4")) {
|
||||||
|
imBG.setVisibility(View.GONE);
|
||||||
|
videoView.setVisibility(View.VISIBLE);
|
||||||
|
try {
|
||||||
|
videoView.setVideoPath(s);
|
||||||
|
videoView.start();
|
||||||
|
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
|
||||||
|
@Override
|
||||||
|
public void onPrepared(MediaPlayer mp) {
|
||||||
|
mp.setLooping(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(App.TAG, "Error playing video: " + e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(App.TAG, "Unknown media type: " + s);
|
||||||
|
}
|
||||||
|
} else if (drawable != null) {
|
||||||
|
myKeyBoardView.setBackground(drawable);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (myKeyBoardView != null) {
|
||||||
|
myKeyBoardView.updateUi(curImeAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
if (videoView != null) {
|
||||||
|
videoView.stopPlayback();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理资源
|
||||||
|
if (myKeyBoardView != null) {
|
||||||
|
myKeyBoardView.setOnKeyboardActionListener(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPress(int primaryCode) {
|
||||||
|
// 可选:播放按键音效或提供触觉反馈
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRelease(int primaryCode) {
|
||||||
|
// 可选:释放资源
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onKey(int primaryCode, int[] keyCodes) {
|
||||||
|
InputConnection curInputConnect = getCurrentInputConnection();
|
||||||
|
if (curInputConnect == null) {
|
||||||
|
Log.w(App.TAG, "InputConnection is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (primaryCode) {
|
||||||
|
case UtilsKeyname.KEY_CODE_DELETE:
|
||||||
|
curInputConnect.deleteSurroundingText(1, 0);
|
||||||
|
break;
|
||||||
|
case UtilsKeyname.KEY_CODE_SHIFT:
|
||||||
|
switchShift();
|
||||||
|
break;
|
||||||
|
case UtilsKeyname.KEY_CODE_NUMBER_SHIFT:
|
||||||
|
case UtilsKeyname.KEY_CODE_SYMBOL_SHIFT:
|
||||||
|
switchMoreOrNumber();
|
||||||
|
break;
|
||||||
|
case UtilsKeyname.KEY_CODE_CHANGE_NUMBER:
|
||||||
|
case UtilsKeyname.KEY_CODE_BACK:
|
||||||
|
switchNormalOrNumber();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UtilsKeyname.KEY_CODE_COMPLETE:
|
||||||
|
case UtilsKeyname.KEY_CODE_CANCEL:
|
||||||
|
if (curImeAction != EditorInfo.IME_ACTION_UNSPECIFIED) {
|
||||||
|
curInputConnect.performEditorAction(curImeAction);
|
||||||
|
} else {
|
||||||
|
// 如果没有指定动作,使用完成动作
|
||||||
|
curInputConnect.performEditorAction(EditorInfo.IME_ACTION_DONE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
String codeToChar = KeybFuncs.INSTANCE.primaryCodeToChar(primaryCode);
|
||||||
|
if (codeToChar != null) {
|
||||||
|
curInputConnect.commitText(codeToChar, 1);
|
||||||
|
if (myKeyBoardView != null && myKeyBoardView.isLowerCase() == 1) {
|
||||||
|
// 自动转小写
|
||||||
|
myKeyBoardView.setLowerCase(0);
|
||||||
|
KeybFuncs.INSTANCE.keyToLowerCase(mKeyBoard);
|
||||||
|
myKeyBoardView.setKeyboard(mKeyBoard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void switchMoreOrNumber() {
|
||||||
|
if (myKeyBoardView == null) return;
|
||||||
|
|
||||||
|
int mode = myKeyBoardView.getMode();
|
||||||
|
switch (mode) {
|
||||||
|
case 1:
|
||||||
|
mKeyBoard = new KeybCode(this, c);
|
||||||
|
myKeyBoardView.setMode(2);
|
||||||
|
myKeyBoardView.setKeyboard(mKeyBoard);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
mKeyBoard = new KeybCode(this, b);
|
||||||
|
myKeyBoardView.setMode(1);
|
||||||
|
myKeyBoardView.setKeyboard(mKeyBoard);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void switchNormalOrNumber() {
|
||||||
|
if (myKeyBoardView == null) return;
|
||||||
|
|
||||||
|
int mode = myKeyBoardView.getMode();
|
||||||
|
switch (mode) {
|
||||||
|
case 0:
|
||||||
|
mKeyBoard = new KeybCode(this, b);
|
||||||
|
myKeyBoardView.setMode(1);
|
||||||
|
myKeyBoardView.setKeyboard(mKeyBoard);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
mKeyBoard = new KeybCode(this, a);
|
||||||
|
myKeyBoardView.setMode(0);
|
||||||
|
myKeyBoardView.setKeyboard(mKeyBoard);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void switchShift() {
|
||||||
|
if (myKeyBoardView == null) return;
|
||||||
|
|
||||||
|
int lowerCase = myKeyBoardView.isLowerCase();
|
||||||
|
switch (lowerCase) {
|
||||||
|
case 0:
|
||||||
|
// 当前小写转大写
|
||||||
|
myKeyBoardView.setLowerCase(1);
|
||||||
|
KeybFuncs.INSTANCE.keyToUpper(mKeyBoard);
|
||||||
|
myKeyBoardView.setKeyboard(mKeyBoard);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
// 当前大写转锁定大写
|
||||||
|
myKeyBoardView.setLowerCase(2);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
// 当前锁定大写转小写
|
||||||
|
myKeyBoardView.setLowerCase(0);
|
||||||
|
KeybFuncs.INSTANCE.keyToLowerCase(mKeyBoard);
|
||||||
|
myKeyBoardView.setKeyboard(mKeyBoard);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onText(CharSequence text) {
|
||||||
|
// 处理文本输入
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void swipeLeft() {
|
||||||
|
// 处理向左滑动
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void swipeRight() {
|
||||||
|
// 处理向右滑动
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void swipeDown() {
|
||||||
|
// 处理向下滑动
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void swipeUp() {
|
||||||
|
// 处理向上滑动
|
||||||
|
}
|
||||||
|
}
|
||||||
168
app/src/main/java/com/rainbow/app/keyboard/helper/ResConfig.java
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
package com.rainbow.app.keyboard.helper;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ResConfig {
|
||||||
|
private String version;
|
||||||
|
private String supportLayouts;
|
||||||
|
private int hideHint;
|
||||||
|
private String layoutStyle;
|
||||||
|
private List<ResLayout> layouts = new ArrayList<>();
|
||||||
|
private List<KeybModel> keybModelList = new ArrayList<>();
|
||||||
|
private LinkedHashMap<String, String> maps = new LinkedHashMap<>();
|
||||||
|
private String KeyDefault;
|
||||||
|
private String KeyMarkDefault;
|
||||||
|
private String KeyFuncDefault;
|
||||||
|
private String KeyShift;
|
||||||
|
private String KeyDelete;
|
||||||
|
private String KeyAlphaSymbol;
|
||||||
|
private String KeyEmoji;
|
||||||
|
private String KeyMark;
|
||||||
|
private String KeySpace;
|
||||||
|
private String KeyEnter;
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public String getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVersion(String version) {
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSupportLayouts() {
|
||||||
|
return supportLayouts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSupportLayouts(String supportLayouts) {
|
||||||
|
this.supportLayouts = supportLayouts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHideHint() {
|
||||||
|
return hideHint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHideHint(int hideHint) {
|
||||||
|
this.hideHint = hideHint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLayoutStyle() {
|
||||||
|
return layoutStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLayoutStyle(String layoutStyle) {
|
||||||
|
this.layoutStyle = layoutStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ResLayout> getLayouts() {
|
||||||
|
return layouts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addLayout(ResLayout layout) {
|
||||||
|
this.layouts.add(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<KeybModel> getKeyList() {
|
||||||
|
return keybModelList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeybModel getLastKeyList() {
|
||||||
|
return keybModelList.isEmpty() ? null : keybModelList.get(keybModelList.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addKey(KeybModel KeybModel) {
|
||||||
|
this.keybModelList.add(KeybModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LinkedHashMap<String, String> getMaps() {
|
||||||
|
return maps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaps(LinkedHashMap<String, String> maps) {
|
||||||
|
this.maps = maps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKeyDefault() {
|
||||||
|
return KeyDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyDefault(String keyDefault) {
|
||||||
|
KeyDefault = keyDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKeyMarkDefault() {
|
||||||
|
return KeyMarkDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyMarkDefault(String keyMarkDefault) {
|
||||||
|
KeyMarkDefault = keyMarkDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKeyFuncDefault() {
|
||||||
|
return KeyFuncDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyFuncDefault(String keyFuncDefault) {
|
||||||
|
KeyFuncDefault = keyFuncDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKeyShift() {
|
||||||
|
return KeyShift;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyShift(String keyShift) {
|
||||||
|
KeyShift = keyShift;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKeyDelete() {
|
||||||
|
return KeyDelete;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyDelete(String keyDelete) {
|
||||||
|
KeyDelete = keyDelete;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKeyAlphaSymbol() {
|
||||||
|
return KeyAlphaSymbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyAlphaSymbol(String keyAlphaSymbol) {
|
||||||
|
KeyAlphaSymbol = keyAlphaSymbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKeyEmoji() {
|
||||||
|
return KeyEmoji;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyEmoji(String keyEmoji) {
|
||||||
|
KeyEmoji = keyEmoji;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKeyMark() {
|
||||||
|
return KeyMark;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyMark(String keyMark) {
|
||||||
|
KeyMark = keyMark;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKeySpace() {
|
||||||
|
return KeySpace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeySpace(String keySpace) {
|
||||||
|
KeySpace = keySpace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKeyEnter() {
|
||||||
|
return KeyEnter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyEnter(String keyEnter) {
|
||||||
|
KeyEnter = keyEnter;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
package com.rainbow.app.keyboard.helper;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ResLayout {
|
||||||
|
private String name;
|
||||||
|
private List<KeybModel> keybModels = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
public ResLayout(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<KeybModel> getKeys() {
|
||||||
|
return keybModels;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addKey(KeybModel KeybModel) {
|
||||||
|
this.keybModels.add(KeybModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeybModel getLastKey() {
|
||||||
|
return keybModels.isEmpty() ? null : keybModels.get(keybModels.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
package com.rainbow.app.keyboard.javabean;
|
||||||
|
|
||||||
|
import androidx.room.Entity;
|
||||||
|
import androidx.room.PrimaryKey;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public class DetailsjavaBean implements Serializable {
|
||||||
|
|
||||||
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
private long id;
|
||||||
|
private String titleName;
|
||||||
|
|
||||||
|
private String thumbUrl;
|
||||||
|
|
||||||
|
private String thumbGif;
|
||||||
|
|
||||||
|
private String zipPath;
|
||||||
|
|
||||||
|
private String imgPath;
|
||||||
|
private String imgGif;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void setImgGif(String imgGif) {
|
||||||
|
this.imgGif = imgGif;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitleName() {
|
||||||
|
|
||||||
|
return titleName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getThumbUrl() {
|
||||||
|
|
||||||
|
return thumbUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getThumbGif() {
|
||||||
|
|
||||||
|
return thumbGif;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getZipPath() {
|
||||||
|
|
||||||
|
return zipPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImgPath() {
|
||||||
|
|
||||||
|
return imgPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitleName(String name) {
|
||||||
|
this.titleName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setThumbUrl(String thumbUrl) {
|
||||||
|
this.thumbUrl = thumbUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setThumbGif(String thumbGif) {
|
||||||
|
this.thumbGif = thumbGif;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setZipPath(String path) {
|
||||||
|
this.zipPath = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImgPath(String imgPath) {
|
||||||
|
this.imgPath = imgPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImgGif() {
|
||||||
|
return imgGif;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
package com.rainbow.app.keyboard.javabean;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class WrapperjavaBean {
|
||||||
|
private String parentName;
|
||||||
|
|
||||||
|
private List<DetailsjavaBean> keyboardList;
|
||||||
|
|
||||||
|
public String getParentName() {
|
||||||
|
|
||||||
|
return parentName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DetailsjavaBean> getKeyboardList() {
|
||||||
|
|
||||||
|
return keyboardList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParentName(String name) {
|
||||||
|
this.parentName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyboardList(List<DetailsjavaBean> keyboardList) {
|
||||||
|
this.keyboardList = keyboardList;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,530 @@
|
|||||||
|
package com.rainbow.app.keyboard.myadapter;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.ViewParent;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.rainbow.app.keyboard.R;
|
||||||
|
import com.rainbow.app.keyboard.javabean.DetailsjavaBean;
|
||||||
|
import com.rainbow.app.keyboard.mycallback.DeleteCallback;
|
||||||
|
import com.rainbow.app.keyboard.mycallback.KBClickCallback;
|
||||||
|
import com.rainbow.app.keyboard.databinding.AdapterKbitemBinding;
|
||||||
|
import com.rainbow.app.keyboard.uiactivity.DetailkbActivity;
|
||||||
|
import com.rainbow.app.keyboard.utils.UtilsCommon;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class KBItemAdapter extends RecyclerView.Adapter<KBItemAdapter.KeyboardViewHolder> {
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
private List<DetailsjavaBean> mList = new ArrayList<>();
|
||||||
|
private DeleteCallback mMyremoveCallback;
|
||||||
|
private KBClickCallback mKBClickCallback; // 新增:用于KBPreviewActivity的回调
|
||||||
|
|
||||||
|
// 模式设置
|
||||||
|
private boolean showFavoriteIcon = false; // 是否显示收藏图标
|
||||||
|
private int spanCount = 2; // 每行显示数量
|
||||||
|
private int itemSpacingDp = 10; // item之间的水平间距(dp)
|
||||||
|
private int verticalSpacingDp = 12; // item之间的垂直间距(dp)
|
||||||
|
private int sidePaddingDp = 10; // 左右边距(dp)
|
||||||
|
private float aspectRatio = 0.65f; // 宽高比 10:7
|
||||||
|
private boolean includeEdgePadding = true; // 是否包含边缘边距
|
||||||
|
private int containerWidth = 0;
|
||||||
|
private boolean isPreviewMode = false;
|
||||||
|
|
||||||
|
public KBItemAdapter(Context context) {
|
||||||
|
mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KBItemAdapter(Context context, List<DetailsjavaBean> list) {
|
||||||
|
mContext = context;
|
||||||
|
this.mList = list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置KBPreviewActivity的回调
|
||||||
|
*/
|
||||||
|
public void setItemClickListener(KBClickCallback callback) {
|
||||||
|
this.mKBClickCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置容器宽度(用于MainActivity中右侧网格)
|
||||||
|
*/
|
||||||
|
public void setContainerWidth(int width) {
|
||||||
|
this.containerWidth = width;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预设配置:MainActivity首页使用(侧边栏布局)
|
||||||
|
* 因为右侧只有屏幕的3/4宽度,间距需要调整
|
||||||
|
*/
|
||||||
|
public void applyMainActivityConfig() {
|
||||||
|
this.spanCount = 2;
|
||||||
|
this.itemSpacingDp = 4; // 适当增加间距
|
||||||
|
this.verticalSpacingDp = 8;
|
||||||
|
this.sidePaddingDp = 6; // 减小边距
|
||||||
|
this.aspectRatio = 0.65f;
|
||||||
|
this.includeEdgePadding = false; // MainActivity中设为false,因为容器已经有padding
|
||||||
|
this.containerWidth = 0; // 需要外部设置
|
||||||
|
this.isPreviewMode = false;
|
||||||
|
this.showFavoriteIcon = false;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预设配置:CollectionActivity收藏页使用(全屏布局)
|
||||||
|
*/
|
||||||
|
public void applyCollectionConfig() {
|
||||||
|
this.spanCount = 2;
|
||||||
|
this.itemSpacingDp = 6;
|
||||||
|
this.verticalSpacingDp = 10;
|
||||||
|
this.sidePaddingDp = 10;
|
||||||
|
this.aspectRatio = 0.65f;
|
||||||
|
this.includeEdgePadding = true;
|
||||||
|
this.isPreviewMode = false;
|
||||||
|
this.showFavoriteIcon = true; // 收藏页面显示收藏图标
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预设配置:KBPreviewActivity推荐列表使用(小图布局)
|
||||||
|
*/
|
||||||
|
public void applyRecoItemConfig() {
|
||||||
|
this.spanCount = 2;
|
||||||
|
this.itemSpacingDp = 5; // 对应原RecoItemAdapter的 marginEnd="5dp"
|
||||||
|
this.verticalSpacingDp = 8; // 对应原RecoItemAdapter的 marginBottom="8dp"
|
||||||
|
this.sidePaddingDp = 0; // 不需要额外的左右边距
|
||||||
|
this.aspectRatio = 0.0f; // 在预览模式下不使用宽高比,使用固定高度
|
||||||
|
this.includeEdgePadding = false;
|
||||||
|
this.isPreviewMode = true; // 关键:启用预览模式
|
||||||
|
this.showFavoriteIcon = false; // 不显示收藏图标
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义配置(灵活调整)
|
||||||
|
*/
|
||||||
|
public void applyCustomConfig(int spanCount, int itemSpacingDp, int verticalSpacingDp,
|
||||||
|
int sidePaddingDp, float aspectRatio, boolean includeEdgePadding) {
|
||||||
|
this.spanCount = spanCount;
|
||||||
|
this.itemSpacingDp = itemSpacingDp;
|
||||||
|
this.verticalSpacingDp = verticalSpacingDp;
|
||||||
|
this.sidePaddingDp = sidePaddingDp;
|
||||||
|
this.aspectRatio = aspectRatio;
|
||||||
|
this.includeEdgePadding = includeEdgePadding;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置数据
|
||||||
|
*/
|
||||||
|
public void setData(List<DetailsjavaBean> list) {
|
||||||
|
this.mList = list;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置是否显示收藏图标
|
||||||
|
*/
|
||||||
|
public void setShowFavoriteIcon(boolean show) {
|
||||||
|
this.showFavoriteIcon = show;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置每行显示数量
|
||||||
|
*/
|
||||||
|
public void setSpanCount(int spanCount) {
|
||||||
|
this.spanCount = spanCount;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置item水平间距(dp)
|
||||||
|
*/
|
||||||
|
public void setItemSpacing(int spacingDp) {
|
||||||
|
this.itemSpacingDp = spacingDp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置item垂直间距(dp)
|
||||||
|
*/
|
||||||
|
public void setVerticalSpacing(int spacingDp) {
|
||||||
|
this.verticalSpacingDp = spacingDp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置左右边距(dp)
|
||||||
|
*/
|
||||||
|
public void setSidePadding(int paddingDp) {
|
||||||
|
this.sidePaddingDp = paddingDp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置宽高比
|
||||||
|
*/
|
||||||
|
public void setAspectRatio(float ratio) {
|
||||||
|
this.aspectRatio = ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置是否包含边缘边距
|
||||||
|
*/
|
||||||
|
public void setIncludeEdgePadding(boolean include) {
|
||||||
|
this.includeEdgePadding = include;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置移除回调
|
||||||
|
*/
|
||||||
|
public void setRemoveCallback(DeleteCallback callback) {
|
||||||
|
this.mMyremoveCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public KeyboardViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
AdapterKbitemBinding binding = AdapterKbitemBinding.inflate(
|
||||||
|
LayoutInflater.from(parent.getContext()), parent, false);
|
||||||
|
|
||||||
|
// 动态计算item尺寸
|
||||||
|
calculateAndSetItemSize(binding, parent);
|
||||||
|
|
||||||
|
return new KeyboardViewHolder(binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态计算并设置item尺寸
|
||||||
|
*/
|
||||||
|
private void calculateAndSetItemSize(AdapterKbitemBinding binding, ViewGroup parent) {
|
||||||
|
if (isPreviewMode) {
|
||||||
|
// KBPreviewActivity模式:固定高度120dp
|
||||||
|
int columnHeight = dpToPx(120); // 固定120dp高度
|
||||||
|
|
||||||
|
// 设置FrameLayout尺寸
|
||||||
|
ViewGroup.LayoutParams layoutParams = binding.getRoot().getLayoutParams();
|
||||||
|
layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT; // 宽度由GridLayoutManager自动分配
|
||||||
|
layoutParams.height = columnHeight;
|
||||||
|
|
||||||
|
// 设置边距
|
||||||
|
if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
|
||||||
|
ViewGroup.MarginLayoutParams marginParams = (ViewGroup.MarginLayoutParams) layoutParams;
|
||||||
|
marginParams.bottomMargin = dpToPx(verticalSpacingDp); // 底部间距8dp
|
||||||
|
// 水平间距将在onBindViewHolder中根据位置设置
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.getRoot().setLayoutParams(layoutParams);
|
||||||
|
|
||||||
|
// 设置CardView尺寸
|
||||||
|
ViewGroup.LayoutParams cardParams = binding.cardView.getLayoutParams();
|
||||||
|
cardParams.width = ViewGroup.LayoutParams.MATCH_PARENT; // 宽度充满
|
||||||
|
cardParams.height = columnHeight;
|
||||||
|
binding.cardView.setLayoutParams(cardParams);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// 普通网格布局:原有的动态计算逻辑
|
||||||
|
int widthToUse;
|
||||||
|
|
||||||
|
// 如果 containerWidth 还没有设置,尝试从RecyclerView获取
|
||||||
|
if (containerWidth <= 0) {
|
||||||
|
try {
|
||||||
|
// 尝试获取RecyclerView的实际宽度
|
||||||
|
RecyclerView recyclerView = findParentRecyclerView(binding.getRoot());
|
||||||
|
if (recyclerView != null && recyclerView.getWidth() > 0) {
|
||||||
|
widthToUse = recyclerView.getWidth();
|
||||||
|
// 缓存这个宽度供下次使用
|
||||||
|
containerWidth = widthToUse;
|
||||||
|
} else {
|
||||||
|
// 如果RecyclerView还没有测量,使用屏幕宽度
|
||||||
|
widthToUse = parent.getContext().getResources().getDisplayMetrics().widthPixels;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
widthToUse = parent.getContext().getResources().getDisplayMetrics().widthPixels;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 使用指定的容器宽度
|
||||||
|
widthToUse = containerWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将dp转换为px
|
||||||
|
int sidePaddingPx = dpToPx(sidePaddingDp);
|
||||||
|
int itemSpacingPx = dpToPx(itemSpacingDp);
|
||||||
|
|
||||||
|
// 计算可用宽度
|
||||||
|
int totalPadding = includeEdgePadding ? sidePaddingPx * 2 : 0; // 左右边距
|
||||||
|
int totalSpacing = itemSpacingPx * (spanCount - 1); // item之间的间距
|
||||||
|
|
||||||
|
int availableWidth = widthToUse - totalPadding - totalSpacing;
|
||||||
|
int columnWidth = availableWidth / spanCount;
|
||||||
|
|
||||||
|
// 确保最小宽度
|
||||||
|
int minWidth = dpToPx(120); // 最小120dp
|
||||||
|
if (columnWidth < minWidth) {
|
||||||
|
columnWidth = minWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
int columnHeight = (int) (columnWidth * aspectRatio);
|
||||||
|
|
||||||
|
// 设置FrameLayout尺寸
|
||||||
|
ViewGroup.LayoutParams layoutParams = binding.getRoot().getLayoutParams();
|
||||||
|
layoutParams.width = columnWidth;
|
||||||
|
layoutParams.height = columnHeight;
|
||||||
|
|
||||||
|
// 设置边距
|
||||||
|
if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
|
||||||
|
ViewGroup.MarginLayoutParams marginParams = (ViewGroup.MarginLayoutParams) layoutParams;
|
||||||
|
// 设置垂直间距(底部间距)
|
||||||
|
marginParams.bottomMargin = dpToPx(verticalSpacingDp);
|
||||||
|
// 水平间距将在onBindViewHolder中根据位置设置
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.getRoot().setLayoutParams(layoutParams);
|
||||||
|
|
||||||
|
// 设置CardView尺寸
|
||||||
|
ViewGroup.LayoutParams cardParams = binding.cardView.getLayoutParams();
|
||||||
|
cardParams.width = columnWidth;
|
||||||
|
cardParams.height = columnHeight;
|
||||||
|
binding.cardView.setLayoutParams(cardParams);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找父RecyclerView
|
||||||
|
*/
|
||||||
|
private RecyclerView findParentRecyclerView(View view) {
|
||||||
|
ViewParent parent = view.getParent();
|
||||||
|
while (parent != null) {
|
||||||
|
if (parent instanceof RecyclerView) {
|
||||||
|
return (RecyclerView) parent;
|
||||||
|
}
|
||||||
|
parent = parent.getParent();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull KeyboardViewHolder holder, @SuppressLint("RecyclerView") int position) {
|
||||||
|
DetailsjavaBean detailsjavaBean = mList.get(position);
|
||||||
|
|
||||||
|
// 显示或隐藏收藏图标
|
||||||
|
holder.binding.favoriteIcon.setVisibility(showFavoriteIcon ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
|
// 设置收藏图标状态
|
||||||
|
if (showFavoriteIcon) {
|
||||||
|
holder.binding.favoriteIcon.setSelected(true); // 假设都是已收藏状态
|
||||||
|
|
||||||
|
holder.binding.favoriteIcon.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
holder.binding.favoriteIcon.setSelected(false);
|
||||||
|
int adapterPosition = holder.getAdapterPosition();
|
||||||
|
if (adapterPosition != RecyclerView.NO_POSITION && mMyremoveCallback != null) {
|
||||||
|
mMyremoveCallback.OnRemoveLike(detailsjavaBean);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载图片
|
||||||
|
String thumbGif = detailsjavaBean.getThumbGif();
|
||||||
|
String thumb = detailsjavaBean.getThumbUrl();
|
||||||
|
|
||||||
|
if (thumbGif != null && !thumbGif.isEmpty()) {
|
||||||
|
UtilsCommon.INSTANCE.loadWepJif(mContext, thumbGif, holder.binding.keyboardImage);
|
||||||
|
} else {
|
||||||
|
Glide.with(mContext)
|
||||||
|
.load(thumb)
|
||||||
|
.error(R.drawable.phold)
|
||||||
|
.placeholder(R.drawable.phold)
|
||||||
|
.centerCrop()
|
||||||
|
.into(holder.binding.keyboardImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置卡片点击事件
|
||||||
|
holder.binding.cardView.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
openKeyboardPreview(detailsjavaBean, thumbGif, thumb);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 根据位置动态设置水平边距
|
||||||
|
setHorizontalMargins(holder, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据位置设置水平边距
|
||||||
|
*/
|
||||||
|
private void setHorizontalMargins(KeyboardViewHolder holder, int position) {
|
||||||
|
ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
|
||||||
|
if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
|
||||||
|
ViewGroup.MarginLayoutParams marginParams = (ViewGroup.MarginLayoutParams) layoutParams;
|
||||||
|
|
||||||
|
int column = position % spanCount; // 获取当前列
|
||||||
|
|
||||||
|
// 重置水平边距
|
||||||
|
marginParams.leftMargin = 0;
|
||||||
|
marginParams.rightMargin = 0;
|
||||||
|
|
||||||
|
if (includeEdgePadding) {
|
||||||
|
// 包含边缘边距的情况
|
||||||
|
int sidePaddingPx = dpToPx(sidePaddingDp);
|
||||||
|
int itemSpacingPx = dpToPx(itemSpacingDp);
|
||||||
|
|
||||||
|
if (column == 0) {
|
||||||
|
// 第一列:左边距 + 右边距为item间距的一半
|
||||||
|
marginParams.leftMargin = sidePaddingPx;
|
||||||
|
marginParams.rightMargin = itemSpacingPx / 2;
|
||||||
|
} else if (column == spanCount - 1) {
|
||||||
|
// 最后一列:左边距为item间距的一半 + 右边距
|
||||||
|
marginParams.leftMargin = itemSpacingPx / 2;
|
||||||
|
marginParams.rightMargin = sidePaddingPx;
|
||||||
|
} else {
|
||||||
|
// 中间列:左右边距各为item间距的一半
|
||||||
|
marginParams.leftMargin = itemSpacingPx / 2;
|
||||||
|
marginParams.rightMargin = itemSpacingPx / 2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 不包含边缘边距的情况
|
||||||
|
int itemSpacingPx = dpToPx(itemSpacingDp);
|
||||||
|
|
||||||
|
if (isPreviewMode) {
|
||||||
|
// 第一列:右边距5dp,第二列:无左边距
|
||||||
|
if (column == 0) {
|
||||||
|
// 第一列:右边距5dp
|
||||||
|
marginParams.rightMargin = itemSpacingPx;
|
||||||
|
}
|
||||||
|
// 第二列:不设置左边距,保持0
|
||||||
|
} else {
|
||||||
|
// 普通网格布局:原有的间距逻辑
|
||||||
|
if (column == 0) {
|
||||||
|
// 第一列:右边距为item间距
|
||||||
|
marginParams.rightMargin = itemSpacingPx;
|
||||||
|
} else if (column == spanCount - 1) {
|
||||||
|
// 最后一列:左边距为item间距
|
||||||
|
marginParams.leftMargin = itemSpacingPx;
|
||||||
|
} else {
|
||||||
|
// 中间列:左右边距各为item间距
|
||||||
|
marginParams.leftMargin = itemSpacingPx;
|
||||||
|
marginParams.rightMargin = itemSpacingPx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.itemView.setLayoutParams(marginParams);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return mList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开键盘预览
|
||||||
|
*/
|
||||||
|
private void openKeyboardPreview(DetailsjavaBean detailsjavaBean, String thumbGif, String thumb) {
|
||||||
|
Intent intentApply = new Intent(mContext, DetailkbActivity.class);
|
||||||
|
intentApply.putExtra(DetailkbActivity.SOURCE_KEY, detailsjavaBean);
|
||||||
|
intentApply.putExtra(DetailkbActivity.DISPLAY_URL_KEY, detailsjavaBean.getImgPath());
|
||||||
|
intentApply.putExtra(DetailkbActivity.ZIP_URL_KEY, detailsjavaBean.getZipPath());
|
||||||
|
intentApply.putExtra(DetailkbActivity.NAME_KEY, detailsjavaBean.getTitleName());
|
||||||
|
intentApply.putExtra(DetailkbActivity.GIF_KEY, detailsjavaBean.getImgGif());
|
||||||
|
|
||||||
|
String intent_thumb;
|
||||||
|
if (thumbGif != null && !thumbGif.isEmpty()) {
|
||||||
|
intent_thumb = thumbGif;
|
||||||
|
} else {
|
||||||
|
intent_thumb = thumb;
|
||||||
|
}
|
||||||
|
intentApply.putExtra(DetailkbActivity.THUMB_KEY, intent_thumb);
|
||||||
|
mContext.startActivity(intentApply);
|
||||||
|
|
||||||
|
// 调用回调(用于KBPreviewActivity)
|
||||||
|
if (mKBClickCallback != null) {
|
||||||
|
mKBClickCallback.OnItemClickListener();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除指定位置的项目
|
||||||
|
*/
|
||||||
|
public void removeItem(int position) {
|
||||||
|
if (position >= 0 && position < mList.size()) {
|
||||||
|
mList.remove(position);
|
||||||
|
notifyItemRemoved(position);
|
||||||
|
// 更新剩余item的位置
|
||||||
|
if (position < mList.size()) {
|
||||||
|
notifyItemRangeChanged(position, mList.size() - position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加项目
|
||||||
|
*/
|
||||||
|
public void addItem(DetailsjavaBean item) {
|
||||||
|
mList.add(item);
|
||||||
|
notifyItemInserted(mList.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空所有项目
|
||||||
|
*/
|
||||||
|
public void clearAll() {
|
||||||
|
mList.clear();
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class KeyboardViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
private AdapterKbitemBinding binding;
|
||||||
|
|
||||||
|
public KeyboardViewHolder(@NonNull AdapterKbitemBinding binding) {
|
||||||
|
super(binding.getRoot());
|
||||||
|
this.binding = binding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定位置的项目
|
||||||
|
*/
|
||||||
|
public DetailsjavaBean getItem(int position) {
|
||||||
|
if (position >= 0 && position < mList.size()) {
|
||||||
|
return mList.get(position);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找指定项目的索引位置
|
||||||
|
*/
|
||||||
|
public int getPositionOfItem(DetailsjavaBean item) {
|
||||||
|
for (int i = 0; i < mList.size(); i++) {
|
||||||
|
if (mList.get(i).equals(item)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dp转px
|
||||||
|
*/
|
||||||
|
private int dpToPx(int dp) {
|
||||||
|
if (mContext == null || mContext.getResources() == null) {
|
||||||
|
return dp;
|
||||||
|
}
|
||||||
|
float density = mContext.getResources().getDisplayMetrics().density;
|
||||||
|
return Math.round(dp * density);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
package com.rainbow.app.keyboard.myadapter;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.rainbow.app.keyboard.R;
|
||||||
|
import com.rainbow.app.keyboard.javabean.DetailsjavaBean;
|
||||||
|
import com.rainbow.app.keyboard.javabean.WrapperjavaBean;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class NavCategoryAdapter extends RecyclerView.Adapter<NavCategoryAdapter.NavViewHolder> {
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
private List<WrapperjavaBean> categoryList;
|
||||||
|
private CategoryClickListener categoryClickListener;
|
||||||
|
private int selectedPosition = 0; // 默认选中第一个分类
|
||||||
|
|
||||||
|
public interface CategoryClickListener {
|
||||||
|
void onCategoryClick(String categoryName, List<DetailsjavaBean> keyboards);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NavCategoryAdapter(Context context, List<WrapperjavaBean> categoryList) {
|
||||||
|
this.context = context;
|
||||||
|
this.categoryList = categoryList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCategoryClickListener(CategoryClickListener listener) {
|
||||||
|
this.categoryClickListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public NavViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View view = LayoutInflater.from(context).inflate(R.layout.item_nav_category, parent, false);
|
||||||
|
return new NavViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull NavViewHolder holder, @SuppressLint("RecyclerView") int position) {
|
||||||
|
WrapperjavaBean category = categoryList.get(position);
|
||||||
|
String categoryName = category.getParentName();
|
||||||
|
List<DetailsjavaBean> keyboards = category.getKeyboardList();
|
||||||
|
|
||||||
|
holder.categoryName.setText(categoryName);
|
||||||
|
|
||||||
|
// 设置选中状态
|
||||||
|
if (position == selectedPosition) {
|
||||||
|
holder.itemView.setBackgroundResource(R.color.nav_selected_bg);
|
||||||
|
holder.categoryName.setTextColor(context.getResources().getColor(R.color.text_color_selected));
|
||||||
|
} else {
|
||||||
|
holder.itemView.setBackgroundResource(R.color.transparent);
|
||||||
|
holder.categoryName.setTextColor(context.getResources().getColor(R.color.text_color));
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.itemView.setOnClickListener(v -> {
|
||||||
|
selectedPosition = position;
|
||||||
|
notifyDataSetChanged(); // 更新选中状态
|
||||||
|
if (categoryClickListener != null) {
|
||||||
|
categoryClickListener.onCategoryClick(categoryName, keyboards);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return categoryList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
static class NavViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
TextView categoryName;
|
||||||
|
|
||||||
|
public NavViewHolder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
categoryName = itemView.findViewById(R.id.nav_category_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package com.rainbow.app.keyboard.mycallback
|
||||||
|
|
||||||
|
import com.rainbow.app.keyboard.javabean.DetailsjavaBean
|
||||||
|
|
||||||
|
interface DeleteCallback {
|
||||||
|
|
||||||
|
fun OnRemoveLike(data: DetailsjavaBean)
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package com.rainbow.app.keyboard.mycallback
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
interface EnablekbCallback {
|
||||||
|
|
||||||
|
fun OnApplySkinListener(fileList: List<File?>?)
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
package com.rainbow.app.keyboard.mycallback
|
||||||
|
|
||||||
|
interface KBClickCallback {
|
||||||
|
|
||||||
|
fun OnItemClickListener( )
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
package com.rainbow.app.keyboard.mycallback
|
||||||
|
|
||||||
|
interface MylistCallback {
|
||||||
|
|
||||||
|
fun OnClickSeeAll(name: String)
|
||||||
|
}
|
||||||
32
app/src/main/java/com/rainbow/app/keyboard/mydb/DatabBase.kt
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package com.rainbow.app.keyboard.mydb
|
||||||
|
|
||||||
|
import androidx.room.Database
|
||||||
|
import androidx.room.Room
|
||||||
|
import androidx.room.RoomDatabase
|
||||||
|
import com.rainbow.app.keyboard.App
|
||||||
|
import com.rainbow.app.keyboard.javabean.DetailsjavaBean
|
||||||
|
|
||||||
|
|
||||||
|
@Database(
|
||||||
|
entities = [DetailsjavaBean::class],
|
||||||
|
version = App.Companion.DB_VERSION,
|
||||||
|
exportSchema = false
|
||||||
|
)
|
||||||
|
abstract class DatabBase : RoomDatabase() {
|
||||||
|
|
||||||
|
abstract fun ThemesDao(): DatabDao
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val databBase: DatabBase by lazy {
|
||||||
|
Room.databaseBuilder(
|
||||||
|
App.Companion.appInstance, DatabBase::class.java,
|
||||||
|
App.Companion.DB_NAME
|
||||||
|
)
|
||||||
|
.fallbackToDestructiveMigration()
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
31
app/src/main/java/com/rainbow/app/keyboard/mydb/DatabDao.kt
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package com.rainbow.app.keyboard.mydb
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Delete
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.Query
|
||||||
|
import androidx.room.Update
|
||||||
|
import com.rainbow.app.keyboard.javabean.DetailsjavaBean
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface DatabDao {
|
||||||
|
@Insert(onConflict = OnConflictStrategy.Companion.IGNORE)
|
||||||
|
suspend fun insertData(data: DetailsjavaBean): Long
|
||||||
|
|
||||||
|
@Query("select * from DetailsjavaBean ")
|
||||||
|
fun queryAllLike(): LiveData<List<DetailsjavaBean?>?>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Query("select * from DetailsjavaBean where titleName = :title ")
|
||||||
|
suspend fun queryIsLike(title: String ): DetailsjavaBean?
|
||||||
|
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
suspend fun delete(data: DetailsjavaBean)
|
||||||
|
|
||||||
|
@Update
|
||||||
|
suspend fun updateLike(data: DetailsjavaBean)
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
package com.rainbow.app.keyboard.mydb
|
||||||
|
|
||||||
|
import com.rainbow.app.keyboard.javabean.DetailsjavaBean
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
object DatabManager {
|
||||||
|
|
||||||
|
|
||||||
|
suspend fun addLike(data: DetailsjavaBean) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
DatabBase.databBase.ThemesDao().insertData(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
suspend fun removeLike(data: DetailsjavaBean) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val queryIsLike = DatabBase.databBase.ThemesDao().queryIsLike(data.titleName)
|
||||||
|
if (queryIsLike != null) {
|
||||||
|
DatabBase.databBase.ThemesDao().delete(queryIsLike)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
suspend fun getIsLike(name: String, action: (isLike: Boolean) -> Unit) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val query = DatabBase.databBase.ThemesDao().queryIsLike(name)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
action.invoke(query != null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,858 @@
|
|||||||
|
package com.rainbow.app.keyboard.mysourcecode;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.content.res.XmlResourceParser;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.util.Xml;
|
||||||
|
|
||||||
|
import androidx.annotation.XmlRes;
|
||||||
|
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
|
||||||
|
import com.rainbow.app.keyboard.R;
|
||||||
|
|
||||||
|
public class KeybCode {
|
||||||
|
|
||||||
|
static final String TAG = "------------Keyboard-----------";
|
||||||
|
|
||||||
|
// Keyboard XML Tags
|
||||||
|
private static final String TAG_KEYBOARD = "Keyboard";
|
||||||
|
private static final String TAG_ROW = "Row";
|
||||||
|
private static final String TAG_KEY = "Key";
|
||||||
|
|
||||||
|
public static final int EDGE_LEFT = 0x01;
|
||||||
|
public static final int EDGE_RIGHT = 0x02;
|
||||||
|
public static final int EDGE_TOP = 0x04;
|
||||||
|
public static final int EDGE_BOTTOM = 0x08;
|
||||||
|
|
||||||
|
public static final int KEYCODE_SHIFT = -1;
|
||||||
|
public static final int KEYCODE_MODE_CHANGE = -2;
|
||||||
|
public static final int KEYCODE_CANCEL = -3;
|
||||||
|
public static final int KEYCODE_DONE = -4;
|
||||||
|
public static final int KEYCODE_DELETE = -5;
|
||||||
|
public static final int KEYCODE_ALT = -6;
|
||||||
|
|
||||||
|
/** Keyboard label **/
|
||||||
|
private CharSequence mLabel;
|
||||||
|
|
||||||
|
/** Horizontal gap default for all rows */
|
||||||
|
private int mDefaultHorizontalGap;
|
||||||
|
|
||||||
|
/** Default key width */
|
||||||
|
private int mDefaultWidth;
|
||||||
|
|
||||||
|
/** Default key height */
|
||||||
|
private int mDefaultHeight;
|
||||||
|
|
||||||
|
/** Default gap between rows */
|
||||||
|
private int mDefaultVerticalGap;
|
||||||
|
|
||||||
|
/** Is the keyboard in the shifted state */
|
||||||
|
private boolean mShifted;
|
||||||
|
|
||||||
|
/** Key instance for the shift key, if present */
|
||||||
|
private KeybCode.Key[] mShiftKeys = { null, null };
|
||||||
|
|
||||||
|
/** Key index for the shift key, if present */
|
||||||
|
private int[] mShiftKeyIndices = {-1, -1};
|
||||||
|
|
||||||
|
/** Current key width, while loading the keyboard */
|
||||||
|
private int mKeyWidth;
|
||||||
|
|
||||||
|
/** Current key height, while loading the keyboard */
|
||||||
|
private int mKeyHeight;
|
||||||
|
|
||||||
|
/** Total height of the keyboard, including the padding and keys */
|
||||||
|
private int mTotalHeight;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Total width of the keyboard, including left side gaps and keys, but not any gaps on the
|
||||||
|
* right side.
|
||||||
|
*/
|
||||||
|
private int mTotalWidth;
|
||||||
|
|
||||||
|
/** List of keys in this keyboard */
|
||||||
|
private List<KeybCode.Key> mKeys;
|
||||||
|
|
||||||
|
/** List of modifier keys such as Shift & Alt, if any */
|
||||||
|
private List<KeybCode.Key> mModifierKeys;
|
||||||
|
|
||||||
|
/** Width of the screen available to fit the keyboard */
|
||||||
|
private int mDisplayWidth;
|
||||||
|
|
||||||
|
/** Height of the screen */
|
||||||
|
private int mDisplayHeight;
|
||||||
|
|
||||||
|
/** Keyboard mode, or zero, if none. */
|
||||||
|
private int mKeyboardMode;
|
||||||
|
|
||||||
|
// Variables for pre-computing nearest keys.
|
||||||
|
|
||||||
|
private static final int GRID_WIDTH = 10;
|
||||||
|
private static final int GRID_HEIGHT = 5;
|
||||||
|
private static final int GRID_SIZE = GRID_WIDTH * GRID_HEIGHT;
|
||||||
|
private int mCellWidth;
|
||||||
|
private int mCellHeight;
|
||||||
|
private int[][] mGridNeighbors;
|
||||||
|
private int mProximityThreshold;
|
||||||
|
/** Number of key widths from current touch point to search for nearest keys. */
|
||||||
|
private static float SEARCH_DISTANCE = 1.8f;
|
||||||
|
|
||||||
|
private ArrayList<KeybCode.Row> rows = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Container for keys in the keyboard. All keys in a row are at the same Y-coordinate.
|
||||||
|
* Some of the key size defaults can be overridden per row from what the {@link KeybCode}
|
||||||
|
* defines.
|
||||||
|
* @attr ref android.R.styleable#King_Keyboard_keyWidth
|
||||||
|
* @attr ref android.R.styleable#King_Keyboard_keyHeight
|
||||||
|
* @attr ref android.R.styleable#King_Keyboard_horizontalGap
|
||||||
|
* @attr ref android.R.styleable#King_Keyboard_verticalGap
|
||||||
|
* @attr ref android.R.styleable#King_Keyboard_Row_rowEdgeFlags
|
||||||
|
* @attr ref android.R.styleable#King_Keyboard_Row_keyboardMode
|
||||||
|
*/
|
||||||
|
public static class Row {
|
||||||
|
/** Default width of a key in this row. */
|
||||||
|
public int defaultWidth;
|
||||||
|
/** Default height of a key in this row. */
|
||||||
|
public int defaultHeight;
|
||||||
|
/** Default horizontal gap between keys in this row. */
|
||||||
|
public int defaultHorizontalGap;
|
||||||
|
/** Vertical gap following this row. */
|
||||||
|
public int verticalGap;
|
||||||
|
|
||||||
|
ArrayList<KeybCode.Key> mKeys = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edge flags for this row of keys. Possible values that can be assigned are
|
||||||
|
* {@link KeybCode#EDGE_TOP EDGE_TOP} and {@link KeybCode#EDGE_BOTTOM EDGE_BOTTOM}
|
||||||
|
*/
|
||||||
|
public int rowEdgeFlags;
|
||||||
|
|
||||||
|
/** The keyboard mode for this row */
|
||||||
|
public int mode;
|
||||||
|
|
||||||
|
private KeybCode parent;
|
||||||
|
|
||||||
|
public Row(KeybCode parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Row(Resources res, KeybCode parent, XmlResourceParser parser) {
|
||||||
|
this.parent = parent;
|
||||||
|
TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
|
||||||
|
R.styleable.My_Keyboard_view);
|
||||||
|
defaultWidth = getDimensionOrFraction(a,
|
||||||
|
R.styleable.My_Keyboard_view_android_keyWidth,
|
||||||
|
parent.mDisplayWidth, parent.mDefaultWidth);
|
||||||
|
defaultHeight = getDimensionOrFraction(a,
|
||||||
|
R.styleable.My_Keyboard_view_android_keyHeight,
|
||||||
|
parent.mDisplayHeight, parent.mDefaultHeight);
|
||||||
|
defaultHorizontalGap = getDimensionOrFraction(a,
|
||||||
|
R.styleable.My_Keyboard_view_android_horizontalGap,
|
||||||
|
parent.mDisplayWidth, parent.mDefaultHorizontalGap);
|
||||||
|
verticalGap = getDimensionOrFraction(a,
|
||||||
|
R.styleable.My_Keyboard_view_android_verticalGap,
|
||||||
|
parent.mDisplayHeight, parent.mDefaultVerticalGap);
|
||||||
|
a.recycle();
|
||||||
|
a = res.obtainAttributes(Xml.asAttributeSet(parser),
|
||||||
|
R.styleable.Kil_Keyboard_Row);
|
||||||
|
rowEdgeFlags = a.getInt(R.styleable.Kil_Keyboard_Row_android_rowEdgeFlags, 0);
|
||||||
|
mode = a.getResourceId(R.styleable.Kil_Keyboard_Row_android_keyboardMode,
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for describing the position and characteristics of a single key in the keyboard.
|
||||||
|
*
|
||||||
|
* @attr ref android.R.styleable#King_Keyboard_keyWidth
|
||||||
|
* @attr ref android.R.styleable#King_Keyboard_keyHeight
|
||||||
|
* @attr ref android.R.styleable#King_Keyboard_horizontalGap
|
||||||
|
* @attr ref android.R.styleable#King_Keyboard_Key_codes
|
||||||
|
* @attr ref android.R.styleable#King_Keyboard_Key_keyIcon
|
||||||
|
* @attr ref android.R.styleable#King_Keyboard_Key_keyLabel
|
||||||
|
* @attr ref android.R.styleable#King_Keyboard_Key_iconPreview
|
||||||
|
* @attr ref android.R.styleable#King_Keyboard_Key_isSticky
|
||||||
|
* @attr ref android.R.styleable#King_Keyboard_Key_isRepeatable
|
||||||
|
* @attr ref android.R.styleable#King_Keyboard_Key_isModifier
|
||||||
|
* @attr ref android.R.styleable#King_Keyboard_Key_popupKeyboard
|
||||||
|
* @attr ref android.R.styleable#King_Keyboard_Key_popupCharacters
|
||||||
|
* @attr ref android.R.styleable#King_Keyboard_Key_keyOutputText
|
||||||
|
* @attr ref android.R.styleable#King_Keyboard_Key_keyEdgeFlags
|
||||||
|
*/
|
||||||
|
public static class Key {
|
||||||
|
/**
|
||||||
|
* All the key codes (unicode or custom code) that this key could generate, zero'th
|
||||||
|
* being the most important.
|
||||||
|
*/
|
||||||
|
public int[] codes;
|
||||||
|
|
||||||
|
/** Label to display */
|
||||||
|
public CharSequence label;
|
||||||
|
|
||||||
|
/** Icon to display instead of a label. Icon takes precedence over a label */
|
||||||
|
public Drawable icon;
|
||||||
|
/** Preview version of the icon, for the preview popup */
|
||||||
|
public Drawable iconPreview;
|
||||||
|
/** Width of the key, not including the gap */
|
||||||
|
public int width;
|
||||||
|
/** Height of the key, not including the gap */
|
||||||
|
public int height;
|
||||||
|
/** The horizontal gap before this key */
|
||||||
|
public int gap;
|
||||||
|
/** Whether this key is sticky, i.e., a toggle key */
|
||||||
|
public boolean sticky;
|
||||||
|
/** X coordinate of the key in the keyboard layout */
|
||||||
|
public int x;
|
||||||
|
/** Y coordinate of the key in the keyboard layout */
|
||||||
|
public int y;
|
||||||
|
/** The current pressed state of this key */
|
||||||
|
public boolean pressed;
|
||||||
|
/** If this is a sticky key, is it on? */
|
||||||
|
public boolean on;
|
||||||
|
/** Text to output when pressed. This can be multiple characters, like ".com" */
|
||||||
|
public CharSequence text;
|
||||||
|
/** Popup characters */
|
||||||
|
public CharSequence popupCharacters;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flags that specify the anchoring to edges of the keyboard for detecting touch events
|
||||||
|
* that are just out of the boundary of the key. This is a bit mask of
|
||||||
|
* {@link KeybCode#EDGE_LEFT}, {@link KeybCode#EDGE_RIGHT}, {@link KeybCode#EDGE_TOP} and
|
||||||
|
* {@link KeybCode#EDGE_BOTTOM}.
|
||||||
|
*/
|
||||||
|
public int edgeFlags;
|
||||||
|
/** Whether this is a modifier key, such as Shift or Alt */
|
||||||
|
public boolean modifier;
|
||||||
|
/** The keyboard that this key belongs to */
|
||||||
|
private KeybCode keyboard;
|
||||||
|
/**
|
||||||
|
* If this key pops up a mini keyboard, this is the resource id for the XML layout for that
|
||||||
|
* keyboard.
|
||||||
|
*/
|
||||||
|
public int popupResId;
|
||||||
|
/** Whether this key repeats itself when held down */
|
||||||
|
public boolean repeatable;
|
||||||
|
|
||||||
|
|
||||||
|
private final static int[] KEY_STATE_NORMAL_ON = {
|
||||||
|
android.R.attr.state_checkable,
|
||||||
|
android.R.attr.state_checked
|
||||||
|
};
|
||||||
|
|
||||||
|
private final static int[] KEY_STATE_PRESSED_ON = {
|
||||||
|
android.R.attr.state_pressed,
|
||||||
|
android.R.attr.state_checkable,
|
||||||
|
android.R.attr.state_checked
|
||||||
|
};
|
||||||
|
|
||||||
|
private final static int[] KEY_STATE_NORMAL_OFF = {
|
||||||
|
android.R.attr.state_checkable
|
||||||
|
};
|
||||||
|
|
||||||
|
private final static int[] KEY_STATE_PRESSED_OFF = {
|
||||||
|
android.R.attr.state_pressed,
|
||||||
|
android.R.attr.state_checkable
|
||||||
|
};
|
||||||
|
|
||||||
|
private final static int[] KEY_STATE_NORMAL = {
|
||||||
|
};
|
||||||
|
|
||||||
|
private final static int[] KEY_STATE_PRESSED = {
|
||||||
|
android.R.attr.state_pressed
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Create an empty key with no attributes. */
|
||||||
|
public Key(KeybCode.Row parent) {
|
||||||
|
keyboard = parent.parent;
|
||||||
|
height = parent.defaultHeight;
|
||||||
|
width = parent.defaultWidth;
|
||||||
|
gap = parent.defaultHorizontalGap;
|
||||||
|
edgeFlags = parent.rowEdgeFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Create a key with the given top-left coordinate and extract its attributes from
|
||||||
|
* the XML parser.
|
||||||
|
* @param res resources associated with the caller's context
|
||||||
|
* @param parent the row that this key belongs to. The row must already be attached to
|
||||||
|
* a {@link KeybCode}.
|
||||||
|
* @param x the x coordinate of the top-left
|
||||||
|
* @param y the y coordinate of the top-left
|
||||||
|
* @param parser the XML parser containing the attributes for this key
|
||||||
|
*/
|
||||||
|
public Key(Resources res, KeybCode.Row parent, int x, int y, XmlResourceParser parser) {
|
||||||
|
this(parent);
|
||||||
|
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
|
||||||
|
TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
|
||||||
|
R.styleable.My_Keyboard_view);
|
||||||
|
|
||||||
|
width = getDimensionOrFraction(a,
|
||||||
|
R.styleable.My_Keyboard_view_android_keyWidth,
|
||||||
|
keyboard.mDisplayWidth, parent.defaultWidth);
|
||||||
|
height = getDimensionOrFraction(a,
|
||||||
|
R.styleable.My_Keyboard_view_android_keyHeight,
|
||||||
|
keyboard.mDisplayHeight, parent.defaultHeight);
|
||||||
|
gap = getDimensionOrFraction(a,
|
||||||
|
R.styleable.My_Keyboard_view_android_horizontalGap,
|
||||||
|
keyboard.mDisplayWidth, parent.defaultHorizontalGap);
|
||||||
|
a.recycle();
|
||||||
|
a = res.obtainAttributes(Xml.asAttributeSet(parser),
|
||||||
|
R.styleable.K_Keyboard_Key);
|
||||||
|
this.x += gap;
|
||||||
|
TypedValue codesValue = new TypedValue();
|
||||||
|
a.getValue(R.styleable.K_Keyboard_Key_android_codes,
|
||||||
|
codesValue);
|
||||||
|
if (codesValue.type == TypedValue.TYPE_INT_DEC
|
||||||
|
|| codesValue.type == TypedValue.TYPE_INT_HEX) {
|
||||||
|
codes = new int[] { codesValue.data };
|
||||||
|
} else if (codesValue.type == TypedValue.TYPE_STRING) {
|
||||||
|
codes = parseCSV(codesValue.string.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
iconPreview = a.getDrawable(R.styleable.K_Keyboard_Key_android_iconPreview);
|
||||||
|
if (iconPreview != null) {
|
||||||
|
iconPreview.setBounds(0, 0, iconPreview.getIntrinsicWidth(),
|
||||||
|
iconPreview.getIntrinsicHeight());
|
||||||
|
}
|
||||||
|
popupCharacters = a.getText(
|
||||||
|
R.styleable.K_Keyboard_Key_android_popupCharacters);
|
||||||
|
popupResId = a.getResourceId(
|
||||||
|
R.styleable.K_Keyboard_Key_android_popupKeyboard, 0);
|
||||||
|
repeatable = a.getBoolean(
|
||||||
|
R.styleable.K_Keyboard_Key_android_isRepeatable, false);
|
||||||
|
modifier = a.getBoolean(
|
||||||
|
R.styleable.K_Keyboard_Key_android_isModifier, false);
|
||||||
|
sticky = a.getBoolean(
|
||||||
|
R.styleable.K_Keyboard_Key_android_isSticky, false);
|
||||||
|
edgeFlags = a.getInt(R.styleable.K_Keyboard_Key_android_keyEdgeFlags, 0);
|
||||||
|
edgeFlags |= parent.rowEdgeFlags;
|
||||||
|
|
||||||
|
icon = a.getDrawable(
|
||||||
|
R.styleable.K_Keyboard_Key_android_keyIcon);
|
||||||
|
if (icon != null) {
|
||||||
|
icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
|
||||||
|
}
|
||||||
|
label = a.getText(R.styleable.K_Keyboard_Key_android_keyLabel);
|
||||||
|
text = a.getText(R.styleable.K_Keyboard_Key_android_keyOutputText);
|
||||||
|
|
||||||
|
if (codes == null && !TextUtils.isEmpty(label)) {
|
||||||
|
codes = new int[] { label.charAt(0) };
|
||||||
|
}
|
||||||
|
a.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Informs the key that it has been pressed, in case it needs to change its appearance or
|
||||||
|
* state.
|
||||||
|
* @see #onReleased(boolean)
|
||||||
|
*/
|
||||||
|
public void onPressed() {
|
||||||
|
pressed = !pressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the pressed state of the key.
|
||||||
|
*
|
||||||
|
* <p>Toggled state of the key will be flipped when all the following conditions are
|
||||||
|
* fulfilled:</p>
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>This is a sticky key, that is, {@link #sticky} is {@code true}.
|
||||||
|
* <li>The parameter {@code inside} is {@code true}.
|
||||||
|
* <li>{@link android.os.Build.VERSION#SDK_INT} is greater than
|
||||||
|
* {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param inside whether the finger was released inside the key. Works only on Android M and
|
||||||
|
* later. See the method document for details.
|
||||||
|
* @see #onPressed()
|
||||||
|
*/
|
||||||
|
public void onReleased(boolean inside) {
|
||||||
|
pressed = !pressed;
|
||||||
|
if (sticky && inside) {
|
||||||
|
on = !on;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] parseCSV(String value) {
|
||||||
|
int count = 0;
|
||||||
|
int lastIndex = 0;
|
||||||
|
if (value.length() > 0) {
|
||||||
|
count++;
|
||||||
|
while ((lastIndex = value.indexOf(",", lastIndex + 1)) > 0) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int[] values = new int[count];
|
||||||
|
count = 0;
|
||||||
|
StringTokenizer st = new StringTokenizer(value, ",");
|
||||||
|
while (st.hasMoreTokens()) {
|
||||||
|
try {
|
||||||
|
values[count++] = Integer.parseInt(st.nextToken());
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects if a point falls inside this key.
|
||||||
|
* @param x the x-coordinate of the point
|
||||||
|
* @param y the y-coordinate of the point
|
||||||
|
* @return whether or not the point falls inside the key. If the key is attached to an edge,
|
||||||
|
* it will assume that all points between the key and the edge are considered to be inside
|
||||||
|
* the key.
|
||||||
|
*/
|
||||||
|
public boolean isInside(int x, int y) {
|
||||||
|
boolean leftEdge = (edgeFlags & EDGE_LEFT) > 0;
|
||||||
|
boolean rightEdge = (edgeFlags & EDGE_RIGHT) > 0;
|
||||||
|
boolean topEdge = (edgeFlags & EDGE_TOP) > 0;
|
||||||
|
boolean bottomEdge = (edgeFlags & EDGE_BOTTOM) > 0;
|
||||||
|
if ((x >= this.x || (leftEdge && x <= this.x + this.width))
|
||||||
|
&& (x < this.x + this.width || (rightEdge && x >= this.x))
|
||||||
|
&& (y >= this.y || (topEdge && y <= this.y + this.height))
|
||||||
|
&& (y < this.y + this.height || (bottomEdge && y >= this.y))) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the square of the distance between the center of the key and the given point.
|
||||||
|
* @param x the x-coordinate of the point
|
||||||
|
* @param y the y-coordinate of the point
|
||||||
|
* @return the square of the distance of the point from the center of the key
|
||||||
|
*/
|
||||||
|
public int squaredDistanceFrom(int x, int y) {
|
||||||
|
int xDist = this.x + width / 2 - x;
|
||||||
|
int yDist = this.y + height / 2 - y;
|
||||||
|
return xDist * xDist + yDist * yDist;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the drawable state for the key, based on the current state and type of the key.
|
||||||
|
* @return the drawable state of the key.
|
||||||
|
* @see android.graphics.drawable.StateListDrawable#setState(int[])
|
||||||
|
*/
|
||||||
|
public int[] getCurrentDrawableState() {
|
||||||
|
int[] states = KEY_STATE_NORMAL;
|
||||||
|
|
||||||
|
if (on) {
|
||||||
|
if (pressed) {
|
||||||
|
states = KEY_STATE_PRESSED_ON;
|
||||||
|
} else {
|
||||||
|
states = KEY_STATE_NORMAL_ON;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (sticky) {
|
||||||
|
if (pressed) {
|
||||||
|
states = KEY_STATE_PRESSED_OFF;
|
||||||
|
} else {
|
||||||
|
states = KEY_STATE_NORMAL_OFF;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (pressed) {
|
||||||
|
states = KEY_STATE_PRESSED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return states;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a keyboard from the given xml key layout file.
|
||||||
|
* @param context the application or service context
|
||||||
|
* @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
|
||||||
|
*/
|
||||||
|
public KeybCode(Context context, int xmlLayoutResId) {
|
||||||
|
this(context, xmlLayoutResId, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a keyboard from the given xml key layout file. Weeds out rows
|
||||||
|
* that have a keyboard mode defined but don't match the specified mode.
|
||||||
|
* @param context the application or service context
|
||||||
|
* @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
|
||||||
|
* @param modeId keyboard mode identifier
|
||||||
|
* @param width sets width of keyboard
|
||||||
|
* @param height sets height of keyboard
|
||||||
|
*/
|
||||||
|
public KeybCode(Context context, @XmlRes int xmlLayoutResId, int modeId, int width,
|
||||||
|
int height) {
|
||||||
|
mDisplayWidth = width;
|
||||||
|
mDisplayHeight = height;
|
||||||
|
|
||||||
|
mDefaultHorizontalGap = 0;
|
||||||
|
mDefaultWidth = mDisplayWidth / 10;
|
||||||
|
mDefaultVerticalGap = 0;
|
||||||
|
mDefaultHeight = mDefaultWidth;
|
||||||
|
mKeys = new ArrayList<>();
|
||||||
|
mModifierKeys = new ArrayList<>();
|
||||||
|
mKeyboardMode = modeId;
|
||||||
|
loadKeyboard(context, context.getResources().getXml(xmlLayoutResId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a keyboard from the given xml key layout file. Weeds out rows
|
||||||
|
* that have a keyboard mode defined but don't match the specified mode.
|
||||||
|
* @param context the application or service context
|
||||||
|
* @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
|
||||||
|
* @param modeId keyboard mode identifier
|
||||||
|
*/
|
||||||
|
public KeybCode(Context context, @XmlRes int xmlLayoutResId, int modeId) {
|
||||||
|
DisplayMetrics dm = context.getResources().getDisplayMetrics();
|
||||||
|
mDisplayWidth = dm.widthPixels;
|
||||||
|
mDisplayHeight = dm.heightPixels;
|
||||||
|
//Log.v(TAG, "keyboard's display metrics:" + dm);
|
||||||
|
|
||||||
|
mDefaultHorizontalGap = 0;
|
||||||
|
mDefaultWidth = mDisplayWidth / 10;
|
||||||
|
mDefaultVerticalGap = 0;
|
||||||
|
mDefaultHeight = mDefaultWidth;
|
||||||
|
mKeys = new ArrayList<>();
|
||||||
|
mModifierKeys = new ArrayList<>();
|
||||||
|
mKeyboardMode = modeId;
|
||||||
|
loadKeyboard(context, context.getResources().getXml(xmlLayoutResId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeybCode(Context context, int layoutTemplateResId,
|
||||||
|
CharSequence characters, int columns, int horizontalPadding) {
|
||||||
|
this(context, layoutTemplateResId);
|
||||||
|
int x = 0;
|
||||||
|
int y = 0;
|
||||||
|
int column = 0;
|
||||||
|
mTotalWidth = 0;
|
||||||
|
|
||||||
|
KeybCode.Row row = new KeybCode.Row(this);
|
||||||
|
row.defaultHeight = mDefaultHeight;
|
||||||
|
row.defaultWidth = mDefaultWidth;
|
||||||
|
row.defaultHorizontalGap = mDefaultHorizontalGap;
|
||||||
|
row.verticalGap = mDefaultVerticalGap;
|
||||||
|
row.rowEdgeFlags = EDGE_TOP | EDGE_BOTTOM;
|
||||||
|
final int maxColumns = columns == -1 ? Integer.MAX_VALUE : columns;
|
||||||
|
for (int i = 0; i < characters.length(); i++) {
|
||||||
|
char c = characters.charAt(i);
|
||||||
|
if (column >= maxColumns
|
||||||
|
|| x + mDefaultWidth + horizontalPadding > mDisplayWidth) {
|
||||||
|
x = 0;
|
||||||
|
y += mDefaultVerticalGap + mDefaultHeight;
|
||||||
|
column = 0;
|
||||||
|
}
|
||||||
|
final KeybCode.Key key = new KeybCode.Key(row);
|
||||||
|
key.x = x;
|
||||||
|
key.y = y;
|
||||||
|
key.label = String.valueOf(c);
|
||||||
|
key.codes = new int[] { c };
|
||||||
|
column++;
|
||||||
|
x += key.width + key.gap;
|
||||||
|
mKeys.add(key);
|
||||||
|
row.mKeys.add(key);
|
||||||
|
if (x > mTotalWidth) {
|
||||||
|
mTotalWidth = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mTotalHeight = y + mDefaultHeight;
|
||||||
|
rows.add(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
final void resize(int newWidth, int newHeight) {
|
||||||
|
int numRows = rows.size();
|
||||||
|
for (int rowIndex = 0; rowIndex < numRows; ++rowIndex) {
|
||||||
|
KeybCode.Row row = rows.get(rowIndex);
|
||||||
|
int numKeys = row.mKeys.size();
|
||||||
|
int totalGap = 0;
|
||||||
|
int totalWidth = 0;
|
||||||
|
for (int keyIndex = 0; keyIndex < numKeys; ++keyIndex) {
|
||||||
|
KeybCode.Key key = row.mKeys.get(keyIndex);
|
||||||
|
if (keyIndex > 0) {
|
||||||
|
totalGap += key.gap;
|
||||||
|
}
|
||||||
|
totalWidth += key.width;
|
||||||
|
}
|
||||||
|
if (totalGap + totalWidth > newWidth) {
|
||||||
|
int x = 0;
|
||||||
|
float scaleFactor = (float)(newWidth - totalGap) / totalWidth;
|
||||||
|
for (int keyIndex = 0; keyIndex < numKeys; ++keyIndex) {
|
||||||
|
KeybCode.Key key = row.mKeys.get(keyIndex);
|
||||||
|
key.width *= scaleFactor;
|
||||||
|
key.x = x;
|
||||||
|
x += key.width + key.gap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mTotalWidth = newWidth;
|
||||||
|
// TODO: This does not adjust the vertical placement according to the new size.
|
||||||
|
// The main problem in the previous code was horizontal placement/size, but we should
|
||||||
|
// also recalculate the vertical sizes/positions when we get this resize call.
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<KeybCode.Key> getKeys() {
|
||||||
|
return mKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<KeybCode.Key> getModifierKeys() {
|
||||||
|
return mModifierKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getHorizontalGap() {
|
||||||
|
return mDefaultHorizontalGap;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setHorizontalGap(int gap) {
|
||||||
|
mDefaultHorizontalGap = gap;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getVerticalGap() {
|
||||||
|
return mDefaultVerticalGap;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setVerticalGap(int gap) {
|
||||||
|
mDefaultVerticalGap = gap;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getKeyHeight() {
|
||||||
|
return mDefaultHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setKeyHeight(int height) {
|
||||||
|
mDefaultHeight = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getKeyWidth() {
|
||||||
|
return mDefaultWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setKeyWidth(int width) {
|
||||||
|
mDefaultWidth = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the total height of the keyboard
|
||||||
|
* @return the total height of the keyboard
|
||||||
|
*/
|
||||||
|
public int getHeight() {
|
||||||
|
return mTotalHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMinWidth() {
|
||||||
|
return mTotalWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setShifted(boolean shiftState) {
|
||||||
|
for (KeybCode.Key shiftKey : mShiftKeys) {
|
||||||
|
if (shiftKey != null) {
|
||||||
|
shiftKey.on = shiftState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mShifted != shiftState) {
|
||||||
|
mShifted = shiftState;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isShifted() {
|
||||||
|
return mShifted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public int[] getShiftKeyIndices() {
|
||||||
|
return mShiftKeyIndices;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getShiftKeyIndex() {
|
||||||
|
return mShiftKeyIndices[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void computeNearestNeighbors() {
|
||||||
|
// Round-up so we don't have any pixels outside the grid
|
||||||
|
mCellWidth = (getMinWidth() + GRID_WIDTH - 1) / GRID_WIDTH;
|
||||||
|
mCellHeight = (getHeight() + GRID_HEIGHT - 1) / GRID_HEIGHT;
|
||||||
|
mGridNeighbors = new int[GRID_SIZE][];
|
||||||
|
int[] indices = new int[mKeys.size()];
|
||||||
|
final int gridWidth = GRID_WIDTH * mCellWidth;
|
||||||
|
final int gridHeight = GRID_HEIGHT * mCellHeight;
|
||||||
|
for (int x = 0; x < gridWidth; x += mCellWidth) {
|
||||||
|
for (int y = 0; y < gridHeight; y += mCellHeight) {
|
||||||
|
int count = 0;
|
||||||
|
for (int i = 0; i < mKeys.size(); i++) {
|
||||||
|
final KeybCode.Key key = mKeys.get(i);
|
||||||
|
if (key.squaredDistanceFrom(x, y) < mProximityThreshold ||
|
||||||
|
key.squaredDistanceFrom(x + mCellWidth - 1, y) < mProximityThreshold ||
|
||||||
|
key.squaredDistanceFrom(x + mCellWidth - 1, y + mCellHeight - 1)
|
||||||
|
< mProximityThreshold ||
|
||||||
|
key.squaredDistanceFrom(x, y + mCellHeight - 1) < mProximityThreshold) {
|
||||||
|
indices[count++] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int [] cell = new int[count];
|
||||||
|
System.arraycopy(indices, 0, cell, 0, count);
|
||||||
|
mGridNeighbors[(y / mCellHeight) * GRID_WIDTH + (x / mCellWidth)] = cell;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the indices of the keys that are closest to the given point.
|
||||||
|
* @param x the x-coordinate of the point
|
||||||
|
* @param y the y-coordinate of the point
|
||||||
|
* @return the array of integer indices for the nearest keys to the given point. If the given
|
||||||
|
* point is out of range, then an array of size zero is returned.
|
||||||
|
*/
|
||||||
|
public int[] getNearestKeys(int x, int y) {
|
||||||
|
if (mGridNeighbors == null) computeNearestNeighbors();
|
||||||
|
if (x >= 0 && x < getMinWidth() && y >= 0 && y < getHeight()) {
|
||||||
|
int index = (y / mCellHeight) * GRID_WIDTH + (x / mCellWidth);
|
||||||
|
if (index < GRID_SIZE) {
|
||||||
|
return mGridNeighbors[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new int[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected KeybCode.Row createRowFromXml(Resources res, XmlResourceParser parser) {
|
||||||
|
return new KeybCode.Row(res, this, parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected KeybCode.Key createKeyFromXml(Resources res, KeybCode.Row parent, int x, int y,
|
||||||
|
XmlResourceParser parser) {
|
||||||
|
return new KeybCode.Key(res, parent, x, y, parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadKeyboard(Context context, XmlResourceParser parser) {
|
||||||
|
boolean inKey = false;
|
||||||
|
boolean inRow = false;
|
||||||
|
boolean leftMostKey = false;
|
||||||
|
int row = 0;
|
||||||
|
int x = 0;
|
||||||
|
int y = 0;
|
||||||
|
KeybCode.Key key = null;
|
||||||
|
KeybCode.Row currentRow = null;
|
||||||
|
Resources res = context.getResources();
|
||||||
|
boolean skipRow = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
int event;
|
||||||
|
while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) {
|
||||||
|
if (event == XmlResourceParser.START_TAG) {
|
||||||
|
String tag = parser.getName();
|
||||||
|
if (TAG_ROW.equals(tag)) {
|
||||||
|
inRow = true;
|
||||||
|
x = 0;
|
||||||
|
currentRow = createRowFromXml(res, parser);
|
||||||
|
rows.add(currentRow);
|
||||||
|
skipRow = currentRow.mode != 0 && currentRow.mode != mKeyboardMode;
|
||||||
|
if (skipRow) {
|
||||||
|
skipToEndOfRow(parser);
|
||||||
|
inRow = false;
|
||||||
|
}
|
||||||
|
} else if (TAG_KEY.equals(tag)) {
|
||||||
|
inKey = true;
|
||||||
|
key = createKeyFromXml(res, currentRow, x, y, parser);
|
||||||
|
mKeys.add(key);
|
||||||
|
if (key.codes[0] == KEYCODE_SHIFT) {
|
||||||
|
// Find available shift key slot and put this shift key in it
|
||||||
|
for (int i = 0; i < mShiftKeys.length; i++) {
|
||||||
|
if (mShiftKeys[i] == null) {
|
||||||
|
mShiftKeys[i] = key;
|
||||||
|
mShiftKeyIndices[i] = mKeys.size()-1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mModifierKeys.add(key);
|
||||||
|
} else if (key.codes[0] == KEYCODE_ALT) {
|
||||||
|
mModifierKeys.add(key);
|
||||||
|
}
|
||||||
|
currentRow.mKeys.add(key);
|
||||||
|
} else if (TAG_KEYBOARD.equals(tag)) {
|
||||||
|
parseKeyboardAttributes(res, parser);
|
||||||
|
}
|
||||||
|
} else if (event == XmlResourceParser.END_TAG) {
|
||||||
|
if (inKey) {
|
||||||
|
inKey = false;
|
||||||
|
x += key.gap + key.width;
|
||||||
|
if (x > mTotalWidth) {
|
||||||
|
mTotalWidth = x;
|
||||||
|
}
|
||||||
|
} else if (inRow) {
|
||||||
|
inRow = false;
|
||||||
|
y += currentRow.verticalGap;
|
||||||
|
y += currentRow.defaultHeight;
|
||||||
|
row++;
|
||||||
|
} else {
|
||||||
|
// TODO: error or extend?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
mTotalHeight = y - mDefaultVerticalGap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void skipToEndOfRow(XmlResourceParser parser)
|
||||||
|
throws XmlPullParserException, IOException {
|
||||||
|
int event;
|
||||||
|
while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) {
|
||||||
|
if (event == XmlResourceParser.END_TAG
|
||||||
|
&& parser.getName().equals(TAG_ROW)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseKeyboardAttributes(Resources res, XmlResourceParser parser) {
|
||||||
|
TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
|
||||||
|
R.styleable.My_Keyboard_view);
|
||||||
|
|
||||||
|
mDefaultWidth = getDimensionOrFraction(a,
|
||||||
|
R.styleable.My_Keyboard_view_android_keyWidth,
|
||||||
|
mDisplayWidth, mDisplayWidth / 10);
|
||||||
|
mDefaultHeight = getDimensionOrFraction(a,
|
||||||
|
R.styleable.My_Keyboard_view_android_keyHeight,
|
||||||
|
mDisplayHeight, 50);
|
||||||
|
mDefaultHorizontalGap = getDimensionOrFraction(a,
|
||||||
|
R.styleable.My_Keyboard_view_android_horizontalGap,
|
||||||
|
mDisplayWidth, 0);
|
||||||
|
mDefaultVerticalGap = getDimensionOrFraction(a,
|
||||||
|
R.styleable.My_Keyboard_view_android_verticalGap,
|
||||||
|
mDisplayHeight, 0);
|
||||||
|
mProximityThreshold = (int) (mDefaultWidth * SEARCH_DISTANCE);
|
||||||
|
mProximityThreshold = mProximityThreshold * mProximityThreshold; // Square it for comparison
|
||||||
|
a.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int getDimensionOrFraction(TypedArray a, int index, int base, int defValue) {
|
||||||
|
TypedValue value = a.peekValue(index);
|
||||||
|
if (value == null) return defValue;
|
||||||
|
if (value.type == TypedValue.TYPE_DIMENSION) {
|
||||||
|
return a.getDimensionPixelOffset(index, defValue);
|
||||||
|
} else if (value.type == TypedValue.TYPE_FRACTION) {
|
||||||
|
// Round it to avoid values like 47.9999 from getting truncated
|
||||||
|
return Math.round(a.getFraction(index, base, base, defValue));
|
||||||
|
}
|
||||||
|
return defValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,137 @@
|
|||||||
|
package com.rainbow.app.keyboard.uiactivity
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.View
|
||||||
|
import androidx.activity.enableEdgeToEdge
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.view.ViewCompat
|
||||||
|
import androidx.core.view.WindowInsetsCompat
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import com.ad.tradpluslibrary.TPAdManager
|
||||||
|
import com.ad.tradpluslibrary.TPAdManager.showTPAD
|
||||||
|
import com.rainbow.app.keyboard.App
|
||||||
|
import com.rainbow.app.keyboard.R
|
||||||
|
import com.rainbow.app.keyboard.myadapter.KBItemAdapter
|
||||||
|
import com.rainbow.app.keyboard.javabean.DetailsjavaBean
|
||||||
|
import com.rainbow.app.keyboard.mycallback.DeleteCallback
|
||||||
|
import com.rainbow.app.keyboard.mydb.DatabBase
|
||||||
|
import com.rainbow.app.keyboard.mydb.DatabManager
|
||||||
|
import com.rainbow.app.keyboard.databinding.ActivityCollectionBinding
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class CollectionActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private lateinit var binding: ActivityCollectionBinding
|
||||||
|
private lateinit var adapter: KBItemAdapter
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
binding = ActivityCollectionBinding.inflate(layoutInflater)
|
||||||
|
this.enableEdgeToEdge()
|
||||||
|
TPAdManager.loadAllAd(this)
|
||||||
|
setContentView(binding.getRoot())
|
||||||
|
showTPAD(this) { }
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v: View, insets: WindowInsetsCompat ->
|
||||||
|
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||||
|
val topPadding = systemBars.top
|
||||||
|
v.setPadding(systemBars.left, topPadding, systemBars.right, systemBars.bottom)
|
||||||
|
insets
|
||||||
|
}
|
||||||
|
|
||||||
|
initViews()
|
||||||
|
initData()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initViews() {
|
||||||
|
binding.back.setOnClickListener {
|
||||||
|
showTPAD(this) {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initData() {
|
||||||
|
// 初始化适配器
|
||||||
|
adapter = KBItemAdapter(this).apply {
|
||||||
|
setShowFavoriteIcon(true)
|
||||||
|
applyCollectionConfig()
|
||||||
|
setRemoveCallback(object : DeleteCallback {
|
||||||
|
override fun OnRemoveLike(data: DetailsjavaBean) {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
DatabManager.removeLike(data)
|
||||||
|
// 从列表中移除该项
|
||||||
|
removeItemFromAdapter(data)
|
||||||
|
|
||||||
|
android.widget.Toast.makeText(
|
||||||
|
this@CollectionActivity,
|
||||||
|
"Removed from favorites",
|
||||||
|
android.widget.Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置RecyclerView
|
||||||
|
binding.likeRecycler.run {
|
||||||
|
adapter = this@CollectionActivity.adapter
|
||||||
|
layoutManager = GridLayoutManager(this@CollectionActivity, 2)
|
||||||
|
clipToPadding = false
|
||||||
|
setPadding(0, 0, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 观察收藏数据变化
|
||||||
|
DatabBase.databBase.ThemesDao().queryAllLike().observe(this) { favoriteList ->
|
||||||
|
Log.d(App.TAG, "Collection size: ${favoriteList?.size}")
|
||||||
|
|
||||||
|
if (favoriteList.isNullOrEmpty()) {
|
||||||
|
showEmptyState()
|
||||||
|
} else {
|
||||||
|
showCollectionList(favoriteList as List<DetailsjavaBean>)
|
||||||
|
updateFavoriteCountDisplay(favoriteList.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showEmptyState() {
|
||||||
|
binding.likeRecycler.visibility = View.GONE
|
||||||
|
binding.textFavoriteCountContainer.visibility = View.GONE
|
||||||
|
|
||||||
|
val emptyContainer = findViewById<View>(R.id.empty_container)
|
||||||
|
emptyContainer?.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showCollectionList(favoriteList: List<DetailsjavaBean>) {
|
||||||
|
binding.likeRecycler.visibility = View.VISIBLE
|
||||||
|
|
||||||
|
val emptyContainer = findViewById<View>(R.id.empty_container)
|
||||||
|
emptyContainer?.visibility = View.GONE
|
||||||
|
|
||||||
|
// 更新适配器数据
|
||||||
|
adapter.setData(favoriteList)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateFavoriteCountDisplay(count: Int) {
|
||||||
|
if (count > 0) {
|
||||||
|
binding.textFavoriteCountContainer.visibility = View.VISIBLE
|
||||||
|
val countText = when (count) {
|
||||||
|
1 -> "1 keyboard collected"
|
||||||
|
else -> "$count keyboards collected"
|
||||||
|
}
|
||||||
|
binding.textFavoriteCount.text = countText
|
||||||
|
} else {
|
||||||
|
binding.textFavoriteCountContainer.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从适配器中移除指定项
|
||||||
|
private fun removeItemFromAdapter(bean: DetailsjavaBean) {
|
||||||
|
val position = adapter.getPositionOfItem(bean)
|
||||||
|
if (position != -1) {
|
||||||
|
adapter.removeItem(position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,347 @@
|
|||||||
|
package com.rainbow.app.keyboard.uiactivity
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.Typeface
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.activity.enableEdgeToEdge
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.view.OnApplyWindowInsetsListener
|
||||||
|
import androidx.core.view.ViewCompat
|
||||||
|
import androidx.core.view.WindowInsetsCompat
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.ad.tradpluslibrary.TPAdManager
|
||||||
|
import com.ad.tradpluslibrary.TPAdManager.showTPAD
|
||||||
|
import com.rainbow.app.keyboard.App
|
||||||
|
import com.rainbow.app.keyboard.R
|
||||||
|
import com.rainbow.app.keyboard.javabean.DetailsjavaBean
|
||||||
|
import com.rainbow.app.keyboard.mycallback.KBClickCallback
|
||||||
|
import com.rainbow.app.keyboard.mycallback.EnablekbCallback
|
||||||
|
import com.rainbow.app.keyboard.mydb.DatabManager
|
||||||
|
import com.rainbow.app.keyboard.utils.UtilsCommon
|
||||||
|
import com.rainbow.app.keyboard.utils.UtilszipFile
|
||||||
|
import com.rainbow.app.keyboard.utils.UtilsSavecurrentkb
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
|
import com.bumptech.glide.integration.webp.decoder.WebpDrawable
|
||||||
|
import com.bumptech.glide.load.DataSource
|
||||||
|
import com.bumptech.glide.load.engine.GlideException
|
||||||
|
import com.bumptech.glide.request.RequestListener
|
||||||
|
import com.bumptech.glide.request.target.Target
|
||||||
|
import com.rainbow.app.keyboard.myadapter.KBItemAdapter
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class DetailkbActivity : AppCompatActivity() {
|
||||||
|
companion object {
|
||||||
|
@JvmField
|
||||||
|
var DISPLAY_URL_KEY: String = "display_url_key"
|
||||||
|
@JvmField
|
||||||
|
val ZIP_URL_KEY = "zip_url_key"
|
||||||
|
@JvmField
|
||||||
|
val NAME_KEY = "name_key"
|
||||||
|
@JvmField
|
||||||
|
val GIF_KEY = "gif_key"
|
||||||
|
@JvmField
|
||||||
|
val THUMB_KEY = "thumb_key"
|
||||||
|
@JvmField
|
||||||
|
val SOURCE_KEY = "data_key"
|
||||||
|
}
|
||||||
|
|
||||||
|
private var dialog: EnablekbDialog? = null
|
||||||
|
private lateinit var displayUrl: String
|
||||||
|
private lateinit var gifUrl: String
|
||||||
|
private lateinit var zipUrl: String
|
||||||
|
private lateinit var name: String
|
||||||
|
private lateinit var applyBtn: LinearLayout
|
||||||
|
private lateinit var imgData: ImageView
|
||||||
|
private lateinit var imgBack: ImageView
|
||||||
|
private lateinit var textName: TextView
|
||||||
|
private lateinit var btnScrollToTop: ImageView
|
||||||
|
private lateinit var recommendedRecycler: RecyclerView
|
||||||
|
private lateinit var nestedScrollView: androidx.core.widget.NestedScrollView
|
||||||
|
private lateinit var loadingLayout: FrameLayout
|
||||||
|
private lateinit var unzipPath: String
|
||||||
|
private lateinit var tvDownload: TextView
|
||||||
|
private lateinit var imDownload: ImageView
|
||||||
|
private lateinit var thumb: String
|
||||||
|
private lateinit var imgLike: ImageView
|
||||||
|
private lateinit var data: DetailsjavaBean
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_detailkb)
|
||||||
|
this.enableEdgeToEdge()
|
||||||
|
TPAdManager.loadAllAd(this)
|
||||||
|
showTPAD(this) { }
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(
|
||||||
|
findViewById(R.id.main),
|
||||||
|
OnApplyWindowInsetsListener { v: View, insets: WindowInsetsCompat ->
|
||||||
|
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||||
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
||||||
|
insets
|
||||||
|
})
|
||||||
|
|
||||||
|
findViewId()
|
||||||
|
getExtraData()
|
||||||
|
displayData()
|
||||||
|
setApply()
|
||||||
|
onClick()
|
||||||
|
setupScrollListener() // 添加滚动监听
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findViewId() {
|
||||||
|
applyBtn = findViewById(R.id.layoutDownloadApply)
|
||||||
|
imgData = findViewById(R.id.image_data)
|
||||||
|
imgBack = findViewById(R.id.back)
|
||||||
|
textName = findViewById(R.id.textview_data_name)
|
||||||
|
recommendedRecycler = findViewById(R.id.recommended_recycler)
|
||||||
|
nestedScrollView = findViewById(R.id.nested_scroll_view) // NestedScrollView
|
||||||
|
btnScrollToTop = findViewById(R.id.btn_scroll_to_top)
|
||||||
|
imgLike = findViewById(R.id.im_like)
|
||||||
|
loadingLayout = findViewById(R.id.loading)
|
||||||
|
imDownload = findViewById(R.id.im_download)
|
||||||
|
tvDownload = findViewById(R.id.tv_download)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getExtraData() {
|
||||||
|
data = intent.getSerializableExtra(SOURCE_KEY) as DetailsjavaBean
|
||||||
|
displayUrl = intent.getStringExtra(DISPLAY_URL_KEY).toString()
|
||||||
|
zipUrl = intent.getStringExtra(ZIP_URL_KEY).toString()
|
||||||
|
name = intent.getStringExtra(NAME_KEY).toString()
|
||||||
|
gifUrl = intent.getStringExtra(GIF_KEY).toString()
|
||||||
|
thumb = intent.getStringExtra(THUMB_KEY).toString()
|
||||||
|
|
||||||
|
val serviceZipName = UtilszipFile.getServiceZipName(zipUrl)
|
||||||
|
unzipPath = UtilszipFile.getUnzipPath(serviceZipName)
|
||||||
|
|
||||||
|
Log.d("KeyboardActivity", "unzipPath=$unzipPath")
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
DatabManager.getIsLike(name) {
|
||||||
|
imgLike.isSelected = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (File(unzipPath).exists()) {
|
||||||
|
imDownload.isVisible = false
|
||||||
|
tvDownload.text = getString(R.string.apply)
|
||||||
|
} else {
|
||||||
|
imDownload.isVisible = true
|
||||||
|
tvDownload.text = getString(R.string.download_apply)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun displayData() {
|
||||||
|
textName.text = name
|
||||||
|
textName.setTypeface(textName.typeface, Typeface.BOLD)
|
||||||
|
|
||||||
|
if (gifUrl.isNotEmpty()) {
|
||||||
|
loadImgGif()
|
||||||
|
} else {
|
||||||
|
Glide.with(this)
|
||||||
|
.load(displayUrl)
|
||||||
|
.thumbnail(Glide.with(this).load(thumb))
|
||||||
|
.into(imgData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onClick() {
|
||||||
|
imgBack.setOnClickListener {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
imgLike.setOnClickListener {
|
||||||
|
showTPAD(this) {
|
||||||
|
imgLike.isSelected = !imgLike.isSelected
|
||||||
|
lifecycleScope.launch {
|
||||||
|
if (imgLike.isSelected) {
|
||||||
|
DatabManager.addLike(data)
|
||||||
|
} else {
|
||||||
|
DatabManager.removeLike(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 回到顶部按钮点击事件
|
||||||
|
btnScrollToTop.setOnClickListener {
|
||||||
|
nestedScrollView.smoothScrollTo(0, 0)
|
||||||
|
btnScrollToTop.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
val forYouList = App.Companion.list.filter {
|
||||||
|
it.parentName == getString(R.string.recommend_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
recommendedRecycler.run {
|
||||||
|
adapter = KBItemAdapter(this@DetailkbActivity).apply {
|
||||||
|
val shuffled = forYouList[0].keyboardList.shuffled()
|
||||||
|
setData(shuffled)
|
||||||
|
applyRecoItemConfig() // 应用RecoItem配置
|
||||||
|
setItemClickListener(object : KBClickCallback {
|
||||||
|
override fun OnItemClickListener() {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
layoutManager = GridLayoutManager(this@DetailkbActivity, 2) // 保持2列网格
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 滚动监听器 ====================
|
||||||
|
private fun setupScrollListener() {
|
||||||
|
nestedScrollView.setOnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY ->
|
||||||
|
// 当滚动超过200像素时显示回到顶部按钮
|
||||||
|
if (scrollY > 200) {
|
||||||
|
btnScrollToTop.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
btnScrollToTop.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 也可以为RecyclerView添加监听(双重保险)
|
||||||
|
recommendedRecycler.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||||
|
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||||
|
super.onScrolled(recyclerView, dx, dy)
|
||||||
|
// 获取NestedScrollView的滚动位置
|
||||||
|
val scrollY = nestedScrollView.scrollY
|
||||||
|
if (scrollY > 200) {
|
||||||
|
btnScrollToTop.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
btnScrollToTop.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 初始检查一次
|
||||||
|
nestedScrollView.post {
|
||||||
|
val scrollY = nestedScrollView.scrollY
|
||||||
|
btnScrollToTop.visibility = if (scrollY > 200) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("CheckResult")
|
||||||
|
private fun loadImgGif() {
|
||||||
|
Glide.with(this)
|
||||||
|
.load(gifUrl)
|
||||||
|
.thumbnail(Glide.with(this).load(thumb))
|
||||||
|
.addListener(object : RequestListener<Drawable> {
|
||||||
|
override fun onLoadFailed(
|
||||||
|
e: GlideException?,
|
||||||
|
model: Any?,
|
||||||
|
target: Target<Drawable>,
|
||||||
|
isFirstResource: Boolean
|
||||||
|
): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResourceReady(
|
||||||
|
resource: Drawable,
|
||||||
|
model: Any,
|
||||||
|
target: Target<Drawable>?,
|
||||||
|
dataSource: DataSource,
|
||||||
|
isFirstResource: Boolean
|
||||||
|
): Boolean {
|
||||||
|
if (resource is WebpDrawable) {
|
||||||
|
resource.loopCount = WebpDrawable.LOOP_FOREVER
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}).into(imgData)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setApply() {
|
||||||
|
applyBtn.setOnClickListener {
|
||||||
|
val checkEnable = UtilsCommon.checkEnable(this)
|
||||||
|
val checkSetDefault = UtilsCommon.checkSetDefault(this)
|
||||||
|
if (!checkEnable || !checkSetDefault) {
|
||||||
|
showDialog()
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
startDown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showDialog() {
|
||||||
|
dialog = dialog ?: EnablekbDialog.newInstance()
|
||||||
|
dialog?.setClickListener {
|
||||||
|
startDown()
|
||||||
|
}
|
||||||
|
dialog?.show(supportFragmentManager, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startDown() {
|
||||||
|
showTPAD(this) {
|
||||||
|
loadingLayout.isVisible = true
|
||||||
|
applyBtn.isEnabled = false
|
||||||
|
|
||||||
|
val file = File(unzipPath)
|
||||||
|
if (file.exists()) {
|
||||||
|
val findFirstDirectory = UtilszipFile.findFirstDirectory(file)
|
||||||
|
apply("${findFirstDirectory}/")
|
||||||
|
applyBtn.isEnabled = true
|
||||||
|
loadingLayout.isVisible = false
|
||||||
|
} else {
|
||||||
|
UtilszipFile.startDownloadZip(zipUrl, object : EnablekbCallback {
|
||||||
|
override fun OnApplySkinListener(fileList: List<File?>?) {
|
||||||
|
runOnUiThread {
|
||||||
|
applyBtn.isEnabled = true
|
||||||
|
loadingLayout.isVisible = false
|
||||||
|
}
|
||||||
|
if (fileList.isNullOrEmpty()) {
|
||||||
|
runOnUiThread {
|
||||||
|
Toast.makeText(
|
||||||
|
this@DetailkbActivity,
|
||||||
|
getString(R.string.download_fail),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (file.exists()) {
|
||||||
|
val findFirstDirectory = UtilszipFile.findFirstDirectory(file)
|
||||||
|
Log.d(
|
||||||
|
App.Companion.TAG,
|
||||||
|
"----apply------------it=$findFirstDirectory"
|
||||||
|
)
|
||||||
|
runOnUiThread {
|
||||||
|
apply("${findFirstDirectory}/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun apply(path: String) {
|
||||||
|
var skinParentPath = path
|
||||||
|
if (path.contains("res")) {
|
||||||
|
skinParentPath = path.substringBeforeLast("res")
|
||||||
|
}
|
||||||
|
|
||||||
|
UtilsSavecurrentkb.updateSkinPath(skinParentPath)
|
||||||
|
Toast.makeText(
|
||||||
|
this@DetailkbActivity,
|
||||||
|
getString(R.string.theme_application_successful),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
startActivity(Intent(this, EditVActivity::class.java).apply {
|
||||||
|
putExtra(EditVActivity.key_name, name)
|
||||||
|
})
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,201 @@
|
|||||||
|
package com.rainbow.app.keyboard.uiactivity;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.ViewTreeObserver;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
|
||||||
|
import androidx.activity.EdgeToEdge;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
|
||||||
|
import com.ad.tradpluslibrary.TPAdManager;
|
||||||
|
import com.rainbow.app.keyboard.R;
|
||||||
|
import com.rainbow.app.keyboard.databinding.ActivityEditvBinding;
|
||||||
|
import com.rainbow.app.keyboard.utils.UtilsKeyname;
|
||||||
|
import com.rainbow.app.keyboard.utils.UtilsCommon;
|
||||||
|
import com.rainbow.app.keyboard.utils.UtilsFastblur;
|
||||||
|
import com.rainbow.app.keyboard.utils.UtilsSavecurrentkb;
|
||||||
|
|
||||||
|
|
||||||
|
public class EditVActivity extends AppCompatActivity {
|
||||||
|
private ActivityEditvBinding vb;
|
||||||
|
public static String key_name = "key_name";
|
||||||
|
private int mPreviousKeyboardHeight = -1;
|
||||||
|
private Drawable blurBackground;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
vb = ActivityEditvBinding.inflate(getLayoutInflater());
|
||||||
|
EdgeToEdge.enable(this);
|
||||||
|
setContentView(vb.getRoot());
|
||||||
|
TPAdManager.INSTANCE.loadAllAd(this);
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
|
||||||
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
|
return insets;
|
||||||
|
});
|
||||||
|
|
||||||
|
onInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
// 清理资源
|
||||||
|
blurBackground = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onInit() {
|
||||||
|
String stringExtra = getIntent().getStringExtra(key_name);
|
||||||
|
vb.title.setText(stringExtra);
|
||||||
|
vb.title.setTypeface(vb.title.getTypeface(), Typeface.BOLD);
|
||||||
|
String curPath = UtilsSavecurrentkb.INSTANCE.getSkinPath();
|
||||||
|
|
||||||
|
vb.idBack.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (curPath == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String bgPath = curPath + "res/drawable-xxhdpi-v4/" + UtilsKeyname.previewBg;
|
||||||
|
|
||||||
|
Drawable bgDraw = UtilsCommon.INSTANCE.getBgDrawable(this, bgPath);
|
||||||
|
if (bgDraw != null) {
|
||||||
|
// 使用FastBlur进行模糊处理
|
||||||
|
applyBlurBackground(bgDraw);
|
||||||
|
}
|
||||||
|
|
||||||
|
keyboardheight();
|
||||||
|
setupEditText();
|
||||||
|
vb.et.requestFocus();
|
||||||
|
|
||||||
|
// 针对不同 API 设置不同的软键盘模式
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
// API 30+
|
||||||
|
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
|
||||||
|
} else {
|
||||||
|
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 延迟显示键盘
|
||||||
|
showKeyboardWithDelay();
|
||||||
|
}
|
||||||
|
private void setupEditText() {
|
||||||
|
// 设置焦点监听
|
||||||
|
vb.et.setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onFocusChange(View v, boolean hasFocus) {
|
||||||
|
if (hasFocus) {
|
||||||
|
showKeyboard();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 设置点击监听
|
||||||
|
vb.et.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
showKeyboard();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showKeyboard() {
|
||||||
|
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
if (imm != null) {
|
||||||
|
imm.showSoftInput(vb.et, InputMethodManager.SHOW_IMPLICIT);
|
||||||
|
new Handler().postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (!isKeyboardVisible()) {
|
||||||
|
// 方法2:尝试强制显示
|
||||||
|
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showKeyboardWithDelay() {
|
||||||
|
new Handler().postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (!isKeyboardVisible()) {
|
||||||
|
showKeyboard();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isKeyboardVisible() {
|
||||||
|
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
return imm != null && imm.isActive(vb.et);
|
||||||
|
}
|
||||||
|
private void applyBlurBackground(Drawable originalDrawable) {
|
||||||
|
// 在子线程中进行模糊处理,避免阻塞UI
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
// 使用FastBlur进行模糊
|
||||||
|
Drawable blurredDrawable = UtilsFastblur.blurDrawable(originalDrawable, 15, this);
|
||||||
|
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
if (blurredDrawable != null) {
|
||||||
|
blurBackground = blurredDrawable;
|
||||||
|
vb.main.setBackground(blurredDrawable);
|
||||||
|
} else {
|
||||||
|
// 如果模糊失败,使用原始背景
|
||||||
|
vb.main.setBackground(originalDrawable);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
// 异常情况下使用原始背景
|
||||||
|
vb.main.setBackground(originalDrawable);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void keyboardheight() {
|
||||||
|
final View rootView = getWindow().getDecorView();
|
||||||
|
rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||||
|
@Override
|
||||||
|
public void onGlobalLayout() {
|
||||||
|
Rect r = new Rect();
|
||||||
|
rootView.getWindowVisibleDisplayFrame(r);
|
||||||
|
int screenHeight = rootView.getRootView().getHeight();
|
||||||
|
int keypadHeight = screenHeight - r.bottom;
|
||||||
|
|
||||||
|
if (keypadHeight != mPreviousKeyboardHeight) {
|
||||||
|
if (mPreviousKeyboardHeight < keypadHeight) {
|
||||||
|
mPreviousKeyboardHeight = keypadHeight;
|
||||||
|
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) vb.et.getLayoutParams();
|
||||||
|
params.bottomMargin = mPreviousKeyboardHeight;
|
||||||
|
vb.et.setLayoutParams(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,180 @@
|
|||||||
|
package com.rainbow.app.keyboard.uiactivity
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.drawable.ColorDrawable
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.provider.Settings
|
||||||
|
import android.view.Gravity
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.WindowManager
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import com.rainbow.app.keyboard.App
|
||||||
|
import com.rainbow.app.keyboard.R
|
||||||
|
import com.rainbow.app.keyboard.databinding.DialogEnablekbBinding
|
||||||
|
import com.rainbow.app.keyboard.utils.UtilsCommon
|
||||||
|
|
||||||
|
|
||||||
|
class EnablekbDialog : DialogFragment() {
|
||||||
|
|
||||||
|
private lateinit var vb: DialogEnablekbBinding
|
||||||
|
|
||||||
|
private lateinit var layoutStepOne: LinearLayout
|
||||||
|
private lateinit var layoutStepTwo: LinearLayout
|
||||||
|
private lateinit var imgStepOkOne: ImageView
|
||||||
|
private lateinit var imgStepOkTwo: ImageView
|
||||||
|
private lateinit var intentFilter: IntentFilter
|
||||||
|
private var myreceiver: BroadcastReceiver? = null
|
||||||
|
|
||||||
|
private lateinit var stepOne: TextView
|
||||||
|
private lateinit var stepTwo: TextView
|
||||||
|
|
||||||
|
private lateinit var context: Context
|
||||||
|
|
||||||
|
private var clickAction: (() -> Unit )? = null
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun newInstance(): EnablekbDialog {
|
||||||
|
val fragment = EnablekbDialog()
|
||||||
|
return fragment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun setClickListener(action:() -> Unit){
|
||||||
|
clickAction = action
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
vb = DialogEnablekbBinding.inflate(layoutInflater)
|
||||||
|
context = App.Companion.appInstance
|
||||||
|
|
||||||
|
|
||||||
|
findViewId()
|
||||||
|
onViewStep()
|
||||||
|
getReceiver()
|
||||||
|
return vb.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
super.onStart()
|
||||||
|
dialog?.run {
|
||||||
|
setCanceledOnTouchOutside(true)
|
||||||
|
window?.run {
|
||||||
|
setGravity(Gravity.BOTTOM)
|
||||||
|
setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||||
|
|
||||||
|
attributes = attributes.apply {
|
||||||
|
width = WindowManager.LayoutParams.MATCH_PARENT
|
||||||
|
height = WindowManager.LayoutParams.WRAP_CONTENT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findViewId() {
|
||||||
|
|
||||||
|
layoutStepOne = vb.linearStepOne
|
||||||
|
layoutStepTwo = vb.linearStepTwo
|
||||||
|
imgStepOkOne = vb.okOne
|
||||||
|
imgStepOkTwo = vb.okTwo
|
||||||
|
stepOne = vb.textStepOne
|
||||||
|
stepTwo = vb.textStepTwo
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onViewStep() {
|
||||||
|
|
||||||
|
layoutStepOne.setOnClickListener {
|
||||||
|
startActivity(Intent(Settings.ACTION_INPUT_METHOD_SETTINGS))
|
||||||
|
}
|
||||||
|
layoutStepTwo.setOnClickListener {
|
||||||
|
val inputMethodManager =
|
||||||
|
context.getSystemService(AppCompatActivity.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
inputMethodManager.showInputMethodPicker()
|
||||||
|
}
|
||||||
|
vb.imClose.setOnClickListener {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
updateUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getReceiver() {
|
||||||
|
myreceiver = object : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
|
updateUI()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intentFilter = IntentFilter(Intent.ACTION_INPUT_METHOD_CHANGED)
|
||||||
|
|
||||||
|
context.registerReceiver(myreceiver, intentFilter)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateUI() {
|
||||||
|
|
||||||
|
val checkEnable = UtilsCommon.checkEnable(App.Companion.appInstance)
|
||||||
|
val checkSetDefault = UtilsCommon.checkSetDefault(App.Companion.appInstance)
|
||||||
|
if (checkEnable && checkSetDefault) {
|
||||||
|
clickAction?.invoke()
|
||||||
|
dismiss()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (checkEnable) {
|
||||||
|
layoutStepOne.isEnabled = false
|
||||||
|
layoutStepOne.isSelected = true
|
||||||
|
imgStepOkOne.isVisible = true
|
||||||
|
stepOne.setTextColor(context.getColor(R.color.step_true))
|
||||||
|
} else {
|
||||||
|
layoutStepOne.isEnabled = true
|
||||||
|
layoutStepOne.isSelected = false
|
||||||
|
imgStepOkOne.isVisible = false
|
||||||
|
stepOne.setTextColor(context.getColor(R.color.white))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkSetDefault) {
|
||||||
|
layoutStepTwo.isEnabled = false
|
||||||
|
layoutStepTwo.isSelected = true
|
||||||
|
imgStepOkTwo.isVisible = true
|
||||||
|
stepTwo.setTextColor(context.getColor(R.color.step_true))
|
||||||
|
} else {
|
||||||
|
layoutStepTwo.isEnabled = true
|
||||||
|
layoutStepTwo.isSelected = false
|
||||||
|
imgStepOkTwo.isVisible = false
|
||||||
|
stepTwo.setTextColor(context.getColor(R.color.white))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
if (myreceiver != null) {
|
||||||
|
context.unregisterReceiver(myreceiver)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,129 @@
|
|||||||
|
package com.rainbow.app.keyboard.uiactivity
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.CountDownTimer
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import android.widget.ProgressBar
|
||||||
|
import android.widget.TextView
|
||||||
|
import com.ad.tradpluslibrary.TPAdManager
|
||||||
|
import com.rainbow.app.keyboard.App
|
||||||
|
import com.rainbow.app.keyboard.R
|
||||||
|
import com.rainbow.app.keyboard.utils.UtilsCommon
|
||||||
|
|
||||||
|
class LaunchActivity : Activity() {
|
||||||
|
|
||||||
|
private lateinit var progressBar: ProgressBar
|
||||||
|
private lateinit var progressPercentText: TextView
|
||||||
|
private lateinit var versionText: TextView
|
||||||
|
|
||||||
|
private var totalTime = 15000L // 总时间15秒
|
||||||
|
private lateinit var timer: CountDownTimer
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_launch)
|
||||||
|
UtilsCommon.initFullScreen(this, true,true)
|
||||||
|
|
||||||
|
initViews()
|
||||||
|
setVersionInfo()
|
||||||
|
initProgressTimer()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initViews() {
|
||||||
|
progressBar = findViewById(R.id.progressbar)
|
||||||
|
progressPercentText = findViewById(R.id.textview_progress_percent)
|
||||||
|
versionText = findViewById(R.id.textview_version)
|
||||||
|
|
||||||
|
// 确保进度条从0开始
|
||||||
|
progressBar.max = 100
|
||||||
|
progressBar.progress = 0
|
||||||
|
// 清除二级进度
|
||||||
|
progressBar.secondaryProgress = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setVersionInfo() {
|
||||||
|
try {
|
||||||
|
val packageInfo = packageManager.getPackageInfo(packageName, 0)
|
||||||
|
val versionName = packageInfo.versionName
|
||||||
|
versionText.text = "Version $versionName"
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
versionText.text = "Version 1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initProgressTimer() {
|
||||||
|
TPAdManager.init(
|
||||||
|
this,
|
||||||
|
App.TAG,
|
||||||
|
"D385CBF21CC0AAEC52780FA0231CAA11",
|
||||||
|
"81263152D7C8427BEAC81DCD943AA312",
|
||||||
|
"429D6B55BF3D4B279B08C3F631EBC012",
|
||||||
|
"647276D17478D3867FD2223AEAB4F112"
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
timer = TPAdManager.showWelcomeAd(this, totalTime, { millisUntilFinished ->
|
||||||
|
//倒计时更新
|
||||||
|
val progressPercentage = (100 * millisUntilFinished) / totalTime
|
||||||
|
val percentage = 100 - progressPercentage
|
||||||
|
progressBar.progress = percentage.toInt()
|
||||||
|
progressPercentText.text = "$percentage%"
|
||||||
|
}) {
|
||||||
|
//跳转首页
|
||||||
|
// 完成时设置进度为100%
|
||||||
|
progressBar.progress = 100
|
||||||
|
progressPercentText.text = "100%"
|
||||||
|
|
||||||
|
// 添加一个短暂的延迟,让用户看到100%的完成状态
|
||||||
|
Handler(Looper.getMainLooper()).postDelayed({
|
||||||
|
toHome()
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
timer.start()
|
||||||
|
//
|
||||||
|
// timer = object : CountDownTimer(totalTime, 100) {
|
||||||
|
// override fun onTick(millisUntilFinished: Long) {
|
||||||
|
// // 计算进度百分比(递增逻辑)
|
||||||
|
// val progressPercentage = (100 * millisUntilFinished / totalTime).toInt()
|
||||||
|
// val countdownPercentage = 100 - progressPercentage
|
||||||
|
//
|
||||||
|
// // 确保百分比在有效范围内
|
||||||
|
// val safePercentage = countdownPercentage.coerceIn(0, 100)
|
||||||
|
//
|
||||||
|
// // 更新进度条和百分比显示
|
||||||
|
// progressBar.progress = safePercentage
|
||||||
|
// progressPercentText.text = "$safePercentage%"
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// override fun onFinish() {
|
||||||
|
// // 完成时设置进度为100%
|
||||||
|
// progressBar.progress = 100
|
||||||
|
// progressPercentText.text = "100%"
|
||||||
|
//
|
||||||
|
// // 添加一个短暂的延迟,让用户看到100%的完成状态
|
||||||
|
// Handler(Looper.getMainLooper()).postDelayed({
|
||||||
|
// toHome()
|
||||||
|
// }, 0)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 启动计时器
|
||||||
|
// timer.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toHome() {
|
||||||
|
startActivity(Intent(this, MainActivity::class.java))
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
if (::timer.isInitialized) {
|
||||||
|
timer.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,232 @@
|
|||||||
|
package com.rainbow.app.keyboard.uiactivity;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewTreeObserver;
|
||||||
|
|
||||||
|
import androidx.activity.EdgeToEdge;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.rainbow.app.keyboard.App;
|
||||||
|
import com.rainbow.app.keyboard.R;
|
||||||
|
import com.rainbow.app.keyboard.javabean.DetailsjavaBean;
|
||||||
|
import com.rainbow.app.keyboard.javabean.WrapperjavaBean;
|
||||||
|
import com.rainbow.app.keyboard.myadapter.KBItemAdapter;
|
||||||
|
import com.rainbow.app.keyboard.myadapter.NavCategoryAdapter;
|
||||||
|
import com.rainbow.app.keyboard.databinding.ActivityMainBinding;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private ActivityMainBinding binding;
|
||||||
|
private List<WrapperjavaBean> categoryList;
|
||||||
|
private KBItemAdapter keyboardAdapter;
|
||||||
|
private NavCategoryAdapter navAdapter;
|
||||||
|
private List<DetailsjavaBean> firstCategoryKeyboards;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
binding = ActivityMainBinding.inflate(getLayoutInflater());
|
||||||
|
EdgeToEdge.enable(this);
|
||||||
|
setContentView(binding.getRoot());
|
||||||
|
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
|
||||||
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
|
return insets;
|
||||||
|
});
|
||||||
|
|
||||||
|
initViews();
|
||||||
|
initData();
|
||||||
|
setupNavigationSidebar();
|
||||||
|
setupClickListeners();
|
||||||
|
setupScrollListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initViews() {
|
||||||
|
// 设置应用名称字体
|
||||||
|
binding.appName.setTypeface(Typeface.create(binding.appName.getTypeface(), Typeface.BOLD));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initData() {
|
||||||
|
// 获取所有分类
|
||||||
|
categoryList = new ArrayList<>(App.list);
|
||||||
|
|
||||||
|
// 初始化成员变量,而不是局部变量
|
||||||
|
firstCategoryKeyboards = new ArrayList<>();
|
||||||
|
if (!categoryList.isEmpty()) {
|
||||||
|
firstCategoryKeyboards = categoryList.get(0).getKeyboardList();
|
||||||
|
}
|
||||||
|
|
||||||
|
keyboardAdapter = new KBItemAdapter(this);
|
||||||
|
keyboardAdapter.setSpanCount(2); // 每行显示两个
|
||||||
|
|
||||||
|
// 先应用配置(使用默认值)
|
||||||
|
keyboardAdapter.applyMainActivityConfig();
|
||||||
|
|
||||||
|
// 设置RecyclerView(键盘壁纸网格)
|
||||||
|
GridLayoutManager layoutManager = new GridLayoutManager(this, 2);
|
||||||
|
binding.recyclerview.setLayoutManager(layoutManager);
|
||||||
|
binding.recyclerview.setAdapter(keyboardAdapter);
|
||||||
|
binding.recyclerview.setClipToPadding(false);
|
||||||
|
binding.recyclerview.setPadding(0, 0, 0, 0);
|
||||||
|
|
||||||
|
// 使用ViewTreeObserver确保在布局完成后设置宽度
|
||||||
|
binding.recyclerview.getViewTreeObserver().addOnGlobalLayoutListener(
|
||||||
|
new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||||
|
@Override
|
||||||
|
public void onGlobalLayout() {
|
||||||
|
// 移除监听器,避免重复执行
|
||||||
|
binding.recyclerview.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||||
|
|
||||||
|
int recyclerViewWidth = binding.recyclerview.getWidth();
|
||||||
|
if (recyclerViewWidth > 0) {
|
||||||
|
// 设置容器宽度
|
||||||
|
keyboardAdapter.setContainerWidth(recyclerViewWidth);
|
||||||
|
|
||||||
|
// 现在设置数据
|
||||||
|
keyboardAdapter.setData(firstCategoryKeyboards);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupNavigationSidebar() {
|
||||||
|
// 初始化侧边栏分类适配器
|
||||||
|
navAdapter = new NavCategoryAdapter(this, categoryList);
|
||||||
|
navAdapter.setCategoryClickListener(new NavCategoryAdapter.CategoryClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onCategoryClick(String categoryName, List<DetailsjavaBean> keyboards) {
|
||||||
|
// 更新主内容区域显示选中的分类
|
||||||
|
updateMainContent(keyboards);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 设置侧边栏RecyclerView
|
||||||
|
LinearLayoutManager navLayoutManager = new LinearLayoutManager(this);
|
||||||
|
binding.navRecyclerview.setLayoutManager(navLayoutManager);
|
||||||
|
binding.navRecyclerview.setAdapter(navAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateMainContent(List<DetailsjavaBean> keyboards) {
|
||||||
|
firstCategoryKeyboards = keyboards;
|
||||||
|
|
||||||
|
// 更新键盘壁纸列表
|
||||||
|
keyboardAdapter.setData(keyboards);
|
||||||
|
keyboardAdapter.notifyDataSetChanged();
|
||||||
|
|
||||||
|
// 滚动到顶部
|
||||||
|
binding.recyclerview.smoothScrollToPosition(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupClickListeners() {
|
||||||
|
// 应用图标点击事件 - 跳转到SettingDialog(保持原样)
|
||||||
|
binding.appIcon.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
openSettingDialog();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 收藏图标点击事件 - 跳转到CollectionActivity
|
||||||
|
binding.collectionIcon.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
openCollectionActivity();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 回到顶部按钮点击事件
|
||||||
|
binding.btnScrollToTop.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
scrollToTop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openSettingDialog() {
|
||||||
|
try {
|
||||||
|
Intent intent = new Intent(this, SettingsActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openCollectionActivity() {
|
||||||
|
try {
|
||||||
|
Intent intent = new Intent(this, CollectionActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupScrollListener() {
|
||||||
|
binding.recyclerview.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||||
|
@Override
|
||||||
|
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
|
||||||
|
super.onScrolled(recyclerView, dx, dy);
|
||||||
|
|
||||||
|
boolean canScrollUp = recyclerView.canScrollVertically(-1);
|
||||||
|
binding.btnScrollToTop.setVisibility(canScrollUp ? View.VISIBLE : View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
|
||||||
|
super.onScrollStateChanged(recyclerView, newState);
|
||||||
|
|
||||||
|
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
|
||||||
|
boolean canScrollUp = recyclerView.canScrollVertically(-1);
|
||||||
|
binding.btnScrollToTop.setVisibility(canScrollUp ? View.VISIBLE : View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
binding.recyclerview.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
boolean canScrollUp = binding.recyclerview.canScrollVertically(-1);
|
||||||
|
binding.btnScrollToTop.setVisibility(canScrollUp ? View.VISIBLE : View.GONE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scrollToTop() {
|
||||||
|
if (binding.recyclerview.getLayoutManager() != null) {
|
||||||
|
binding.recyclerview.smoothScrollToPosition(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// dp转px的辅助方法
|
||||||
|
private int dpToPx(int dp) {
|
||||||
|
return (int) (dp * getResources().getDisplayMetrics().density);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
binding = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
// 从CollectionActivity返回时,可以在这里更新数据(如果需要)
|
||||||
|
// 例如刷新收藏状态等
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,264 @@
|
|||||||
|
package com.rainbow.app.keyboard.uiactivity;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.graphics.drawable.GradientDrawable;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
|
import com.ad.tradpluslibrary.TPAdManager;
|
||||||
|
import com.rainbow.app.keyboard.R;
|
||||||
|
import com.google.android.material.button.MaterialButton;
|
||||||
|
|
||||||
|
public class SettingsActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private int currentRating = 0;
|
||||||
|
private AlertDialog ratingDialog;
|
||||||
|
|
||||||
|
@SuppressLint("MissingInflatedId")
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||||
|
setContentView(R.layout.activity_settings);
|
||||||
|
setupDialogWindow();
|
||||||
|
TPAdManager.INSTANCE.loadAllAd(this);
|
||||||
|
setupViews();
|
||||||
|
setVersionNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupDialogWindow() {
|
||||||
|
Window window = getWindow();
|
||||||
|
if (window != null) {
|
||||||
|
// 设置窗口宽度和高度
|
||||||
|
WindowManager.LayoutParams params = window.getAttributes();
|
||||||
|
params.width = WindowManager.LayoutParams.MATCH_PARENT;
|
||||||
|
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
|
||||||
|
params.dimAmount = 0.5f; // 背景暗色程度
|
||||||
|
window.setAttributes(params);
|
||||||
|
|
||||||
|
// 创建圆角背景
|
||||||
|
GradientDrawable background = new GradientDrawable();
|
||||||
|
background.setColor(getResources().getColor(R.color.theme_background));
|
||||||
|
background.setCornerRadius(dpToPx(20));
|
||||||
|
|
||||||
|
// 设置背景
|
||||||
|
window.setBackgroundDrawable(background);
|
||||||
|
|
||||||
|
// 确保没有标题栏
|
||||||
|
if (getSupportActionBar() != null) {
|
||||||
|
getSupportActionBar().hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int dpToPx(int dp) {
|
||||||
|
float density = getResources().getDisplayMetrics().density;
|
||||||
|
return Math.round(dp * density);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupViews() {
|
||||||
|
// 关闭按钮
|
||||||
|
ImageView closeButton = findViewById(R.id.close_button);
|
||||||
|
closeButton.setOnClickListener(v -> finish());
|
||||||
|
|
||||||
|
// 评分区域
|
||||||
|
View rateUsLayout = findViewById(R.id.rateUsLayout);
|
||||||
|
rateUsLayout.setOnClickListener(v -> showRatingDialog());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setVersionNumber() {
|
||||||
|
try {
|
||||||
|
PackageInfo info = getPackageManager()
|
||||||
|
.getPackageInfo(getPackageName(), 0);
|
||||||
|
String version = info.versionName;
|
||||||
|
TextView versionValue = findViewById(R.id.versionValue);
|
||||||
|
versionValue.setText(version);
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
TextView versionValue = findViewById(R.id.versionValue);
|
||||||
|
versionValue.setText("1.0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showRatingDialog() {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
|
|
||||||
|
android.view.ContextThemeWrapper themedContext =
|
||||||
|
new android.view.ContextThemeWrapper(this, R.style.Theme_PaintingHelper);
|
||||||
|
|
||||||
|
View dialogView;
|
||||||
|
try {
|
||||||
|
LayoutInflater inflater = LayoutInflater.from(themedContext);
|
||||||
|
dialogView = inflater.inflate(R.layout.dialog_feedback, null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Toast.makeText(this, "Error loading rating dialog", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找所有视图
|
||||||
|
ImageView star1 = dialogView.findViewById(R.id.star1);
|
||||||
|
ImageView star2 = dialogView.findViewById(R.id.star2);
|
||||||
|
ImageView star3 = dialogView.findViewById(R.id.star3);
|
||||||
|
ImageView star4 = dialogView.findViewById(R.id.star4);
|
||||||
|
ImageView star5 = dialogView.findViewById(R.id.star5);
|
||||||
|
|
||||||
|
// 使用MaterialButton类型
|
||||||
|
MaterialButton btnCancel = dialogView.findViewById(R.id.btn_cancel);
|
||||||
|
MaterialButton btnRate = dialogView.findViewById(R.id.btn_rate);
|
||||||
|
|
||||||
|
currentRating = 0;
|
||||||
|
if (btnRate != null) {
|
||||||
|
btnRate.setEnabled(false);
|
||||||
|
updateRateButtonState(btnRate, currentRating);
|
||||||
|
}
|
||||||
|
|
||||||
|
View.OnClickListener starClickListener = v -> {
|
||||||
|
int rating = 0;
|
||||||
|
int viewId = v.getId();
|
||||||
|
|
||||||
|
if (viewId == R.id.star1) rating = 1;
|
||||||
|
else if (viewId == R.id.star2) rating = 2;
|
||||||
|
else if (viewId == R.id.star3) rating = 3;
|
||||||
|
else if (viewId == R.id.star4) rating = 4;
|
||||||
|
else if (viewId == R.id.star5) rating = 5;
|
||||||
|
|
||||||
|
updateStarRating(star1, star2, star3, star4, star5, rating);
|
||||||
|
currentRating = rating;
|
||||||
|
|
||||||
|
if (btnRate != null) {
|
||||||
|
btnRate.setEnabled(rating > 0);
|
||||||
|
updateRateButtonState(btnRate, rating);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (star1 != null) star1.setOnClickListener(starClickListener);
|
||||||
|
if (star2 != null) star2.setOnClickListener(starClickListener);
|
||||||
|
if (star3 != null) star3.setOnClickListener(starClickListener);
|
||||||
|
if (star4 != null) star4.setOnClickListener(starClickListener);
|
||||||
|
if (star5 != null) star5.setOnClickListener(starClickListener);
|
||||||
|
|
||||||
|
ratingDialog = builder.setView(dialogView)
|
||||||
|
.setCancelable(true)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
// 设置Dialog窗口属性
|
||||||
|
Window ratingWindow = ratingDialog.getWindow();
|
||||||
|
if (ratingWindow != null) {
|
||||||
|
ratingWindow.setBackgroundDrawableResource(android.R.color.transparent);
|
||||||
|
ratingWindow.setDimAmount(0.6f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (btnCancel != null) {
|
||||||
|
btnCancel.setOnClickListener(v -> {
|
||||||
|
if (ratingDialog != null && ratingDialog.isShowing()) {
|
||||||
|
ratingDialog.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (btnRate != null) {
|
||||||
|
btnRate.setOnClickListener(v -> {
|
||||||
|
if (currentRating > 0) {
|
||||||
|
handleRatingSubmission(currentRating);
|
||||||
|
if (ratingDialog != null && ratingDialog.isShowing()) {
|
||||||
|
ratingDialog.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ratingDialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateStarRating(ImageView star1, ImageView star2, ImageView star3,
|
||||||
|
ImageView star4, ImageView star5, int rating) {
|
||||||
|
if (star1 == null || star2 == null || star3 == null || star4 == null || star5 == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int starFilledRes = R.mipmap.ic_star;
|
||||||
|
int starOutlineRes = R.mipmap.ic_star_outline;
|
||||||
|
|
||||||
|
star1.setImageResource(rating >= 1 ? starFilledRes : starOutlineRes);
|
||||||
|
star2.setImageResource(rating >= 2 ? starFilledRes : starOutlineRes);
|
||||||
|
star3.setImageResource(rating >= 3 ? starFilledRes : starOutlineRes);
|
||||||
|
star4.setImageResource(rating >= 4 ? starFilledRes : starOutlineRes);
|
||||||
|
star5.setImageResource(rating >= 5 ? starFilledRes : starOutlineRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateRateButtonState(MaterialButton rateButton, int rating) {
|
||||||
|
if (rateButton == null) return;
|
||||||
|
|
||||||
|
if (rating > 0) {
|
||||||
|
rateButton.setEnabled(true);
|
||||||
|
try {
|
||||||
|
rateButton.setBackgroundResource(R.drawable.bg_rateit);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 使用默认背景
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rateButton.setEnabled(false);
|
||||||
|
try {
|
||||||
|
rateButton.setBackgroundResource(R.drawable.bg_rateit_outline);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 使用默认背景
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleRatingSubmission(int rating) {
|
||||||
|
String message;
|
||||||
|
if (rating >= 4) {
|
||||||
|
message = "Thank you for your " + rating + " star rating!";
|
||||||
|
openPlayStoreForReview();
|
||||||
|
} else {
|
||||||
|
message = "Thanks for your " + rating + " star feedback!";
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openPlayStoreForReview() {
|
||||||
|
try {
|
||||||
|
String packageName = getPackageName();
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW,
|
||||||
|
Uri.parse("market://details?id=" + packageName));
|
||||||
|
startActivity(intent);
|
||||||
|
} catch (android.content.ActivityNotFoundException e) {
|
||||||
|
String packageName = getPackageName();
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW,
|
||||||
|
Uri.parse("https://play.google.com/store/apps/details?id=" + packageName));
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finish() {
|
||||||
|
super.finish();
|
||||||
|
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
if (ratingDialog != null && ratingDialog.isShowing()) {
|
||||||
|
ratingDialog.dismiss();
|
||||||
|
}
|
||||||
|
ratingDialog = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
153
app/src/main/java/com/rainbow/app/keyboard/utils/UtilsCommon.kt
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
package com.rainbow.app.keyboard.utils
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.drawable.BitmapDrawable
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.os.Build
|
||||||
|
import android.provider.Settings
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.View
|
||||||
|
import android.view.WindowManager
|
||||||
|
import android.view.inputmethod.EditorInfo
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
import android.widget.ImageView
|
||||||
|
import com.rainbow.app.keyboard.App
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
|
import com.bumptech.glide.integration.webp.decoder.WebpDrawable
|
||||||
|
import com.bumptech.glide.load.DataSource
|
||||||
|
import com.bumptech.glide.load.engine.GlideException
|
||||||
|
import com.bumptech.glide.load.resource.bitmap.CenterCrop
|
||||||
|
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||||
|
import com.bumptech.glide.request.RequestListener
|
||||||
|
import com.bumptech.glide.request.RequestOptions
|
||||||
|
import com.bumptech.glide.request.target.Target
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
object UtilsCommon {
|
||||||
|
|
||||||
|
val transform = RequestOptions().transform(CenterCrop(), RoundedCorners(dpToPx(8f)))
|
||||||
|
fun initFullScreen(activity: Activity, dark: Boolean? = true, showStatusBar: Boolean = false) {
|
||||||
|
val window = activity.window
|
||||||
|
val decorView = window.decorView
|
||||||
|
|
||||||
|
if (showStatusBar) {
|
||||||
|
// 显示状态栏,但保持透明
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
||||||
|
window.statusBarColor = Color.TRANSPARENT
|
||||||
|
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
|
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置内容延伸到状态栏下
|
||||||
|
var flags = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && dark == false) {
|
||||||
|
flags = flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
|
||||||
|
}
|
||||||
|
decorView.systemUiVisibility = flags
|
||||||
|
} else {
|
||||||
|
// 使用真正的全屏标志
|
||||||
|
decorView.systemUiVisibility = (
|
||||||
|
View.SYSTEM_UI_FLAG_FULLSCREEN or
|
||||||
|
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
|
||||||
|
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||||
|
)
|
||||||
|
|
||||||
|
// 移除状态栏的透明效果
|
||||||
|
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
||||||
|
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun loadWepJif(mContext: Context, webpGifUrl: String, view: ImageView) {
|
||||||
|
Glide.with(mContext)
|
||||||
|
.load(webpGifUrl)
|
||||||
|
// .apply(transform)
|
||||||
|
.addListener(object : RequestListener<Drawable> {
|
||||||
|
override fun onLoadFailed(
|
||||||
|
e: GlideException?,
|
||||||
|
model: Any?,
|
||||||
|
target: Target<Drawable>,
|
||||||
|
isFirstResource: Boolean
|
||||||
|
): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResourceReady(
|
||||||
|
resource: Drawable,
|
||||||
|
model: Any,
|
||||||
|
target: Target<Drawable>,
|
||||||
|
dataSource: DataSource,
|
||||||
|
isFirstResource: Boolean
|
||||||
|
): Boolean {
|
||||||
|
if (resource is WebpDrawable) {
|
||||||
|
resource.loopCount = WebpDrawable.LOOP_FOREVER
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}).into(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun getBgDrawable(con: Context, filePath: String): Drawable? {
|
||||||
|
if (!File(filePath).exists()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return BitmapDrawable(con.resources, BitmapFactory.decodeFile(filePath))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private val systemService =
|
||||||
|
App.Companion.appInstance.getSystemService(Context.INPUT_METHOD_SERVICE)
|
||||||
|
private val inputMethodManager = systemService as InputMethodManager
|
||||||
|
fun checkSetDefault(con: Context): Boolean {
|
||||||
|
// API 30+ 需要 QUERY_ALL_PACKAGES 权限
|
||||||
|
return try {
|
||||||
|
val defaultId =
|
||||||
|
Settings.Secure.getString(con.contentResolver, Settings.Secure.DEFAULT_INPUT_METHOD)
|
||||||
|
defaultId != null && defaultId.startsWith(con.packageName)
|
||||||
|
} catch (e: SecurityException) {
|
||||||
|
// API 30+ 可能没有权限访问
|
||||||
|
Log.e("Comutils", "Permission denied to check default input method", e)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkEnable(con: Context): Boolean {
|
||||||
|
return try {
|
||||||
|
// 总是使用传入的 context
|
||||||
|
val inputMethodManager = con.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
|
||||||
|
// 添加空值检查
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
val enabledMethods = inputMethodManager.enabledInputMethodList
|
||||||
|
enabledMethods.any { methodInfo ->
|
||||||
|
methodInfo.id.startsWith(con.packageName)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 旧版本的回退方案
|
||||||
|
true // 假设已启用,避免阻塞
|
||||||
|
}
|
||||||
|
} catch (e: SecurityException) {
|
||||||
|
Log.e("Comutils", "Permission denied checking enabled IMEs", e)
|
||||||
|
// 在 API 30+ 上,如果没有权限,假设已启用
|
||||||
|
true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("Comutils", "Error checking enabled IMEs", e)
|
||||||
|
true // 出错时假设已启用,让用户继续
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTextForImeAction(imeOptions: Int): Int {
|
||||||
|
return imeOptions and EditorInfo.IME_MASK_ACTION
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dpToPx(dpValue: Float): Int {
|
||||||
|
val scale = App.Companion.appInstance.resources.displayMetrics.density
|
||||||
|
return (dpValue * scale + 0.5f).toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,291 @@
|
|||||||
|
package com.rainbow.app.keyboard.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
|
||||||
|
public class UtilsFastblur {
|
||||||
|
|
||||||
|
public static Bitmap fastBlur(Bitmap sentBitmap, int radius, boolean canReuseInBitmap) {
|
||||||
|
// 图片缩放比例,提高模糊效率
|
||||||
|
int scaleRatio = 8;
|
||||||
|
int blurRadius = radius;
|
||||||
|
|
||||||
|
if (sentBitmap == null || sentBitmap.isRecycled()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int width = Math.max(sentBitmap.getWidth() / scaleRatio, 1);
|
||||||
|
int height = Math.max(sentBitmap.getHeight() / scaleRatio, 1);
|
||||||
|
|
||||||
|
Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig() != null ? sentBitmap.getConfig() : Bitmap.Config.ARGB_8888, true);
|
||||||
|
|
||||||
|
if (canReuseInBitmap) {
|
||||||
|
bitmap = sentBitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建缩放后的Bitmap
|
||||||
|
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, width, height, false);
|
||||||
|
|
||||||
|
// 进行模糊处理
|
||||||
|
Bitmap blurredBitmap = doBlur(scaledBitmap, blurRadius, true);
|
||||||
|
|
||||||
|
// 缩放回原始尺寸
|
||||||
|
Bitmap resultBitmap = Bitmap.createScaledBitmap(blurredBitmap, sentBitmap.getWidth(), sentBitmap.getHeight(), false);
|
||||||
|
|
||||||
|
// 清理临时Bitmap
|
||||||
|
if (scaledBitmap != null && scaledBitmap != blurredBitmap) {
|
||||||
|
scaledBitmap.recycle();
|
||||||
|
}
|
||||||
|
if (blurredBitmap != null && blurredBitmap != resultBitmap) {
|
||||||
|
blurredBitmap.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultBitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Bitmap doBlur(Bitmap sentBitmap, int radius, boolean canReuseInBitmap) {
|
||||||
|
Bitmap bitmap;
|
||||||
|
if (canReuseInBitmap) {
|
||||||
|
bitmap = sentBitmap;
|
||||||
|
} else {
|
||||||
|
bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (radius < 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int w = bitmap.getWidth();
|
||||||
|
int h = bitmap.getHeight();
|
||||||
|
|
||||||
|
int[] pix = new int[w * h];
|
||||||
|
bitmap.getPixels(pix, 0, w, 0, 0, w, h);
|
||||||
|
|
||||||
|
int wm = w - 1;
|
||||||
|
int hm = h - 1;
|
||||||
|
int wh = w * h;
|
||||||
|
int div = radius + radius + 1;
|
||||||
|
|
||||||
|
int[] r = new int[wh];
|
||||||
|
int[] g = new int[wh];
|
||||||
|
int[] b = new int[wh];
|
||||||
|
int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
|
||||||
|
int[] vmin = new int[Math.max(w, h)];
|
||||||
|
|
||||||
|
int divsum = (div + 1) >> 1;
|
||||||
|
divsum *= divsum;
|
||||||
|
int[] dv = new int[256 * divsum];
|
||||||
|
for (i = 0; i < 256 * divsum; i++) {
|
||||||
|
dv[i] = (i / divsum);
|
||||||
|
}
|
||||||
|
|
||||||
|
yw = yi = 0;
|
||||||
|
|
||||||
|
int[][] stack = new int[div][3];
|
||||||
|
int stackpointer;
|
||||||
|
int stackstart;
|
||||||
|
int[] sir;
|
||||||
|
int rbs;
|
||||||
|
int r1 = radius + 1;
|
||||||
|
int routsum, goutsum, boutsum;
|
||||||
|
int rinsum, ginsum, binsum;
|
||||||
|
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
|
||||||
|
for (i = -radius; i <= radius; i++) {
|
||||||
|
p = pix[Math.min(wm, Math.max(i, 0)) + y * w];
|
||||||
|
sir = stack[i + radius];
|
||||||
|
sir[0] = (p & 0xff0000) >> 16;
|
||||||
|
sir[1] = (p & 0x00ff00) >> 8;
|
||||||
|
sir[2] = (p & 0x0000ff);
|
||||||
|
rbs = r1 - Math.abs(i);
|
||||||
|
rsum += sir[0] * rbs;
|
||||||
|
gsum += sir[1] * rbs;
|
||||||
|
bsum += sir[2] * rbs;
|
||||||
|
if (i > 0) {
|
||||||
|
rinsum += sir[0];
|
||||||
|
ginsum += sir[1];
|
||||||
|
binsum += sir[2];
|
||||||
|
} else {
|
||||||
|
routsum += sir[0];
|
||||||
|
goutsum += sir[1];
|
||||||
|
boutsum += sir[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stackpointer = radius;
|
||||||
|
|
||||||
|
for (x = 0; x < w; x++) {
|
||||||
|
r[yi] = dv[rsum];
|
||||||
|
g[yi] = dv[gsum];
|
||||||
|
b[yi] = dv[bsum];
|
||||||
|
|
||||||
|
rsum -= routsum;
|
||||||
|
gsum -= goutsum;
|
||||||
|
bsum -= boutsum;
|
||||||
|
|
||||||
|
stackstart = stackpointer - radius + div;
|
||||||
|
sir = stack[stackstart % div];
|
||||||
|
|
||||||
|
routsum -= sir[0];
|
||||||
|
goutsum -= sir[1];
|
||||||
|
boutsum -= sir[2];
|
||||||
|
|
||||||
|
if (y == 0) {
|
||||||
|
vmin[x] = Math.min(x + radius + 1, wm);
|
||||||
|
}
|
||||||
|
p = pix[vmin[x] + y * w];
|
||||||
|
|
||||||
|
sir[0] = (p & 0xff0000) >> 16;
|
||||||
|
sir[1] = (p & 0x00ff00) >> 8;
|
||||||
|
sir[2] = (p & 0x0000ff);
|
||||||
|
rinsum += sir[0];
|
||||||
|
ginsum += sir[1];
|
||||||
|
binsum += sir[2];
|
||||||
|
rsum += rinsum;
|
||||||
|
gsum += ginsum;
|
||||||
|
bsum += binsum;
|
||||||
|
|
||||||
|
stackpointer = (stackpointer + 1) % div;
|
||||||
|
sir = stack[(stackpointer) % div];
|
||||||
|
|
||||||
|
routsum += sir[0];
|
||||||
|
goutsum += sir[1];
|
||||||
|
boutsum += sir[2];
|
||||||
|
|
||||||
|
rinsum -= sir[0];
|
||||||
|
ginsum -= sir[1];
|
||||||
|
binsum -= sir[2];
|
||||||
|
|
||||||
|
yi++;
|
||||||
|
}
|
||||||
|
yw += w;
|
||||||
|
}
|
||||||
|
for (x = 0; x < w; x++) {
|
||||||
|
rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
|
||||||
|
yp = -radius * w;
|
||||||
|
for (i = -radius; i <= radius; i++) {
|
||||||
|
yi = Math.max(0, yp) + x;
|
||||||
|
|
||||||
|
sir = stack[i + radius];
|
||||||
|
|
||||||
|
sir[0] = r[yi];
|
||||||
|
sir[1] = g[yi];
|
||||||
|
sir[2] = b[yi];
|
||||||
|
|
||||||
|
rbs = r1 - Math.abs(i);
|
||||||
|
|
||||||
|
rsum += sir[0] * rbs;
|
||||||
|
gsum += sir[1] * rbs;
|
||||||
|
bsum += sir[2] * rbs;
|
||||||
|
|
||||||
|
if (i > 0) {
|
||||||
|
rinsum += sir[0];
|
||||||
|
ginsum += sir[1];
|
||||||
|
binsum += sir[2];
|
||||||
|
} else {
|
||||||
|
routsum += sir[0];
|
||||||
|
goutsum += sir[1];
|
||||||
|
boutsum += sir[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < hm) {
|
||||||
|
yp += w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yi = x;
|
||||||
|
stackpointer = radius;
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
// Preserve alpha channel: ( 0xff000000 & pix[yi] )
|
||||||
|
pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];
|
||||||
|
|
||||||
|
rsum -= routsum;
|
||||||
|
gsum -= goutsum;
|
||||||
|
bsum -= boutsum;
|
||||||
|
|
||||||
|
stackstart = stackpointer - radius + div;
|
||||||
|
sir = stack[stackstart % div];
|
||||||
|
|
||||||
|
routsum -= sir[0];
|
||||||
|
goutsum -= sir[1];
|
||||||
|
boutsum -= sir[2];
|
||||||
|
|
||||||
|
if (x == 0) {
|
||||||
|
vmin[y] = Math.min(y + r1, hm) * w;
|
||||||
|
}
|
||||||
|
p = x + vmin[y];
|
||||||
|
|
||||||
|
sir[0] = r[p];
|
||||||
|
sir[1] = g[p];
|
||||||
|
sir[2] = b[p];
|
||||||
|
|
||||||
|
rinsum += sir[0];
|
||||||
|
ginsum += sir[1];
|
||||||
|
binsum += sir[2];
|
||||||
|
|
||||||
|
rsum += rinsum;
|
||||||
|
gsum += ginsum;
|
||||||
|
bsum += binsum;
|
||||||
|
|
||||||
|
stackpointer = (stackpointer + 1) % div;
|
||||||
|
sir = stack[stackpointer];
|
||||||
|
|
||||||
|
routsum += sir[0];
|
||||||
|
goutsum += sir[1];
|
||||||
|
boutsum += sir[2];
|
||||||
|
|
||||||
|
rinsum -= sir[0];
|
||||||
|
ginsum -= sir[1];
|
||||||
|
binsum -= sir[2];
|
||||||
|
|
||||||
|
yi += w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitmap.setPixels(pix, 0, w, 0, 0, w, h);
|
||||||
|
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Drawable blurDrawable(Drawable drawable, int blurRadius, Context context) {
|
||||||
|
if (drawable == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 将Drawable转换为Bitmap
|
||||||
|
Bitmap bitmap;
|
||||||
|
if (drawable instanceof BitmapDrawable) {
|
||||||
|
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
|
||||||
|
bitmap = bitmapDrawable.getBitmap();
|
||||||
|
if (bitmap == null || bitmap.isRecycled()) {
|
||||||
|
return drawable;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 非BitmapDrawable,创建新的Bitmap
|
||||||
|
bitmap = Bitmap.createBitmap(
|
||||||
|
drawable.getIntrinsicWidth(),
|
||||||
|
drawable.getIntrinsicHeight(),
|
||||||
|
Bitmap.Config.ARGB_8888
|
||||||
|
);
|
||||||
|
Canvas canvas = new Canvas(bitmap);
|
||||||
|
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||||
|
drawable.draw(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 应用模糊
|
||||||
|
Bitmap blurredBitmap = fastBlur(bitmap, blurRadius, false);
|
||||||
|
|
||||||
|
if (blurredBitmap != null && !blurredBitmap.isRecycled()) {
|
||||||
|
return new BitmapDrawable(context.getResources(), blurredBitmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
return drawable;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return drawable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,181 @@
|
|||||||
|
package com.rainbow.app.keyboard.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.drawable.BitmapDrawable
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.graphics.drawable.StateListDrawable
|
||||||
|
import android.util.Xml
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import com.rainbow.app.keyboard.App
|
||||||
|
import com.rainbow.app.keyboard.R
|
||||||
|
import com.rainbow.app.keyboard.helper.ResConfig
|
||||||
|
import org.xmlpull.v1.XmlPullParser
|
||||||
|
import java.io.File
|
||||||
|
import java.io.StringReader
|
||||||
|
import kotlin.collections.iterator
|
||||||
|
|
||||||
|
class UtilsKbmanager(var context: Context) {
|
||||||
|
|
||||||
|
|
||||||
|
private var textSize = 13f
|
||||||
|
|
||||||
|
var functionDraw: Drawable =
|
||||||
|
getDefaultDrawList(R.drawable.phold_key_outline, R.drawable.phold_key)
|
||||||
|
var generalDraw: Drawable =
|
||||||
|
getDefaultDrawList(R.drawable.phold_key_outline, R.drawable.phold_key)
|
||||||
|
|
||||||
|
var toDraw: Drawable = getDefaultDrawList(R.drawable.phold_key_outline, R.drawable.phold_key)
|
||||||
|
var spaceDraw: Drawable = getDefaultDrawList(R.drawable.phold_key_outline, R.drawable.phold_key)
|
||||||
|
|
||||||
|
var switchDraw: Drawable? = null
|
||||||
|
var deleteDraw: Drawable? = null
|
||||||
|
var backDraw: Drawable? = null
|
||||||
|
var searchDraw: Drawable? = null
|
||||||
|
|
||||||
|
var shiftDraw: Drawable? = null
|
||||||
|
var shiftLockDraw: Drawable? = null
|
||||||
|
|
||||||
|
var keyTextColor: Int = ContextCompat.getColor(context, R.color.black)
|
||||||
|
var keyTextColorFunction: Int = ContextCompat.getColor(context, R.color.black)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fun getConfig(): ResConfig? {
|
||||||
|
val skinPath = UtilsSavecurrentkb.getSkinPath()
|
||||||
|
val configFilePath = skinPath + "assets/keyboard.conf"
|
||||||
|
val file = File(configFilePath)
|
||||||
|
return if (file.exists()) {
|
||||||
|
UtilsconfigFile.initConfig(configFilePath)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getConfigBg(name: String): Drawable? {
|
||||||
|
UtilsSavecurrentkb.getSkinPath()?.let { resPath ->
|
||||||
|
val pPath = "${resPath}res/drawable-xhdpi-v4/"
|
||||||
|
|
||||||
|
return getDrawList(
|
||||||
|
pPath + name,
|
||||||
|
pPath + name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateSkinConfig() {
|
||||||
|
UtilsSavecurrentkb.getSkinPath()?.let { resPath ->
|
||||||
|
val pPath = "${resPath}res/drawable-xhdpi-v4/"
|
||||||
|
pPath.let {
|
||||||
|
readColors(resPath) {
|
||||||
|
for ((name, value) in it) {
|
||||||
|
if (name == UtilsKeyname.keyTextColorName) {
|
||||||
|
keyTextColor = value
|
||||||
|
}
|
||||||
|
if (name == UtilsKeyname.keyTextColorFunctionName) {
|
||||||
|
keyTextColorFunction = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
functionDraw = getDrawList(
|
||||||
|
it + UtilsKeyname.functionNormalName,
|
||||||
|
it + UtilsKeyname.functionPressName
|
||||||
|
)
|
||||||
|
generalDraw = getDrawList(it + UtilsKeyname.normalName, it + UtilsKeyname.pressName)
|
||||||
|
toDraw = getDrawList(it + UtilsKeyname.toNormalName, it + UtilsKeyname.toPressName)
|
||||||
|
spaceDraw =
|
||||||
|
getDrawList(it + UtilsKeyname.spaceNormalName, it + UtilsKeyname.spacePressName)
|
||||||
|
switchDraw =
|
||||||
|
getDrawList(it + UtilsKeyname.imeSwitchName, it + UtilsKeyname.imeSwitchName)
|
||||||
|
deleteDraw = getDrawList(
|
||||||
|
it + UtilsKeyname.deleteNormalName,
|
||||||
|
it + UtilsKeyname.deletePressName
|
||||||
|
)
|
||||||
|
backDraw = getDrawList(it + UtilsKeyname.backName, it + UtilsKeyname.backName)
|
||||||
|
searchDraw = getDrawList(it + UtilsKeyname.searchName, it + UtilsKeyname.searchName)
|
||||||
|
shiftDraw = getDrawList(
|
||||||
|
it + UtilsKeyname.shiftNormalName,
|
||||||
|
it + UtilsKeyname.shiftNormalName
|
||||||
|
)
|
||||||
|
shiftLockDraw =
|
||||||
|
getDrawList(it + UtilsKeyname.shiftLockName, it + UtilsKeyname.shiftLockName)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun getDefaultDrawList(normalDrawId: Int, pressDrawId: Int): StateListDrawable {
|
||||||
|
val normalDraw = ContextCompat.getDrawable(App.Companion.appInstance, normalDrawId)
|
||||||
|
val pressDraw = ContextCompat.getDrawable(App.Companion.appInstance, pressDrawId)
|
||||||
|
val stateListDrawable = StateListDrawable().apply {
|
||||||
|
addState(
|
||||||
|
intArrayOf(android.R.attr.state_pressed),
|
||||||
|
pressDraw
|
||||||
|
)
|
||||||
|
addState(intArrayOf(), normalDraw)
|
||||||
|
}
|
||||||
|
|
||||||
|
return stateListDrawable
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun getDrawList(normalPath: String, pressPath: String): StateListDrawable {
|
||||||
|
val pressDraw = BitmapFactory.decodeFile(pressPath)
|
||||||
|
val normalDraw = BitmapFactory.decodeFile(normalPath)
|
||||||
|
val stateListDrawable = StateListDrawable().apply {
|
||||||
|
addState(
|
||||||
|
intArrayOf(android.R.attr.state_pressed),
|
||||||
|
BitmapDrawable(context.resources, pressDraw)
|
||||||
|
)
|
||||||
|
addState(intArrayOf(), BitmapDrawable(context.resources, normalDraw))
|
||||||
|
}
|
||||||
|
|
||||||
|
return stateListDrawable
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun readColors(resPath: String, callBack: (Map<String, Int>) -> Unit) {
|
||||||
|
val resMaps = mutableMapOf<String, Int>()
|
||||||
|
|
||||||
|
val pPath = "${resPath}res/colors.xml"
|
||||||
|
val file = File(pPath)
|
||||||
|
if (file.exists()) {
|
||||||
|
val xmlPullParser = Xml.newPullParser().apply {
|
||||||
|
setInput(StringReader(file.readText()))
|
||||||
|
setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false)
|
||||||
|
|
||||||
|
}
|
||||||
|
var curType = xmlPullParser.eventType
|
||||||
|
while (curType != XmlPullParser.END_DOCUMENT) {
|
||||||
|
val b = curType == XmlPullParser.START_TAG
|
||||||
|
val b1 = xmlPullParser.name == "color"
|
||||||
|
val b2 = xmlPullParser.name == "item"
|
||||||
|
if (b && (b1 || b2)) {
|
||||||
|
val attributeName = xmlPullParser.getAttributeValue(null, "name")
|
||||||
|
val nextTextValue = xmlPullParser.nextText()
|
||||||
|
val b3 = attributeName == UtilsKeyname.keyTextColorName
|
||||||
|
val b4 = attributeName == UtilsKeyname.keyTextColorFunctionName
|
||||||
|
if (b3 || b4) {
|
||||||
|
resMaps[attributeName] = Color.parseColor(nextTextValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
curType = xmlPullParser.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
callBack.invoke(resMaps)
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
package com.rainbow.app.keyboard.utils
|
||||||
|
|
||||||
|
object UtilsKeyname {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const val KEY_CODE_DELETE = -5
|
||||||
|
|
||||||
|
|
||||||
|
//同一个按键
|
||||||
|
const val KEY_CODE_SHIFT = -1
|
||||||
|
const val KEY_CODE_NUMBER_SHIFT = -103
|
||||||
|
const val KEY_CODE_SYMBOL_SHIFT = -101
|
||||||
|
|
||||||
|
//同一个按键
|
||||||
|
const val KEY_CODE_CHANGE_NUMBER = -2
|
||||||
|
const val KEY_CODE_BACK = -102
|
||||||
|
|
||||||
|
|
||||||
|
const val KEY_CODE_COMPLETE = -4
|
||||||
|
const val KEY_CODE_CANCEL = -3
|
||||||
|
|
||||||
|
const val KEY_CODE_SPACE = 32
|
||||||
|
|
||||||
|
|
||||||
|
const val functionNormalName = "btn_keyboard_key_functional_normal.9.png"
|
||||||
|
const val functionPressName = "btn_keyboard_key_functional_pressed.9.png"
|
||||||
|
|
||||||
|
const val normalName = "btn_keyboard_key_normal_normal.9.png"
|
||||||
|
const val pressName = "btn_keyboard_key_normal_pressed.9.png"
|
||||||
|
|
||||||
|
const val toNormalName="btn_keyboard_key_toggle_normal_on.9.png"
|
||||||
|
const val toPressName="btn_keyboard_key_toggle_pressed_on.9.png"
|
||||||
|
|
||||||
|
const val spaceNormalName = "btn_keyboard_spacekey_normal_normal.9.png"
|
||||||
|
const val spacePressName = "btn_keyboard_spacekey_normal_pressed.9.png"
|
||||||
|
|
||||||
|
const val imeSwitchName ="ic_ime_switcher.png"
|
||||||
|
|
||||||
|
const val deleteNormalName = "sym_keyboard_delete_normal.png"
|
||||||
|
const val deletePressName = "sym_keyboard_delete_pressed.png"
|
||||||
|
|
||||||
|
const val backName ="sym_keyboard_return_normal.png"
|
||||||
|
|
||||||
|
const val searchName ="sym_keyboard_search.png"
|
||||||
|
|
||||||
|
const val shiftNormalName ="sym_keyboard_shift.png"
|
||||||
|
const val shiftLockName ="sym_keyboard_shift_locked.png"
|
||||||
|
|
||||||
|
const val keyTextColorName ="key_text_color_normal"
|
||||||
|
const val keyTextColorFunctionName ="key_text_color_functional"
|
||||||
|
|
||||||
|
const val videoName ="keyboard_background_video.mp4"
|
||||||
|
const val bgName ="keyboard_background.jpg"
|
||||||
|
const val bgName_png ="keyboard_background.png"
|
||||||
|
|
||||||
|
const val previewBg="keyboard_preview_screenshot.jpg"
|
||||||
|
|
||||||
|
const val video ="keyboard_background_video.gif"
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
package com.rainbow.app.keyboard.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.rainbow.app.keyboard.App
|
||||||
|
|
||||||
|
object UtilsSavecurrentkb {
|
||||||
|
|
||||||
|
val SP_NAME = "keyboard_skin"
|
||||||
|
val SKIN_PATH = "skin_path"
|
||||||
|
val spSkin = App.Companion.appInstance.getSharedPreferences(SP_NAME,Context.MODE_PRIVATE)
|
||||||
|
|
||||||
|
fun updateSkinPath(skinPath:String){
|
||||||
|
spSkin.edit().putString(SKIN_PATH,skinPath).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSkinPath( ):String?{
|
||||||
|
return spSkin.getString(SKIN_PATH,null)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package com.rainbow.app.keyboard.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.rainbow.app.keyboard.App;
|
||||||
|
import com.rainbow.app.keyboard.R;
|
||||||
|
|
||||||
|
|
||||||
|
public class UtilsTextvcustom extends androidx.appcompat.widget.AppCompatTextView {
|
||||||
|
|
||||||
|
|
||||||
|
public UtilsTextvcustom(Context context, @Nullable AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
initAttrs(context,attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void initAttrs(Context context, AttributeSet attrs){
|
||||||
|
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyTV);
|
||||||
|
boolean aBoolean = typedArray.getBoolean(R.styleable.MyTV_apply_font,false);
|
||||||
|
if(aBoolean){
|
||||||
|
initFont(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedArray.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void initFont(TextView tv) {
|
||||||
|
tv.setTypeface(App.Companion.getDefaultFont());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,106 @@
|
|||||||
|
package com.rainbow.app.keyboard.utils;
|
||||||
|
|
||||||
|
import com.rainbow.app.keyboard.helper.KeybModel;
|
||||||
|
import com.rainbow.app.keyboard.helper.ResConfig;
|
||||||
|
import com.rainbow.app.keyboard.helper.ResLayout;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class UtilsconfigFile {
|
||||||
|
|
||||||
|
public static ResConfig initConfig(String path) {
|
||||||
|
String filePath = "keyboard.conf"; // 文件路径
|
||||||
|
ResConfig config = parseConfig(path);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ResConfig parseConfig(String filePath) {
|
||||||
|
// InputStream open = App.appInstance.getAssets().open(filePath);
|
||||||
|
ResConfig config = new ResConfig();
|
||||||
|
try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
|
||||||
|
String line;
|
||||||
|
ResLayout currentLayout = null;
|
||||||
|
while ((line = br.readLine()) != null) {
|
||||||
|
line = line.trim();
|
||||||
|
if (line.isEmpty()) {
|
||||||
|
continue; // 跳过空行
|
||||||
|
}
|
||||||
|
if (line.startsWith("Version:")) {
|
||||||
|
config.setVersion(line.split(":")[1].trim());
|
||||||
|
} else if (line.startsWith("SupportLayouts:")) {
|
||||||
|
config.setSupportLayouts(line.split(":")[1].trim());
|
||||||
|
} else if (line.startsWith("HideHint:")) {
|
||||||
|
config.setHideHint(Integer.parseInt(line.split(":")[1].trim()));
|
||||||
|
} else if (line.startsWith("LayoutStyle:")) {
|
||||||
|
config.setLayoutStyle(line.split(":")[1].trim());
|
||||||
|
} else if (line.equals("KeyDefault") || line.equals("KeyMarkDefault") || line.equals("KeyFuncDefault")) {
|
||||||
|
LinkedHashMap<String, String> maps = config.getMaps();
|
||||||
|
maps.put(line, "");
|
||||||
|
} else if (line.contains(":") && currentLayout == null) {
|
||||||
|
String[] parts = line.split(":");
|
||||||
|
String keyName = parts[0].trim();
|
||||||
|
String keyValue = parts[1].trim();
|
||||||
|
|
||||||
|
String latestKey = null;
|
||||||
|
LinkedHashMap<String, String> maps = config.getMaps();
|
||||||
|
for (Map.Entry<String, String> entry : maps.entrySet()) {
|
||||||
|
latestKey = entry.getKey();
|
||||||
|
}
|
||||||
|
if (latestKey != null) {
|
||||||
|
maps.put(latestKey, keyValue);
|
||||||
|
}
|
||||||
|
} else if (line.startsWith("Row")) {
|
||||||
|
currentLayout = new ResLayout(line.split(":")[0].trim());
|
||||||
|
config.addLayout(currentLayout);
|
||||||
|
} else if (currentLayout != null) {
|
||||||
|
if (line.equals("Key")) {
|
||||||
|
String[] parts = line.split(":");
|
||||||
|
String keyName = parts[0].trim();
|
||||||
|
KeybModel KeybModel = new KeybModel(keyName);
|
||||||
|
currentLayout.addKey(KeybModel);
|
||||||
|
} else if (line.contains(":") && currentLayout.getLastKey().getBackground() == null) {
|
||||||
|
// 解析按键的其他属性(如 Label)
|
||||||
|
String[] parts = line.split(":");
|
||||||
|
String keyName = parts[0].trim();
|
||||||
|
String keyValue = parts[1].trim();
|
||||||
|
KeybModel KeybModel = currentLayout.getLastKey();
|
||||||
|
if (keyName.equals("Label")) {
|
||||||
|
KeybModel.setLabel(keyValue);
|
||||||
|
}
|
||||||
|
if (keyName.equals("Background")) {
|
||||||
|
KeybModel.setBackground(keyValue);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (line.equals("KeyShift") || line.equals("KeyDelete") || line.equals("KeyAlphaSymbol") || line.equals("KeyEmoji")
|
||||||
|
|| line.equals("KeyMark")
|
||||||
|
|| line.equals("KeySpace")
|
||||||
|
|| line.equals("KeyEnter")) {
|
||||||
|
KeybModel funcationKeybModel = new KeybModel(line);
|
||||||
|
config.addKey(funcationKeybModel);
|
||||||
|
} else if (line.contains(":")) {
|
||||||
|
String[] parts = line.split(":");
|
||||||
|
String keyName = parts[0].trim();
|
||||||
|
String keyValue = parts[1].trim();
|
||||||
|
KeybModel lastKeybModel = config.getLastKeyList();
|
||||||
|
if (keyName.equals("Label")) {
|
||||||
|
lastKeybModel.setLabel(keyValue);
|
||||||
|
}
|
||||||
|
if (keyName.equals("Background")) {
|
||||||
|
lastKeybModel.setBackground(keyValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,238 @@
|
|||||||
|
package com.rainbow.app.keyboard.utils;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.rainbow.app.keyboard.App;
|
||||||
|
import com.rainbow.app.keyboard.mycallback.EnablekbCallback;
|
||||||
|
|
||||||
|
import net.sf.sevenzipjbinding.ArchiveFormat;
|
||||||
|
import net.sf.sevenzipjbinding.IArchiveOpenCallback;
|
||||||
|
import net.sf.sevenzipjbinding.IInArchive;
|
||||||
|
import net.sf.sevenzipjbinding.SevenZip;
|
||||||
|
import net.sf.sevenzipjbinding.SevenZipException;
|
||||||
|
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
|
||||||
|
import net.sf.sevenzipjbinding.impl.RandomAccessFileOutStream;
|
||||||
|
import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
|
||||||
|
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import okhttp3.Call;
|
||||||
|
import okhttp3.Callback;
|
||||||
|
import okhttp3.MediaType;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.Response;
|
||||||
|
|
||||||
|
public class UtilszipFile {
|
||||||
|
|
||||||
|
public static void startDownloadZip(String zipPath, EnablekbCallback callback) {
|
||||||
|
OkHttpClient clientZip = new OkHttpClient().newBuilder().
|
||||||
|
connectTimeout(20, TimeUnit.SECONDS)
|
||||||
|
.writeTimeout(10, TimeUnit.SECONDS)
|
||||||
|
.readTimeout(10, TimeUnit.SECONDS).build();
|
||||||
|
Request.Builder builder = new Request.Builder();
|
||||||
|
Request request = builder.get().url(zipPath).build();
|
||||||
|
|
||||||
|
clientZip.newCall(request).enqueue(new Callback() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call call, IOException e) {
|
||||||
|
callback.OnApplySkinListener(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call call, Response response) {
|
||||||
|
|
||||||
|
InputStream inputStream = response.body().byteStream();
|
||||||
|
long l = response.body().contentLength();
|
||||||
|
MediaType mediaType = response.body().contentType();
|
||||||
|
|
||||||
|
saveZipFile(inputStream, getServiceZipName(zipPath), callback);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String getServiceZipName(String zipPath) {
|
||||||
|
String pointStr = "/";
|
||||||
|
int lastIndexOf = zipPath.lastIndexOf(pointStr);
|
||||||
|
String zipName = zipPath.substring(lastIndexOf + pointStr.length());
|
||||||
|
|
||||||
|
|
||||||
|
return zipName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getunZipFolderName(String zipPath) {
|
||||||
|
String pointStr = ".";
|
||||||
|
int lastIndexOf = zipPath.lastIndexOf(pointStr);
|
||||||
|
String zipName = zipPath.substring(0, lastIndexOf);
|
||||||
|
|
||||||
|
|
||||||
|
return zipName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void saveZipFile(InputStream inputStream, String zipFileName, EnablekbCallback callback) {
|
||||||
|
File zipfFile = new File(App.appInstance.getFilesDir(), zipFileName);
|
||||||
|
|
||||||
|
Log.d("-------------------","-------zipFileName="+zipFileName);
|
||||||
|
byte[] bytes = new byte[4096];
|
||||||
|
int readLength = 0;
|
||||||
|
InputStream is = inputStream;
|
||||||
|
FileOutputStream fileOs = null;
|
||||||
|
try {
|
||||||
|
fileOs = new FileOutputStream(zipfFile);
|
||||||
|
|
||||||
|
while ((readLength = is.read(bytes)) != -1) {
|
||||||
|
fileOs.write(bytes, 0, readLength);
|
||||||
|
}
|
||||||
|
fileOs.flush();
|
||||||
|
|
||||||
|
} catch (Exception exception) {
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (is != null) {
|
||||||
|
is.close();
|
||||||
|
}
|
||||||
|
if (fileOs != null) {
|
||||||
|
fileOs.close();
|
||||||
|
}
|
||||||
|
} catch (IOException ioException) {
|
||||||
|
|
||||||
|
}
|
||||||
|
un7ZZipFile(zipfFile, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getUnzipPath(String zipName){
|
||||||
|
String folderName = getunZipFolderName(zipName);
|
||||||
|
String replace = folderName.replace(".", "");
|
||||||
|
return App.appInstance.getFilesDir().getPath() + "/" + replace;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void un7ZZipFile(File saveZipFile, EnablekbCallback callback) {
|
||||||
|
List<File> fileList = new ArrayList<>();
|
||||||
|
|
||||||
|
String unzipFolderPath = getUnzipPath(saveZipFile.getName());
|
||||||
|
|
||||||
|
try {
|
||||||
|
RandomAccessFileInStream inStream = new RandomAccessFileInStream(new RandomAccessFile(saveZipFile, "r"));
|
||||||
|
IInArchive open = SevenZip.openInArchive(ArchiveFormat.SEVEN_ZIP, inStream, new IArchiveOpenCallback() {
|
||||||
|
@Override
|
||||||
|
public void setTotal(Long files, Long bytes) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCompleted(Long files, Long bytes) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
ISimpleInArchive simple = open.getSimpleInterface();
|
||||||
|
for (ISimpleInArchiveItem archiveItem : simple.getArchiveItems()) {
|
||||||
|
RandomAccessFileOutStream outStream = null;
|
||||||
|
try {
|
||||||
|
File itemFile;
|
||||||
|
if (archiveItem.isFolder()) {
|
||||||
|
File itemFolder = new File(unzipFolderPath, archiveItem.getPath());
|
||||||
|
boolean mkdirs = itemFolder.mkdirs();
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
itemFile = new File(unzipFolderPath, archiveItem.getPath());
|
||||||
|
if (!itemFile.getParentFile().exists()) {
|
||||||
|
boolean mkdirs = itemFile.getParentFile().mkdirs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outStream = new RandomAccessFileOutStream(new RandomAccessFile(itemFile, "rw"));
|
||||||
|
archiveItem.extractSlow(outStream);
|
||||||
|
fileList.add(itemFile);
|
||||||
|
} finally {
|
||||||
|
if (outStream != null) {
|
||||||
|
outStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inStream.close();
|
||||||
|
open.close();
|
||||||
|
|
||||||
|
} catch (FileNotFoundException | SevenZipException exception) {
|
||||||
|
|
||||||
|
} catch (IOException ioException) {
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
if (saveZipFile.exists()) {
|
||||||
|
|
||||||
|
saveZipFile.delete();
|
||||||
|
|
||||||
|
}
|
||||||
|
callback.OnApplySkinListener(fileList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File findFirstDirectory(File dir) {
|
||||||
|
if (dir.isDirectory()) {
|
||||||
|
File[] files = dir.listFiles();
|
||||||
|
if (files != null) {
|
||||||
|
for (File file : files) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
return file; // 返回第一个文件目录
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null; // 如果没有找到文件目录,则返回null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static Bitmap drawableToBitmap(Drawable drawable) {
|
||||||
|
if (drawable instanceof BitmapDrawable) {
|
||||||
|
return ((BitmapDrawable) drawable).getBitmap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果不是 BitmapDrawable,则创建一个空的 Bitmap 对象
|
||||||
|
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
|
||||||
|
drawable.getIntrinsicHeight(),
|
||||||
|
Bitmap.Config.ARGB_8888);
|
||||||
|
Canvas canvas = new Canvas(bitmap);
|
||||||
|
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||||
|
drawable.draw(canvas);
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将 Bitmap 保存到文件
|
||||||
|
private static void saveBitmapToFile(Bitmap bitmap, File file) throws IOException {
|
||||||
|
if(!file.exists()){
|
||||||
|
file.createNewFile();
|
||||||
|
}
|
||||||
|
FileOutputStream out = new FileOutputStream(file);
|
||||||
|
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); // 保存为 PNG 格式
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 示例:将 Drawable 写入文件
|
||||||
|
public static void saveDrawableToFile(Drawable drawable, File file) throws IOException {
|
||||||
|
Bitmap bitmap = drawableToBitmap(drawable);
|
||||||
|
saveBitmapToFile(bitmap, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
6
app/src/main/res/drawable/bg_cancel.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<corners android:radius="12dp" />
|
||||||
|
<solid android:color="@color/button_cancel_bg" />
|
||||||
|
|
||||||
|
</shape>
|
||||||
5
app/src/main/res/drawable/bg_dia.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<corners android:radius="18dp" />
|
||||||
|
<solid android:color="@color/card_bg" />
|
||||||
|
</shape>
|
||||||
7
app/src/main/res/drawable/bg_editv.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<corners android:radius="10dp"/>
|
||||||
|
<solid android:color="@color/theme_background"/>
|
||||||
|
|
||||||
|
</shape>
|
||||||
26
app/src/main/res/drawable/bg_launchprogress.xml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<!-- 进度条背景轨道 -->
|
||||||
|
<item android:id="@android:id/background">
|
||||||
|
<shape>
|
||||||
|
<corners android:radius="7dp" />
|
||||||
|
<solid android:color="@color/launch_progress_bg" />
|
||||||
|
<!-- 添加边框增加层次感 -->
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="#D1D5DB" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<!-- 主进度条 - 使用纯色 -->
|
||||||
|
<item android:id="@android:id/progress">
|
||||||
|
<clip>
|
||||||
|
<shape>
|
||||||
|
<corners android:radius="7dp" />
|
||||||
|
<solid android:color="@color/launch_progress" />
|
||||||
|
</shape>
|
||||||
|
</clip>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
</layer-list>
|
||||||
6
app/src/main/res/drawable/bg_rateit.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<corners android:radius="12dp" />
|
||||||
|
<solid android:color="@color/button_rate_bg" />
|
||||||
|
|
||||||
|
</shape>
|
||||||
6
app/src/main/res/drawable/bg_rateit_outline.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<corners android:radius="12dp" />
|
||||||
|
<solid android:color="@color/button_rate_bg_disabled" />
|
||||||
|
|
||||||
|
</shape>
|
||||||
6
app/src/main/res/drawable/bg_topicon.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<solid android:color="@color/ic_bg" />
|
||||||
|
<corners android:radius="23dp" />
|
||||||
|
</shape>
|
||||||
BIN
app/src/main/res/drawable/phold.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
app/src/main/res/drawable/phold_key.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
app/src/main/res/drawable/phold_key_outline.png
Normal file
|
After Width: | Height: | Size: 481 B |
6
app/src/main/res/drawable/selector_collect.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="@mipmap/ic_star_outline" android:state_selected="false"/>
|
||||||
|
<item android:drawable="@mipmap/ic_star" android:state_selected="true" />
|
||||||
|
|
||||||
|
</selector>
|
||||||
6
app/src/main/res/drawable/selector_enablekb.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<item android:drawable="@drawable/bg_rateit" android:state_selected="false" />
|
||||||
|
<item android:drawable="@drawable/bg_rateit_outline" />
|
||||||
|
</selector>
|
||||||
15
app/src/main/res/drawable/selector_presskb.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_pressed="false">
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<corners android:radius="4dp"/>
|
||||||
|
<solid android:color="@color/white"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
<item android:state_pressed="true">
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<corners android:radius="4dp"/>
|
||||||
|
<solid android:color="@color/key_bg_press"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</selector>
|
||||||
BIN
app/src/main/res/font/alimama_shuheiti_.otf
Normal file
105
app/src/main/res/layout/activity_collection.xml
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/theme_background"
|
||||||
|
android:paddingTop="8dp">
|
||||||
|
|
||||||
|
<!-- 标题 -->
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/back"
|
||||||
|
android:layout_width="44dp"
|
||||||
|
android:layout_height="44dp"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:paddingStart="5dp"
|
||||||
|
android:paddingTop="10dp"
|
||||||
|
android:paddingBottom="10dp"
|
||||||
|
android:src="@mipmap/ic_back"
|
||||||
|
android:tint="@color/black"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/collection_title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:letterSpacing="0.03"
|
||||||
|
android:text="Collection"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/black"
|
||||||
|
android:textSize="20sp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<!-- 收藏数量显示 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/text_favorite_count_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/collection_title">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="1.2dp"
|
||||||
|
android:layout_marginStart="30dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:background="@color/underline_color" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textFavoriteCount"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/underline_color"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="1.2dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="30dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:background="@color/underline_color" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 内容区域 -->
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/text_favorite_count_container">
|
||||||
|
|
||||||
|
<!-- 收藏列表 -->
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/likeRecycler"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<include
|
||||||
|
android:id="@+id/empty_container"
|
||||||
|
layout="@layout/empty_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
255
app/src/main/res/layout/activity_detailkb.xml
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/main"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/sidebar_bg">
|
||||||
|
|
||||||
|
<!-- ==================== 顶部键盘详情部分 ==================== -->
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:id="@+id/top_section_card"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
app:cardElevation="12dp"
|
||||||
|
app:cardCornerRadius="0dp"
|
||||||
|
app:cardBackgroundColor="@android:color/transparent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingBottom="16dp"
|
||||||
|
android:background="@color/sidebar_bg">
|
||||||
|
|
||||||
|
<!-- 返回按钮 -->
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/back"
|
||||||
|
android:layout_width="44dp"
|
||||||
|
android:layout_height="44dp"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:paddingStart="5dp"
|
||||||
|
android:paddingTop="10dp"
|
||||||
|
android:paddingBottom="10dp"
|
||||||
|
android:src="@mipmap/ic_back"
|
||||||
|
android:tint="@color/text_color"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<!-- 标题 -->
|
||||||
|
<com.rainbow.app.keyboard.utils.UtilsTextvcustom
|
||||||
|
android:id="@+id/textview_data_name"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/app_name"
|
||||||
|
android:textColor="@color/text_color"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:apply_font="true"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/back"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/im_like"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/back"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/back" />
|
||||||
|
|
||||||
|
<!-- 收藏按钮 -->
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/im_like"
|
||||||
|
android:layout_width="44dp"
|
||||||
|
android:layout_height="44dp"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:padding="9dp"
|
||||||
|
android:src="@drawable/selector_collect"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/back"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/back" />
|
||||||
|
|
||||||
|
<!-- 键盘预览卡片 -->
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:id="@+id/card_viewData"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="200dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
app:cardCornerRadius="12dp"
|
||||||
|
app:cardElevation="4dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/back">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/image_data"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop" />
|
||||||
|
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<!-- 下载应用按钮 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/layoutDownloadApply"
|
||||||
|
android:layout_width="260dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginTop="22dp"
|
||||||
|
android:background="@drawable/bg_rateit"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/card_viewData">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/im_download"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:src="@mipmap/kbpre_download" />
|
||||||
|
|
||||||
|
<com.rainbow.app.keyboard.utils.UtilsTextvcustom
|
||||||
|
android:id="@+id/tv_download"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:text="@string/download_apply"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:apply_font="true" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<!-- ==================== 推荐部分 ==================== -->
|
||||||
|
<androidx.core.widget.NestedScrollView
|
||||||
|
android:id="@+id/nested_scroll_view"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:fillViewport="true"
|
||||||
|
android:background="@color/theme_background"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/top_section_card">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<!-- 推荐标题区域 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/title_container"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingTop="20dp"
|
||||||
|
android:paddingBottom="10dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<!-- 左边下划线 -->
|
||||||
|
<View
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="2dp"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:background="@color/underline_color" />
|
||||||
|
|
||||||
|
<!-- 推荐标题 -->
|
||||||
|
<com.rainbow.app.keyboard.utils.UtilsTextvcustom
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/recommend"
|
||||||
|
android:textColor="@color/text_color"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:letterSpacing="0.05"
|
||||||
|
app:apply_font="true" />
|
||||||
|
|
||||||
|
<!-- 右边下划线 -->
|
||||||
|
<View
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="2dp"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:background="@color/underline_color" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 副标题 -->
|
||||||
|
<com.rainbow.app.keyboard.utils.UtilsTextvcustom
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="@string/more_suggestions"
|
||||||
|
android:textColor="@color/subtext_color"
|
||||||
|
android:textSize="12sp"
|
||||||
|
app:apply_font="true" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 推荐键盘列表 -->
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recommended_recycler"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/title_container" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
|
<!-- ==================== 回到顶部按钮 ==================== -->
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/btn_scroll_to_top"
|
||||||
|
android:layout_width="36dp"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
android:layout_marginEnd="20dp"
|
||||||
|
android:layout_marginBottom="30dp"
|
||||||
|
android:background="@drawable/bg_topicon"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:src="@mipmap/ic_top"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
<!-- ==================== 加载动画 ==================== -->
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/loading"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/bg_loading"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:indeterminateTint="@color/white" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
65
app/src/main/res/layout/activity_editv.xml
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:id="@+id/main"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/bg_loading" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/et"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_alignParentBottom="true"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:layout_marginTop="50dp"
|
||||||
|
android:layout_marginEnd="5dp"
|
||||||
|
android:background="@drawable/bg_editv"
|
||||||
|
android:focusable="true"
|
||||||
|
android:focusableInTouchMode="true"
|
||||||
|
android:hint="@string/et_hint"
|
||||||
|
android:paddingStart="15dp"
|
||||||
|
android:textColor="@color/black"
|
||||||
|
android:textColorHint="@color/apply_step_false"
|
||||||
|
android:textCursorDrawable="@color/color_74CBFF" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/id_back"
|
||||||
|
android:layout_width="44dp"
|
||||||
|
android:layout_height="44dp"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:paddingStart="5dp"
|
||||||
|
android:paddingTop="10dp"
|
||||||
|
android:paddingBottom="10dp"
|
||||||
|
android:src="@mipmap/ic_back" />
|
||||||
|
|
||||||
|
<com.rainbow.app.keyboard.utils.UtilsTextvcustom
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignTop="@id/id_back"
|
||||||
|
android:layout_alignBottom="@id/id_back"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:gravity="center"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:paddingStart="5dp"
|
||||||
|
android:paddingEnd="5dp"
|
||||||
|
android:text="@string/app_name"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="18sp"
|
||||||
|
app:apply_font="true" />
|
||||||
|
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
71
app/src/main/res/layout/activity_launch.xml
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:id="@+id/main"
|
||||||
|
android:background="@color/launch_bg"
|
||||||
|
tools:context=".uiactivity.LaunchActivity">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/image"
|
||||||
|
android:layout_width="170dp"
|
||||||
|
android:layout_height="170dp"
|
||||||
|
android:layout_marginTop="160dp"
|
||||||
|
android:src="@mipmap/logo"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textview_appname"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="28dp"
|
||||||
|
android:text="@string/app_name"
|
||||||
|
android:fontFamily="@font/alimama_shuheiti_"
|
||||||
|
android:textColor="@color/launch_text"
|
||||||
|
android:textSize="42sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/image" />
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progressbar"
|
||||||
|
style="?android:attr/progressBarStyleHorizontal"
|
||||||
|
android:layout_width="280dp"
|
||||||
|
android:layout_height="14dp"
|
||||||
|
android:layout_marginTop="40dp"
|
||||||
|
android:max="100"
|
||||||
|
android:progress="0"
|
||||||
|
android:progressDrawable="@drawable/bg_launchprogress"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/textview_appname" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textview_progress_percent"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="0%"
|
||||||
|
android:textColor="@color/launch_subtext"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/progressbar" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textview_version"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="40dp"
|
||||||
|
android:textColor="@color/launch_subtext"
|
||||||
|
android:textSize="14sp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
124
app/src/main/res/layout/activity_main.xml
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/main"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/theme_background"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
tools:context=".uiactivity.MainActivity">
|
||||||
|
|
||||||
|
<!-- 主内容区域 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<!-- 顶部标题栏 -->
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="14dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/appIcon"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@mipmap/logo" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/appName"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:fontFamily="@font/alimama_shuheiti_"
|
||||||
|
android:letterSpacing="0.03"
|
||||||
|
android:text="@string/app_name"
|
||||||
|
android:textColor="@color/title_color"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/collectionIcon"
|
||||||
|
android:layout_width="30dp"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:src="@mipmap/home_tocollection" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<!-- 分割线 -->
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="@color/card_divider" />
|
||||||
|
|
||||||
|
<!-- 固定双栏布局:左侧分类列表 + 右侧键盘网格 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<!-- 左侧分类列表(屏幕宽度四分之一) -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:background="@color/sidebar_bg"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingStart="6dp"
|
||||||
|
android:paddingEnd="6dp">
|
||||||
|
|
||||||
|
<!-- 分类列表 -->
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/navRecyclerview"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clipToPadding="false" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 右侧键盘网格(屏幕宽度四分之三) -->
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="3"
|
||||||
|
android:padding="8dp">
|
||||||
|
|
||||||
|
<!-- 键盘壁纸网格 -->
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recyclerview"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clipToPadding="false" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 回到顶部按钮 -->
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/btn_scroll_to_top"
|
||||||
|
android:layout_width="36dp"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
android:layout_gravity="bottom|end"
|
||||||
|
android:layout_marginEnd="20dp"
|
||||||
|
android:layout_marginBottom="30dp"
|
||||||
|
android:background="@drawable/bg_topicon"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:src="@mipmap/ic_top"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
162
app/src/main/res/layout/activity_settings.xml
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/theme_background"
|
||||||
|
android:minHeight="500dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/close_button"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:src="@mipmap/dia_close"
|
||||||
|
android:padding="4dp"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Guideline
|
||||||
|
android:id="@+id/guideline_20_percent"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layout_constraintGuide_percent="0.12" />
|
||||||
|
|
||||||
|
<!-- 顶部图标和名称区域 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/iconContainer"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/guideline_20_percent">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/appIcon"
|
||||||
|
android:layout_width="@dimen/logo_size"
|
||||||
|
android:layout_height="@dimen/logo_size"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@mipmap/logo" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:fontFamily="@font/alimama_shuheiti_"
|
||||||
|
android:letterSpacing="0.05"
|
||||||
|
android:text="@string/app_name"
|
||||||
|
android:textColor="@color/title_color"
|
||||||
|
android:textSize="@dimen/app_name_text_size" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_marginEnd="20dp"
|
||||||
|
android:layout_marginBottom="32dp"
|
||||||
|
app:cardBackgroundColor="@color/card_bg"
|
||||||
|
app:cardCornerRadius="@dimen/card_corner_radius"
|
||||||
|
app:cardElevation="@dimen/card_elevation"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/iconContainer"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="@dimen/card_padding">
|
||||||
|
|
||||||
|
<!-- Rate Us 选项 -->
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/rateUsLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/option_item_height"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:paddingHorizontal="@dimen/option_padding_horizontal"
|
||||||
|
android:paddingVertical="@dimen/option_padding_vertical">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/rateUsIcon"
|
||||||
|
android:layout_width="@dimen/option_icon_size"
|
||||||
|
android:layout_height="@dimen/option_icon_size"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:src="@mipmap/dia_feedback" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginStart="@dimen/option_text_margin_start"
|
||||||
|
android:layout_toEndOf="@id/rateUsIcon"
|
||||||
|
android:text="@string/rate_us"
|
||||||
|
android:textColor="@color/card_text"
|
||||||
|
android:textSize="@dimen/option_text_size"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="@dimen/option_arrow_size"
|
||||||
|
android:layout_height="@dimen/option_arrow_size"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:src="@mipmap/dia_toright"
|
||||||
|
android:tint="@color/card_icon_tint" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<!-- 分割线 -->
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/divider_height"
|
||||||
|
android:layout_marginTop="@dimen/divider_margin_vertical"
|
||||||
|
android:layout_marginBottom="@dimen/divider_margin_vertical"
|
||||||
|
android:background="@color/card_divider" />
|
||||||
|
|
||||||
|
<!-- Version 选项 -->
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/option_item_height"
|
||||||
|
android:paddingHorizontal="@dimen/option_padding_horizontal"
|
||||||
|
android:paddingVertical="@dimen/option_padding_vertical">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/versionIcon"
|
||||||
|
android:layout_width="@dimen/option_icon_size"
|
||||||
|
android:layout_height="@dimen/option_icon_size"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:src="@mipmap/dia_version" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginStart="@dimen/option_text_margin_start"
|
||||||
|
android:layout_toEndOf="@id/versionIcon"
|
||||||
|
android:text="@string/version"
|
||||||
|
android:textColor="@color/card_text"
|
||||||
|
android:textSize="@dimen/option_text_size"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/versionValue"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:textColor="@color/card_text"
|
||||||
|
android:textSize="@dimen/version_value_text_size" />
|
||||||
|
</RelativeLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
31
app/src/main/res/layout/adapter_kbitem.xml
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:id="@+id/cardView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:cardCornerRadius="12dp"
|
||||||
|
app:cardElevation="2dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/keyboardImage"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/favoriteIcon"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:layout_marginEnd="6dp"
|
||||||
|
android:src="@drawable/selector_collect"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
</FrameLayout>
|
||||||
99
app/src/main/res/layout/dialog_enablekb.xml
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/bg_dia"
|
||||||
|
android:paddingBottom="30dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/im_close"
|
||||||
|
android:layout_width="45dp"
|
||||||
|
android:layout_height="45dp"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:layout_marginEnd="15dp"
|
||||||
|
android:padding="5dp"
|
||||||
|
android:src="@mipmap/dia_close"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<com.rainbow.app.keyboard.utils.UtilsTextvcustom
|
||||||
|
android:id="@+id/text_open"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="50dp"
|
||||||
|
android:layout_marginEnd="50dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/open_str"
|
||||||
|
android:textColor="@color/black"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:apply_font="true"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/im_close" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/linear_step_one"
|
||||||
|
android:layout_width="280dp"
|
||||||
|
android:layout_height="52dp"
|
||||||
|
android:layout_marginTop="22dp"
|
||||||
|
android:background="@drawable/selector_enablekb"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/text_open">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ok_one"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:src="@mipmap/kbpre_already"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<com.rainbow.app.keyboard.utils.UtilsTextvcustom
|
||||||
|
android:id="@+id/text_step_one"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/step_1"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="15sp"
|
||||||
|
app:apply_font="true" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/linear_step_two"
|
||||||
|
android:layout_width="280dp"
|
||||||
|
android:layout_height="52dp"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:background="@drawable/selector_enablekb"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/linear_step_one">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ok_two"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:src="@mipmap/kbpre_already"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<com.rainbow.app.keyboard.utils.UtilsTextvcustom
|
||||||
|
android:id="@+id/text_step_two"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/step_2"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="15sp"
|
||||||
|
app:apply_font="true" />
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</FrameLayout>
|
||||||
115
app/src/main/res/layout/dialog_feedback.xml
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/bg_dia"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:clipToPadding="false">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="Rate us"
|
||||||
|
android:textColor="@color/card_title_color"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="18dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:lineSpacingExtra="4dp"
|
||||||
|
android:text="We hope this app is useful for you, if it does, would you please give us a 5 star and a nice review on Google Play, it really helps!"
|
||||||
|
android:textColor="@color/card_text"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
|
<!-- 星星容器 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="18dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/star1"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:layout_margin="4dp"
|
||||||
|
android:src="@mipmap/ic_star_outline" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/star2"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:layout_margin="4dp"
|
||||||
|
android:src="@mipmap/ic_star_outline" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/star3"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:layout_margin="4dp"
|
||||||
|
android:src="@mipmap/ic_star_outline" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/star4"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:layout_margin="4dp"
|
||||||
|
android:src="@mipmap/ic_star_outline" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/star5"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:layout_margin="4dp"
|
||||||
|
android:src="@mipmap/ic_star_outline" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 按钮布局 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:clipChildren="false">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btn_cancel"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:background="@drawable/bg_cancel"
|
||||||
|
android:text="cancel"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
android:textColor="@color/card_button_text"
|
||||||
|
android:gravity="center"
|
||||||
|
app:backgroundTint="@null" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btn_rate"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:background="@drawable/bg_rateit_outline"
|
||||||
|
android:enabled="false"
|
||||||
|
android:text="RATE IT"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:gravity="center"
|
||||||
|
app:backgroundTint="@null" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
44
app/src/main/res/layout/empty_layout.xml
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_bias="0.4">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="100dp"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
android:src="@mipmap/layout_emptycollect"
|
||||||
|
android:tint="@color/card_icon_tint" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:letterSpacing="0.02"
|
||||||
|
android:text="@string/empty_collection_title"
|
||||||
|
android:textColor="@color/title_color"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/empty_collection_content"
|
||||||
|
android:textColor="@color/card_text"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
37
app/src/main/res/layout/item_editv.xml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/gif_bg"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_alignTop="@id/custom_input_view"
|
||||||
|
android:layout_alignBottom="@id/custom_input_view" />
|
||||||
|
|
||||||
|
<VideoView
|
||||||
|
android:id="@+id/video_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_alignTop="@id/custom_input_view"
|
||||||
|
android:layout_alignBottom="@id/custom_input_view" />
|
||||||
|
|
||||||
|
<com.rainbow.app.keyboard.helper.KeybView
|
||||||
|
android:id="@+id/custom_input_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:animateLayoutChanges="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:focusableInTouchMode="true"
|
||||||
|
android:keyBackground="@drawable/selector_presskb"
|
||||||
|
android:keyTextColor="@color/white"
|
||||||
|
android:keyTextSize="0sp"
|
||||||
|
android:labelTextSize="12sp"
|
||||||
|
android:paddingStart="5dp"
|
||||||
|
android:paddingTop="5dp"
|
||||||
|
android:paddingEnd="5dp"
|
||||||
|
android:paddingBottom="5dp" />
|
||||||
|
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
19
app/src/main/res/layout/item_nav_category.xml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingBottom="16dp"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:background="?android:attr/selectableItemBackground">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/nav_category_name"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/text_color"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:gravity="center" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
BIN
app/src/main/res/mipmap-xxxhdpi/dia_close.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/dia_feedback.png
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/dia_toright.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/dia_version.png
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/home_tocollection.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_back.png
Normal file
|
After Width: | Height: | Size: 341 B |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_star.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_star_outline.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_top.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/kbpre_already.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/kbpre_download.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/layout_emptycollect.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/logo.png
Normal file
|
After Width: | Height: | Size: 327 KiB |
16
app/src/main/res/values-night/themes.xml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="MyKeyBoard" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||||
|
<!-- Primary brand color. -->
|
||||||
|
<item name="colorPrimary">@color/purple_200</item>
|
||||||
|
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||||
|
<item name="colorOnPrimary">@color/black</item>
|
||||||
|
<!-- Secondary brand color. -->
|
||||||
|
<item name="colorSecondary">@color/teal_200</item>
|
||||||
|
<item name="colorSecondaryVariant">@color/teal_200</item>
|
||||||
|
<item name="colorOnSecondary">@color/black</item>
|
||||||
|
<!-- Status bar color. -->
|
||||||
|
<item name="android:statusBarColor">@color/transparent</item>
|
||||||
|
<!-- Customize your theme here. -->
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
6
app/src/main/res/values/attrs.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<declare-styleable name="MyTV">
|
||||||
|
<attr name="apply_font" format="boolean"/>
|
||||||
|
</declare-styleable>
|
||||||
|
</resources>
|
||||||
49
app/src/main/res/values/colors.xml
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="purple_200">#FFBB86FC</color>
|
||||||
|
<color name="purple_500">#FF6200EE</color>
|
||||||
|
<color name="purple_700">#FF3700B3</color>
|
||||||
|
<color name="teal_200">#FF03DAC5</color>
|
||||||
|
<color name="teal_700">#FF018786</color>
|
||||||
|
<color name="black">#FF000000</color>
|
||||||
|
<color name="white">#FFFFFFFF</color>
|
||||||
|
<color name="apply_step_false">#1A000000</color>
|
||||||
|
<color name="key_bg_press">#ffbbbbbb</color>
|
||||||
|
<color name="step_true">#666666</color>
|
||||||
|
<color name="transparent">#00000000</color>
|
||||||
|
<color name="bg_loading">#80000000</color>
|
||||||
|
<color name="color_74CBFF">#74CBFF</color>
|
||||||
|
|
||||||
|
<color name="title_color">#2C3E50</color> <!-- 深蓝灰色标题 -->
|
||||||
|
<color name="text_color">#34495E</color> <!-- 蓝灰色文字 -->
|
||||||
|
|
||||||
|
<!-- 首页色系 -->
|
||||||
|
<color name="theme_background">#E3F2FD</color> <!-- 浅蓝背景(蓝色更明显) -->
|
||||||
|
<color name="sidebar_bg">#BBDEFB</color> <!-- 中蓝侧边栏(明显蓝色调) -->
|
||||||
|
<color name="text_color_selected">#E74C3C</color> <!-- 红色选中文字 -->
|
||||||
|
<color name="nav_selected_bg">#26E74C3C</color> <!-- 淡红选中背景 -->
|
||||||
|
<color name="ic_bg">#F1948A</color> <!-- 极淡蓝灰图标背景 -->
|
||||||
|
<color name="underline_color">#2196F3</color> <!-- 亮蓝色下划线(更鲜艳) -->
|
||||||
|
<color name="subtext_color">#E74C3C</color> <!-- 红色辅助文字(点缀) -->
|
||||||
|
|
||||||
|
<!-- 启动页 -->
|
||||||
|
<color name="launch_bg">#3498DB</color> <!-- 蓝色启动背景 -->
|
||||||
|
<color name="launch_text">#FFFFFF</color> <!-- 白色主文字 -->
|
||||||
|
<color name="launch_subtext">#E8EDF2</color> <!-- 浅蓝灰辅助文字 -->
|
||||||
|
<color name="launch_progress_bg">#D6EAF8</color> <!-- 淡蓝进度背景 -->
|
||||||
|
<color name="launch_progress">#E74C3C</color> <!-- 红色进度条(点缀) -->
|
||||||
|
|
||||||
|
<!-- 卡片 -->
|
||||||
|
<color name="card_bg">#FFFFFF</color> <!-- 白色卡片 -->
|
||||||
|
<color name="card_text">#34495E</color> <!-- 蓝灰色卡片文字 -->
|
||||||
|
<color name="card_icon_tint">#F1948A</color> <!-- 蓝色图标 -->
|
||||||
|
<color name="card_divider">#D6EAF8</color> <!-- 淡蓝分割线 -->
|
||||||
|
<color name="card_title_color">#2980B9</color> <!-- 深蓝色标题 -->
|
||||||
|
|
||||||
|
<!-- 按钮 -->
|
||||||
|
<color name="button_cancel_bg">#F5F5F5</color> <!-- 浅灰取消按钮 -->
|
||||||
|
<color name="button_rate_bg">#F1948A</color> <!-- 蓝色评价按钮 -->
|
||||||
|
<color name="button_rate_bg_disabled">#BDC3C7</color> <!-- 灰蓝禁用状态 -->
|
||||||
|
<color name="card_button_text">#2C3E50</color> <!-- 深蓝灰按钮文字 -->
|
||||||
|
|
||||||
|
</resources>
|
||||||
32
app/src/main/res/values/dimen.xml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<dimen name="text_size">15sp</dimen>
|
||||||
|
<dimen name="key_height">48dp</dimen>
|
||||||
|
<dimen name="keyboard_vertical_gap">5dp</dimen>
|
||||||
|
|
||||||
|
<!-- Logo 尺寸 -->
|
||||||
|
<dimen name="logo_size">100dp</dimen>
|
||||||
|
|
||||||
|
<!-- 文本尺寸 -->
|
||||||
|
<dimen name="app_name_text_size">20sp</dimen>
|
||||||
|
<dimen name="option_text_size">18sp</dimen>
|
||||||
|
<dimen name="version_value_text_size">16sp</dimen>
|
||||||
|
|
||||||
|
<!-- 卡片样式 -->
|
||||||
|
<dimen name="card_corner_radius">30dp</dimen>
|
||||||
|
<dimen name="card_elevation">4dp</dimen>
|
||||||
|
<dimen name="card_padding">16dp</dimen>
|
||||||
|
|
||||||
|
<!-- 选项项样式 -->
|
||||||
|
<dimen name="option_item_height">60dp</dimen>
|
||||||
|
<dimen name="option_icon_size">38dp</dimen>
|
||||||
|
<dimen name="option_arrow_size">28dp</dimen>
|
||||||
|
<dimen name="option_padding_horizontal">16dp</dimen>
|
||||||
|
<dimen name="option_padding_vertical">10dp</dimen>
|
||||||
|
<dimen name="option_text_margin_start">14dp</dimen>
|
||||||
|
|
||||||
|
<!-- 分割线 -->
|
||||||
|
<dimen name="divider_height">1dp</dimen>
|
||||||
|
<dimen name="divider_margin_vertical">8dp</dimen>
|
||||||
|
</resources>
|
||||||
19
app/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name">RainbowBoard</string>
|
||||||
|
<string name="download_apply">Download & Apply</string>
|
||||||
|
<string name="apply">Apply</string>
|
||||||
|
<string name="recommend_name">recommend</string>
|
||||||
|
<string name="recommend">Recommendation</string>
|
||||||
|
<string name="open_str">Activate RainbowBoard to enable more functions!</string>
|
||||||
|
<string name="step_1">Step 1:Select</string>
|
||||||
|
<string name="step_2">Step 2:Enable</string>
|
||||||
|
<string name="theme_application_successful">Theme application successful</string>
|
||||||
|
<string name="download_fail">Download failed, please try again</string>
|
||||||
|
<string name="et_hint">Type a Message</string>
|
||||||
|
|
||||||
|
<string name="rate_us">Rate us</string>
|
||||||
|
<string name="version">Version</string>
|
||||||
|
<string name="empty_collection_title">No Favorites Yet</string>
|
||||||
|
<string name="empty_collection_content">Tap the heart icon on images to add them to your collection</string>
|
||||||
|
<string name="more_suggestions">Discover more</string>
|
||||||
|
</resources>
|
||||||
44
app/src/main/res/values/themes.xml
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="MyKeyBoard" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||||
|
<!-- Primary brand color. -->
|
||||||
|
<item name="colorPrimary">@color/purple_500</item>
|
||||||
|
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||||
|
<item name="colorOnPrimary">@color/white</item>
|
||||||
|
<!-- Secondary brand color. -->
|
||||||
|
<item name="colorSecondary">@color/teal_200</item>
|
||||||
|
<item name="colorSecondaryVariant">@color/teal_700</item>
|
||||||
|
<item name="colorOnSecondary">@color/black</item>
|
||||||
|
<!-- Status bar color. -->
|
||||||
|
<item name="android:statusBarColor">@color/transparent</item>
|
||||||
|
<!-- Customize your theme here. -->
|
||||||
|
</style>
|
||||||
|
<style name="Base.Theme.PaintingHelper" parent="Theme.Material3.Light.NoActionBar">
|
||||||
|
<item name="android:statusBarColor">@color/launch_bg</item>
|
||||||
|
<item name="materialButtonStyle">@style/Widget.AppCompat.Button</item>
|
||||||
|
<item name="materialButtonOutlinedStyle">@style/Widget.AppCompat.Button.Borderless</item>
|
||||||
|
</style>
|
||||||
|
<style name="Theme.PaintingHelper" parent="Base.Theme.PaintingHelper" />
|
||||||
|
|
||||||
|
<declare-styleable name="CustomInputView">
|
||||||
|
<attr format="integer" name="text_size_label"/>
|
||||||
|
<attr format="integer" name="text_size_key"/>
|
||||||
|
<attr format="color" name="text_color_key"/>
|
||||||
|
<attr format="color" name="background_color_key"/>
|
||||||
|
<attr format="color" name="icon_color_key"/>
|
||||||
|
<attr format="color" name="text_color_special"/>
|
||||||
|
<attr format="color" name="text_color_done"/>
|
||||||
|
<attr format="color" name="text_color_none"/>
|
||||||
|
<attr format="reference" name="drawable_delete"/>
|
||||||
|
<attr format="reference" name="drawable_capital"/>
|
||||||
|
<attr format="reference" name="drawable_capital_lock"/>
|
||||||
|
<attr format="reference" name="drawable_cancel"/>
|
||||||
|
<attr format="reference" name="drawable_space"/>
|
||||||
|
<attr format="reference" name="drawable_special"/>
|
||||||
|
<attr format="reference" name="drawable_done"/>
|
||||||
|
<attr format="reference" name="drawable_none"/>
|
||||||
|
</declare-styleable>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</resources>
|
||||||
75
app/src/main/res/values/value.xml
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<declare-styleable name="K_Keyboard_Key">
|
||||||
|
|
||||||
|
<attr name="android:codes"/>
|
||||||
|
|
||||||
|
<attr name="android:popupKeyboard"/>
|
||||||
|
|
||||||
|
<attr name="android:popupCharacters"/>
|
||||||
|
|
||||||
|
<attr name="android:keyEdgeFlags"/>
|
||||||
|
|
||||||
|
<attr name="android:isModifier"/>
|
||||||
|
|
||||||
|
<attr name="android:isSticky"/>
|
||||||
|
|
||||||
|
<attr name="android:isRepeatable"/>
|
||||||
|
|
||||||
|
<attr name="android:iconPreview"/>
|
||||||
|
|
||||||
|
<attr name="android:keyOutputText"/>
|
||||||
|
|
||||||
|
<attr name="android:keyLabel"/>
|
||||||
|
|
||||||
|
<attr name="android:keyIcon"/>
|
||||||
|
|
||||||
|
<attr name="android:keyboardMode"/>
|
||||||
|
</declare-styleable>
|
||||||
|
<declare-styleable name="Kil_Keyboard_Row">
|
||||||
|
|
||||||
|
<attr name="android:rowEdgeFlags"/>
|
||||||
|
|
||||||
|
<attr name="android:keyboardMode"/>
|
||||||
|
</declare-styleable>
|
||||||
|
<declare-styleable name="My_Keyboard_view">
|
||||||
|
|
||||||
|
<attr name="android:keyWidth"/>
|
||||||
|
|
||||||
|
<attr name="android:keyHeight"/>
|
||||||
|
|
||||||
|
<attr name="android:horizontalGap"/>
|
||||||
|
|
||||||
|
<attr name="android:verticalGap"/>
|
||||||
|
</declare-styleable>
|
||||||
|
<declare-styleable name="My_KeyboardView">
|
||||||
|
|
||||||
|
<attr name="android:keyBackground"/>
|
||||||
|
|
||||||
|
<attr name="android:keyTextSize"/>
|
||||||
|
|
||||||
|
<attr name="android:labelTextSize"/>
|
||||||
|
|
||||||
|
<attr name="android:keyTextColor"/>
|
||||||
|
|
||||||
|
<attr name="android:keyPreviewLayout"/>
|
||||||
|
|
||||||
|
<attr name="android:keyPreviewOffset"/>
|
||||||
|
|
||||||
|
<attr name="android:keyPreviewHeight"/>
|
||||||
|
|
||||||
|
<attr name="android:verticalCorrection"/>
|
||||||
|
|
||||||
|
<attr name="android:popupLayout"/>
|
||||||
|
|
||||||
|
<attr name="android:shadowColor"/>
|
||||||
|
|
||||||
|
<attr name="android:shadowRadius"/>
|
||||||
|
</declare-styleable>
|
||||||
|
<declare-styleable name="IM_KeyboardViewPreviewState">
|
||||||
|
|
||||||
|
<attr name="android:state_long_pressable"/>
|
||||||
|
</declare-styleable>
|
||||||
|
|
||||||
|
</resources>
|
||||||
8
app/src/main/res/xml/keyborad_xml.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:label="@string/app_name"
|
||||||
|
android:imeSubtypeLocale="en_US"
|
||||||
|
android:imeSubtypeMode="keyboard" />
|
||||||
|
|
||||||
|
</input-method>
|
||||||
6
app/src/main/res/xml/net.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<network-security-config xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<domain-config cleartextTrafficPermitted="true">
|
||||||
|
<domain tools:ignore="NetworkSecurityConfig">mobile-server.lux-ad.com</domain>
|
||||||
|
</domain-config>
|
||||||
|
</network-security-config>
|
||||||
142
app/src/main/res/xml/xml_one.xml
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:horizontalGap="1%p"
|
||||||
|
android:keyWidth="9%p"
|
||||||
|
android:keyHeight="@dimen/key_height"
|
||||||
|
android:verticalGap="@dimen/keyboard_vertical_gap">
|
||||||
|
<Row>
|
||||||
|
<Key
|
||||||
|
android:codes="113"
|
||||||
|
android:keyLabel="q"
|
||||||
|
android:keyWidth="8.9%p"
|
||||||
|
android:keyEdgeFlags="left" />
|
||||||
|
<Key
|
||||||
|
android:codes="119"
|
||||||
|
android:keyLabel="w"
|
||||||
|
android:keyWidth="8.9%p"/>
|
||||||
|
<Key
|
||||||
|
android:codes="101"
|
||||||
|
android:keyLabel="e"
|
||||||
|
android:keyWidth="8.9%p" />
|
||||||
|
<Key
|
||||||
|
android:codes="114"
|
||||||
|
android:keyWidth="8.9%p"
|
||||||
|
android:keyLabel="r" />
|
||||||
|
<Key
|
||||||
|
android:codes="116"
|
||||||
|
android:keyLabel="t"
|
||||||
|
android:keyWidth="8.9%p"/>
|
||||||
|
<Key
|
||||||
|
android:codes="121"
|
||||||
|
android:keyLabel="y"
|
||||||
|
android:keyWidth="8.9%p"/>
|
||||||
|
<Key
|
||||||
|
android:codes="117"
|
||||||
|
android:keyLabel="u"
|
||||||
|
android:keyWidth="8.9%p"/>
|
||||||
|
<Key
|
||||||
|
android:codes="105"
|
||||||
|
android:keyLabel="i"
|
||||||
|
android:keyWidth="8.9%p"/>
|
||||||
|
<Key
|
||||||
|
android:codes="111"
|
||||||
|
android:keyLabel="o"
|
||||||
|
android:keyWidth="8.9%p"/>
|
||||||
|
<Key
|
||||||
|
android:codes="112"
|
||||||
|
android:keyLabel="p"
|
||||||
|
android:keyWidth="8.9%p"
|
||||||
|
android:keyEdgeFlags="right" />
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<Row>
|
||||||
|
<Key
|
||||||
|
android:codes="97"
|
||||||
|
android:keyLabel="a"
|
||||||
|
android:horizontalGap="5.5%p"
|
||||||
|
android:keyEdgeFlags="left" />
|
||||||
|
<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"
|
||||||
|
android:keyEdgeFlags="right" />
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<Row>
|
||||||
|
<Key
|
||||||
|
android:codes="-1"
|
||||||
|
android:keyWidth="13.5%p"
|
||||||
|
android:isModifier="true"
|
||||||
|
android:isSticky="true"
|
||||||
|
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" />
|
||||||
|
<Key
|
||||||
|
android:codes="-5"
|
||||||
|
android:keyWidth="13.5%p"
|
||||||
|
android:isRepeatable="true" />
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<Row android:rowEdgeFlags="bottom">
|
||||||
|
<Key
|
||||||
|
android:codes="-2"
|
||||||
|
android:keyLabel="\?123"
|
||||||
|
android:keyWidth="17%p" />
|
||||||
|
<Key
|
||||||
|
android:codes="46"
|
||||||
|
android:keyLabel="."
|
||||||
|
android:keyWidth="12%p" />
|
||||||
|
<Key
|
||||||
|
android:codes="32"
|
||||||
|
android:keyLabel="English"
|
||||||
|
android:keyWidth="36%p"
|
||||||
|
android:isRepeatable="false"/>
|
||||||
|
<Key
|
||||||
|
android:codes="63"
|
||||||
|
android:keyLabel="\?"
|
||||||
|
android:keyWidth="12%p" />
|
||||||
|
<Key
|
||||||
|
android:codes="-4"
|
||||||
|
android:keyWidth="17%p"
|
||||||
|
android:keyEdgeFlags="right" />
|
||||||
|
</Row>
|
||||||
|
</Keyboard>
|
||||||
147
app/src/main/res/xml/xml_three.xml
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:horizontalGap="1%p"
|
||||||
|
android:keyWidth="9%p"
|
||||||
|
android:keyHeight="@dimen/key_height"
|
||||||
|
android:verticalGap="@dimen/keyboard_vertical_gap">
|
||||||
|
<Row>
|
||||||
|
<Key
|
||||||
|
android:codes="91"
|
||||||
|
android:keyLabel="["
|
||||||
|
android:horizontalGap="1%p"
|
||||||
|
android:keyWidth="8.9%p"
|
||||||
|
android:keyEdgeFlags="left" />
|
||||||
|
<Key
|
||||||
|
android:codes="93"
|
||||||
|
android:keyLabel="]"
|
||||||
|
android:keyWidth="8.9%p" />
|
||||||
|
<Key
|
||||||
|
android:codes="123"
|
||||||
|
android:keyLabel="{"
|
||||||
|
android:keyWidth="8.9%p" />
|
||||||
|
<Key
|
||||||
|
android:codes="125"
|
||||||
|
android:keyLabel="}"
|
||||||
|
android:keyWidth="8.9%p" />
|
||||||
|
<Key
|
||||||
|
android:codes="35"
|
||||||
|
android:keyLabel="#"
|
||||||
|
android:keyWidth="8.9%p" />
|
||||||
|
<Key
|
||||||
|
android:codes="37"
|
||||||
|
android:keyLabel="%"
|
||||||
|
android:keyWidth="8.9%p"
|
||||||
|
/>
|
||||||
|
<Key
|
||||||
|
android:codes="94"
|
||||||
|
android:keyLabel="^"
|
||||||
|
android:keyWidth="8.9%p" />
|
||||||
|
<Key
|
||||||
|
android:codes="96"
|
||||||
|
android:keyLabel="`"
|
||||||
|
android:keyWidth="8.9%p" />
|
||||||
|
<Key
|
||||||
|
android:codes="43"
|
||||||
|
android:keyLabel="+"
|
||||||
|
android:keyWidth="8.9%p" />
|
||||||
|
<Key
|
||||||
|
android:codes="61"
|
||||||
|
android:keyLabel="="
|
||||||
|
android:keyWidth="8.9%p"
|
||||||
|
android:keyEdgeFlags="right" />
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<Row>
|
||||||
|
<Key
|
||||||
|
android:codes="126"
|
||||||
|
android:keyLabel="~"
|
||||||
|
android:horizontalGap="5.5%p"
|
||||||
|
android:keyEdgeFlags="left" />
|
||||||
|
<Key
|
||||||
|
android:codes="92"
|
||||||
|
android:keyLabel="\\" />
|
||||||
|
<Key
|
||||||
|
android:codes="124"
|
||||||
|
android:keyLabel="|" />
|
||||||
|
<Key
|
||||||
|
android:codes="60"
|
||||||
|
android:keyLabel="<" />
|
||||||
|
<Key
|
||||||
|
android:codes="62"
|
||||||
|
android:keyLabel=">" />
|
||||||
|
<Key
|
||||||
|
android:codes="165"
|
||||||
|
android:keyLabel="¥" />
|
||||||
|
<Key
|
||||||
|
android:codes="8364"
|
||||||
|
android:keyLabel="€" />
|
||||||
|
<Key
|
||||||
|
android:codes="163"
|
||||||
|
android:keyLabel="£" />
|
||||||
|
<Key
|
||||||
|
android:codes="8356"
|
||||||
|
android:keyLabel="₤"
|
||||||
|
android:keyEdgeFlags="right" />
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
|
||||||
|
<Row>
|
||||||
|
<Key
|
||||||
|
android:codes="-101"
|
||||||
|
android:keyLabel="123"
|
||||||
|
android:keyWidth="13.5%p"
|
||||||
|
android:isModifier="true"
|
||||||
|
android:isSticky="true"
|
||||||
|
android:keyEdgeFlags="left" />
|
||||||
|
<Key
|
||||||
|
android:codes="39"
|
||||||
|
android:keyLabel="'" />
|
||||||
|
<Key
|
||||||
|
android:codes="47"
|
||||||
|
android:keyLabel="/" />
|
||||||
|
<Key
|
||||||
|
android:codes="45"
|
||||||
|
android:keyLabel="-" />
|
||||||
|
<Key
|
||||||
|
android:codes="95"
|
||||||
|
android:keyLabel="_" />
|
||||||
|
<Key
|
||||||
|
android:codes="58"
|
||||||
|
android:keyLabel=":" />
|
||||||
|
<Key
|
||||||
|
android:codes="59"
|
||||||
|
android:keyLabel=";" />
|
||||||
|
<Key
|
||||||
|
android:codes="44"
|
||||||
|
android:keyLabel="," />
|
||||||
|
<Key
|
||||||
|
android:codes="-5"
|
||||||
|
android:keyWidth="13.5%p"
|
||||||
|
android:isRepeatable="true" />
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
|
||||||
|
<Row android:rowEdgeFlags="bottom">
|
||||||
|
<Key
|
||||||
|
android:codes="-102"
|
||||||
|
android:keyWidth="17%p"
|
||||||
|
android:keyLabel="Back" />
|
||||||
|
<Key
|
||||||
|
android:codes="46"
|
||||||
|
android:keyWidth="12%p"
|
||||||
|
android:keyLabel="." />
|
||||||
|
<Key
|
||||||
|
android:codes="32"
|
||||||
|
android:isRepeatable="false"
|
||||||
|
android:keyWidth="36%p"
|
||||||
|
android:keyLabel="English" />
|
||||||
|
<Key
|
||||||
|
android:codes="63"
|
||||||
|
android:keyWidth="12%p"
|
||||||
|
android:keyLabel="\?" />
|
||||||
|
<Key
|
||||||
|
android:codes="-3"
|
||||||
|
android:keyWidth="17%p"
|
||||||
|
android:keyEdgeFlags="right" />
|
||||||
|
</Row>
|
||||||
|
</Keyboard>
|
||||||
147
app/src/main/res/xml/xml_two.xml
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:horizontalGap="1%p"
|
||||||
|
android:keyWidth="9%p"
|
||||||
|
android:keyHeight="@dimen/key_height"
|
||||||
|
android:verticalGap="@dimen/keyboard_vertical_gap">
|
||||||
|
<Row>
|
||||||
|
<Key
|
||||||
|
android:codes="49"
|
||||||
|
android:keyLabel="1"
|
||||||
|
android:horizontalGap="1%p"
|
||||||
|
android:keyWidth="8.9%p"
|
||||||
|
android:keyEdgeFlags="left" />
|
||||||
|
<Key
|
||||||
|
android:codes="50"
|
||||||
|
android:keyLabel="2"
|
||||||
|
android:keyWidth="8.9%p" />
|
||||||
|
<Key
|
||||||
|
android:codes="51"
|
||||||
|
android:keyLabel="3"
|
||||||
|
android:keyWidth="8.9%p" />
|
||||||
|
<Key
|
||||||
|
android:codes="52"
|
||||||
|
android:keyLabel="4"
|
||||||
|
android:keyWidth="8.9%p" />
|
||||||
|
<Key
|
||||||
|
android:codes="53"
|
||||||
|
android:keyLabel="5"
|
||||||
|
android:keyWidth="8.9%p" />
|
||||||
|
<Key
|
||||||
|
android:codes="54"
|
||||||
|
android:keyLabel="6"
|
||||||
|
android:keyWidth="8.9%p"
|
||||||
|
/>
|
||||||
|
<Key
|
||||||
|
android:codes="55"
|
||||||
|
android:keyLabel="7"
|
||||||
|
android:keyWidth="8.9%p" />
|
||||||
|
<Key
|
||||||
|
android:codes="56"
|
||||||
|
android:keyLabel="8"
|
||||||
|
android:keyWidth="8.9%p" />
|
||||||
|
<Key
|
||||||
|
android:codes="57"
|
||||||
|
android:keyLabel="9"
|
||||||
|
android:keyWidth="8.9%p" />
|
||||||
|
<Key
|
||||||
|
android:codes="48"
|
||||||
|
android:keyLabel="0"
|
||||||
|
android:keyWidth="8.9%p"
|
||||||
|
android:keyEdgeFlags="right" />
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<Row>
|
||||||
|
<Key
|
||||||
|
android:codes="33"
|
||||||
|
android:keyLabel="!"
|
||||||
|
android:horizontalGap="5.5%p"
|
||||||
|
android:keyEdgeFlags="left" />
|
||||||
|
<Key
|
||||||
|
android:codes="64"
|
||||||
|
android:keyLabel="\@" />
|
||||||
|
<Key
|
||||||
|
android:codes="35"
|
||||||
|
android:keyLabel="#" />
|
||||||
|
<Key
|
||||||
|
android:codes="36"
|
||||||
|
android:keyLabel="\$" />
|
||||||
|
<Key
|
||||||
|
android:codes="37"
|
||||||
|
android:keyLabel="%" />
|
||||||
|
<Key
|
||||||
|
android:codes="38"
|
||||||
|
android:keyLabel="&" />
|
||||||
|
<Key
|
||||||
|
android:codes="42"
|
||||||
|
android:keyLabel="*" />
|
||||||
|
<Key
|
||||||
|
android:codes="40"
|
||||||
|
android:keyLabel="(" />
|
||||||
|
<Key
|
||||||
|
android:codes="41"
|
||||||
|
android:keyLabel=")"
|
||||||
|
android:keyEdgeFlags="right" />
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
|
||||||
|
<Row>
|
||||||
|
<Key
|
||||||
|
android:codes="-103"
|
||||||
|
android:keyLabel="More"
|
||||||
|
android:keyWidth="13.5%p"
|
||||||
|
android:isModifier="true"
|
||||||
|
android:isSticky="true"
|
||||||
|
android:keyEdgeFlags="left" />
|
||||||
|
<Key
|
||||||
|
android:codes="39"
|
||||||
|
android:keyLabel="'" />
|
||||||
|
<Key
|
||||||
|
android:codes="47"
|
||||||
|
android:keyLabel="/" />
|
||||||
|
<Key
|
||||||
|
android:codes="45"
|
||||||
|
android:keyLabel="-" />
|
||||||
|
<Key
|
||||||
|
android:codes="95"
|
||||||
|
android:keyLabel="_" />
|
||||||
|
<Key
|
||||||
|
android:codes="58"
|
||||||
|
android:keyLabel=":" />
|
||||||
|
<Key
|
||||||
|
android:codes="59"
|
||||||
|
android:keyLabel=";" />
|
||||||
|
<Key
|
||||||
|
android:codes="44"
|
||||||
|
android:keyLabel="," />
|
||||||
|
<Key
|
||||||
|
android:codes="-5"
|
||||||
|
android:keyWidth="13.5%p"
|
||||||
|
android:isRepeatable="true" />
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
|
||||||
|
<Row android:rowEdgeFlags="bottom">
|
||||||
|
<Key
|
||||||
|
android:codes="-102"
|
||||||
|
android:keyWidth="17%p"
|
||||||
|
android:keyLabel="Back" />
|
||||||
|
<Key
|
||||||
|
android:codes="46"
|
||||||
|
android:keyWidth="12%p"
|
||||||
|
android:keyLabel="." />
|
||||||
|
<Key
|
||||||
|
android:codes="32"
|
||||||
|
android:isRepeatable="false"
|
||||||
|
android:keyWidth="36%p"
|
||||||
|
android:keyLabel="English" />
|
||||||
|
<Key
|
||||||
|
android:codes="63"
|
||||||
|
android:keyWidth="12%p"
|
||||||
|
android:keyLabel="\?" />
|
||||||
|
<Key
|
||||||
|
android:codes="-3"
|
||||||
|
android:keyWidth="17%p"
|
||||||
|
android:keyEdgeFlags="right" />
|
||||||
|
</Row>
|
||||||
|
</Keyboard>
|
||||||