Compare commits

...

15 Commits

Author SHA1 Message Date
8ee3ae17ba docs: update reference link 2025-08-24 02:50:00 +08:00
0d99806004 Bump hikage-core version to 1.0.2, hikage-extension, hikage-extension-betterandroid, hikage-compiler, hikage-widget-androidx, hikage-widget-material version to 1.0.1 2025-08-24 02:39:05 +08:00
1cb95849ab chore: downgrade lint and lock version 2025-08-24 02:35:07 +08:00
714c8552ab feat: add SurfaceView, WebView in Widgets 2025-08-24 00:07:57 +08:00
18ccf196b6 refactor: adjust some components that are not allow to add performer param 2025-08-23 23:48:05 +08:00
76df8fa06c feat: add final param in HikageView, HikageViewDeclaration 2025-08-23 22:39:35 +08:00
8e25430d68 feat: add MotionLayout, ImageFilterButton, ImageFilterView, MockView, MotionButton, MotionLabel, MotionTelltales in ConstraintLayout 2025-08-23 21:36:28 +08:00
0529d6a2b6 feat: add LP generic function in ViewGroup 2025-08-23 21:22:05 +08:00
51a59b672c chore: update jdk to 21 2025-08-19 15:48:34 +08:00
59d09b9611 chore: bump gradle to 8.14.3 2025-08-19 15:48:00 +08:00
b6c46b7240 chore: bump dependencies 2025-08-19 15:45:58 +08:00
646e5e5056 chore: bump dependencies
Some checks failed
Deploy to GitHub pages / docs (push) Has been cancelled
2025-08-16 01:48:55 +08:00
14ba71cc2b chore: update target sdk to 36 2025-08-03 23:30:02 +08:00
ee97222bcb refactor: merge to BetterAndroid new usage 2025-08-03 23:27:46 +08:00
8990e03e97 chore: update project files 2025-08-03 03:56:26 +08:00
61 changed files with 481 additions and 190 deletions

View File

@@ -29,10 +29,10 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: 18
- name: Prepare Java 17
- name: Prepare Java 21
uses: actions/setup-java@v4
with:
java-version: 17
java-version: 21
java-package: jdk
distribution: 'temurin'
cache: 'gradle'

View File

@@ -0,0 +1 @@
2019 HighCapable

View File

@@ -0,0 +1,13 @@
* Apache License Version 2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.

View File

@@ -0,0 +1,13 @@
/*
* #parse("project-name") - #parse("project-description")
* Copyright (C) #parse("copyright-name")
* #parse("project-url")
*
#parse("license-content")
*
* This file is created by $USER on $DATE.
*/

View File

@@ -0,0 +1 @@
An Android responsive UI building tool.

View File

@@ -0,0 +1 @@
Hikage

View File

@@ -0,0 +1 @@
https://github.com/BetterAndroid/Hikage

View File

@@ -0,0 +1,6 @@
#parse("open-source-license-header")
#if (${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
#end
annotation class ${NAME}

View File

@@ -0,0 +1,7 @@
#parse("open-source-license-header")
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
#end
class ${NAME} {
}

View File

@@ -0,0 +1,6 @@
#parse("open-source-license-header")
#if (${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
#end
data class ${NAME}()

View File

@@ -0,0 +1,7 @@
#parse("open-source-license-header")
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
#end
enum class ${NAME} {
}

View File

@@ -0,0 +1,5 @@
#parse("open-source-license-header")
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
#end

View File

@@ -0,0 +1,7 @@
#parse("open-source-license-header")
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
#end
interface ${NAME} {
}

View File

@@ -0,0 +1,7 @@
#parse("open-source-license-header")
#if (${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
#end
object ${NAME} {
}

View File

