From b535a7667de3c7e8174325b9bc3f3529d9a27d06 Mon Sep 17 00:00:00 2001 From: renhaoting <370797079@qq.com> Date: Tue, 21 Oct 2025 11:43:55 +0800 Subject: [PATCH] init project uploading --- VisualNovel/.idea/AndroidProjectSystem.xml | 6 + VisualNovel/app/src/main/AndroidManifest.xml | 27 ++ .../visualnovel/api/interceptor/util/AES.java | 76 ++++++ .../java/com/remax/visualnovel/app/AbsView.kt | 17 ++ .../activityresultapi/ActivityResultApiEx.kt | 230 ++++++++++++++++++ .../impl/ActivityLifecycleInitializer.kt | 101 ++++++++ .../visualnovel/entity/request/AIFeedback.kt | 34 +++ .../visualnovel/entity/response/AIDict.kt | 68 ++++++ .../visualnovel/entity/response/AIVoice.kt | 38 +++ .../visualnovel/entity/response/Album.kt | 50 ++++ .../repository/api/ActorsRepository.kt | 14 ++ .../ui/main/actor/ActorListFragment.kt | 78 ++++++ .../ui/main/actor/ActorListViewModel.kt | 26 ++ .../res/anim/act_slide_in_from_bottom.xml | 9 + .../res/anim/act_slide_out_from_bottom.xml | 9 + .../app/src/main/res/layout/activity_main.xml | 78 ++++++ .../src/main/AndroidManifest.xml | 5 + .../src/main/AndroidManifest.xml | 1 + .../src/main/AndroidManifest.xml | 5 + .../viewbinding/base/ActivityBinding.kt | 38 +++ .../src/main/AndroidManifest.xml | 5 + .../viewbinding/nonreflection/Activity.kt | 30 +++ 22 files changed, 945 insertions(+) create mode 100644 VisualNovel/.idea/AndroidProjectSystem.xml create mode 100644 VisualNovel/app/src/main/AndroidManifest.xml create mode 100644 VisualNovel/app/src/main/java/com/remax/visualnovel/api/interceptor/util/AES.java create mode 100644 VisualNovel/app/src/main/java/com/remax/visualnovel/app/AbsView.kt create mode 100644 VisualNovel/app/src/main/java/com/remax/visualnovel/app/activityresultapi/ActivityResultApiEx.kt create mode 100644 VisualNovel/app/src/main/java/com/remax/visualnovel/app/initializer/impl/ActivityLifecycleInitializer.kt create mode 100644 VisualNovel/app/src/main/java/com/remax/visualnovel/entity/request/AIFeedback.kt create mode 100644 VisualNovel/app/src/main/java/com/remax/visualnovel/entity/response/AIDict.kt create mode 100644 VisualNovel/app/src/main/java/com/remax/visualnovel/entity/response/AIVoice.kt create mode 100644 VisualNovel/app/src/main/java/com/remax/visualnovel/entity/response/Album.kt create mode 100644 VisualNovel/app/src/main/java/com/remax/visualnovel/repository/api/ActorsRepository.kt create mode 100644 VisualNovel/app/src/main/java/com/remax/visualnovel/ui/main/actor/ActorListFragment.kt create mode 100644 VisualNovel/app/src/main/java/com/remax/visualnovel/ui/main/actor/ActorListViewModel.kt create mode 100644 VisualNovel/app/src/main/res/anim/act_slide_in_from_bottom.xml create mode 100644 VisualNovel/app/src/main/res/anim/act_slide_out_from_bottom.xml create mode 100644 VisualNovel/app/src/main/res/layout/activity_main.xml create mode 100644 VisualNovel/loadingstateview-ktx/src/main/AndroidManifest.xml create mode 100644 VisualNovel/loadingstateview/src/main/AndroidManifest.xml create mode 100644 VisualNovel/viewbinding-base/src/main/AndroidManifest.xml create mode 100644 VisualNovel/viewbinding-base/src/main/java/com/dylanc/viewbinding/base/ActivityBinding.kt create mode 100644 VisualNovel/viewbinding-nonreflection-ktx/src/main/AndroidManifest.xml create mode 100644 VisualNovel/viewbinding-nonreflection-ktx/src/main/java/com/dylanc/viewbinding/nonreflection/Activity.kt diff --git a/VisualNovel/.idea/AndroidProjectSystem.xml b/VisualNovel/.idea/AndroidProjectSystem.xml new file mode 100644 index 0000000..4a53bee --- /dev/null +++ b/VisualNovel/.idea/AndroidProjectSystem.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/VisualNovel/app/src/main/AndroidManifest.xml b/VisualNovel/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..3cc5eb7 --- /dev/null +++ b/VisualNovel/app/src/main/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/VisualNovel/app/src/main/java/com/remax/visualnovel/api/interceptor/util/AES.java b/VisualNovel/app/src/main/java/com/remax/visualnovel/api/interceptor/util/AES.java new file mode 100644 index 0000000..0ff7863 --- /dev/null +++ b/VisualNovel/app/src/main/java/com/remax/visualnovel/api/interceptor/util/AES.java @@ -0,0 +1,76 @@ +package com.remax.visualnovel.api.interceptor.util; + +import java.nio.charset.StandardCharsets; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import timber.log.Timber; + +public class AES { + + private static final String KEY_SUFFIX = "90e8kDQUIWpXg8Jp"; + + /** + * For a 128-bit AES key you need 16 bytes. + * For a 256-bit AES key you need 32 bytes. + */ + public static String KEY; + + /** + * IV length: must be 16 bytes long + */ + public static final String IV = "sdf4ddfsFD86Vdf2"; + + private AES() { + } + + public static void genKey(String token) { + //token加密最多截取前32位 + String encodeStr = (token.length() > 32 ? token.substring(0, 32) : token) + KEY_SUFFIX; + KEY = Md5.encode(encodeStr).toUpperCase(); + Timber.d("genKey: %s", KEY); + } + + public static String encrypt(String data) { + return encrypt(data, KEY, IV); + } + + public static String desEncrypt(String data) { + return desEncrypt(data, KEY, IV); + } + + public static String encrypt(String data, String key, String initVector) { + try { + IvParameterSpec iv = new IvParameterSpec(initVector.getBytes(StandardCharsets.UTF_8)); + SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES"); + + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); + cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv); + + byte[] encrypted = cipher.doFinal(data.getBytes()); + return Base64.encode(encrypted); + } catch (Exception ex) { + ex.printStackTrace(); + return data; + } + } + + public static String desEncrypt(String data, String key, String initVector) { + try { + byte[] encrypted = Base64.decode(data); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); + SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES"); + IvParameterSpec ivParameterSpec = new IvParameterSpec(initVector.getBytes()); + cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec); + + byte[] original = cipher.doFinal(encrypted); + + return new String(original); + } catch (Exception e) { + e.printStackTrace(); + return data; + } + } +} diff --git a/VisualNovel/app/src/main/java/com/remax/visualnovel/app/AbsView.kt b/VisualNovel/app/src/main/java/com/remax/visualnovel/app/AbsView.kt new file mode 100644 index 0000000..3a9ac54 --- /dev/null +++ b/VisualNovel/app/src/main/java/com/remax/visualnovel/app/AbsView.kt @@ -0,0 +1,17 @@ +package com.remax.visualnovel.app + + +import androidx.lifecycle.LifecycleOwner + + +interface AbsView : LifecycleOwner{ + + fun showLoading() + + fun hideLoading() + + fun showToast(text: String?) + + fun showToast(resId: Int) +} + diff --git a/VisualNovel/app/src/main/java/com/remax/visualnovel/app/activityresultapi/ActivityResultApiEx.kt b/VisualNovel/app/src/main/java/com/remax/visualnovel/app/activityresultapi/ActivityResultApiEx.kt new file mode 100644 index 0000000..d3b216a --- /dev/null +++ b/VisualNovel/app/src/main/java/com/remax/visualnovel/app/activityresultapi/ActivityResultApiEx.kt @@ -0,0 +1,230 @@ +package com.remax.visualnovel.app.activityresultapi + +import android.content.Intent +import android.text.TextUtils +import android.widget.Toast +import androidx.activity.result.ActivityResult +import androidx.activity.result.ActivityResultCallback +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import com.alibaba.android.arouter.core.LogisticsCenter +import com.alibaba.android.arouter.exception.NoRouteFoundException +import com.alibaba.android.arouter.facade.Postcard +import com.alibaba.android.arouter.facade.callback.InterceptorCallback +import com.alibaba.android.arouter.facade.callback.NavigationCallback +import com.alibaba.android.arouter.facade.enums.RouteType +import com.alibaba.android.arouter.facade.service.DegradeService +import com.alibaba.android.arouter.facade.service.InterceptorService +import com.alibaba.android.arouter.facade.service.PretreatmentService +import com.alibaba.android.arouter.launcher.ARouter +import com.remax.visualnovel.app.initializer.impl.ActivityLifecycleInitializer + +/** + * 获取activityResultLauncher + */ +fun FragmentActivity.activityResultLauncher(): XActivityResultContract? { + val activityKey = intent.getStringExtra(ActivityLifecycleInitializer.KEY_ACTIVITY_RESULT_API) + return if (TextUtils.isEmpty(activityKey)) null else ActivityLifecycleInitializer.resultLauncherMap[activityKey] +} + +/** + * 在Activity使用registerForActivityResult + */ +@JvmOverloads +fun FragmentActivity.registerForActivityResult( + intent: Intent, + activityResultCallback: ActivityResultCallback? = null +) { + activityResultLauncher()?.launch(intent, activityResultCallback) +} + +/** + * 在Activity使用registerForActivityResult + */ +@JvmOverloads +inline fun FragmentActivity.registerForActivityResult( + intentExtra: (intent: Intent) -> Unit = {}, + activityResultCallback: ActivityResultCallback? = null +) { + val intent = Intent(this, T::class.java) + intentExtra(intent) + registerForActivityResult(intent, activityResultCallback) +} + +/** + * 在Fragment使用registerForActivityResult + */ +@JvmOverloads +fun Fragment.registerForActivityResult( + intent: Intent, + activityResultCallback: ActivityResultCallback? = null +) { + requireActivity().activityResultLauncher()?.launch(intent, activityResultCallback) +} + +/** + * 在Fragment使用registerForActivityResult + */ +@JvmOverloads +inline fun Fragment.registerForActivityResult( + intentExtra: (intent: Intent) -> Unit = {}, + activityResultCallback: ActivityResultCallback? = null +) { + val intent = Intent(this.requireActivity(), T::class.java) + intentExtra(intent) + registerForActivityResult(intent, activityResultCallback) +} + +/** + * Activity中ARouter导航 + * @param [activity] activity + * @param [activityResultCallback] 返回数据回调 + */ +fun Postcard.navigation( + activity: FragmentActivity?, + activityResultCallback: ActivityResultCallback +): Any? { + return navigation(activity, null, activityResultCallback) +} + +/** + * Fragment中ARouter导航 + * @param [fragment] Fragment + * @param [activityResultCallback] 返回数据回调 + */ +fun Postcard.navigation( + fragment: Fragment?, + activityResultCallback: ActivityResultCallback +): Any? { + return navigation(fragment?.requireActivity(), null, activityResultCallback) +} + + +/** + * Fragment中ARouter导航 + * @param [fragment] Fragment + * @param [callback] 回调 + * @param [activityResultCallback] 返回数据回调 + */ +fun Postcard.navigation( + fragment: Fragment?, + callback: NavigationCallback?, + activityResultCallback: ActivityResultCallback +): Any? { + return navigation(fragment?.requireActivity(), callback, activityResultCallback) +} + +/** + * Activity中ARouter导航 + * @param [activity] Fragment + * @param [callback] 回调 + * @param [activityResultCallback] 返回数据回调 + */ +fun Postcard.navigation( + activity: FragmentActivity?, + callback: NavigationCallback?, + activityResultCallback: ActivityResultCallback +): Any? { + if (activity == null) { + return null + } + val _postcard = this + val pretreatmentService = ARouter.getInstance().navigation(PretreatmentService::class.java) + if (null != pretreatmentService && !pretreatmentService.onPretreatment(activity, this)) { + return null + } + + try { + LogisticsCenter.completion(_postcard) + } catch (ex: NoRouteFoundException) { + debugLog(activity, path, group) + if (null != callback) { + callback.onLost(_postcard) + } else { + val degradeService = ARouter.getInstance().navigation(DegradeService::class.java) + degradeService?.onLost(activity, _postcard) + } + + return null + } + callback?.onFound(_postcard) + val interceptorService = ARouter.getInstance().navigation(InterceptorService::class.java) + if (!isGreenChannel && interceptorService != null) { + interceptorService.doInterceptions(_postcard, object : InterceptorCallback { + + override fun onContinue(postcard: Postcard?) { + _navigation(activity, _postcard, activityResultCallback) + } + + override fun onInterrupt(exception: Throwable?) { + callback?.onInterrupt(_postcard) + } + + }) + } else { + return _navigation(activity, this, activityResultCallback) + } + return null +} + +/** + * Debug模式下日志打印 + */ +private fun debugLog(activity: FragmentActivity, path: String?, group: String?) { + if (ARouter.debuggable()) { + // Show friendly tips for user. + activity.runOnUiThread { + Toast.makeText( + activity, + "There's no route matched!Path = [${path}]Group = [${group}]", + Toast.LENGTH_LONG + ).show() + } + } +} + +private fun _navigation( + activity: FragmentActivity, + postcard: Postcard, + activityResultCallback: ActivityResultCallback, +): Any? { + + return when (postcard.type) { + RouteType.ACTIVITY -> { + + val intent = Intent(activity, postcard.destination) + postcard.extras?.let { intent.putExtras(it) } + if (postcard.flags != -1) { + intent.flags = postcard.flags + } + + postcard.action?.let { intent.action = postcard.action } + activity.runOnUiThread { + //适配动画 + if ((postcard.enterAnim != -1 && postcard.exitAnim != -1)) { + activity.overridePendingTransition(postcard.enterAnim, postcard.exitAnim) + } + activity.registerForActivityResult(intent, activityResultCallback) + } + null + } + RouteType.PROVIDER -> { + postcard.provider + } + RouteType.FRAGMENT -> { + val fragmentMeta = postcard.destination + try { + val instance = fragmentMeta.getConstructor().newInstance() + if (instance is Fragment) { + instance.arguments = postcard.extras + } + instance + } catch (ex: Exception) { + ex.printStackTrace() + } + } + else -> { + null + } + } +} \ No newline at end of file diff --git a/VisualNovel/app/src/main/java/com/remax/visualnovel/app/initializer/impl/ActivityLifecycleInitializer.kt b/VisualNovel/app/src/main/java/com/remax/visualnovel/app/initializer/impl/ActivityLifecycleInitializer.kt new file mode 100644 index 0000000..965e8d3 --- /dev/null +++ b/VisualNovel/app/src/main/java/com/remax/visualnovel/app/initializer/impl/ActivityLifecycleInitializer.kt @@ -0,0 +1,101 @@ +package com.remax.visualnovel.app.initializer.impl + +import android.app.Activity +import android.app.Application +import android.content.Intent +import android.os.Bundle +import android.text.TextUtils +import androidx.activity.result.ActivityResult +import androidx.activity.result.ActivityResultCaller +import androidx.activity.result.contract.ActivityResultContracts +import androidx.fragment.app.FragmentActivity +import com.remax.visualnovel.app.activityresultapi.XActivityResultContract +import com.remax.visualnovel.app.initializer.AppInitializers +import com.remax.visualnovel.app.viewmodel.AppIMViewModel +import com.remax.visualnovel.configs.NovelApplication +import com.remax.visualnovel.utils.StatusBarUtils +import timber.log.Timber + +/** + * Created by HJW on 2023/5/11 + */ +class ActivityLifecycleInitializer(val application: Application, private val appIMViewModel: AppIMViewModel) : AppInitializers { + + override fun init() { + application.registerActivityLifecycleCallbacks(AppLifecycleCallbacks(appIMViewModel)) + } + + inner class AppLifecycleCallbacks constructor(private val appIMViewModel: AppIMViewModel) : Application.ActivityLifecycleCallbacks { + + private var activityCount = 0 + + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { + if (activity is ActivityResultCaller) { + //生成一个Key + val activityKey = activity.javaClass.simpleName + System.currentTimeMillis() + //添加一个默认ActivityResultLauncher + val resultLauncher = + XActivityResultContract(activity, ActivityResultContracts.StartActivityForResult()) + //把生成的Key放到intent中,作为每一个Activity的唯一标识 + activity.intent.putExtra(KEY_ACTIVITY_RESULT_API, activityKey) + //存放到Map中 + resultLauncherMap[activityKey] = resultLauncher + } + } + + override fun onActivityStarted(activity: Activity) { + activityCount++ + } + + override fun onActivityPaused(activity: Activity) { + NovelApplication.setCurrentActivity(null) + } + + override fun onActivityResumed(activity: Activity) { + Timber.d("currentActivity:${activity::class.java.name}") + NovelApplication.setCurrentActivity(activity) + if (StatusBarUtils.statusBarHeight == 0) { + StatusBarUtils.getStatusBarHeight(activity) + } + + //TODO - check pay info later + /*if (activity !is WelcomeActivity) { + GooglePayManager.checkProductDetails() + }*/ + } + + override fun onActivityStopped(activity: Activity) { + activityCount-- + } + + override fun onActivityDestroyed(activity: Activity) { + if (activity is FragmentActivity) { + val activityKey = activity.intent.getStringExtra(KEY_ACTIVITY_RESULT_API) + if (!TextUtils.isEmpty(activityKey)) { + resultLauncherMap[activityKey]?.unregister() + //移除activity的resultLauncher + resultLauncherMap.remove(activityKey) + } + } + } + + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) { + + } + + } + + companion object { + /** + * 保存ActivityResultApi的Key + */ + const val KEY_ACTIVITY_RESULT_API = "activityResultApi" + + /** + * 保存activity和Fragment的resultLauncher + */ + val resultLauncherMap: MutableMap> = + mutableMapOf() + + } +} \ No newline at end of file diff --git a/VisualNovel/app/src/main/java/com/remax/visualnovel/entity/request/AIFeedback.kt b/VisualNovel/app/src/main/java/com/remax/visualnovel/entity/request/AIFeedback.kt new file mode 100644 index 0000000..fe7a839 --- /dev/null +++ b/VisualNovel/app/src/main/java/com/remax/visualnovel/entity/request/AIFeedback.kt @@ -0,0 +1,34 @@ +package com.remax.visualnovel.entity.request + +/** + * Created by HJW on 2025/8/26 + */ +data class AIFeedback( + /** + * AI的ID + */ + val aiId: String, + + /** + * 文本内容 + */ + val content: String?, + + /** + * 消息ID + */ + val messageId: String?, + + /** + * 操作类型:空或0 无操作、1 赞、2 踩 + */ + val optType: Int +) { + + companion object { + const val NONE = 0 + const val LIKE = 1 + const val DISLIKE = 2 + } + +} diff --git a/VisualNovel/app/src/main/java/com/remax/visualnovel/entity/response/AIDict.kt b/VisualNovel/app/src/main/java/com/remax/visualnovel/entity/response/AIDict.kt new file mode 100644 index 0000000..d27aa0e --- /dev/null +++ b/VisualNovel/app/src/main/java/com/remax/visualnovel/entity/response/AIDict.kt @@ -0,0 +1,68 @@ +package com.remax.visualnovel.entity.response + +import android.os.Parcelable +import androidx.annotation.DrawableRes +import kotlinx.parcelize.IgnoredOnParcel +import kotlinx.parcelize.Parcelize + +/** + * Created by HJW on 2025/7/18 + */ +@Parcelize +data class AIDict( + val roleDictList: List, + val characterDictList: List, + val imageStyleDictList: List, + val timbreDictList: List, +) : Parcelable { + + fun setInitSelect(character: Character?) { + val imageStyleCode = character?.aiUserExt?.imageStyleCode + val findImageStyle = imageStyleDictList.find { character -> character.code == imageStyleCode } + (findImageStyle ?: imageStyleDictList.firstOrNull())?.select = true + + val roleCode = character?.roleCode + if (roleCode == null) { + roleDictList.firstOrNull()?.childDictList?.firstOrNull()?.select = true + } else { + roleDictList.forEach { roleChild -> + roleChild.childDictList?.forEach { role -> + role.select = role.code == roleCode + } + } + } + + val characterCode = character?.characterCode + val findCharacter = characterDictList.find { character -> character.code == characterCode } + (findCharacter ?: characterDictList.firstOrNull())?.select = true + + val tagCode = character?.tagCode + if (tagCode == null) { + characterDictList.firstOrNull()?.childDictList?.firstOrNull()?.select = true + } else { + characterDictList.forEach { characterChild -> + characterChild.childDictList?.forEach { tag -> + tag.select = tag.code == tagCode + } + } + } + } +} + +@Parcelize +data class AIDictItem( + val code: String, + val name: String, + val url: String?, + val prompt: String?, + val childDictList: List?, + var select: Boolean = false +) : Parcelable { + @IgnoredOnParcel + @DrawableRes + var filterIcon: Int? = null + + @IgnoredOnParcel + @DrawableRes + var selectBackgroundRes: Int? = null +} diff --git a/VisualNovel/app/src/main/java/com/remax/visualnovel/entity/response/AIVoice.kt b/VisualNovel/app/src/main/java/com/remax/visualnovel/entity/response/AIVoice.kt new file mode 100644 index 0000000..901685f --- /dev/null +++ b/VisualNovel/app/src/main/java/com/remax/visualnovel/entity/response/AIVoice.kt @@ -0,0 +1,38 @@ +package com.remax.visualnovel.entity.response + +import android.os.Parcelable +import com.remax.visualnovel.entity.response.base.BaseVoice +import kotlinx.parcelize.Parcelize + +/** + * Created by HJW on 2025/7/21 + */ + +@Parcelize +data class AIVoice( + val type: Int, // 1 男 2 女 + var select: Boolean = false, + var name: String?, + val description: String?, + val voiceType: String?, + val supportEmotions: String?, + val voiceText: String?, + var url: String?, + var filePath: String?, + val code: String?, + val pitchRate: Int?, + val speechRate: Int? +) : Parcelable, BaseVoice() { + + override fun filePathName(): String { + return filePath ?: "" + } + + override fun id(): String { + return name ?: "" + } + + override fun url(): String { + return url ?: "" + } +} diff --git a/VisualNovel/app/src/main/java/com/remax/visualnovel/entity/response/Album.kt b/VisualNovel/app/src/main/java/com/remax/visualnovel/entity/response/Album.kt new file mode 100644 index 0000000..1786e3b --- /dev/null +++ b/VisualNovel/app/src/main/java/com/remax/visualnovel/entity/response/Album.kt @@ -0,0 +1,50 @@ +package com.remax.visualnovel.entity.response + +import android.os.Parcelable +import com.remax.visualnovel.constant.LockTypeConstant +import com.remax.visualnovel.entity.model.base.BasePhoto +import com.remax.visualnovel.manager.login.LoginManager +import kotlinx.parcelize.Parcelize + +/** + * Created by HJW on 2025/7/14 + */ +@Parcelize +data class Album( + val albumId: Long, + var userId: String?, + var aiId: String?, + var imgUrl: String?, + var img1: String?, + var img2: String?, + var img3: String?, + var likedStatus: String?, + var likedCount: Long, + var width: Long, + var height: Long, + var unlockPrice: Long?, + val imgOrder: Int, + var lockStatus: String?, + var isDefault: Boolean? +) : BasePhoto(), Parcelable { + + fun isLike() = LIKED == likedStatus + + /** + * 该图片是否解锁 + */ + fun isUnLock() = LoginManager.isMyself(userId) || LockTypeConstant.isUnLock(lockStatus) + + /** + * 该图片是否上锁 + */ + fun isOpen() = (unlockPrice ?: 0L) <= 0L + + override fun paramId(): Long = albumId + + companion object { + const val CANCELED: String = "CANCELED" + const val LIKED: String = "LIKED" + + } +} diff --git a/VisualNovel/app/src/main/java/com/remax/visualnovel/repository/api/ActorsRepository.kt b/VisualNovel/app/src/main/java/com/remax/visualnovel/repository/api/ActorsRepository.kt new file mode 100644 index 0000000..f2496b8 --- /dev/null +++ b/VisualNovel/app/src/main/java/com/remax/visualnovel/repository/api/ActorsRepository.kt @@ -0,0 +1,14 @@ +package com.remax.visualnovel.repository.api + +import com.remax.visualnovel.entity.response.Book +import com.remax.visualnovel.repository.api.base.BaseRepository +import com.remax.visualnovel.api.service.BookService +import com.remax.visualnovel.entity.response.base.Response +import javax.inject.Inject + + +class ActorsRepository @Inject constructor(private val bookService: BookService) : BaseRepository() { + suspend fun getBooks(): Response { + return bookService.getBooks() + } +} \ No newline at end of file diff --git a/VisualNovel/app/src/main/java/com/remax/visualnovel/ui/main/actor/ActorListFragment.kt b/VisualNovel/app/src/main/java/com/remax/visualnovel/ui/main/actor/ActorListFragment.kt new file mode 100644 index 0000000..1373154 --- /dev/null +++ b/VisualNovel/app/src/main/java/com/remax/visualnovel/ui/main/actor/ActorListFragment.kt @@ -0,0 +1,78 @@ +package com.remax.visualnovel.ui.main.actor + +import android.os.Bundle +import android.util.Log +import android.widget.Toast +import androidx.fragment.app.viewModels +import com.alibaba.android.arouter.facade.annotation.Route +import com.alibaba.android.arouter.launcher.ARouter +import com.dylanc.loadingstateview.BgColorType +import com.remax.visualnovel.app.base.BaseBindingFragment +import com.remax.visualnovel.databinding.FragmentMainActorBinding +import com.remax.visualnovel.databinding.FragmentMainBookBinding +import com.remax.visualnovel.ui.main.book.BookListViewModel +import com.remax.visualnovel.utils.Routers +import com.remax.visualnovel.widget.custom.TagItem +import dagger.hilt.android.AndroidEntryPoint +import kotlin.getValue + + + +@AndroidEntryPoint +@Route(path = Routers.ROUTE_FRAG_ACTORLIST) +class ActorListFragment : BaseBindingFragment() { + + private val contactViewModel by viewModels() + + override fun onCreated(bundle: Bundle?) { + setUI() + } + + override fun backgroundColorType(): BgColorType { + return BgColorType.TRANSPARENT + } + + private fun setUI() { + initTagLayout() + + with(binding) { + tagFlowLayout + } + } + + private fun initTagLayout() { + with(binding) { + // 模拟数据 + val tags = listOf( + TagItem("1", "Youth"), + TagItem("2", "Lolita"), + TagItem("2", "Lolita2"), + TagItem("3", "Overbearing CEO ABCDEFG Overbearing CEO ABCDEFG Overbearing CEO ABCDEFG"), + TagItem("3", "ggggggggggg CEO ABCDEFG Overbearing CEO ABCDEFG Overbearing CEO ABCDEFG"), + TagItem("4", "Uncle"), + TagItem("5", "Character Status"), + TagItem("6", "Imouto"), + TagItem("7", "Fanwork"), + TagItem("8", "LastLine"), + ) + + tagFlowLayout.setTags(tags) + + tagFlowLayout.setOnTagClickListener { tag -> + Toast.makeText(context, "Clicked: ${tag.text}", Toast.LENGTH_SHORT).show() + } + + tagFlowLayout.setOnExpandStateChangeListener { isExpanded -> + Log.d("TagFlowLayout", "Expand state: $isExpanded") + } + } + } + + companion object { + fun newInstance(): ActorListFragment { + return ARouter.getInstance().build(Routers.ROUTE_FRAG_ACTORLIST) + .navigation() as ActorListFragment + } + } + +} \ No newline at end of file diff --git a/VisualNovel/app/src/main/java/com/remax/visualnovel/ui/main/actor/ActorListViewModel.kt b/VisualNovel/app/src/main/java/com/remax/visualnovel/ui/main/actor/ActorListViewModel.kt new file mode 100644 index 0000000..323ebb8 --- /dev/null +++ b/VisualNovel/app/src/main/java/com/remax/visualnovel/ui/main/actor/ActorListViewModel.kt @@ -0,0 +1,26 @@ +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 com.remax.visualnovel.repository.api.BooksRepository +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>() + val msgStatFlow = _msgStatFlow.asSharedFlow() + + suspend fun getMessageStat() { + _msgStatFlow.emit(chatRepository.getBooks()) + } + +} diff --git a/VisualNovel/app/src/main/res/anim/act_slide_in_from_bottom.xml b/VisualNovel/app/src/main/res/anim/act_slide_in_from_bottom.xml new file mode 100644 index 0000000..5a3d3db --- /dev/null +++ b/VisualNovel/app/src/main/res/anim/act_slide_in_from_bottom.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/VisualNovel/app/src/main/res/anim/act_slide_out_from_bottom.xml b/VisualNovel/app/src/main/res/anim/act_slide_out_from_bottom.xml new file mode 100644 index 0000000..ca47db8 --- /dev/null +++ b/VisualNovel/app/src/main/res/anim/act_slide_out_from_bottom.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/VisualNovel/app/src/main/res/layout/activity_main.xml b/VisualNovel/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..3eefb4d --- /dev/null +++ b/VisualNovel/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/VisualNovel/loadingstateview-ktx/src/main/AndroidManifest.xml b/VisualNovel/loadingstateview-ktx/src/main/AndroidManifest.xml new file mode 100644 index 0000000..901e222 --- /dev/null +++ b/VisualNovel/loadingstateview-ktx/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/VisualNovel/loadingstateview/src/main/AndroidManifest.xml b/VisualNovel/loadingstateview/src/main/AndroidManifest.xml new file mode 100644 index 0000000..cc947c5 --- /dev/null +++ b/VisualNovel/loadingstateview/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/VisualNovel/viewbinding-base/src/main/AndroidManifest.xml b/VisualNovel/viewbinding-base/src/main/AndroidManifest.xml new file mode 100644 index 0000000..901e222 --- /dev/null +++ b/VisualNovel/viewbinding-base/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/VisualNovel/viewbinding-base/src/main/java/com/dylanc/viewbinding/base/ActivityBinding.kt b/VisualNovel/viewbinding-base/src/main/java/com/dylanc/viewbinding/base/ActivityBinding.kt new file mode 100644 index 0000000..15bcf1b --- /dev/null +++ b/VisualNovel/viewbinding-base/src/main/java/com/dylanc/viewbinding/base/ActivityBinding.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2020. Dylan Cai + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dylanc.viewbinding.base + +import android.app.Activity +import android.view.View +import androidx.viewbinding.ViewBinding + +interface ActivityBinding { + val binding: VB + fun Activity.setContentViewWithBinding(): View +} + +class ActivityBindingDelegate : ActivityBinding { + private lateinit var _binding: VB + + override val binding: VB get() = _binding + + override fun Activity.setContentViewWithBinding(): View { + _binding = ViewBindingUtil.inflateWithGeneric(this, layoutInflater) + setContentView(_binding.root) + return _binding.root + } +} \ No newline at end of file diff --git a/VisualNovel/viewbinding-nonreflection-ktx/src/main/AndroidManifest.xml b/VisualNovel/viewbinding-nonreflection-ktx/src/main/AndroidManifest.xml new file mode 100644 index 0000000..901e222 --- /dev/null +++ b/VisualNovel/viewbinding-nonreflection-ktx/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/VisualNovel/viewbinding-nonreflection-ktx/src/main/java/com/dylanc/viewbinding/nonreflection/Activity.kt b/VisualNovel/viewbinding-nonreflection-ktx/src/main/java/com/dylanc/viewbinding/nonreflection/Activity.kt new file mode 100644 index 0000000..7b23cb5 --- /dev/null +++ b/VisualNovel/viewbinding-nonreflection-ktx/src/main/java/com/dylanc/viewbinding/nonreflection/Activity.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2020. Dylan Cai + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dylanc.viewbinding.nonreflection + +import android.view.LayoutInflater +import androidx.activity.ComponentActivity +import androidx.databinding.ViewDataBinding +import androidx.viewbinding.ViewBinding +import kotlin.LazyThreadSafetyMode.NONE + +fun ComponentActivity.binding(inflate: (LayoutInflater) -> VB, setContentView: Boolean = true) = lazy(NONE) { + inflate(layoutInflater).also { binding -> + if (setContentView) setContentView(binding.root) + if (binding is ViewDataBinding) binding.lifecycleOwner = this + } +}