From a51b448cd81cf531d3c0044aa7d2d59ead47cda1 Mon Sep 17 00:00:00 2001 From: Justwen Date: Thu, 6 Feb 2025 21:41:58 +0800 Subject: [PATCH] =?UTF-8?q?=E7=9F=AD=E6=B6=88=E6=81=AF=E8=AF=A6=E6=83=85?= =?UTF-8?q?=E6=94=B9=E4=B8=BAcompose=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../base/activity/ARouterConstants.kt | 2 +- .../core/data/MessageArticlePageInfo.java | 21 ++- .../src/main/AndroidManifest.xml | 8 +- .../module/message/MessageConvertFactory.java | 168 ++++++++++++++++-- .../message/compose/MessageRepository.kt | 2 +- .../message/compose/MessageViewModel.kt | 3 +- .../compose/detail/MessageDetailActivity.kt | 154 ++++++++++++++++ .../compose/detail/MessageDetailModel.kt | 24 +++ .../compose/detail/MessageDetailRepository.kt | 73 ++++++++ .../src/main/res/values/strings.xml | 1 + .../src/main/AndroidManifest.xml | 2 +- .../activity/MessageDetailActivity.java | 1 - 12 files changed, 433 insertions(+), 26 deletions(-) create mode 100644 lib_module_message/src/main/java/com/justwen/androidnga/module/message/compose/detail/MessageDetailActivity.kt create mode 100644 lib_module_message/src/main/java/com/justwen/androidnga/module/message/compose/detail/MessageDetailModel.kt create mode 100644 lib_module_message/src/main/java/com/justwen/androidnga/module/message/compose/detail/MessageDetailRepository.kt diff --git a/lib_base_service_api/src/main/java/com/justwen/androidnga/base/activity/ARouterConstants.kt b/lib_base_service_api/src/main/java/com/justwen/androidnga/base/activity/ARouterConstants.kt index c0faefec..6823227c 100644 --- a/lib_base_service_api/src/main/java/com/justwen/androidnga/base/activity/ARouterConstants.kt +++ b/lib_base_service_api/src/main/java/com/justwen/androidnga/base/activity/ARouterConstants.kt @@ -6,7 +6,7 @@ class ARouterConstants { const val ACTIVITY_MESSAGE_LIST_NAME = "com.justwen.androidnga.module.message.compose.MessageListActivity" const val ACTIVITY_MESSAGE_POST = "/activity/post" - const val ACTIVITY_MESSAGE_DETAIL = "/activity/detail" + const val ACTIVITY_MESSAGE_DETAIL = "/message/detail" } } \ No newline at end of file diff --git a/lib_core_data/src/main/java/com/justwen/androidnga/core/data/MessageArticlePageInfo.java b/lib_core_data/src/main/java/com/justwen/androidnga/core/data/MessageArticlePageInfo.java index d36052c2..ca6f2e12 100644 --- a/lib_core_data/src/main/java/com/justwen/androidnga/core/data/MessageArticlePageInfo.java +++ b/lib_core_data/src/main/java/com/justwen/androidnga/core/data/MessageArticlePageInfo.java @@ -1,5 +1,10 @@ package com.justwen.androidnga.core.data; +import android.util.Pair; + +import java.util.ArrayList; +import java.util.List; + import gov.anzong.androidnga.common.base.JavaBean; public class MessageArticlePageInfo implements JavaBean { @@ -16,6 +21,16 @@ public class MessageArticlePageInfo implements JavaBean { private String signature; private String formated_html_data; + private List> contentSections = new ArrayList<>(); + + public List> getContentSections() { + return contentSections; + } + + public void setContentSections(List> contentSections) { + this.contentSections = contentSections; + } + public int getLou() { return lou; } @@ -25,7 +40,7 @@ public void setLou(int lou) { } public String getTime() { - return time; + return time == null ? "22 13" : time; } public void setTime(String time) { @@ -41,7 +56,7 @@ public void setFrom(String from) { } public String getContent() { - return content; + return content == null ? "content" : content; } public void setContent(String content) { @@ -49,7 +64,7 @@ public void setContent(String content) { } public String getSubject() { - return subject; + return subject == null ? "subject" : subject; } public void setSubject(String subject) { diff --git a/lib_module_message/src/main/AndroidManifest.xml b/lib_module_message/src/main/AndroidManifest.xml index ffbe1855..04674271 100644 --- a/lib_module_message/src/main/AndroidManifest.xml +++ b/lib_module_message/src/main/AndroidManifest.xml @@ -6,8 +6,12 @@ + android:label="@string/label_activity_message_list" /> + + diff --git a/lib_module_message/src/main/java/com/justwen/androidnga/module/message/MessageConvertFactory.java b/lib_module_message/src/main/java/com/justwen/androidnga/module/message/MessageConvertFactory.java index a8dee547..e833bf51 100644 --- a/lib_module_message/src/main/java/com/justwen/androidnga/module/message/MessageConvertFactory.java +++ b/lib_module_message/src/main/java/com/justwen/androidnga/module/message/MessageConvertFactory.java @@ -1,5 +1,7 @@ package com.justwen.androidnga.module.message; +import android.util.Pair; + import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.justwen.androidnga.core.data.MessageArticlePageInfo; @@ -106,21 +108,164 @@ public MessageListInfo getMessageListInfo(String js) { return ret; } + public MessageDetailInfo getMessageDetailInfo(String js, int page) { + + if (js == null) { + mErrorMsg = ContextUtils.getString(R.string.network_error); + return null; + } + js = js.replaceAll("window.script_muti_get_var_store=", ""); + if (js.indexOf("/*error fill content") > 0) + js = js.substring(0, js.indexOf("/*error fill content")); + js = js.replaceAll("\"content\":\\+(\\d+),", "\"content\":\"+$1\","); + js = js.replaceAll("\"subject\":\\+(\\d+),", "\"subject\":\"+$1\","); + js = js.replaceAll("/\\*\\$js\\$\\*/", ""); + js = js.replaceAll("\\[img\\]./mon_", "[img]http://img6.nga.178.com/attachments/mon_"); + + JSONObject o = null; + try { + o = (JSONObject) JSON.parseObject(js).get("data"); + } catch (Exception e) { + NLog.e(TAG, "can not parse :\n" + js); + } + if (o == null) { + + try { + o = (JSONObject) JSON.parseObject(js).get("error"); + } catch (Exception e) { + NLog.e(TAG, "can not parse :\n" + js); + } + if (o == null) { + mErrorMsg = "请重新登录"; + } else { + mErrorMsg = o.getString("0"); + if (StringUtils.isEmpty(mErrorMsg)) + mErrorMsg = "请重新登录"; + } + return null; + } + MessageDetailInfo ret = parseJsonThreadPage(js, page); + return ret; + } + public String getErrorMsg() { return mErrorMsg; } + /** + * 解析页面内容 + * + * @param js + * @param page + * @return + */ + public MessageDetailInfo parseJsonThreadPage(String js, int page) { + js = js.replaceAll("\"content\":\\+(\\d+),", "\"content\":\"+$1\","); + js = js.replaceAll("\"subject\":\\+(\\d+),", "\"subject\":\"+$1\","); + + js = js.replaceAll("\"content\":(0\\d+),", "\"content\":\"$1\","); + js = js.replaceAll("\"subject\":(0\\d+),", "\"subject\":\"$1\","); + js = js.replaceAll("\"author\":(0\\d+),", "\"author\":\"$1\","); + final String start = "\"__P\":{\"aid\":"; + final String end = "\"this_visit_rows\":"; + if (js.indexOf(start) != -1 && js.indexOf(end) != -1) { + NLog.w(TAG, "here comes an invalid response"); + String validJs = js.substring(0, js.indexOf(start)); + validJs += js.substring(js.indexOf(end)); + js = validJs; + + } + JSONObject o = null; + try { + o = (JSONObject) JSON.parseObject(js).get("data"); + } catch (Exception e) { + NLog.e(TAG, "can not parse :\n" + js); + } + if (o == null) + return null; + + MessageDetailInfo data = new MessageDetailInfo(); + + JSONObject o1; + o1 = (JSONObject) o.get("0"); + if (o1 == null) + return null; + + JSONObject userInfoMap = (JSONObject) o1.get("userInfo"); + + List messageEntryList = convertJSobjToList(o1, userInfoMap, page); + if (messageEntryList == null) + return null; + data.setMessageEntryList(messageEntryList); + data.set__currentPage(o1.getIntValue("currentPage")); + data.set__nextPage(o1.getIntValue("nextPage")); + String alluser = o1.getString("allUsers"), allusertmp = ""; + alluser = alluser.replaceAll(" ", " "); + String alluserarray[] = alluser.split(" "); + for (int i = 1; i < alluserarray.length; i += 2) { + allusertmp += alluserarray[i] + ","; + } + if (allusertmp.length() > 0) + allusertmp = allusertmp.substring(0, allusertmp.length() - 1); + data.set_Alluser(allusertmp); + if (data.getMessageEntryList().get(0) != null) { + String title = data.getMessageEntryList().get(0).getSubject(); + if (!StringUtils.isEmpty(title)) { + data.set_Title(title); + } else { + data.set_Title(""); + } + } + return data; + + } + private List convertJSobjToList(JSONObject rowMap, JSONObject userInfoMap, int page) { + List __R = new ArrayList(); + if (rowMap == null) + return null; + rowMap = (JSONObject) rowMap.get("allmsgs"); + JSONObject rowObj = (JSONObject) rowMap.get("0"); + for (int i = 1; rowObj != null; i++) { + MessageArticlePageInfo row = new MessageArticlePageInfo(); + + row.setContent(rowObj.getString("content")); + row.setLou(20 * (page - 1) + i); + row.setSubject(rowObj.getString("subject")); + int time = rowObj.getIntValue("time"); + if (time > 0) { + row.setTime(StringUtils.timeStamp2Date1(String.valueOf(time))); + } else { + row.setTime(""); + } + row.setFrom(rowObj.getString("from")); + fillUserInfo(row, userInfoMap); + formatContent(row); + __R.add(row); + rowObj = (JSONObject) rowMap.get(String.valueOf(i)); + } + return __R; + } + private static void formatContent(MessageArticlePageInfo row) { + String content = row.getContent(); + content = StringUtils.replaceAll(content, "\\[quote\\](.+?)\\[/quote\\]", "\n$1\n").replaceAll("
", "\n"); + row.setContent(content); - public static int showImageQuality() { - return 0; -// if (isInWifi()) { -// return 0; -// } else { -// return PhoneConfiguration.getInstance().imageQuality; -// } + List> contentSections = row.getContentSections(); + for (String section : row.getContent().split("\\[url]")) { + if (section.contains("[/url]")) { + String[] urlSection = section.split("\\[/url]"); + contentSections.add(new Pair<>(urlSection[0], Boolean.TRUE)); + if (urlSection.length > 1) { + contentSections.add(new Pair<>(urlSection[1], Boolean.FALSE)); + } + } else { + contentSections.add(new Pair<>(section, Boolean.FALSE)); + } + } + row.setContentSections(contentSections); } private void fillUserInfo(MessageArticlePageInfo row, JSONObject userInfoMap) { @@ -136,13 +281,4 @@ private void fillUserInfo(MessageArticlePageInfo row, JSONObject userInfoMap) { row.setSignature(userInfo.getString("signature")); } - private static String buildHeader(MessageArticlePageInfo row, String fgColorStr) { - if (row == null || StringUtils.isEmpty(row.getSubject())) - return ""; - StringBuilder sb = new StringBuilder(); - sb.append("

") - .append(row.getSubject()).append("

"); - return sb.toString(); - } - } diff --git a/lib_module_message/src/main/java/com/justwen/androidnga/module/message/compose/MessageRepository.kt b/lib_module_message/src/main/java/com/justwen/androidnga/module/message/compose/MessageRepository.kt index 07ce8e67..54a1b09c 100644 --- a/lib_module_message/src/main/java/com/justwen/androidnga/module/message/compose/MessageRepository.kt +++ b/lib_module_message/src/main/java/com/justwen/androidnga/module/message/compose/MessageRepository.kt @@ -43,7 +43,7 @@ class MessagePagingSource : PagingSource() { val factory = MessageConvertFactory() var nextKey: Int? = null val result = factory.getMessageListInfo(jsonString)?.let { - nextKey = if (it.__nextPage > 0) it.__nextPage else null + nextKey = if (it.__nextPage > 0) page + 1 else null it.messageEntryList ?: emptyList() } diff --git a/lib_module_message/src/main/java/com/justwen/androidnga/module/message/compose/MessageViewModel.kt b/lib_module_message/src/main/java/com/justwen/androidnga/module/message/compose/MessageViewModel.kt index e83e411c..41082a7e 100644 --- a/lib_module_message/src/main/java/com/justwen/androidnga/module/message/compose/MessageViewModel.kt +++ b/lib_module_message/src/main/java/com/justwen/androidnga/module/message/compose/MessageViewModel.kt @@ -4,11 +4,12 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.paging.PagingData import androidx.paging.cachedIn +import com.justwen.androidnga.core.data.MessageThreadPageInfo import kotlinx.coroutines.flow.Flow class MessageViewModel : ViewModel() { - fun getMessageListData(): Flow> { + fun getMessageListData(): Flow> { return MessageRepository.getMessageListData().cachedIn(viewModelScope) } } \ No newline at end of file diff --git a/lib_module_message/src/main/java/com/justwen/androidnga/module/message/compose/detail/MessageDetailActivity.kt b/lib_module_message/src/main/java/com/justwen/androidnga/module/message/compose/detail/MessageDetailActivity.kt new file mode 100644 index 00000000..cc07d3e9 --- /dev/null +++ b/lib_module_message/src/main/java/com/justwen/androidnga/module/message/compose/detail/MessageDetailActivity.kt @@ -0,0 +1,154 @@ +package com.justwen.androidnga.module.message.compose.detail + +import android.content.ActivityNotFoundException +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.text.TextUtils +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.ClickableText +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.withStyle +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.lifecycle.ViewModelProvider +import androidx.paging.compose.collectAsLazyPagingItems +import com.alibaba.android.arouter.facade.annotation.Route +import com.alibaba.android.arouter.launcher.ARouter +import com.justwen.androidnga.base.activity.ARouterConstants +import com.justwen.androidnga.core.data.MessageArticlePageInfo +import com.justwen.androidnga.ui.compose.BaseComposeActivity +import com.justwen.androidnga.ui.compose.widget.PullRefreshColumn + +@Route(path = ARouterConstants.ACTIVITY_MESSAGE_DETAIL) +class MessageDetailActivity : BaseComposeActivity() { + + lateinit var mid: String + + private val viewModel: MessageDetailModel by lazy { + ViewModelProvider(this)[MessageDetailModel::class.java] + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mid = intent.getIntExtra("mid", 0).toString() + } + + override fun getFabClickAction(): () -> Unit { + return { + ARouter.getInstance() + .build(ARouterConstants.ACTIVITY_MESSAGE_POST) + .withInt("mid", mid.toInt()) + .withString("action", "reply") + .withString("to", viewModel.getRecipient()) + .withString("title", viewModel.getMessageTitle()) + .navigation(this) + } + } + + @Composable + override fun ContentView() { + val items = viewModel.getMessageDetailData(mid = mid).collectAsLazyPagingItems() + + PullRefreshColumn(columnItem = { MessageListItem(messageInfo = it) }, + lazyPagingItems = items, + onRefresh = { items.refresh() }) + } + + private fun startWebView(url: String) { + val intent = Intent(Intent.ACTION_VIEW) + intent.setData(Uri.parse(url)) + try { + startActivity(intent) + } catch (e: ActivityNotFoundException) { + e.printStackTrace() + } + } + + @Preview + @Composable + fun MessageListItem(messageInfo: MessageArticlePageInfo = MessageArticlePageInfo()) { + + val annotatedText = buildAnnotatedString { + messageInfo.contentSections.forEach(action = { + if (!it.second) { + withStyle(style = SpanStyle(fontSize = 16.sp)) { + append(it.first) + } + } else { + pushStringAnnotation(tag = "URL", annotation = it.first) + withStyle(style = SpanStyle(color = Color.Blue, fontWeight = FontWeight.Bold, fontSize = 16.sp)) { + append(it.first) + } + //代表结束 + pop() + } + }) + } + + Column( + modifier = Modifier + .fillMaxWidth() + .height(IntrinsicSize.Min) + .padding(16.dp) + ) { + + if (!messageInfo.subject.isNullOrEmpty()) { + Text( + text = messageInfo.subject, + fontWeight = FontWeight.SemiBold, + fontSize = 18.sp, + modifier = Modifier.padding(bottom = 8.dp), + ) + } + + ClickableText( + text = annotatedText, + onClick = { + val annotationList = + annotatedText.getStringAnnotations(tag = "URL", start = it, end = it) + annotationList.firstOrNull()?.let { annotation -> + startWebView(annotation.item) + } + }, + ) + + var userName = messageInfo.author; + if (TextUtils.isEmpty(userName)) { + userName = "#SYSTEM#" + } + + Box( + modifier = Modifier + .padding(top = 12.dp) + .fillMaxWidth() + ) { + Text( + modifier = Modifier.align(alignment = Alignment.CenterStart), + text = userName, fontSize = 14.sp, color = Color(0xFF294563), + ) + Text( + modifier = Modifier.align(alignment = Alignment.CenterEnd), + text = messageInfo.time, + fontSize = 14.sp, + color = Color(0xFF294563) + ) + } + + } + } +} + diff --git a/lib_module_message/src/main/java/com/justwen/androidnga/module/message/compose/detail/MessageDetailModel.kt b/lib_module_message/src/main/java/com/justwen/androidnga/module/message/compose/detail/MessageDetailModel.kt new file mode 100644 index 00000000..36f56d88 --- /dev/null +++ b/lib_module_message/src/main/java/com/justwen/androidnga/module/message/compose/detail/MessageDetailModel.kt @@ -0,0 +1,24 @@ +package com.justwen.androidnga.module.message.compose.detail + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import androidx.paging.PagingData +import androidx.paging.cachedIn +import com.justwen.androidnga.core.data.MessageArticlePageInfo +import kotlinx.coroutines.flow.Flow + +class MessageDetailModel : ViewModel() { + + fun getMessageDetailData(mid: String): Flow> { + return MessageDetailRepository.getMessageDetailData(mid).cachedIn(viewModelScope) + } + + fun getRecipient(): String? { + return MessageDetailRepository.recipient + } + + fun getMessageTitle(): String? { + return MessageDetailRepository.msgTitle + } + +} \ No newline at end of file diff --git a/lib_module_message/src/main/java/com/justwen/androidnga/module/message/compose/detail/MessageDetailRepository.kt b/lib_module_message/src/main/java/com/justwen/androidnga/module/message/compose/detail/MessageDetailRepository.kt new file mode 100644 index 00000000..5a35ddb6 --- /dev/null +++ b/lib_module_message/src/main/java/com/justwen/androidnga/module/message/compose/detail/MessageDetailRepository.kt @@ -0,0 +1,73 @@ +package com.justwen.androidnga.module.message.compose.detail + +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData +import androidx.paging.PagingSource +import androidx.paging.PagingState +import com.justwen.androidnga.base.network.retrofit.RetrofitHelper +import com.justwen.androidnga.base.network.retrofit.RetrofitServiceKt +import com.justwen.androidnga.core.data.MessageArticlePageInfo +import com.justwen.androidnga.module.message.MessageConvertFactory +import kotlinx.coroutines.flow.Flow + +object MessageDetailRepository { + + private const val PAGE_SIZE = 20 + + var recipient: String? = null + + var msgTitle: String? = null + + fun getMessageDetailData(mid: String): Flow> { + return Pager( + config = PagingConfig(PAGE_SIZE), + pagingSourceFactory = { MessageDetailPagingSource(mid) }).flow + } + +} + +class MessageDetailPagingSource(mid: String) : PagingSource() { + + private val netService = + RetrofitHelper.getInstance().getService(RetrofitServiceKt::class.java) as RetrofitServiceKt + + /** + * http://bbs.nga.cn/nuke.php?__lib=message&__act=message&act=read&page=1&mid=1&lite=js + */ + private val paramMap: HashMap = + hashMapOf("__lib" to "message", "__act" to "message", "act" to "read", "lite" to "js") + + init { + paramMap["mid"] = mid + } + + override fun getRefreshKey(state: PagingState): Int? { + return null; + } + + override suspend fun load(params: LoadParams): LoadResult { + try { + val page = params.key ?: 1 + val preKey = if (page > 1) page - 1 else null + paramMap["page"] = page.toString() + val jsonString = netService.getString(paramMap) + val factory = MessageConvertFactory() + var nextKey: Int? = null + val result = factory.getMessageDetailInfo(jsonString, page)?.let { + nextKey = if (it.__nextPage > 0) page + 1 else null + MessageDetailRepository.msgTitle = it._Title + MessageDetailRepository.recipient = it._Alluser + it.messageEntryList ?: emptyList() + } + + if (!result.isNullOrEmpty()) { + return LoadResult.Page(result, preKey, nextKey) + } else { + return LoadResult.Error(Exception(factory.errorMsg)) + } + } catch (e: Exception) { + return LoadResult.Error(e) + } + } +} \ No newline at end of file diff --git a/lib_module_message/src/main/res/values/strings.xml b/lib_module_message/src/main/res/values/strings.xml index 16aef3a7..962f3045 100644 --- a/lib_module_message/src/main/res/values/strings.xml +++ b/lib_module_message/src/main/res/values/strings.xml @@ -2,4 +2,5 @@ 短消息 短消息正文 + 网络错误 \ No newline at end of file diff --git a/nga_phone_base_3.0/src/main/AndroidManifest.xml b/nga_phone_base_3.0/src/main/AndroidManifest.xml index ac728a4e..aac52ff2 100644 --- a/nga_phone_base_3.0/src/main/AndroidManifest.xml +++ b/nga_phone_base_3.0/src/main/AndroidManifest.xml @@ -24,7 +24,7 @@ android:requestLegacyExternalStorage="true" android:preserveLegacyExternalStorage="true" android:roundIcon="@mipmap/ic_launcher_round" - android:theme="@android:style/Theme.Holo.Light" + android:theme="@style/AppThemeDayNight" tools:ignore="AllowBackup" tools:replace="android:label"> diff --git a/nga_phone_base_3.0/src/main/java/gov/anzong/androidnga/activity/MessageDetailActivity.java b/nga_phone_base_3.0/src/main/java/gov/anzong/androidnga/activity/MessageDetailActivity.java index 091d4268..594c6bc4 100644 --- a/nga_phone_base_3.0/src/main/java/gov/anzong/androidnga/activity/MessageDetailActivity.java +++ b/nga_phone_base_3.0/src/main/java/gov/anzong/androidnga/activity/MessageDetailActivity.java @@ -11,7 +11,6 @@ import sp.phone.ui.fragment.MessageDetailFragment; import sp.phone.util.StringUtils; -@Route(path = ARouterConstants.ACTIVITY_MESSAGE_DETAIL) public class MessageDetailActivity extends BaseActivity { @Override