This commit is contained in:
coco
2026-07-03 16:23:31 +08:00
commit 7a4fb0e6ae
1979 changed files with 101570 additions and 0 deletions
@@ -0,0 +1 @@
/build
@@ -0,0 +1,130 @@
def isBuildModule = rootProject.ext.module.isBuildModule
if (Boolean.valueOf(isBuildModule)) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-parcelize'
apply plugin: 'com.google.dagger.hilt.android'
android {
compileSdkVersion rootProject.ext.android.compileSdkVersion
buildToolsVersion rootProject.ext.android.buildToolsVersion
defaultConfig {
minSdkVersion rootProject.ext.android.minSdkVersion
targetSdkVersion rootProject.ext.android.targetSdkVersion
versionCode rootProject.ext.android.versionCode
versionName rootProject.ext.android.versionName
multiDexEnabled true
javaCompileOptions {
annotationProcessorOptions {
arguments += [
"room.schemaLocation":"$projectDir/schemas".toString(),
"room.incremental":"true",
"room.expandProjection":"true",
AROUTER_MODULE_NAME: project.getName()
]
}
}
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
composeOptions {
kotlinCompilerExtensionVersion compose_version
kotlinCompilerVersion kotlin_version
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
useIR = true
}
buildFeatures {
viewBinding true
}
buildFeatures {
compose true
}
sourceSets {
main {
if (Boolean.valueOf(isBuildModule)) {
manifest.srcFile 'src/main/module/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
java {
//排除java/debug文件夹下的所有文件
exclude '*module'
}
}
}
}
}
kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
generateStubs = true
useBuildCache = true
javacOptions {
option("-Xmaxerrs", 500)
}
}
dependencies {
implementation project(":common:common-base")
implementation project(":common:common-service")
// compose
implementation "io.coil-kt:coil-compose:1.3.1"
implementation "androidx.compose.material:material:$compose_version"
implementation 'androidx.activity:activity-compose:1.3.1'
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.ui:ui-tooling:$compose_version"
implementation "androidx.compose.runtime:runtime:$compose_version"
implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:1.0.0-alpha07"
implementation 'androidx.navigation:navigation-compose:2.4.0-alpha06'
kapt rootProject.ext.compiler["arouterCompiler"]
compileOnly(rootProject.ext.jetpack["hilt"])
kapt rootProject.ext.compiler["hiltAndroidCompiler"]
}
// 发布module到maven仓库
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.17.0'
}
}
allprojects {
plugins.withId("com.vanniktech.maven.publish") {
mavenPublish {
sonatypeHost = "S01"
}
}
}
apply plugin: "com.vanniktech.maven.publish"
@@ -0,0 +1,2 @@
-keep class com.bbgo.module_compose.bean.** {*;}
@@ -0,0 +1,23 @@
# 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
-keep class com.bbgo.module_compose.bean.** {*;}
@@ -0,0 +1,24 @@
package com.bbgo.module_compose
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.bbgo.module_compose.test", appContext.packageName)
}
}
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bbgo.module_compose">
<application>
<activity android:name=".activity.ComposeActivity"/>
</application>
</manifest>
@@ -0,0 +1,13 @@
package com.bbgo.module_compose
import com.bbgo.common_base.BaseApplication
import dagger.hilt.android.HiltAndroidApp
/**
* @Description:
* @Author: wangyuebin
* @Date: 2021/9/10 5:12 下午
*/
//@HiltAndroidApp
class ComposeApp : BaseApplication() {
}
@@ -0,0 +1,394 @@
package com.bbgo.module_compose.activity
import android.app.Activity
import android.os.Bundle
import android.text.Html
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.alibaba.android.arouter.facade.annotation.Route
import com.bbgo.common_base.constants.RouterPath
import com.bbgo.module_compose.R
import com.bbgo.module_compose.bean.ArticleDetail
import com.bbgo.module_compose.theme.*
import com.bbgo.module_compose.util.InjectorUtil
import com.bbgo.module_compose.viewmodel.ComposeViewModel
@Route(path = RouterPath.Compose.PAGE_COMPOSE)
class ComposeActivity : AppCompatActivity() {
@ExperimentalFoundationApi
val composeViewModel: ComposeViewModel by viewModels { InjectorUtil.getComposeViewModelFactory() }
@ExperimentalFoundationApi
lateinit var context: Activity
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@OptIn(ExperimentalFoundationApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
context = this
composeViewModel.getArticles(0)
setContent {
WanAndroidTheme {
RenderTopAppBar(composeViewModel)
}
}
}
}
@ExperimentalFoundationApi
@Composable
fun RenderTopAppBar(composeViewModel: ComposeViewModel) {
Scaffold(
topBar = {
TopAppBar(
title = {
Text(
text = stringResource(id = R.string.nav_my_collect),
color = Color.White,
fontSize = 18.sp
)
},
navigationIcon = {
IconButton(onClick = {
}) {
Icon(Icons.Filled.ArrowBack, null, tint = Color.White)
}
},
backgroundColor = colorResource(id = R.color.colorPrimary)
)
},
){
Request(composeViewModel)
}
}
@ExperimentalFoundationApi
@Composable
fun Request(composeViewModel: ComposeViewModel) {
val liveData by composeViewModel.articleLiveData.observeAsState()
Logs.d("======================")
liveData?.data?.let {
RenderArticleList(it)
}
}
@ExperimentalFoundationApi
@Composable
fun RenderArticleList(articles: List<ArticleDetail>) {
LazyColumn {
items(articles.size) {
articles.forEach {
ItemCard(articleDetail = it)
}
}
}
/*LazyColumn {
items(articles) { articleDetail ->
ItemCard(articleDetail = articleDetail)
}
}*/
}
@ExperimentalFoundationApi
@Composable
fun ItemCard(articleDetail: ArticleDetail) {
val dp2 = dimensionResource(id = R.dimen.dp_2)
val dp5 = dimensionResource(id = R.dimen.dp_5)
val dp10 = dimensionResource(id = R.dimen.dp_10)
Surface(elevation = dimensionResource(id = R.dimen.dp_1),
) {
Column() {
Spacer(modifier = Modifier.padding(vertical = dp5))
Row {
Row(modifier = Modifier.weight(1f)) {
if (articleDetail.top == "1") {
Spacer(modifier = Modifier.padding(horizontal = dp5))
Text(
text = "置顶",
color = Color.Red,
fontSize = sp10,
modifier = Modifier
.border(dp05, Color.Red, shape = RoundedCornerShape(dp2))
.padding(start = dp4, end = dp4, top = dp2, bottom = dp2)
)
}
if (articleDetail.fresh) {
Spacer(modifier = Modifier.padding(horizontal = dp5))
Text(
text = "",
color = Color.Red,
fontSize = sp10,
modifier = Modifier
.border(dp05, Color.Red, shape = RoundedCornerShape(dp2))
.padding(start = dp4, end = dp4, top = dp2, bottom = dp2)
)
}
if (articleDetail.tags.isNotEmpty()) {
Spacer(modifier = Modifier.padding(horizontal = dp5))
Text(
text = articleDetail.tags[0].name,
color = Color.Red,
fontSize = sp12,
modifier = Modifier
.border(dp05, Color.Red, shape = RoundedCornerShape(dp2))
.padding(start = dp4, end = dp4, top = dp2, bottom = dp2)
)
}
Spacer(modifier = Modifier.padding(horizontal = dp5))
Text(
text = articleDetail.author,
color = colorResource(id = R.color.Grey600),
fontSize = sp12,
modifier = Modifier
.padding(start = dp4, end = dp4, top = dp2, bottom = dp2)
)
}
Row(modifier = Modifier.padding(end = dp5)) {
Text(
text = articleDetail.niceDate,
color = colorResource(id = R.color.Grey600),
fontSize = sp12,
modifier = Modifier
.padding(start = dp4, end = dp4, top = dp2, bottom = dp2)
)
}
}
Spacer(modifier = Modifier.padding(vertical = dp2))
Row(horizontalArrangement = Arrangement.Start) {
Spacer(modifier = Modifier.padding(horizontal = dp5))
Text(
text = Html.fromHtml(articleDetail.title).toString(),
color = colorResource(id = R.color.item_title),
fontSize = sp16,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.padding(end = dp5),
textAlign = TextAlign.Start
)
}
Spacer(modifier = Modifier.padding(vertical = dp5))
val chapterName = when {
articleDetail.superChapterName.isNotEmpty() and articleDetail.chapterName.isNotEmpty() ->
"${articleDetail.superChapterName} / ${articleDetail.chapterName}"
articleDetail.superChapterName.isNotEmpty() -> articleDetail.superChapterName
articleDetail.chapterName.isNotEmpty() -> articleDetail.chapterName
else -> ""
}
Row(verticalAlignment = Alignment.Bottom) {
Text(
text = chapterName,
color = colorResource(id = R.color.item_title),
fontSize = sp12,
textAlign = TextAlign.Start,
modifier = Modifier
.weight(1f)
.padding(start = dp10)
)
val imgId = if (articleDetail.collect) {
R.drawable.ic_like
} else {
R.drawable.ic_like_not
}
Image(
painter = painterResource(id = imgId),
contentDescription = null,
alignment = Alignment.CenterEnd,
modifier = Modifier
.weight(0.1f)
.size(26.dp)
.padding(end = dp5)
.clickable {
}
)
}
Spacer(modifier = Modifier.padding(vertical = dp5))
Divider(thickness = 0.2.dp)
}
}
}
@Composable
fun TestItemCard() {
val dp2 = dimensionResource(id = R.dimen.dp_2)
val dp5 = dimensionResource(id = R.dimen.dp_5)
Surface(shape = MaterialTheme.shapes.medium,
elevation = dimensionResource(id = R.dimen.dp_1),
) {
Column() {
Spacer(modifier = Modifier.padding(vertical = dp2))
Row {
Row(modifier = Modifier.weight(1f)) {
Spacer(modifier = Modifier.padding(horizontal = dp5))
Text(
text = "置顶",
color = Color.Red,
fontSize = sp10,
modifier = Modifier
.border(dp05, Color.Red, shape = RectangleShape)
.padding(start = dp4, end = dp4, top = dp2, bottom = dp2)
)
Spacer(modifier = Modifier.padding(horizontal = dp5))
Text(
text = "",
color = Color.Red,
fontSize = sp10,
modifier = Modifier
.border(dp05, Color.Red, shape = RectangleShape)
.padding(start = dp4, end = dp4, top = dp2, bottom = dp2)
)
Spacer(modifier = Modifier.padding(horizontal = dp5))
Text(
text = "玩安卓TAG",
color = Color.Red,
fontSize = sp10,
modifier = Modifier
.border(dp05, Color.Red, shape = RectangleShape)
.padding(start = dp4, end = dp4, top = dp2, bottom = dp2)
)
Spacer(modifier = Modifier.padding(horizontal = dp5))
Text(
text = "玩安卓",
color = colorResource(id = R.color.Grey600),
fontSize = sp12,
modifier = Modifier
.padding(start = dp4, end = dp4, top = dp2, bottom = dp2)
)
}
Row {
Spacer(modifier = Modifier.padding(horizontal = dp20))
Text(
text = "玩安卓",
color = colorResource(id = R.color.Grey600),
fontSize = sp12,
modifier = Modifier
.padding(start = dp4, end = dp4, top = dp2, bottom = dp2)
)
}
}
Spacer(modifier = Modifier.padding(vertical = dp2))
Row(horizontalArrangement = Arrangement.Start) {
Spacer(modifier = Modifier.padding(horizontal = dp5))
Text(
text = "Compose 有效地处理嵌套布局,使其成为设计复杂UI的好方法。这是对 Android Views 的改进,在 Android Views 中,出于性能原因,您需要避免嵌套布局。你好呀陌生人,这是一个标题,不是很长,因为我想不出其他什么比较好的标题了",
color = colorResource(id = R.color.item_title),
fontSize = sp16,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.padding(end = dp5),
textAlign = TextAlign.Start
)
}
Spacer(modifier = Modifier.padding(vertical = dp2))
/*Scaffold(
topBar = {
TopAppBar(
title = {
Text(
text = "问答 / 官方",
color = colorResource(id = R.color.item_title),
fontSize = sp8,
textAlign = TextAlign.Center,
)
},
actions = {
Image(
painter = painterResource(id = R.drawable.ic_like_not),
contentDescription = null,
modifier = Modifier.padding(end = 10.dp)
)
},
backgroundColor = Color.White
)
},
) {}*/
Row {
Text(
text = "问答 / 官方",
color = colorResource(id = R.color.item_title),
fontSize = sp12,
textAlign = TextAlign.Start,
modifier = Modifier
.weight(1f)
.padding(start = dp10)
)
Image(
painter = painterResource(id = R.drawable.ic_like_not),
contentDescription = null,
alignment = Alignment.CenterEnd,
modifier = Modifier
.weight(0.1f)
.size(20.dp)
.padding(end = dp5)
.clickable {
}
)
}
}
}
}
@Preview
@Composable
fun DefaultPreview() {
// ArticleList(articleList = mutableListOf())
TestItemCard()
}
@@ -0,0 +1,58 @@
package com.bbgo.module_compose.bean
import androidx.annotation.Keep
@Keep
data class ArticleData(
val curPage: Int,
val datas: MutableList<ArticleDetail>,
val offset: Int,
val over: Boolean,
val pageCount: Int,
val size: Int,
val total: Int
)
@Keep
data class ArticleDetail(
val apkLink: String,
val audit: Int,
val author: String,
val canEdit: Boolean,
val chapterId: Int,
val chapterName: String,
var collect: Boolean,
val courseId: Int,
val desc: String,
val descMd: String,
val envelopePic: String,
val fresh: Boolean,
val host: String,
val id: Int,
val link: String,
val niceDate: String,
val niceShareDate: String,
val origin: String,
val prefix: String,
val projectLink: String,
val publishTime: Long,
val realSuperChapterId: Int,
val selfVisible: Int,
val shareDate: Long,
val shareUser: String,
val superChapterId: Int,
val superChapterName: String,
val tags: List<Tag>,
val title: String,
val type: Int,
val userId: Int,
val visible: Int,
val zan: Int,
var top: String,
)
@Keep
data class Tag(
val name: String,
val url: String
)
@@ -0,0 +1,32 @@
package com.bbgo.module_compose.net.api
import com.bbgo.common_base.bean.HttpResult
import com.bbgo.module_compose.bean.ArticleData
import com.bbgo.module_compose.bean.ArticleDetail
import kotlinx.coroutines.flow.Flow
import retrofit2.http.GET
import retrofit2.http.Path
/**
* author: wangyb
* date: 4/7/21 9:24 PM
* description: http api
*/
interface HttpComposeApiService {
/**
* 获取首页置顶文章列表
* http://www.wanandroid.com/article/top/json
*/
@GET("article/top/json")
fun getTopArticles(): Flow<HttpResult<List<ArticleDetail>>>
/**
* 获取文章列表
* http://www.wanandroid.com/article/list/0/json
* @param pageNum
*/
@GET("article/list/{pageNum}/json")
fun getArticles(@Path("pageNum") pageNum: Int): Flow<HttpResult<ArticleData>>
}
@@ -0,0 +1,25 @@
package com.bbgo.module_compose.repository
/**
* author: wangyb
* date: 3/30/21 2:36 PM
* description: todo
*/
class ComposeLocalRepository private constructor(){
companion object {
private var repository: ComposeLocalRepository? = null
fun getInstance(): ComposeLocalRepository {
if (repository == null) {
synchronized(ComposeLocalRepository::class.java) {
if (repository == null) {
repository = ComposeLocalRepository()
}
}
}
return repository!!
}
}
}
@@ -0,0 +1,37 @@
package com.bbgo.module_compose.repository
import com.bbgo.common_base.bean.HttpResult
import com.bbgo.common_base.net.ServiceCreators
import com.bbgo.module_compose.bean.ArticleData
import com.bbgo.module_compose.bean.ArticleDetail
import com.bbgo.module_compose.net.api.HttpComposeApiService
import kotlinx.coroutines.flow.Flow
/**
* author: wangyb
* date: 3/30/21 2:35 PM
* description: todo
*/
class ComposeRemoteRepository private constructor(){
private val service = ServiceCreators.create(HttpComposeApiService::class.java)
fun getTopArticles() : Flow<HttpResult<List<ArticleDetail>>> {
return service.getTopArticles()
}
fun getArticles(pageNum: Int) : Flow<HttpResult<ArticleData>> {
return service.getArticles(pageNum)
}
/********************静态内部类单例***************************/
companion object {
val instance = SingleTonHolder.holder
}
private object SingleTonHolder {
val holder = ComposeRemoteRepository()
}
}
@@ -0,0 +1,38 @@
package com.bbgo.module_compose.repository
import com.bbgo.common_base.bean.HttpResult
import com.bbgo.module_compose.bean.ArticleData
import com.bbgo.module_compose.bean.ArticleDetail
import kotlinx.coroutines.flow.Flow
/**
* author: wangyb
* date: 3/29/21 9:32 PM
* description: todo
*/
class ComposeRepository private constructor(private val remoteRepository: ComposeRemoteRepository, private val localRepository: ComposeLocalRepository) {
companion object {
@Volatile
private var repository: ComposeRepository? = null
fun getInstance(remoteRepository: ComposeRemoteRepository, localRepository: ComposeLocalRepository): ComposeRepository {
if (repository == null) {
synchronized(ComposeRepository::class.java) {
if (repository == null) {
repository = ComposeRepository(remoteRepository, localRepository)
}
}
}
return repository!!
}
}
fun getTopArticles() : Flow<HttpResult<List<ArticleDetail>>> {
return remoteRepository.getTopArticles()
}
fun getArticles(pageNum: Int) : Flow<HttpResult<ArticleData>> {
return remoteRepository.getArticles(pageNum)
}
}
@@ -0,0 +1,8 @@
package com.bbgo.module_compose.theme
import androidx.compose.ui.graphics.Color
val Purple200 = Color(0xFFBB86FC)
val Purple500 = Color(0xFF6200EE)
val Purple700 = Color(0xFF3700B3)
val Teal200 = Color(0xFF03DAC5)
@@ -0,0 +1,17 @@
package com.bbgo.module_compose.theme
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
val dp05 = 0.5.dp
val dp2 = 2.dp
val dp5 = 5.dp
val dp4 = 4.dp
val dp8 = 8.dp
val dp10 = 10.dp
val dp20 = 20.dp
val sp8 = 8.sp
val sp10 = 10.sp
val sp12 = 12.sp
val sp16 = 16.sp
@@ -0,0 +1,11 @@
package com.bbgo.module_compose.theme
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Shapes
import androidx.compose.ui.unit.dp
val Shapes = Shapes(
small = RoundedCornerShape(4.dp),
medium = RoundedCornerShape(4.dp),
large = RoundedCornerShape(0.dp)
)
@@ -0,0 +1,44 @@
package com.bbgo.module_compose.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material.MaterialTheme
import androidx.compose.material.darkColors
import androidx.compose.material.lightColors
import androidx.compose.runtime.Composable
private val DarkColorPalette = darkColors(
primary = Purple200,
primaryVariant = Purple700,
secondary = Teal200
)
private val LightColorPalette = lightColors(
primary = Purple500,
primaryVariant = Purple700,
secondary = Teal200
/* Other default colors to override
background = Color.White,
surface = Color.White,
onPrimary = Color.White,
onSecondary = Color.Black,
onBackground = Color.Black,
onSurface = Color.Black,
*/
)
@Composable
fun WanAndroidTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable() () -> Unit) {
val colors = if (darkTheme) {
DarkColorPalette
} else {
LightColorPalette
}
MaterialTheme(
colors = colors,
typography = Typography,
shapes = Shapes,
content = content
)
}
@@ -0,0 +1,28 @@
package com.bbgo.module_compose.theme
import androidx.compose.material.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with
val Typography = Typography(
body1 = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp
)
/* Other default text styles to override
button = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.W500,
fontSize = 14.sp
),
caption = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 12.sp
)
*/
)
@@ -0,0 +1,20 @@
package com.bbgo.module_compose.util
import com.bbgo.module_compose.repository.ComposeLocalRepository
import com.bbgo.module_compose.repository.ComposeRemoteRepository
import com.bbgo.module_compose.repository.ComposeRepository
import com.bbgo.module_compose.viewmodel.ComposeViewModelFactory
/**
* author: wangyb
* date: 3/29/21 9:44 PM
* description: todo
*/
object InjectorUtil {
fun getComposeViewModelFactory() = ComposeViewModelFactory(
ComposeRepository.getInstance(
ComposeRemoteRepository.instance, ComposeLocalRepository.getInstance()))
}
@@ -0,0 +1,51 @@
package com.bbgo.module_compose.viewmodel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.bbgo.common_base.ext.Resource
import com.bbgo.common_base.ext.logE
import com.bbgo.module_compose.bean.ArticleDetail
import com.bbgo.module_compose.repository.ComposeRepository
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.zip
import kotlinx.coroutines.launch
/**
* author: wangyb
* date: 3/29/21 9:31 PM
* description: todo
*/
class ComposeViewModel(private val repository: ComposeRepository) : ViewModel() {
val articleLiveData = MutableLiveData<Resource<MutableList<ArticleDetail>>>()
fun getArticles(pageNum: Int) {
viewModelScope.launch {
repository.getTopArticles()
.zip(repository.getArticles(pageNum)) { topArticles, articles ->
{
val allArticles = mutableListOf<ArticleDetail>()
topArticles.data.forEach {
it.top = "1"
}
allArticles.addAll(topArticles.data)
allArticles.addAll(articles.data.datas)
allArticles
}
}
.catch {
Logs.e(TAG, it.message, it)
}
.collectLatest {
articleLiveData.value = Resource.Success(it.invoke())
}
}
}
companion object {
private const val TAG = "ComposeViewModel"
}
}
@@ -0,0 +1,19 @@
package com.bbgo.module_compose.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.bbgo.module_compose.repository.ComposeRepository
/**
* author: wangyb
* date: 3/29/21 9:47 PM
* description: todo
*/
class ComposeViewModelFactory(private val repository: ComposeRepository) : ViewModelProvider.NewInstanceFactory() {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return ComposeViewModel(repository) as T
}
}
@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bbgo.module_compose">
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <!-- 配置权限,用来记录应用配置信息 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 重力加速度传感器权限 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- Android8.0安装apk需要请求安装权限 -->
<uses-permission android:name="android.permission.VIBRATE" /> <!-- 小米push -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.REORDER_TASKS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.hardware.sensor.accelerometer" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<application
android:name=".ComposeApp"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true">
<activity
android:name=".activity.ComposeActivity"
android:theme="@style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
@@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
@@ -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/list_divider" />
</shape>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke android:color="@color/Red"
android:width="@dimen/dp_05"/>
<corners android:radius="@dimen/dp_2" />
</shape>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:width="@dimen/dp_05"
android:color="@color/colorAccent" />
<corners android:radius="@dimen/dp_2" />
</shape>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/colorAccent"/>
<corners android:radius="@dimen/dp_2" />
</shape>
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape>
<solid android:color="#A6000000" />
<padding android:left="8dp" android:top="4dp" android:right="8dp" android:bottom="4dp" />
<corners android:radius="2dp" />
</shape>
</item>
<item>
<shape>
<solid android:color="#99000000" />
<padding android:left="8dp" android:top="4dp" android:right="8dp" android:bottom="4dp" />
<corners android:radius="2dp" />
</shape>
</item>
</selector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#ffe5acbf"
android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z"/>
</vector>
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:strokeColor="#ffA8A8A8"
android:strokeWidth="1.0"
android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z"/>
</vector>
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/viewBackground">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
style="@style/RecyclerViewStyle"
tools:listitem="@layout/item_home_list" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.appbar.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/app_bar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:theme="@style/AppTheme.AppBarOverlay"
app:elevation="0dp">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/AppTheme.PopupOverlay"
app:titleTextAppearance="@style/Toolbar.TitleText">
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:singleLine="true"
android:textColor="@color/viewBackground"
android:textSize="@dimen/sp_18" />
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<cn.bingoogolapple.bgabanner.BGABanner
android:id="@+id/banner"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_230"
android:scaleType="fitXY"
app:banner_indicatorGravity="bottom|right"
app:banner_placeholderDrawable="@drawable/placeholder_banner"
app:banner_tipTextColor="@color/color_title_bg"
app:banner_tipTextSize="@dimen/textSize_titleSmall"
app:banner_transitionEffect="alpha" />
</LinearLayout>
@@ -0,0 +1,152 @@
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/cardView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
app:cardBackgroundColor="@color/viewBackground"
app:cardCornerRadius="@dimen/dp_1"
app:cardElevation="@dimen/dp_1">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/item_content_padding"
android:paddingRight="@dimen/item_content_padding"
android:paddingBottom="@dimen/item_content_padding">
<TextView
android:id="@+id/tv_article_top"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dp_10"
android:background="@drawable/bg_fresh"
android:paddingLeft="@dimen/dp_4"
android:paddingTop="@dimen/dp_2"
android:paddingRight="@dimen/dp_4"
android:paddingBottom="@dimen/dp_2"
android:text="@string/top_tip"
android:textColor="@color/Red"
android:textSize="@dimen/sp_10"
android:visibility="gone"
tools:visibility="visible" />
<TextView
android:id="@+id/tv_article_fresh"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dp_10"
android:layout_toRightOf="@+id/tv_article_top"
android:background="@drawable/bg_fresh"
android:paddingLeft="@dimen/dp_4"
android:paddingTop="@dimen/dp_2"
android:paddingRight="@dimen/dp_4"
android:paddingBottom="@dimen/dp_2"
android:text="@string/new_fresh"
android:textColor="@color/Red"
android:textSize="@dimen/sp_10"
android:visibility="gone"
tools:visibility="visible" />
<TextView
android:id="@+id/tv_article_tag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dp_10"
android:layout_toRightOf="@+id/tv_article_fresh"
android:background="@drawable/bg_tag"
android:paddingLeft="@dimen/dp_4"
android:paddingTop="@dimen/dp_2"
android:paddingRight="@dimen/dp_4"
android:paddingBottom="@dimen/dp_2"
android:textColor="@color/colorAccent"
android:textSize="@dimen/sp_10"
android:visibility="gone"
tools:text="@string/app_name"
tools:visibility="visible" />
<TextView
android:id="@+id/tv_article_author"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/tv_article_top"
android:layout_marginLeft="@dimen/dp_10"
android:layout_toRightOf="@+id/tv_article_tag"
android:textColor="@color/item_author"
android:textSize="@dimen/item_tv_author"
tools:text="@string/app_name" />
<TextView
android:id="@+id/tv_article_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/tv_article_top"
android:layout_alignParentRight="true"
android:textColor="@color/item_date"
android:textSize="@dimen/item_tv_date"
tools:text="@string/app_name" />
<ImageView
android:id="@+id/iv_article_thumbnail"
android:layout_width="@dimen/item_img_width"
android:layout_height="@dimen/item_img_height"
android:layout_below="@+id/tv_article_author"
android:layout_marginLeft="@dimen/dp_10"
android:layout_marginTop="@dimen/dp_8"
android:scaleType="centerCrop" />
<TextView
android:id="@+id/tv_article_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_article_author"
android:layout_marginLeft="@dimen/dp_10"
android:layout_marginTop="@dimen/dp_8"
android:layout_toRightOf="@+id/iv_article_thumbnail"
android:ellipsize="end"
android:gravity="top|start"
android:lineSpacingExtra="2dp"
android:maxLines="2"
android:paddingBottom="@dimen/dp_6"
android:textColor="@color/item_title"
android:textSize="@dimen/item_tv_title" />
<TextView
android:id="@+id/tv_article_chapterName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_article_title"
android:layout_alignParentBottom="true"
android:layout_marginStart="@dimen/dp_10"
android:layout_marginLeft="@dimen/dp_10"
android:layout_marginTop="@dimen/dp_10"
android:layout_marginEnd="@dimen/dp_10"
android:layout_marginRight="@dimen/dp_10"
android:layout_toRightOf="@+id/iv_article_thumbnail"
android:gravity="center"
android:textColor="@color/item_chapter"
android:textSize="@dimen/item_tv_tag"
tools:text="@string/app_name" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:orientation="horizontal">
<ImageView
android:id="@+id/iv_like"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_like_not" />
</LinearLayout>
</RelativeLayout>
</androidx.cardview.widget.CardView>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/colorPrimary"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1,8 @@
<resources>
<string name="app_name">ComposeUI模块</string>
<string name="nav_my_collect">我的收藏</string>
<string name="new_fresh"></string>
<string name="top_tip">置顶</string>
</resources>
@@ -0,0 +1,146 @@
<resources>
<!-- Base application theme. -->
<style name="BaseAppTheme" parent="@style/Theme.AppCompat.DayNight">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="textColorPrimary">@color/textColorPrimary</item>
<item name="viewBackground">@color/viewBackground</item>
<!--关闭启动窗口-->
<item name="android:windowDisablePreview">true</item>
<item name="android:listDivider">@drawable/bg_divider</item>
</style>
<style name="AppTheme" parent="BaseAppTheme.NoActionBar"></style>
<style name="AppTheme.FullScreen" parent="@style/Theme.AppCompat.DayNight">
<item name="windowNoTitle">true</item>
<item name="windowActionBar">false</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowBackground">@null</item>
<item name="android:windowDisablePreview">true</item>
</style>
<style name="SplashTheme" parent="BaseAppTheme.NoActionBar">
<item name="android:windowFullscreen">true</item>
<item name="android:windowIsTranslucent">true</item>
</style>
<style name="BaseAppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="BaseAppTheme.NoActionBar.Slidable" parent="BaseAppTheme.NoActionBar">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
<style name="MyTabLayoutStyle" parent="Base.Widget.Design.TabLayout">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:background">@color/colorPrimary</item>
<item name="tabIndicatorColor">@color/viewBackground</item>
<item name="tabIndicatorHeight">@dimen/dp_1</item>
<item name="tabSelectedTextColor">@color/viewBackground</item>
<item name="tabTextAppearance">@style/MyTabTextAppearance</item>
</style>
<style name="MyTabTextAppearance">
<item name="android:textSize">@dimen/sp_14</item>
<item name="textAllCaps">false</item>
<item name="android:textColor">?android:textColorSecondary</item>
</style>
<style name="WindowAnimationFadeInOut">
<item name="android:windowEnterAnimation">@anim/fade_in</item>
<item name="android:windowExitAnimation">@anim/fade_out</item>
</style>
<style name="ToolbarStyle" parent="AppTheme">
<item name="android:textColorSecondary">@android:color/white</item>
<item name="searchHintIcon">@null</item>
</style>
<!-- 设置Toolbar标题字体的大小 -->
<style name="Toolbar.TitleText" parent="TextAppearance.Widget.AppCompat.Toolbar.Title">
<item name="android:textSize">@dimen/sp_18</item>
</style>
<!-- 浮动窗口动画 -->
<style name="anim_float_view">
<item name="android:windowEnterAnimation">@anim/push_up_in</item>
<item name="android:windowExitAnimation">@anim/push_up_out</item>
</style>
<style name="MenuLabelsStyle">
<item name="android:background">@drawable/fab_label_background</item>
<item name="android:textColor">#FFFFFF</item>
<item name="android:textSize">@dimen/sp_14</item>
<item name="android:maxLines">2</item>
<item name="android:ellipsize">end</item>
</style>
<style name="RecyclerViewStyle">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">match_parent</item>
<item name="android:scrollbarSize">@dimen/dp_2</item>
<item name="android:scrollbars">vertical</item>
</style>
<style name="TabLayout" parent="Base.Widget.Design.TabLayout">
<item name="tabIndicatorColor">@color/colorPrimary</item>
<item name="tabIndicatorHeight">2dp</item>
<item name="tabPaddingStart">12dp</item>
<item name="tabPaddingEnd">12dp</item>
<item name="tabBackground">@color/colorPrimary</item>
<item name="tabTextAppearance">@style/TextAppearance.Design.Tab</item>
<item name="tabSelectedTextColor">@color/colorPrimary</item>
<item name="tabTextColor">@color/colorPrimary</item>
</style>
<declare-styleable name="CircleProgressView">
<attr name="cpv_progressNormalColor" format="color"/>
<attr name="cpv_progressReachColor" format="color"/>
<attr name="cpv_progressTextColor" format="color"/>
<attr name="cpv_progressTextSize" format="dimension"/>
<attr name="cpv_progressTextOffset" format="dimension"/>
<attr name="cpv_progressNormalSize" format="dimension"/>
<attr name="cpv_progressReachSize" format="dimension"/>
<attr name="cpv_radius" format="dimension"/>
<attr name="cpv_progressTextVisible" format="boolean"/>
<attr name="cpv_progressStartArc" format="integer"/>
<attr name="cpv_progressTextSkewX" format="dimension"/>
<attr name="cpv_progressTextPrefix" format="string"/>
<attr name="cpv_progressTextSuffix" format="string"/>
<attr name="cpv_innerBackgroundColor" format="color"/>
<attr name="cpv_progressStyle" format="enum">
<enum name="Normal" value="0"/>
<enum name="FillInner" value="1"/>
<enum name="FillInnerArc" value="2"/>
</attr>
<attr name="cpv_innerProgressColor" format="color"/>
<attr name="cpv_innerPadding" format="dimension"/>
<attr name="cpv_outerColor" format="color"/>
<attr name="cpv_outerSize" format="dimension"/>
<attr name="cpv_reachCapRound" format="boolean"/>
</declare-styleable>
<declare-styleable name="CircleImageView">
<attr name="type" format="enum">
<enum name="none" value="0" />
<enum name="circle" value="1" />
<enum name="rect_rounded" value="2" />
</attr>
<attr name="borderColor" format="color" />
<attr name="borderWidth" format="dimension" />
<attr name="rectRoundRadius" format="dimension" />
</declare-styleable>
</resources>
@@ -0,0 +1,17 @@
package com.bbgo.module_compose
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}