接入tradplus广告
This commit is contained in:
commit
dbaff1b05e
18
.gitignore
vendored
Normal file
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
1
app/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build
|
||||
BIN
app/BreezeBoard
Normal file
BIN
app/BreezeBoard
Normal file
Binary file not shown.
173
app/build.gradle.kts
Normal file
173
app/build.gradle.kts
Normal file
@ -0,0 +1,173 @@
|
||||
|
||||
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.app.input.breeze.board"
|
||||
compileSdk = 36
|
||||
defaultConfig {
|
||||
applicationId = "com.app.input.breeze.board"
|
||||
minSdk = 24
|
||||
targetSdk = 36
|
||||
versionCode = 2
|
||||
versionName = "2.0"
|
||||
setProperty(
|
||||
"archivesBaseName",
|
||||
"BreezeBoard_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")
|
||||
|
||||
//upload
|
||||
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
29
app/google-services.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"project_info": {
|
||||
"project_number": "813677613184",
|
||||
"project_id": "breezeboard-d7ee6",
|
||||
"storage_bucket": "breezeboard-d7ee6.firebasestorage.app"
|
||||
},
|
||||
"client": [
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:813677613184:android:e5d0616a75c7df5df59665",
|
||||
"android_client_info": {
|
||||
"package_name": "com.app.input.breeze.board"
|
||||
}
|
||||
},
|
||||
"oauth_client": [],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": "AIzaSyBO3XszZGdKKi0ICF_Kwt6lc2qdF3s0ayY"
|
||||
}
|
||||
],
|
||||
"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/TradPlusLibrary_01_04_12_20-release.aar
Normal file
Binary file not shown.
BIN
app/libs/UpLoadLibrary_12_03_15_13-release.aar
Normal file
BIN
app/libs/UpLoadLibrary_12_03_15_13-release.aar
Normal file
Binary file not shown.
45
app/proguard-rules.pro
vendored
Normal file
45
app/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
|
||||
|
||||
# 保持 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.** { *; }
|
||||
|
||||
Binary file not shown.
Binary file not shown.
37
app/release/output-metadata.json
Normal file
37
app/release/output-metadata.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"version": 3,
|
||||
"artifactType": {
|
||||
"type": "APK",
|
||||
"kind": "Directory"
|
||||
},
|
||||
"applicationId": "com.app.input.breeze.board",
|
||||
"variantName": "release",
|
||||
"elements": [
|
||||
{
|
||||
"type": "SINGLE",
|
||||
"filters": [],
|
||||
"attributes": [],
|
||||
"versionCode": 2,
|
||||
"versionName": "2.0",
|
||||
"outputFile": "BreezeBoard_V2.0(2)_01_06_11_24-release.apk"
|
||||
}
|
||||
],
|
||||
"elementType": "File",
|
||||
"baselineProfiles": [
|
||||
{
|
||||
"minApi": 28,
|
||||
"maxApi": 30,
|
||||
"baselineProfiles": [
|
||||
"baselineProfiles/1/BreezeBoard_V2.0(2)_01_06_11_24-release.dm"
|
||||
]
|
||||
},
|
||||
{
|
||||
"minApi": 31,
|
||||
"maxApi": 2147483647,
|
||||
"baselineProfiles": [
|
||||
"baselineProfiles/0/BreezeBoard_V2.0(2)_01_06_11_24-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)
|
||||
|
||||
}
|
||||
}
|
||||
72
app/src/main/AndroidManifest.xml
Normal file
72
app/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,72 @@
|
||||
<?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:networkSecurityConfig="@xml/net"
|
||||
android:roundIcon="@mipmap/logo"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/MyKeyBoard"
|
||||
tools:replace="android:networkSecurityConfig"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".uiactivity.ThemeListActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".uiactivity.SplashActivity"
|
||||
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.MainActivity"
|
||||
android:exported="false"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name=".uiactivity.ThemeDetailActivity"
|
||||
android:exported="false"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name=".uiactivity.PreviewActivity"
|
||||
android:exported="false"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name=".uiactivity.CategoryScreen"
|
||||
android:exported="false"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name=".uiactivity.FavoriteThemeActivity"
|
||||
android:exported="false"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name=".uiactivity.SettingsScreen"
|
||||
android:exported="false"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<service
|
||||
android:name=".keyboardhelper.BreezeInputMethodService"
|
||||
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>
|
||||
BIN
app/src/main/assets/my_font.ttf
Normal file
BIN
app/src/main/assets/my_font.ttf
Normal file
Binary file not shown.
21607
app/src/main/assets/new_res.json
Normal file
21607
app/src/main/assets/new_res.json
Normal file
File diff suppressed because it is too large
Load Diff
123
app/src/main/java/com/app/input/breeze/board/App.kt
Normal file
123
app/src/main/java/com/app/input/breeze/board/App.kt
Normal file
@ -0,0 +1,123 @@
|
||||
package com.app.input.breeze.board
|
||||
|
||||
//import com.pretty.keyboard.theme.keyboard.helper.ObjectBox
|
||||
import android.app.Application
|
||||
import android.graphics.Typeface
|
||||
import com.app.input.breeze.board.bean.CategoryWrapper
|
||||
import com.app.input.breeze.board.bean.KeyboardTheme
|
||||
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<CategoryWrapper>
|
||||
|
||||
const val TAG = "-----------------"
|
||||
var defaultFont: Typeface? = null
|
||||
const val DB_VERSION = 2
|
||||
const val DB_NAME = "db_name"
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
appInstance = this
|
||||
UpLoadManager.init(this, TAG, { s: String?, s2: String? -> null })
|
||||
defaultFont = Typeface.createFromAsset(assets, "my_font.ttf")
|
||||
dealFile()
|
||||
}
|
||||
private fun dealFile() {
|
||||
val openFile = appInstance.assets.open("new_res.json")
|
||||
val jsonString = getJsonString(openFile)
|
||||
if (jsonString != null) {
|
||||
resolveJsonString(jsonString)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun resolveJsonString(string: String) {
|
||||
val jsonData = JSONArray(string)
|
||||
var dataList: MutableList<CategoryWrapper> = mutableListOf()
|
||||
for (i in 0 until jsonData.length()) {
|
||||
jsonData.getJSONObject(i).run {
|
||||
val pName = getString("parent_name")
|
||||
val listArray = getJSONArray("keyboard_list")
|
||||
var KeyboardThemeList: MutableList<KeyboardTheme> = 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 {
|
||||
|
||||
}
|
||||
KeyboardThemeList.add(
|
||||
KeyboardTheme().apply {
|
||||
setImgPath(imgPath)
|
||||
setZipPath(zipPath)
|
||||
setTitleName(title)
|
||||
setImgGif(imgGif)
|
||||
thumbUrl = thUrl
|
||||
thumbGif = thGif
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
val shuffled = KeyboardThemeList.shuffled()
|
||||
val dataCategoryWrapper = CategoryWrapper()
|
||||
.apply {
|
||||
parentName = pName
|
||||
keyboardList = shuffled
|
||||
}
|
||||
dataList.add(dataCategoryWrapper)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
updateDataList(dataList)
|
||||
|
||||
}
|
||||
|
||||
private fun updateDataList(mainList: MutableList<CategoryWrapper>) {
|
||||
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) {
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package com.app.input.breeze.board.bean;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CategoryWrapper {
|
||||
private String parentName;
|
||||
|
||||
private List<KeyboardTheme> keyboardList;
|
||||
|
||||
public String getParentName() {
|
||||
|
||||
return parentName;
|
||||
}
|
||||
|
||||
public List<KeyboardTheme> getKeyboardList() {
|
||||
|
||||
return keyboardList;
|
||||
}
|
||||
|
||||
public void setParentName(String name) {
|
||||
this.parentName = name;
|
||||
}
|
||||
|
||||
public void setKeyboardList(List<KeyboardTheme> keyboardList) {
|
||||
this.keyboardList = keyboardList;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,87 @@
|
||||
package com.app.input.breeze.board.bean;
|
||||
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.PrimaryKey;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Entity
|
||||
public class KeyboardTheme 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,6 @@
|
||||
package com.app.input.breeze.board.callback
|
||||
|
||||
interface ItemClickListener {
|
||||
|
||||
fun OnItemClickListener( )
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
package com.app.input.breeze.board.callback
|
||||
|
||||
import java.io.File
|
||||
|
||||
interface KeyboardApplyCallback {
|
||||
|
||||
fun OnApplySkinListener(fileList: List<File?>?)
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
package com.app.input.breeze.board.callback
|
||||
|
||||
import com.app.input.breeze.board.bean.KeyboardTheme
|
||||
|
||||
interface RemoveFavoriteCallback {
|
||||
|
||||
fun OnRemoveLike(data: KeyboardTheme)
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
package com.app.input.breeze.board.callback
|
||||
|
||||
interface ViewAllClickListener {
|
||||
|
||||
fun OnClickSeeAll(name: String)
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package com.app.input.breeze.board.database
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import com.app.input.breeze.board.App
|
||||
import com.app.input.breeze.board.bean.KeyboardTheme
|
||||
|
||||
|
||||
@Database(
|
||||
entities = [KeyboardTheme::class],
|
||||
version = App.Companion.DB_VERSION,
|
||||
exportSchema = false
|
||||
)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
abstract fun favoriteThemeDao(): FavoriteThemeDao
|
||||
|
||||
|
||||
|
||||
companion object {
|
||||
val instance: AppDatabase by lazy {
|
||||
Room.databaseBuilder(
|
||||
App.Companion.appInstance, AppDatabase::class.java,
|
||||
App.Companion.DB_NAME
|
||||
).build()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package com.app.input.breeze.board.database
|
||||
|
||||
import com.app.input.breeze.board.bean.KeyboardTheme
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
object FavoriteRepository {
|
||||
|
||||
|
||||
suspend fun addLike(data: KeyboardTheme) {
|
||||
withContext(Dispatchers.IO) {
|
||||
AppDatabase.instance.favoriteThemeDao().insertData(data)
|
||||
}
|
||||
|
||||
}
|
||||
suspend fun removeLike(data: KeyboardTheme) {
|
||||
withContext(Dispatchers.IO) {
|
||||
val queryIsLike = AppDatabase.instance.favoriteThemeDao().queryIsLike(data.titleName)
|
||||
if (queryIsLike != null) {
|
||||
AppDatabase.instance.favoriteThemeDao().delete(queryIsLike)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
suspend fun getIsLike(name: String, action: (isLike: Boolean) -> Unit) {
|
||||
withContext(Dispatchers.IO) {
|
||||
val query = AppDatabase.instance.favoriteThemeDao().queryIsLike(name)
|
||||
withContext(Dispatchers.Main) {
|
||||
action.invoke(query != null)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package com.app.input.breeze.board.database
|
||||
|
||||
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.app.input.breeze.board.bean.KeyboardTheme
|
||||
|
||||
@Dao
|
||||
interface FavoriteThemeDao {
|
||||
@Insert(onConflict = OnConflictStrategy.Companion.IGNORE)
|
||||
suspend fun insertData(data: KeyboardTheme): Long
|
||||
|
||||
@Query("select * from KeyboardTheme ")
|
||||
fun queryAllLike(): LiveData<List<KeyboardTheme?>?>
|
||||
|
||||
|
||||
|
||||
@Query("select * from KeyboardTheme where titleName = :title ")
|
||||
suspend fun queryIsLike(title: String ): KeyboardTheme?
|
||||
|
||||
|
||||
@Delete
|
||||
suspend fun delete(data: KeyboardTheme)
|
||||
|
||||
@Update
|
||||
suspend fun updateLike(data: KeyboardTheme)
|
||||
}
|
||||
@ -0,0 +1,285 @@
|
||||
package com.app.input.breeze.board.keyboardhelper;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.inputmethodservice.InputMethodService;
|
||||
|
||||
import android.media.MediaPlayer;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
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.app.input.breeze.board.App;
|
||||
import com.app.input.breeze.board.R;
|
||||
import com.app.input.breeze.board.utils.KeyboardConstants;
|
||||
import com.app.input.breeze.board.utils.AppUtils;
|
||||
import com.app.input.breeze.board.utils.ThemePreferences;
|
||||
import com.app.input.breeze.board.sourcecode.Keyboard;
|
||||
import com.app.input.breeze.board.sourcecode.KeyboardView;
|
||||
|
||||
|
||||
import kotlin.Unit;
|
||||
import kotlin.jvm.functions.Function2;
|
||||
|
||||
public class BreezeInputMethodService extends InputMethodService implements KeyboardView.OnKeyboardActionListener {
|
||||
|
||||
|
||||
private BreezeKeyboardView keyboardView;
|
||||
private Keyboard 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.keyboard_default_view, null);
|
||||
findView();
|
||||
return parentView;
|
||||
}
|
||||
|
||||
private void findView() {
|
||||
|
||||
imBG = parentView.findViewById(R.id.gif_bg);
|
||||
videoView = parentView.findViewById(R.id.video_view);
|
||||
mKeyBoard = new Keyboard(this, a);
|
||||
keyboardView = parentView.findViewById(R.id.custom_input_view);
|
||||
keyboardView.setEnabled(true);
|
||||
keyboardView.setPreviewEnabled(false);
|
||||
keyboardView.setKeyboard(mKeyBoard);
|
||||
keyboardView.setOnKeyboardActionListener(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onWindowHidden() {
|
||||
super.onWindowHidden();
|
||||
if(videoView.isPlaying()){
|
||||
videoView.pause();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowShown() {
|
||||
super.onWindowShown();
|
||||
EditorInfo currentInputEditorInfo = getCurrentInputEditorInfo();
|
||||
curImeAction = AppUtils.INSTANCE.getTextForImeAction(currentInputEditorInfo.imeOptions);
|
||||
String skinPath = ThemePreferences.INSTANCE.getSkinPath();
|
||||
|
||||
if(skinPath == null || skinPath.isEmpty()){
|
||||
Log.d(App.TAG, "---------skinPath= bull");
|
||||
keyboardView.updateUi(curImeAction);
|
||||
}else {
|
||||
Log.d(App.TAG, "---------skinPath= 1111");
|
||||
KeyboardHelper.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 (s != null) {
|
||||
keyboardView.setBackground(null);
|
||||
if(s.endsWith(".gif")){
|
||||
imBG.setVisibility(View.VISIBLE);
|
||||
videoView.setVisibility(View.GONE);
|
||||
Glide.with(BreezeInputMethodService.this)
|
||||
.load(s)
|
||||
.addListener(new RequestListener<Drawable>() {
|
||||
@Override
|
||||
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> target, boolean isFirstResource) {
|
||||
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) ;
|
||||
}else if(s.endsWith(".mp4")){
|
||||
imBG.setVisibility(View.GONE);
|
||||
videoView.setVisibility(View.VISIBLE);
|
||||
videoView.setVideoPath(s);
|
||||
videoView.start();
|
||||
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
|
||||
@Override
|
||||
public void onPrepared(MediaPlayer mp) {
|
||||
mp.setLooping(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} else {
|
||||
keyboardView.setBackground(drawable);
|
||||
}
|
||||
keyboardView.updateUi(curImeAction);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
videoView.stopPlayback();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPress(int primaryCode) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRelease(int primaryCode) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onKey(int primaryCode, int[] keyCodes) {
|
||||
|
||||
InputConnection curInputConnect = getCurrentInputConnection();
|
||||
switch (primaryCode) {
|
||||
case KeyboardConstants.KEY_CODE_DELETE:
|
||||
curInputConnect.deleteSurroundingText(1, 0);
|
||||
break;
|
||||
case KeyboardConstants.KEY_CODE_SHIFT:
|
||||
switchShift();
|
||||
break;
|
||||
case KeyboardConstants.KEY_CODE_NUMBER_SHIFT:
|
||||
case KeyboardConstants.KEY_CODE_SYMBOL_SHIFT:
|
||||
switchMoreOrNumber();
|
||||
break;
|
||||
case KeyboardConstants.KEY_CODE_CHANGE_NUMBER:
|
||||
case KeyboardConstants.KEY_CODE_BACK:
|
||||
switchNormalOrNumber();
|
||||
break;
|
||||
|
||||
case KeyboardConstants.KEY_CODE_COMPLETE:
|
||||
case KeyboardConstants.KEY_CODE_CANCEL:
|
||||
curInputConnect.performEditorAction(curImeAction);
|
||||
// curInputConnect.performEditorAction(EditorInfo.IME_ACTION_DONE);
|
||||
break;
|
||||
default:
|
||||
String codeToChar = KeyboardHelper.INSTANCE.primaryCodeToChar(primaryCode);
|
||||
curInputConnect.commitText(codeToChar, 1);
|
||||
if (keyboardView.isLowerCase() == 1) {
|
||||
//自动转小写
|
||||
keyboardView.setLowerCase(0);
|
||||
KeyboardHelper.INSTANCE.keyToLowerCase(mKeyBoard);
|
||||
keyboardView.setKeyboard(mKeyBoard);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void switchMoreOrNumber() {
|
||||
int mode = keyboardView.getMode();
|
||||
switch (mode) {
|
||||
case 1:
|
||||
mKeyBoard = new Keyboard(this, c);
|
||||
keyboardView.setMode(2);
|
||||
keyboardView.setKeyboard(mKeyBoard);
|
||||
break;
|
||||
case 2:
|
||||
mKeyBoard = new Keyboard(this, b);
|
||||
keyboardView.setMode(1);
|
||||
keyboardView.setKeyboard(mKeyBoard);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void switchNormalOrNumber() {
|
||||
int mode = keyboardView.getMode();
|
||||
switch (mode) {
|
||||
case 0:
|
||||
mKeyBoard = new Keyboard(this, b);
|
||||
keyboardView.setMode(1);
|
||||
keyboardView.setKeyboard(mKeyBoard);
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
mKeyBoard = new Keyboard(this, a);
|
||||
keyboardView.setMode(0);
|
||||
keyboardView.setKeyboard(mKeyBoard);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void switchShift() {
|
||||
int lowerCase = keyboardView.isLowerCase();
|
||||
switch (lowerCase) {
|
||||
case 0:
|
||||
//当前小写转大写
|
||||
keyboardView.setLowerCase(1);
|
||||
KeyboardHelper.INSTANCE.keyToUpper(mKeyBoard);
|
||||
keyboardView.setKeyboard(mKeyBoard);
|
||||
break;
|
||||
case 1:
|
||||
//当前大写转锁定大写
|
||||
keyboardView.setLowerCase(2);
|
||||
break;
|
||||
case 2:
|
||||
//当前锁定大写转小写
|
||||
keyboardView.setLowerCase(0);
|
||||
KeyboardHelper.INSTANCE.keyToLowerCase(mKeyBoard);
|
||||
keyboardView.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() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,324 @@
|
||||
package com.app.input.breeze.board.keyboardhelper;
|
||||
|
||||
|
||||
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.app.input.breeze.board.App;
|
||||
import com.app.input.breeze.board.R;
|
||||
import com.app.input.breeze.board.sourcecode.Keyboard;
|
||||
import com.app.input.breeze.board.sourcecode.KeyboardView;
|
||||
import com.app.input.breeze.board.utils.KeyboardConstants;
|
||||
import com.app.input.breeze.board.utils.KeyboardThemeManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class BreezeKeyboardView extends KeyboardView {
|
||||
|
||||
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 KeyboardThemeManager KeyboardThemeManager;
|
||||
private int curImeAction = EditorInfo.IME_ACTION_UNSPECIFIED;
|
||||
|
||||
public BreezeKeyboardView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mContext = context;
|
||||
setAttribute(attrs, context);
|
||||
}
|
||||
|
||||
public BreezeKeyboardView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
mContext = context;
|
||||
setAttribute(attrs, context);
|
||||
}
|
||||
|
||||
public BreezeKeyboardView(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;
|
||||
KeyboardThemeManager.updateSkinConfig();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
private void initPaint() {
|
||||
mPaint = new Paint();
|
||||
mPaint.setAntiAlias(true);
|
||||
mPaint.setTextAlign(Paint.Align.CENTER);
|
||||
|
||||
}
|
||||
|
||||
private void setAttribute(AttributeSet attrs, Context con) {
|
||||
KeyboardThemeManager = new KeyboardThemeManager(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);
|
||||
|
||||
|
||||
KeyboardConfig config = KeyboardThemeManager.getConfig();
|
||||
List<KeyboardKey> KeyboardKeys = new ArrayList<>();
|
||||
int i = 0;
|
||||
|
||||
for (Keyboard.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;
|
||||
KeyboardLayout KeyboardLayout = config.getLayouts().get(0);
|
||||
KeyboardKeys = KeyboardLayout.getKeys();
|
||||
} else if (code == 97||code == 65 || code == 33||code == 126) {
|
||||
i = 0;
|
||||
KeyboardLayout KeyboardLayout = config.getLayouts().get(1);
|
||||
KeyboardKeys = KeyboardLayout.getKeys();
|
||||
}else if (code == -1 || code == -103||code==-101) {
|
||||
i = 0;
|
||||
KeyboardLayout KeyboardLayout = config.getLayouts().get(2);
|
||||
KeyboardKeys = KeyboardLayout.getKeys();
|
||||
}else if (code == -2 || code == -102) {
|
||||
i = 0;
|
||||
KeyboardLayout KeyboardLayout = config.getLayouts().get(3);
|
||||
KeyboardKeys = KeyboardLayout.getKeys();
|
||||
}
|
||||
String background = KeyboardKeys.get(i).getBackground()+".9.png";
|
||||
i++;
|
||||
Drawable configBg = KeyboardThemeManager.getConfigBg(background);
|
||||
realNewDraw(configBg, curKey, canvas, code);
|
||||
|
||||
} else {
|
||||
realDraw(curKey, canvas, code);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void realNewDraw(Drawable configBg, Keyboard.Key curKey, Canvas canvas, int code) {
|
||||
switch (code) {
|
||||
case KeyboardConstants.KEY_CODE_SHIFT:
|
||||
// drawAllShift(curKey, canvas);
|
||||
onDrawCurKey(curKey, canvas, "Shift", configBg, null);
|
||||
break;
|
||||
case KeyboardConstants.KEY_CODE_NUMBER_SHIFT:
|
||||
onDrawCurKey(curKey, canvas, "More", configBg, null);
|
||||
break;
|
||||
case KeyboardConstants.KEY_CODE_DELETE:
|
||||
onDrawCurKey(curKey, canvas, "Delete", configBg, null);
|
||||
break;
|
||||
case KeyboardConstants.KEY_CODE_SYMBOL_SHIFT:
|
||||
onDrawCurKey(curKey, canvas, "123", configBg, null);
|
||||
break;
|
||||
case KeyboardConstants.KEY_CODE_CHANGE_NUMBER:
|
||||
onDrawCurKey(curKey, canvas, null, configBg, null);
|
||||
break;
|
||||
case KeyboardConstants.KEY_CODE_BACK:
|
||||
onDrawCurKey(curKey, canvas, "Back", configBg, null);
|
||||
break;
|
||||
case KeyboardConstants.KEY_CODE_SPACE:
|
||||
onDrawCurKey(curKey, canvas, null, configBg, null);
|
||||
break;
|
||||
case KeyboardConstants.KEY_CODE_COMPLETE, KeyboardConstants.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(Keyboard.Key curKey, Canvas canvas, int code) {
|
||||
switch (code) {
|
||||
case KeyboardConstants.KEY_CODE_SHIFT:
|
||||
drawAllShift(curKey, canvas);
|
||||
break;
|
||||
case KeyboardConstants.KEY_CODE_NUMBER_SHIFT:
|
||||
onDrawCurKey(curKey, canvas, "More", KeyboardThemeManager.getFunctionDraw(), null);
|
||||
break;
|
||||
case KeyboardConstants.KEY_CODE_DELETE:
|
||||
onDrawCurKey(curKey, canvas, "Delete", KeyboardThemeManager.getFunctionDraw(), null);
|
||||
break;
|
||||
case KeyboardConstants.KEY_CODE_SYMBOL_SHIFT:
|
||||
onDrawCurKey(curKey, canvas, "123", KeyboardThemeManager.getFunctionDraw(), null);
|
||||
break;
|
||||
case KeyboardConstants.KEY_CODE_CHANGE_NUMBER:
|
||||
onDrawCurKey(curKey, canvas, null, KeyboardThemeManager.getToDraw(), null);
|
||||
break;
|
||||
case KeyboardConstants.KEY_CODE_BACK:
|
||||
onDrawCurKey(curKey, canvas, "Back", KeyboardThemeManager.getToDraw(), null);
|
||||
break;
|
||||
case KeyboardConstants.KEY_CODE_SPACE:
|
||||
onDrawCurKey(curKey, canvas, null, KeyboardThemeManager.getSpaceDraw(), null);
|
||||
break;
|
||||
case KeyboardConstants.KEY_CODE_COMPLETE, KeyboardConstants.KEY_CODE_CANCEL:
|
||||
Log.d(App.TAG, "-11111111111---------curImeAction=" + curImeAction);
|
||||
if (curImeAction == EditorInfo.IME_ACTION_SEARCH) {
|
||||
onDrawCurKey(curKey, canvas, "Search", KeyboardThemeManager.getFunctionDraw(), null);
|
||||
} else {
|
||||
onDrawCurKey(curKey, canvas, "Done", KeyboardThemeManager.getFunctionDraw(), null);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
onDrawCurKey(curKey, canvas, null, KeyboardThemeManager.getGeneralDraw(), null);
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void drawAllShift(Keyboard.Key curKey, Canvas canvas) {
|
||||
if (isLowerCase == 0) {
|
||||
onDrawCurKey(curKey, canvas, "Shift", KeyboardThemeManager.getFunctionDraw(), null);
|
||||
} else if (isLowerCase == 1) {
|
||||
onDrawCurKey(curKey, canvas, "Shift", KeyboardThemeManager.getFunctionDraw(), null);
|
||||
} else if (isLowerCase == 2) {
|
||||
onDrawCurKey(curKey, canvas, "Shift", KeyboardThemeManager.getFunctionDraw(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private void onDrawCurKey(Keyboard.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(Keyboard.Key curKey, Canvas curCanvas, String label) {
|
||||
mPaint.setColor(KeyboardThemeManager.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(Keyboard.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(Keyboard.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,168 @@
|
||||
package com.app.input.breeze.board.keyboardhelper;
|
||||
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class KeyboardConfig {
|
||||
private String version;
|
||||
private String supportLayouts;
|
||||
private int hideHint;
|
||||
private String layoutStyle;
|
||||
private List<KeyboardLayout> layouts = new ArrayList<>();
|
||||
private List<KeyboardKey> KeyboardKeyList = 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<KeyboardLayout> getLayouts() {
|
||||
return layouts;
|
||||
}
|
||||
|
||||
public void addLayout(KeyboardLayout layout) {
|
||||
this.layouts.add(layout);
|
||||
}
|
||||
|
||||
public List<KeyboardKey> getKeyList() {
|
||||
return KeyboardKeyList;
|
||||
}
|
||||
|
||||
public KeyboardKey getLastKeyList() {
|
||||
return KeyboardKeyList.isEmpty() ? null : KeyboardKeyList.get(KeyboardKeyList.size() - 1);
|
||||
}
|
||||
|
||||
public void addKey(KeyboardKey KeyboardKey) {
|
||||
this.KeyboardKeyList.add(KeyboardKey);
|
||||
}
|
||||
|
||||
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,100 @@
|
||||
package com.app.input.breeze.board.keyboardhelper
|
||||
|
||||
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.app.input.breeze.board.App
|
||||
import com.app.input.breeze.board.sourcecode.Keyboard
|
||||
import com.app.input.breeze.board.utils.KeyboardConstants
|
||||
import com.app.input.breeze.board.utils.ThemePreferences
|
||||
import java.io.File
|
||||
|
||||
object KeyboardHelper {
|
||||
fun keyToUpper(mKeyBoard: Keyboard) {
|
||||
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: Keyboard) {
|
||||
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
|
||||
) {
|
||||
|
||||
ThemePreferences.getSkinPath()?.let { resPath ->
|
||||
val videoPath = "${resPath}res/raw/${KeyboardConstants.videoName}"
|
||||
val videoPath2 = "${resPath}res/raw/${KeyboardConstants.video}"
|
||||
val backgroundPath = "${resPath}res/drawable-xxhdpi-v4/${KeyboardConstants.bgName}"
|
||||
val backgroundPath_png = "${resPath}res/drawable-xxhdpi-v4/${KeyboardConstants.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.app.input.breeze.board.keyboardhelper;
|
||||
|
||||
// 按键对象模型
|
||||
public class KeyboardKey {
|
||||
private String name;
|
||||
private String background;
|
||||
private String label;
|
||||
|
||||
public KeyboardKey(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;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package com.app.input.breeze.board.keyboardhelper;
|
||||
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class KeyboardLayout {
|
||||
private String name;
|
||||
private List<KeyboardKey> KeyboardKeys = new ArrayList<>();
|
||||
|
||||
|
||||
public KeyboardLayout(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
// Getters and Setters
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<KeyboardKey> getKeys() {
|
||||
return KeyboardKeys;
|
||||
}
|
||||
|
||||
public void addKey(KeyboardKey KeyboardKey) {
|
||||
this.KeyboardKeys.add(KeyboardKey);
|
||||
}
|
||||
|
||||
public KeyboardKey getLastKey() {
|
||||
return KeyboardKeys.isEmpty() ? null : KeyboardKeys.get(KeyboardKeys.size() - 1);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,90 @@
|
||||
package com.app.input.breeze.board.listutils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.app.input.breeze.board.bean.CategoryWrapper;
|
||||
import com.app.input.breeze.board.databinding.CategoryCardItemBinding;
|
||||
import com.bumptech.glide.Glide;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CategoryCardAdapter extends RecyclerView.Adapter<CategoryCardAdapter.CategoryCardViewHolder> {
|
||||
|
||||
private Context mContext;
|
||||
private List<CategoryWrapper> mList;
|
||||
private OnCategoryCardClick mOnClick;
|
||||
|
||||
public interface OnCategoryCardClick {
|
||||
void onCategoryClick(String categoryName);
|
||||
}
|
||||
|
||||
public CategoryCardAdapter(Context context, List<CategoryWrapper> list, OnCategoryCardClick onClick) {
|
||||
mContext = context;
|
||||
mList = list;
|
||||
mOnClick = onClick;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public CategoryCardViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
CategoryCardItemBinding binding = CategoryCardItemBinding.inflate(
|
||||
LayoutInflater.from(parent.getContext()),
|
||||
parent,
|
||||
false
|
||||
);
|
||||
return new CategoryCardViewHolder(binding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull CategoryCardViewHolder holder, int position) {
|
||||
CategoryWrapper wrapper = mList.get(position);
|
||||
holder.bind(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mList.size();
|
||||
}
|
||||
|
||||
class CategoryCardViewHolder extends RecyclerView.ViewHolder {
|
||||
private CategoryCardItemBinding binding;
|
||||
|
||||
public CategoryCardViewHolder(@NonNull CategoryCardItemBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
|
||||
void bind(CategoryWrapper wrapper) {
|
||||
String categoryName = wrapper.getParentName();
|
||||
binding.categoryTitle.setText(categoryName);
|
||||
|
||||
// 如果有主题,显示第一个主题的缩略图作为背景
|
||||
if (wrapper.getKeyboardList() != null && !wrapper.getKeyboardList().isEmpty()) {
|
||||
String thumbUrl = wrapper.getKeyboardList().get(0).getThumbUrl();
|
||||
if (thumbUrl != null && !thumbUrl.isEmpty()) {
|
||||
Glide.with(mContext)
|
||||
.load(thumbUrl)
|
||||
.centerCrop()
|
||||
.into(binding.categoryImage);
|
||||
}
|
||||
}
|
||||
|
||||
binding.getRoot().setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mOnClick != null) {
|
||||
mOnClick.onCategoryClick(categoryName);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,74 @@
|
||||
package com.app.input.breeze.board.listutils;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.app.input.breeze.board.bean.CategoryWrapper;
|
||||
import com.app.input.breeze.board.callback.ViewAllClickListener;
|
||||
import com.app.input.breeze.board.databinding.HomeAdapterItemBinding;
|
||||
import com.app.input.breeze.board.utils.GridItemDecoration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CategoryListAdapter extends RecyclerView.Adapter<CategoryListAdapter.MainViewHolder> {
|
||||
private Context mContext;
|
||||
private List<CategoryWrapper> mList = new ArrayList<>();
|
||||
|
||||
private ViewAllClickListener mCallBack;
|
||||
|
||||
public CategoryListAdapter(Context context, List<CategoryWrapper> list) {
|
||||
mContext = context;
|
||||
this.mList = list;
|
||||
}
|
||||
public void setClickAction(ViewAllClickListener callback) {
|
||||
mCallBack = callback;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public MainViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
HomeAdapterItemBinding inflate = HomeAdapterItemBinding.inflate(LayoutInflater.from(parent.getContext()));
|
||||
return new MainViewHolder(inflate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull MainViewHolder holder, int position) {
|
||||
CategoryWrapper CategoryWrapper = mList.get(position);
|
||||
String parentName = CategoryWrapper.getParentName();
|
||||
holder.binding.className.setText(parentName);
|
||||
GridItemDecoration GridItemDecoration = new GridItemDecoration(3, 3, 0);
|
||||
ThemeGridAdapter ThemeGridAdapter = new ThemeGridAdapter(mContext, CategoryWrapper.getKeyboardList().subList(0, 6));
|
||||
holder.binding.childRecycler.setLayoutManager(new GridLayoutManager(mContext, 3));
|
||||
holder.binding.childRecycler.setAdapter(ThemeGridAdapter);
|
||||
if (holder.binding.childRecycler.getItemDecorationCount() <= 0) {
|
||||
holder.binding.childRecycler.addItemDecoration(GridItemDecoration);
|
||||
}
|
||||
holder.binding.tvAll.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mCallBack.OnClickSeeAll(parentName);
|
||||
}
|
||||
});
|
||||
}
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mList.size();
|
||||
}
|
||||
|
||||
public static class MainViewHolder extends RecyclerView.ViewHolder {
|
||||
private HomeAdapterItemBinding binding;
|
||||
public MainViewHolder(@NonNull HomeAdapterItemBinding itemView) {
|
||||
super(itemView.getRoot());
|
||||
binding = itemView;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,126 @@
|
||||
package com.app.input.breeze.board.listutils;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.app.input.breeze.board.R;
|
||||
import com.app.input.breeze.board.bean.KeyboardTheme;
|
||||
import com.app.input.breeze.board.utils.AppUtils;
|
||||
import com.app.input.breeze.board.callback.RemoveFavoriteCallback;
|
||||
import com.app.input.breeze.board.uiactivity.ThemeDetailActivity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class FavoriteThemeAdapter extends RecyclerView.Adapter<FavoriteThemeAdapter.ForYouViewHolder> {
|
||||
|
||||
private Context mContext;
|
||||
private List<KeyboardTheme> mList = new ArrayList<>();
|
||||
|
||||
private RemoveFavoriteCallback mCallBack;
|
||||
|
||||
public FavoriteThemeAdapter(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public void setForYouList(List<KeyboardTheme> list) {
|
||||
this.mList = list;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
|
||||
public void setRemoveLike(RemoveFavoriteCallback callback) {
|
||||
mCallBack = callback;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ForYouViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
|
||||
View view = LayoutInflater.from(mContext).inflate(R.layout.favorite_theme_adapter_item, parent, false);
|
||||
return new ForYouViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ForYouViewHolder holder, int position) {
|
||||
KeyboardTheme KeyboardTheme = mList.get(position);
|
||||
String thumbGif = KeyboardTheme.getThumbGif();
|
||||
String thumb = KeyboardTheme.getThumbUrl();
|
||||
if (!thumbGif.isEmpty()) {
|
||||
AppUtils.INSTANCE.loadWepJif(mContext, thumbGif, holder.itemImg);
|
||||
} else {
|
||||
Glide.with(mContext)
|
||||
.load(thumb).error(R.drawable.place_holder)
|
||||
.placeholder(R.drawable.place_holder).into(holder.itemImg);
|
||||
}
|
||||
holder.itemFavorite.setSelected(true);
|
||||
holder.layoutFavorite.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
holder.itemFavorite.setSelected(false);
|
||||
int adapterPosition = holder.getAdapterPosition();
|
||||
notifyItemRemoved(adapterPosition);
|
||||
if (mCallBack != null) {
|
||||
mCallBack.OnRemoveLike(KeyboardTheme);
|
||||
}
|
||||
}
|
||||
});
|
||||
holder.cardView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intentApply = new Intent(mContext, ThemeDetailActivity.class);
|
||||
intentApply.putExtra(ThemeDetailActivity.SOURCE_KEY, KeyboardTheme);
|
||||
intentApply.putExtra(ThemeDetailActivity.DISPLAY_URL_KEY, KeyboardTheme.getImgPath());
|
||||
intentApply.putExtra(ThemeDetailActivity.ZIP_URL_KEY, KeyboardTheme.getZipPath());
|
||||
intentApply.putExtra(ThemeDetailActivity.NAME_KEY, KeyboardTheme.getTitleName());
|
||||
intentApply.putExtra(ThemeDetailActivity.GIF_KEY, KeyboardTheme.getImgGif());
|
||||
String intent_thumb;
|
||||
if (!thumbGif.isEmpty()) {
|
||||
intent_thumb = thumbGif;
|
||||
} else {
|
||||
intent_thumb = thumb;
|
||||
}
|
||||
intentApply.putExtra(ThemeDetailActivity.THUMB_KEY, intent_thumb);
|
||||
mContext.startActivity(intentApply);
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mList.size();
|
||||
}
|
||||
|
||||
public static class ForYouViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private CardView cardView;
|
||||
private FrameLayout layoutFavorite;
|
||||
private ImageView itemImg, itemFavorite;
|
||||
|
||||
public ForYouViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
cardView = itemView.findViewById(R.id.card_view);
|
||||
layoutFavorite = itemView.findViewById(R.id.layout_favorite);
|
||||
itemImg = itemView.findViewById(R.id.im);
|
||||
itemFavorite = itemView.findViewById(R.id.im_favorite);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,95 @@
|
||||
package com.app.input.breeze.board.listutils
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.app.input.breeze.board.R
|
||||
import com.app.input.breeze.board.bean.KeyboardTheme
|
||||
import com.app.input.breeze.board.databinding.FeaturedThemeItemBinding
|
||||
import com.app.input.breeze.board.utils.AppUtils
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
|
||||
class FeaturedThemeAdapter(
|
||||
private val context: android.content.Context,
|
||||
private val heroList: List<KeyboardTheme>,
|
||||
private val onItemClick: (KeyboardTheme) -> Unit
|
||||
) : RecyclerView.Adapter<FeaturedThemeAdapter.HeroViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HeroViewHolder {
|
||||
val binding = FeaturedThemeItemBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
return HeroViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: HeroViewHolder, position: Int) {
|
||||
val KeyboardTheme = heroList[position]
|
||||
holder.bind(KeyboardTheme)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = heroList.size
|
||||
|
||||
inner class HeroViewHolder(
|
||||
private val binding: FeaturedThemeItemBinding
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(KeyboardTheme: KeyboardTheme) {
|
||||
// 先设置占位图,确保立即显示
|
||||
binding.heroImage.setImageResource(R.drawable.place_holder)
|
||||
|
||||
// 设置标题(已简化,移除副标题)
|
||||
binding.heroTitle.text = KeyboardTheme.titleName
|
||||
|
||||
// 加载图片 - 优先使用预览图,如果没有则使用缩略图
|
||||
val imgPath = KeyboardTheme.imgPath
|
||||
val imgGif = KeyboardTheme.imgGif
|
||||
val thumbGif = KeyboardTheme.thumbGif
|
||||
val thumb = KeyboardTheme.thumbUrl
|
||||
|
||||
// 优先使用预览图(imgPath/imgGif),如果没有则使用缩略图(thumbUrl/thumbGif)
|
||||
when {
|
||||
imgGif != null && imgGif.isNotEmpty() -> {
|
||||
AppUtils.loadWepJif(context, imgGif, binding.heroImage)
|
||||
}
|
||||
imgPath != null && imgPath.isNotEmpty() -> {
|
||||
val options = RequestOptions()
|
||||
.transform(RoundedCorners(AppUtils.dpToPx(20f)))
|
||||
.centerCrop()
|
||||
.placeholder(R.drawable.place_holder)
|
||||
.error(R.drawable.place_holder)
|
||||
|
||||
Glide.with(context)
|
||||
.load(imgPath)
|
||||
.apply(options)
|
||||
.into(binding.heroImage)
|
||||
}
|
||||
thumbGif != null && thumbGif.isNotEmpty() -> {
|
||||
AppUtils.loadWepJif(context, thumbGif, binding.heroImage)
|
||||
}
|
||||
thumb != null && thumb.isNotEmpty() -> {
|
||||
val options = RequestOptions()
|
||||
.transform(RoundedCorners(AppUtils.dpToPx(20f)))
|
||||
.centerCrop()
|
||||
.placeholder(R.drawable.place_holder)
|
||||
.error(R.drawable.place_holder)
|
||||
|
||||
Glide.with(context)
|
||||
.load(thumb)
|
||||
.apply(options)
|
||||
.into(binding.heroImage)
|
||||
}
|
||||
// else 情况已经在开头设置了占位图
|
||||
}
|
||||
|
||||
// 点击事件
|
||||
binding.root.setOnClickListener {
|
||||
onItemClick(KeyboardTheme)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,96 @@
|
||||
package com.app.input.breeze.board.listutils
|
||||
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.app.input.breeze.board.R
|
||||
import com.app.input.breeze.board.databinding.QuickCategoryItemBinding
|
||||
|
||||
class QuickCategoryAdapter(
|
||||
private val context: android.content.Context,
|
||||
private val categories: List<Pair<String, String>>, // (名称, emoji)
|
||||
private val onCategoryClick: (String) -> Unit
|
||||
) : RecyclerView.Adapter<QuickCategoryAdapter.CategoryViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryViewHolder {
|
||||
val binding = QuickCategoryItemBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
return CategoryViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: CategoryViewHolder, position: Int) {
|
||||
val (name, emoji) = categories[position]
|
||||
holder.bind(name, emoji)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = categories.size
|
||||
|
||||
inner class CategoryViewHolder(
|
||||
private val binding: QuickCategoryItemBinding
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(name: String, emoji: String) {
|
||||
binding.categoryEmoji.text = emoji
|
||||
binding.categoryName.text = name
|
||||
|
||||
// 根据分类名称设置不同的渐变背景(更精致的多色渐变)
|
||||
val gradient = GradientDrawable().apply {
|
||||
orientation = GradientDrawable.Orientation.TL_BR
|
||||
cornerRadius = 22f * context.resources.displayMetrics.density
|
||||
colors = getCategoryColors(name)
|
||||
}
|
||||
binding.categoryBg.background = gradient
|
||||
|
||||
binding.root.setOnClickListener {
|
||||
onCategoryClick(name)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCategoryColors(categoryName: String): IntArray {
|
||||
// 降低饱和度10-20%,使用同色系不同明度,更成熟优雅
|
||||
return when (categoryName.lowercase()) {
|
||||
"cute" -> intArrayOf(
|
||||
0xFFFFB3D9.toInt(), // 降低饱和度:浅粉
|
||||
0xFFFFC5E5.toInt(), // 中粉
|
||||
0xFFFFD9F0.toInt() // 更浅粉
|
||||
)
|
||||
"neon" -> intArrayOf(
|
||||
0xFFB794F6.toInt(), // 降低饱和度:柔紫
|
||||
0xFFA78BEA.toInt(), // 中柔紫
|
||||
0xFF9678E0.toInt() // 深柔紫
|
||||
)
|
||||
"cool" -> intArrayOf(
|
||||
0xFF6BB6FF.toInt(), // 降低饱和度:柔蓝
|
||||
0xFF5AA3F0.toInt(), // 中柔蓝
|
||||
0xFF4A90E0.toInt() // 深柔蓝
|
||||
)
|
||||
"aesthetic" -> intArrayOf(
|
||||
0xFFFFD699.toInt(), // 降低饱和度:柔橙
|
||||
0xFFFFE0B3.toInt(), // 中柔橙
|
||||
0xFFFFEACC.toInt() // 浅柔橙
|
||||
)
|
||||
"love" -> intArrayOf(
|
||||
0xFFF5A3C7.toInt(), // 降低饱和度:柔玫红
|
||||
0xFFFFB3D9.toInt(), // 中柔粉
|
||||
0xFFFFC5E5.toInt() // 浅柔粉
|
||||
)
|
||||
"gravity" -> intArrayOf(
|
||||
0xFF9D8FEB.toInt(), // 降低饱和度:柔紫蓝
|
||||
0xFF8B7DD8.toInt(), // 中柔紫蓝
|
||||
0xFF7A6BC5.toInt() // 深柔紫蓝
|
||||
)
|
||||
else -> intArrayOf(
|
||||
0xFFB0BEC5.toInt(), // 降低饱和度:柔灰蓝
|
||||
0xFFCFD8DC.toInt(), // 中柔灰蓝
|
||||
0xFFE1E8ED.toInt() // 浅柔灰蓝
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,127 @@
|
||||
package com.app.input.breeze.board.listutils;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.app.input.breeze.board.R;
|
||||
import com.app.input.breeze.board.bean.KeyboardTheme;
|
||||
import com.app.input.breeze.board.callback.ItemClickListener;
|
||||
import com.app.input.breeze.board.uiactivity.ThemeDetailActivity;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.app.input.breeze.board.utils.AppUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class RecommendedThemeAdapter extends RecyclerView.Adapter<RecommendedThemeAdapter.ForYouViewHolder> {
|
||||
|
||||
private Context mContext;
|
||||
private List<KeyboardTheme> mList = new ArrayList<>();
|
||||
|
||||
private ItemClickListener mCallBack;
|
||||
|
||||
public RecommendedThemeAdapter(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public void setForYouList(List<KeyboardTheme> list) {
|
||||
this.mList = list;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
public void setClickAction(ItemClickListener callback) {
|
||||
mCallBack = callback;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ForYouViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
|
||||
View view = LayoutInflater.from(mContext).inflate(R.layout.recommended_theme_adapter_item, parent, false);
|
||||
return new ForYouViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ForYouViewHolder holder, int position) {
|
||||
KeyboardTheme KeyboardTheme = mList.get(position);
|
||||
String thumbGif = KeyboardTheme.getThumbGif();
|
||||
String thumb = KeyboardTheme.getThumbUrl();
|
||||
if (!thumbGif.isEmpty()) {
|
||||
AppUtils.INSTANCE.loadWepJif(mContext, thumbGif, holder.itemImg);
|
||||
} else {
|
||||
Glide.with(mContext).load(thumb).into(holder.itemImg);
|
||||
}
|
||||
|
||||
holder.cardView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intentApply = new Intent(mContext, ThemeDetailActivity.class);
|
||||
intentApply.putExtra(ThemeDetailActivity.SOURCE_KEY, KeyboardTheme);
|
||||
intentApply.putExtra(ThemeDetailActivity.DISPLAY_URL_KEY, KeyboardTheme.getImgPath());
|
||||
intentApply.putExtra(ThemeDetailActivity.ZIP_URL_KEY, KeyboardTheme.getZipPath());
|
||||
intentApply.putExtra(ThemeDetailActivity.NAME_KEY, KeyboardTheme.getTitleName());
|
||||
intentApply.putExtra(ThemeDetailActivity.GIF_KEY, KeyboardTheme.getImgGif());
|
||||
String intent_thumb;
|
||||
if (!thumbGif.isEmpty()) {
|
||||
intent_thumb = thumbGif;
|
||||
} else {
|
||||
intent_thumb = thumb;
|
||||
}
|
||||
intentApply.putExtra(ThemeDetailActivity.THUMB_KEY, intent_thumb);
|
||||
mContext.startActivity(intentApply);
|
||||
if (mCallBack != null) {
|
||||
mCallBack.OnItemClickListener();
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mList.size();
|
||||
}
|
||||
|
||||
public static class ForYouViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private CardView cardView;
|
||||
private ImageView itemImg;
|
||||
|
||||
public ForYouViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
cardView = itemView.findViewById(R.id.card_view);
|
||||
itemImg = itemView.findViewById(R.id.imPreview);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// private void loadWepJif(String webpGifUrl,ImageView view){
|
||||
// Glide.with(mContext).load(webpGifUrl).addListener(new RequestListener<Drawable>() {
|
||||
// @Override
|
||||
// public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> target, boolean isFirstResource) {
|
||||
// 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 webpDrawable = (WebpDrawable) resource;
|
||||
// webpDrawable.setLoopCount(LOOP_FOREVER);
|
||||
// }
|
||||
//
|
||||
// return false;
|
||||
// }
|
||||
// }).into(view);
|
||||
// }
|
||||
}
|
||||
@ -0,0 +1,125 @@
|
||||
package com.app.input.breeze.board.listutils;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.app.input.breeze.board.R;
|
||||
import com.app.input.breeze.board.bean.KeyboardTheme;
|
||||
import com.app.input.breeze.board.databinding.ThemeGridItemBinding;
|
||||
import com.app.input.breeze.board.uiactivity.ThemeDetailActivity;
|
||||
import com.app.input.breeze.board.utils.AppUtils;
|
||||
import com.bumptech.glide.Glide;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ThemeGridAdapter extends RecyclerView.Adapter<ThemeGridAdapter.ChildViewHolder> {
|
||||
|
||||
private Context mContext;
|
||||
private List<KeyboardTheme> mList = new ArrayList<>();
|
||||
|
||||
|
||||
public ThemeGridAdapter(Context context, List<KeyboardTheme> list) {
|
||||
mContext = context;
|
||||
this.mList = list;
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ChildViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
ThemeGridItemBinding inflate = ThemeGridItemBinding.inflate(LayoutInflater.from(parent.getContext()));
|
||||
|
||||
|
||||
return new ChildViewHolder(inflate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ChildViewHolder holder, int position) {
|
||||
KeyboardTheme KeyboardTheme = mList.get(position);
|
||||
|
||||
|
||||
String thumbGif = KeyboardTheme.getThumbGif();
|
||||
String thumb = KeyboardTheme.getThumbUrl();
|
||||
if (!thumbGif.isEmpty()) {
|
||||
AppUtils.INSTANCE.loadWepJif(mContext, thumbGif, holder.binding.imageView);
|
||||
} else {
|
||||
Glide.with(mContext).load(thumb)
|
||||
.error(R.drawable.place_holder)
|
||||
.placeholder(R.drawable.place_holder)
|
||||
.into(holder.binding.imageView);
|
||||
}
|
||||
|
||||
// 设置标题
|
||||
holder.binding.titleText.setText(KeyboardTheme.getTitleName());
|
||||
|
||||
holder.binding.fragme.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intentApply = new Intent(mContext, ThemeDetailActivity.class);
|
||||
intentApply.putExtra(ThemeDetailActivity.SOURCE_KEY, KeyboardTheme);
|
||||
intentApply.putExtra(ThemeDetailActivity.DISPLAY_URL_KEY, KeyboardTheme.getImgPath());
|
||||
intentApply.putExtra(ThemeDetailActivity.ZIP_URL_KEY, KeyboardTheme.getZipPath());
|
||||
intentApply.putExtra(ThemeDetailActivity.NAME_KEY, KeyboardTheme.getTitleName());
|
||||
intentApply.putExtra(ThemeDetailActivity.GIF_KEY, KeyboardTheme.getImgGif());
|
||||
String intent_thumb;
|
||||
if (!thumbGif.isEmpty()) {
|
||||
intent_thumb = thumbGif;
|
||||
} else {
|
||||
intent_thumb = thumb;
|
||||
}
|
||||
intentApply.putExtra(ThemeDetailActivity.THUMB_KEY, intent_thumb);
|
||||
mContext.startActivity(intentApply);
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mList.size();
|
||||
}
|
||||
|
||||
public static class ChildViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private ThemeGridItemBinding binding;
|
||||
|
||||
|
||||
public ChildViewHolder(@NonNull ThemeGridItemBinding itemView) {
|
||||
super(itemView.getRoot());
|
||||
binding = itemView;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// private void loadWepJif(String webpGifUrl,ImageView view){
|
||||
// Glide.with(mContext).load(webpGifUrl).addListener(new RequestListener<Drawable>() {
|
||||
// @Override
|
||||
// public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> target, boolean isFirstResource) {
|
||||
// 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 webpDrawable = (WebpDrawable) resource;
|
||||
// webpDrawable.setLoopCount(LOOP_FOREVER);
|
||||
// }
|
||||
//
|
||||
// return false;
|
||||
// }
|
||||
// }).into(view);
|
||||
// }
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
package com.app.input.breeze.board.listutils
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.app.input.breeze.board.bean.KeyboardTheme
|
||||
import com.app.input.breeze.board.databinding.TrendingThemeItemBinding
|
||||
import com.app.input.breeze.board.utils.AppUtils
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
|
||||
class TrendingThemeAdapter(
|
||||
private val context: android.content.Context,
|
||||
private val trendingList: List<KeyboardTheme>,
|
||||
private val onItemClick: (KeyboardTheme) -> Unit
|
||||
) : RecyclerView.Adapter<TrendingThemeAdapter.TrendingViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TrendingViewHolder {
|
||||
val binding = TrendingThemeItemBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
return TrendingViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: TrendingViewHolder, position: Int) {
|
||||
val KeyboardTheme = trendingList[position]
|
||||
holder.bind(KeyboardTheme)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = trendingList.size
|
||||
|
||||
inner class TrendingViewHolder(
|
||||
private val binding: TrendingThemeItemBinding
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(KeyboardTheme: KeyboardTheme) {
|
||||
val thumbGif = KeyboardTheme.thumbGif
|
||||
val thumb = KeyboardTheme.thumbUrl
|
||||
|
||||
val options = RequestOptions()
|
||||
.transform(RoundedCorners(AppUtils.dpToPx(16f)))
|
||||
|
||||
if (thumbGif != null && thumbGif.isNotEmpty()) {
|
||||
AppUtils.loadWepJif(context, thumbGif, binding.trendingImage)
|
||||
} else {
|
||||
Glide.with(context)
|
||||
.load(thumb)
|
||||
.apply(options)
|
||||
.into(binding.trendingImage)
|
||||
}
|
||||
|
||||
binding.root.setOnClickListener {
|
||||
onItemClick(KeyboardTheme)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,767 @@
|
||||
package com.app.input.breeze.board.sourcecode;
|
||||
|
||||
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.app.input.breeze.board.R;
|
||||
|
||||
public class Keyboard {
|
||||
|
||||
static final String TAG = "------------Keyboard-----------";
|
||||
|
||||
// 键盘 XML 标签
|
||||
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;
|
||||
|
||||
/** 键盘标签 **/
|
||||
private CharSequence mLabel;
|
||||
|
||||
/** 所有行的默认水平间距 */
|
||||
private int mDefaultHorizontalGap;
|
||||
|
||||
/** 默认按键宽度 */
|
||||
private int mDefaultWidth;
|
||||
|
||||
/** 默认按键高度 */
|
||||
private int mDefaultHeight;
|
||||
|
||||
/** 行之间的默认间距 */
|
||||
private int mDefaultVerticalGap;
|
||||
|
||||
/** 键盘是否处于大写状态 */
|
||||
private boolean mShifted;
|
||||
|
||||
/** Shift 键的实例(如果存在) */
|
||||
private Keyboard.Key[] mShiftKeys = { null, null };
|
||||
|
||||
/** Shift 键的索引(如果存在) */
|
||||
private int[] mShiftKeyIndices = {-1, -1};
|
||||
|
||||
/** 加载键盘时的当前按键宽度 */
|
||||
private int mKeyWidth;
|
||||
|
||||
/** 加载键盘时的当前按键高度 */
|
||||
private int mKeyHeight;
|
||||
|
||||
/** 键盘的总高度,包括内边距和按键 */
|
||||
private int mTotalHeight;
|
||||
|
||||
/**
|
||||
* 键盘的总宽度,包括左侧间距和按键,但不包括右侧的任何间距。
|
||||
*/
|
||||
private int mTotalWidth;
|
||||
|
||||
/** 此键盘中的按键列表 */
|
||||
private List<Keyboard.Key> mKeys;
|
||||
|
||||
/** 修饰键列表,如 Shift 和 Alt(如果有) */
|
||||
private List<Keyboard.Key> mModifierKeys;
|
||||
|
||||
/** 可用于放置键盘的屏幕宽度 */
|
||||
private int mDisplayWidth;
|
||||
|
||||
/** 屏幕高度 */
|
||||
private int mDisplayHeight;
|
||||
|
||||
/** 键盘模式,如果没有则为零 */
|
||||
private int mKeyboardMode;
|
||||
|
||||
// 用于预计算最近按键的变量
|
||||
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;
|
||||
|
||||
/** 从当前触摸点到搜索最近按键的按键宽度数量 */
|
||||
private static float SEARCH_DISTANCE = 1.8f;
|
||||
|
||||
private ArrayList<Keyboard.Row> rows = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 键盘中按键的容器。一行中的所有按键具有相同的 Y 坐标。
|
||||
* 某些按键大小的默认值可以在每行中覆盖 {@link Keyboard} 定义的值。
|
||||
*/
|
||||
public static class Row {
|
||||
|
||||
/** 此行中按键的默认宽度 */
|
||||
public int defaultWidth;
|
||||
|
||||
/** 此行中按键的默认高度 */
|
||||
public int defaultHeight;
|
||||
|
||||
/** 此行中按键之间的默认水平间距 */
|
||||
public int defaultHorizontalGap;
|
||||
|
||||
/** 此行之后的垂直间距 */
|
||||
public int verticalGap;
|
||||
|
||||
ArrayList<Keyboard.Key> mKeys = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 此行按键的边缘标志。可以赋值的可能值有
|
||||
* {@link Keyboard#EDGE_TOP EDGE_TOP} 和 {@link Keyboard#EDGE_BOTTOM EDGE_BOTTOM}
|
||||
*/
|
||||
public int rowEdgeFlags;
|
||||
|
||||
/** 此行的键盘模式 */
|
||||
public int mode;
|
||||
|
||||
private Keyboard parent;
|
||||
|
||||
public Row(Keyboard parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public Row(Resources res, Keyboard 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于描述键盘中单个按键的位置和特征的类。
|
||||
*/
|
||||
public static class Key {
|
||||
|
||||
/**
|
||||
* 此按键可以生成的所有键码(Unicode 或自定义代码),
|
||||
* 第零个是最重要的。
|
||||
*/
|
||||
public int[] codes;
|
||||
|
||||
/** 要显示的标签 */
|
||||
public CharSequence label;
|
||||
|
||||
/** 要显示的图标(替代标签)。图标优先于标签 */
|
||||
public Drawable icon;
|
||||
|
||||
/** 图标的预览版本,用于预览弹出窗口 */
|
||||
public Drawable iconPreview;
|
||||
|
||||
/** 按键的宽度,不包括间距 */
|
||||
public int width;
|
||||
|
||||
/** 按键的高度,不包括间距 */
|
||||
public int height;
|
||||
|
||||
/** 此按键之前的水平间距 */
|
||||
public int gap;
|
||||
|
||||
/** 此按键是否为粘性键,即切换键 */
|
||||
public boolean sticky;
|
||||
|
||||
/** 按键在键盘布局中的 X 坐标 */
|
||||
public int x;
|
||||
|
||||
/** 按键在键盘布局中的 Y 坐标 */
|
||||
public int y;
|
||||
|
||||
/** 此按键的当前按下状态 */
|
||||
public boolean pressed;
|
||||
|
||||
/** 如果这是粘性键,它是否处于开启状态? */
|
||||
public boolean on;
|
||||
|
||||
/** 按下时输出的文本。可以是多个字符,如 ".com" */
|
||||
public CharSequence text;
|
||||
|
||||
/** 弹出字符 */
|
||||
public CharSequence popupCharacters;
|
||||
|
||||
/**
|
||||
* 指定键盘边缘锚定的标志,用于检测刚好超出按键边界的触摸事件。
|
||||
* 这是 {@link Keyboard#EDGE_LEFT}、{@link Keyboard#EDGE_RIGHT}、{@link Keyboard#EDGE_TOP} 和
|
||||
* {@link Keyboard#EDGE_BOTTOM} 的位掩码。
|
||||
*/
|
||||
public int edgeFlags;
|
||||
|
||||
/** 这是否为修饰键,如 Shift 或 Alt */
|
||||
public boolean modifier;
|
||||
|
||||
/** 此按键所属的键盘 */
|
||||
private Keyboard keyboard;
|
||||
|
||||
/**
|
||||
* 如果此按键弹出迷你键盘,这是该键盘的 XML 布局的资源 ID。
|
||||
*/
|
||||
public int popupResId;
|
||||
|
||||
/** 此按键在按住时是否重复 */
|
||||
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
|
||||
};
|
||||
|
||||
/** 创建一个没有属性的空按键 */
|
||||
public Key(Keyboard.Row parent) {
|
||||
keyboard = parent.parent;
|
||||
height = parent.defaultHeight;
|
||||
width = parent.defaultWidth;
|
||||
gap = parent.defaultHorizontalGap;
|
||||
edgeFlags = parent.rowEdgeFlags;
|
||||
}
|
||||
|
||||
/** 使用给定的左上角坐标创建按键,并从 XML 解析器中提取其属性 */
|
||||
public Key(Resources res, Keyboard.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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知按键已被按下,以防它需要更改外观或状态。
|
||||
*/
|
||||
public void onPressed() {
|
||||
pressed = !pressed;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更改按键的按下状态。
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测点是否落在此按键内。
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回按键中心与给定点之间距离的平方。
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据按键的当前状态和类型返回按键的可绘制状态。
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从给定的 XML 按键布局文件创建键盘。
|
||||
*/
|
||||
public Keyboard(Context context, int xmlLayoutResId) {
|
||||
this(context, xmlLayoutResId, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从给定的 XML 按键布局文件创建键盘。过滤掉定义了键盘模式但与指定模式不匹配的行。
|
||||
*/
|
||||
public Keyboard(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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 从给定的 XML 按键布局文件创建键盘。过滤掉定义了键盘模式但与指定模式不匹配的行。
|
||||
*/
|
||||
public Keyboard(Context context, @XmlRes int xmlLayoutResId, int modeId) {
|
||||
DisplayMetrics dm = context.getResources().getDisplayMetrics();
|
||||
mDisplayWidth = dm.widthPixels;
|
||||
mDisplayHeight = dm.heightPixels;
|
||||
mDefaultHorizontalGap = 0;
|
||||
mDefaultWidth = mDisplayWidth / 10;
|
||||
mDefaultVerticalGap = 0;
|
||||
mDefaultHeight = mDefaultWidth;
|
||||
mKeys = new ArrayList<>();
|
||||
mModifierKeys = new ArrayList<>();
|
||||
mKeyboardMode = modeId;
|
||||
loadKeyboard(context, context.getResources().getXml(xmlLayoutResId));
|
||||
}
|
||||
|
||||
public Keyboard(Context context, int layoutTemplateResId,
|
||||
CharSequence characters, int columns, int horizontalPadding) {
|
||||
this(context, layoutTemplateResId);
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int column = 0;
|
||||
mTotalWidth = 0;
|
||||
Keyboard.Row row = new Keyboard.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 Keyboard.Key key = new Keyboard.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) {
|
||||
Keyboard.Row row = rows.get(rowIndex);
|
||||
int numKeys = row.mKeys.size();
|
||||
int totalGap = 0;
|
||||
int totalWidth = 0;
|
||||
for (int keyIndex = 0; keyIndex < numKeys; ++keyIndex) {
|
||||
Keyboard.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) {
|
||||
Keyboard.Key key = row.mKeys.get(keyIndex);
|
||||
key.width *= scaleFactor;
|
||||
key.x = x;
|
||||
x += key.width + key.gap;
|
||||
}
|
||||
}
|
||||
}
|
||||
mTotalWidth = newWidth;
|
||||
}
|
||||
|
||||
public List<Keyboard.Key> getKeys() {
|
||||
return mKeys;
|
||||
}
|
||||
|
||||
public List<Keyboard.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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回键盘的总高度
|
||||
*/
|
||||
public int getHeight() {
|
||||
return mTotalHeight;
|
||||
}
|
||||
|
||||
public int getMinWidth() {
|
||||
return mTotalWidth;
|
||||
}
|
||||
|
||||
public boolean setShifted(boolean shiftState) {
|
||||
for (Keyboard.Key shiftKey : mShiftKeys) {
|
||||
if (shiftKey != null) {
|
||||
shiftKey.on = shiftState;
|
||||
}
|
||||
}
|
||||
if (mShifted != shiftState) {
|
||||
mShifted = shiftState;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isShifted() {
|
||||
return mShifted;
|
||||
}
|
||||
|
||||
public int[] getShiftKeyIndices() {
|
||||
return mShiftKeyIndices;
|
||||
}
|
||||
|
||||
public int getShiftKeyIndex() {
|
||||
return mShiftKeyIndices[0];
|
||||
}
|
||||
|
||||
private void computeNearestNeighbors() {
|
||||
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 Keyboard.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回最接近给定点的按键索引。
|
||||
*/
|
||||
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 Keyboard.Row createRowFromXml(Resources res, XmlResourceParser parser) {
|
||||
return new Keyboard.Row(res, this, parser);
|
||||
}
|
||||
|
||||
protected Keyboard.Key createKeyFromXml(Resources res, Keyboard.Row parent, int x, int y,
|
||||
XmlResourceParser parser) {
|
||||
return new Keyboard.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;
|
||||
Keyboard.Key key = null;
|
||||
Keyboard.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) {
|
||||
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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} 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;
|
||||
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) {
|
||||
return Math.round(a.getFraction(index, base, base, defValue));
|
||||
}
|
||||
return defValue;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,148 @@
|
||||
package com.app.input.breeze.board.uiactivity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
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 com.ad.tradpluslibrary.TPAdManager;
|
||||
import com.app.input.breeze.board.App;
|
||||
import com.app.input.breeze.board.R;
|
||||
import com.app.input.breeze.board.bean.CategoryWrapper;
|
||||
import com.app.input.breeze.board.databinding.CategoryScreenBinding;
|
||||
import com.app.input.breeze.board.listutils.CategoryCardAdapter;
|
||||
|
||||
public class CategoryScreen extends AppCompatActivity {
|
||||
|
||||
private CategoryScreenBinding vb;
|
||||
private CategoryCardAdapter categoryCardAdapter;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
vb = CategoryScreenBinding.inflate(getLayoutInflater());
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(vb.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;
|
||||
});
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
setupRecyclerView();
|
||||
setupClickListeners();
|
||||
}
|
||||
|
||||
private void setupRecyclerView() {
|
||||
// 使用 GridLayoutManager,一行 2 个
|
||||
categoryCardAdapter = new CategoryCardAdapter(this, App.list, new CategoryCardAdapter.OnCategoryCardClick() {
|
||||
@Override
|
||||
public void onCategoryClick(String categoryName) {
|
||||
// 点击分类卡片,跳转到分类详情页
|
||||
Intent intent = new Intent(CategoryScreen.this, ThemeListActivity.class);
|
||||
intent.putExtra(ThemeListActivity.KEY_NAME, categoryName);
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
|
||||
vb.categoryRecycler.setLayoutManager(new GridLayoutManager(this, 2));
|
||||
vb.categoryRecycler.setAdapter(categoryCardAdapter);
|
||||
}
|
||||
|
||||
private void setupClickListeners() {
|
||||
setupBottomNavigation();
|
||||
}
|
||||
|
||||
private void setupBottomNavigation() {
|
||||
// 设置默认选中分类
|
||||
updateBottomNavState(R.id.nav_category);
|
||||
|
||||
// 主页点击
|
||||
View navHome = findViewById(R.id.nav_home);
|
||||
navHome.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
startActivity(new Intent(CategoryScreen.this, MainActivity.class));
|
||||
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
|
||||
// 分类点击
|
||||
View navCategory = findViewById(R.id.nav_category);
|
||||
navCategory.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// 已经在分类页,不需要操作
|
||||
updateBottomNavState(R.id.nav_category);
|
||||
}
|
||||
});
|
||||
|
||||
// 设置点击
|
||||
View navSettings = findViewById(R.id.nav_settings);
|
||||
navSettings.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
startActivity(new Intent(CategoryScreen.this, SettingsScreen.class));
|
||||
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateBottomNavState(int selectedId) {
|
||||
int selectedColor = getColor(R.color.blue_primary);
|
||||
int unselectedColor = 0x80000000;
|
||||
|
||||
android.widget.ImageView homeIcon = findViewById(R.id.nav_home_icon);
|
||||
android.widget.TextView homeText = findViewById(R.id.nav_home_text);
|
||||
View homeIndicator = findViewById(R.id.nav_home_indicator);
|
||||
android.widget.ImageView categoryIcon = findViewById(R.id.nav_category_icon);
|
||||
android.widget.TextView categoryText = findViewById(R.id.nav_category_text);
|
||||
View categoryIndicator = findViewById(R.id.nav_category_indicator);
|
||||
android.widget.ImageView settingsIcon = findViewById(R.id.nav_settings_icon);
|
||||
android.widget.TextView settingsText = findViewById(R.id.nav_settings_text);
|
||||
View settingsIndicator = findViewById(R.id.nav_settings_indicator);
|
||||
|
||||
if (selectedId == R.id.nav_home) {
|
||||
homeIcon.setColorFilter(selectedColor);
|
||||
homeText.setTextColor(selectedColor);
|
||||
homeIndicator.setVisibility(View.VISIBLE);
|
||||
categoryIcon.setColorFilter(unselectedColor);
|
||||
categoryText.setTextColor(unselectedColor);
|
||||
categoryIndicator.setVisibility(View.GONE);
|
||||
settingsIcon.setColorFilter(unselectedColor);
|
||||
settingsText.setTextColor(unselectedColor);
|
||||
settingsIndicator.setVisibility(View.GONE);
|
||||
} else if (selectedId == R.id.nav_category) {
|
||||
homeIcon.setColorFilter(unselectedColor);
|
||||
homeText.setTextColor(unselectedColor);
|
||||
homeIndicator.setVisibility(View.GONE);
|
||||
categoryIcon.setColorFilter(selectedColor);
|
||||
categoryText.setTextColor(selectedColor);
|
||||
categoryIndicator.setVisibility(View.VISIBLE);
|
||||
settingsIcon.setColorFilter(unselectedColor);
|
||||
settingsText.setTextColor(unselectedColor);
|
||||
settingsIndicator.setVisibility(View.GONE);
|
||||
} else if (selectedId == R.id.nav_settings) {
|
||||
homeIcon.setColorFilter(unselectedColor);
|
||||
homeText.setTextColor(unselectedColor);
|
||||
homeIndicator.setVisibility(View.GONE);
|
||||
categoryIcon.setColorFilter(unselectedColor);
|
||||
categoryText.setTextColor(unselectedColor);
|
||||
categoryIndicator.setVisibility(View.GONE);
|
||||
settingsIcon.setColorFilter(selectedColor);
|
||||
settingsText.setTextColor(selectedColor);
|
||||
settingsIndicator.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
package com.app.input.breeze.board.uiactivity
|
||||
|
||||
import android.os.Bundle
|
||||
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.showTPAD
|
||||
import com.app.input.breeze.board.bean.KeyboardTheme
|
||||
import com.app.input.breeze.board.database.AppDatabase
|
||||
import com.app.input.breeze.board.database.FavoriteRepository
|
||||
import com.app.input.breeze.board.databinding.FavoriteActivityBinding
|
||||
import com.app.input.breeze.board.listutils.FavoriteThemeAdapter
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class FavoriteThemeActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var vb: FavoriteActivityBinding
|
||||
private lateinit var FavoriteThemeAdapter: FavoriteThemeAdapter
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
vb = FavoriteActivityBinding.inflate(layoutInflater)
|
||||
enableEdgeToEdge()
|
||||
setContentView(vb.root)
|
||||
showTPAD(this) { }
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(android.R.id.content)) { v, insets ->
|
||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
||||
insets
|
||||
}
|
||||
|
||||
setupRecyclerView()
|
||||
observeFavorites()
|
||||
setupClickListeners()
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
FavoriteThemeAdapter = FavoriteThemeAdapter(this).apply {
|
||||
setRemoveLike(object : com.app.input.breeze.board.callback.RemoveFavoriteCallback {
|
||||
override fun OnRemoveLike(data: KeyboardTheme) {
|
||||
lifecycleScope.launch {
|
||||
FavoriteRepository.removeLike(data)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
vb.likeRecycler.apply {
|
||||
adapter = FavoriteThemeAdapter
|
||||
layoutManager = GridLayoutManager(this@FavoriteThemeActivity, 2)
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeFavorites() {
|
||||
AppDatabase.instance.favoriteThemeDao().queryAllLike().observe(this) { favorites ->
|
||||
if (favorites.isNullOrEmpty()) {
|
||||
vb.likeRecycler.visibility = android.view.View.GONE
|
||||
vb.emptyTitle.visibility = android.view.View.VISIBLE
|
||||
} else {
|
||||
vb.likeRecycler.visibility = android.view.View.VISIBLE
|
||||
vb.emptyTitle.visibility = android.view.View.GONE
|
||||
FavoriteThemeAdapter.setForYouList(favorites)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupClickListeners() {
|
||||
vb.back.setOnClickListener {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,403 @@
|
||||
package com.app.input.breeze.board.uiactivity
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.PagerSnapHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.app.input.breeze.board.App
|
||||
import com.app.input.breeze.board.R
|
||||
import com.app.input.breeze.board.bean.KeyboardTheme
|
||||
import com.app.input.breeze.board.bean.CategoryWrapper
|
||||
import com.app.input.breeze.board.databinding.MainActivityBinding
|
||||
import com.app.input.breeze.board.listutils.QuickCategoryAdapter
|
||||
import com.app.input.breeze.board.listutils.FeaturedThemeAdapter
|
||||
import com.app.input.breeze.board.listutils.TrendingThemeAdapter
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var vb: MainActivityBinding
|
||||
private lateinit var FeaturedThemeAdapter: FeaturedThemeAdapter
|
||||
private lateinit var QuickCategoryAdapter: QuickCategoryAdapter
|
||||
private lateinit var TrendingThemeAdapter: TrendingThemeAdapter
|
||||
private lateinit var newAdapter: TrendingThemeAdapter
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
vb = MainActivityBinding.inflate(layoutInflater)
|
||||
enableEdgeToEdge()
|
||||
setContentView(vb.root)
|
||||
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
|
||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
||||
insets
|
||||
}
|
||||
|
||||
initViews()
|
||||
setupHero()
|
||||
setupQuickCategories()
|
||||
setupTrending()
|
||||
setupNew()
|
||||
setupClickListeners()
|
||||
setupBottomNavigation()
|
||||
}
|
||||
|
||||
private fun initViews() {
|
||||
// Hero 区域 - 横向滑动,每次滑动显示一张完整卡片
|
||||
val heroLayoutManager = LinearLayoutManager(this, RecyclerView.HORIZONTAL, false)
|
||||
vb.heroRecycler.layoutManager = heroLayoutManager
|
||||
vb.heroRecycler.isNestedScrollingEnabled = false
|
||||
|
||||
// 使用 PagerSnapHelper 实现每次滑动对齐到一张完整卡片
|
||||
val snapHelper = PagerSnapHelper()
|
||||
snapHelper.attachToRecyclerView(vb.heroRecycler)
|
||||
|
||||
// 快速分类 - 横向滑动
|
||||
vb.quickCategoryRecycler.layoutManager = LinearLayoutManager(this, RecyclerView.HORIZONTAL, false)
|
||||
|
||||
// Trending - 横向滑动
|
||||
vb.trendingRecycler.layoutManager = LinearLayoutManager(this, RecyclerView.HORIZONTAL, false)
|
||||
|
||||
// New - 横向滑动
|
||||
vb.newRecycler.layoutManager = LinearLayoutManager(this, RecyclerView.HORIZONTAL, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 Hero 区域 - 今日精选键盘
|
||||
* 固定显示三个指定的精选主题,支持横向滑动
|
||||
*/
|
||||
private fun setupHero() {
|
||||
val heroList = mutableListOf<KeyboardTheme>()
|
||||
|
||||
// 固定的三个精选主题名称
|
||||
val featuredThemeNames = listOf(
|
||||
"Gravity Water Ice",
|
||||
"Snowflake Gravity",
|
||||
"Pastel Clouds Sky"
|
||||
)
|
||||
|
||||
// 从所有分类中查找这三个主题
|
||||
val allThemes = mutableListOf<KeyboardTheme>()
|
||||
App.list.forEach { wrapper ->
|
||||
allThemes.addAll(wrapper.keyboardList)
|
||||
}
|
||||
|
||||
// 按照指定顺序查找并添加主题
|
||||
featuredThemeNames.forEach { themeName ->
|
||||
val theme = allThemes.find { it.titleName == themeName }
|
||||
if (theme != null) {
|
||||
heroList.add(theme)
|
||||
}
|
||||
}
|
||||
|
||||
FeaturedThemeAdapter = FeaturedThemeAdapter(this, heroList) { KeyboardTheme ->
|
||||
navigateToDetail(KeyboardTheme)
|
||||
}
|
||||
|
||||
// 设置 adapter 并立即刷新
|
||||
vb.heroRecycler.adapter = FeaturedThemeAdapter
|
||||
|
||||
// 确保 RecyclerView 可见
|
||||
if (vb.heroRecycler.visibility != View.VISIBLE) {
|
||||
vb.heroRecycler.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
// 立即通知数据变化
|
||||
FeaturedThemeAdapter.notifyDataSetChanged()
|
||||
|
||||
// 强制请求布局,确保立即显示
|
||||
vb.heroRecycler.requestLayout()
|
||||
|
||||
// 设置指示器
|
||||
setupHeroIndicator(heroList.size)
|
||||
|
||||
// 初始化指示器状态
|
||||
vb.heroRecycler.post {
|
||||
updateHeroIndicator()
|
||||
}
|
||||
|
||||
// 监听滚动状态变化,在滑动结束时更新指示器
|
||||
vb.heroRecycler.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
super.onScrollStateChanged(recyclerView, newState)
|
||||
// 当滑动停止时更新指示器
|
||||
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
|
||||
updateHeroIndicator()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
// 滑动过程中也更新,但主要依赖 SCROLL_STATE_IDLE
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 Hero 指示器
|
||||
*/
|
||||
private fun setupHeroIndicator(count: Int) {
|
||||
vb.heroIndicatorContainer.removeAllViews()
|
||||
|
||||
for (i in 0 until count) {
|
||||
val indicator = View(this).apply {
|
||||
layoutParams = android.widget.LinearLayout.LayoutParams(
|
||||
resources.getDimensionPixelSize(android.R.dimen.app_icon_size) / 4,
|
||||
resources.getDimensionPixelSize(android.R.dimen.app_icon_size) / 4
|
||||
).apply {
|
||||
if (i > 0) {
|
||||
marginStart = resources.getDimensionPixelSize(android.R.dimen.app_icon_size) / 8
|
||||
}
|
||||
}
|
||||
background = if (i == 0) {
|
||||
resources.getDrawable(R.drawable.dot_indicator_selected, theme)
|
||||
} else {
|
||||
resources.getDrawable(R.drawable.dot_indicator, theme)
|
||||
}
|
||||
}
|
||||
vb.heroIndicatorContainer.addView(indicator)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新 Hero 指示器状态
|
||||
*/
|
||||
private fun updateHeroIndicator() {
|
||||
val layoutManager = vb.heroRecycler.layoutManager as? LinearLayoutManager ?: return
|
||||
|
||||
// 使用 PagerSnapHelper 后,findFirstCompletelyVisibleItemPosition 更准确
|
||||
val currentPosition = layoutManager.findFirstCompletelyVisibleItemPosition()
|
||||
|
||||
// 如果找不到完全可见的,使用第一个可见的
|
||||
val position = if (currentPosition != RecyclerView.NO_POSITION) {
|
||||
currentPosition
|
||||
} else {
|
||||
layoutManager.findFirstVisibleItemPosition().coerceAtLeast(0)
|
||||
}.coerceIn(0, vb.heroIndicatorContainer.childCount - 1)
|
||||
|
||||
// 更新指示器状态
|
||||
for (i in 0 until vb.heroIndicatorContainer.childCount) {
|
||||
val indicator = vb.heroIndicatorContainer.getChildAt(i)
|
||||
indicator.background = if (i == position) {
|
||||
resources.getDrawable(R.drawable.dot_indicator_selected, theme)
|
||||
} else {
|
||||
resources.getDrawable(R.drawable.dot_indicator, theme)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置快速分类入口
|
||||
* 显示圆形/胶囊按钮,只是入口,不展示内容
|
||||
* 使用实际数据中存在的分类名称
|
||||
*/
|
||||
private fun setupQuickCategories() {
|
||||
// 快速分类列表:显示名称 -> (实际分类名称, emoji)
|
||||
// 根据实际数据中的分类:recommend, aesthetic, cool, cute, festival, live, love, neon, gravity, super theme
|
||||
// 为每个分类选择更匹配的emoji图标
|
||||
val categoryData = listOf(
|
||||
Triple("Cute", "cute", "💖"), // 粉色爱心 - 可爱
|
||||
Triple("Neon", "neon", "🌌"), // 银河 - 霓虹
|
||||
Triple("Cool", "cool", "😎"), // 墨镜笑脸 - 酷
|
||||
Triple("Aesthetic", "aesthetic", "✨"), // 星星 - 美学
|
||||
Triple("Love", "love", "💕"), // 粉色爱心 - 爱情
|
||||
Triple("Gravity", "gravity", "🌍") // 地球 - 重力
|
||||
)
|
||||
|
||||
// 创建显示列表(名称, emoji)
|
||||
val categories = categoryData.map { it.first to it.third }
|
||||
|
||||
// 创建映射关系(显示名称 -> 实际分类名称)
|
||||
val categoryMapping = categoryData.associate { it.first to it.second }
|
||||
|
||||
QuickCategoryAdapter = QuickCategoryAdapter(this, categories) { displayName ->
|
||||
// 根据显示名称找到对应的实际分类名称
|
||||
val actualCategoryName = categoryMapping[displayName] ?: displayName.lowercase()
|
||||
// 跳转到分类详情页
|
||||
navigateToCategory(actualCategoryName)
|
||||
}
|
||||
vb.quickCategoryRecycler.adapter = QuickCategoryAdapter
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 Trending 推荐
|
||||
* 从 recommend 分类获取数据,横向滑动
|
||||
*/
|
||||
private fun setupTrending() {
|
||||
val trendingList = mutableListOf<KeyboardTheme>()
|
||||
|
||||
val recommendWrapper = App.list.find { it.parentName == "recommend" }
|
||||
recommendWrapper?.keyboardList?.take(10)?.let {
|
||||
trendingList.addAll(it)
|
||||
}
|
||||
|
||||
TrendingThemeAdapter = TrendingThemeAdapter(this, trendingList) { KeyboardTheme ->
|
||||
navigateToDetail(KeyboardTheme)
|
||||
}
|
||||
vb.trendingRecycler.adapter = TrendingThemeAdapter
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 New 推荐
|
||||
* 从除了 recommend 之外的其他分类中混合获取主题
|
||||
*/
|
||||
private fun setupNew() {
|
||||
val newList = mutableListOf<KeyboardTheme>()
|
||||
|
||||
// 从所有分类中收集主题(排除 recommend)
|
||||
val otherCategories = App.list.filter {
|
||||
it.parentName != "recommend"
|
||||
}
|
||||
|
||||
// 从多个分类中混合获取主题,每个分类取前几个
|
||||
otherCategories.forEachIndexed { index, wrapper ->
|
||||
if (index < 3 && newList.size < 10) { // 最多从3个分类获取,总共不超过10个
|
||||
val takeCount = (10 - newList.size).coerceAtMost(4) // 每个分类最多取4个
|
||||
wrapper.keyboardList.take(takeCount).let {
|
||||
newList.addAll(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果还是没有数据,从第一个非 recommend 分类获取
|
||||
if (newList.isEmpty() && App.list.size > 1) {
|
||||
App.list.find { it.parentName != "recommend" }?.keyboardList?.take(10)?.let {
|
||||
newList.addAll(it)
|
||||
}
|
||||
}
|
||||
|
||||
// 打乱顺序,让不同分类的主题混合显示
|
||||
val shuffled = newList.shuffled()
|
||||
|
||||
newAdapter = TrendingThemeAdapter(this, shuffled) { KeyboardTheme ->
|
||||
navigateToDetail(KeyboardTheme)
|
||||
}
|
||||
vb.newRecycler.adapter = newAdapter
|
||||
}
|
||||
|
||||
private fun setupClickListeners() {
|
||||
// 跳转到收藏页面
|
||||
vb.btnFavorite.setOnClickListener {
|
||||
startActivity(Intent(this, FavoriteThemeActivity::class.java))
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupBottomNavigation() {
|
||||
// 设置默认选中主页
|
||||
updateBottomNavState(R.id.nav_home)
|
||||
|
||||
// 主页点击
|
||||
findViewById<View>(R.id.nav_home).setOnClickListener {
|
||||
// 已经在主页,不需要操作
|
||||
updateBottomNavState(R.id.nav_home)
|
||||
}
|
||||
|
||||
// 分类点击
|
||||
findViewById<View>(R.id.nav_category).setOnClickListener {
|
||||
startActivity(Intent(this, CategoryScreen::class.java))
|
||||
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
|
||||
finish()
|
||||
}
|
||||
|
||||
// 设置点击
|
||||
findViewById<View>(R.id.nav_settings).setOnClickListener {
|
||||
startActivity(Intent(this, SettingsScreen::class.java))
|
||||
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateBottomNavState(selectedId: Int) {
|
||||
val selectedColor = getColor(R.color.blue_primary)
|
||||
val unselectedColor = 0x80000000.toInt()
|
||||
|
||||
// 重置所有状态
|
||||
val homeIcon = findViewById<android.widget.ImageView>(R.id.nav_home_icon)
|
||||
val homeText = findViewById<android.widget.TextView>(R.id.nav_home_text)
|
||||
val homeIndicator = findViewById<View>(R.id.nav_home_indicator)
|
||||
val categoryIcon = findViewById<android.widget.ImageView>(R.id.nav_category_icon)
|
||||
val categoryText = findViewById<android.widget.TextView>(R.id.nav_category_text)
|
||||
val categoryIndicator = findViewById<View>(R.id.nav_category_indicator)
|
||||
val settingsIcon = findViewById<android.widget.ImageView>(R.id.nav_settings_icon)
|
||||
val settingsText = findViewById<android.widget.TextView>(R.id.nav_settings_text)
|
||||
val settingsIndicator = findViewById<View>(R.id.nav_settings_indicator)
|
||||
|
||||
when (selectedId) {
|
||||
R.id.nav_home -> {
|
||||
homeIcon.setColorFilter(selectedColor)
|
||||
homeText.setTextColor(selectedColor)
|
||||
homeIndicator.visibility = View.VISIBLE
|
||||
categoryIcon.setColorFilter(unselectedColor)
|
||||
categoryText.setTextColor(unselectedColor)
|
||||
categoryIndicator.visibility = View.GONE
|
||||
settingsIcon.setColorFilter(unselectedColor)
|
||||
settingsText.setTextColor(unselectedColor)
|
||||
settingsIndicator.visibility = View.GONE
|
||||
}
|
||||
R.id.nav_category -> {
|
||||
homeIcon.setColorFilter(unselectedColor)
|
||||
homeText.setTextColor(unselectedColor)
|
||||
homeIndicator.visibility = View.GONE
|
||||
categoryIcon.setColorFilter(selectedColor)
|
||||
categoryText.setTextColor(selectedColor)
|
||||
categoryIndicator.visibility = View.VISIBLE
|
||||
settingsIcon.setColorFilter(unselectedColor)
|
||||
settingsText.setTextColor(unselectedColor)
|
||||
settingsIndicator.visibility = View.GONE
|
||||
}
|
||||
R.id.nav_settings -> {
|
||||
homeIcon.setColorFilter(unselectedColor)
|
||||
homeText.setTextColor(unselectedColor)
|
||||
homeIndicator.visibility = View.GONE
|
||||
categoryIcon.setColorFilter(unselectedColor)
|
||||
categoryText.setTextColor(unselectedColor)
|
||||
categoryIndicator.visibility = View.GONE
|
||||
settingsIcon.setColorFilter(selectedColor)
|
||||
settingsText.setTextColor(selectedColor)
|
||||
settingsIndicator.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigateToDetail(KeyboardTheme: KeyboardTheme) {
|
||||
val intent = Intent(this, ThemeDetailActivity::class.java).apply {
|
||||
putExtra(ThemeDetailActivity.SOURCE_KEY, KeyboardTheme)
|
||||
putExtra(ThemeDetailActivity.DISPLAY_URL_KEY, KeyboardTheme.imgPath)
|
||||
putExtra(ThemeDetailActivity.ZIP_URL_KEY, KeyboardTheme.zipPath)
|
||||
putExtra(ThemeDetailActivity.NAME_KEY, KeyboardTheme.titleName)
|
||||
putExtra(ThemeDetailActivity.GIF_KEY, KeyboardTheme.imgGif)
|
||||
val thumbGif = KeyboardTheme.thumbGif
|
||||
val thumb = if (thumbGif != null && thumbGif.isNotEmpty()) {
|
||||
thumbGif
|
||||
} else {
|
||||
KeyboardTheme.thumbUrl
|
||||
}
|
||||
putExtra(ThemeDetailActivity.THUMB_KEY, thumb)
|
||||
}
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
private fun navigateToCategory(categoryName: String) {
|
||||
// 查找对应的分类(精确匹配,不区分大小写)
|
||||
val categoryWrapper = App.list.find {
|
||||
it.parentName.equals(categoryName, ignoreCase = true)
|
||||
}
|
||||
|
||||
if (categoryWrapper != null) {
|
||||
val intent = Intent(this, ThemeListActivity::class.java).apply {
|
||||
putExtra(ThemeListActivity.KEY_NAME, categoryWrapper.parentName)
|
||||
}
|
||||
startActivity(intent)
|
||||
} else {
|
||||
// 如果找不到,跳转到分类页面(让用户自己选择)
|
||||
startActivity(Intent(this, CategoryScreen::class.java))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,136 @@
|
||||
package com.app.input.breeze.board.uiactivity;
|
||||
|
||||
import static com.bumptech.glide.request.RequestOptions.bitmapTransform;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.annotation.NonNull;
|
||||
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.app.input.breeze.board.R;
|
||||
import com.app.input.breeze.board.databinding.PreviewActivityBinding;
|
||||
import com.app.input.breeze.board.utils.KeyboardConstants;
|
||||
import com.app.input.breeze.board.utils.AppUtils;
|
||||
import com.app.input.breeze.board.utils.ThemePreferences;
|
||||
import com.bumptech.glide.Glide;
|
||||
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 jp.wasabeef.glide.transformations.BlurTransformation;
|
||||
|
||||
|
||||
public class PreviewActivity extends AppCompatActivity {
|
||||
private PreviewActivityBinding vb;
|
||||
public static String key_name = "key_name";
|
||||
private int mPreviousKeyboardHeight = -1;
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
vb = PreviewActivityBinding.inflate(getLayoutInflater());
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(vb.getRoot());
|
||||
TPAdManager.INSTANCE.showTPAD(this,()-> null);
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void onInit() {
|
||||
|
||||
String stringExtra = getIntent().getStringExtra(key_name);
|
||||
vb.title.setText(stringExtra);
|
||||
|
||||
String curPath = ThemePreferences.INSTANCE.getSkinPath();
|
||||
|
||||
vb.idBack.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
TPAdManager.INSTANCE.showTPAD(PreviewActivity.this,()-> {
|
||||
finish();
|
||||
return null;
|
||||
} );
|
||||
}
|
||||
});
|
||||
if (curPath == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String bgPath = curPath+"res/drawable-xxhdpi-v4/"+ KeyboardConstants.previewBg;
|
||||
|
||||
Drawable bgDraw = AppUtils.INSTANCE.getBgDrawable(this, bgPath);
|
||||
if (bgDraw != null) {
|
||||
|
||||
Glide.with(this)
|
||||
.load(bgDraw)
|
||||
.apply(bitmapTransform(new BlurTransformation(15, 3))) // 设置模糊半径和模糊采样
|
||||
.listener(new RequestListener<Drawable>() {
|
||||
@Override
|
||||
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> target, boolean isFirstResource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target<Drawable> target, @NonNull DataSource dataSource, boolean isFirstResource) {
|
||||
vb.main.setBackground(resource);
|
||||
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.preload();
|
||||
|
||||
}
|
||||
keyboardheight();
|
||||
vb.et.requestFocus();
|
||||
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
|
||||
|
||||
}
|
||||
|
||||
|
||||
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,211 @@
|
||||
package com.app.input.breeze.board.uiactivity
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.provider.Settings
|
||||
import android.view.View
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.app.input.breeze.board.BuildConfig
|
||||
import com.app.input.breeze.board.R
|
||||
import com.app.input.breeze.board.databinding.SettingsActivityBinding
|
||||
import com.app.input.breeze.board.utils.AppUtils
|
||||
|
||||
class SettingsScreen : AppCompatActivity() {
|
||||
|
||||
private lateinit var vb: SettingsActivityBinding
|
||||
private var inputMethodReceiver: BroadcastReceiver? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
vb = SettingsActivityBinding.inflate(layoutInflater)
|
||||
enableEdgeToEdge()
|
||||
setContentView(vb.root)
|
||||
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
|
||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
||||
insets
|
||||
}
|
||||
|
||||
setupClickListeners()
|
||||
setupBottomNavigation()
|
||||
registerInputMethodReceiver()
|
||||
setupVersion()
|
||||
}
|
||||
|
||||
private fun setupClickListeners() {
|
||||
// 启用键盘设置
|
||||
vb.layoutEnableKeyboard.setOnClickListener {
|
||||
startActivity(Intent(Settings.ACTION_INPUT_METHOD_SETTINGS))
|
||||
}
|
||||
|
||||
// 设置默认键盘(只有在第一步完成后才能点击)
|
||||
vb.layoutSetDefaultKeyboard.setOnClickListener {
|
||||
val isEnabled = AppUtils.checkEnable(this)
|
||||
if (isEnabled) {
|
||||
val inputMethodManager = getSystemService(INPUT_METHOD_SERVICE) as android.view.inputmethod.InputMethodManager
|
||||
inputMethodManager.showInputMethodPicker()
|
||||
} else {
|
||||
// 如果第一步未完成,提示用户先完成第一步
|
||||
android.widget.Toast.makeText(
|
||||
this,
|
||||
getString(R.string.please_enable_keyboard_first),
|
||||
android.widget.Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
|
||||
// 版本(不再需要点击事件,只显示版本号)
|
||||
}
|
||||
|
||||
private fun setupBottomNavigation() {
|
||||
// 设置默认选中设置
|
||||
updateBottomNavState(R.id.nav_settings)
|
||||
|
||||
// 主页点击
|
||||
findViewById<View>(R.id.nav_home).setOnClickListener {
|
||||
startActivity(Intent(this, MainActivity::class.java))
|
||||
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
|
||||
finish()
|
||||
}
|
||||
|
||||
// 分类点击
|
||||
findViewById<View>(R.id.nav_category).setOnClickListener {
|
||||
startActivity(Intent(this, CategoryScreen::class.java))
|
||||
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
|
||||
finish()
|
||||
}
|
||||
|
||||
// 设置点击
|
||||
findViewById<View>(R.id.nav_settings).setOnClickListener {
|
||||
// 已经在设置页,不需要操作
|
||||
updateBottomNavState(R.id.nav_settings)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateBottomNavState(selectedId: Int) {
|
||||
val selectedColor = getColor(R.color.blue_primary)
|
||||
val unselectedColor = 0x80000000.toInt()
|
||||
|
||||
val homeIcon = findViewById<android.widget.ImageView>(R.id.nav_home_icon)
|
||||
val homeText = findViewById<android.widget.TextView>(R.id.nav_home_text)
|
||||
val homeIndicator = findViewById<View>(R.id.nav_home_indicator)
|
||||
val categoryIcon = findViewById<android.widget.ImageView>(R.id.nav_category_icon)
|
||||
val categoryText = findViewById<android.widget.TextView>(R.id.nav_category_text)
|
||||
val categoryIndicator = findViewById<View>(R.id.nav_category_indicator)
|
||||
val settingsIcon = findViewById<android.widget.ImageView>(R.id.nav_settings_icon)
|
||||
val settingsText = findViewById<android.widget.TextView>(R.id.nav_settings_text)
|
||||
val settingsIndicator = findViewById<View>(R.id.nav_settings_indicator)
|
||||
|
||||
when (selectedId) {
|
||||
R.id.nav_home -> {
|
||||
homeIcon.setColorFilter(selectedColor)
|
||||
homeText.setTextColor(selectedColor)
|
||||
homeIndicator.visibility = View.VISIBLE
|
||||
categoryIcon.setColorFilter(unselectedColor)
|
||||
categoryText.setTextColor(unselectedColor)
|
||||
categoryIndicator.visibility = View.GONE
|
||||
settingsIcon.setColorFilter(unselectedColor)
|
||||
settingsText.setTextColor(unselectedColor)
|
||||
settingsIndicator.visibility = View.GONE
|
||||
}
|
||||
R.id.nav_category -> {
|
||||
homeIcon.setColorFilter(unselectedColor)
|
||||
homeText.setTextColor(unselectedColor)
|
||||
homeIndicator.visibility = View.GONE
|
||||
categoryIcon.setColorFilter(selectedColor)
|
||||
categoryText.setTextColor(selectedColor)
|
||||
categoryIndicator.visibility = View.VISIBLE
|
||||
settingsIcon.setColorFilter(unselectedColor)
|
||||
settingsText.setTextColor(unselectedColor)
|
||||
settingsIndicator.visibility = View.GONE
|
||||
}
|
||||
R.id.nav_settings -> {
|
||||
homeIcon.setColorFilter(unselectedColor)
|
||||
homeText.setTextColor(unselectedColor)
|
||||
homeIndicator.visibility = View.GONE
|
||||
categoryIcon.setColorFilter(unselectedColor)
|
||||
categoryText.setTextColor(unselectedColor)
|
||||
categoryIndicator.visibility = View.GONE
|
||||
settingsIcon.setColorFilter(selectedColor)
|
||||
settingsText.setTextColor(selectedColor)
|
||||
settingsIndicator.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
// 延迟更新,确保系统状态已更新
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
updateKeyboardStatus()
|
||||
}, 300)
|
||||
}
|
||||
|
||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||
super.onWindowFocusChanged(hasFocus)
|
||||
if (hasFocus) {
|
||||
// 当窗口获得焦点时更新状态(用户从系统设置返回时)
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
updateKeyboardStatus()
|
||||
}, 200)
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerInputMethodReceiver() {
|
||||
inputMethodReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
// 当输入法状态改变时自动更新
|
||||
updateKeyboardStatus()
|
||||
}
|
||||
}
|
||||
val filter = IntentFilter(Intent.ACTION_INPUT_METHOD_CHANGED)
|
||||
registerReceiver(inputMethodReceiver, filter)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
// 注销广播接收器
|
||||
inputMethodReceiver?.let {
|
||||
try {
|
||||
unregisterReceiver(it)
|
||||
} catch (e: Exception) {
|
||||
// 忽略已注销的异常
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupVersion() {
|
||||
// 从 BuildConfig 获取版本号
|
||||
vb.tvVersion.text = BuildConfig.VERSION_NAME
|
||||
}
|
||||
|
||||
private fun updateKeyboardStatus() {
|
||||
val isEnabled = AppUtils.checkEnable(this)
|
||||
val isDefault = AppUtils.checkSetDefault(this)
|
||||
|
||||
vb.tvEnableStatus.text = if (isEnabled) getString(R.string.enabled) else getString(R.string.not_enabled)
|
||||
vb.tvDefaultStatus.text = if (isDefault) getString(R.string.set_as_default) else getString(R.string.not_set_as_default)
|
||||
|
||||
// 根据第一步是否完成来控制第二步的可用性和视觉状态
|
||||
if (isEnabled) {
|
||||
// 第一步已完成,第二步可用
|
||||
vb.layoutSetDefaultKeyboard.isEnabled = true
|
||||
vb.layoutSetDefaultKeyboard.alpha = 1.0f
|
||||
} else {
|
||||
// 第一步未完成,第二步禁用
|
||||
vb.layoutSetDefaultKeyboard.isEnabled = false
|
||||
vb.layoutSetDefaultKeyboard.alpha = 0.5f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,197 @@
|
||||
package com.app.input.breeze.board.uiactivity
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.animation.ObjectAnimator
|
||||
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.view.View
|
||||
import android.view.animation.AccelerateDecelerateInterpolator
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import com.ad.tradpluslibrary.TPAdManager
|
||||
import com.app.input.breeze.board.App
|
||||
import com.app.input.breeze.board.R
|
||||
import com.app.input.breeze.board.utils.AppUtils
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
||||
/**
|
||||
* 不要修改启动页继承Activity这点
|
||||
*/
|
||||
class SplashActivity : Activity() {
|
||||
|
||||
private lateinit var progressBar: ProgressBar
|
||||
private lateinit var loadingText: TextView
|
||||
private lateinit var logoView: View
|
||||
private var countTime = 15000L
|
||||
private lateinit var timer: CountDownTimer
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// 全屏沉浸式设置
|
||||
setupImmersiveMode()
|
||||
|
||||
setContentView(R.layout.splash_activity)
|
||||
|
||||
progressBar = findViewById(R.id.nova_progress)
|
||||
loadingText = findViewById(R.id.loading_text)
|
||||
logoView = findViewById(R.id.image)
|
||||
|
||||
init()
|
||||
}
|
||||
|
||||
private fun setupImmersiveMode() {
|
||||
val decorView = window.decorView
|
||||
// 内容延伸到系统栏下方,但保持系统栏可见
|
||||
val flags = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
|
||||
decorView.systemUiVisibility = flags
|
||||
|
||||
// 设置状态栏透明,导航栏白色
|
||||
window.statusBarColor = android.graphics.Color.TRANSPARENT
|
||||
window.navigationBarColor = android.graphics.Color.WHITE
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
// 保持全屏沉浸式状态
|
||||
setupImmersiveMode()
|
||||
}
|
||||
|
||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||
super.onWindowFocusChanged(hasFocus)
|
||||
if (hasFocus) {
|
||||
// 保持全屏沉浸式状态
|
||||
setupImmersiveMode()
|
||||
}
|
||||
}
|
||||
|
||||
private fun init() {
|
||||
// 启动动画
|
||||
startAnimations()
|
||||
|
||||
TPAdManager.init(
|
||||
this,
|
||||
App.TAG,
|
||||
"491CF93F7A3E504CC90F215A3B43D011",
|
||||
"163EB8E51E42672FDBB21327BF580412",
|
||||
"ACF89F38DB6757D632C0ED73E4E3F712",
|
||||
"56A126D9C53FCAEAFCEADB9FA6070612"
|
||||
) {
|
||||
|
||||
}
|
||||
timer = TPAdManager.showWelcomeAd(this, countTime, { millisUntilFinished ->
|
||||
//倒计时更新
|
||||
val progressPercentage = (100 * millisUntilFinished) / countTime
|
||||
val percentage = 100 - progressPercentage
|
||||
progressBar.progress = percentage.toInt()
|
||||
|
||||
// 更新加载文字
|
||||
updateLoadingText(percentage.toInt())
|
||||
}) {
|
||||
//跳转首页
|
||||
progressBar.progress = 100
|
||||
updateLoadingText(100)
|
||||
|
||||
// 延迟一点再跳转,让用户看到完成状态
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
toHome()
|
||||
}, 200)
|
||||
}
|
||||
timer.start()
|
||||
}
|
||||
|
||||
private fun startAnimations() {
|
||||
// Logo 淡入和缩放动画
|
||||
logoView.alpha = 0f
|
||||
logoView.scaleX = 0.8f
|
||||
logoView.scaleY = 0.8f
|
||||
|
||||
val logoFadeIn = ObjectAnimator.ofFloat(logoView, "alpha", 0f, 1f)
|
||||
val logoScaleX = ObjectAnimator.ofFloat(logoView, "scaleX", 0.8f, 1f)
|
||||
val logoScaleY = ObjectAnimator.ofFloat(logoView, "scaleY", 0.8f, 1f)
|
||||
|
||||
logoFadeIn.duration = 600
|
||||
logoScaleX.duration = 600
|
||||
logoScaleY.duration = 600
|
||||
|
||||
logoFadeIn.interpolator = DecelerateInterpolator()
|
||||
logoScaleX.interpolator = DecelerateInterpolator()
|
||||
logoScaleY.interpolator = DecelerateInterpolator()
|
||||
|
||||
logoFadeIn.start()
|
||||
logoScaleX.start()
|
||||
logoScaleY.start()
|
||||
|
||||
// 应用名称淡入
|
||||
val appNameView = findViewById<View>(R.id.tv)
|
||||
appNameView.alpha = 0f
|
||||
appNameView.translationY = 20f
|
||||
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
val nameFadeIn = ObjectAnimator.ofFloat(appNameView, "alpha", 0f, 1f)
|
||||
val nameTranslate = ObjectAnimator.ofFloat(appNameView, "translationY", 20f, 0f)
|
||||
|
||||
nameFadeIn.duration = 500
|
||||
nameTranslate.duration = 500
|
||||
|
||||
nameFadeIn.interpolator = DecelerateInterpolator()
|
||||
nameTranslate.interpolator = DecelerateInterpolator()
|
||||
|
||||
nameFadeIn.start()
|
||||
nameTranslate.start()
|
||||
}, 300)
|
||||
|
||||
// 进度条淡入
|
||||
progressBar.alpha = 0f
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
val progressFadeIn = ObjectAnimator.ofFloat(progressBar, "alpha", 0f, 1f)
|
||||
progressFadeIn.duration = 400
|
||||
progressFadeIn.start()
|
||||
|
||||
loadingText.alpha = 0f
|
||||
val textFadeIn = ObjectAnimator.ofFloat(loadingText, "alpha", 0f, 1f)
|
||||
textFadeIn.duration = 400
|
||||
textFadeIn.start()
|
||||
}, 600)
|
||||
}
|
||||
|
||||
private fun updateLoadingText(progress: Int) {
|
||||
when {
|
||||
progress < 30 -> loadingText.text = getString(R.string.initializing)
|
||||
progress < 60 -> loadingText.text = getString(R.string.loading_themes)
|
||||
progress < 90 -> loadingText.text = getString(R.string.preparing_interface)
|
||||
else -> loadingText.text = getString(R.string.almost_done)
|
||||
}
|
||||
}
|
||||
|
||||
private fun toHome() {
|
||||
// 淡出动画
|
||||
val fadeOut = ObjectAnimator.ofFloat(findViewById<View>(R.id.main), "alpha", 1f, 0f)
|
||||
fadeOut.duration = 300
|
||||
fadeOut.interpolator = AccelerateDecelerateInterpolator()
|
||||
fadeOut.addListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
startActivity(Intent(this@SplashActivity, MainActivity::class.java))
|
||||
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
|
||||
finish()
|
||||
}
|
||||
})
|
||||
fadeOut.start()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
if (::timer.isInitialized) {
|
||||
timer.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,376 @@
|
||||
package com.app.input.breeze.board.uiactivity
|
||||
|
||||
import android.animation.AnimatorSet
|
||||
import android.animation.ObjectAnimator
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
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.showTPAD
|
||||
import com.app.input.breeze.board.App
|
||||
import com.app.input.breeze.board.R
|
||||
import com.app.input.breeze.board.bean.KeyboardTheme
|
||||
import com.app.input.breeze.board.callback.ItemClickListener
|
||||
import com.app.input.breeze.board.callback.KeyboardApplyCallback
|
||||
import com.app.input.breeze.board.database.FavoriteRepository
|
||||
import com.app.input.breeze.board.listutils.RecommendedThemeAdapter
|
||||
import com.app.input.breeze.board.uifragment.EnableKeyboardDialog
|
||||
import com.app.input.breeze.board.utils.AppUtils
|
||||
import com.app.input.breeze.board.utils.ThemePreferences
|
||||
import com.app.input.breeze.board.utils.ZipFileHandler
|
||||
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 kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
|
||||
|
||||
class ThemeDetailActivity : 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: EnableKeyboardDialog? = 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 recommendedRecycler: RecyclerView
|
||||
// private lateinit var viewAllLayout: LinearLayout
|
||||
|
||||
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: KeyboardTheme
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.theme_detail_activity)
|
||||
this.enableEdgeToEdge()
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
findViewById<View?>(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()
|
||||
initLikeState()
|
||||
}
|
||||
|
||||
|
||||
private fun getExtraData() {
|
||||
data = intent.getSerializableExtra(SOURCE_KEY) as KeyboardTheme
|
||||
|
||||
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 = ZipFileHandler.getServiceZipName(zipUrl)
|
||||
unzipPath = ZipFileHandler.getUnzipPath(serviceZipName)
|
||||
|
||||
Log.d("-------------------", "-------unzipPath=" + unzipPath)
|
||||
|
||||
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 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)
|
||||
// viewAllLayout = findViewById(R.id.layout_view_all)
|
||||
|
||||
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 displayData() {
|
||||
textName.text = name
|
||||
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) {
|
||||
FavoriteRepository.addLike(data)
|
||||
} else {
|
||||
FavoriteRepository.removeLike(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val forYouList = App.Companion.list.filter {
|
||||
it.parentName == getString(R.string.recommend_name)
|
||||
|
||||
}
|
||||
|
||||
recommendedRecycler.run {
|
||||
adapter = RecommendedThemeAdapter(
|
||||
this@ThemeDetailActivity
|
||||
).apply {
|
||||
val shuffled = forYouList[0].keyboardList.shuffled()
|
||||
setForYouList(shuffled)
|
||||
}.apply {
|
||||
setClickAction(object : ItemClickListener {
|
||||
override fun OnItemClickListener() {
|
||||
finish()
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
layoutManager = GridLayoutManager(this@ThemeDetailActivity, 2)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@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)
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private fun setApply() {
|
||||
// 添加按下态缩放动画
|
||||
applyBtn.setOnTouchListener { v, event ->
|
||||
when (event.action) {
|
||||
android.view.MotionEvent.ACTION_DOWN -> {
|
||||
val scaleX = ObjectAnimator.ofFloat(v, "scaleX", 1f, 0.95f)
|
||||
val scaleY = ObjectAnimator.ofFloat(v, "scaleY", 1f, 0.95f)
|
||||
val animatorSet = AnimatorSet()
|
||||
animatorSet.playTogether(scaleX, scaleY)
|
||||
animatorSet.duration = 100
|
||||
animatorSet.start()
|
||||
}
|
||||
android.view.MotionEvent.ACTION_UP, android.view.MotionEvent.ACTION_CANCEL -> {
|
||||
val scaleX = ObjectAnimator.ofFloat(v, "scaleX", 0.95f, 1f)
|
||||
val scaleY = ObjectAnimator.ofFloat(v, "scaleY", 0.95f, 1f)
|
||||
val animatorSet = AnimatorSet()
|
||||
animatorSet.playTogether(scaleX, scaleY)
|
||||
animatorSet.duration = 100
|
||||
animatorSet.start()
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
applyBtn.setOnClickListener {
|
||||
val checkEnable = AppUtils.checkEnable(this)
|
||||
val checkSetDefault = AppUtils.checkSetDefault(this)
|
||||
if (!checkEnable || !checkSetDefault) {
|
||||
showDialog()
|
||||
return@setOnClickListener
|
||||
}
|
||||
startDown()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun showDialog() {
|
||||
dialog = dialog ?: EnableKeyboardDialog.newInstance() .apply{
|
||||
|
||||
|
||||
}
|
||||
dialog?.setClickListener {
|
||||
startDown()
|
||||
}
|
||||
dialog?.show(supportFragmentManager, "")
|
||||
}
|
||||
|
||||
private fun startDown() {
|
||||
loadingLayout.isVisible = true
|
||||
applyBtn.isEnabled = false
|
||||
|
||||
val file = File(unzipPath)
|
||||
if (file.exists()) {
|
||||
val findFirstDirectory = ZipFileHandler.findFirstDirectory(file)
|
||||
|
||||
apply("${findFirstDirectory}/")
|
||||
applyBtn.isEnabled = true
|
||||
loadingLayout.isVisible = false
|
||||
} else {
|
||||
ZipFileHandler.startDownloadZip(zipUrl, object :
|
||||
KeyboardApplyCallback {
|
||||
override fun OnApplySkinListener(fileList: List<File?>?) {
|
||||
runOnUiThread {
|
||||
applyBtn.isEnabled = true
|
||||
loadingLayout.isVisible = false
|
||||
}
|
||||
if (fileList.isNullOrEmpty()) {
|
||||
runOnUiThread {
|
||||
Toast.makeText(
|
||||
this@ThemeDetailActivity,
|
||||
getString(R.string.download_fail),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
} else {
|
||||
// lifecycleScope.launch {
|
||||
// DbFunction.addDownload(data)
|
||||
// }
|
||||
if (file.exists()) {
|
||||
val findFirstDirectory = ZipFileHandler.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")
|
||||
}
|
||||
|
||||
|
||||
ThemePreferences.updateSkinPath(skinParentPath)
|
||||
Toast.makeText(
|
||||
this@ThemeDetailActivity,
|
||||
getString(R.string.theme_application_successful),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
startActivity(Intent(this, PreviewActivity::class.java).apply {
|
||||
putExtra(PreviewActivity.key_name, name)
|
||||
})
|
||||
finish()
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化收藏状态
|
||||
*/
|
||||
private fun initLikeState() {
|
||||
lifecycleScope.launch {
|
||||
FavoriteRepository.getIsLike(name) { isLiked ->
|
||||
imgLike.isSelected = isLiked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
package com.app.input.breeze.board.uiactivity;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
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 com.ad.tradpluslibrary.TPAdManager;
|
||||
import com.app.input.breeze.board.App;
|
||||
import com.app.input.breeze.board.R;
|
||||
import com.app.input.breeze.board.bean.KeyboardTheme;
|
||||
import com.app.input.breeze.board.bean.CategoryWrapper;
|
||||
import com.app.input.breeze.board.databinding.ThemeListScreenBinding;
|
||||
import com.app.input.breeze.board.listutils.ThemeGridAdapter;
|
||||
import com.app.input.breeze.board.utils.AppUtils;
|
||||
import com.app.input.breeze.board.utils.GridItemDecoration;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ThemeListActivity extends AppCompatActivity {
|
||||
private ThemeListScreenBinding vb;
|
||||
public static final String KEY_NAME ="class_name";
|
||||
private String name;
|
||||
private List<KeyboardTheme> data;
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
vb = ThemeListScreenBinding.inflate(getLayoutInflater());
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(vb.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;
|
||||
});
|
||||
name = getIntent().getStringExtra(KEY_NAME);
|
||||
TPAdManager.INSTANCE.showTPAD(this,()-> null);
|
||||
initData();
|
||||
initClick();
|
||||
}
|
||||
|
||||
private void initData(){
|
||||
vb.className.setText(name);
|
||||
for (CategoryWrapper CategoryWrapper : App.list) {
|
||||
if(CategoryWrapper.getParentName().equals(name)){
|
||||
data = CategoryWrapper.getKeyboardList();
|
||||
}
|
||||
}
|
||||
// 两列布局,增大上下间距
|
||||
GridItemDecoration GridItemDecoration = new GridItemDecoration(16, 12, 0);
|
||||
ThemeGridAdapter adapterMain = new ThemeGridAdapter(this,data);
|
||||
vb.recycler.setLayoutManager(new GridLayoutManager(ThemeListActivity.this,2));
|
||||
vb.recycler.setAdapter(adapterMain);
|
||||
vb.recycler.addItemDecoration(GridItemDecoration);
|
||||
|
||||
}
|
||||
private void initClick(){
|
||||
vb.back.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
TPAdManager.INSTANCE.showTPAD(ThemeListActivity.this,()-> {
|
||||
finish();
|
||||
return null;
|
||||
} );
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,181 @@
|
||||
package com.app.input.breeze.board.uifragment
|
||||
|
||||
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.app.input.breeze.board.App
|
||||
import com.app.input.breeze.board.R
|
||||
import com.app.input.breeze.board.databinding.DialogEnableKeyboardBinding
|
||||
import com.app.input.breeze.board.utils.AppUtils
|
||||
import com.app.input.breeze.board.utils.BreezeTextView
|
||||
|
||||
|
||||
class EnableKeyboardDialog : DialogFragment() {
|
||||
|
||||
private lateinit var vb: DialogEnableKeyboardBinding
|
||||
|
||||
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: BreezeTextView
|
||||
private lateinit var stepTwo: BreezeTextView
|
||||
|
||||
private lateinit var context: Context
|
||||
|
||||
private var clickAction: (() -> Unit )? = null
|
||||
|
||||
|
||||
|
||||
companion object {
|
||||
fun newInstance(): EnableKeyboardDialog {
|
||||
val fragment = EnableKeyboardDialog()
|
||||
return fragment
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun setClickListener(action:() -> Unit){
|
||||
clickAction = action
|
||||
}
|
||||
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
vb = DialogEnableKeyboardBinding.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 = AppUtils.checkEnable(App.Companion.appInstance)
|
||||
val checkSetDefault = AppUtils.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,76 @@
|
||||
package com.app.input.breeze.board.uifragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.app.input.breeze.board.App
|
||||
import com.app.input.breeze.board.bean.KeyboardTheme
|
||||
import com.app.input.breeze.board.databinding.FragmentFavoriteKeyboardBinding
|
||||
import com.app.input.breeze.board.database.AppDatabase
|
||||
import com.app.input.breeze.board.database.FavoriteRepository
|
||||
import com.app.input.breeze.board.callback.RemoveFavoriteCallback
|
||||
import com.app.input.breeze.board.listutils.FavoriteThemeAdapter
|
||||
import com.app.input.breeze.board.utils.BreezeTextView
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class FavoriteKeyboardFragment : Fragment() {
|
||||
private lateinit var vb: FragmentFavoriteKeyboardBinding
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun newInstance() =
|
||||
FavoriteKeyboardFragment()
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
vb = FragmentFavoriteKeyboardBinding.inflate(layoutInflater)
|
||||
|
||||
init()
|
||||
|
||||
return vb.root
|
||||
}
|
||||
|
||||
|
||||
private fun init() {
|
||||
val mainAdapter = FavoriteThemeAdapter(
|
||||
requireContext()
|
||||
).apply {
|
||||
setRemoveLike(object : RemoveFavoriteCallback {
|
||||
override fun OnRemoveLike(data: KeyboardTheme) {
|
||||
lifecycleScope.launch {
|
||||
FavoriteRepository.removeLike(data)
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
vb.likeRecycler.run {
|
||||
adapter = mainAdapter
|
||||
layoutManager = GridLayoutManager(requireContext(), 2)
|
||||
}
|
||||
|
||||
AppDatabase.instance.favoriteThemeDao().queryAllLike().observe(requireActivity()) { favorites ->
|
||||
Log.d(App.Companion.TAG, "---------it=${favorites?.size}")
|
||||
if(favorites.isNullOrEmpty()){
|
||||
vb.likeRecycler.isVisible = false
|
||||
vb.emptyTitle.isVisible = true
|
||||
}else{
|
||||
vb.likeRecycler.isVisible = true
|
||||
vb.emptyTitle.isVisible = false
|
||||
mainAdapter.setForYouList(favorites)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
package com.app.input.breeze.board.uifragment
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.app.input.breeze.board.App
|
||||
import com.app.input.breeze.board.bean.CategoryWrapper
|
||||
import com.app.input.breeze.board.databinding.HomeFragmentBinding
|
||||
import com.app.input.breeze.board.callback.ViewAllClickListener
|
||||
import com.app.input.breeze.board.listutils.CategoryListAdapter
|
||||
import com.app.input.breeze.board.uiactivity.ThemeListActivity
|
||||
|
||||
class HomeFragment : Fragment() {
|
||||
|
||||
|
||||
private lateinit var vb: HomeFragmentBinding
|
||||
|
||||
|
||||
|
||||
lateinit var viewAllList: MutableList<CategoryWrapper>
|
||||
private lateinit var adapterParent: CategoryListAdapter
|
||||
|
||||
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun newInstance() =
|
||||
HomeFragment()
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
vb = HomeFragmentBinding.inflate(layoutInflater)
|
||||
|
||||
setTabRecycler()
|
||||
|
||||
return vb.root
|
||||
}
|
||||
|
||||
|
||||
private fun setTabRecycler() {
|
||||
viewAllList = App.Companion.list
|
||||
adapterParent = CategoryListAdapter(
|
||||
requireContext(), viewAllList
|
||||
).apply {
|
||||
setClickAction(object : ViewAllClickListener {
|
||||
override fun OnClickSeeAll(name: String) {
|
||||
startActivity(Intent(requireContext(),
|
||||
ThemeListActivity::class.java).apply {
|
||||
putExtra(ThemeListActivity.KEY_NAME,name)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
vb.tabRecycler.run {
|
||||
adapter = adapterParent
|
||||
layoutManager = LinearLayoutManager(requireContext())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
115
app/src/main/java/com/app/input/breeze/board/utils/AppUtils.kt
Normal file
115
app/src/main/java/com/app/input/breeze/board/utils/AppUtils.kt
Normal file
@ -0,0 +1,115 @@
|
||||
package com.app.input.breeze.board.utils
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.provider.Settings
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.ImageView
|
||||
import com.app.input.breeze.board.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 AppUtils {
|
||||
|
||||
val transform = RequestOptions().transform(CenterCrop(), RoundedCorners(dpToPx(8f)))
|
||||
fun initFullScreen(activity: Activity, dark: Boolean? = true) {
|
||||
val window = activity.window
|
||||
val decorView = window.decorView
|
||||
val rootView = decorView.rootView
|
||||
//
|
||||
if (dark == null) return
|
||||
|
||||
if (dark) {
|
||||
decorView.setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
|
||||
)
|
||||
} else {
|
||||
decorView.setSystemUiVisibility(
|
||||
(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
|
||||
)
|
||||
}
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
||||
}
|
||||
|
||||
|
||||
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 {
|
||||
val defaultId =
|
||||
Settings.Secure.getString(con.contentResolver, Settings.Secure.DEFAULT_INPUT_METHOD)
|
||||
return defaultId != null && defaultId.startsWith(con.packageName)
|
||||
}
|
||||
|
||||
fun checkEnable(con: Context): Boolean {
|
||||
for (methodInfo in inputMethodManager.enabledInputMethodList) {
|
||||
if (methodInfo.id.startsWith(con.packageName)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
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,37 @@
|
||||
package com.app.input.breeze.board.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.app.input.breeze.board.App;
|
||||
import com.app.input.breeze.board.R;
|
||||
|
||||
|
||||
public class BreezeTextView extends androidx.appcompat.widget.AppCompatTextView {
|
||||
|
||||
|
||||
public BreezeTextView(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.app.input.breeze.board.utils;
|
||||
|
||||
import com.app.input.breeze.board.keyboardhelper.KeyboardKey;
|
||||
import com.app.input.breeze.board.keyboardhelper.KeyboardConfig;
|
||||
import com.app.input.breeze.board.keyboardhelper.KeyboardLayout;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ConfigFileHandler {
|
||||
|
||||
public static KeyboardConfig initConfig(String path) {
|
||||
String filePath = "keyboard.conf"; // 文件路径
|
||||
KeyboardConfig config = parseConfig(path);
|
||||
return config;
|
||||
}
|
||||
|
||||
public static KeyboardConfig parseConfig(String filePath) {
|
||||
// InputStream open = App.appInstance.getAssets().open(filePath);
|
||||
KeyboardConfig config = new KeyboardConfig();
|
||||
try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
|
||||
String line;
|
||||
KeyboardLayout 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 KeyboardLayout(line.split(":")[0].trim());
|
||||
config.addLayout(currentLayout);
|
||||
} else if (currentLayout != null) {
|
||||
if (line.equals("Key")) {
|
||||
String[] parts = line.split(":");
|
||||
String keyName = parts[0].trim();
|
||||
KeyboardKey KeyboardKey = new KeyboardKey(keyName);
|
||||
currentLayout.addKey(KeyboardKey);
|
||||
} else if (line.contains(":") && currentLayout.getLastKey().getBackground() == null) {
|
||||
// 解析按键的其他属性(如 Label)
|
||||
String[] parts = line.split(":");
|
||||
String keyName = parts[0].trim();
|
||||
String keyValue = parts[1].trim();
|
||||
KeyboardKey KeyboardKey = currentLayout.getLastKey();
|
||||
if (keyName.equals("Label")) {
|
||||
KeyboardKey.setLabel(keyValue);
|
||||
}
|
||||
if (keyName.equals("Background")) {
|
||||
KeyboardKey.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")) {
|
||||
KeyboardKey funcationKeyboardKey = new KeyboardKey(line);
|
||||
config.addKey(funcationKeyboardKey);
|
||||
} else if (line.contains(":")) {
|
||||
String[] parts = line.split(":");
|
||||
String keyName = parts[0].trim();
|
||||
String keyValue = parts[1].trim();
|
||||
KeyboardKey lastKeyboardKey = config.getLastKeyList();
|
||||
if (keyName.equals("Label")) {
|
||||
lastKeyboardKey.setLabel(keyValue);
|
||||
}
|
||||
if (keyName.equals("Background")) {
|
||||
lastKeyboardKey.setBackground(keyValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,76 @@
|
||||
package com.app.input.breeze.board.utils;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
|
||||
|
||||
import com.app.input.breeze.board.App;
|
||||
|
||||
|
||||
public class GridItemDecoration extends RecyclerView.ItemDecoration {
|
||||
|
||||
private int v, h, ex;
|
||||
|
||||
public GridItemDecoration(int v, int h, int ex) {
|
||||
this.v = Math.round(dpToPx(v));
|
||||
this.h = Math.round(dpToPx(h));
|
||||
this.ex = Math.round(dpToPx(ex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
|
||||
super.getItemOffsets(outRect, view, parent, state);
|
||||
int spanCount = 1;
|
||||
int spanSize = 1;
|
||||
int spanIndex = 0;
|
||||
|
||||
int childAdapterPosition = parent.getChildAdapterPosition(view);
|
||||
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
|
||||
if (layoutManager instanceof StaggeredGridLayoutManager) {
|
||||
StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
|
||||
StaggeredGridLayoutManager.LayoutParams layoutParams = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams();
|
||||
spanCount = staggeredGridLayoutManager.getSpanCount();
|
||||
if (layoutParams.isFullSpan()) {
|
||||
spanSize = spanCount;
|
||||
}
|
||||
spanIndex = layoutParams.getSpanIndex();
|
||||
} else if (layoutManager instanceof GridLayoutManager) {
|
||||
GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
|
||||
GridLayoutManager.LayoutParams layoutParams = (GridLayoutManager.LayoutParams) view.getLayoutParams();
|
||||
spanCount = gridLayoutManager.getSpanCount();
|
||||
spanSize = gridLayoutManager.getSpanSizeLookup().getSpanSize(childAdapterPosition);
|
||||
spanIndex = layoutParams.getSpanIndex();
|
||||
} else if (layoutManager instanceof LinearLayoutManager) {
|
||||
outRect.left = v;
|
||||
outRect.right = v;
|
||||
outRect.bottom = h;
|
||||
}
|
||||
|
||||
if (spanSize == spanCount) {
|
||||
outRect.left = v + ex;
|
||||
outRect.right = v + ex;
|
||||
outRect.bottom = h;
|
||||
|
||||
} else {
|
||||
int itemAllSpacing = (v * (spanCount + 1) + ex * 2) / spanCount;
|
||||
int left = v * (spanIndex + 1) - itemAllSpacing * spanIndex + ex;
|
||||
int right = itemAllSpacing - left;
|
||||
outRect.left = left;
|
||||
outRect.right = right;
|
||||
outRect.bottom = h;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static float dpToPx(float dpValue) {
|
||||
float density = App.appInstance.getResources().getDisplayMetrics().density;
|
||||
return density * dpValue + 0.5f;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
package com.app.input.breeze.board.utils
|
||||
|
||||
object KeyboardConstants {
|
||||
|
||||
|
||||
|
||||
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,181 @@
|
||||
package com.app.input.breeze.board.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.app.input.breeze.board.App
|
||||
import com.app.input.breeze.board.keyboardhelper.KeyboardConfig
|
||||
import com.app.input.breeze.board.R
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
import java.io.File
|
||||
import java.io.StringReader
|
||||
import kotlin.collections.iterator
|
||||
|
||||
class KeyboardThemeManager(var context: Context) {
|
||||
|
||||
|
||||
private var textSize = 13f
|
||||
|
||||
var functionDraw: Drawable =
|
||||
getDefaultDrawList(R.drawable.default_normal_key, R.drawable.default_pressed_key)
|
||||
var generalDraw: Drawable =
|
||||
getDefaultDrawList(R.drawable.default_normal_key, R.drawable.default_pressed_key)
|
||||
|
||||
var toDraw: Drawable = getDefaultDrawList(R.drawable.default_normal_key, R.drawable.default_pressed_key)
|
||||
var spaceDraw: Drawable = getDefaultDrawList(R.drawable.default_normal_key, R.drawable.default_pressed_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(): KeyboardConfig? {
|
||||
val skinPath = ThemePreferences.getSkinPath()
|
||||
val configFilePath = skinPath + "assets/keyboard.conf"
|
||||
val file = File(configFilePath)
|
||||
return if (file.exists()) {
|
||||
ConfigFileHandler.initConfig(configFilePath)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun getConfigBg(name: String): Drawable? {
|
||||
ThemePreferences.getSkinPath()?.let { resPath ->
|
||||
val pPath = "${resPath}res/drawable-xhdpi-v4/"
|
||||
|
||||
return getDrawList(
|
||||
pPath + name,
|
||||
pPath + name
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun updateSkinConfig() {
|
||||
ThemePreferences.getSkinPath()?.let { resPath ->
|
||||
val pPath = "${resPath}res/drawable-xhdpi-v4/"
|
||||
pPath.let {
|
||||
readColors(resPath) {
|
||||
for ((name, value) in it) {
|
||||
if (name == KeyboardConstants.keyTextColorName) {
|
||||
keyTextColor = value
|
||||
}
|
||||
if (name == KeyboardConstants.keyTextColorFunctionName) {
|
||||
keyTextColorFunction = value
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
functionDraw = getDrawList(
|
||||
it + KeyboardConstants.functionNormalName,
|
||||
it + KeyboardConstants.functionPressName
|
||||
)
|
||||
generalDraw = getDrawList(it + KeyboardConstants.normalName, it + KeyboardConstants.pressName)
|
||||
toDraw = getDrawList(it + KeyboardConstants.toNormalName, it + KeyboardConstants.toPressName)
|
||||
spaceDraw =
|
||||
getDrawList(it + KeyboardConstants.spaceNormalName, it + KeyboardConstants.spacePressName)
|
||||
switchDraw =
|
||||
getDrawList(it + KeyboardConstants.imeSwitchName, it + KeyboardConstants.imeSwitchName)
|
||||
deleteDraw = getDrawList(
|
||||
it + KeyboardConstants.deleteNormalName,
|
||||
it + KeyboardConstants.deletePressName
|
||||
)
|
||||
backDraw = getDrawList(it + KeyboardConstants.backName, it + KeyboardConstants.backName)
|
||||
searchDraw = getDrawList(it + KeyboardConstants.searchName, it + KeyboardConstants.searchName)
|
||||
shiftDraw = getDrawList(
|
||||
it + KeyboardConstants.shiftNormalName,
|
||||
it + KeyboardConstants.shiftNormalName
|
||||
)
|
||||
shiftLockDraw =
|
||||
getDrawList(it + KeyboardConstants.shiftLockName, it + KeyboardConstants.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 == KeyboardConstants.keyTextColorName
|
||||
val b4 = attributeName == KeyboardConstants.keyTextColorFunctionName
|
||||
if (b3 || b4) {
|
||||
resMaps[attributeName] = Color.parseColor(nextTextValue)
|
||||
}
|
||||
}
|
||||
curType = xmlPullParser.next()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
callBack.invoke(resMaps)
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package com.app.input.breeze.board.utils
|
||||
|
||||
import android.content.Context
|
||||
import com.app.input.breeze.board.App
|
||||
|
||||
object ThemePreferences {
|
||||
|
||||
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,238 @@
|
||||
package com.app.input.breeze.board.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.app.input.breeze.board.App;
|
||||
import com.app.input.breeze.board.callback.KeyboardApplyCallback;
|
||||
|
||||
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 ZipFileHandler {
|
||||
|
||||
public static void startDownloadZip(String zipPath, KeyboardApplyCallback 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, KeyboardApplyCallback 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, KeyboardApplyCallback 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
BIN
app/src/main/res/drawable/already_enabled_icon.png
Normal file
BIN
app/src/main/res/drawable/already_enabled_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
12
app/src/main/res/drawable/arrow_right.xml
Normal file
12
app/src/main/res/drawable/arrow_right.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M8.59,16.59L13.17,12 8.59,7.41 10,6l6,6 -6,6z"/>
|
||||
</vector>
|
||||
|
||||
|
||||
BIN
app/src/main/res/drawable/back_ffffff.png
Normal file
BIN
app/src/main/res/drawable/back_ffffff.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 341 B |
12
app/src/main/res/drawable/back_icon.xml
Normal file
12
app/src/main/res/drawable/back_icon.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
|
||||
</vector>
|
||||
|
||||
|
||||
9
app/src/main/res/drawable/back_svg.xml
Normal file
9
app/src/main/res/drawable/back_svg.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="256dp"
|
||||
android:height="256dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M608,736c-6.4,0 -19.2,0 -25.6,-6.4l-192,-192C384,524.8 384,499.2 390.4,486.4l192,-192c12.8,-12.8 32,-12.8 44.8,0s12.8,32 0,44.8L460.8,512l166.4,166.4c12.8,12.8 12.8,32 0,44.8C627.2,736 614.4,736 608,736z"
|
||||
android:fillColor="@color/main_text_color"/>
|
||||
</vector>
|
||||
6
app/src/main/res/drawable/bg_oval_white.xml
Normal file
6
app/src/main/res/drawable/bg_oval_white.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="oval">
|
||||
<solid android:color="@color/white"/>
|
||||
|
||||
</shape>
|
||||
@ -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/white"/>
|
||||
|
||||
</shape>
|
||||
6
app/src/main/res/drawable/card_background_rounded.xml
Normal file
6
app/src/main/res/drawable/card_background_rounded.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="#E0FFFFFF" />
|
||||
<corners android:radius="16dp" />
|
||||
</shape>
|
||||
12
app/src/main/res/drawable/category_icon.xml
Normal file
12
app/src/main/res/drawable/category_icon.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M4,10.5c-0.83,0 -1.5,-0.67 -1.5,-1.5S3.17,7.5 4,7.5S5.5,8.17 5.5,9S4.83,10.5 4,10.5zM4,4.5c-0.83,0 -1.5,-0.67 -1.5,-1.5S3.17,1.5 4,1.5S5.5,2.17 5.5,3S4.83,4.5 4,4.5zM4,16.5c-0.83,0 -1.5,-0.67 -1.5,-1.5S3.17,13.5 4,13.5S5.5,14.17 5.5,15S4.83,16.5 4,16.5zM7,19h14v-2L7,17v2zM7,13h14v-2L7,11v2zM7,7h14L21,5L7,5v2z"/>
|
||||
</vector>
|
||||
|
||||
|
||||
6
app/src/main/res/drawable/decorative_circle.xml
Normal file
6
app/src/main/res/drawable/decorative_circle.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="oval">
|
||||
<solid android:color="#30FFFFFF" />
|
||||
</shape>
|
||||
|
||||
BIN
app/src/main/res/drawable/default_normal_key.png
Normal file
BIN
app/src/main/res/drawable/default_normal_key.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 481 B |
BIN
app/src/main/res/drawable/default_pressed_key.png
Normal file
BIN
app/src/main/res/drawable/default_pressed_key.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
7
app/src/main/res/drawable/dialog_enable_bg1.xml
Normal file
7
app/src/main/res/drawable/dialog_enable_bg1.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">
|
||||
<solid android:color="@color/apply_step_false" />
|
||||
<corners android:radius="12dp" />
|
||||
|
||||
</shape>
|
||||
10
app/src/main/res/drawable/dialog_unenable_bg.xml
Normal file
10
app/src/main/res/drawable/dialog_unenable_bg.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/white"/>
|
||||
<corners
|
||||
android:topLeftRadius="40dp"
|
||||
android:topRightRadius="40dp" />
|
||||
|
||||
|
||||
</shape>
|
||||
7
app/src/main/res/drawable/dot_indicator.xml
Normal file
7
app/src/main/res/drawable/dot_indicator.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="oval">
|
||||
<solid android:color="#80FFFFFF" />
|
||||
<size android:width="8dp" android:height="8dp" />
|
||||
</shape>
|
||||
|
||||
7
app/src/main/res/drawable/dot_indicator_selected.xml
Normal file
7
app/src/main/res/drawable/dot_indicator_selected.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="oval">
|
||||
<solid android:color="#FFFFFF" />
|
||||
<size android:width="8dp" android:height="8dp" />
|
||||
</shape>
|
||||
|
||||
12
app/src/main/res/drawable/favorite_icon.xml
Normal file
12
app/src/main/res/drawable/favorite_icon.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/transparent"
|
||||
android:strokeWidth="2"
|
||||
android:strokeColor="#1A1A1A"
|
||||
android:pathData="M20.84,4.61a5.5,5.5 0,0 0,-7.78 0L12,5.67 10.94,4.61a5.5,5.5 0,0 0,-7.78 7.78l1.06,1.06L12,21.23l7.78,-7.78 1.06,-1.06a5.5,5.5 0,0 0,0 -7.78z"/>
|
||||
</vector>
|
||||
22
app/src/main/res/drawable/favorite_normal_gradient.xml
Normal file
22
app/src/main/res/drawable/favorite_normal_gradient.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="64dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M716.8,60.2c-81.3,0 -153.6,36.1 -204.8,96.4 -51.2,-60.2 -123.5,-96.4 -204.8,-96.4C153.6,60.2 30.1,192.8 30.1,358.4c0,45.2 6,87.3 21.1,126.5 0,0 12,36.1 21.1,57.2C189.7,798.1 512,963.8 512,963.8s322.3,-165.6 439.7,-424.7c0,0 15.1,-33.1 21.1,-57.2 12,-39.2 21.1,-81.3 21.1,-126.5C993.9,192.8 870.4,60.2 716.8,60.2zM915.6,466.8c-6,15.1 -15.1,42.2 -18.1,48.2 -87.3,192.8 -307.2,334.3 -385.5,379.5 -78.3,-45.2 -298.2,-186.7 -385.5,-379.5 -6,-15.1 -15.1,-36.1 -18.1,-51.2 -12,-33.1 -18.1,-72.3 -18.1,-105.4C90.4,225.9 189.7,120.5 307.2,120.5c60.2,0 117.5,27.1 159.6,75.3L512,247l45.2,-51.2C599.3,147.6 656.6,120.5 716.8,120.5c120.5,0 216.8,105.4 216.8,234.9 0,36.1 -6,72.3 -18.1,111.4z">
|
||||
<aapt:attr name="android:fillColor" xmlns:aapt="http://schemas.android.com/aapt">
|
||||
<gradient
|
||||
android:startY="0"
|
||||
android:startX="0"
|
||||
android:endY="1024"
|
||||
android:endX="0"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#64B5F6"/>
|
||||
<item android:offset="1" android:color="#FFFFFF"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
</vector>
|
||||
|
||||
22
app/src/main/res/drawable/favorite_selected_gradient.xml
Normal file
22
app/src/main/res/drawable/favorite_selected_gradient.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="64dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M667.8,117.3C832.9,117.3 938.7,249.7 938.7,427.9c0,138.3 -125.1,290.5 -371.6,461.6a96.8,96.8 0,0 1,-110.2 0C210.4,718.4 85.3,566.1 85.3,427.9 85.3,249.7 191.1,117.3 356.2,117.3c59.6,0 100.1,20.8 155.8,68.1C567.7,138.2 608.2,117.3 667.8,117.3z">
|
||||
<aapt:attr name="android:fillColor" xmlns:aapt="http://schemas.android.com/aapt">
|
||||
<gradient
|
||||
android:startY="0"
|
||||
android:startX="0"
|
||||
android:endY="1024"
|
||||
android:endX="0"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FF6B9D"/>
|
||||
<item android:offset="1" android:color="#FFB3D9"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
</vector>
|
||||
|
||||
11
app/src/main/res/drawable/gradient_overlay.xml
Normal file
11
app/src/main/res/drawable/gradient_overlay.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<gradient
|
||||
android:angle="270"
|
||||
android:startColor="#00000000"
|
||||
android:centerColor="#66000000"
|
||||
android:endColor="#CC000000"
|
||||
android:type="linear" />
|
||||
</shape>
|
||||
|
||||
|
||||
10
app/src/main/res/drawable/home_bg_gradient.xml
Normal file
10
app/src/main/res/drawable/home_bg_gradient.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<gradient
|
||||
android:type="linear"
|
||||
android:angle="270"
|
||||
android:startColor="#87CEEB"
|
||||
android:centerColor="#B0E0E6"
|
||||
android:endColor="#E0F6FF" />
|
||||
</shape>
|
||||
12
app/src/main/res/drawable/home_icon.xml
Normal file
12
app/src/main/res/drawable/home_icon.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"/>
|
||||
</vector>
|
||||
|
||||
|
||||
9
app/src/main/res/drawable/im_close_dialog.xml
Normal file
9
app/src/main/res/drawable/im_close_dialog.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:pathData="M8.72,8L14.21,2.51C14.412,2.307 14.412,1.992 14.21,1.79C14.007,1.587 13.692,1.587 13.49,1.79L8,7.28L2.51,1.79C2.307,1.587 1.992,1.587 1.79,1.79C1.587,1.992 1.587,2.307 1.79,2.51L7.28,8L1.79,13.49C1.587,13.692 1.587,14.007 1.79,14.21C1.88,14.3 2.015,14.367 2.15,14.367C2.285,14.367 2.42,14.322 2.51,14.21L8,8.72L13.49,14.21C13.58,14.3 13.715,14.367 13.85,14.367C13.985,14.367 14.12,14.322 14.21,14.21C14.412,14.007 14.412,13.692 14.21,13.49L8.72,8Z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
||||
10
app/src/main/res/drawable/launcher_gradient_bg.xml
Normal file
10
app/src/main/res/drawable/launcher_gradient_bg.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<gradient
|
||||
android:type="linear"
|
||||
android:angle="135"
|
||||
android:startColor="#87CEEB"
|
||||
android:centerColor="#B0E0E6"
|
||||
android:endColor="#E0F6FF" />
|
||||
</shape>
|
||||
18
app/src/main/res/drawable/launcher_progress_drawable.xml
Normal file
18
app/src/main/res/drawable/launcher_progress_drawable.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<?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 android:shape="rectangle">
|
||||
<solid android:color="#40FFFFFF" />
|
||||
<corners android:radius="4dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<item android:id="@android:id/progress">
|
||||
<clip>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#FFFFFF" />
|
||||
<corners android:radius="4dp" />
|
||||
</shape>
|
||||
</clip>
|
||||
</item>
|
||||
</layer-list>
|
||||
|
||||
7
app/src/main/res/drawable/nav_indicator.xml
Normal file
7
app/src/main/res/drawable/nav_indicator.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="oval">
|
||||
<solid android:color="@color/blue_primary" />
|
||||
<size android:width="6dp" android:height="6dp" />
|
||||
</shape>
|
||||
|
||||
BIN
app/src/main/res/drawable/place_holder.png
Normal file
BIN
app/src/main/res/drawable/place_holder.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
6
app/src/main/res/drawable/selector_enable.xml
Normal file
6
app/src/main/res/drawable/selector_enable.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/shape_set_buttoon_bg" android:state_selected="false" />
|
||||
<item android:drawable="@drawable/dialog_enable_bg1" />
|
||||
</selector>
|
||||
5
app/src/main/res/drawable/selector_favorite.xml
Normal file
5
app/src/main/res/drawable/selector_favorite.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/favorite_selected_gradient" android:state_selected="true"/>
|
||||
<item android:drawable="@drawable/favorite_normal_gradient" />
|
||||
</selector>
|
||||
15
app/src/main/res/drawable/selector_item_key_white.xml
Normal file
15
app/src/main/res/drawable/selector_item_key_white.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>
|
||||
7
app/src/main/res/drawable/selector_set_button.xml
Normal file
7
app/src/main/res/drawable/selector_set_button.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/shape_set_button_pressed" android:state_pressed="true"/>
|
||||
<item android:drawable="@drawable/shape_set_buttoon_bg" />
|
||||
</selector>
|
||||
|
||||
|
||||
BIN
app/src/main/res/drawable/set_white_icon.png
Normal file
BIN
app/src/main/res/drawable/set_white_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
12
app/src/main/res/drawable/settings_icon.xml
Normal file
12
app/src/main/res/drawable/settings_icon.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z"/>
|
||||
</vector>
|
||||
|
||||
|
||||
8
app/src/main/res/drawable/shape_set_button_pressed.xml
Normal file
8
app/src/main/res/drawable/shape_set_button_pressed.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/blue_dark" />
|
||||
<corners android:radius="30dp" />
|
||||
</shape>
|
||||
|
||||
|
||||
6
app/src/main/res/drawable/shape_set_buttoon_bg.xml
Normal file
6
app/src/main/res/drawable/shape_set_buttoon_bg.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/blue_primary" />
|
||||
<corners android:radius="30dp" />
|
||||
</shape>
|
||||
143
app/src/main/res/layout/bottom_navigation_bar.xml
Normal file
143
app/src/main/res/layout/bottom_navigation_bar.xml
Normal file
@ -0,0 +1,143 @@
|
||||
<?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="56dp"
|
||||
android:orientation="horizontal"
|
||||
android:background="@color/white"
|
||||
android:elevation="8dp"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/nav_home"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/nav_home_icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/home_icon"
|
||||
app:tint="#80000000" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/nav_home_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="@string/tab_home"
|
||||
android:textSize="12sp"
|
||||
android:textColor="#80000000" />
|
||||
|
||||
<!-- 閫変腑鎸囩ず锟?-->
|
||||
<View
|
||||
android:id="@+id/nav_home_indicator"
|
||||
android:layout_width="6dp"
|
||||
android:layout_height="6dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="@drawable/nav_indicator"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
|
||||
<!-- Category 瀵艰埅锟?-->
|
||||
<RelativeLayout
|
||||
android:id="@+id/nav_category"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/nav_category_icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/category_icon"
|
||||
app:tint="#80000000" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/nav_category_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="@string/categories"
|
||||
android:textSize="12sp"
|
||||
android:textColor="#80000000" />
|
||||
|
||||
<!-- 閫変腑鎸囩ず锟?-->
|
||||
<View
|
||||
android:id="@+id/nav_category_indicator"
|
||||
android:layout_width="6dp"
|
||||
android:layout_height="6dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="@drawable/nav_indicator"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
|
||||
<!-- Settings 瀵艰埅锟?-->
|
||||
<RelativeLayout
|
||||
android:id="@+id/nav_settings"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/nav_settings_icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/settings_icon"
|
||||
app:tint="#80000000" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/nav_settings_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="@string/settings"
|
||||
android:textSize="12sp"
|
||||
android:textColor="#80000000" />
|
||||
|
||||
<!-- 閫変腑鎸囩ず锟?-->
|
||||
<View
|
||||
android:id="@+id/nav_settings_indicator"
|
||||
android:layout_width="6dp"
|
||||
android:layout_height="6dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="@drawable/nav_indicator"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
||||
49
app/src/main/res/layout/category_card_item.xml
Normal file
49
app/src/main/res/layout/category_card_item.xml
Normal file
@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.cardview.widget.CardView 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="180dp"
|
||||
android:layout_margin="8dp"
|
||||
app:cardCornerRadius="20dp"
|
||||
app:cardElevation="0dp"
|
||||
android:foreground="?attr/selectableItemBackground">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/category_image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop"
|
||||
android:contentDescription="Category" />
|
||||
|
||||
<!-- 渐变遮罩 -->
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/gradient_overlay" />
|
||||
|
||||
<!-- 内容 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:orientation="vertical"
|
||||
android:padding="20dp"
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/category_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Aesthetic"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="#FFFFFF" />
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
60
app/src/main/res/layout/category_screen.xml
Normal file
60
app/src/main/res/layout/category_screen.xml
Normal file
@ -0,0 +1,60 @@
|
||||
<?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"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/home_bg_gradient">
|
||||
|
||||
<!-- 顶部导航栏 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/top_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:background="@android:color/transparent"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:text="@string/categories"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="#1A1A1A" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="@string/find_your_style"
|
||||
android:textSize="14sp"
|
||||
android:textColor="#1A1A1A"
|
||||
android:alpha="0.7" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 分类卡片列表 -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/category_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/top_bar"
|
||||
android:layout_above="@id/bottom_navigation"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:clipToPadding="false" />
|
||||
|
||||
<!-- 底部导航栏 -->
|
||||
<include
|
||||
android:id="@+id/bottom_navigation"
|
||||
layout="@layout/bottom_navigation_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:layout_alignParentBottom="true" />
|
||||
|
||||
</RelativeLayout>
|
||||
100
app/src/main/res/layout/dialog_enable_keyboard.xml
Normal file
100
app/src/main/res/layout/dialog_enable_keyboard.xml
Normal file
@ -0,0 +1,100 @@
|
||||
<?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:background="@drawable/dialog_unenable_bg"
|
||||
android:paddingBottom="20dp"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="55dp"
|
||||
android:layout_height="45dp"
|
||||
android:padding="12dp"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:id="@+id/im_close"
|
||||
android:layout_marginTop="10dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:src="@drawable/im_close_dialog"
|
||||
app:layout_constraintRight_toRightOf="parent" />
|
||||
|
||||
<com.app.input.breeze.board.utils.BreezeTextView
|
||||
android:id="@+id/text_open"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="50dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginEnd="50dp"
|
||||
android:gravity="center"
|
||||
app:apply_font="true"
|
||||
android:text="@string/open_str"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="16sp"
|
||||
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="32dp"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:background="@drawable/selector_enable"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
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="@drawable/already_enabled_icon"
|
||||
android:visibility="gone" />
|
||||
|
||||
<com.app.input.breeze.board.utils.BreezeTextView
|
||||
android:id="@+id/text_step_one"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/step_1"
|
||||
app:apply_font="true"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linear_step_two"
|
||||
android:layout_width="280dp"
|
||||
android:layout_height="52dp"
|
||||
android:layout_marginTop="20dp"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:background="@drawable/selector_enable"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
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="@drawable/already_enabled_icon"
|
||||
android:visibility="gone" />
|
||||
|
||||
<com.app.input.breeze.board.utils.BreezeTextView
|
||||
android:id="@+id/text_step_two"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/step_2"
|
||||
app:apply_font="true"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp" />
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</FrameLayout>
|
||||
73
app/src/main/res/layout/favorite_activity.xml
Normal file
73
app/src/main/res/layout/favorite_activity.xml
Normal file
@ -0,0 +1,73 @@
|
||||
<?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"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/home_bg_gradient">
|
||||
|
||||
<!-- 椤堕儴瀵艰埅锟?-->
|
||||
<LinearLayout
|
||||
android:id="@+id/top_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:background="@android:color/transparent"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="20dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/back"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:src="@drawable/back_icon"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:padding="6dp"
|
||||
android:contentDescription="Back" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="16dp"
|
||||
android:text="@string/tab_favorite"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="#1A1A1A" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/like_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/top_bar"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginEnd="15dp" />
|
||||
|
||||
<com.app.input.breeze.board.utils.BreezeTextView
|
||||
android:id="@+id/empty_title"
|
||||
android:layout_width="260dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_marginStart="25dp"
|
||||
android:layout_marginTop="15dp"
|
||||
android:layout_marginEnd="25dp"
|
||||
android:gravity="center"
|
||||
android:lineSpacingExtra="2dp"
|
||||
android:text="@string/no_like"
|
||||
android:textColor="@color/main_text_color"
|
||||
android:textSize="16sp"
|
||||
android:visibility="gone"
|
||||
app:apply_font="true" />
|
||||
</RelativeLayout>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user