Compare commits
5 Commits
b426fda4d3
...
48b23cbdd3
| Author | SHA1 | Date |
|---|---|---|
|
|
48b23cbdd3 | |
|
|
a792dddc7c | |
|
|
19cd98cea5 | |
|
|
7c4691a1ed | |
|
|
e2f4c50410 |
|
|
@ -129,8 +129,9 @@ android {
|
||||||
buildConfigString("API_PIGEON", "https://test-pigeon.xxxx.ai")
|
buildConfigString("API_PIGEON", "https://test-pigeon.xxxx.ai")
|
||||||
buildConfigString("API_LION", "https://test-lion.xxxx.ai")
|
buildConfigString("API_LION", "https://test-lion.xxxx.ai")
|
||||||
buildConfigString("RECHAEGE_SERVICES", "https://test.xxxxx.ai/policy/recharge")
|
buildConfigString("RECHAEGE_SERVICES", "https://test.xxxxx.ai/policy/recharge")
|
||||||
|
|
||||||
buildConfigString("RTC_APP_ID", "689ade491323ae01797818e0-XXX-TODO")
|
buildConfigString("RTC_APP_ID", "689ade491323ae01797818e0-XXX-TODO")
|
||||||
|
|
||||||
|
buildConfigString("API_BASE", "http://54.223.196.180:9090")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -147,8 +148,9 @@ android {
|
||||||
buildConfigString("API_PIGEON", "https://test-pigeon.xxxx.ai")
|
buildConfigString("API_PIGEON", "https://test-pigeon.xxxx.ai")
|
||||||
buildConfigString("API_LION", "https://test-lion.xxxx.ai")
|
buildConfigString("API_LION", "https://test-lion.xxxx.ai")
|
||||||
buildConfigString("RECHAEGE_SERVICES", "https://test.xxxxx.ai/policy/recharge")
|
buildConfigString("RECHAEGE_SERVICES", "https://test.xxxxx.ai/policy/recharge")
|
||||||
|
|
||||||
buildConfigString("RTC_APP_ID", "689ade491323ae01797818e0-XXX-TODO")
|
buildConfigString("RTC_APP_ID", "689ade491323ae01797818e0-XXX-TODO")
|
||||||
|
|
||||||
|
buildConfigString("API_BASE", "http://54.223.196.180:9090")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@
|
||||||
|
|
||||||
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
||||||
|
|
||||||
|
<!--TODO - remove usesCleartextTraffic below-->
|
||||||
<application
|
<application
|
||||||
android:name=".configs.NovelApplication"
|
android:name=".configs.NovelApplication"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
|
@ -40,7 +41,11 @@
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme"
|
||||||
|
|
||||||
|
android:usesCleartextTraffic="true"
|
||||||
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
|
>
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.splash.SplashActivity"
|
android:name=".ui.splash.SplashActivity"
|
||||||
android:theme="@style/AppTheme.Launcher"
|
android:theme="@style/AppTheme.Launcher"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.remax.visualnovel.api.service
|
||||||
|
|
||||||
|
import com.remax.visualnovel.BuildConfig
|
||||||
|
import com.remax.visualnovel.entity.response.basenew.ResponseNew
|
||||||
|
import com.remax.visualnovel.entity.request.ParamActorList
|
||||||
|
import com.remax.visualnovel.entity.response.ActorBean
|
||||||
|
import com.remax.visualnovel.entity.response.ActorTag
|
||||||
|
import retrofit2.http.Body
|
||||||
|
import retrofit2.http.GET
|
||||||
|
import retrofit2.http.POST
|
||||||
|
|
||||||
|
|
||||||
|
interface ActorsService {
|
||||||
|
@GET(BuildConfig.API_BASE + "/tag/getTags")
|
||||||
|
suspend fun requestActorTags(): ResponseNew<List<ActorTag>>
|
||||||
|
|
||||||
|
@POST(BuildConfig.API_BASE + "/character/select/list")
|
||||||
|
suspend fun requestActorList(@Body param: ParamActorList): ResponseNew<List<ActorBean>>
|
||||||
|
|
||||||
|
/*@GET(BuildConfig.API_BASE + "/character/select/roleInfo/{roleId}")
|
||||||
|
suspend fun requestActorInfo(): ResponseNew<ActorTag>*/
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ package com.remax.visualnovel.app.di
|
||||||
|
|
||||||
import com.remax.visualnovel.api.factory.ServiceFactory
|
import com.remax.visualnovel.api.factory.ServiceFactory
|
||||||
import com.remax.visualnovel.api.service.AIService
|
import com.remax.visualnovel.api.service.AIService
|
||||||
|
import com.remax.visualnovel.api.service.ActorsService
|
||||||
import com.remax.visualnovel.api.service.BookService
|
import com.remax.visualnovel.api.service.BookService
|
||||||
import com.remax.visualnovel.api.service.ChatService
|
import com.remax.visualnovel.api.service.ChatService
|
||||||
import com.remax.visualnovel.api.service.DictService
|
import com.remax.visualnovel.api.service.DictService
|
||||||
|
|
@ -64,4 +65,10 @@ object ApiServiceModule {
|
||||||
private inline fun <reified T> create(): T {
|
private inline fun <reified T> create(): T {
|
||||||
return ServiceFactory.createService()
|
return ServiceFactory.createService()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Provides
|
||||||
|
fun actorsService() = create<ActorsService>()
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.remax.visualnovel.entity.request
|
||||||
|
|
||||||
|
|
||||||
|
data class ParamActorList(
|
||||||
|
var index: Int = 0,
|
||||||
|
var limit: Int = 2,
|
||||||
|
var tagIds: List<Long> = listOf<Long>(),
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.remax.visualnovel.entity.response
|
||||||
|
|
||||||
|
|
||||||
|
data class ActorBean(
|
||||||
|
val id: Long,
|
||||||
|
val roleName: String,
|
||||||
|
val description: String,
|
||||||
|
val coverImageId: String,
|
||||||
|
val sourceId: Int, //来源ID;所属书籍/漫剧
|
||||||
|
val sourceType: Int, //来源分类
|
||||||
|
val commonCount: Int, //评论数
|
||||||
|
val createTime: String,
|
||||||
|
val status: String
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.remax.visualnovel.entity.response
|
||||||
|
|
||||||
|
|
||||||
|
data class ActorTag(
|
||||||
|
val tagName: String,
|
||||||
|
val tagId: Long
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
package com.remax.visualnovel.entity.response.basenew
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ResponseData<T> {
|
||||||
|
val total = 0
|
||||||
|
val index = 0
|
||||||
|
val limit = 0
|
||||||
|
val rows: T? = null
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
package com.remax.visualnovel.entity.response.basenew
|
||||||
|
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
import com.remax.visualnovel.app.base.app.CommonApplicationProxy
|
||||||
|
import com.remax.visualnovel.extension.toast
|
||||||
|
|
||||||
|
|
||||||
|
open class ResponseNew<T>(
|
||||||
|
@SerializedName(value = "data")
|
||||||
|
val data: ResponseData<T>? = null,
|
||||||
|
open val code: Int = -1,
|
||||||
|
open val message: String = "",
|
||||||
|
) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val SuccessCode = 200
|
||||||
|
|
||||||
|
/**
|
||||||
|
* zip打包的错误error封装
|
||||||
|
* new
|
||||||
|
*/
|
||||||
|
inline fun <reified T> createZipFailResponse(vararg data: ResponseNew<*>): ApiFailedResponse<T> {
|
||||||
|
val failedResponse = ApiFailedResponse<T>()
|
||||||
|
for (t in data) {
|
||||||
|
if (!t.isApiSuccess) {
|
||||||
|
failedResponse.code = t.code
|
||||||
|
failedResponse.message = t.message
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return failedResponse
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val isOk: Boolean
|
||||||
|
get() = code == SuccessCode
|
||||||
|
|
||||||
|
val isApiSuccess: Boolean
|
||||||
|
get() =
|
||||||
|
this is ApiSuccessResponse || this is ApiEmptyResponse
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将返回结果分为成功和失败2个高阶函数
|
||||||
|
*
|
||||||
|
* 使用inline修饰,使2个参数可以调用外部函数return
|
||||||
|
*/
|
||||||
|
inline fun transformResult(apiSuccessCallback: ((T?) -> Unit) = {}, apiFailedCallback: ((ResponseNew<T>) -> Unit) = {}): ResponseNew<T> {
|
||||||
|
if (isApiSuccess) {
|
||||||
|
apiSuccessCallback.invoke(data?.rows)
|
||||||
|
} else {
|
||||||
|
apiFailedCallback.invoke(this)
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T> ResponseNew<T>.parseData(listenerBuilder: (ResultBuilder<T>.() -> Unit), showToast: Boolean = false) {
|
||||||
|
val listener = ResultBuilder<T>().also(listenerBuilder)
|
||||||
|
when (this) {
|
||||||
|
is ApiSuccessResponse -> listener.onSuccess(response?.rows)
|
||||||
|
is ApiEmptyResponse -> listener.onSuccess(null)
|
||||||
|
is ApiFailedResponse -> {
|
||||||
|
listener.onFailed(this.code, this.message)
|
||||||
|
listener.onFailedWithData(data?.rows)
|
||||||
|
if (showToast) {
|
||||||
|
CommonApplicationProxy.application.toast(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
listener.onComplete()
|
||||||
|
}
|
||||||
|
|
||||||
|
class ResultBuilder<T> {
|
||||||
|
var onSuccess: (data: T?) -> Unit = {}
|
||||||
|
var onFailed: (errorCode: Int, errorMsg: String) -> Unit = { _, _ ->
|
||||||
|
}
|
||||||
|
var onFailedWithData: (errorData: T?) -> Unit = {}
|
||||||
|
var onComplete: () -> Unit = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ApiSuccessResponse<T>(val response: ResponseData<T>? = null) : ResponseNew<T>(data = response)
|
||||||
|
|
||||||
|
class ApiEmptyResponse<T> : ResponseNew<T>()
|
||||||
|
|
||||||
|
data class ApiFailedResponse<T>(override var code: Int = -1, override var message: String = "") :
|
||||||
|
ResponseNew<T>(code = code, message = message)
|
||||||
|
|
||||||
|
|
@ -6,8 +6,10 @@ import android.text.method.ScrollingMovementMethod
|
||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.annotation.StringRes
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import com.remax.visualnovel.R
|
import com.remax.visualnovel.R
|
||||||
|
import com.remax.visualnovel.databinding.DialogConfirmBinding
|
||||||
import com.remax.visualnovel.databinding.DialogDoubleBtnBinding
|
import com.remax.visualnovel.databinding.DialogDoubleBtnBinding
|
||||||
import com.remax.visualnovel.databinding.DialogSingleBtnLayout2Binding
|
import com.remax.visualnovel.databinding.DialogSingleBtnLayout2Binding
|
||||||
import com.remax.visualnovel.databinding.DialogSingleBtnLayoutBinding
|
import com.remax.visualnovel.databinding.DialogSingleBtnLayoutBinding
|
||||||
|
|
@ -22,9 +24,53 @@ import com.remax.visualnovel.widget.uitoken.changeTextStyle
|
||||||
import com.remax.visualnovel.widget.uitoken.view.UITokenTextView
|
import com.remax.visualnovel.widget.uitoken.view.UITokenTextView
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by HJW on 2025/7/18
|
* 底部水平布局 两个按钮的dialog
|
||||||
*/
|
*/
|
||||||
|
fun Activity.showConfirmDialog(
|
||||||
|
@DrawableRes dialogBg: Int? = null,
|
||||||
|
@StringRes title: Int? = null,
|
||||||
|
@StringRes description: Int? = null,
|
||||||
|
@DrawableRes topBtnIconRes: Int? = null,
|
||||||
|
topBtnClick: (() -> Unit)? = null,
|
||||||
|
@StringRes bottomLeftStrRes: Int? = null,
|
||||||
|
@StringRes bottomRightStrRes: Int? = null,
|
||||||
|
bottomRightClick: (() -> Unit)? = null,
|
||||||
|
): LBindingDialog<DialogConfirmBinding> {
|
||||||
|
|
||||||
|
val dialog = LBindingDialog(this, DialogConfirmBinding::inflate)
|
||||||
|
.with()
|
||||||
|
.setCenter()
|
||||||
|
|
||||||
|
dialog.setCanceledOnTouchOutside(false)
|
||||||
|
dialog.setCancelable(false)
|
||||||
|
|
||||||
|
dialog.binding.run {
|
||||||
|
setOnClick(ivTop, tvRight, tvLeft) {
|
||||||
|
when (this) {
|
||||||
|
tvLeft -> {
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
tvRight -> {
|
||||||
|
bottomRightClick?.invoke()
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
ivTop -> topBtnClick?.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dialogBg != null) root.background = getDrawable(dialogBg)
|
||||||
|
if (topBtnIconRes != null) ivTop.setImageResource(topBtnIconRes) else tvTitle.isVisible = false
|
||||||
|
if (title != null) tvTitle.text = getString(title) else tvTitle.isVisible = false
|
||||||
|
if (description != null) tvDescription.text = getString(description) else tvDescription.isVisible = false
|
||||||
|
if (bottomLeftStrRes != null) tvLeft.text = getString(bottomLeftStrRes) else tvLeft.isVisible = false
|
||||||
|
if (bottomRightStrRes != null) tvRight.text = getString(bottomRightStrRes) else tvRight.isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.show()
|
||||||
|
return dialog
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 双按钮的全局统一的dialog样式
|
* 双按钮的全局统一的dialog样式
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,144 @@
|
||||||
|
package com.remax.visualnovel.extension
|
||||||
|
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
|
import com.remax.visualnovel.app.AbsView
|
||||||
|
import com.remax.visualnovel.entity.response.basenew.ResponseNew
|
||||||
|
import com.remax.visualnovel.entity.response.basenew.ResultBuilder
|
||||||
|
import com.remax.visualnovel.entity.response.basenew.parseData
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import kotlinx.coroutines.flow.onCompletion
|
||||||
|
import kotlinx.coroutines.flow.onStart
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
inline fun <T> launchFlow2(
|
||||||
|
crossinline requestBlock: suspend () -> ResponseNew<T>,
|
||||||
|
noinline startCallback: (() -> Unit)? = null,
|
||||||
|
noinline completeCallback: (() -> Unit)? = null,
|
||||||
|
): Flow<ResponseNew<T>> {
|
||||||
|
return flow {
|
||||||
|
emit(requestBlock())
|
||||||
|
}.onStart {
|
||||||
|
startCallback?.invoke()
|
||||||
|
}.onCompletion {
|
||||||
|
completeCallback?.invoke()
|
||||||
|
}.flowOn(Dispatchers.Main)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 简单的请求,不返回任何实体类, 无loading和toast,数据可通过livedata/flow监听
|
||||||
|
*/
|
||||||
|
inline fun AbsView.launchWithRequest2(crossinline requestBlock: suspend () -> Unit, showLoading: Boolean = false) {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
flow {
|
||||||
|
emit(requestBlock())
|
||||||
|
}.onStart {
|
||||||
|
if (showLoading) showLoading()
|
||||||
|
}.onCompletion {
|
||||||
|
if (showLoading) hideLoading()
|
||||||
|
}.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用上面的,默认loading
|
||||||
|
*/
|
||||||
|
inline fun AbsView.launchWithRequestLoading2(crossinline requestBlock: suspend () -> Unit) {
|
||||||
|
launchWithRequest(requestBlock, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 链式调用,返回结果的处理都在一起,viewmodel中不需要创建一个livedata对象
|
||||||
|
* 适用于不需要监听数据变化/一次性使用的场景,比如提交表单/登录
|
||||||
|
* 屏幕旋转,Activity销毁重建,数据会消失
|
||||||
|
*
|
||||||
|
* 默认无toast,无loading
|
||||||
|
*/
|
||||||
|
inline fun <reified T> AbsView.launchAndCollect2(
|
||||||
|
crossinline requestBlock: suspend () -> ResponseNew<T>,
|
||||||
|
showLoading: Boolean = false,
|
||||||
|
showToast: Boolean = true,
|
||||||
|
crossinline listenerBuilder: (ResultBuilder<T>.() -> Unit) = {}
|
||||||
|
) {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
launchFlow2(requestBlock, { if (showLoading) showLoading() }) { if (showLoading) hideLoading() }.collect { response ->
|
||||||
|
response.parseData(listenerBuilder, showToast)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T> AbsView.launchAndLoadingCollect2(
|
||||||
|
crossinline requestBlock: suspend () -> ResponseNew<T>, showToast: Boolean = true, crossinline listenerBuilder: (ResultBuilder<T>.() -> Unit) = {}
|
||||||
|
) {
|
||||||
|
launchAndCollect2(requestBlock, showLoading = true, showToast = showToast, listenerBuilder = listenerBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 简单flow流订阅 生命周期安全
|
||||||
|
*/
|
||||||
|
inline fun <T> Flow<T?>.flowWithLaunch2(
|
||||||
|
lifecycleOwner: LifecycleOwner, minActiveState: Lifecycle.State = Lifecycle.State.STARTED, crossinline resCallback: ((t: T?) -> Unit)
|
||||||
|
) {
|
||||||
|
lifecycleOwner.lifecycleScope.launch {
|
||||||
|
flowWithLifecycle(lifecycleOwner.lifecycle, minActiveState).collect {
|
||||||
|
resCallback.invoke(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun <T> Flow<T>.flowWithLifecycle2(lifecycle: Lifecycle, minActiveState: Lifecycle.State = Lifecycle.State.STARTED): Flow<T> = callbackFlow {
|
||||||
|
lifecycle.repeatOnLifecycle(minActiveState) {
|
||||||
|
this@flowWithLifecycle2.collect {
|
||||||
|
send(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* response liveData监听
|
||||||
|
*/
|
||||||
|
inline fun <reified T> LiveData<ResponseNew<T>>.observeIn2(
|
||||||
|
lifecycleOwner: LifecycleOwner, showToast: Boolean = true, crossinline listenerBuilder: ResultBuilder<T>.() -> Unit
|
||||||
|
) {
|
||||||
|
this.observe(lifecycleOwner, Observer {
|
||||||
|
it.parseData(listenerBuilder, showToast)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订阅UI上展示Flow数据流
|
||||||
|
*
|
||||||
|
* 状态(State)用 StateFlow,粘性的 ;事件(Event)用 SharedFlow 在其 replayCache 中保留特定数量的最新值
|
||||||
|
* MutableSharedFlow :一次性事件,不需要重放的状态变更(例如 Toast)
|
||||||
|
* MutableStateFlow : 页面需要的状态,比如UI的刷新,多次执行没有任何问题
|
||||||
|
* collectLastValue = true时,stateFlow也不会发送未改变的value,就和sharedFlow一样的用法
|
||||||
|
*/
|
||||||
|
inline fun <reified T> Flow<ResponseNew<T>>.collectIn2(
|
||||||
|
lifecycleOwner: LifecycleOwner,
|
||||||
|
minActiveState: Lifecycle.State = Lifecycle.State.STARTED,
|
||||||
|
showToast: Boolean = true,
|
||||||
|
crossinline listenerBuilder: ResultBuilder<T>.() -> Unit
|
||||||
|
): Job {
|
||||||
|
return lifecycleOwner.lifecycleScope.launch {
|
||||||
|
flowWithLifecycle2(lifecycleOwner.lifecycle, minActiveState).collect {
|
||||||
|
it.parseData(listenerBuilder, showToast)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,14 +1,18 @@
|
||||||
package com.remax.visualnovel.repository.api
|
package com.remax.visualnovel.repository.api
|
||||||
|
|
||||||
import com.remax.visualnovel.entity.response.Book
|
import com.remax.visualnovel.api.service.ActorsService
|
||||||
import com.remax.visualnovel.repository.api.base.BaseRepository
|
import com.remax.visualnovel.entity.request.ParamActorList
|
||||||
import com.remax.visualnovel.api.service.BookService
|
import com.remax.visualnovel.repository.api.base.BaseRepositoryNew
|
||||||
import com.remax.visualnovel.entity.response.base.Response
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
||||||
class ActorsRepository @Inject constructor(private val bookService: BookService) : BaseRepository() {
|
class ActorsRepository @Inject constructor(private val mActorsService: ActorsService) : BaseRepositoryNew() {
|
||||||
suspend fun getBooks(): Response<Book> {
|
suspend fun getActorTags() = executeHttp {
|
||||||
return bookService.getBooks()
|
mActorsService.requestActorTags()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getActorList(param: ParamActorList) = executeHttp {
|
||||||
|
mActorsService.requestActorList(param)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
package com.remax.visualnovel.repository.api.base
|
||||||
|
|
||||||
|
|
||||||
|
import com.remax.visualnovel.R
|
||||||
|
import com.remax.visualnovel.app.base.app.CommonApplicationProxy
|
||||||
|
import com.remax.visualnovel.constant.StatusCode
|
||||||
|
import com.remax.visualnovel.entity.response.basenew.ApiEmptyResponse
|
||||||
|
import com.remax.visualnovel.entity.response.basenew.ApiFailedResponse
|
||||||
|
import com.remax.visualnovel.entity.response.basenew.ApiSuccessResponse
|
||||||
|
import com.remax.visualnovel.entity.response.basenew.ResponseNew
|
||||||
|
import com.remax.visualnovel.extension.toast
|
||||||
|
import com.remax.visualnovel.manager.login.LoginManager
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
|
open class BaseRepositoryNew {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果不需要检查登录,那么在未登录的情况下 直接返回Success
|
||||||
|
* @param checkLogin 检查登录,默认都需要检查
|
||||||
|
*/
|
||||||
|
suspend fun <T> executeHttp(checkLogin: Boolean = true, block: suspend () -> ResponseNew<T>): ResponseNew<T> {
|
||||||
|
return if (!checkLogin) {
|
||||||
|
if (LoginManager.isLogin) {
|
||||||
|
execute(block)
|
||||||
|
} else {
|
||||||
|
ApiSuccessResponse()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
execute(block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun <T> execute(block: suspend () -> ResponseNew<T>): ResponseNew<T> {
|
||||||
|
return try {
|
||||||
|
val data = block.invoke()
|
||||||
|
handleHttpOk(data)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
handleHttpError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 非后台返回错误,捕获到的异常
|
||||||
|
*/
|
||||||
|
private fun <T> handleHttpError(e: Throwable): ApiFailedResponse<T> {
|
||||||
|
Timber.e("responseAsync error -> ${e.localizedMessage}")
|
||||||
|
val errorMsg = CommonApplicationProxy.application.getString(R.string.your_network_error)
|
||||||
|
return ApiFailedResponse(message = errorMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* http返回200,还要判断后端业务层isSuccess
|
||||||
|
*/
|
||||||
|
private fun <T> handleHttpOk(response: ResponseNew<T>): ResponseNew<T> {
|
||||||
|
return when {
|
||||||
|
//后端业务正常
|
||||||
|
response.isOk -> {
|
||||||
|
getHttpSuccessResponse(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
//登录超时
|
||||||
|
response.code == /*StatusCode.TOKEN_EXPIRED.code*/403 -> {
|
||||||
|
CommonApplicationProxy.application.toast(response.message)
|
||||||
|
LoginManager.logout()
|
||||||
|
ApiFailedResponse(response.code, response.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
//余额不足
|
||||||
|
response.code == /*StatusCode.INSUFFICIENT_BALANCE.code*/ 610 -> {
|
||||||
|
/*WalletManager.refreshWallet()
|
||||||
|
WalletManager.showChargeDialog()*/
|
||||||
|
ApiFailedResponse(response.code, response.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
ApiFailedResponse(response.code, response.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成功和数据为空的处理
|
||||||
|
*/
|
||||||
|
private fun <T> getHttpSuccessResponse(response: ResponseNew<T>): ResponseNew<T> {
|
||||||
|
val data = response.data
|
||||||
|
return if (data == null) {
|
||||||
|
ApiEmptyResponse()
|
||||||
|
} else {
|
||||||
|
ApiSuccessResponse(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -15,9 +15,13 @@ import com.remax.visualnovel.entity.response.ChatBubble
|
||||||
import com.remax.visualnovel.entity.response.ChatHistory
|
import com.remax.visualnovel.entity.response.ChatHistory
|
||||||
import com.remax.visualnovel.entity.response.ChatMode
|
import com.remax.visualnovel.entity.response.ChatMode
|
||||||
import com.remax.visualnovel.entity.response.ChatSound
|
import com.remax.visualnovel.entity.response.ChatSound
|
||||||
|
import com.remax.visualnovel.extension.showConfirmDialog
|
||||||
import com.remax.visualnovel.ui.chat.ui.expandableSelector.ExpandAiModelSelectView
|
import com.remax.visualnovel.ui.chat.ui.expandableSelector.ExpandAiModelSelectView
|
||||||
|
import com.remax.visualnovel.ui.chat.ui.expandableSelector.ExpandBubbleSelectView
|
||||||
|
import com.remax.visualnovel.ui.chat.ui.expandableSelector.ExpandChatModeSelectView
|
||||||
import com.remax.visualnovel.ui.chat.ui.expandableSelector.ExpandSoundSelectView
|
import com.remax.visualnovel.ui.chat.ui.expandableSelector.ExpandSoundSelectView
|
||||||
import com.remax.visualnovel.ui.chat.ui.expandableSelector.SelectorItem
|
import com.remax.visualnovel.ui.chat.ui.expandableSelector.SelectorItem
|
||||||
|
import com.remax.visualnovel.widget.imageviewer.utils.activity
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
class ChatSettingView @JvmOverloads constructor(
|
class ChatSettingView @JvmOverloads constructor(
|
||||||
|
|
@ -44,10 +48,30 @@ class ChatSettingView @JvmOverloads constructor(
|
||||||
initBubbleSelectView()
|
initBubbleSelectView()
|
||||||
initBackgroundSelectView()
|
initBackgroundSelectView()
|
||||||
initHistoryListView()
|
initHistoryListView()
|
||||||
|
initOtherClickEvent()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initOtherClickEvent() {
|
||||||
|
with (mBinding) {
|
||||||
|
llDelete.setOnClickListener {
|
||||||
|
activity?.showConfirmDialog (
|
||||||
|
dialogBg = R.drawable.chat_delete_bg,
|
||||||
|
R.string.delete_chat,
|
||||||
|
R.string.delete_chat_hint,
|
||||||
|
R.mipmap.setting_delete,
|
||||||
|
null,
|
||||||
|
R.string.no,
|
||||||
|
R.string.sure,
|
||||||
|
{
|
||||||
|
// TODO - do delete the chat
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun scroll2Position(targetSubView: View) {
|
private fun scroll2Position(targetSubView: View) {
|
||||||
mBinding.scrollView.smoothScrollTo(0, targetSubView.top)
|
mBinding.scrollView.scroll2ChildView(targetSubView)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun initAiModelSelectorView() {
|
fun initAiModelSelectorView() {
|
||||||
|
|
@ -123,11 +147,26 @@ class ChatSettingView @JvmOverloads constructor(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
//aiModelSelector.setOnItemSelectedListener()
|
with(mBinding.chatModelSelector) {
|
||||||
mBinding.chatModelSelector.setTitleIcon(R.mipmap.setting_chat_mode_icon)
|
setTitleIcon(R.mipmap.setting_chat_mode_icon)
|
||||||
mBinding.chatModelSelector.setTitleText(R.string.chat_mode)
|
setTitleText(R.string.chat_mode)
|
||||||
mBinding.chatModelSelector.setItems(items)
|
setItems(items)
|
||||||
mBinding.chatModelSelector.selectItem(0)
|
selectItem(0)
|
||||||
|
setOnEventListener(object : ExpandChatModeSelectView.IEventListener {
|
||||||
|
override fun onItemSelected(
|
||||||
|
position: Int,
|
||||||
|
item: ChatMode
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onExpanded(isExpanded: Boolean) {
|
||||||
|
if (isExpanded)
|
||||||
|
scroll2Position(this@with)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -223,7 +262,23 @@ class ChatSettingView @JvmOverloads constructor(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
mBinding.bubbleSelectView.setItems(items)
|
with(mBinding.bubbleSelectView) {
|
||||||
|
setTitleText(R.string.chat_bubble)
|
||||||
|
setItems(items)
|
||||||
|
setOnEventListener(object : ExpandBubbleSelectView.IEventListener {
|
||||||
|
override fun onItemSelected(
|
||||||
|
position: Int,
|
||||||
|
item: ChatBubble
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onExpanded(isExpanded: Boolean) {
|
||||||
|
if (isExpanded)
|
||||||
|
scroll2Position(this@with)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun initBackgroundSelectView() {
|
fun initBackgroundSelectView() {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.remax.visualnovel.ui.chat.ui
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.View
|
||||||
|
import android.graphics.Rect
|
||||||
|
import androidx.core.widget.NestedScrollView
|
||||||
|
|
||||||
|
class MyScrollView @JvmOverloads constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyleAttr: Int = 0
|
||||||
|
) : NestedScrollView(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
|
fun scroll2ChildView(child: View) {
|
||||||
|
val tempRect = Rect()
|
||||||
|
child.getDrawingRect(tempRect)
|
||||||
|
offsetDescendantRectToMyCoords(child, tempRect)
|
||||||
|
val scrollDelta: Int = computeScrollDeltaToGetChildRectOnScreen(tempRect)
|
||||||
|
/*if (scrollDelta != 0) {
|
||||||
|
scrollBy(0, scrollDelta)
|
||||||
|
}*/
|
||||||
|
smoothScrollTo(0, tempRect.top)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -34,6 +34,10 @@ class ExpandAiModelSelectView @JvmOverloads constructor(
|
||||||
fun onExpanded(isExpanded: Boolean)
|
fun onExpanded(isExpanded: Boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setOnEventListener(listener: IEventListener) {
|
||||||
|
this.mEventListener = listener
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
initView(context, attrs)
|
initView(context, attrs)
|
||||||
}
|
}
|
||||||
|
|
@ -205,9 +209,7 @@ class ExpandAiModelSelectView @JvmOverloads constructor(
|
||||||
return height
|
return height
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setOnEventListener(listener: IEventListener) {
|
|
||||||
this.mEventListener = listener
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -25,7 +25,16 @@ class ExpandBubbleSelectView @JvmOverloads constructor(
|
||||||
|
|
||||||
private var isExpanded = false
|
private var isExpanded = false
|
||||||
private var animationDuration = 300
|
private var animationDuration = 300
|
||||||
private var itemSelectedListener: OnItemSelectedListener? = null
|
private var mEventListener: IEventListener? = null
|
||||||
|
|
||||||
|
interface IEventListener {
|
||||||
|
fun onItemSelected(position: Int, item: ChatBubble)
|
||||||
|
fun onExpanded(isExpanded: Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setOnEventListener(listener: IEventListener) {
|
||||||
|
this.mEventListener = listener
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
initView(context, attrs)
|
initView(context, attrs)
|
||||||
|
|
@ -63,6 +72,7 @@ class ExpandBubbleSelectView @JvmOverloads constructor(
|
||||||
|
|
||||||
fun toggle() {
|
fun toggle() {
|
||||||
if (isExpanded) collapse() else expand()
|
if (isExpanded) collapse() else expand()
|
||||||
|
mEventListener?.onExpanded(isExpanded)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun expand() {
|
fun expand() {
|
||||||
|
|
@ -128,11 +138,5 @@ class ExpandBubbleSelectView @JvmOverloads constructor(
|
||||||
return height
|
return height
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setOnItemSelectedListener(listener: OnItemSelectedListener) {
|
|
||||||
this.itemSelectedListener = listener
|
|
||||||
}
|
|
||||||
|
|
||||||
interface OnItemSelectedListener {
|
|
||||||
fun onItemSelected(position: Int, item: SelectorItem)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -29,7 +29,16 @@ class ExpandChatModeSelectView @JvmOverloads constructor(
|
||||||
private var isExpanded = false
|
private var isExpanded = false
|
||||||
private var animationDuration = 300
|
private var animationDuration = 300
|
||||||
private var items: List<ChatMode> = emptyList()
|
private var items: List<ChatMode> = emptyList()
|
||||||
private var itemSelectedListener: OnItemSelectedListener? = null
|
|
||||||
|
private var mEventListener: IEventListener? = null
|
||||||
|
interface IEventListener {
|
||||||
|
fun onItemSelected(position: Int, item: ChatMode)
|
||||||
|
fun onExpanded(isExpanded: Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setOnEventListener(listener: IEventListener) {
|
||||||
|
this.mEventListener = listener
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
initView(context, attrs)
|
initView(context, attrs)
|
||||||
|
|
@ -96,7 +105,7 @@ class ExpandChatModeSelectView @JvmOverloads constructor(
|
||||||
|
|
||||||
binding.root.setOnClickListener {
|
binding.root.setOnClickListener {
|
||||||
selectItem(position)
|
selectItem(position)
|
||||||
itemSelectedListener?.onItemSelected(position, item)
|
mEventListener?.onItemSelected(position, item)
|
||||||
}
|
}
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
@ -129,6 +138,7 @@ class ExpandChatModeSelectView @JvmOverloads constructor(
|
||||||
|
|
||||||
fun toggle() {
|
fun toggle() {
|
||||||
if (isExpanded) collapse() else expand()
|
if (isExpanded) collapse() else expand()
|
||||||
|
mEventListener?.onExpanded(isExpanded)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun expand() {
|
fun expand() {
|
||||||
|
|
@ -194,11 +204,5 @@ class ExpandChatModeSelectView @JvmOverloads constructor(
|
||||||
return height
|
return height
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setOnItemSelectedListener(listener: OnItemSelectedListener) {
|
|
||||||
this.itemSelectedListener = listener
|
|
||||||
}
|
|
||||||
|
|
||||||
interface OnItemSelectedListener {
|
|
||||||
fun onItemSelected(position: Int, item: ChatMode)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -112,7 +112,7 @@ class ExpandSoundSubView @JvmOverloads constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
tvSoundName.text = item.name
|
tvSoundName.text = item.name
|
||||||
itemRoot.setBgColorDirectly(bgColor = ResUtil.getColor(if (item.isMale) R.color.male_bg else R.color.female_bg), radius = ResUtil.getPixelSize(R.dimen.dp_10).toFloat())
|
itemRoot.setBgColorDirectly(bgColor = ResUtil.getColor(if (item.isMale) R.color.male_bg else R.color.female_bg), radius = ResUtil.getPixelSize(R.dimen.dp_10).toFloat(), bgDrawable = null)
|
||||||
tvSoundDescrible.setTextColor(ResUtil.getColor(if (item.isMale) R.color.male_text_color else R.color.female_text_color))
|
tvSoundDescrible.setTextColor(ResUtil.getColor(if (item.isMale) R.color.male_text_color else R.color.female_text_color))
|
||||||
tvSoundDescrible.text = item.description
|
tvSoundDescrible.text = item.description
|
||||||
ivSelect.setImageResource(if (item.select) R.drawable.sound_item_selected else R.drawable.sound_item_unselected)
|
ivSelect.setImageResource(if (item.select) R.drawable.sound_item_selected else R.drawable.sound_item_unselected)
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,14 @@ import androidx.recyclerview.widget.DefaultItemAnimator
|
||||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager
|
import androidx.recyclerview.widget.StaggeredGridLayoutManager
|
||||||
import com.alibaba.android.arouter.facade.annotation.Route
|
import com.alibaba.android.arouter.facade.annotation.Route
|
||||||
import com.alibaba.android.arouter.launcher.ARouter
|
import com.alibaba.android.arouter.launcher.ARouter
|
||||||
|
import com.chad.library.adapter.base.loadmore.LoadMoreStatus
|
||||||
import com.dylanc.loadingstateview.BgColorType
|
import com.dylanc.loadingstateview.BgColorType
|
||||||
import com.remax.visualnovel.R
|
import com.remax.visualnovel.R
|
||||||
import com.remax.visualnovel.app.base.BaseBindingFragment
|
import com.remax.visualnovel.app.base.BaseBindingFragment
|
||||||
import com.remax.visualnovel.configs.NovelApplication
|
import com.remax.visualnovel.configs.NovelApplication
|
||||||
import com.remax.visualnovel.databinding.FragmentMainActorBinding
|
import com.remax.visualnovel.databinding.FragmentMainActorBinding
|
||||||
|
import com.remax.visualnovel.entity.request.ParamActorList
|
||||||
|
import com.remax.visualnovel.extension.launchAndCollect2
|
||||||
import com.remax.visualnovel.utils.Routers
|
import com.remax.visualnovel.utils.Routers
|
||||||
import com.remax.visualnovel.utils.StatusBarUtil3
|
import com.remax.visualnovel.utils.StatusBarUtil3
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
@ -26,7 +29,10 @@ import kotlin.math.max
|
||||||
class ActorListFragment : BaseBindingFragment<FragmentMainActorBinding>() {
|
class ActorListFragment : BaseBindingFragment<FragmentMainActorBinding>() {
|
||||||
|
|
||||||
private lateinit var mActorAdapter: ActorsAdapter
|
private lateinit var mActorAdapter: ActorsAdapter
|
||||||
private val actorsViewModel by viewModels<ActorListViewModel>()
|
private val mActorsModel by viewModels<ActorsViewModel>()
|
||||||
|
private var mLoadedPageIndex = 0
|
||||||
|
private val mRequestParam by lazy { ParamActorList() }
|
||||||
|
|
||||||
|
|
||||||
override fun onCreated(bundle: Bundle?) {
|
override fun onCreated(bundle: Bundle?) {
|
||||||
setUI()
|
setUI()
|
||||||
|
|
@ -36,6 +42,10 @@ class ActorListFragment : BaseBindingFragment<FragmentMainActorBinding>() {
|
||||||
return BgColorType.TRANSPARENT
|
return BgColorType.TRANSPARENT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun lazyInit() {
|
||||||
|
getActorList(true, showLoading = false)
|
||||||
|
}
|
||||||
|
|
||||||
private fun setUI() {
|
private fun setUI() {
|
||||||
with(binding.root) {
|
with(binding.root) {
|
||||||
setPadding(
|
setPadding(
|
||||||
|
|
@ -59,11 +69,22 @@ class ActorListFragment : BaseBindingFragment<FragmentMainActorBinding>() {
|
||||||
mActorsRv.addItemDecoration(GridSpacingItemDecoration(16))
|
mActorsRv.addItemDecoration(GridSpacingItemDecoration(16))
|
||||||
mActorsRv.setHasFixedSize(true)
|
mActorsRv.setHasFixedSize(true)
|
||||||
mActorsRv.itemAnimator = DefaultItemAnimator()
|
mActorsRv.itemAnimator = DefaultItemAnimator()
|
||||||
|
|
||||||
mActorAdapter = ActorsAdapter()
|
mActorAdapter = ActorsAdapter()
|
||||||
mActorsRv.adapter = mActorAdapter
|
mActorsRv.adapter = mActorAdapter
|
||||||
|
|
||||||
val characterList = createSampleData()
|
val characterList = createSampleData()
|
||||||
mActorAdapter.setList(characterList)
|
with(mActorAdapter) {
|
||||||
|
setList(characterList)
|
||||||
|
loadMoreModule.setOnLoadMoreListener {
|
||||||
|
getActorList(false, showLoading = false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
with(refreshLayout) {
|
||||||
|
onRefresh {
|
||||||
|
getActorList(true, showLoading = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,6 +115,55 @@ class ActorListFragment : BaseBindingFragment<FragmentMainActorBinding>() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun getActorList(isRefresh: Boolean, showLoading: Boolean) {
|
||||||
|
if (isRefresh) {
|
||||||
|
mLoadedPageIndex = 0
|
||||||
|
}
|
||||||
|
mRequestParam.index = mLoadedPageIndex + 1
|
||||||
|
|
||||||
|
launchAndCollect2({
|
||||||
|
mActorsModel.getActorList(mRequestParam)
|
||||||
|
}, showLoading = showLoading) {
|
||||||
|
onSuccess = {
|
||||||
|
val data = it ?: emptyList()
|
||||||
|
with(mActorAdapter) {
|
||||||
|
/*if (isRefresh) {
|
||||||
|
setList(data)
|
||||||
|
setMyEmptyView(R.string.no_character_yet, topMargin = 60)
|
||||||
|
} else {
|
||||||
|
addData(data)
|
||||||
|
loadMoreModule.loadMoreComplete()
|
||||||
|
}
|
||||||
|
if (data.size < PageQuery.DEFAULT_PAGE_SIZE) {
|
||||||
|
loadMoreModule.loadMoreEnd()
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
mLoadedPageIndex++
|
||||||
|
}
|
||||||
|
|
||||||
|
onComplete = {
|
||||||
|
binding.refreshLayout.finishRefresh()
|
||||||
|
mActorAdapter.loadMoreModule.loadMoreComplete()
|
||||||
|
}
|
||||||
|
|
||||||
|
onFailed = { errorCode, errorMsg ->
|
||||||
|
var temp = 100
|
||||||
|
}
|
||||||
|
|
||||||
|
onFailedWithData = {
|
||||||
|
var temp = 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private fun createSampleData(): List<ActorItem> {
|
private fun createSampleData(): List<ActorItem> {
|
||||||
return listOf(
|
return listOf(
|
||||||
ActorItem(
|
ActorItem(
|
||||||
|
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
package com.remax.visualnovel.ui.main.actor
|
|
||||||
|
|
||||||
|
|
||||||
import com.remax.visualnovel.entity.response.Book
|
|
||||||
import com.remax.visualnovel.app.viewmodel.base.BaseViewModel
|
|
||||||
import com.remax.visualnovel.entity.response.base.Response
|
|
||||||
import com.remax.visualnovel.repository.api.ActorsRepository
|
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
|
||||||
import kotlinx.coroutines.flow.asSharedFlow
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@HiltViewModel
|
|
||||||
class ActorListViewModel @Inject constructor(private val chatRepository: ActorsRepository) : BaseViewModel() {
|
|
||||||
|
|
||||||
private val _msgStatFlow = MutableSharedFlow<Response<Book>>()
|
|
||||||
val msgStatFlow = _msgStatFlow.asSharedFlow()
|
|
||||||
|
|
||||||
suspend fun getMessageStat() {
|
|
||||||
_msgStatFlow.emit(chatRepository.getBooks())
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.remax.visualnovel.ui.main.actor
|
||||||
|
|
||||||
|
|
||||||
|
import com.remax.visualnovel.app.viewmodel.base.BaseViewModel
|
||||||
|
import com.remax.visualnovel.entity.request.ParamActorList
|
||||||
|
import com.remax.visualnovel.entity.response.basenew.ResponseNew
|
||||||
|
import com.remax.visualnovel.entity.response.ActorBean
|
||||||
|
import com.remax.visualnovel.repository.api.ActorsRepository
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class ActorsViewModel @Inject constructor(private val mActorsRepository: ActorsRepository) : BaseViewModel() {
|
||||||
|
|
||||||
|
private val _actorsStatFlow = MutableSharedFlow<ResponseNew<List<ActorBean>>>()
|
||||||
|
val actorsStatFlow = _actorsStatFlow.asSharedFlow()
|
||||||
|
|
||||||
|
suspend fun getActorList(param: ParamActorList) = mActorsRepository.getActorList(param)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,7 @@ import android.graphics.Outline
|
||||||
import android.graphics.Shader
|
import android.graphics.Shader
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.graphics.drawable.GradientDrawable
|
import android.graphics.drawable.GradientDrawable
|
||||||
|
import android.graphics.drawable.LayerDrawable
|
||||||
import android.graphics.drawable.StateListDrawable
|
import android.graphics.drawable.StateListDrawable
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
|
|
@ -184,10 +185,12 @@ fun View.changeBackground(customViewToken: CustomViewToken) {
|
||||||
customViewToken.run {
|
customViewToken.run {
|
||||||
if (advRadius > 0 || advStrokeWidth > 0 ||
|
if (advRadius > 0 || advStrokeWidth > 0 ||
|
||||||
advTopRightRadius > 0 || advTopLeftRadius > 0 ||
|
advTopRightRadius > 0 || advTopLeftRadius > 0 ||
|
||||||
advBottomLeftRadius > 0 || advBottomRightRadius > 0) {
|
advBottomLeftRadius > 0 || advBottomRightRadius > 0
|
||||||
|
|| advBgDrawable != null) {
|
||||||
setBgColorDirectly(
|
setBgColorDirectly(
|
||||||
bgColor = advBgColor,
|
bgColor = advBgColor,
|
||||||
radius = advRadius,
|
radius = advRadius,
|
||||||
|
bgDrawable = advBgDrawable,
|
||||||
topLeftRadius = if (advTopLeftRadius > 0F) advTopLeftRadius else advRadius,
|
topLeftRadius = if (advTopLeftRadius > 0F) advTopLeftRadius else advRadius,
|
||||||
topRightRadius = if (advTopRightRadius > 0F) advTopRightRadius else advRadius,
|
topRightRadius = if (advTopRightRadius > 0F) advTopRightRadius else advRadius,
|
||||||
bottomRightRadius = if (advBottomRightRadius > 0F) advBottomRightRadius else advRadius,
|
bottomRightRadius = if (advBottomRightRadius > 0F) advBottomRightRadius else advRadius,
|
||||||
|
|
@ -522,6 +525,7 @@ fun View.getGradientDrawable(
|
||||||
fun View.setBgColorDirectly(
|
fun View.setBgColorDirectly(
|
||||||
bgColor: Int = 0,
|
bgColor: Int = 0,
|
||||||
radius: Float = 0F,
|
radius: Float = 0F,
|
||||||
|
bgDrawable: Drawable?,
|
||||||
topLeftRadius: Float = radius,
|
topLeftRadius: Float = radius,
|
||||||
topRightRadius: Float = radius,
|
topRightRadius: Float = radius,
|
||||||
bottomRightRadius: Float = radius,
|
bottomRightRadius: Float = radius,
|
||||||
|
|
@ -532,6 +536,7 @@ fun View.setBgColorDirectly(
|
||||||
val resDrawable = StateListDrawable()
|
val resDrawable = StateListDrawable()
|
||||||
val normalDrawable = getGradientDrawableDirectly(
|
val normalDrawable = getGradientDrawableDirectly(
|
||||||
bgColor,
|
bgColor,
|
||||||
|
bgDrawable,
|
||||||
topLeftRadius,
|
topLeftRadius,
|
||||||
topRightRadius,
|
topRightRadius,
|
||||||
bottomRightRadius,
|
bottomRightRadius,
|
||||||
|
|
@ -546,6 +551,7 @@ fun View.setBgColorDirectly(
|
||||||
|
|
||||||
fun View.getGradientDrawableDirectly(
|
fun View.getGradientDrawableDirectly(
|
||||||
bgColor: Int = 0,
|
bgColor: Int = 0,
|
||||||
|
bgDrawable: Drawable?,
|
||||||
topLeftRadius: Float = 0F,
|
topLeftRadius: Float = 0F,
|
||||||
topRightRadius: Float = 0F,
|
topRightRadius: Float = 0F,
|
||||||
bottomRightRadius: Float = 0F,
|
bottomRightRadius: Float = 0F,
|
||||||
|
|
@ -571,6 +577,6 @@ fun View.getGradientDrawableDirectly(
|
||||||
bottomLeftRadius,
|
bottomLeftRadius,
|
||||||
bottomLeftRadius
|
bottomLeftRadius
|
||||||
)
|
)
|
||||||
return gradientDrawable
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return if (bgDrawable != null) LayerDrawable(arrayOf(bgDrawable, gradientDrawable)) else gradientDrawable
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.remax.visualnovel.widget.uitoken.bean
|
package com.remax.visualnovel.widget.uitoken.bean
|
||||||
|
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by HJW on 2022/9/1
|
* Created by HJW on 2022/9/1
|
||||||
|
|
@ -47,6 +48,7 @@ data class CustomViewToken(
|
||||||
|
|
||||||
//---------------------- New added -----------------------
|
//---------------------- New added -----------------------
|
||||||
var advBgColor:Int = Color.TRANSPARENT,
|
var advBgColor:Int = Color.TRANSPARENT,
|
||||||
|
var advBgDrawable: Drawable? = null,
|
||||||
var advRadius:Float = 0F,
|
var advRadius:Float = 0F,
|
||||||
var advTopLeftRadius:Float = 0F,
|
var advTopLeftRadius:Float = 0F,
|
||||||
var advTopRightRadius:Float = 0F,
|
var advTopRightRadius:Float = 0F,
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,7 @@ open class UITokenTextView @JvmOverloads constructor(context: Context, private v
|
||||||
|
|
||||||
//<!-- new added 2025.10.29 -->
|
//<!-- new added 2025.10.29 -->
|
||||||
advBgColor = getColor(R.styleable.UITokenTextView_advBgColor, advBgColor)
|
advBgColor = getColor(R.styleable.UITokenTextView_advBgColor, advBgColor)
|
||||||
|
advBgDrawable = getDrawable(R.styleable.UITokenTextView_advBgDrawable)
|
||||||
advRadius = getDimension(R.styleable.UITokenTextView_advRadius, advRadius)
|
advRadius = getDimension(R.styleable.UITokenTextView_advRadius, advRadius)
|
||||||
advTopLeftRadius = getDimension(R.styleable.UITokenTextView_advTopLeftRadius, advTopLeftRadius)
|
advTopLeftRadius = getDimension(R.styleable.UITokenTextView_advTopLeftRadius, advTopLeftRadius)
|
||||||
advTopRightRadius = getDimension(R.styleable.UITokenTextView_advTopRightRadius, advTopRightRadius)
|
advTopRightRadius = getDimension(R.styleable.UITokenTextView_advTopRightRadius, advTopRightRadius)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle"
|
||||||
|
>
|
||||||
|
<size
|
||||||
|
android:width="270dp"
|
||||||
|
android:height="300dp"
|
||||||
|
/>
|
||||||
|
<corners android:radius="25dp" />
|
||||||
|
<gradient android:type="linear"
|
||||||
|
android:angle="270"
|
||||||
|
android:startColor="#ffffd4d4"
|
||||||
|
android:endColor="#ffffffff"
|
||||||
|
/>
|
||||||
|
</shape>
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.remax.visualnovel.widget.uitoken.view.UITokenConstraintLayout
|
||||||
|
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:padding="@dimen/dp_20"
|
||||||
|
app:advRadius="@dimen/dp_25"
|
||||||
|
app:advBgDrawable="@drawable/chat_delete_bg">
|
||||||
|
|
||||||
|
|
||||||
|
<com.remax.visualnovel.widget.uitoken.view.UITokenImageView
|
||||||
|
android:id="@+id/iv_top"
|
||||||
|
android:layout_width="@dimen/dp_98"
|
||||||
|
android:layout_height="@dimen/dp_98"
|
||||||
|
android:padding="@dimen/dp_20"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:src="@mipmap/setting_delete"
|
||||||
|
android:tint="@color/white"
|
||||||
|
app:advBgColor="@color/red_ff3b30"
|
||||||
|
app:advRadius="@dimen/dp_49"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<com.remax.visualnovel.widget.uitoken.view.UITokenTextView
|
||||||
|
android:id="@+id/tv_title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/iv_top"
|
||||||
|
android:layout_marginTop="@dimen/dp_10"
|
||||||
|
android:text="title"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="@dimen/sp_18"
|
||||||
|
android:textColor="@color/black"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<com.remax.visualnovel.widget.uitoken.view.UITokenTextView
|
||||||
|
android:id="@+id/tv_description"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/tv_title"
|
||||||
|
android:layout_marginTop="@dimen/dp_20"
|
||||||
|
android:text="des"
|
||||||
|
android:textSize="@dimen/sp_14"
|
||||||
|
android:textColor="@color/gray9"
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
<com.remax.visualnovel.widget.uitoken.view.UITokenLinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/tv_description"
|
||||||
|
android:layout_marginTop="@dimen/dp_50"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
<com.remax.visualnovel.widget.uitoken.view.UITokenTextView
|
||||||
|
android:id="@+id/tv_left"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="@dimen/dp_10"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/tv_title"
|
||||||
|
android:layout_marginTop="@dimen/dp_20"
|
||||||
|
android:text="@string/no"
|
||||||
|
android:textSize="@dimen/sp_15"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/black"
|
||||||
|
android:padding="@dimen/dp_15"
|
||||||
|
app:advStrokeColor="@color/gray9"
|
||||||
|
app:advStrokeWidth="@dimen/dp_2"
|
||||||
|
app:advRadius="@dimen/dp_25"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<com.remax.visualnovel.widget.uitoken.view.UITokenTextView
|
||||||
|
android:id="@+id/tv_right"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="@dimen/dp_10"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/tv_title"
|
||||||
|
android:layout_marginTop="@dimen/dp_20"
|
||||||
|
android:text="@string/sure"
|
||||||
|
android:textSize="@dimen/sp_15"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:padding="@dimen/dp_15"
|
||||||
|
app:advBgColor="@color/red_ff3b30"
|
||||||
|
app:advRadius="@dimen/dp_25"
|
||||||
|
/>
|
||||||
|
</com.remax.visualnovel.widget.uitoken.view.UITokenLinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
</com.remax.visualnovel.widget.uitoken.view.UITokenConstraintLayout>
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout
|
<com.remax.visualnovel.widget.uitoken.view.UITokenLinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
@ -27,12 +27,17 @@
|
||||||
app:tag_expand_drawable="@mipmap/tag_flow_expand"
|
app:tag_expand_drawable="@mipmap/tag_flow_expand"
|
||||||
app:tag_shrink_drawable="@mipmap/tag_flow_shrink" />
|
app:tag_shrink_drawable="@mipmap/tag_flow_shrink" />
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<com.drake.brv.PageRefreshLayout
|
||||||
android:id="@+id/m_actors_rv"
|
android:id="@+id/refreshLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="match_parent"
|
||||||
android:layout_weight="1"
|
android:layout_marginTop="@dimen/dp_4">
|
||||||
android:layout_marginTop="@dimen/dp_4"
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
/>
|
android:id="@+id/m_actors_rv"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
/>
|
||||||
|
</com.drake.brv.PageRefreshLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
</com.remax.visualnovel.widget.uitoken.view.UITokenLinearLayout>
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@
|
||||||
</com.remax.visualnovel.widget.uitoken.view.UITokenFrameLayout>
|
</com.remax.visualnovel.widget.uitoken.view.UITokenFrameLayout>
|
||||||
|
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
<com.remax.visualnovel.ui.chat.ui.MyScrollView
|
||||||
android:id="@+id/scroll_view"
|
android:id="@+id/scroll_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
|
@ -349,7 +349,7 @@
|
||||||
|
|
||||||
</com.remax.visualnovel.widget.uitoken.view.UITokenLinearLayout>
|
</com.remax.visualnovel.widget.uitoken.view.UITokenLinearLayout>
|
||||||
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
</com.remax.visualnovel.ui.chat.ui.MyScrollView>
|
||||||
|
|
||||||
|
|
||||||
</com.remax.visualnovel.widget.uitoken.view.UITokenRelativeLayout>
|
</com.remax.visualnovel.widget.uitoken.view.UITokenRelativeLayout>
|
||||||
|
|
@ -61,6 +61,7 @@
|
||||||
|
|
||||||
<!-- new added 2025.10.29 -->
|
<!-- new added 2025.10.29 -->
|
||||||
<attr name="advBgColor" format="color" />
|
<attr name="advBgColor" format="color" />
|
||||||
|
<attr name="advBgDrawable" format="reference" />
|
||||||
<attr name="advRadius" format="dimension" />
|
<attr name="advRadius" format="dimension" />
|
||||||
<attr name="advTopLeftRadius" format="dimension" />
|
<attr name="advTopLeftRadius" format="dimension" />
|
||||||
<attr name="advTopRightRadius" format="dimension" />
|
<attr name="advTopRightRadius" format="dimension" />
|
||||||
|
|
@ -112,6 +113,7 @@
|
||||||
|
|
||||||
<!-- new added 2025.10.29 -->
|
<!-- new added 2025.10.29 -->
|
||||||
<attr name="advBgColor" />
|
<attr name="advBgColor" />
|
||||||
|
<attr name="advBgDrawable" />
|
||||||
<attr name="advRadius" />
|
<attr name="advRadius" />
|
||||||
<attr name="advTopLeftRadius" />
|
<attr name="advTopLeftRadius" />
|
||||||
<attr name="advTopRightRadius" />
|
<attr name="advTopRightRadius" />
|
||||||
|
|
@ -150,6 +152,7 @@
|
||||||
|
|
||||||
<!-- new added 2025.10.29 -->
|
<!-- new added 2025.10.29 -->
|
||||||
<attr name="advBgColor" />
|
<attr name="advBgColor" />
|
||||||
|
<attr name="advBgDrawable" />
|
||||||
<attr name="advRadius" />
|
<attr name="advRadius" />
|
||||||
<attr name="advTopLeftRadius" />
|
<attr name="advTopLeftRadius" />
|
||||||
<attr name="advTopRightRadius" />
|
<attr name="advTopRightRadius" />
|
||||||
|
|
@ -183,6 +186,7 @@
|
||||||
|
|
||||||
<!-- new added 2025.10.29 -->
|
<!-- new added 2025.10.29 -->
|
||||||
<attr name="advBgColor" />
|
<attr name="advBgColor" />
|
||||||
|
<attr name="advBgDrawable" />
|
||||||
<attr name="advRadius" />
|
<attr name="advRadius" />
|
||||||
<attr name="advTopLeftRadius" />
|
<attr name="advTopLeftRadius" />
|
||||||
<attr name="advTopRightRadius" />
|
<attr name="advTopRightRadius" />
|
||||||
|
|
@ -216,6 +220,7 @@
|
||||||
|
|
||||||
<!-- new added 2025.10.29 -->
|
<!-- new added 2025.10.29 -->
|
||||||
<attr name="advBgColor" />
|
<attr name="advBgColor" />
|
||||||
|
<attr name="advBgDrawable" />
|
||||||
<attr name="advRadius" />
|
<attr name="advRadius" />
|
||||||
<attr name="advTopLeftRadius" />
|
<attr name="advTopLeftRadius" />
|
||||||
<attr name="advTopRightRadius" />
|
<attr name="advTopRightRadius" />
|
||||||
|
|
@ -249,6 +254,7 @@
|
||||||
|
|
||||||
<!-- new added 2025.10.29 -->
|
<!-- new added 2025.10.29 -->
|
||||||
<attr name="advBgColor" />
|
<attr name="advBgColor" />
|
||||||
|
<attr name="advBgDrawable" />
|
||||||
<attr name="advRadius" />
|
<attr name="advRadius" />
|
||||||
<attr name="advTopLeftRadius" />
|
<attr name="advTopLeftRadius" />
|
||||||
<attr name="advTopRightRadius" />
|
<attr name="advTopRightRadius" />
|
||||||
|
|
@ -282,6 +288,7 @@
|
||||||
|
|
||||||
<!-- new added 2025.10.29 -->
|
<!-- new added 2025.10.29 -->
|
||||||
<attr name="advBgColor" />
|
<attr name="advBgColor" />
|
||||||
|
<attr name="advBgDrawable" />
|
||||||
<attr name="advRadius" />
|
<attr name="advRadius" />
|
||||||
<attr name="advTopLeftRadius" />
|
<attr name="advTopLeftRadius" />
|
||||||
<attr name="advTopRightRadius" />
|
<attr name="advTopRightRadius" />
|
||||||
|
|
@ -315,6 +322,7 @@
|
||||||
|
|
||||||
<!-- new added 2025.10.29 -->
|
<!-- new added 2025.10.29 -->
|
||||||
<attr name="advBgColor" />
|
<attr name="advBgColor" />
|
||||||
|
<attr name="advBgDrawable" />
|
||||||
<attr name="advRadius" />
|
<attr name="advRadius" />
|
||||||
<attr name="advTopLeftRadius" />
|
<attr name="advTopLeftRadius" />
|
||||||
<attr name="advTopRightRadius" />
|
<attr name="advTopRightRadius" />
|
||||||
|
|
|
||||||
|
|
@ -486,5 +486,11 @@
|
||||||
<string name="call_connecting">Connecting...</string>
|
<string name="call_connecting">Connecting...</string>
|
||||||
<string name="call_fail_hint">Network connection issue. Please check your connection and try again.</string>
|
<string name="call_fail_hint">Network connection issue. Please check your connection and try again.</string>
|
||||||
<string name="start_new_chat">Start New Chat</string>
|
<string name="start_new_chat">Start New Chat</string>
|
||||||
|
<string name="delete_chat">DELETE CHAT</string>
|
||||||
|
<string name="delete_chat_hint">This will permanently remove all data. This action is irreversible. Are you sure?</string>
|
||||||
|
<string name="sure">SURE</string>
|
||||||
|
<string name="no">NO</string>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<network-security-config>
|
||||||
|
<domain-config cleartextTrafficPermitted="true">
|
||||||
|
<domain includeSubdomains="true">54.223.196.180</domain>
|
||||||
|
</domain-config>
|
||||||
|
</network-security-config>
|
||||||
Loading…
Reference in New Issue