a
This commit is contained in:
@@ -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.** {*;}
|
||||
+24
@@ -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>
|
||||
+11
@@ -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)
|
||||
+33
@@ -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>>
|
||||
|
||||
|
||||
}
|
||||
+41
@@ -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)
|
||||
}
|
||||
}
|
||||
+43
@@ -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?) {
|
||||
}
|
||||
}
|
||||
+58
@@ -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"
|
||||
}
|
||||
}
|
||||
+17
@@ -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.** {*;}
|
||||
+24
@@ -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>
|
||||
+13
@@ -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() {
|
||||
}
|
||||
+394
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
+58
@@ -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
|
||||
)
|
||||
+32
@@ -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>>
|
||||
|
||||
}
|
||||
+25
@@ -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!!
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -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()
|
||||
}
|
||||
}
|
||||
+38
@@ -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)
|
||||
}
|
||||
}
|
||||
+8
@@ -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)
|
||||
+17
@@ -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
|
||||
+11
@@ -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)
|
||||
)
|
||||
+44
@@ -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
|
||||
)
|
||||
}
|
||||
+28
@@ -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
|
||||
)
|
||||
*/
|
||||
)
|
||||
+20
@@ -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()))
|
||||
|
||||
}
|
||||
+51
@@ -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"
|
||||
}
|
||||
}
|
||||
+19
@@ -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>
|
||||
+30
@@ -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>
|
||||
+20
@@ -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>
|
||||
BIN
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>
|
||||
+5
@@ -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>
|
||||
+17
@@ -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
|
||||
+24
@@ -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>
|
||||
+39
@@ -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)
|
||||
+252
@@ -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)
|
||||
|
||||
}
|
||||
+41
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+20
@@ -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() ?: ""
|
||||
}
|
||||
|
||||
}
|
||||
+95
@@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
+59
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+22
@@ -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>
|
||||
+20
@@ -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>
|
||||
+4
@@ -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>
|
||||
+5
@@ -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>
|
||||
+17
@@ -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.** {*;}
|
||||
+24
@@ -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() {
|
||||
}
|
||||
+12
@@ -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)
|
||||
}
|
||||
+19
@@ -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
Reference in New Issue
Block a user