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,89 @@
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'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
buildFeatures {
viewBinding true
}
}
kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
generateStubs = true
useBuildCache = true
javacOptions {
option("-Xmaxerrs", 500)
}
}
dependencies {
compileOnly(project(":common:common-base"))
compileOnly(project(":common:common-service"))
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_collect.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_collect.bean.** {*;}
@@ -0,0 +1,24 @@
package com.bbgo.module_collect
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_collect.test", appContext.packageName)
}
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bbgo.module_collect">
</manifest>
@@ -0,0 +1,11 @@
package com.bbgo.module_collect.bean
import androidx.annotation.Keep
/**
* author: wangyb
* date: 2021/5/27 7:10 下午
* description: todo
*/
@Keep
data class CollectBean(val data: Any, var positon: Int, var type: String)
@@ -0,0 +1,33 @@
package com.bbgo.module_collect.net.api
import com.bbgo.common_base.bean.HttpResult
import com.bbgo.module_collect.bean.CollectBean
import kotlinx.coroutines.flow.Flow
import retrofit2.http.POST
import retrofit2.http.Path
/**
* author: wangyb
* date: 4/7/21 9:24 PM
* description: http api
*/
interface HttpApiService {
/**
* 收藏站内文章
* http://www.wanandroid.com/lg/collect/1165/json
* @param id article id
*/
@POST("lg/collect/{id}/json")
fun addCollectArticle(@Path("id") id: Int): Flow<HttpResult<CollectBean>>
/**
* 文章列表中取消收藏文章
* http://www.wanandroid.com/lg/uncollect_originId/2333/json
* @param id
*/
@POST("lg/uncollect_originId/{id}/json")
fun cancelCollectArticle(@Path("id") id: Int): Flow<HttpResult<CollectBean>>
}
@@ -0,0 +1,41 @@
package com.bbgo.module_collect.repository
import com.bbgo.common_base.bean.HttpResult
import com.bbgo.common_base.net.ServiceCreators
import com.bbgo.module_collect.bean.CollectBean
import com.bbgo.module_collect.net.api.HttpApiService
import kotlinx.coroutines.flow.Flow
/**
* author: wangyb
* date: 3/29/21 9:32 PM
* description: todo
*/
class CollectRepository private constructor() {
companion object {
@Volatile
private var repository: CollectRepository? = null
fun getInstance(): CollectRepository {
if (repository == null) {
synchronized(CollectRepository::class.java) {
if (repository == null) {
repository = CollectRepository()
}
}
}
return repository!!
}
}
private val service = ServiceCreators.create(HttpApiService::class.java)
fun collectArticle(id: Int) : Flow<HttpResult<CollectBean>> {
return service.addCollectArticle(id)
}
fun unCollectArticle(id: Int) : Flow<HttpResult<CollectBean>> {
return service.cancelCollectArticle(id)
}
}
@@ -0,0 +1,43 @@
package com.bbgo.module_collect.service
import android.content.Context
import com.alibaba.android.arouter.facade.annotation.Route
import com.bbgo.common_base.constants.RouterPath
import com.bbgo.common_service.collect.CollectService
import com.bbgo.module_collect.repository.CollectRepository
import com.bbgo.module_collect.viewmodel.CollectViewModel
/**
* author: wangyb
* date: 2021/5/27 7:31 下午
* description: todo
*/
@Route(path = RouterPath.Collect.SERVICE_COLLECT)
class CollectServiceImpl : CollectService{
private val viewModel = CollectViewModel(CollectRepository.getInstance())
override fun collect(indexPage: Int, position: Int,pageId: Int) {
viewModel.collectArticle(indexPage, position, pageId)
}
override fun unCollect(indexPage: Int, position: Int,pageId: Int) {
viewModel.unCollectArticle(indexPage, position, pageId)
// viewModelScope.launch {
// CollectRepository.getInstance().unCollectArticle(pageId)
// .catch {
//
// }
// .collectLatest {
// if (it.errorCode != 0) {
// context?.showToast("取消收藏失败")
// return@collectLatest
// }
// context?.getString(R.string.cancel_collect_success)?.let { context?.showToast(it) }
// }
// }
}
override fun init(context: Context?) {
}
}
@@ -0,0 +1,58 @@
package com.bbgo.module_collect.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.bbgo.common_base.bus.BusKey
import com.bbgo.common_base.bus.LiveDataBus
import com.bbgo.common_base.constants.Constants.CollectType.COLLECT
import com.bbgo.common_base.constants.Constants.CollectType.UNCOLLECT
import com.bbgo.common_base.constants.Constants.CollectType.UNKNOWN
import com.bbgo.common_base.event.MessageEvent
import com.bbgo.common_base.ext.USER_NOT_LOGIN
import com.bbgo.common_base.util.log.Logs
import com.bbgo.module_collect.repository.CollectRepository
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
/**
* author: wangyb
* date: 3/29/21 9:31 PM
* description: todo
*/
class CollectViewModel(private val repository: CollectRepository) : ViewModel() {
fun collectArticle(indexPage: Int, position: Int, id: Int) = viewModelScope.launch {
repository.collectArticle(id)
.catch {
Logs.e(TAG, it.message, it)
}
.collectLatest {
val event = if (it.errorCode == USER_NOT_LOGIN) {
MessageEvent(indexPage, UNKNOWN, position, id)
} else {
MessageEvent(indexPage, COLLECT, position, id)
}
LiveDataBus.get().with(BusKey.COLLECT).value = event
}
}
fun unCollectArticle(indexPage: Int, position: Int, id: Int) = viewModelScope.launch {
repository.unCollectArticle(id)
.catch {
Logs.e(TAG, it.message, it)
}
.collectLatest {
val event = if (it.errorCode == USER_NOT_LOGIN) {
MessageEvent(indexPage, UNKNOWN, position, id)
} else {
MessageEvent(indexPage, UNCOLLECT, position, id)
}
LiveDataBus.get().with(BusKey.COLLECT).value = event
}
}
companion object {
private const val TAG = "CollectViewModel"
}
}
@@ -0,0 +1,17 @@
package com.bbgo.module_collect
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)
}
}
@@ -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)
}
}
@@ -0,0 +1 @@
/build
@@ -0,0 +1,95 @@
def isBuildModule = rootProject.ext.module.isBuildModule
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'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
buildFeatures {
viewBinding true
}
}
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")
kapt rootProject.ext.compiler["arouterCompiler"]
if (Boolean.valueOf(isBuildModule)) {
implementation project(":modules:module-collect")
}
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,31 @@
# 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 public class com.alibaba.android.arouter.routes.**{*;}
-keep public class com.alibaba.android.arouter.facade.**{*;}
-keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}
# 如果使用了 byType 的方式获取 Service,需添加下面规则,保护接口
-keep interface * implements com.alibaba.android.arouter.facade.template.IProvider
# 如果使用了 单类注入,即不定义接口实现 IProvider,需添加下面规则,保护实现
# -keep class * implements com.alibaba.android.arouter.facade.template.IProvider
@@ -0,0 +1,24 @@
package com.bbgo.module_content
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_content.test", appContext.packageName)
}
}
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bbgo.module_content">
<application>
<activity android:name=".ui.ContentActivity"/>
</application>
</manifest>
@@ -0,0 +1,39 @@
package com.bbgo.module_content.ext
import android.app.Activity
import android.view.ViewGroup
import android.webkit.WebView
import com.bbgo.module_content.R
import com.just.agentweb.AgentWeb
import com.just.agentweb.DefaultWebClient
import com.just.agentweb.WebChromeClient
import com.just.agentweb.WebViewClient
/**
* Created by chenxz on 2018/4/22.
*/
/**
* getAgentWeb
*/
fun String.getAgentWeb(
activity: Activity,
webContent: ViewGroup,
layoutParams: ViewGroup.LayoutParams,
webView: WebView,
webViewClient: WebViewClient?,
webChromeClient: WebChromeClient?,
indicatorColor: Int
): AgentWeb = AgentWeb.with(activity)//传入Activity or Fragment
.setAgentWebParent(webContent, 1, layoutParams)//传入AgentWeb 的父控件
.useDefaultIndicator(indicatorColor, 2)// 使用默认进度条
.setWebView(webView)
.setWebViewClient(webViewClient)
.setWebChromeClient(webChromeClient)
.setMainFrameErrorView(R.layout.agentweb_error_page, -1)
.setSecurityType(AgentWeb.SecurityType.STRICT_CHECK)
.setOpenOtherPageWays(DefaultWebClient.OpenOtherPageWays.ASK)//打开其他应用时,弹窗咨询用户是否前往其他应用
.interceptUnkownUrl()
.createAgentWeb()//
.ready()
.go(this)
@@ -0,0 +1,252 @@
package com.bbgo.module_content.ui
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.view.KeyEvent
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.webkit.WebSettings
import android.webkit.WebView
import androidx.coordinatorlayout.widget.CoordinatorLayout
import com.alibaba.android.arouter.facade.annotation.Autowired
import com.alibaba.android.arouter.facade.annotation.Route
import com.alibaba.android.arouter.launcher.ARouter
import com.bbgo.common_base.base.BaseActivity
import com.bbgo.common_base.bus.BusKey
import com.bbgo.common_base.bus.LiveDataBus
import com.bbgo.common_base.constants.Constants
import com.bbgo.common_base.constants.RouterPath
import com.bbgo.common_base.event.MessageEvent
import com.bbgo.common_base.ext.showToast
import com.bbgo.common_base.util.AppUtil
import com.bbgo.common_base.util.SettingUtil
import com.bbgo.common_service.collect.CollectService
import com.bbgo.module_content.R
import com.bbgo.module_content.databinding.ActivityContentBinding
import com.bbgo.module_content.ext.getAgentWeb
import com.bbgo.module_content.webclient.WebClientFactory
import com.google.android.material.appbar.AppBarLayout
import com.just.agentweb.AgentWeb
import com.just.agentweb.NestedScrollAgentWebView
import com.just.agentweb.WebChromeClient
@Route(path = RouterPath.Content.PAGE_CONTENT)
class ContentActivity : BaseActivity<ActivityContentBinding>(){
private var mAgentWeb: AgentWeb? = null
@Autowired
lateinit var title: String
@Autowired
lateinit var url: String
@Autowired
lateinit var id: String
@Autowired
lateinit var position: String
@Autowired
lateinit var isCollect: String
@Autowired
lateinit var collectService: CollectService
private lateinit var menu: Menu
companion object {
fun start(context: Context?, position: Int, id: Int, title: String, url: String, bundle: Bundle? = null) {
Intent(context, ContentActivity::class.java).run {
putExtra(Constants.CONTENT_ID_KEY, id)
putExtra(Constants.CONTENT_TITLE_KEY, title)
putExtra(Constants.CONTENT_URL_KEY, url)
putExtra(Constants.POSITION, position)
context?.startActivity(this, bundle)
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
initView()
}
private fun initView() {
ARouter.getInstance().inject(this)
initBus()
binding.actionBar.apply {
title = ""//getString(R.string.loading)
setSupportActionBar(binding.actionBar.toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
binding.actionBar.tvTitle.apply {
text = getString(R.string.agentweb_loading)
visibility = View.VISIBLE
postDelayed({
binding.actionBar.tvTitle.isSelected = true
}, 2000)
}
initWebView()
}
/**
* 初始化 WebView
*/
private fun initWebView() {
val webView = NestedScrollAgentWebView(this)
val layoutParams = CoordinatorLayout.LayoutParams(-1, -1)
layoutParams.behavior = AppBarLayout.ScrollingViewBehavior()
mAgentWeb = url.getAgentWeb(
this,
binding.clMain,
layoutParams,
webView,
WebClientFactory.create(url),
mWebChromeClient,
SettingUtil.getColor())
//if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// WebView.setWebContentsDebuggingEnabled(true)
//}
mAgentWeb?.webCreator?.webView?.apply {
overScrollMode = WebView.OVER_SCROLL_NEVER
settings.domStorageEnabled = true
settings.javaScriptEnabled = true
settings.loadsImagesAutomatically = true
settings.useWideViewPort = true
settings.loadWithOverviewMode = true
settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.SINGLE_COLUMN
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
}
}
}
private val mWebChromeClient = object : WebChromeClient() {
override fun onReceivedTitle(view: WebView, title: String) {
super.onReceivedTitle(view, title)
binding.actionBar.tvTitle.text = title
}
}
private fun initBus() {
LiveDataBus.get().with(BusKey.COLLECT, MessageEvent::class.java).observe(this) {
handleCollect(it)
}
}
private fun handleCollect(event: MessageEvent) {
when (event.type) {
Constants.CollectType.UNKNOWN -> {
ARouter.getInstance().build(RouterPath.LoginRegister.PAGE_LOGIN).navigation()
}
else -> {
val menuItem = menu.findItem(R.id.action_like)
if (event.type == Constants.CollectType.COLLECT) {
showToast(getString(R.string.collect_success))
menuItem.title = getString(R.string.action_un_like)
isCollect = "true"
return
}
showToast(getString(R.string.cancel_collect_success))
menuItem.title = getString(R.string.action_like)
isCollect = "false"
}
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_content, menu)
this.menu = menu
val title = if (isCollect.toBoolean()) {
getString(R.string.action_un_like)
} else {
getString(R.string.action_like)
}
val menuItem = menu.findItem(R.id.action_like)
menuItem.title = title
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_share -> {
Intent().run {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, getString(
R.string.share_article_url,
getString(R.string.app_name), title, url
))
type = Constants.CONTENT_SHARE_TYPE
startActivity(Intent.createChooser(this, getString(R.string.action_share)))
}
return true
}
R.id.action_like -> {
if (AppUtil.isLogin) {
if (id == "-1") return true
if (isCollect.toBoolean()) {
collectService.unCollect(-1, position.toInt(), id.toInt())
} else {
collectService.collect(-1, position.toInt(), id.toInt())
}
} else {
ARouter.getInstance().build(RouterPath.LoginRegister.PAGE_LOGIN).navigation()
showToast(resources.getString(R.string.login_tint))
}
return true
}
R.id.action_browser -> {
Intent().run {
action = "android.intent.action.VIEW"
data = Uri.parse(url)
startActivity(this)
}
return true
}
}
return super.onOptionsItemSelected(item)
}
override fun onBackPressed() {
mAgentWeb?.let {
if (!it.back()) {
super.onBackPressed()
}
}
}
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
if (mAgentWeb?.handleKeyEvent(keyCode, event)!!) {
return true
}
return super.onKeyDown(keyCode, event)
}
override fun onResume() {
mAgentWeb?.webLifeCycle?.onResume()
super.onResume()
}
override fun onPause() {
mAgentWeb?.webLifeCycle?.onPause()
super.onPause()
}
override fun onDestroy() {
mAgentWeb?.webLifeCycle?.onDestroy()
super.onDestroy()
}
override fun inflateViewBinding() = ActivityContentBinding.inflate(layoutInflater)
}
@@ -0,0 +1,41 @@
package com.bbgo.module_content.ui
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.util.AttributeSet
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.content.ContextCompat
import com.bbgo.common_base.util.ColorUtil
import com.bbgo.common_base.util.SettingUtil
import com.bbgo.module_content.R
/**
* @author chenxz
* @date 2019/11/24
* @desc WebContainer
*/
class WebContainer @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0)
: CoordinatorLayout(context, attrs, defStyleAttr) {
private var mDarkTheme: Boolean = false
private var mMaskColor = Color.TRANSPARENT
init {
mDarkTheme = SettingUtil.getIsNightMode()
if (mDarkTheme) {
mMaskColor = ColorUtil.alphaColor(ContextCompat.getColor(getContext(),
R.color.mask_color
), 0.6f)
}
}
override fun dispatchDraw(canvas: Canvas) {
super.dispatchDraw(canvas)
if (mDarkTheme) {
canvas.drawColor(mMaskColor)
}
}
}
@@ -0,0 +1,20 @@
package com.bbgo.module_content.util
import okhttp3.OkHttpClient
import okhttp3.Request
object Wget {
fun get(url: String): String {
val client = OkHttpClient.Builder()
.build()
val request = Request.Builder()
.url(url)
.header("user-agent",
"Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3887.7 Mobile Safari/537.36")
.build()
val response = client.newCall(request).execute()
return response.body?.string() ?: ""
}
}
@@ -0,0 +1,95 @@
package com.bbgo.module_content.webclient
import android.graphics.Bitmap
import android.net.Uri
import android.net.http.SslError
import android.os.Build
import android.util.Log
import android.webkit.*
import androidx.annotation.RequiresApi
import com.just.agentweb.WebViewClient
/**
* @author chenxz
* @date 2019/11/24
* @desc BaseWebClient
*/
open class BaseWebClient : WebViewClient() {
protected val TAG = "BaseWebClient"
// 拦截的网址
private val blackHostList = arrayListOf(
"www.taobao.com",
"www.jd.com",
"yun.tuisnake.com",
"yun.lvehaisen.com",
"yun.tuitiger.com"
)
private fun isBlackHost(host: String): Boolean {
for (blackHost in blackHostList) {
if (blackHost == host) {
return true
}
}
return false
}
private fun shouldInterceptRequest(uri: Uri?): Boolean {
if (uri != null) {
val host = uri.host ?: ""
return isBlackHost(host)
}
return false
}
private fun shouldOverrideUrlLoading(uri: Uri?): Boolean {
if (uri != null) {
val host = uri.host ?: ""
return isBlackHost(host)
}
return false
}
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest?): WebResourceResponse? {
if (shouldInterceptRequest(request?.url)) {
return WebResourceResponse(null, null, null)
}
return super.shouldInterceptRequest(view, request)
}
override fun shouldInterceptRequest(view: WebView?, url: String?): WebResourceResponse? {
Log.e(TAG, "------------>>$url")
if (shouldInterceptRequest(Uri.parse(url))) {
return WebResourceResponse(null, null, null)
}
return super.shouldInterceptRequest(view, url)
}
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
return shouldOverrideUrlLoading(Uri.parse(url))
}
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
return shouldOverrideUrlLoading(request?.url)
}
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
Log.e(TAG, "onPageStarted---->>$url")
}
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
Log.e(TAG, "onPageFinished---->>$url")
}
override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) {
// super.onReceivedSslError(view, handler, error)
handler?.proceed()
}
}
@@ -0,0 +1,59 @@
package com.bbgo.module_content.webclient
import android.content.Context
import android.webkit.WebResourceResponse
import android.webkit.WebView
import com.bbgo.common_base.util.StringUtil
import com.bbgo.module_content.util.Wget
import java.io.ByteArrayInputStream
import java.util.regex.Pattern
/**
* @author chenxz
* @date 2019/11/24
* @desc JianShuWebClient 参考文章:https://mp.weixin.qq.com/s/gs2bojFLBB4IAWMyN9lfnw
*/
class JianShuWebClient : BaseWebClient() {
private val rex = "(<style data-vue-ssr-id=[\\s\\S]*?>)([\\s\\S]*]?)(<\\/style>)"
private val bodyRex = "<body class=\"([\\ss\\S]*?)\""
override fun shouldInterceptRequest(view: WebView?, url: String?): WebResourceResponse? {
val urlStr = url ?: ""
if (urlStr.startsWith(WebClientFactory.JIAN_SHU)) {
val response = Wget.get(url ?: "")
val res = darkBody(replaceCss(response, view!!.context))
val input = ByteArrayInputStream(res.toByteArray())
return WebResourceResponse("text/html", "utf-8", input)
}
return super.shouldInterceptRequest(view, url)
}
private fun darkBody(res: String): String {
val pattern = Pattern.compile(bodyRex)
val m = pattern.matcher(res)
return res
//return if (m.find()) {
// val s = "<body class=\"reader-night-mode normal-size\""
// res.replace(bodyRex.toRegex(), s)
//} else res
}
private fun replaceCss(res: String, context: Context): String {
val pattern = Pattern.compile(rex)
val m = pattern.matcher(res)
return if (m.find()) {
val css = StringUtil.getString(context.assets.open("jianshu/jianshu.css"))
val sb = StringBuilder()
sb.append(m.group(1))
sb.append(css)
sb.append(m.group(3))
val res = res.replace(rex.toRegex(), sb.toString())
res
} else {
res
}
}
}
@@ -0,0 +1,22 @@
package com.bbgo.module_content.webclient
import com.just.agentweb.WebViewClient
/**
* @author chenxz
* @date 2019/11/24
* @desc WebClientFactory
*/
object WebClientFactory {
val JIAN_SHU = "https://www.jianshu.com"
fun create(url: String): WebViewClient {
return when {
url.startsWith(JIAN_SHU) -> JianShuWebClient()
else -> BaseWebClient()
}
}
}
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/decelerate_interpolator">
<scale android:fromXScale="2.0" android:toXScale="1.0"
android:fromYScale="2.0" android:toYScale="1.0"
android:pivotX="50%p" android:pivotY="50%p"
android:duration="@android:integer/config_mediumAnimTime" />
</set>
@@ -0,0 +1,10 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/decelerate_interpolator"
android:zAdjustment="top">
<scale android:fromXScale="1.0" android:toXScale=".5"
android:fromYScale="1.0" android:toYScale=".5"
android:pivotX="50%p" android:pivotY="50%p"
android:duration="@android:integer/config_mediumAnimTime" />
<alpha android:fromAlpha="1.0" android:toAlpha="0"
android:duration="@android:integer/config_mediumAnimTime"/>
</set>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha
android:duration="1000"
android:fromAlpha="0"
android:interpolator="@android:anim/decelerate_interpolator"
android:toAlpha="1.0"/>
</set>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha
android:duration="1500"
android:fromAlpha="1.0"
android:interpolator="@android:anim/decelerate_interpolator"
android:toAlpha="0"/>
</set>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="500"
android:fromXDelta="-30.0%p"
android:toXDelta="0.0" />
</set>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="500"
android:fromXDelta="0.0"
android:toXDelta="-30.0%p" />
</set>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromYDelta="-50%p" android:toYDelta="0" android:duration="1000"/>
<alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="1000" />
</set>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromYDelta="0" android:toYDelta="-50%p" android:duration="1000"/>
<alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="1000" />
</set>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="500"
android:fromXDelta="100.0%p"
android:toXDelta="0.0" />
</set>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="500"
android:fromXDelta="0.0"
android:toXDelta="100.0%p" />
</set>
@@ -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,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,4 @@
<vector android:height="24dp" android:viewportHeight="1024.0"
android:viewportWidth="1024.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#ffffff" android:pathData="M981.8,311.4c-25.7,-60.7 -62.4,-115.2 -109.2,-162.1 -46.8,-46.8 -101.3,-83.6 -162.1,-109.2 -62.2,-26.3 -128.2,-39.8 -196.2,-40.1 -0.7,0 -1.5,-0 -2.2,-0s-1.5,0 -2.2,0c-68,0.3 -134,13.8 -196.2,40.1 -60.7,25.7 -115.2,62.4 -162.1,109.2 -46.8,46.8 -83.6,101.3 -109.2,162.1 -26.6,62.9 -40.1,129.6 -40.1,198.5 0,68.8 13.5,135.6 40.1,198.5 25.7,60.7 62.4,115.2 109.2,162.1s101.3,83.6 162.1,109.2c62.2,26.3 128.2,39.8 196.2,40.1 0.7,0 1.5,0 2.2,0s1.5,-0 2.2,-0c68,-0.3 134,-13.8 196.2,-40.1 60.7,-25.7 115.2,-62.4 162.1,-109.2s83.6,-101.3 109.2,-162.1c26.6,-62.9 40.1,-129.6 40.1,-198.5C1021.8,441 1008.3,374.2 981.8,311.4zM180.4,178.3c43.1,-43.1 93.2,-76.9 149,-100.5 19.2,-8.1 38.7,-14.9 58.6,-20.3 -24.8,24.9 -47.6,57.5 -67.4,97.2 -12.7,25.4 -23.9,53.1 -33.5,82.5 -44.1,-11.8 -84.6,-27 -119.7,-45.4C171.7,187.2 176,182.7 180.4,178.3zM79.9,327.3c15.7,-37.2 36,-71.9 60.6,-103.7 39.3,21.6 85.1,39.3 135.2,52.7 -17.3,65.3 -27.1,137.8 -28.6,213L43.6,489.4C45.9,433.3 58.1,378.9 79.9,327.3zM140.5,796c-24.5,-31.8 -44.8,-66.5 -60.5,-103.7 -21.8,-51.5 -34,-106 -36.4,-162.1l203.5,0c1.5,75.2 11.3,147.6 28.6,213C225.6,756.7 179.8,774.4 140.5,796zM329.5,941.9c-55.8,-23.6 -106,-57.4 -149,-100.5 -4.4,-4.4 -8.7,-8.9 -12.9,-13.4 35.1,-18.4 75.5,-33.7 119.7,-45.5 9.6,29.4 20.8,57.1 33.5,82.5 19.9,39.7 42.6,72.3 67.4,97.2C368.2,956.8 348.6,950 329.5,941.9zM491.5,976.7c-21,-4.2 -41.6,-14.7 -61.6,-31.6 -27,-22.8 -51.5,-56 -72.7,-98.4 -11.4,-22.7 -21.5,-47.4 -30.2,-73.7 51.5,-10.8 107,-17 164.4,-18.2L491.5,976.7zM491.5,713.9c-61.3,1.2 -120.9,8.1 -176.1,19.9 -16.5,-62.1 -25.9,-131.3 -27.4,-203.4l203.5,0L491.5,713.9zM491.5,489.4l-203.5,0c1.5,-72.2 10.9,-141.3 27.4,-203.5 55.3,11.8 114.8,18.7 176.1,19.9L491.5,489.4zM491.5,264.9c-57.3,-1.2 -112.9,-7.5 -164.4,-18.2 8.7,-26.2 18.8,-50.9 30.1,-73.7 21.2,-42.4 45.7,-75.6 72.7,-98.4 19.9,-16.9 40.6,-27.4 61.6,-31.6L491.5,264.9zM694.5,77.8c55.8,23.6 106,57.4 149,100.5 4.4,4.4 8.7,8.9 12.9,13.4 -34.7,18.1 -75.4,33.6 -119.7,45.4 -9.6,-29.4 -20.8,-57 -33.5,-82.5 -19.9,-39.7 -42.6,-72.3 -67.4,-97.2C655.8,62.9 675.4,69.7 694.5,77.8zM532.5,43c21,4.2 41.6,14.7 61.5,31.6 27,22.9 51.5,56 72.7,98.4 11.4,22.7 21.4,47.4 30.1,73.6 -51.9,10.9 -107.8,17.1 -164.4,18.3L532.5,43zM532.5,305.8c61.2,-1.2 120.3,-7.9 176.1,-19.8 16.5,62.1 25.9,131.3 27.4,203.4l-203.5,0L532.5,305.8 532.5,305.8zM532.5,530.3l203.5,0c-1.5,72.1 -10.9,141.3 -27.5,203.5 -55.3,-11.8 -114.8,-18.7 -176.1,-19.9L532.5,530.3 532.5,530.3zM594,945.1c-19.9,16.9 -40.5,27.4 -61.5,31.6L532.5,754.8c57.3,1.2 112.8,7.5 164.4,18.2 -8.7,26.2 -18.8,50.9 -30.1,73.7C645.5,889.2 621,922.3 594,945.1zM843.6,841.4c-43.1,43.1 -93.2,76.9 -149,100.5 -19.2,8.1 -38.7,14.9 -58.6,20.3 24.8,-24.9 47.6,-57.5 67.4,-97.2 12.7,-25.4 23.9,-53.1 33.5,-82.5 44.1,11.7 84.6,27 119.7,45.4C852.3,832.5 848,837 843.6,841.4zM944.1,692.3c-15.7,37.2 -36,71.9 -60.6,103.7 -39.3,-21.6 -85.1,-39.3 -135.2,-52.7 17.3,-65.4 27.1,-137.8 28.6,-213l203.5,0C978.1,586.4 965.9,640.8 944.1,692.3zM776.9,489.4c-1.5,-75.2 -11.3,-147.6 -28.6,-213 50.8,-13.6 96.2,-31.3 135.2,-52.7 24.5,31.8 44.8,66.4 60.6,103.7 21.8,51.5 34,106 36.4,162.1L776.9,489.4z"/>
</vector>
@@ -0,0 +1,4 @@
<vector android:height="24dp" android:viewportHeight="1024.0"
android:viewportWidth="1024.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#ffffff" android:pathData="M768,686.1c-32.4,0 -61.4,12.8 -83.6,32.9l-304.2,-177.1c2.1,-9.8 3.8,-19.6 3.8,-29.9s-1.7,-20.1 -3.8,-29.9l300.8,-175.4c23,21.3 53.3,34.6 87,34.6 70.8,0 128,-57.2 128,-128s-57.2,-128 -128,-128c-70.8,0 -128,57.2 -128,128 0,10.2 1.7,20.1 3.8,29.9l-300.8,175.4c-23,-21.3 -53.3,-34.6 -87,-34.6 -70.8,0 -128,57.2 -128,128s57.2,128 128,128c33.7,0 64,-13.2 87,-34.6l303.8,177.5c-2.1,9 -3.4,18.3 -3.4,27.7 0,68.7 55.9,124.6 124.6,124.6s124.6,-55.9 124.6,-124.6c0,-68.7 -55.9,-124.6 -124.6,-124.6l0,0z"/>
</vector>
@@ -0,0 +1,4 @@
<vector android:height="24dp" android:viewportHeight="1024.0"
android:viewportWidth="1024.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFF" android:pathData="M761.4,136.9C588.3,90.9 512,238.2 512,238.2 512,238.2 435.7,90.9 262.6,136.9 89.6,183 -5.3,414.9 208.7,623 422.7,831.1 512,910.2 512,910.2 512,910.2 601.3,831.1 815.3,623 1029.3,414.9 934.4,183 761.4,136.9"/>
</vector>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<com.bbgo.module_content.ui.WebContainer xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/cl_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/viewBackground"
android:orientation="vertical">
<include
android:id="@+id/action_bar"
layout="@layout/content_toolbar" />
</com.bbgo.module_content.ui.WebContainer>
@@ -0,0 +1,31 @@
<?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/white"
android:textSize="@dimen/sp_18"
android:visibility="gone" />
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_share"
android:icon="@drawable/ic_action_share"
android:orderInCategory="100"
android:title="@string/action_share"
app:showAsAction="never" />
<item
android:id="@+id/action_like"
android:icon="@drawable/ic_action_white_like"
android:orderInCategory="100"
android:title="@string/action_like"
app:showAsAction="never" />
<item
android:id="@+id/action_browser"
android:icon="@drawable/ic_action_browser"
android:orderInCategory="100"
android:title="@string/action_browser"
app:showAsAction="never" />
</menu>
@@ -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,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="textColorPrimary" format="color" />
<attr name="viewBackground" format="color" />
</resources>
@@ -0,0 +1,85 @@
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="avatar_width">80dp</dimen>
<dimen name="avatar_height">80dp</dimen>
<dimen name="appbar_layout_height">260dp</dimen>
<!-- item -->
<dimen name="item_tv_title">16sp</dimen>
<dimen name="item_tv_content">14sp</dimen>
<dimen name="item_tv_date">12sp</dimen>
<dimen name="item_tv_tag">12sp</dimen>
<dimen name="item_tv_author">12sp</dimen>
<dimen name="item_tv_via">12sp</dimen>
<dimen name="item_content_padding">10dp</dimen>
<dimen name="item_img_width">120dp</dimen>
<dimen name="item_img_height">90dp</dimen>
<dimen name="textSize_small_10">10sp</dimen>
<dimen name="textSize_small">12sp</dimen>
<dimen name="textSize_content">14sp</dimen>
<dimen name="textSize_titleSmall">16sp</dimen>
<dimen name="textSize_title">18sp</dimen>
<!-- dp -->
<dimen name="dp_05">0.5dp</dimen>
<dimen name="dp_1">1dp</dimen>
<dimen name="dp_2">2dp</dimen>
<dimen name="dp_3">3dp</dimen>
<dimen name="dp_4">4dp</dimen>
<dimen name="dp_5">5dp</dimen>
<dimen name="dp_6">6dp</dimen>
<dimen name="dp_8">8dp</dimen>
<dimen name="dp_10">10dp</dimen>
<dimen name="dp_12">12dp</dimen>
<dimen name="dp_16">16dp</dimen>
<dimen name="dp_18">18dp</dimen>
<dimen name="dp_20">20dp</dimen>
<dimen name="dp_24">24dp</dimen>
<dimen name="dp_26">26dp</dimen>
<dimen name="dp_28">28dp</dimen>
<dimen name="dp_30">30dp</dimen>
<dimen name="dp_36">36dp</dimen>
<dimen name="dp_46">46dp</dimen>
<dimen name="dp_50">50dp</dimen>
<dimen name="dp_54">54dp</dimen>
<dimen name="dp_60">60dp</dimen>
<dimen name="dp_80">80dp</dimen>
<dimen name="dp_90">90dp</dimen>
<dimen name="dp_100">100dp</dimen>
<dimen name="dp_110">110dp</dimen>
<dimen name="dp_168">168dp</dimen>
<dimen name="dp_180">180dp</dimen>
<dimen name="dp_200">200dp</dimen>
<dimen name="dp_230">230dp</dimen>
<dimen name="dp_240">240dp</dimen>
<dimen name="dp_260">260dp</dimen>
<dimen name="dp_280">280dp</dimen>
<dimen name="dp_300">300dp</dimen>
<!-- sp -->
<dimen name="sp_8">8sp</dimen>
<dimen name="sp_9">9sp</dimen>
<dimen name="sp_10">10sp</dimen>
<dimen name="sp_12">12sp</dimen>
<dimen name="sp_13">13sp</dimen>
<dimen name="sp_14">14sp</dimen>
<dimen name="sp_15">15sp</dimen>
<dimen name="sp_16">16sp</dimen>
<dimen name="sp_17">17sp</dimen>
<dimen name="sp_18">18sp</dimen>
<dimen name="sp_20">20sp</dimen>
<dimen name="sp_22">22sp</dimen>
<dimen name="sp_24">24sp</dimen>
<dimen name="sp_40">40sp</dimen>
<dimen name="sp_50">50sp</dimen>
<!--BottomNavigationView 的选中没有选中的字体大小-->
<dimen name="design_bottom_navigation_active_text_size">@dimen/sp_13</dimen>
<dimen name="design_bottom_navigation_text_size">@dimen/sp_12</dimen>
</resources>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item type="id" name="expandable_text"/>
<item type="id" name="expand_collapse"/>
<item name="statusbarutil_fake_status_bar_view" type="id" />
<item name="statusbarutil_translucent_view" type="id" />
</resources>
@@ -0,0 +1,11 @@
<resources>
<string name="app_name">WebContent模块</string>
<string name="share_article_url">%1$s分享【%2$s】:%3$s</string>
<string name="action_share">分享</string>
<string name="login_tint">请先登录</string>
<string name="action_like">收藏</string>
<string name="action_un_like">取消收藏</string>
<string name="action_browser">用浏览器打开</string>
<string name="collect_success">收藏成功</string>
<string name="cancel_collect_success">已取消收藏</string>
</resources>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="transition_movie_img">transition_movie_img</string>
<string name="transition_news_img">transition_news_img</string>
<string name="transition_book_img">transition_book_img</string>
</resources>
@@ -0,0 +1,17 @@
package com.bbgo.module_content
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)
}
}
@@ -0,0 +1 @@
/build
@@ -0,0 +1,105 @@
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'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
buildFeatures {
viewBinding 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'
}
}
}
}
lintOptions {
checkReleaseBuilds false
// Or, if you prefer, you can continue to check for errors in release builds,
// but continue the build even when errors are found:
abortOnError false
}
}
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")
kapt rootProject.ext.compiler["arouterCompiler"]
implementation rootProject.ext.roomLibs
kapt rootProject.ext.compiler["roomCompiler"]
compileOnly(rootProject.ext.jetpack["hilt"])
kapt rootProject.ext.compiler["hiltAndroidCompiler"]
if (Boolean.valueOf(isBuildModule)) {
implementation project(":modules:module-collect")
implementation project(":modules:module-content")
implementation project(":modules:module-login")
}
}
@@ -0,0 +1,2 @@
-keep class com.bbgo.module_home.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_home.bean.** {*;}
@@ -0,0 +1,24 @@
package com.bbgo.module_home
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_home", appContext.packageName)
}
}
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bbgo.module_home">
<application>
<activity android:name=".activity.HomeActivity">
</activity>
</application>
</manifest>
@@ -0,0 +1,13 @@
package com.bbgo.module_home
import com.bbgo.common_base.BaseApplication
import dagger.hilt.android.HiltAndroidApp
/**
* @Description:
* @Author: wangyuebin
* @Date: 2021/9/10 5:12 下午
*/
//@HiltAndroidApp
class HomeApp : BaseApplication() {
}
@@ -0,0 +1,12 @@
package com.bbgo.module_home.activity
import com.bbgo.common_base.base.BaseActivity
import com.bbgo.module_home.R
import com.bbgo.module_home.databinding.FragmentHomeBinding
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class HomeActivity : BaseActivity<FragmentHomeBinding>() {
override fun inflateViewBinding() = FragmentHomeBinding.inflate(layoutInflater)
}
@@ -0,0 +1,19 @@
package com.bbgo.module_home.bean
import androidx.annotation.Keep
/**
* @Description:
* @Author: wangyuebin
* @Date: 2021/10/22 8:59 下午
*/
@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
)

Some files were not shown because too many files have changed in this diff Show More