This commit is contained in:
coco
2026-07-03 16:23:31 +08:00
commit 7a4fb0e6ae
1979 changed files with 101570 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
/build
+75
View File
@@ -0,0 +1,75 @@
plugins {
id 'com.android.library'
id 'kotlin-android'
id 'kotlin-kapt'
id 'kotlin-parcelize'
}
android {
compileSdk compile_sdk_version
defaultConfig {
minSdk min_sdk_version
targetSdk target_sdk_version
versionCode version_code
versionName version_name
}
compileOptions {
sourceCompatibility java_version
targetCompatibility java_version
}
}
dependencies {
api project(path: ':net')
api "androidx.core:core-ktx:$core_ktx_version"
api "androidx.appcompat:appcompat:$appcompat_version"
api "com.google.android.material:material:$material_version"
api "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
//Initializer初始化
api "androidx.startup:startup-runtime:$startup_version"
api "androidx.compose.ui:ui:$compose_version"
api "androidx.compose.material:material:$compose_version"
api "androidx.compose.ui:ui-tooling-preview:$compose_version"
api "androidx.activity:activity-compose:$activity_compose_version"
api "androidx.navigation:navigation-compose:$navigation_version"
//刷新头
api "com.google.accompanist:accompanist-swiperefresh:$accompanist_pager"
//UI ProvideWindowInsets正确获取状态栏高度
api "com.google.accompanist:accompanist-insets-ui:$accompanist_pager"
//控制UI栏
api "com.google.accompanist:accompanist-systemuicontroller:$accompanist_pager"
//提供了分页布局支持,类似viewPager
api "com.google.accompanist:accompanist-pager:$accompanist_pager"
/**
* compose提供有viewModel等其他方便的函数
* 以下为Compose扩展库
*/
api "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_compose_version"
//提供observeAsState等方法
api "androidx.compose.runtime:runtime-livedata:$compose_version"
//Coil 核心库
api "io.coil-kt:coil-compose:$coil_version"
//Coil 选择添加
// implementation("io.coil-kt:coil-gif:1.2.2")//支持GIF
// implementation("io.coil-kt:coil-svg:1.2.2")//支持SVG
// implementation("io.coil-kt:coil-video:1.2.2")//支持Video
//加载lottie动画
api "com.airbnb.android:lottie-compose:$lottie_version"
// MMKV
api "com.tencent:mmkv-static:$mmkv_version"
//room
api "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
api "androidx.room:room-ktx:$room_version"
}
+21
View File
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.linx.common">
</manifest>
@@ -0,0 +1,44 @@
package com.linx.common.base
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.linx.common.baseData.CommonConstant
import com.linx.common.ext.addTo
import com.linx.common.model.ThemeType
import com.linx.common.widget.SpUtilsMMKV
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
/**
* ViewModel的基类
*/
abstract class BaseViewModel: ViewModel() {
//job列表
private val jobs: MutableList<Job> = mutableListOf<Job>()
//标记网络loading状态
val isLoading = MutableLiveData<Boolean>()
protected fun serverAwait(block: suspend CoroutineScope.() -> Unit) = viewModelScope.launch {
//协程启动之前
isLoading.value = true
block.invoke(this)
//协程启动之后
isLoading.value = false
}.addTo(jobs)
//取消全部协程
override fun onCleared() {
jobs.forEach { it.cancel() }
super.onCleared()
}
//是否登录
fun isLogin(): Boolean = SpUtilsMMKV.getBoolean(CommonConstant.IS_LOGIN) == true
}
@@ -0,0 +1,17 @@
package com.linx.common.baseData
/**
* 项目中的一切全局变量
*/
object CommonConstant {
//是否登录
const val IS_LOGIN = "is_login"
//主题颜色
const val THEME_COLOR = "theme_color"
//是否隐藏置顶文章
const val GONE_ARTICLE_TOP = "article_top"
}
@@ -0,0 +1,42 @@
package com.linx.common.baseData
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.res.painterResource
import androidx.lifecycle.MutableLiveData
import com.linx.common.R
/**
* 导航相关
*/
object Nav {
//密封类关联目的地路线和字符串资源
sealed class BottomNavScreen(val route: String, @StringRes val resourceId: Int, @DrawableRes val id: Int) {
object HomeScreen: BottomNavScreen("home", R.string.bottom_home, R.mipmap.nav_home)
object ProjectScreen: BottomNavScreen("project", R.string.bottom_project, R.mipmap.nav_project)
object SquareScreen: BottomNavScreen("square", R.string.bottom_square, R.mipmap.nav_square)
object PublicNumScreen: BottomNavScreen("publicNum", R.string.bottom_public_num, R.mipmap.nav_public_num)
object LearnScreen: BottomNavScreen("learn", R.string.bottom_learn, R.mipmap.nav_learn)
object MineScreen: BottomNavScreen("mine", R.string.bottom_mine, R.mipmap.nav_mine)
}
//记录BottomNav当前的Item
val bottomNavRoute = mutableStateOf<BottomNavScreen>(BottomNavScreen.HomeScreen)
//是否点击两次返回的activity
var twoBackFinishActivity = false
//项目页面指示器index
val projectTopBarIndex = mutableStateOf(0)
//广场页面指示器index
val squareTopBarIndex = mutableStateOf(0)
//公众号页面指示器index
val publicNumIndex = mutableStateOf(0)
}
@@ -0,0 +1,44 @@
package com.linx.common.baseData
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import com.linx.common.model.ThemeType
import com.linx.common.widget.SpUtilsMMKV
val themeColorList = listOf(
ThemeType.Default,
ThemeType.Theme1,
ThemeType.Theme2,
ThemeType.Theme3,
ThemeType.Theme4,
ThemeType.Theme5,
)
/**
* 标题栏和导航栏颜色
*/
val themeTypeState: MutableState<ThemeType> by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
mutableStateOf(getThemeType())
}
/**
* 获取保存下来的主题颜色
*/
private fun getThemeType(): ThemeType {
return when(SpUtilsMMKV.getInt(CommonConstant.THEME_COLOR) ?: 0) {
0 -> ThemeType.Default
1 -> ThemeType.Theme1
2 -> ThemeType.Theme2
3 -> ThemeType.Theme3
4 -> ThemeType.Theme4
5 -> ThemeType.Theme5
else -> ThemeType.Theme5
}
}
/**
* 刷新个人信息数据
*/
val refreshUserMessageData: MutableState<String> by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
mutableStateOf("")
}
@@ -0,0 +1,23 @@
package com.linx.common.ext
import android.content.Context
import android.content.Intent
import android.net.Uri
/**
* 加入qq聊天群
*/
fun joinQQGroup(context: Context, key: String): Boolean {
val intent = Intent()
intent.data =
Uri.parse("mqqopensdkapi://bizAgent/qm/qr?url=http%3A%2F%2Fqm.qq.com%2Fcgi-bin%2Fqm%2Fqr%3Ffrom%3Dapp%26p%3Dandroid%26k%3D$key")
// 此Flag可根据具体产品需要自定义,如设置,则在加群界面按返回,返回手Q主界面,不设置,按返回会返回到呼起产品界面 //intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
return try {
context.startActivity(intent)
true
} catch (e: Exception) {
// 未安装手Q或安装的版本不支持
"未安装手机QQ或安装的版本不支持".toast(context)
false
}
}
@@ -0,0 +1,21 @@
package com.linx.common.ext
import android.content.Context
import android.widget.Toast
import com.linx.common.widget.GsonUtils
/**
* 一些常用的方法
*/
fun Any?.toJsonStr(): String {
return GsonUtils.toJson(this)
}
/**
* toast
*/
fun Any.toast(context: Context) =
Toast.makeText(context, this.toString(), Toast.LENGTH_SHORT).show()
@@ -0,0 +1,10 @@
package com.linx.common.ext
import kotlinx.coroutines.Job
/**
* 添加Job到list中
*/
fun Job.addTo(list: MutableList<Job>) {
list.add(this)
}
@@ -0,0 +1,12 @@
package com.linx.common.ext
import java.text.SimpleDateFormat
import java.util.*
/**
* Long类型的时间转Date
* [type] 转换出来的日期格式
*/
fun Long.transitionDate(type: String = "yyyy-MM-dd HH:mm:ss"): String = SimpleDateFormat(type).format(
Date(this)
)
@@ -0,0 +1,14 @@
package com.linx.common.model
import androidx.compose.ui.graphics.vector.ImageVector
/**
* 标题栏的数据
*/
data class StatusBarTitleData(
val title: String,
val leftIcon: ImageVector? = null,
val rightIcon: ImageVector? = null,
val onLeftClick: () -> Unit = {},
val onRightClick: () -> Unit = {}
)
@@ -0,0 +1,16 @@
package com.linx.common.model
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
/**
* 主题类型
*/
enum class ThemeType {
Default,
Theme1,
Theme2,
Theme3,
Theme4,
Theme5
}
@@ -0,0 +1,17 @@
package com.linx.common.widget
import com.google.gson.Gson
import com.google.gson.GsonBuilder
/**
* 数据解析帮助类
*/
object GsonUtils {
private val gsonBuilder: GsonBuilder by lazy { GsonBuilder() }
val gson: Gson by lazy { gsonBuilder.create() }
fun toJson(paramObject: Any?): String = gson.toJson(paramObject)
}
@@ -0,0 +1,36 @@
package com.linx.common.widget
import androidx.annotation.MainThread
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import java.util.concurrent.atomic.AtomicBoolean
/**
* 用一个原子 AtomicBoolean记录一次setValue
* 在发送一次后在将AtomicBoolean设置为false,阻止后续前台重新触发时的数据发送。
*/
class SingleLiveEvent<T> : MutableLiveData<T>() {
private val mPending = AtomicBoolean(false)
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
super.observe(owner, { t ->
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t)
}
})
}
@MainThread
override fun setValue(t: T?) {
mPending.set(true)
super.setValue(t)
}
/**
* Used for cases where T is Void, to make calls cleaner.
*/
@MainThread
fun call() {
value = null
}
}
@@ -0,0 +1,31 @@
package com.linx.common.widget
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
/**
* 开启一个IO线程,一段时间后执行代码
*/
@Composable
fun sleepTime(millis: Long = 1500, block: () -> Unit) {
LaunchedEffect(Unit) {
launch(Dispatchers.IO) {
Thread.sleep(millis)
block()
}
}
}
/**
* 开启一个IO线程,一段时间后执行代码
*/
fun ViewModel.sleepTime(millis: Long = 1500, block: () -> Unit) {
viewModelScope.launch(Dispatchers.IO) {
Thread.sleep(millis)
block()
}
}
@@ -0,0 +1,95 @@
package com.linx.common.widget
import android.os.Parcelable
import com.tencent.mmkv.MMKV
import java.util.*
/**
* 腾讯mmkv
* 自定义的key-value
*/
object SpUtilsMMKV {
var mmkv: MMKV? = null
init {
mmkv = MMKV.defaultMMKV()
}
fun put(key: String, value: Any?): Boolean {
return when (value) {
is String -> mmkv?.encode(key, value)!!
is Float -> mmkv?.encode(key, value)!!
is Boolean -> mmkv?.encode(key, value)!!
is Int -> mmkv?.encode(key, value)!!
is Long -> mmkv?.encode(key, value)!!
is Double -> mmkv?.encode(key, value)!!
is ByteArray -> mmkv?.encode(key, value)!!
else -> false
}
}
/**
* 这里使用安卓自带的Parcelable序列化,它比java支持的Serializer序列化性能好些
*/
fun <T : Parcelable> put(key: String, t: T?): Boolean {
if (t == null) {
return false
}
return mmkv?.encode(key, t)!!
}
fun put(key: String, sets: Set<String>?): Boolean {
if (sets == null) {
return false
}
return mmkv?.encode(key, sets)!!
}
fun getInt(key: String): Int? {
return mmkv?.decodeInt(key, 0)
}
fun getDouble(key: String): Double? {
return mmkv?.decodeDouble(key, 0.00)
}
fun getLong(key: String): Long? {
return mmkv?.decodeLong(key, 0L)
}
fun getBoolean(key: String): Boolean? {
return mmkv?.decodeBool(key, false)
}
fun getFloat(key: String): Float? {
return mmkv?.decodeFloat(key, 0F)
}
fun getByteArray(key: String): ByteArray? {
return mmkv?.decodeBytes(key)
}
fun getString(key: String): String? {
return mmkv?.decodeString(key, "")
}
/**
* SpUtils.getParcelable<Class>("")
*/
inline fun <reified T : Parcelable> getParcelable(key: String): T? {
return mmkv?.decodeParcelable(key, T::class.java)
}
fun getStringSet(key: String): Set<String>? {
return mmkv?.decodeStringSet(key, Collections.emptySet())
}
fun removeKey(key: String) {
mmkv?.removeValueForKey(key)
}
fun clearAll() {
mmkv?.clearAll()
}
}
@@ -0,0 +1,34 @@
package com.linx.common.widget
import android.content.Context
import android.view.KeyEvent
import android.widget.Toast
import androidx.activity.ComponentActivity
import com.linx.common.baseData.Nav
/**
* 点击两次返回按钮关闭app
*/
class TwoBackFinish {
companion object {
var mExitTime: Long = 0
}
fun execute(context: Context, finish:() -> Unit) {
when {
/**
* 点击两次退出程序 有事件间隔,间隔内退出程序,否则提示
*/
(System.currentTimeMillis() - mExitTime) > 1500 -> {
Toast.makeText(context, "再按一次退出程序", Toast.LENGTH_SHORT).show()
mExitTime = System.currentTimeMillis()
}
else -> {
Nav.twoBackFinishActivity = true
finish()
}
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 783 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="bottom_home">首页</string>
<string name="bottom_project">项目</string>
<string name="bottom_square">广场</string>
<string name="bottom_public_num">公众号</string>
<string name="bottom_learn">学习</string>
<string name="bottom_mine">我的</string>
</resources>