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