diff --git a/app/build.gradle b/app/build.gradle
index 39c93a6..811902e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -48,6 +48,7 @@ android {
}
buildFeatures {
viewBinding true
+ buildConfig true
}
externalNativeBuild {
cmake {
@@ -88,4 +89,5 @@ dependencies {
implementation deps.webkit
implementation deps.ansi4j_api
implementation deps.ansi4j_impl
+ implementation deps.viewpagerindicator
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index b78ffd6..41c571c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools">
+
- @GET("get_group")
- suspend fun getGroup(): ApiResponse>
-
@GET("manage/get_friend_list")
suspend fun getFriendList(@Query("bot_id") botId: String): ApiResponse>
@GET("manage/get_group_list")
suspend fun getGroupList(@Query("bot_id") botId: String): ApiResponse>
- @POST("update_group")
+ @GET("manage/get_group_detail")
+ suspend fun getGroupDetail(
+ @Query("bot_id") botId: String,
+ @Query("group_id") groupId: String
+ ): ApiResponse
+
+ @POST("manage/update_group")
suspend fun updateGroup(@Body updateGroup: UpdateGroup): ApiResponse
@GET("plugin/get_plugin_list")
suspend fun getPluginList(
- @Query("plugin_type") type: String,
+ @Query("plugin_type") type: Array,
@Query("menu_type") menuType: String? = null
): ApiResponse>
diff --git a/app/src/main/java/me/kbai/zhenxunui/api/BotApi.kt b/app/src/main/java/me/kbai/zhenxunui/api/BotApi.kt
index af1bc75..090a53f 100644
--- a/app/src/main/java/me/kbai/zhenxunui/api/BotApi.kt
+++ b/app/src/main/java/me/kbai/zhenxunui/api/BotApi.kt
@@ -1,7 +1,6 @@
package me.kbai.zhenxunui.api
import com.google.gson.Gson
-import com.google.gson.GsonBuilder
import me.kbai.zhenxunui.Constants
import okhttp3.Interceptor
import okhttp3.OkHttpClient
diff --git a/app/src/main/java/me/kbai/zhenxunui/extends/AnimationExtends.kt b/app/src/main/java/me/kbai/zhenxunui/extends/AnimationExtends.kt
index 9d01eca..8790469 100644
--- a/app/src/main/java/me/kbai/zhenxunui/extends/AnimationExtends.kt
+++ b/app/src/main/java/me/kbai/zhenxunui/extends/AnimationExtends.kt
@@ -2,7 +2,6 @@ package me.kbai.zhenxunui.extends
import android.view.animation.Animation
import android.view.animation.Animation.AnimationListener
-import androidx.dynamicanimation.animation.DynamicAnimation.OnAnimationEndListener
fun Animation.setAnimationListener(
diff --git a/app/src/main/java/me/kbai/zhenxunui/model/GroupInfo.kt b/app/src/main/java/me/kbai/zhenxunui/model/GroupInfo.kt
index ff68df1..3865d24 100644
--- a/app/src/main/java/me/kbai/zhenxunui/model/GroupInfo.kt
+++ b/app/src/main/java/me/kbai/zhenxunui/model/GroupInfo.kt
@@ -6,35 +6,44 @@ import com.google.gson.annotations.SerializedName
* @author Sean on 2023/5/31
*/
data class GroupInfo(
- val group: Group,
+ @SerializedName("group_id")
+ val groupId: String,
+
+ val name: String,
+
+ @SerializedName("call_count")
+ val callCount: Int,
+
+ @SerializedName("chat_count")
+ val chatCount: Int,
+
val level: Int,
+
val status: Boolean,
+
+ @SerializedName("ava_url")
+ val avatarUrl: String,
+
+ @SerializedName("max_member_count")
+ val maxMemberCount: Int,
+
+ @SerializedName("member_count")
+ val memberCount: Int,
+
+ @SerializedName("like_plugin")
+ val favouritePlugins: Map,
+
@SerializedName("close_plugins")
val closedPlugins: List,
+
val task: List
) {
- data class Group(
- @SerializedName("group_id")
- val groupId: String,
- @SerializedName("group_name")
- val groupName: String,
- @SerializedName("member_count")
- val memberCount: Int,
- @SerializedName("max_member_count")
- val maxMemberCount: Int
- )
-
data class Task(
val name: String,
- val nameZh: String,
+ @SerializedName("zh_name")
+ val zhName: String,
val status: Boolean
)
-
- fun makeUpdateGroup() = UpdateGroup(
- group.groupId,
- status,
- level
- )
}
diff --git a/app/src/main/java/me/kbai/zhenxunui/model/UpdateGroup.kt b/app/src/main/java/me/kbai/zhenxunui/model/UpdateGroup.kt
index 87c4ae4..b1b125f 100644
--- a/app/src/main/java/me/kbai/zhenxunui/model/UpdateGroup.kt
+++ b/app/src/main/java/me/kbai/zhenxunui/model/UpdateGroup.kt
@@ -8,6 +8,9 @@ import com.google.gson.annotations.SerializedName
data class UpdateGroup(
@SerializedName("group_id")
val groupId: String,
- var status: Boolean,
- var level: Int
+ val status: Boolean,
+ val level: Int,
+ @SerializedName("close_plugins")
+ val closedPlugins: List,
+ val task: List
)
diff --git a/app/src/main/java/me/kbai/zhenxunui/repository/ApiRepository.kt b/app/src/main/java/me/kbai/zhenxunui/repository/ApiRepository.kt
index 8336ff8..4f93e75 100644
--- a/app/src/main/java/me/kbai/zhenxunui/repository/ApiRepository.kt
+++ b/app/src/main/java/me/kbai/zhenxunui/repository/ApiRepository.kt
@@ -35,10 +35,18 @@ object ApiRepository {
fun getGroupList(botId: String) = networkFlow { BotApi.service.getGroupList(botId) }
+ fun getGroupDetail(botId: String, groupId: String) =
+ networkFlow { BotApi.service.getGroupDetail(botId, groupId) }
+
fun updateGroup(updateGroup: UpdateGroup) =
networkFlow { BotApi.service.updateGroup(updateGroup) }
- fun getPluginList(type: PluginType) = networkFlow { BotApi.service.getPluginList(type.string) }
+ fun getPluginList(vararg types: PluginType, menuType: String? = null) = networkFlow {
+ BotApi.service.getPluginList(
+ types.map { it.string }.toTypedArray(),
+ menuType
+ )
+ }
fun getPluginDetail(pluginInfo: PluginInfo) = networkFlow(PluginDetail(pluginInfo, null)) {
BotApi.service.getPluginDetail(pluginInfo.module)
diff --git a/app/src/main/java/me/kbai/zhenxunui/ui/console/ConsoleFragment.kt b/app/src/main/java/me/kbai/zhenxunui/ui/console/ConsoleFragment.kt
index 1235185..0262655 100644
--- a/app/src/main/java/me/kbai/zhenxunui/ui/console/ConsoleFragment.kt
+++ b/app/src/main/java/me/kbai/zhenxunui/ui/console/ConsoleFragment.kt
@@ -39,9 +39,9 @@ import kotlin.math.abs
class ConsoleFragment : BaseFragment() {
companion object {
- private const val LOCAL_DOMAIN = "local.host"
- private const val BAR_CHART_FILE = "http://$LOCAL_DOMAIN/assets/console_bar_charts.html"
- private const val TANGENTIAL_BAR_FILE =
+ const val LOCAL_DOMAIN = "local.host"
+ const val BAR_CHART_FILE = "http://$LOCAL_DOMAIN/assets/console_bar_charts.html"
+ const val TANGENTIAL_BAR_FILE =
"http://$LOCAL_DOMAIN/assets/console_horizontal_bar_charts.html"
}
@@ -267,29 +267,29 @@ class ConsoleFragment : BaseFragment() {
icPopularPlugin.wvCharts.detach()
}
}
+}
- class ConsoleChartWebViewClient(
- private val assetsLoader: WebViewAssetLoader,
- private val doOnPageFinished: (webView: WebView, url: String) -> Unit
- ) : WebViewClient() {
- private var mLoading = true
+class ConsoleChartWebViewClient(
+ private val assetsLoader: WebViewAssetLoader,
+ private val doOnPageFinished: (webView: WebView, url: String) -> Unit
+) : WebViewClient() {
+ private var mLoading = true
- override fun onPageFinished(view: WebView, url: String) {
- super.onPageFinished(view, url)
- mLoading = false
- doOnPageFinished.invoke(view, url)
- }
+ override fun onPageFinished(view: WebView, url: String) {
+ super.onPageFinished(view, url)
+ mLoading = false
+ doOnPageFinished.invoke(view, url)
+ }
- override fun shouldInterceptRequest(
- view: WebView,
- request: WebResourceRequest
- ): WebResourceResponse? {
- return assetsLoader.shouldInterceptRequest(request.url)
- }
+ override fun shouldInterceptRequest(
+ view: WebView,
+ request: WebResourceRequest
+ ): WebResourceResponse? {
+ return assetsLoader.shouldInterceptRequest(request.url)
+ }
- suspend fun blockDuringLoading(block: () -> Unit) {
- while (mLoading) delay(100)
- block.invoke()
- }
+ suspend fun blockDuringLoading(block: () -> Unit) {
+ while (mLoading) delay(100)
+ block.invoke()
}
}
\ No newline at end of file
diff --git a/app/src/main/java/me/kbai/zhenxunui/ui/group/ConversationFragment.kt b/app/src/main/java/me/kbai/zhenxunui/ui/group/ConversationFragment.kt
index 9f16ffc..00a48c8 100644
--- a/app/src/main/java/me/kbai/zhenxunui/ui/group/ConversationFragment.kt
+++ b/app/src/main/java/me/kbai/zhenxunui/ui/group/ConversationFragment.kt
@@ -1,10 +1,16 @@
package me.kbai.zhenxunui.ui.group
+import android.os.Bundle
import android.view.LayoutInflater
+import android.view.Menu
+import android.view.MenuInflater
+import android.view.MenuItem
import android.view.ViewGroup
import androidx.appcompat.widget.Toolbar
+import androidx.core.view.MenuProvider
import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.viewModels
+import androidx.navigation.fragment.findNavController
import me.kbai.zhenxunui.R
import me.kbai.zhenxunui.base.BaseFragment
import me.kbai.zhenxunui.databinding.FragmentConversationBinding
@@ -36,9 +42,6 @@ class ConversationFragment : BaseFragment() {
override fun initView(): Unit = viewBinding.run {
rvMessage.adapter = mMessageAdapter
- requireActivity().findViewById(R.id.toolbar).title =
- arguments?.getString(ARGS_NAME)
-
etText.addTextChangedListener {
btnSend.isEnabled = !it.isNullOrEmpty()
}
@@ -81,5 +84,29 @@ class ConversationFragment : BaseFragment() {
mMessageAdapter.addData(it)
}
}
+
+ requireActivity().addMenuProvider(object : MenuProvider {
+ override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
+ menuInflater.inflate(R.menu.conversation_menu_items, menu)
+ }
+
+ override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
+ if (menuItem.itemId == R.id.menu_edit) {
+ if (groupId != null) {
+ findNavController().navigate(
+ R.id.action_conversationFragment_to_editGroupFragment,
+ Bundle().apply {
+ putString(EditGroupFragment.ARGS_GROUP_ID, groupId)
+ }
+ )
+ }
+ return true
+ }
+ return false
+ }
+ }, viewLifecycleOwner)
+
+ requireActivity().findViewById(R.id.toolbar).title =
+ arguments?.getString(ARGS_NAME)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/me/kbai/zhenxunui/ui/group/EditGroupDialogFragment.kt b/app/src/main/java/me/kbai/zhenxunui/ui/group/EditGroupDialogFragment.kt
deleted file mode 100644
index ae5a2e7..0000000
--- a/app/src/main/java/me/kbai/zhenxunui/ui/group/EditGroupDialogFragment.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-package me.kbai.zhenxunui.ui.group
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.fragment.app.DialogFragment
-import me.kbai.zhenxunui.R
-import me.kbai.zhenxunui.base.BaseDialogFragment
-import me.kbai.zhenxunui.databinding.DialogEditGroupBinding
-import me.kbai.zhenxunui.model.GroupInfo
-import me.kbai.zhenxunui.model.UpdateGroup
-
-/**
- * @author Sean on 2023/6/7
- */
-class EditGroupDialogFragment(
- private val group: GroupInfo,
- private val onConfirmListener: (dialog: DialogFragment, button: View, update: UpdateGroup) -> Unit
-) : BaseDialogFragment() {
- private lateinit var mBinding: DialogEditGroupBinding
- private val mUpdateGroup = group.makeUpdateGroup()
-
- init {
- isCancelable = false
- }
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ) = DialogEditGroupBinding.inflate(inflater).also { mBinding = it }.root
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- initView()
- }
-
- private fun initView() = mBinding.run {
- swEnabled.isChecked = group.status
- etGroupLevel.setText(group.level.toString())
-
- swEnabled.setOnCheckedChangeListener { _, isChecked ->
- mUpdateGroup.status = isChecked
- }
-
- btnCancel.setOnClickListener { dismiss() }
-
- btnConfirm.setOnClickListener {
- val level = try {
- etGroupLevel.text.toString().toInt()
- } catch (e: NumberFormatException) {
- tilGroupLevel.error = tilGroupLevel.context.getString(R.string.error_valid_number)
- return@setOnClickListener
- }
- mUpdateGroup.level = level
-
- onConfirmListener.invoke(this@EditGroupDialogFragment, it, mUpdateGroup)
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/me/kbai/zhenxunui/ui/group/EditGroupFragment.kt b/app/src/main/java/me/kbai/zhenxunui/ui/group/EditGroupFragment.kt
new file mode 100644
index 0000000..7b5fe9b
--- /dev/null
+++ b/app/src/main/java/me/kbai/zhenxunui/ui/group/EditGroupFragment.kt
@@ -0,0 +1,234 @@
+package me.kbai.zhenxunui.ui.group
+
+import android.annotation.SuppressLint
+import android.text.SpannableString
+import android.text.Spanned
+import android.text.style.ForegroundColorSpan
+import android.view.LayoutInflater
+import android.view.Menu
+import android.view.MenuInflater
+import android.view.MenuItem
+import android.view.ViewGroup
+import android.webkit.WebSettings
+import android.webkit.WebView
+import androidx.annotation.StringRes
+import androidx.core.content.res.ResourcesCompat
+import androidx.core.view.MenuProvider
+import androidx.fragment.app.viewModels
+import androidx.webkit.WebViewAssetLoader
+import com.google.gson.JsonArray
+import kotlinx.coroutines.launch
+import me.kbai.zhenxunui.R
+import me.kbai.zhenxunui.base.BaseFragment
+import me.kbai.zhenxunui.databinding.FragmentEditGroupBinding
+import me.kbai.zhenxunui.extends.apiCollect
+import me.kbai.zhenxunui.extends.dp
+import me.kbai.zhenxunui.extends.isNightMode
+import me.kbai.zhenxunui.extends.setOnProgressChangedListener
+import me.kbai.zhenxunui.extends.viewLifecycleScope
+import me.kbai.zhenxunui.model.GroupInfo
+import me.kbai.zhenxunui.model.PluginInfo
+import me.kbai.zhenxunui.model.UpdateGroup
+import me.kbai.zhenxunui.repository.Resource
+import me.kbai.zhenxunui.tool.GlobalToast
+import me.kbai.zhenxunui.tool.glide.GlideApp
+import me.kbai.zhenxunui.ui.console.ConsoleChartWebViewClient
+import me.kbai.zhenxunui.ui.console.ConsoleFragment
+import me.kbai.zhenxunui.viewmodel.EditGroupViewModel
+
+/**
+ * @author Sean on 2023/6/7
+ */
+class EditGroupFragment : BaseFragment() {
+
+ companion object {
+ const val ARGS_GROUP_ID = "GROUP_ID"
+ }
+
+ private lateinit var mGroupId: String
+
+ private val mViewModel by viewModels()
+
+ private val mAssetLoader by lazy {
+ WebViewAssetLoader.Builder()
+ .setDomain(ConsoleFragment.LOCAL_DOMAIN)
+ .setHttpAllowed(true)
+ .addPathHandler("/assets/", WebViewAssetLoader.AssetsPathHandler(requireContext()))
+ .build()
+ }
+
+ private lateinit var mFavouriteWebViewClient: ConsoleChartWebViewClient
+
+ private var mPassiveAdapter: PassiveTasksPagerAdapter? = null
+
+ private var mPluginStatusAdapter: PluginStatusAdapter? = null
+
+ private var mSaving = false
+
+ override fun getViewBinding(
+ inflater: LayoutInflater,
+ container: ViewGroup?
+ ) = FragmentEditGroupBinding.inflate(inflater)
+
+ override fun initView(): Unit = viewBinding.run {
+ sbGroupLevel.setOnProgressChangedListener { progress, _ ->
+ etGroupLevel.setText(progress.toString())
+ }
+ mFavouriteWebViewClient =
+ icFavouritePlugins.wvCharts.initChartWebView(ConsoleFragment.BAR_CHART_FILE)
+ }
+
+ override fun initData() {
+ mGroupId = arguments?.getString(ARGS_GROUP_ID)!!
+
+ mViewModel.groupInfo.observe(viewLifecycleOwner) {
+ viewBinding.bindGroupInfoData(it)
+ }
+ mViewModel.plugins.observe(viewLifecycleOwner) { list ->
+ val group = mViewModel.groupInfo.value ?: return@observe
+ setPluginStatus(group, list)
+ }
+
+ mViewModel.requestGroupInfo(mGroupId)
+
+ addSaveButton()
+ }
+
+ private fun setPluginStatus(group: GroupInfo?, list: List?) {
+ if (group == null) return
+ if (list == null) return
+
+ viewBinding.rvPlugin.adapter =
+ PluginStatusAdapter(list, group).also { mPluginStatusAdapter = it }
+ }
+
+ private fun addSaveButton() {
+ requireActivity().addMenuProvider(object : MenuProvider {
+ override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
+ menuInflater.inflate(R.menu.group_info_menu_items, menu)
+ }
+
+ override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
+ if (menuItem.itemId == R.id.btn_save) {
+ updateGroup()
+ return true
+ }
+ return false
+ }
+ }, viewLifecycleOwner)
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ viewBinding.icFavouritePlugins.wvCharts.destroy()
+ }
+
+ private fun updateGroup() = viewLifecycleScope.launch {
+ if (mSaving) return@launch
+ mSaving = true
+ val updateGroup = collectUpdateData()
+ if (updateGroup == null) {
+ mSaving = false
+ return@launch
+ }
+ mViewModel.updateGroup(updateGroup)
+ .apiCollect {
+ if (it.status == Resource.Status.LOADING) return@apiCollect
+ GlobalToast.showToast(it.message)
+ }
+ mSaving = false
+ }
+
+ private fun collectUpdateData(): UpdateGroup? = viewBinding.run {
+ val closedPlugins = mPluginStatusAdapter?.getClosedPlugins()
+ val enabledTasks = mPassiveAdapter?.getEnabledTasks()
+
+ if (closedPlugins == null || enabledTasks == null) {
+ return@run null
+ }
+
+ UpdateGroup(
+ mGroupId,
+ swEnabled.isChecked,
+ sbGroupLevel.progress,
+ closedPlugins,
+ enabledTasks
+ )
+ }
+
+ private fun FragmentEditGroupBinding.bindGroupInfoData(data: GroupInfo) {
+ GlideApp.with(ivAvatar)
+ .load(data.avatarUrl)
+ .into(ivAvatar)
+ tvName.text = data.name
+ tvId.text = data.groupId
+ tvNum.text = getSpannedCount(R.string.number_format, data.memberCount)
+ tvMax.text = getSpannedCount(R.string.max_format, data.maxMemberCount)
+ tvReceived.text = getSpannedCount(R.string.received_format, data.chatCount)
+ tvCall.text = getSpannedCount(R.string.fn_call_format, data.callCount)
+ swEnabled.isChecked = data.status
+ icFavouritePlugins.apply {
+ tvTitle.text = getString(R.string.favourite_plugins)
+ viewLifecycleScope.launch {
+ mFavouriteWebViewClient.blockDuringLoading {
+ wvCharts.setChartData(data.favouritePlugins)
+ }
+ }
+ }
+ sbGroupLevel.progress = data.level
+ vpPassive.apply {
+ offscreenPageLimit = 2
+ setPageTransformer(false) { page, position ->
+ page.apply {
+ translationX = -position * 40.dp
+ }
+ }
+ adapter = PassiveTasksPagerAdapter(data.task).also { mPassiveAdapter = it }
+ }
+ }
+
+ private fun getSpannedCount(@StringRes id: Int, count: Int) =
+ SpannableString(getString(id, count)).apply {
+ val colorSpan = ForegroundColorSpan(
+ ResourcesCompat.getColor(resources, R.color.text_gray, null)
+ )
+ setSpan(colorSpan, 0, indexOf(':') + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
+ }
+
+ @SuppressLint("SetJavaScriptEnabled")
+ private fun WebView.initChartWebView(path: String): ConsoleChartWebViewClient {
+ settings.run {
+ javaScriptEnabled = true
+ setSupportZoom(false)
+ setNeedInitialFocus(false)
+ builtInZoomControls = true
+ loadWithOverviewMode = true
+ useWideViewPort = true
+ loadsImagesAutomatically = true
+ cacheMode = WebSettings.LOAD_NO_CACHE
+ setSupportMultipleWindows(true)
+ }
+ val client = ConsoleChartWebViewClient(mAssetLoader) { webView, _ ->
+ webView.setChartNightMode(resources.configuration.isNightMode())
+ }
+ webViewClient = client
+ setBackgroundColor(0)
+ loadUrl(path)
+ return client
+ }
+
+ private fun WebView.setChartNightMode(isNightMode: Boolean) {
+ evaluateJavascript("javascript:setDarkMode($isNightMode)") {}
+ }
+
+ private fun WebView.setChartData(data: Map) {
+ val array = JsonArray()
+ data.forEach {
+ val obj = JsonArray()
+ obj.add(it.key)
+ obj.add(it.value)
+ array.add(obj)
+ }
+ evaluateJavascript("javascript:setChartData2($array)", null)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/me/kbai/zhenxunui/ui/group/FriendListAdapter.kt b/app/src/main/java/me/kbai/zhenxunui/ui/group/FriendListAdapter.kt
index fc84c66..b008631 100644
--- a/app/src/main/java/me/kbai/zhenxunui/ui/group/FriendListAdapter.kt
+++ b/app/src/main/java/me/kbai/zhenxunui/ui/group/FriendListAdapter.kt
@@ -4,15 +4,10 @@ import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.ViewGroup
-import android.view.animation.AnimationUtils
-import androidx.core.content.res.ResourcesCompat
-import androidx.core.view.isVisible
-import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import me.kbai.zhenxunui.R
import me.kbai.zhenxunui.databinding.ItemFriendListBinding
import me.kbai.zhenxunui.databinding.ItemFriendListClassifyBinding
-import me.kbai.zhenxunui.extends.setAnimationListener
import me.kbai.zhenxunui.extends.setOnDebounceClickListener
import me.kbai.zhenxunui.model.FriendListItem
import me.kbai.zhenxunui.model.GroupListItem
diff --git a/app/src/main/java/me/kbai/zhenxunui/ui/group/FriendListFragment.kt b/app/src/main/java/me/kbai/zhenxunui/ui/group/FriendListFragment.kt
index 719cf2a..d7feaa5 100644
--- a/app/src/main/java/me/kbai/zhenxunui/ui/group/FriendListFragment.kt
+++ b/app/src/main/java/me/kbai/zhenxunui/ui/group/FriendListFragment.kt
@@ -12,7 +12,6 @@ import me.kbai.zhenxunui.R
import me.kbai.zhenxunui.base.BaseFragment
import me.kbai.zhenxunui.databinding.FragmentGroupBinding
import me.kbai.zhenxunui.extends.launchAndCollectIn
-import me.kbai.zhenxunui.extends.logI
import me.kbai.zhenxunui.extends.setOnDebounceClickListener
import me.kbai.zhenxunui.extends.viewLifecycleScope
import me.kbai.zhenxunui.tool.glide.GlideApp
@@ -41,14 +40,6 @@ class FriendListFragment : BaseFragment() {
override fun initView(): Unit = viewBinding.run {
rvFriends.adapter = mAdapter
mAdapter.onGroupClickListener = {
-// EditGroupDialogFragment(mAdapter.data[position]) { dialog, button, update ->
-// viewLifecycleScope.launch {
-// val result = mViewModel.updateGroup(update).apiCollect(button)
-// GlobalToast.showToast(result.message)
-// dialog.dismiss()
-// requestGroupData(true)
-// }
-// }.show(childFragmentManager)
val args = Bundle().apply {
putString(ConversationFragment.ARGS_GROUP_ID, it.groupId)
putString(ConversationFragment.ARGS_NAME, it.groupName)
diff --git a/app/src/main/java/me/kbai/zhenxunui/ui/group/MessageAdapter.kt b/app/src/main/java/me/kbai/zhenxunui/ui/group/MessageAdapter.kt
index 7ba0887..4411e2c 100644
--- a/app/src/main/java/me/kbai/zhenxunui/ui/group/MessageAdapter.kt
+++ b/app/src/main/java/me/kbai/zhenxunui/ui/group/MessageAdapter.kt
@@ -15,7 +15,7 @@ import me.kbai.zhenxunui.model.ChatMessage
import me.kbai.zhenxunui.model.MessageType
import me.kbai.zhenxunui.tool.glide.GlideApp
-class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+class MessageViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val binding: ItemMessageReceiveBinding
get() = ItemMessageReceiveBinding.bind(itemView)
}
@@ -23,16 +23,16 @@ class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
const val SENDER = 1
const val RECEIVER = 0
-class MessageAdapter : RecyclerView.Adapter() {
+class MessageAdapter : RecyclerView.Adapter() {
private val mData: MutableList = ArrayList()
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MessageViewHolder {
val inflater = LayoutInflater.from(parent.context)
if (viewType == SENDER) {
- return ViewHolder(inflater.inflate(R.layout.item_message_send, parent, false))
+ return MessageViewHolder(inflater.inflate(R.layout.item_message_send, parent, false))
}
- return ViewHolder(inflater.inflate(R.layout.item_message_receive, parent, false))
+ return MessageViewHolder(inflater.inflate(R.layout.item_message_receive, parent, false))
}
override fun getItemCount() = mData.size
@@ -45,7 +45,7 @@ class MessageAdapter : RecyclerView.Adapter() {
}
- override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ override fun onBindViewHolder(holder: MessageViewHolder, position: Int) {
val item = mData[position]
holder.binding.run {
diff --git a/app/src/main/java/me/kbai/zhenxunui/ui/group/PassiveTasksPagerAdapter.kt b/app/src/main/java/me/kbai/zhenxunui/ui/group/PassiveTasksPagerAdapter.kt
new file mode 100644
index 0000000..e40ed69
--- /dev/null
+++ b/app/src/main/java/me/kbai/zhenxunui/ui/group/PassiveTasksPagerAdapter.kt
@@ -0,0 +1,82 @@
+package me.kbai.zhenxunui.ui.group
+
+import android.util.SparseArray
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.view.isVisible
+import androidx.viewpager.widget.PagerAdapter
+import me.kbai.zhenxunui.databinding.ItemPassiveTasksPageBinding
+import me.kbai.zhenxunui.model.GroupInfo
+
+private class ItemHolder(
+ val binding: ItemPassiveTasksPageBinding
+) {
+ val children = arrayOf(
+ binding.item0.root,
+ binding.item1.root,
+ binding.item2.root,
+ binding.item3.root
+ )
+}
+
+class PassiveTasksPagerAdapter(
+ val data: List
+) : PagerAdapter() {
+ private val mNewState = HashMap().also { map ->
+ data.forEach {
+ map[it.name] = it.status
+ }
+ }
+
+ private val mViews = SparseArray()
+
+ fun getEnabledTasks(): List {
+ val list = ArrayList()
+ mNewState.forEach {
+ if (it.value) list.add(it.key)
+ }
+ return list
+ }
+
+ override fun getCount(): Int = (data.size + 3) shr 2
+
+ override fun isViewFromObject(view: View, `object`: Any): Boolean {
+ return view == `object`
+ }
+
+ override fun instantiateItem(container: ViewGroup, position: Int): Any {
+ val holder = mViews[position] ?: createPage(container, position)
+ var index = position * 4
+
+ holder.children.forEach {
+ if (index < data.size) {
+ val item = data[index]
+ it.text = item.zhName
+ it.isChecked = mNewState[item.name]!!
+ it.setOnCheckedChangeListener { _, isChecked ->
+ mNewState[item.name] = isChecked
+ }
+ it.isVisible = true
+ index++
+ } else {
+ it.isVisible = false
+ }
+ }
+ return holder.binding.root.also { container.addView(it) }
+ }
+
+ override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
+ container.removeView(mViews[position].binding.root)
+ mViews.remove(position)
+ }
+
+ private fun createPage(container: ViewGroup, position: Int) =
+ ItemHolder(
+ ItemPassiveTasksPageBinding.inflate(
+ LayoutInflater.from(container.context),
+ container,
+ false
+ )
+ ).also { mViews[position] = it }
+}
\ No newline at end of file
diff --git a/app/src/main/java/me/kbai/zhenxunui/ui/group/PluginStatusAdapter.kt b/app/src/main/java/me/kbai/zhenxunui/ui/group/PluginStatusAdapter.kt
new file mode 100644
index 0000000..a002f2a
--- /dev/null
+++ b/app/src/main/java/me/kbai/zhenxunui/ui/group/PluginStatusAdapter.kt
@@ -0,0 +1,45 @@
+package me.kbai.zhenxunui.ui.group
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import me.kbai.zhenxunui.databinding.ItemPluginStatusSwitchBinding
+import me.kbai.zhenxunui.model.GroupInfo
+import me.kbai.zhenxunui.model.PluginInfo
+
+
+class PluginStatusAdapter(
+ val data: List,
+ group: GroupInfo
+) : RecyclerView.Adapter() {
+ private val mNewState = HashMap().apply {
+ data.forEach {
+ put(it.module, true)
+ }
+ group.closedPlugins.forEach {
+ put(it, false)
+ }
+ }
+
+ fun getClosedPlugins(): List = mNewState.filter { !it.value }.map { it.key }
+
+ class ViewHolder(val binding: ItemPluginStatusSwitchBinding) :
+ RecyclerView.ViewHolder(binding.root)
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
+ ItemPluginStatusSwitchBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ )
+
+ override fun getItemCount(): Int = data.size
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int): Unit =
+ holder.binding.root.run {
+ val item = data[position]
+
+ text = item.name
+ setOnCheckedChangeListener { _, isChecked ->
+ mNewState[item.module] = isChecked
+ }
+ isChecked = mNewState[item.module]!!
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/me/kbai/zhenxunui/ui/plugin/PluginAdapter.kt b/app/src/main/java/me/kbai/zhenxunui/ui/plugin/PluginAdapter.kt
index 93b47e0..95e97d3 100644
--- a/app/src/main/java/me/kbai/zhenxunui/ui/plugin/PluginAdapter.kt
+++ b/app/src/main/java/me/kbai/zhenxunui/ui/plugin/PluginAdapter.kt
@@ -3,12 +3,10 @@ package me.kbai.zhenxunui.ui.plugin
import android.annotation.SuppressLint
import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater
-import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.widget.SwitchCompat
import androidx.core.content.res.ResourcesCompat
-import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import me.kbai.zhenxunui.R
import me.kbai.zhenxunui.databinding.ItemPluginListBinding
diff --git a/app/src/main/java/me/kbai/zhenxunui/ui/plugin/PluginConfigAdapter.kt b/app/src/main/java/me/kbai/zhenxunui/ui/plugin/PluginConfigAdapter.kt
index c9c3b52..f8245f3 100644
--- a/app/src/main/java/me/kbai/zhenxunui/ui/plugin/PluginConfigAdapter.kt
+++ b/app/src/main/java/me/kbai/zhenxunui/ui/plugin/PluginConfigAdapter.kt
@@ -2,7 +2,6 @@ package me.kbai.zhenxunui.ui.plugin
import android.content.Context
import android.graphics.Typeface
-import android.text.Editable
import android.text.InputType
import android.text.Spannable
import android.text.SpannableString
@@ -31,14 +30,12 @@ import me.kbai.zhenxunui.databinding.ItemEditConfigValueListTextBinding
import me.kbai.zhenxunui.databinding.ItemEditConfigValueTextBinding
import me.kbai.zhenxunui.databinding.LayoutPluginConfigsBinding
import me.kbai.zhenxunui.extends.addOnTabSelectedListener
-import me.kbai.zhenxunui.extends.logI
import me.kbai.zhenxunui.extends.setOnProgressChangedListener
import me.kbai.zhenxunui.model.BlockType
import me.kbai.zhenxunui.model.ConfigValueType
import me.kbai.zhenxunui.model.PluginDetail
import me.kbai.zhenxunui.model.UpdatePlugin
import me.kbai.zhenxunui.tool.GlobalToast
-import java.util.Objects
import kotlin.math.max
/**
diff --git a/app/src/main/java/me/kbai/zhenxunui/ui/plugin/PluginTypeFragment.kt b/app/src/main/java/me/kbai/zhenxunui/ui/plugin/PluginTypeFragment.kt
index 16d822d..8a60401 100644
--- a/app/src/main/java/me/kbai/zhenxunui/ui/plugin/PluginTypeFragment.kt
+++ b/app/src/main/java/me/kbai/zhenxunui/ui/plugin/PluginTypeFragment.kt
@@ -19,7 +19,6 @@ import me.kbai.zhenxunui.extends.setOnDebounceClickListener
import me.kbai.zhenxunui.extends.viewLifecycleScope
import me.kbai.zhenxunui.model.PluginInfo
import me.kbai.zhenxunui.model.PluginType
-import me.kbai.zhenxunui.model.UpdatePlugin
import me.kbai.zhenxunui.repository.Resource
import me.kbai.zhenxunui.tool.GlobalToast
import me.kbai.zhenxunui.tool.glide.GlideApp
diff --git a/app/src/main/java/me/kbai/zhenxunui/viewmodel/EditGroupViewModel.kt b/app/src/main/java/me/kbai/zhenxunui/viewmodel/EditGroupViewModel.kt
new file mode 100644
index 0000000..98e3373
--- /dev/null
+++ b/app/src/main/java/me/kbai/zhenxunui/viewmodel/EditGroupViewModel.kt
@@ -0,0 +1,46 @@
+package me.kbai.zhenxunui.viewmodel
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.async
+import kotlinx.coroutines.launch
+import me.kbai.zhenxunui.Constants
+import me.kbai.zhenxunui.extends.apiCollect
+import me.kbai.zhenxunui.model.GroupInfo
+import me.kbai.zhenxunui.model.PluginInfo
+import me.kbai.zhenxunui.model.PluginType
+import me.kbai.zhenxunui.model.UpdateGroup
+import me.kbai.zhenxunui.repository.ApiRepository
+
+class EditGroupViewModel : ViewModel() {
+
+ private val _groupInfo: MutableLiveData = MutableLiveData()
+ val groupInfo: LiveData = _groupInfo
+
+ private val _plugins: MutableLiveData> = MutableLiveData(ArrayList())
+ val plugins: LiveData> = _plugins
+
+ /**
+ * 这里 plugins 在 groupInfo 后设置值, 因为插件状态列表需要两者数据
+ */
+ fun requestGroupInfo(groupId: String) = viewModelScope.launch {
+ val groupDeferred = async {
+ ApiRepository.getGroupDetail(Constants.currentBot!!.selfId, groupId).apiCollect()
+ }
+ val pluginDeferred = async {
+ ApiRepository.getPluginList(PluginType.NORMAL, PluginType.ADMIN).apiCollect()
+ }
+ val groupRes = groupDeferred.await()
+ val pluginRes = pluginDeferred.await()
+ if (groupRes.success()) {
+ _groupInfo.value = groupRes.data
+ }
+ if (pluginRes.success()) {
+ _plugins.value = pluginRes.data
+ }
+ }
+
+ fun updateGroup(group: UpdateGroup) = ApiRepository.updateGroup(group)
+}
\ No newline at end of file
diff --git a/app/src/main/java/me/kbai/zhenxunui/viewmodel/PluginTypeViewModel.kt b/app/src/main/java/me/kbai/zhenxunui/viewmodel/PluginTypeViewModel.kt
index 1da38e4..3aa245b 100644
--- a/app/src/main/java/me/kbai/zhenxunui/viewmodel/PluginTypeViewModel.kt
+++ b/app/src/main/java/me/kbai/zhenxunui/viewmodel/PluginTypeViewModel.kt
@@ -3,14 +3,11 @@ package me.kbai.zhenxunui.viewmodel
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import me.kbai.zhenxunui.model.PluginInfo
import me.kbai.zhenxunui.model.PluginSwitch
import me.kbai.zhenxunui.model.PluginType
-import me.kbai.zhenxunui.model.UpdatePlugin
import me.kbai.zhenxunui.repository.ApiRepository
-import me.kbai.zhenxunui.repository.Resource
/**
* @author Sean on 2023/6/2
diff --git a/app/src/main/java/me/kbai/zhenxunui/viewmodel/RequestViewModel.kt b/app/src/main/java/me/kbai/zhenxunui/viewmodel/RequestViewModel.kt
index 86c82af..a5e85a0 100644
--- a/app/src/main/java/me/kbai/zhenxunui/viewmodel/RequestViewModel.kt
+++ b/app/src/main/java/me/kbai/zhenxunui/viewmodel/RequestViewModel.kt
@@ -3,8 +3,8 @@ package me.kbai.zhenxunui.viewmodel
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.onEach
-import me.kbai.zhenxunui.model.HandleRequest
import me.kbai.zhenxunui.model.BotRequest
+import me.kbai.zhenxunui.model.HandleRequest
import me.kbai.zhenxunui.repository.ApiRepository
/**
diff --git a/app/src/main/java/me/kbai/zhenxunui/widget/CommonDecoration.kt b/app/src/main/java/me/kbai/zhenxunui/widget/CommonDecoration.kt
index 1659ca5..167b2a2 100644
--- a/app/src/main/java/me/kbai/zhenxunui/widget/CommonDecoration.kt
+++ b/app/src/main/java/me/kbai/zhenxunui/widget/CommonDecoration.kt
@@ -6,7 +6,6 @@ import android.graphics.Rect
import android.view.View
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
-import kotlin.math.round
import kotlin.math.roundToInt
/**
diff --git a/app/src/main/res/drawable/bg_list_item_round_top.xml b/app/src/main/res/drawable/bg_list_item_round_top.xml
new file mode 100644
index 0000000..5697761
--- /dev/null
+++ b/app/src/main/res/drawable/bg_list_item_round_top.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_edit_group.xml b/app/src/main/res/layout/dialog_edit_group.xml
deleted file mode 100644
index 9971bd6..0000000
--- a/app/src/main/res/layout/dialog_edit_group.xml
+++ /dev/null
@@ -1,101 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_edit_group.xml b/app/src/main/res/layout/fragment_edit_group.xml
new file mode 100644
index 0000000..09df9bf
--- /dev/null
+++ b/app/src/main/res/layout/fragment_edit_group.xml
@@ -0,0 +1,285 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_passive_tasks_page.xml b/app/src/main/res/layout/item_passive_tasks_page.xml
new file mode 100644
index 0000000..3513614
--- /dev/null
+++ b/app/src/main/res/layout/item_passive_tasks_page.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_passive_tasks_switch.xml b/app/src/main/res/layout/item_passive_tasks_switch.xml
new file mode 100644
index 0000000..803583e
--- /dev/null
+++ b/app/src/main/res/layout/item_passive_tasks_switch.xml
@@ -0,0 +1,7 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_plugin_status_switch.xml b/app/src/main/res/layout/item_plugin_status_switch.xml
new file mode 100644
index 0000000..a81e645
--- /dev/null
+++ b/app/src/main/res/layout/item_plugin_status_switch.xml
@@ -0,0 +1,8 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_plugin_configs.xml b/app/src/main/res/layout/layout_plugin_configs.xml
index 55d0670..477a958 100644
--- a/app/src/main/res/layout/layout_plugin_configs.xml
+++ b/app/src/main/res/layout/layout_plugin_configs.xml
@@ -157,6 +157,7 @@
android:layout_height="match_parent"
android:cursorVisible="false"
android:focusable="false"
+ android:inputType="none"
android:padding="0dp"
android:textSize="@dimen/et_text_size"
tools:text="0" />
diff --git a/app/src/main/res/menu/conversation_menu_items.xml b/app/src/main/res/menu/conversation_menu_items.xml
new file mode 100644
index 0000000..91986ef
--- /dev/null
+++ b/app/src/main/res/menu/conversation_menu_items.xml
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/group_info_menu_items.xml b/app/src/main/res/menu/group_info_menu_items.xml
new file mode 100644
index 0000000..d7f6d15
--- /dev/null
+++ b/app/src/main/res/menu/group_info_menu_items.xml
@@ -0,0 +1,8 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/navigation/nav_main.xml b/app/src/main/res/navigation/nav_main.xml
index abdc96c..a13e427 100644
--- a/app/src/main/res/navigation/nav_main.xml
+++ b/app/src/main/res/navigation/nav_main.xml
@@ -48,7 +48,15 @@
+ android:label=""
+ tools:layout="@layout/fragment_conversation" >
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index a2c9414..7daf700 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -79,6 +79,7 @@
内存
硬盘
账号信息
+ 群组信息
好友数量:
群组数量:
已连接 %d天%d时%d分%d秒
@@ -111,4 +112,12 @@
群组
好友
发送
+ 人数: %d
+ 上限: %d
+ 消息数: %d
+ 调用数: %d
+ 被动功能
+ 最喜欢的插件
+ 保存
+ 群功能开关
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index db97b24..f7783fb 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -83,6 +83,7 @@
Disk
%.1f%%
Account Information
+ Group Information
Friends:
Group:
Connected for %dd%dh%dm%ds
@@ -116,4 +117,12 @@
Groups
Friends
Send
+ Num: %d
+ Max: %d
+ Received: %d
+ Fn Call: %d
+ Passive Tasks
+ Favourite Plugins
+ Save
+ Group Function
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index a2e90d8..f19c7b9 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -21,5 +21,4 @@ kotlin.code.style=official
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
-android.defaults.buildfeatures.buildconfig=true
android.nonFinalResIds=false
\ No newline at end of file
diff --git a/versions.gradle b/versions.gradle
index 4f048b7..0839f8e 100644
--- a/versions.gradle
+++ b/versions.gradle
@@ -23,6 +23,7 @@ versions.echartsjava = "1.0.7"
versions.webkit = "1.10.0"
versions.ansi4j = "ansi4j-1.1.0"
versions.free_reflection = "3.2.0"
+versions.viewpagerindicator = "1.2.3"
deps.constraintlayout = "androidx.constraintlayout:constraintlayout:$versions.constraintlayout"
@@ -113,4 +114,6 @@ deps.ansi4j_impl = "com.github.PavelKastornyy.ansi4j:ansi4j-core-impl:$versions.
deps.free_reflection = "com.github.tiann:FreeReflection:$versions.free_reflection"
+deps.viewpagerindicator = "com.github.zhpanvip:viewpagerindicator:$versions.viewpagerindicator"
+
ext.deps = deps
\ No newline at end of file