@@ -2,6 +2,8 @@ import com.android.build.gradle.LibraryExtension
import com.vanniktech.maven.publish.AndroidSingleVariantLibrary
import com.vanniktech.maven.publish.MavenPublishBaseExtension
import org.jetbrains.dokka.gradle.DokkaTask
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
plugins {
autowire(libs.plugins.android.application) apply false
@@ -65,6 +67,20 @@ libraryProjects {
}
}
allprojects {
tasks.withType<KotlinJvmCompile>().configureEach {
compilerOptions {
jvmTarget = JvmTarget.JVM_17
freeCompilerArgs.addAll(
"-opt-in=kotlin.ExperimentalStdlibApi",
"-Xno-param-assertions",
"-Xno-call-assertions",
"-Xno-receiver-assertions"
)
}
}
}
fun libraryProjects(action: Action<in Project>) {
val libraries = listOf(
Libraries.HIKAGE_CORE,

View File

@@ -18,7 +18,16 @@ Time zone of version release date: **UTC+8**
## hikage-core
### 1.0.1 | 2025.05.06 &ensp;<Badge type="tip" text="latest" vertical="middle" />
### 1.0.2 | 2025.08.24 &ensp;<Badge type="tip" text="latest" vertical="middle" />
- Migrated Java reflection related behaviors from [YukiReflection](https://github.com/HighCapable/YukiReflection) to [KavaRef](https://github.com/HighCapable/KavaRef)
- Adapted to Android 16 (API 36), fixed the `XmlBlock` crash issue on Android 16
- Optimized layout performance, removed unnecessary inline operations, added caching for reflection operations
- Added `final` parameter to `HikageView` and `HikageViewDeclaration` to support new features in `hikage-compiler`
- Added `SurfaceView` and `WebView` built-in components to `Widgets`
- Adjusted some components in `Widgets` to be `final`
### 1.0.1 | 2025.05.06 &ensp;<Badge type="warning" text="stale" vertical="middle" />
- Fixed the issue where the KSP source code was not successfully released
- Added states management feature
@@ -29,19 +38,32 @@ Time zone of version release date: **UTC+8**
## hikage-compiler
### 1.0.0 | 2025.04.20 &ensp;<Badge type="tip" text="latest" vertical="middle" />
### 1.0.1 | 2025.08.24 &ensp;<Badge type="tip" text="latest" vertical="middle" />
- Added support for the `final` parameter of `HikageView` and `HikageViewDeclaration`, please refer to the relevant usage in the documentation
### 1.0.0 | 2025.04.20 &ensp;<Badge type="warning" text="stale" vertical="middle" />
- The first version is submitted to Maven
## hikage-extension
### 1.0.0 | 2025.04.20 &ensp;<Badge type="tip" text="latest" vertical="middle" />
### 1.0.1 | 2025.08.24 &ensp;<Badge type="tip" text="latest" vertical="middle" />
- Migrated Java reflection related behaviors from [YukiReflection](https://github.com/HighCapable/YukiReflection) to [KavaRef](https://github.com/HighCapable/KavaRef)
- Added generic `ViewGroup.LayoutParams` support for `addView` in `ViewGroup`
### 1.0.0 | 2025.04.20 &ensp;<Badge type="warning" text="stale" vertical="middle" />
- The first version is submitted to Maven
## hikage-extension-betterandroid
### 1.0.0 | 2025.04.20 &ensp;<Badge type="tip" text="latest" vertical="middle" />
### 1.0.1 | 2025.08.24 &ensp;<Badge type="tip" text="latest" vertical="middle" />
- Adapted to decoupled `ui-component` and `ui-component-adapter` in `BetterAndroid`
### 1.0.0 | 2025.04.20 &ensp;<Badge type="warning" text="stale" vertical="middle" />
- The first version is submitted to Maven
@@ -53,12 +75,21 @@ Time zone of version release date: **UTC+8**
## hikage-widget-androidx
### 1.0.0 | 2025.04.20 &ensp;<Badge type="tip" text="latest" vertical="middle" />
### 1.0.1 | 2025.08.24 &ensp;<Badge type="tip" text="latest" vertical="middle" />
- Added `MotionLayout`, `ImageFilterButton`, `ImageFilterView`, `MockView`, `MotionButton`, `MotionLabel`, `MotionTelltales` components to `ConstraintLayout`
- Adjusted some components to be `final`
### 1.0.0 | 2025.04.20 &ensp;<Badge type="warning" text="stale" vertical="middle" />
- The first version is submitted to Maven
## hikage-widget-material
### 1.0.0 | 2025.04.20 &ensp;<Badge type="tip" text="latest" vertical="middle" />
### 1.0.1 | 2025.08.24 &ensp;<Badge type="tip" text="latest" vertical="middle" />
- Adjusted some components to be `final`
### 1.0.0 | 2025.04.20 &ensp;<Badge type="warning" text="stale" vertical="middle" />
- The first version is submitted to Maven

View File

@@ -139,12 +139,13 @@ Hikage can automatically generate the `Hikageable` function corresponding to the
You can add the `HikageView` annotation on your custom `View` to mark it as a Hikage layout component.
| Parameter Name | Description |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `lparams` | LayoutParams `Class` object, if your custom `View` is a subclass of `ViewGroup`, you can declare or leave it blank to use the default value |
| `alias` | The alias of the layout component, that is, the function name to be generated, gets the name of the current Class by default |
| `requireInit` | Whether to fill in the initialization method block of the layout, the default is the omitted parameters |
| `requirePerformer` | Whether to fill in the `performer` method block of the layout, the default is an omitted parameter, which only takes effect when your custom `View` is a subclass of `ViewGroup` |
| Parameter Name | Description |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `lparams` | LayoutParams `Class` object, if your custom `View` is a subclass of `ViewGroup`, you can declare or leave it blank to use the default value |
| `alias` | The alias of the layout component, that is, the function name to be generated, gets the name of the current Class by default |
| `requireInit` | Whether to fill in the initialization method block of the layout, the default is the omitted parameters |
| `requirePerformer` | Whether to fill in the `performer` method block of the layout, the default is an omitted parameter, which only takes effect when your custom `View` is a subclass of `ViewGroup` |
| `final` | Whether to declare the layout as "final layout", the default is false, that is, whether this layout is `ViewGroup` or its subclasses will not generate the `performer` method block. After set to `true`, `lparams` and `requirePerformer` will no longer be valid. |
> The following example
@@ -177,13 +178,14 @@ Hikageable {
Hikage can also automatically generate layout component functions for the `View` component provided by third parties, and you can use the `HikageViewDeclaration` annotation to complete it.
| Parameter Name | Description |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `view` | Class object of layout component that needs to be declared |
| `lparams` | LayoutParams `Class` object, if your custom `View` is a subclass of `ViewGroup`, you can declare or leave it blank to use the default value |
| `alias` | The alias of the layout component, that is, the name of the function to be generated, obtains the name of the `view` Class by default |
| `requireInit` | Whether to fill in the initialization method block of the layout, the default is the omitted parameters |
| `requirePerformer` | Whether to fill in the `performer` method block of the layout, the default is an omitted parameter, which only takes effect when your custom `View` is a subclass of `ViewGroup` |
| Parameter Name | Description |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `view` | Class object of layout component that needs to be declared |
| `lparams` | LayoutParams `Class` object, if your custom `View` is a subclass of `ViewGroup`, you can declare or leave it blank to use the default value |
| `alias` | The alias of the layout component, that is, the name of the function to be generated, obtains the name of the `view` Class by default |
| `requireInit` | Whether to fill in the initialization method block of the layout, the default is the omitted parameters |
| `requirePerformer` | Whether to fill in the `performer` method block of the layout, the default is an omitted parameter, which only takes effect when your custom `View` is a subclass of `ViewGroup` |
| `final` | Whether to declare the layout as "final layout", the default is false, that is, whether this layout is `ViewGroup` or its subclasses will not generate the `performer` method block. After set to `true`, `lparams` and `requirePerformer` will no longer be valid. |
> The following example

View File

@@ -65,7 +65,7 @@ You can view the KDoc [click here](kdoc://hikage-extension-betterandroid).
### Adapter Extension
Hikage provides layout extension function for BetterAndroid's [Adapter](https://betterandroid.github.io/BetterAndroid/en/library/ui-component#adapter),
Hikage provides layout extension function for BetterAndroid's [Adapter](https://betterandroid.github.io/BetterAndroid/en/library/ui-component-adapter),
you can use the Hikage layout directly on the original extension method of the adapter.
It uses the `ViewHolderDelegate` provided by BetterAndroid to create extension methods.
@@ -76,9 +76,9 @@ Here is a simple example based on `RecyclerView`.
```kotlin
// Assume this is the dataset you need to bind to.
val listData = ArrayList<CustomBean>()
val listData = ArrayList<MyEntity>()
// Create and bind to a custom RecyclerView.Adapter.
val adapter = recyclerView.bindAdapter<CustomBean> {
val adapter = recyclerView.bindAdapter<MyEntity> {
onBindData { listData }
onBindItemView(
Hikageable = {
@@ -87,8 +87,8 @@ val adapter = recyclerView.bindAdapter<CustomBean> {
textSize = 16f
}
}
) { hikage, bean, position ->
hikage.get<TextView>("text_view").text = bean.name
) { hikage, entity, position ->
hikage.get<TextView>("text_view").text = entity.name
}
}
```

View File

@@ -219,7 +219,7 @@ Or, use in a custom `View`.
class CustomView(context: Context, attrs: AttributeSet? = null) : FrameLayout(context, attrs) {
init {
addView {
addView<FrameLayout.LayoutParams> {
TextView {
text = "Hello, World!"
textSize = 16f

View File

@@ -10,7 +10,16 @@
## hikage-core
### 1.0.1 | 2025.05.06 &ensp;<Badge type="tip" text="最新" vertical="middle" />
### 1.0.2 | 2025.08.24 &ensp;<Badge type="tip" text="最新" vertical="middle" />
- 将 Java 反射相关行为由 [YukiReflection](https://github.com/HighCapable/YukiReflection) 迁移至 [KavaRef](https://github.com/HighCapable/KavaRef)
- 适配 Android 16 (API 36),解决了 Android 16 上 `XmlBlock` 的崩溃问题
- 优化布局性能,移除了不必要的内联操作,对反射操作增加缓存
- `HikageView``HikageViewDeclaration` 新增 `final` 参数以配合 `hikage-compiler` 实现新功能
- `Widgets` 新增 `SurfaceView``WebView` 内置组件
- `Widgets` 调整部分组件为 `final`
### 1.0.1 | 2025.05.06 &ensp;<Badge type="warning" text="过旧" vertical="middle" />
- 修复 KSP 源码没有成功发布的问题
- 新增状态管理功能
@@ -21,19 +30,32 @@
## hikage-compiler
### 1.0.0 | 2025.04.20 &ensp;<Badge type="tip" text="最新" vertical="middle" />
### 1.0.1 | 2025.08.24 &ensp;<Badge type="tip" text="最新" vertical="middle" />
- 新增对 `HikageView``HikageViewDeclaration``final` 参数的支持,详情请参考文档的相关用法
### 1.0.0 | 2025.04.20 &ensp;<Badge type="warning" text="过旧" vertical="middle" />
- 首个版本提交至 Maven
## hikage-extension
### 1.0.0 | 2025.04.20 &ensp;<Badge type="tip" text="最新" vertical="middle" />
### 1.0.1 | 2025.08.24 &ensp;<Badge type="tip" text="最新" vertical="middle" />
- 将 Java 反射相关行为由 [YukiReflection](https://github.com/HighCapable/YukiReflection) 迁移至 [KavaRef](https://github.com/HighCapable/KavaRef)
- `ViewGroup` 新增对 `addView` 的泛型 `ViewGroup.LayoutParams` 支持
### 1.0.0 | 2025.04.20 &ensp;<Badge type="warning" text="过旧" vertical="middle" />
- 首个版本提交至 Maven
## hikage-extension-betterandroid
### 1.0.0 | 2025.04.20 &ensp;<Badge type="tip" text="最新" vertical="middle" />
### 1.0.1 | 2025.08.24 &ensp;<Badge type="tip" text="最新" vertical="middle" />
- 适配了 `BetterAndroid` 解耦合后的 `ui-component``ui-component-adapter`
### 1.0.0 | 2025.04.20 &ensp;<Badge type="warning" text="过旧" vertical="middle" />
- 首个版本提交至 Maven
@@ -45,12 +67,21 @@
## hikage-widget-androidx
### 1.0.0 | 2025.04.20 &ensp;<Badge type="tip" text="最新" vertical="middle" />
### 1.0.1 | 2025.08.24 &ensp;<Badge type="tip" text="最新" vertical="middle" />
- `ConstraintLayout` 新增 `MotionLayout``ImageFilterButton``ImageFilterView``MockView``MotionButton``MotionLabel``MotionTelltales` 组件
- 调整部分组件为 `final`
### 1.0.0 | 2025.04.20 &ensp;<Badge type="warning" text="过旧" vertical="middle" />
- 首个版本提交至 Maven
## hikage-widget-material
### 1.0.0 | 2025.04.20 &ensp;<Badge type="tip" text="最新" vertical="middle" />
### 1.0.1 | 2025.08.24 &ensp;<Badge type="tip" text="最新" vertical="middle" />
- 调整部分组件为 `final`
### 1.0.0 | 2025.04.20 &ensp;<Badge type="warning" text="过旧" vertical="middle" />
- 首个版本提交至 Maven

View File

@@ -136,12 +136,13 @@ Hikage 可以在编译时为指定的布局组件自动生成布局组件对应
你可以在你的自定义 `View` 上加入 `HikageView` 注解,以标记它生成为 Hikage 布局组件。
| 参数名称 | 描述 |
| ------------------ | --------------------------------------------------------------------------------------------------------------------- |
| `lparams` | 布局参数 `ViewGroup.LayoutParams` Class 对象,如果你的自定义 `View``ViewGroup` 的子类,则可以声明或留空使用默认值 |
| `alias` | 布局组件的别名,即要生成的函数名称,默认获取当前 Class 的名称 |
| `requireInit` | 是否要求填写布局的初始化方法块,默认为可省略的参数 |
| `requirePerformer` | 是否要求填写布局的 `performer` 方法块,默认为可省略的参数,仅在你的自定义 `View``ViewGroup` 的子类时生效 |
| 参数名称 | 描述 |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `lparams` | 布局参数 `ViewGroup.LayoutParams` Class 对象,如果你的自定义 `View``ViewGroup` 的子类,则可以声明或留空使用默认值 |
| `alias` | 布局组件的别名,即要生成的函数名称,默认获取当前 Class 的名称 |
| `requireInit` | 是否要求填写布局的初始化方法块,默认为可省略的参数 |
| `requirePerformer` | 是否要求填写布局的 `performer` 方法块,默认为可省略的参数,仅在你的自定义 `View``ViewGroup` 的子类时生效 |
| `final` | 是否将布局声明为 “最终布局”,默认否,即此布局是否是 `ViewGroup` 还是从其继承都将不会生成 `performer` 方法块,设置为 `true` 之后,`lparams``requirePerformer` 将不再有效。 |
> 示例如下
@@ -174,13 +175,14 @@ Hikageable {
Hikage 同样可以为第三方提供的 `View` 组件自动生成布局组件函数,你可以使用 `HikageViewDeclaration` 注解来完成。
| 参数名称 | 描述 |
| ------------------ | --------------------------------------------------------------------------------------------------------------------- |
| `view` | 需要声明的布局组件的 Class 对象 |
| `lparams` | 布局参数 `ViewGroup.LayoutParams` Class 对象,如果你的自定义 `View``ViewGroup` 的子类,则可以声明或留空使用默认值 |
| `alias` | 布局组件的别名,即要生成的函数名称,默认获取 `view` Class 的名称 |
| `requireInit` | 是否要求填写布局的初始化方法块,默认为可省略的参数 |
| `requirePerformer` | 是否要求填写布局的 `performer` 方法块,默认为可省略的参数,仅在你的自定义 `View``ViewGroup` 的子类时生效 |
| 参数名称 | 描述 |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `view` | 需要声明的布局组件的 Class 对象 |
| `lparams` | 布局参数 `ViewGroup.LayoutParams` Class 对象,如果你的自定义 `View``ViewGroup` 的子类,则可以声明或留空使用默认值 |
| `alias` | 布局组件的别名,即要生成的函数名称,默认获取 `view` Class 的名称 |
| `requireInit` | 是否要求填写布局的初始化方法块,默认为可省略的参数 |
| `requirePerformer` | 是否要求填写布局的 `performer` 方法块,默认为可省略的参数,仅在你的自定义 `View``ViewGroup` 的子类时生效 |
| `final` | 是否将布局声明为 “最终布局”,默认否,即此布局是否是 `ViewGroup` 还是从其继承都将不会生成 `performer` 方法块,设置为 `true` 之后,`lparams``requirePerformer` 将不再有效。 |
> 示例如下

View File

@@ -65,7 +65,7 @@ implementation("com.highcapable.hikage:hikage-extension-betterandroid:<version>"
### 适配器 (Adapter) 扩展
Hikage 为 BetterAndroid 提供的 [适配器](https://betterandroid.github.io/BetterAndroid/zh-cn/library/ui-component#%E9%80%82%E9%85%8D%E5%99%A8-adapter)
Hikage 为 BetterAndroid 提供的 [适配器](https://betterandroid.github.io/BetterAndroid/zh-cn/library/ui-component-adapter)
提供了布局扩展功能,你可以直接在适配器的原始扩展方法上使用 Hikage 布局。
它使用了 BetterAndroid 提供的 `ViewHolderDelegate` 来创建扩展方法。
@@ -76,9 +76,9 @@ Hikage 为 BetterAndroid 提供的 [适配器](https://betterandroid.github.io/B
```kotlin
// 假设这就是你需要绑定的数据集
val listData = ArrayList<CustomBean>()
val listData = ArrayList<MyEntity>()
// 创建并绑定到自定义的 RecyclerView.Adapter
val adapter = recyclerView.bindAdapter<CustomBean> {
val adapter = recyclerView.bindAdapter<MyEntity> {
onBindData { listData }
onBindItemView(
Hikageable = {
@@ -87,8 +87,8 @@ val adapter = recyclerView.bindAdapter<CustomBean> {
textSize = 16f
}
}
) { hikage, bean, position ->
hikage.get<TextView>("text_view").text = bean.name
) { hikage, entity, position ->
hikage.get<TextView>("text_view").text = entity.name
}
}
```

View File

@@ -217,7 +217,7 @@ root.addView {
class CustomView(context: Context, attrs: AttributeSet? = null) : FrameLayout(context, attrs) {
init {
addView {
addView<FrameLayout.LayoutParams> {
TextView {
text = "Hello, World!"
textSize = 16f

View File

@@ -3,35 +3,34 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
android.useAndroidX=true
android.nonTransitiveRClass=true
kotlin.code.style=official
kotlin.incremental.useClasspathSnapshot=true
# Project Configuration
project.name=Hikage
project.url=https://github.com/BetterAndroid/Hikage
project.groupName=com.highcapable.hikage
project.android.compileSdk=35
project.android.compileSdk=36
project.android.minSdk=21
project.android.targetSdk=35
project.android.targetSdk=36
project.samples-app.packageName=com.highcapable.hikage.demo
project.samples-app.versionName=universal
project.samples-app.versionCode=1
project.hikage-core.namespace=${project.groupName}.core
project.hikage-core.version="1.0.1"
project.hikage-core.version="1.0.2"
project.hikage-core-lint.namespace=${project.groupName}.core.lint
project.hikage-core-lint.identifier=${project.groupName}:hikage-core:${project.hikage-core.version}
project.hikage-core-lint.min-api=9
project.hikage-core-lint.registry-v2-class="${project.hikage-core-lint.namespace}.HikageIssueRegistry"
project.hikage-extension.namespace=${project.groupName}.extension
project.hikage-extension.version="1.0.0"
project.hikage-extension.version="1.0.1"
project.hikage-extension-betterandroid.namespace=${project.groupName}.extension.betterandroid
project.hikage-extension-betterandroid.version="1.0.0"
project.hikage-extension-betterandroid.version="1.0.1"
project.hikage-extension-compose.namespace=${project.groupName}.extension.androidx.compose
project.hikage-extension-compose.version="1.0.0"
project.hikage-compiler.namespace="${project.groupName}.compiler"
project.hikage-compiler.version="1.0.0"
project.hikage-compiler.version="1.0.1"
project.hikage-widget-androidx.namespace=${project.groupName}.widget.androidx
project.hikage-widget-androidx.version="1.0.0"
project.hikage-widget-androidx.version="1.0.1"
project.hikage-widget-material.namespace=${project.groupName}.widget.google.material
project.hikage-widget-material.version="1.0.0"
project.hikage-widget-material.version="1.0.1"
# Maven Publish Configuration
SONATYPE_HOST=CENTRAL_PORTAL
RELEASE_SIGNING_ENABLED=true

View File

@@ -24,7 +24,7 @@ plugins:
auto-update: false
com.android.application:
alias: android-application
version: 8.9.3
version: 8.12.1
com.android.library:
alias: android-library
version-ref: android-application
@@ -37,7 +37,7 @@ plugins:
auto-update: false
com.vanniktech.maven.publish:
alias: maven-publish
version: 0.33.0
version: 0.34.0
libraries:
org.jetbrains.kotlin:
@@ -59,11 +59,13 @@ libraries:
version-ref: <this>::kotlinpoet
com.highcapable.betterandroid:
ui-component:
version: 1.0.7
version: 1.0.8
ui-component-adapter:
version: 1.0.0
ui-extension:
version: 1.0.6
version: 1.0.7
system-extension:
version: 1.0.2
version: 1.0.3
org.lsposed.hiddenapibypass:
hiddenapibypass:
version: 6.1
@@ -74,10 +76,10 @@ libraries:
version: 1.0.1
com.highcapable.pangutext:
pangutext-android:
version: 1.0.2
version: 1.0.4
androidx.core:
core:
version: 1.16.0
version: 1.17.0
core-ktx:
version-ref: <this>::core
androidx.appcompat:
@@ -117,19 +119,20 @@ libraries:
version: 1.4.0
androidx.compose.ui:
ui:
version: 1.8.3
version: 1.9.0
junit:
junit:
version: 4.13.2
androidx.test.ext:
junit:
version: 1.2.1
version: 1.3.0
androidx.test.espresso:
espresso-core:
version: 3.6.1
version: 3.7.0
com.android.tools.lint:
lint:
version: 31.11.0
version: 31.9.0
auto-update: false
lint-api:
version-ref: <this>::lint
lint-checks:

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

View File

@@ -39,15 +39,20 @@ fun KSDeclaration.getSimpleNameString(): String {
fun KSClassDeclaration.isSubclassOf(superType: KSType?): Boolean {
if (superType == null) return false
if (this == superType.declaration) return true
superTypes.forEach { parent ->
val resolvedParent = parent.resolve()
// Direct match.
if (resolvedParent == superType) return true
val parentDeclaration = resolvedParent.declaration as? KSClassDeclaration
?: return@forEach
// Recursively check the parent class.
if (parentDeclaration.isSubclassOf(superType)) return true
}; return false
}
return false
}
fun KSClassDeclaration.asType() = asType(emptyList())

View File

@@ -71,6 +71,7 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
override fun startProcess(resolver: Resolver) {
Processor.init(logger, resolver)
val dependencies = Dependencies(aggregating = true, *resolver.getAllFiles().toList().toTypedArray())
resolver.getSymbolsWithAnnotation(HikageViewSpec.CLASS)
.filterIsInstance<KSClassDeclaration>()
@@ -79,9 +80,11 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
ksClass.annotations.forEach {
// Get annotation parameters.
val (annotation, declaration) = HikageViewSpec.create(resolver, it, ksClass)
performers += Performer(annotation, declaration)
}
}
resolver.getSymbolsWithAnnotation(HikageViewDeclarationSpec.CLASS)
.filterIsInstance<KSClassDeclaration>()
.distinctBy { it.qualifiedName }
@@ -89,14 +92,17 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
ksClass.annotations.forEach {
// Get annotation parameters.
val (annotation, declaration) = HikageViewDeclarationSpec.create(resolver, it, ksClass)
performers += Performer(annotation, declaration)
}
}
processPerformer(dependencies)
}
private fun processPerformer(dependencies: Dependencies) {
val duplicatedItems = performers.groupBy { it.declaration.key }.filter { it.value.size > 1 }.flatMap { it.value }
require(duplicatedItems.isEmpty()) {
"Discover duplicate @HikageView or @HikageViewDeclaration's class name or alias definitions, " +
"you can re-specify the class name using the `alias` parameter.\n" +
@@ -109,25 +115,30 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
private fun generateCodeFile(dependencies: Dependencies, performer: Performer) {
val classNameSet = performer.declaration.alias ?: performer.declaration.className
val fileName = "_$classNameSet"
val viewClass = performer.declaration.toClassName().let {
val packageName = it.packageName
val simpleName = it.simpleName
val topClassName = if (simpleName.contains(".")) simpleName.split(".")[0] else null
// com.example.MyViewScope
// com.example.MyViewScope.MyView
topClassName?.let { name -> ClassName(packageName, name) } to it
}
val lparamsClass = performer.annotation.lparams?.let {
val packageName = it.packageName.asString()
val subClassName = it.getSimpleNameString()
val simpleName = it.simpleName.asString()
val topClassName = subClassName.replace(".$simpleName", "")
// android.view.ViewGroup
// android.view.ViewGroup.LayoutParams
(if (topClassName != subClassName)
ClassName(packageName, topClassName)
else null) to ClassName(packageName, subClassName)
}
val packageName = "$PACKAGE_NAME_PREFIX.${performer.declaration.packageName}"
val fileSpec = FileSpec.builder(packageName, fileName).apply {
addFileComment(
@@ -140,6 +151,7 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
**DO NOT EDIT THIS FILE MANUALLY**
""".trimIndent()
)
addAnnotation(
AnnotationSpec.builder(Suppress::class)
.addMember("%S, %S, %S", "unused", "FunctionName", "DEPRECATION")
@@ -150,17 +162,22 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
.addMember("%S", "${classNameSet}Performer")
.build()
)
addImport(DeclaredSymbol.ANDROID_VIEW_PACKAGE_NAME, DeclaredSymbol.ANDROID_VIEW_GROUP_CLASS_NAME)
addImport(DeclaredSymbol.HIKAGE_CORE_PACKAGE_NAME, DeclaredSymbol.HIKAGE_CLASS_NAME)
// Kotlin's import rule is to introduce the parent class that also needs to be introduced at the same time.
// If a child class exists, it needs to import the parent class,
// and kotlinpoet will not perform this operation automatically.
viewClass.first?.let { addImport(it.packageName, it.simpleName) }
lparamsClass?.first?.let { addImport(it.packageName, it.simpleName) }
// May conflict with other [LayoutParams].
lparamsClass?.second?.let { addAliasedImport(it, it.getTypedSimpleName()) }
addAliasedImport(ViewGroupLpClass, ViewGroupLpClass.getTypedSimpleName())
addAliasedImport(HikageLparamClass, HikageLparamClass.getTypedSimpleName())
addFunction(FunSpec.builder(classNameSet).apply {
addKdoc(
"""
@@ -170,10 +187,12 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
@return [${performer.declaration.className}]
""".trimIndent()
)
addAnnotation(HikageableClass)
addModifiers(KModifier.INLINE)
addTypeVariable(TypeVariableName(name = "LP", ViewGroupLpClass).copy(reified = true))
receiver(PerformerClass.parameterizedBy(TypeVariableName("LP")))
addParameter(
ParameterSpec.builder(name = "lparams", HikageLparamClass.copy(nullable = true))
.defaultValue("null")
@@ -192,7 +211,7 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
if (!performer.annotation.requireInit) defaultValue("{}")
}.build()
)
lparamsClass?.second?.let {
lparamsClass?.second?.takeIf { !performer.annotation.final }?.let {
addParameter(
ParameterSpec.builder(
name = "performer",
@@ -203,9 +222,11 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
)
addStatement("return ViewGroup<${performer.declaration.className}, ${it.simpleName}>(lparams, id, init, performer)")
} ?: addStatement("return View<${performer.declaration.className}>(lparams, id, init)")
returns(viewClass.second)
}.build())
}.build()
// May already exists, no need to generate.
runCatching {
fileSpec.writeTo(codeGenerator, dependencies)
@@ -239,6 +260,7 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
ksType.declaration.qualifiedName?.asString() != Any::class.qualifiedName
}
var resolvedLparams = lparamsType?.declaration?.getClassDeclaration(resolver)
when {
// If the current view is not a view group but the lparams parameter is declared,
// remove the lparams parameter.
@@ -253,6 +275,7 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
// set the default type parameter for it.
declaration.isViewGroup && resolvedLparams == null -> resolvedLparams = lparamsDeclaration
}
// Verify layout parameter class.
if (resolvedLparams != null) require(resolvedLparams.isSubclassOf(lparamsDeclaration.asType())) {
val resolvedLparamsName = resolvedLparams.qualifiedName?.asString()
@@ -271,17 +294,21 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
val packageName = ksClass.packageName.asString()
val className = ksClass.getSimpleNameString()
val isViewGroup = ksClass.isSubclassOf(viewGroupDeclaration.asType())
var _alias = alias
// If no alias name is set, if the class name contains a subclass,
// replace it with an underscore and use it as an alias name.
if (_alias.isNullOrBlank() && className.contains("."))
_alias = className.replace(".", "_")
_alias = _alias?.takeIf { it.isNotBlank() }
val declaration = ViewDeclaration(packageName, className, _alias, isViewGroup, baseType)
// Verify the legality of the class name.
if (!_alias.isNullOrBlank()) require(ClassDetector.verify(_alias)) {
"Declares @$tagName's alias \"$_alias\" is illegal.\n${declaration.locateDesc}"
}
// [ViewGroup] cannot be new instance.
require(ksClass != viewGroupDeclaration) {
"Declares @$tagName's class must not be a directly \"${DeclaredSymbol.ANDROID_VIEW_GROUP_CLASS}\".\n${declaration.locateDesc}"
@@ -290,6 +317,7 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
require(ksClass.isSubclassOf(viewDeclaration.asType())) {
"Declares @$tagName's class must be subclass of \"${DeclaredSymbol.ANDROID_VIEW_CLASS}\".\n${declaration.locateDesc}"
}
return declaration
}
}
@@ -315,7 +343,8 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
override val lparams: KSClassDeclaration?,
override val alias: String?,
override val requireInit: Boolean,
override val requirePerformer: Boolean
override val requirePerformer: Boolean,
override val final: Boolean
) : HikageAnnotationSpec {
companion object {
@@ -332,10 +361,13 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
val alias = annotation.arguments.getOrNull<String>("alias")
val requireInit = annotation.arguments.getOrNull<Boolean>("requireInit") ?: false
val requirePerformer = annotation.arguments.getOrNull<Boolean>("requirePerformer") ?: false
val final = annotation.arguments.getOrNull<Boolean>("final") ?: false
// Solve the actual content of the annotation parameters.
val declaration = Processor.createViewDeclaration(NAME, alias, ksClass)
val resolvedLparams = Processor.resolvedLparamsDeclaration(NAME, resolver, declaration, lparams)
return HikageViewSpec(resolvedLparams, alias, requireInit, requirePerformer) to declaration
return HikageViewSpec(resolvedLparams, alias, requireInit, requirePerformer, final) to declaration
}
}
}
@@ -345,7 +377,8 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
override val lparams: KSClassDeclaration?,
override val alias: String?,
override val requireInit: Boolean,
override val requirePerformer: Boolean
override val requirePerformer: Boolean,
override val final: Boolean
) : HikageAnnotationSpec {
companion object {
@@ -363,9 +396,12 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
val alias = annotation.arguments.getOrNull<String>("alias")
val requireInit = annotation.arguments.getOrNull<Boolean>("requireInit") ?: false
val requirePerformer = annotation.arguments.getOrNull<Boolean>("requirePerformer") ?: false
val final = annotation.arguments.getOrNull<Boolean>("final") ?: false
// Solve the actual content of the annotation parameters.
val resolvedView = view?.declaration?.getClassDeclaration(resolver) ?: error("Internal error.")
val declaration = Processor.createViewDeclaration(NAME, alias, resolvedView, ksClass)
// Only object classes can be used as view declarations.
require(ksClass.classKind == ClassKind.OBJECT) {
"Declares @$NAME's class must be an object.\n${declaration.locateDesc}"
@@ -373,8 +409,9 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
require(!ksClass.isCompanionObject) {
"Declares @$NAME's class must not be a companion object.\n${declaration.locateDesc}"
}
val resolvedLparams = Processor.resolvedLparamsDeclaration(NAME, resolver, declaration, lparams)
return HikageViewDeclarationSpec(resolvedView, resolvedLparams, alias, requireInit, requirePerformer) to declaration
return HikageViewDeclarationSpec(resolvedView, resolvedLparams, alias, requireInit, requirePerformer, final) to declaration
}
}
}
@@ -384,6 +421,7 @@ class HikageViewGenerator(override val environment: SymbolProcessorEnvironment)
val alias: String?
val requireInit: Boolean
val requirePerformer: Boolean
val final: Boolean
}
private data class Performer(

View File

@@ -60,6 +60,7 @@ class HikageSafeTypeCastDetector : Detector(), Detector.UastScanner {
override fun visitQualifiedReferenceExpression(node: UQualifiedReferenceExpression) {
if (node.selector !is UBinaryExpressionWithType) return
val castExpr = node.selector as UBinaryExpressionWithType
visitAndLint(context, castExpr, node)
}
@@ -75,11 +76,14 @@ class HikageSafeTypeCastDetector : Detector(), Detector.UastScanner {
) {
// Get the parent node, if it is wrapped with brackets will also be included.
val locationNode = node.uastParent as? UParenthesizedExpression ?: node
val receiver = parent?.receiver ?: node.operand
val receiverType = (node.operand as? UArrayAccessExpression)?.receiver?.getExpressionType() ?: return
val receiverClass = receiverType.canonicalText
// Filter retains results that meet the conditions.
if (receiverClass != DeclaredSymbol.HIKAGE_CLASS) return
// Like `hikage["your_id"] as YourView`.
val exprText = node.sourcePsi?.text ?: return
// Like `hikage["your_id"]`.
@@ -88,11 +92,15 @@ class HikageSafeTypeCastDetector : Detector(), Detector.UastScanner {
val receiverNameText = receiverText.split("[")[0]
// Like `"your_id"`.
val receiverContent = runCatching { receiverText.split("[")[1].split("]")[0] }.getOrNull() ?: return
val isSafeCast = exprText.contains("as?") || exprText.endsWith("?")
// Like `YourView`.
val castTypeContent = node.typeReference?.sourcePsi?.text?.removeSuffix("?") ?: return
val replacement = "$receiverNameText.${if (isSafeCast) "getOrNull" else "get"}<$castTypeContent>($receiverContent)"
val replaceSuggestion = if (isSafeCast) "Hikage.getOrNull<$castTypeContent>" else "Hikage.get<$castTypeContent>"
val location = context.getLocation(locationNode)
val lintFix = LintFix.create()
.name("Replace with '$replacement'")
@@ -101,6 +109,7 @@ class HikageSafeTypeCastDetector : Detector(), Detector.UastScanner {
.with(replacement)
.reformat(true)
.build()
context.report(
ISSUE, locationNode, location,
message = "Can be replaced with safe type cast `$replaceSuggestion`.",

View File

@@ -70,6 +70,7 @@ class HikageableBeyondScopeDetector : Detector(), Detector.UastScanner {
override fun visitCallExpression(node: UCallExpression) {
val callExpr = node.sourcePsi as? KtCallExpression ?: return
val method = node.resolve() ?: return
startLint(callExpr, method)
organizeAndReport()
}
@@ -78,6 +79,7 @@ class HikageableBeyondScopeDetector : Detector(), Detector.UastScanner {
val className = method.containingClass?.qualifiedName ?: ""
val hasHikageable = method.hasHikageable()
val hasLayoutParams = className == DeclaredSymbol.HIKAGE_PERFORMER_CLASS && method.name == "LayoutParams"
if (hasHikageable || hasLayoutParams) visitAndLint(callExpr, method)
}
@@ -85,6 +87,7 @@ class HikageableBeyondScopeDetector : Detector(), Detector.UastScanner {
reports.forEach {
// Check if the call has been reported before reporting.
if (reportedNodes.contains(it.callExpr)) return@forEach
val location = context.getLocation(it.callExpr)
val lintFix = LintFix.create()
.name("Delete Call Expression")
@@ -93,6 +96,7 @@ class HikageableBeyondScopeDetector : Detector(), Detector.UastScanner {
// Delete the call expression.
.with("")
.build()
context.report(ISSUE, it.callExpr, location, it.message, lintFix)
reportedNodes.add(it.callExpr)
}
@@ -102,28 +106,37 @@ class HikageableBeyondScopeDetector : Detector(), Detector.UastScanner {
val bodyBlocks = mutableMapOf<String, KtExpression>()
val parameters = method.parameterList.parameters
val valueArguments = callExpr.valueArgumentList?.arguments ?: emptyList()
fun visitValueArg(arg: KtValueArgument) {
val name = arg.getArgumentName()?.asName?.identifier ?: ""
val expr = arg.getArgumentExpression()
val parameter = parameters.firstOrNull { it.name == name }
// If the last bit is a lambda expression, then `parameter` must have a lambda parameter defined by the last bit.
?: if (arg is KtLambdaArgument) parameters.lastOrNull() else null
val isMatched = parameter?.type?.canonicalText?.matches(DeclaredSymbol.HIKAGE_VIEW_REGEX) == true &&
!parameter.type.canonicalText.contains(DeclaredSymbol.HIKAGE_PERFORMER_CLASS)
if (expr is KtLambdaExpression && isMatched)
expr.bodyExpression?.let { bodyBlocks[name] = it }
}
// Get the last lambda expression.
val lastLambda = callExpr.lambdaArguments.lastOrNull()
if (lastLambda != null) visitValueArg(lastLambda)
valueArguments.forEach { arg -> visitValueArg(arg) }
bodyBlocks.toList().flatMap { (_, value) -> value.children.filterIsInstance<KtCallExpression>() }.forEach {
val expression = it.toUElementOfType<UCallExpression>() ?: return@forEach
val sCallExpr = expression.sourcePsi as? KtCallExpression ?: return@forEach
val sMethod = expression.resolve() ?: return@forEach
if (sMethod.hasHikageable()) {
val message = "Performers are not allowed to appear in `${method.name}` DSL creation process."
reports.add(ReportDetail(message, expression))
// Recursively to visit next level.
visitAndLint(sCallExpr, sMethod)
}

View File

@@ -65,6 +65,7 @@ class HikageableFunctionsDetector : Detector(), Detector.UastScanner {
override fun visitMethod(node: UMethod) {
val uastBody = node.uastBody as? UBlockExpression ?: return
val bodyHasHikageable = uastBody.expressions.any {
when (it) {
is UCallExpression -> it.resolve()?.hasHikageable() ?: false
@@ -73,17 +74,22 @@ class HikageableFunctionsDetector : Detector(), Detector.UastScanner {
else -> false
}
}
if (!node.hasHikageable() && bodyHasHikageable) {
val location = context.getLocation(node)
val nameLocation = context.getNameLocation(node)
val message = "Function `${node.name}` must be marked with the `@Hikageable` annotation."
val functionText = node.asSourceString()
val hasDoubleSlash = functionText.startsWith("//")
val replacement = functionRegex.replace(functionText) { result ->
val functionBody = result.groupValues.getOrNull(0) ?: functionText
val prefix = if (hasDoubleSlash) "\n" else ""
"$prefix@Hikageable $functionBody"
}
val lintFix = LintFix.create()
.name("Add '@Hikageable' to '${node.name}'")
.replace()
@@ -92,6 +98,7 @@ class HikageableFunctionsDetector : Detector(), Detector.UastScanner {
.imports(DeclaredSymbol.HIKAGEABLE_ANNOTATION_CLASS)
.reformat(true)
.build()
context.report(ISSUE, node, nameLocation, message, lintFix)
}
}

View File

@@ -135,18 +135,23 @@ class WidgetsUsageDetector : Detector(), Detector.UastScanner {
importRefs.getOrNull(1)?.trim()
else null
val importPrefix = importRefs[0]
val hasPrefix = importPrefix.contains(".")
val name = if (hasPrefix)
importPrefix.split(".").last()
else importPrefix
val packagePrefix = importPrefix.replace(if (hasPrefix) ".$name" else name, "")
val reference = ImportReference(packagePrefix, name, alias)
importReferences.add(reference)
}
override fun visitCallExpression(node: UCallExpression) {
val callExpr = node.sourcePsi as? KtCallExpression ?: return
val method = node.resolve() ?: return
startLint(callExpr, method)
}
@@ -165,7 +170,9 @@ class WidgetsUsageDetector : Detector(), Detector.UastScanner {
type.canonicalText == VIEW_GROUP_CLASS_NAME
}
}
val isTypedViewFunction = typedViewFunctionIndex >= 0
val imports = typeArgumentsText.mapNotNull { typeName ->
when {
// Like `TextView`.
@@ -179,18 +186,24 @@ class WidgetsUsageDetector : Detector(), Detector.UastScanner {
else -> null
}
}
val matchedIndex = builtInWidgets.indexOfFirst { imports.any { e -> e.alias == it || e.name == it } }
val isBuiltInWidget = matchedIndex >= 0
if (isTypedViewFunction && isBuiltInWidget) {
val widgetName = builtInWidgets[matchedIndex]
val sourceLocation = context.getLocation(callExpr)
val sourceText = callExpr.toUElement()?.asSourceString() ?: return
val callExprElement = callExpr.toUElement() ?: return
// Matchs '>' and like `View<TextView`'s length + 1.
val callExprLength = sourceText.split(">")[0].trim().length + 1
val nameLocation = context.getRangeLocation(callExprElement, fromDelta = 0, callExprLength)
// Only replace the first one, because there may be multiple sub-functions in DSL.
val replacement = sourceText.replaceFirst(viewExpressionRegex, widgetName)
val lintFix = LintFix.create()
.name("Replace with '$widgetName'")
.replace()
@@ -199,6 +212,7 @@ class WidgetsUsageDetector : Detector(), Detector.UastScanner {
.imports("$WIDGET_FUNCTION_PREFIX.$widgetName")
.reformat(true)
.build()
val message = "Can be simplified to `$widgetName`."
context.report(ISSUE, callExpr, nameLocation, message, lintFix)
}

View File

@@ -28,15 +28,6 @@ android {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
freeCompilerArgs = listOf(
"-opt-in=kotlin.ExperimentalStdlibApi",
"-Xno-param-assertions",
"-Xno-call-assertions",
"-Xno-receiver-assertions"
)
}
}
dependencies {

View File

@@ -45,6 +45,9 @@ import kotlin.reflect.KClass
* @param requireInit whether to force the `init` parameter to be called, default is false.
* @param requirePerformer whether to force the `performer` parameter to be called, default is false,
* this parameter will be ignored when no `performer` parameter is needed here.
* @param final whether to declare this layout as "final layout", default is false, that is,
* whether this layout inherits from or is [ViewGroup], the `performer` parameter will not be generated.
* After set to `true`, [lparams] and [requirePerformer] will no longer be valid.
*/
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS)
@@ -53,5 +56,6 @@ annotation class HikageView(
val lparams: KClass<*> = Any::class,
val alias: String = "",
val requireInit: Boolean = false,
val requirePerformer: Boolean = false
val requirePerformer: Boolean = false,
val final: Boolean = false
)

View File

@@ -48,6 +48,9 @@ import kotlin.reflect.KClass
* @param requireInit whether to force the `init` parameter to be called, default is false.
* @param requirePerformer whether to force the `performer` parameter to be called, default is false,
* this parameter will be ignored when no `performer` parameter is needed here.
* @param final whether to declare this layout as "final layout", default is false, that is,
* whether this layout inherits from or is [ViewGroup], the `performer` parameter will not be generated.
* After set to `true`, [lparams] and [requirePerformer] will no longer be valid.
*/
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS)
@@ -57,5 +60,6 @@ annotation class HikageViewDeclaration(
val lparams: KClass<*> = Any::class,
val alias: String = "",
val requireInit: Boolean = false,
val requirePerformer: Boolean = false
val requirePerformer: Boolean = false,
val final: Boolean = false
)

View File

@@ -31,7 +31,7 @@ import android.content.res.loader.AssetsProvider
import android.content.res.loader.ResourcesProvider
import android.util.AttributeSet
import androidx.annotation.StyleRes
import com.highcapable.betterandroid.system.extension.tool.SystemVersion
import com.highcapable.betterandroid.system.extension.tool.AndroidVersion
import com.highcapable.betterandroid.ui.extension.view.inflateOrNull
import com.highcapable.betterandroid.ui.extension.view.layoutInflater
import com.highcapable.hikage.core.R
@@ -116,12 +116,12 @@ internal object XmlBlockBypass {
private val resolver = object : MemberProcessor.Resolver() {
override fun <T : Any> getDeclaredConstructors(declaringClass: Class<T>): List<Constructor<T>> =
SystemVersion.require(SystemVersion.P, super.getDeclaredConstructors(declaringClass)) {
AndroidVersion.require(AndroidVersion.P, super.getDeclaredConstructors(declaringClass)) {
HiddenApiBypass.getDeclaredMethods(declaringClass).filterIsInstance<Constructor<T>>().toList()
}
override fun <T : Any> getDeclaredMethods(declaringClass: Class<T>): List<Method> =
SystemVersion.require(SystemVersion.P, super.getDeclaredMethods(declaringClass)) {
AndroidVersion.require(AndroidVersion.P, super.getDeclaredMethods(declaringClass)) {
HiddenApiBypass.getDeclaredMethods(declaringClass).filterIsInstance<Method>().toList()
}
}
@@ -143,6 +143,7 @@ internal object XmlBlockBypass {
fun init(context: Context) {
// Context may be loaded from the preview and other non-Android platforms, ignoring this.
if (context.javaClass.name.endsWith("BridgeContext")) return
init(context.applicationContext.applicationInfo)
}
@@ -151,11 +152,12 @@ internal object XmlBlockBypass {
* @param info the application info.
*/
private fun init(info: ApplicationInfo) {
if (SystemVersion.isLowOrEqualsTo(SystemVersion.P)) return
if (AndroidVersion.isAtMost(AndroidVersion.P)) return
if (isInitOnce) return
val sourceDir = info.sourceDir
xmlBlock = when {
SystemVersion.isHighOrEqualsTo(SystemVersion.R) ->
AndroidVersion.isAtLeast(AndroidVersion.R) ->
// private static native long nativeLoad(@FormatType int format, @NonNull String path,
// @PropertyFlags int flags, @Nullable AssetsProvider asset) throws IOException;
ApkAssetsClass.resolve()
@@ -166,7 +168,7 @@ internal object XmlBlockBypass {
parameters(Int::class, String::class, Int::class, AssetsProvider::class)
modifiers(Modifiers.NATIVE)
}?.invokeQuietly(FORMAT_APK, sourceDir, PROPERTY_SYSTEM, null)
SystemVersion.isHighOrEqualsTo(SystemVersion.P) ->
AndroidVersion.isAtLeast(AndroidVersion.P) ->
// private static native long nativeLoad(
// @NonNull String path, boolean system, boolean forceSharedLib, boolean overlay)
// throws IOException;
@@ -180,18 +182,20 @@ internal object XmlBlockBypass {
}?.invokeQuietly(sourceDir, false, false, false)
else -> error("Unsupported Android version.")
} as? Long? ?: error("Failed to create ApkAssets.")
blockParser = XmlBlockClass.resolve()
.processor(resolver)
.optional()
.firstConstructorOrNull {
if (SystemVersion.isHighOrEqualsTo(36))
if (AndroidVersion.isAtLeast(AndroidVersion.BAKLAVA))
parameters(AssetManager::class, Long::class, Boolean::class)
else parameters(AssetManager::class, Long::class)
}?.let {
if (SystemVersion.isHighOrEqualsTo(36))
if (AndroidVersion.isAtLeast(AndroidVersion.BAKLAVA))
it.createQuietly(null, xmlBlock, false)
else it.createQuietly(null, xmlBlock)
} ?: error("Failed to create XmlBlock\$Parser.")
isInitOnce = true
}
@@ -208,8 +212,10 @@ internal object XmlBlockBypass {
*/
fun createViewAttrs() = context.layoutInflater.inflateOrNull<HikageAttrsView>(R.layout.layout_hikage_attrs_view)?.attrs
as? XmlResourceParser? ?: error("Failed to create AttributeSet.")
return if (SystemVersion.isHighOrEqualsTo(SystemVersion.P)) {
return if (AndroidVersion.isAtLeast(AndroidVersion.P)) {
if (!isInitOnce) return createViewAttrs()
require(blockParser != null) { "Hikage initialization failed." }
newParser?.copy()?.of(blockParser)
?.invokeQuietly<XmlResourceParser>(resId)

View File

@@ -53,7 +53,6 @@ import com.highcapable.betterandroid.ui.extension.view.inflate
import com.highcapable.betterandroid.ui.extension.view.layoutInflater
import com.highcapable.hikage.annotation.Hikageable
import com.highcapable.hikage.bypass.XmlBlockBypass
import com.highcapable.hikage.core.Hikage.LayoutParamsBody
import com.highcapable.hikage.core.base.HikageFactory
import com.highcapable.hikage.core.base.HikageFactoryBuilder
import com.highcapable.hikage.core.base.HikagePerformer
@@ -167,7 +166,9 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
if (parent != null && attachToParent) {
val parentId = generateRandomViewId()
provideView(parent, parentId)
}; newPerformer(lpClass, parent, attachToParent, context).apply(performer)
}
newPerformer(lpClass, parent, attachToParent, context).apply(performer)
}
/**
@@ -317,11 +318,13 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
*/
private fun <V : View> createView(viewClass: Class<V>, id: String?, context: Context): V {
val attrs = createAttributeSet(context)
val view = createViewFromFactory(viewClass, id, context, attrs) ?: getViewConstructor(viewClass)?.build(context, attrs)
if (view == null) throw PerformerException(
"Create view of type ${viewClass.name} failed. " +
"Please make sure the view class has a constructor with a single parameter of type Context."
)
provideView(view, id)
return view
}
@@ -334,16 +337,20 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
private fun <V : View> getViewConstructor(viewClass: Class<V>) =
viewConstructors[viewClass.name] as? ViewConstructor<V>? ?: run {
var parameterCount = 0
val twoParams = viewClass.resolve()
.optional(silent = true)
.firstConstructorOrNull { parameters(Context::class, AttributeSet::class) }
val onceParam = viewClass.resolve()
.optional(silent = true)
.firstConstructorOrNull { parameters(Context::class) }
val constructor = onceParam?.apply { parameterCount = 1 }
?: twoParams?.apply { parameterCount = 2 }
val viewConstructor = constructor?.let { ViewConstructor(it, parameterCount) }
if (viewConstructor != null) viewConstructors[viewClass.name] = viewConstructor
viewConstructor
}
@@ -358,15 +365,20 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
private fun <V : View> createViewFromFactory(viewClass: Class<V>, id: String?, context: Context, attrs: AttributeSet): V? {
val parent = performers.firstOrNull()?.parent
var processed: V? = null
factories.forEach { factory ->
val params = PerformerParams(id, attrs, viewClass as Class<View>)
val view = factory(parent, processed, context, params)
if (view != null && view.javaClass isNotSubclassOf viewClass) throw PerformerException(
"HikageFactory cannot cast the created view type \"${view.javaClass}\" to \"${viewClass.name}\", " +
"please confirm that the view type you created is correct."
)
if (view != null) processed = view as? V?
}; return processed
}
return processed
}
/**
@@ -379,6 +391,7 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
val (requireId, viewId) = generateViewId(id)
view.id = viewId
views[requireId] = view
return requireId
}
@@ -395,12 +408,16 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
*/
fun doGenerate(id: String): Int {
val generateId = View.generateViewId()
if (viewIds.contains(id)) throw PerformerException("View with id \"$id\" already exists.")
viewIds[id] = generateId
return generateId
}
val requireId = id ?: generateRandomViewId()
val viewId = doGenerate(requireId)
return requireId to viewId
}
@@ -456,6 +473,7 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
*/
internal inline fun requireNoPerformers(name: String, block: () -> Unit) {
val viewCount = views.size
block()
if (views.size != viewCount) throw PerformerException(
"Performers are not allowed to appear in $name DSL creation process."
@@ -472,13 +490,16 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
private fun include(delegate: Delegate<*>, context: Context, embedded: Boolean): Hikage {
val hikage = delegate.create(context)
if (!embedded) return hikage
val duplicateId = hikage.viewIds.toList().firstOrNull { (k, _) -> viewIds.containsKey(k) }?.first
if (duplicateId != null) throw PerformerException(
"Embedded layout view IDs conflict, the view id \"$duplicateId\" is already exists."
)
viewIds.putAll(hikage.viewIds)
views.putAll(hikage.views)
performers.addAll(hikage.performers)
return hikage
}
@@ -544,9 +565,11 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
val lpDelegate = LayoutParams.from(current, lpClass, parent, lparams)
val view = createView(classOf<V>(), id, context)
view.layoutParams = lpDelegate.create()
requireNoPerformers(classOf<V>().name) { view.init() }
startProvide(id, classOf<V>())
addToParentIfRequired(view)
return view
}
@@ -587,10 +610,12 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
val lpDelegate = LayoutParams.from(current, lpClass, parent, lparams)
val view = createView(classOf<VG>(), id, context)
view.layoutParams = lpDelegate.create()
requireNoPerformers(classOf<VG>().name) { view.init() }
startProvide(id, classOf<VG>())
addToParentIfRequired(view)
newPerformer<LP>(view).apply(performer)
return view
}
@@ -625,10 +650,12 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
id: String? = null
): View {
val view = context.layoutInflater.inflate(resId, parent, attachToRoot = false)
startProvide(id, view.javaClass, view)
lparams?.create()?.let { view.layoutParams = it }
provideView(view, id)
addToParentIfRequired(view)
return view
}
@@ -645,14 +672,17 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
): VB {
val viewBinding = ViewBinding<VB>().inflate(context.layoutInflater, parent, attachToParent = false)
val view = viewBinding.root
startProvide(id, view.javaClass, view)
if (view.parent != null) throw ProvideException(
"The ViewBinding($view) already has a parent, " +
"it may have been created using layout root node <merge> or <include>, cannot be provided."
)
lparams?.create()?.let { view.layoutParams = it }
provideView(view, id)
addToParentIfRequired(view)
return viewBinding
}
@@ -670,11 +700,14 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
id: String? = null
): View {
if (view.parent != null) throw ProvideException("The view $view already has a parent, cannot be provided.")
startProvide(id, view.javaClass, view)
val lpDelegate = LayoutParams.from(current = this@Hikage, lpClass, parent, lparams, view.layoutParams)
view.layoutParams = lpDelegate.create()
provideView(view, id)
addToParentIfRequired(view)
return view
}
@@ -708,13 +741,17 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
): Hikage {
val view = hikage.root
startProvide(id, view.javaClass, view)
val lpDelegate = LayoutParams.from(current = this@Hikage, lpClass, parent, lparams, view.layoutParams)
if (view.parent != null) throw ProvideException(
"The Hikage layout root view $view already has a parent, cannot be provided."
)
view.layoutParams = lpDelegate.create()
provideView(view, id)
addToParentIfRequired(view)
return hikage
}
@@ -783,6 +820,7 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
*/
private fun <V : View> startProvide(id: String?, viewClass: Class<V>, view: V? = null) {
provideCount++
if (provideCount > 1 && (parent == null || !attachToParent)) throw ProvideException(
"Provide view ${view?.javaClass ?: viewClass}(${id?.let { "\"$it\""} ?: "<anonymous>"}) failed. ${
if (parent == null) "No parent view group found"
@@ -900,6 +938,7 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
superclass()
}?.invokeQuietly<ViewGroup.LayoutParams>(it)
} ?: lparams
return wrapped
// Build a default.
?: lpClass.createInstanceOrNull(LayoutParamsWrapContent, LayoutParamsWrapContent)
@@ -912,12 +951,15 @@ class Hikage private constructor(private val factories: List<HikageFactory>) {
*/
fun create(): ViewGroup.LayoutParams {
if (bodyBuilder == null && wrapperBuilder == null) throw PerformerException("No layout params builder found.")
return bodyBuilder?.let {
val lparams = ViewLayoutParams(lpClass, it.width, it.height, it.matchParent, it.widthMatchParent, it.heightMatchParent)
current.requireNoPerformers(lparams.javaClass.name) { it.body(lparams) }
lparams
} ?: wrapperBuilder?.let {
val lparams = it.delegate?.create() ?: it.lparams
createDefaultLayoutParams(lparams)
} ?: throw PerformerException("Internal error of build layout params.")
}

View File

@@ -47,6 +47,7 @@ abstract class HikagePreview(context: Context, attrs: AttributeSet? = null) : Fr
@CallSuper
override fun onAttachedToWindow() {
super.onAttachedToWindow()
removeAllViews()
build().create(context, parent = this)
}

View File

@@ -24,7 +24,8 @@
package com.highcapable.hikage.widget.android
import android.widget.AbsListView
import android.view.SurfaceView
import android.webkit.WebView
import android.widget.ActionMenuView
import android.widget.AutoCompleteTextView
import android.widget.Button
@@ -101,7 +102,7 @@ private object SpaceDeclaration
@HikageViewDeclaration(CheckedTextView::class)
private object CheckedTextViewDeclaration
@HikageViewDeclaration(ExpandableListView::class, AbsListView.LayoutParams::class)
@HikageViewDeclaration(ExpandableListView::class, final = true)
private object ExpandableListViewDeclaration
@HikageViewDeclaration(Spinner::class)
@@ -143,13 +144,13 @@ private object TextSwitcherDeclaration
@HikageViewDeclaration(ActionMenuView::class, ActionMenuView.LayoutParams::class)
private object ActionMenuViewDeclaration
@HikageViewDeclaration(CalendarView::class, FrameLayout.LayoutParams::class)
@HikageViewDeclaration(CalendarView::class, final = true)
private object CalendarViewDeclaration
@HikageViewDeclaration(DatePicker::class, FrameLayout.LayoutParams::class)
@HikageViewDeclaration(DatePicker::class, final = true)
private object DatePickerDeclaration
@HikageViewDeclaration(TimePicker::class, FrameLayout.LayoutParams::class)
@HikageViewDeclaration(TimePicker::class, final = true)
private object TimePickerDeclaration
@HikageViewDeclaration(RatingBar::class)
@@ -173,19 +174,25 @@ private object ViewFlipperDeclaration
@HikageViewDeclaration(ViewAnimator::class, FrameLayout.LayoutParams::class)
private object ViewAnimatorDeclaration
@HikageViewDeclaration(SurfaceView::class)
private object SurfaceVieweclaration
@HikageViewDeclaration(VideoView::class)
private object VideoViewDeclaration
@HikageViewDeclaration(Toolbar::class, Toolbar.LayoutParams::class)
@HikageViewDeclaration(WebView::class, final = true)
private object WebViewDeclaration
@HikageViewDeclaration(Toolbar::class, final = true)
private object ToolbarDeclaration
@HikageViewDeclaration(GridLayout::class, GridLayout.LayoutParams::class)
private object GridLayoutDeclaration
@HikageViewDeclaration(GridView::class, AbsListView.LayoutParams::class)
@HikageViewDeclaration(GridView::class, final = true)
private object GridViewDeclaration
@HikageViewDeclaration(ListView::class, AbsListView.LayoutParams::class)
@HikageViewDeclaration(ListView::class, final = true)
private object ListViewDeclaration
@HikageViewDeclaration(ImageView::class)
@@ -200,10 +207,10 @@ private object TableLayoutDeclaration
@HikageViewDeclaration(TableRow::class, TableRow.LayoutParams::class)
private object TableRowDeclaration
@HikageViewDeclaration(NumberPicker::class, LinearLayout.LayoutParams::class)
@HikageViewDeclaration(NumberPicker::class, final = true)
private object NumberPickerDeclaration
@HikageViewDeclaration(SearchView::class, FrameLayout.LayoutParams::class)
@HikageViewDeclaration(SearchView::class, final = true)
private object SearchViewDeclaration
@HikageViewDeclaration(Switch::class)
@@ -212,5 +219,5 @@ private object SwitchDeclaration
@HikageViewDeclaration(TabHost::class, FrameLayout.LayoutParams::class)
private object TabHostDeclaration
@HikageViewDeclaration(TabWidget::class, LinearLayout.LayoutParams::class)
@HikageViewDeclaration(TabWidget::class, final = true)
private object TabWidgetDeclaration

View File

@@ -27,14 +27,6 @@ android {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
freeCompilerArgs = listOf(
"-Xno-param-assertions",
"-Xno-call-assertions",
"-Xno-receiver-assertions"
)
}
}
dependencies {
@@ -42,6 +34,7 @@ dependencies {
implementation(com.highcapable.kavaref.kavaref.core)
implementation(com.highcapable.kavaref.kavaref.extension)
implementation(com.highcapable.betterandroid.ui.component)
implementation(com.highcapable.betterandroid.ui.component.adapter)
implementation(com.highcapable.betterandroid.ui.extension)
implementation(com.highcapable.betterandroid.system.extension)
implementation(androidx.core.core.ktx)

View File

@@ -25,7 +25,7 @@
package com.highcapable.hikage.extension.betterandroid.ui.component.adapter
import android.view.ViewGroup
import com.highcapable.betterandroid.ui.component.adapter.CommonAdapterBuilder
import com.highcapable.betterandroid.ui.component.adapter.BaseAdapterBuilder
import com.highcapable.hikage.core.Hikage
import com.highcapable.hikage.core.base.HikagePerformer
import com.highcapable.hikage.core.base.Hikageable
@@ -51,26 +51,26 @@ import com.highcapable.hikage.extension.betterandroid.ui.component.adapter.viewh
* hikage.get<TextView>("text").text = "Item ${entity.name} of ${position + 1}"
* }
* ```
* @see CommonAdapterBuilder.onBindItemView
* @receiver [CommonAdapterBuilder]<[E]>
* @see BaseAdapterBuilder.onBindItemView
* @receiver [BaseAdapterBuilder]<[E]>
* @param Hikageable the performer body.
* @return [CommonAdapterBuilder]<[E]>
* @return [BaseAdapterBuilder]<[E]>
*/
@JvmOverloads
fun <E> CommonAdapterBuilder<E>.onBindItemView(
fun <E> BaseAdapterBuilder<E>.onBindItemView(
Hikageable: HikagePerformer<ViewGroup.LayoutParams>,
viewHolder: (hikage: Hikage, entity: E, position: Int) -> Unit = { _, _, _ -> }
) = onBindItemView(Hikageable(performer = Hikageable), viewHolder)
/**
* Create and add view holder from [Hikage.Delegate].
* @see CommonAdapterBuilder.onBindItemView
* @receiver [CommonAdapterBuilder]<[E]>
* @see BaseAdapterBuilder.onBindItemView
* @receiver [BaseAdapterBuilder]<[E]>
* @param delegate the delegate.
* @return [CommonAdapterBuilder]<[E]>
* @return [BaseAdapterBuilder]<[E]>
*/
@JvmOverloads
fun <E> CommonAdapterBuilder<E>.onBindItemView(
fun <E> BaseAdapterBuilder<E>.onBindItemView(
delegate: Hikage.Delegate<*>,
viewHolder: (hikage: Hikage, entity: E, position: Int) -> Unit = { _, _, _ -> }
) = onBindItemView(HikageHolderDelegate(delegate), viewHolder)

View File

@@ -28,14 +28,6 @@ android {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
freeCompilerArgs = listOf(
"-Xno-param-assertions",
"-Xno-call-assertions",
"-Xno-receiver-assertions"
)
}
}
dependencies {

View File

@@ -27,14 +27,6 @@ android {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
freeCompilerArgs = listOf(
"-Xno-param-assertions",
"-Xno-call-assertions",
"-Xno-receiver-assertions"
)
}
}
dependencies {

View File

@@ -30,6 +30,18 @@ import com.highcapable.hikage.core.base.HikageFactoryBuilder
import com.highcapable.hikage.core.base.HikagePerformer
import com.highcapable.hikage.core.base.Hikageable
/**
* @see ViewGroup.addView
* @see Hikageable
* @return [Hikage]
*/
@JvmName("addViewTyped")
inline fun <reified LP : ViewGroup.LayoutParams> ViewGroup.addView(
index: Int = -1,
factory: HikageFactoryBuilder.() -> Unit = {},
performer: HikagePerformer<LP>
) = Hikageable<LP>(context = context, factory = factory, performer = performer).apply { addView(root, index) }
/**
* @see ViewGroup.addView
* @see Hikageable
@@ -39,7 +51,7 @@ inline fun ViewGroup.addView(
index: Int = -1,
factory: HikageFactoryBuilder.() -> Unit = {},
performer: HikagePerformer<ViewGroup.LayoutParams>
) = Hikageable(context = context, factory = factory, performer = performer).apply { addView(root, index) }
) = addView<ViewGroup.LayoutParams>(index, factory, performer)
/**
* @see ViewGroup.addView

View File

@@ -27,14 +27,6 @@ android {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
freeCompilerArgs = listOf(
"-Xno-param-assertions",
"-Xno-call-assertions",
"-Xno-receiver-assertions"
)
}
}
dependencies {

View File

@@ -78,7 +78,7 @@ private object AppCompatTextViewDeclaration
@HikageViewDeclaration(AppCompatToggleButton::class)
private object AppCompatToggleButtonDeclaration
@HikageViewDeclaration(AppCompatToolbar::class, AppCompatToolbar.LayoutParams::class)
@HikageViewDeclaration(AppCompatToolbar::class, final = true)
private object AppCompatToolbarDeclaration
@HikageViewDeclaration(AppCompatCheckedTextView::class)
@@ -87,7 +87,7 @@ private object AppCompatCheckedTextViewDeclaration
@HikageViewDeclaration(SwitchCompat::class)
private object SwitchCompatDeclaration
@HikageViewDeclaration(AppCompatSearchView::class)
@HikageViewDeclaration(AppCompatSearchView::class, final = true)
private object AppCompatSearchViewDeclaration
@HikageViewDeclaration(LinearLayoutCompat::class, LinearLayoutCompat.LayoutParams::class)

View File

@@ -24,8 +24,36 @@
package com.highcapable.hikage.widget.androidx.constraintlayout
import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.constraintlayout.utils.widget.ImageFilterButton
import androidx.constraintlayout.utils.widget.ImageFilterView
import androidx.constraintlayout.utils.widget.MockView
import androidx.constraintlayout.utils.widget.MotionButton
import androidx.constraintlayout.utils.widget.MotionLabel
import androidx.constraintlayout.utils.widget.MotionTelltales
import androidx.constraintlayout.widget.ConstraintLayout
import com.highcapable.hikage.annotation.HikageViewDeclaration
@HikageViewDeclaration(ConstraintLayout::class, ConstraintLayout.LayoutParams::class)
private object ConstraintLayoutDeclaration
private object ConstraintLayoutDeclaration
@HikageViewDeclaration(MotionLayout::class, ConstraintLayout.LayoutParams::class)
private object MotionLayoutDeclaration
@HikageViewDeclaration(ImageFilterButton::class)
private object ImageFilterButtonDeclaration
@HikageViewDeclaration(ImageFilterView::class)
private object ImageFilterViewDeclaration
@HikageViewDeclaration(MockView::class)
private object MockViewDeclaration
@HikageViewDeclaration(MotionButton::class)
private object MotionButtonDeclaration
@HikageViewDeclaration(MotionLabel::class)
private object MotionLabelDeclaration
@HikageViewDeclaration(MotionTelltales::class)
private object MotionTelltalesDeclaration

View File

@@ -27,5 +27,5 @@ package com.highcapable.hikage.widget.androidx.recyclerview
import androidx.recyclerview.widget.RecyclerView
import com.highcapable.hikage.annotation.HikageViewDeclaration
@HikageViewDeclaration(RecyclerView::class, RecyclerView.LayoutParams::class)
@HikageViewDeclaration(RecyclerView::class, final = true)
private object RecyclerViewDeclaration

View File

@@ -28,8 +28,8 @@ import androidx.viewpager.widget.ViewPager
import androidx.viewpager2.widget.ViewPager2
import com.highcapable.hikage.annotation.HikageViewDeclaration
@HikageViewDeclaration(ViewPager::class, ViewPager.LayoutParams::class)
@HikageViewDeclaration(ViewPager::class, final = true)
private object ViewPagerDeclaration
@HikageViewDeclaration(ViewPager2::class)
@HikageViewDeclaration(ViewPager2::class, final = true)
private object ViewPager2Declaration

View File

@@ -27,14 +27,6 @@ android {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
freeCompilerArgs = listOf(
"-Xno-param-assertions",
"-Xno-call-assertions",
"-Xno-receiver-assertions"
)
}
}
dependencies {

View File

@@ -24,7 +24,6 @@
package com.highcapable.hikage.widget.google.material.appbar
import android.widget.Toolbar
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.CollapsingToolbarLayout
import com.google.android.material.appbar.MaterialToolbar
@@ -33,7 +32,7 @@ import com.highcapable.hikage.annotation.HikageViewDeclaration
@HikageViewDeclaration(AppBarLayout::class, AppBarLayout.LayoutParams::class)
private object AppBarLayoutDeclaration
@HikageViewDeclaration(MaterialToolbar::class, Toolbar.LayoutParams::class)
@HikageViewDeclaration(MaterialToolbar::class, final = true)
private object MaterialToolbarDeclaration
@HikageViewDeclaration(CollapsingToolbarLayout::class, CollapsingToolbarLayout.LayoutParams::class)

View File

@@ -24,9 +24,8 @@
package com.highcapable.hikage.widget.google.material.bottomappbar
import androidx.appcompat.widget.Toolbar
import com.google.android.material.bottomappbar.BottomAppBar
import com.highcapable.hikage.annotation.HikageViewDeclaration
@HikageViewDeclaration(BottomAppBar::class, Toolbar.LayoutParams::class)
@HikageViewDeclaration(BottomAppBar::class, final = true)
private object BottomAppBarDeclaration

View File

@@ -24,9 +24,8 @@
package com.highcapable.hikage.widget.google.material.bottomnavigation
import android.widget.FrameLayout
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.highcapable.hikage.annotation.HikageViewDeclaration
@HikageViewDeclaration(BottomNavigationView::class, FrameLayout.LayoutParams::class)
@HikageViewDeclaration(BottomNavigationView::class, final = true)
private object BottomNavigationViewDeclaration

View File

@@ -24,9 +24,8 @@
package com.highcapable.hikage.widget.google.material.navigation
import android.widget.FrameLayout
import com.google.android.material.navigation.NavigationView
import com.highcapable.hikage.annotation.HikageViewDeclaration
@HikageViewDeclaration(NavigationView::class, FrameLayout.LayoutParams::class)
@HikageViewDeclaration(NavigationView::class, final = true)
private object NavigationViewDeclaration

View File

@@ -24,9 +24,8 @@
package com.highcapable.hikage.widget.google.material.navigationrail
import android.widget.FrameLayout
import com.google.android.material.navigationrail.NavigationRailView
import com.highcapable.hikage.annotation.HikageViewDeclaration
@HikageViewDeclaration(NavigationRailView::class, FrameLayout.LayoutParams::class)
@HikageViewDeclaration(NavigationRailView::class, final = true)
private object NavigationRailViewDeclaration

View File

@@ -25,12 +25,11 @@
package com.highcapable.hikage.widget.google.material.search
import android.widget.FrameLayout
import androidx.appcompat.widget.Toolbar
import com.google.android.material.search.SearchBar
import com.google.android.material.search.SearchView
import com.highcapable.hikage.annotation.HikageViewDeclaration
@HikageViewDeclaration(SearchBar::class, Toolbar.LayoutParams::class)
@HikageViewDeclaration(SearchBar::class, final = true)
private object SearchBarDeclaration
@HikageViewDeclaration(SearchView::class, FrameLayout.LayoutParams::class)

View File

@@ -27,14 +27,6 @@ android {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
freeCompilerArgs = listOf(
"-Xno-param-assertions",
"-Xno-call-assertions",
"-Xno-receiver-assertions"
)
}
buildFeatures {
buildConfig = true
viewBinding = true

View File

@@ -28,6 +28,7 @@ class DemoApp : Application() {
override fun onCreate() {
super.onCreate()
DynamicColors.applyToActivitiesIfAvailable(this)
}
}

View File

@@ -52,9 +52,11 @@ class MainActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView {
var username = ""
var password = ""
CoordinatorLayout(
lparams = LayoutParams(matchParent = true)
) {

View File

@@ -31,6 +31,7 @@ abstract class BaseActivity : AppViewsActivity() {
@CallSuper
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
PanguTextFactory2.inject(this)
}
}

View File

@@ -8,7 +8,7 @@ pluginManagement {
}
plugins {
id("com.highcapable.sweetdependency") version "1.0.4"
id("com.highcapable.sweetproperty") version "1.0.5"
id("com.highcapable.sweetproperty") version "1.0.8"
}
sweetProperty {
rootProject { all { isEnable = false } }