feat: 组件化消息中心相关功能 https://jira.shanqu.cc/browse/GHZS-2972

This commit is contained in:
叶子维
2023-07-24 17:49:02 +08:00
parent 33ed2796c8
commit f9c6bb1e56
150 changed files with 3260 additions and 985 deletions

View File

@ -0,0 +1,24 @@
package com.gh.gamecenter.message
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.gh.gamecenter.message", appContext.packageName)
}
}

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.gh.gamecenter.message">
<application>
<activity
android:name=".view.MessageActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".view.MessageKeFuActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".view.MessageVoteActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".view.MessageInviteActivity"
android:exported="false"
android:screenOrientation="portrait" />
</application>
</manifest>

View File

@ -0,0 +1,43 @@
package com.gh.gamecenter.message
import android.app.Application
import android.content.res.Configuration
import com.gh.gamecenter.core.iinterface.IApplication
import com.google.auto.service.AutoService
@AutoService(IApplication::class)
class HaloApp : IApplication {
override fun attachBaseContext() {
// Do nothing
}
override fun onCreate(application: Application) {
mApp = application
}
override fun onLowMemory() {
// Do nothing
}
override fun onTerminate() {
// Do nothing
}
override fun onTrimMemory(level: Int) {
// Do nothing
}
override fun onConfigurationChanged(newConfig: Configuration) {
// Do nothing
}
companion object {
private lateinit var mApp: Application
@JvmStatic
fun getInstance(): Application {
return mApp
}
}
}

View File

@ -0,0 +1,8 @@
package com.gh.gamecenter.message.entity
import com.gh.gamecenter.feature.entity.UserEntity
data class MessageFold(
val time: Long = 0,
var user: UserEntity = UserEntity()
)

View File

@ -0,0 +1,50 @@
package com.gh.gamecenter.message.entity
import com.gh.gamecenter.common.entity.LinkEntity
import com.gh.gamecenter.feature.entity.Auth
import com.google.gson.annotations.SerializedName
/**
* Created by khy on 2017/4/10.
*/
class MessageKeFuEntity {
@SerializedName("_id")
var id: String = ""
@SerializedName("read")
var isRead: Boolean = true
@SerializedName("receive")
var isReceive: Boolean = false
var message: String? = null
var time: Long = 0
var suggestion: String? = null
var links: List<MessageLinkEntity>? = null
@SerializedName("new_links")
var newLinks: List<LinkEntity>? = null
var images: List<String>? = null
@SerializedName("service")
var serviceEntity: ServiceEntity? = null
@SerializedName("show_user_id")
var showUserId: Boolean? = false
var type: String = ""
class ServiceEntity {
var name: String? = null
var icon: String? = null
@SerializedName("_id")
var id: String? = null
var auth: Auth? = null
}
}

View File

@ -0,0 +1,28 @@
package com.gh.gamecenter.message.entity
import com.gh.gamecenter.common.entity.CommunityEntity
import com.google.gson.annotations.SerializedName
/**
* Created by khy on 2017/4/18.
*/
class MessageLinkEntity {
@SerializedName("_id")
var id: String? = null
var document: String? = null
var type: String? = null
var url: String? = null
var qq: String? = null
var key: String? = null
@SerializedName("activity_id")
var activityId: String? = null
var community: CommunityEntity? = null
}

View File

@ -0,0 +1,20 @@
package com.gh.gamecenter.message.provider
import android.content.Context
import android.content.Intent
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.feature.provider.IMessageProvider
import com.gh.gamecenter.message.view.MessageActivity
@Route(path = RouteConsts.provider.message, name = "MessageActivity暴露服务")
class MessageProviderImpl : IMessageProvider {
override fun getIntent(context: Context, entrance: String): Intent {
return MessageActivity.getIntent(context, entrance)
}
override fun init(context: Context?) {
// Do nothing
}
}

View File

@ -0,0 +1,62 @@
package com.gh.gamecenter.message.retrofit
import com.gh.gamecenter.feature.entity.MessageEntity
import com.gh.gamecenter.message.entity.*
import io.reactivex.Observable
import okhttp3.RequestBody
import okhttp3.ResponseBody
import retrofit2.http.*
interface ApiService {
@GET("users/{user_id}/messages")
fun getMessage(
@Path("user_id") user_id: String,
@Query("view") type: String,
@Query("timestamp") timestamp: Long,
@Query("page") page: Int
): Observable<List<MessageEntity>>
/**
* 将消息删除
*/
@POST("users/{user_id}/messages/{message_id}:inactivate")
fun deleteMessage(
@Path("user_id") userId: String,
@Path("message_id") messageId: String
): Observable<ResponseBody>
/**
* 将消息标记为已读
*/
@POST("users/{user_id}/messages/{message_id}:read")
fun postMessageRead(
@Path("user_id") userId: String,
@Path("message_id") messageId: String,
@Body body: RequestBody?
): Observable<ResponseBody>
/**
* 获取消息-客服数据
*/
@GET("users/{user_id}/private_messages")
fun getMessageKeFuData(
@Path("user_id") user_id: String,
@Query("page") page: Int
): Observable<List<MessageKeFuEntity>>
/**
* 将消息删除
*/
@POST("users/{user_id}/private_messages/{message_id}:inactivate")
fun deleteKaiFuMessage(
@Path("user_id") userId: String,
@Path("message_id") messageId: String
): Observable<ResponseBody>
@GET("users/{user_id}/messages/{resource_id}/fold-list")
fun getMessageFoldList(
@Path("user_id") userId: String,
@Path("resource_id") resourceId: String
): Observable<List<MessageFold>>
}

View File

@ -0,0 +1,19 @@
package com.gh.gamecenter.message.retrofit
import com.gh.gamecenter.common.retrofit.BaseRetrofitManager
import com.gh.gamecenter.common.utils.EnvHelper.getHost
import com.gh.gamecenter.core.utils.SingletonHolder
import com.gh.gamecenter.message.HaloApp
class RetrofitManager private constructor() : BaseRetrofitManager() {
val api: ApiService
init {
val context = HaloApp.getInstance().applicationContext
val okHttpNormalConfig = getOkHttpConfig(context, 0, 2)
api = provideService(okHttpNormalConfig, getHost(), ApiService::class.java)
}
companion object : SingletonHolder<RetrofitManager>(::RetrofitManager)
}

View File

@ -0,0 +1,77 @@
package com.gh.gamecenter.message.utils
import com.gh.gamecenter.common.json.JsonObjectBuilder
import com.gh.gamecenter.common.json.json
import com.gh.gamecenter.common.loghub.LoghubUtils
import com.gh.gamecenter.common.utils.LogUtils
import com.lightgame.utils.Utils
import org.json.JSONObject
object NewLogUtils {
private const val KEY_EVENT = "event"
private const val KEY_GAME_ID = "game_id"
private const val KEY_CONTENT_ID = "content_id"
private const val LOG_STORE_EVENT = "event"
private fun log(jsonObject: JSONObject, logStore: String, uploadImmediately: Boolean = false) {
Utils.log("NewLogUtils", jsonObject.toString(4))
LoghubUtils.log(jsonObject, logStore, uploadImmediately)
}
fun parseAndPutMeta(): JsonObjectBuilder.() -> Unit = {
val meta = LogUtils.getMetaObject()
val metaKeys = meta.keys()
while (metaKeys.hasNext()) {
val key: String = metaKeys.next().toString()
val value = meta.getString(key)
key to value
}
}
//点击消息中心-头部图标/赞同/系统图标
@JvmStatic
fun logMessageInformTopIconClick(
isInform: Boolean,
displayType: String,
) {
val json = json {
KEY_EVENT to "message_inform_top_icon_click"
"is_inform" to isInform
"display_type" to displayType
parseAndPutMeta().invoke(this)
}
log(json, LOG_STORE_EVENT)
}
//点击消息通知
@JvmStatic
fun logMessageInformClick(
contentId: String,
newsId: String,
gameId: String,
gameCollectionId: String,
messageType: String
) {
val json = json {
KEY_EVENT to "message_inform_click"
KEY_CONTENT_ID to contentId
"news_id" to newsId
KEY_GAME_ID to gameId
"game_collect_id" to gameCollectionId
"message_type" to messageType
parseAndPutMeta().invoke(this)
}
log(json, LOG_STORE_EVENT)
}
//点击消息通知信息用户头像/昵称
@JvmStatic
fun logMessageInformUserClick(event: String) {
val json = json {
KEY_EVENT to event
parseAndPutMeta().invoke(this)
}
log(json, LOG_STORE_EVENT)
}
}

View File

@ -0,0 +1,52 @@
package com.gh.gamecenter.message.utils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
public class NewsUtils {
public static String getFormattedTime(long time) {
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd", Locale.getDefault());
try {
long day = time * 1000;
String year = String.valueOf(Calendar.getInstance().get(Calendar.YEAR));
format.applyPattern("yyyy");
String currentYear = format.format(day);
format.applyPattern("yyyyMMdd");
long today = format.parse(format.format(new Date())).getTime();
if (day >= today && day < today + 86400 * 1000) {
long min = new Date().getTime() / 1000 - day / 1000;
int hour = (int) (min / (60 * 60));
if (hour == 0) {
if (min < 60) {
return "刚刚";
} else {
return (String.format(Locale.getDefault(), "%d分钟前", (int) (min / 60)));
}
} else {
return (String.format(Locale.getDefault(), "%d小时前", hour));
}
} else if (day >= today - 86400 * 1000 && day < today) {
format.applyPattern("HH:mm");
return ("昨天 ");
} else if (day >= today - 86400 * 1000 * 7 && day < today - 86400 * 1000) {
format.applyPattern("HH:mm");
long days = (today - day) / 86400000 + 1;
return String.format(Locale.getDefault(), "%d天前 ", days);
} else if (day < today - 86400 * 1000 * 7 && year.equals(currentYear)) {
format.applyPattern("MM-dd");
return (format.format(day));
} else {
format.applyPattern("yyyy-MM-dd");
return (format.format(day));
}
} catch (ParseException e) {
e.printStackTrace();
format.applyPattern("yyyy-MM-dd");
return (format.format(time * 1000));
}
}
}

View File

@ -0,0 +1,106 @@
package com.gh.gamecenter.message.view
import android.app.Activity
import android.content.Context
import android.graphics.Point
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.alibaba.android.arouter.launcher.ARouter
import com.gh.gamecenter.common.base.fragment.BaseDialogFragment
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.utils.ImageUtils
import com.gh.gamecenter.common.utils.toBinding
import com.gh.gamecenter.feature.entity.UserEntity
import com.gh.gamecenter.core.provider.IDirectProvider
import com.gh.gamecenter.message.databinding.DialogAskFollowMoreBinding
import com.gh.gamecenter.message.databinding.ItemAskFollowMoreBinding
import com.gh.gamecenter.message.utils.NewsUtils
class AskFollowMoreDialog : BaseDialogFragment() {
private var mType = ""
private var mPath = ""
private var mUserList = ArrayList<UserEntity>()
private lateinit var mBinding: DialogAskFollowMoreBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requireArguments().run {
mType = getString(KEY_TYPE) ?: ""
mPath = getString(KEY_PATH) ?: ""
mUserList = getParcelableArrayList(KEY_USER_LIST) ?: arrayListOf()
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return DialogAskFollowMoreBinding.inflate(LayoutInflater.from(requireContext()), null, false).apply {
mBinding = this
}.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
dialog?.setCanceledOnTouchOutside(true)
val lp = mBinding.container.layoutParams
lp.width = ((getScreenWidth(requireActivity())) * 0.8).toInt()
mBinding.container.layoutParams = lp
mBinding.tvTitle.text = mType
mBinding.recyclerview.adapter = AskFollowMoreAdapter(requireContext(), mUserList, mPath)
mBinding.recyclerview.layoutManager = LinearLayoutManager(mBinding.recyclerview.context)
}
private fun getScreenWidth(activity: Activity): Int {
val size = Point()
activity.windowManager.defaultDisplay.getSize(size)
return size.x
}
class AskFollowMoreAdapter(var context: Context, var list: ArrayList<UserEntity>, var path: String) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return ItemViewHolder(parent.toBinding())
}
override fun getItemCount(): Int {
return list.size
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
(holder as ItemViewHolder).run {
val user = list[position]
ImageUtils.display(binding.ivIcon, user.icon)
binding.tvUsername.text = user.name
binding.tvTime.text = NewsUtils.getFormattedTime(user.time ?: 0)
binding.root.setOnClickListener {
val directUtils =
ARouter.getInstance().build(RouteConsts.provider.directUtils).navigation() as? IDirectProvider
directUtils?.directToHomeActivity(context, list[position].id, "问答-关注", path)
}
}
}
class ItemViewHolder(var binding: ItemAskFollowMoreBinding) : RecyclerView.ViewHolder(binding.root)
}
companion object {
const val KEY_TYPE = "type"
const val KEY_PATH = "path"
const val KEY_USER_LIST = "user_list"
@JvmStatic
fun getInstance(type: String, path: String, userList: ArrayList<UserEntity>) = AskFollowMoreDialog().apply {
arguments = Bundle().apply {
putString(KEY_TYPE, type)
putString(KEY_PATH, path)
putParcelableArrayList(KEY_USER_LIST, userList)
}
}
}
}

View File

@ -0,0 +1,50 @@
package com.gh.gamecenter.message.view
import android.os.Bundle
import android.view.View
import com.gh.gamecenter.common.baselist.ListAdapter
import com.gh.gamecenter.common.baselist.ListFragment
import com.gh.gamecenter.common.utils.viewModelProvider
import com.gh.gamecenter.core.utils.MtaHelper.onEvent
import com.gh.gamecenter.message.R
import com.gh.gamecenter.message.entity.MessageKeFuEntity
class KeFuFragment : ListFragment<MessageKeFuEntity, KeFuViewModel>() {
private var mAdapter: KeFuFragmentAdapter? = null
private var mUnreadViewModel: MessageUnreadViewModel? = null
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
setNavigationTitle("系统")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mUnreadViewModel = viewModelProvider()
}
override fun onLoadDone() {
super.onLoadDone()
mUnreadViewModel?.markRead(MessageUnreadViewModel.ReadType.SERVICE)
}
override fun provideListAdapter(): ListAdapter<*> {
return if (mAdapter == null) KeFuFragmentAdapter(
context,
this,
mListViewModel,
mEntrance
).also { mAdapter = it } else mAdapter!!
}
override fun <LIST : Any?> onListClick(view: View?, position: Int, data: Any?) {
if (view?.id == R.id.message_kaifu_item) {
onEvent("消息中心", "系统_二级列表", "点击卡片")
val keFuEntity: MessageKeFuEntity = data as MessageKeFuEntity
if (!keFuEntity.isRead) {
mListViewModel.postMessageRead(keFuEntity.id)
mAdapter?.notifyDataSetChanged()
}
}
}
}

View File

@ -0,0 +1,519 @@
package com.gh.gamecenter.message.view;
import android.app.Activity;
import android.content.Context;
import android.text.Html;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.alibaba.android.arouter.launcher.ARouter;
import com.facebook.drawee.generic.GenericDraweeHierarchy;
import com.facebook.drawee.generic.RoundingParams;
import com.facebook.drawee.view.SimpleDraweeView;
import com.gh.gamecenter.common.base.activity.BaseActivity;
import com.gh.gamecenter.common.baselist.ListAdapter;
import com.gh.gamecenter.common.callback.OnListClickListener;
import com.gh.gamecenter.common.constant.Constants;
import com.gh.gamecenter.common.constant.EntranceConsts;
import com.gh.gamecenter.common.constant.ItemViewType;
import com.gh.gamecenter.common.constant.RouteConsts;
import com.gh.gamecenter.common.entity.LinkEntity;
import com.gh.gamecenter.common.utils.DialogHelper;
import com.gh.gamecenter.common.utils.ExtensionsKt;
import com.gh.gamecenter.common.utils.ImageUtils;
import com.gh.gamecenter.common.viewholder.FooterViewHolder;
import com.gh.gamecenter.feature.provider.ICommentUtilsProvider;
import com.gh.gamecenter.core.provider.IDirectProvider;
import com.gh.gamecenter.feature.provider.ISubjectProvider;
import com.gh.gamecenter.core.utils.DisplayUtils;
import com.gh.gamecenter.core.utils.GsonUtils;
import com.gh.gamecenter.core.utils.MtaHelper;
import com.gh.gamecenter.feature.exposure.ExposureSource;
import com.gh.gamecenter.feature.provider.IGameDetailProvider;
import com.gh.gamecenter.feature.provider.ILinkDirectUtilsProvider;
import com.gh.gamecenter.login.user.UserManager;
import com.gh.gamecenter.message.R;
import com.gh.gamecenter.message.databinding.MessageKefuItemBinding;
import com.gh.gamecenter.message.entity.MessageKeFuEntity;
import com.gh.gamecenter.message.entity.MessageLinkEntity;
import com.gh.gamecenter.message.utils.NewLogUtils;
import com.google.android.flexbox.FlexboxLayout;
import com.lightgame.utils.Utils;
import java.util.ArrayList;
import java.util.List;
/**
* Created by khy on 2017/4/10.
* 消息-客服适配器
*/
public class KeFuFragmentAdapter extends ListAdapter<MessageKeFuEntity> {
private OnListClickListener mClickListener;
private KeFuViewModel mListViewModel;
private String mEntrance;
private int mImageSize;
private IDirectProvider mDirectUtils;
private ILinkDirectUtilsProvider mLinkDirectUtils;
private IGameDetailProvider mGameDetailProvider;
public KeFuFragmentAdapter(Context context, OnListClickListener listener, KeFuViewModel listViewModel, String entrance) {
super(context);
mListViewModel = listViewModel;
mClickListener = listener;
mEntrance = entrance;
mImageSize = (mContext.getResources().getDisplayMetrics().widthPixels - DisplayUtils.dip2px(36)) / 3;
mDirectUtils = (IDirectProvider) ARouter.getInstance().build(RouteConsts.provider.directUtils).navigation();
mLinkDirectUtils = (ILinkDirectUtilsProvider) ARouter.getInstance().build(RouteConsts.provider.linkDirectUtils).navigation();
mGameDetailProvider = (IGameDetailProvider) ARouter.getInstance().build(RouteConsts.provider.gameDetail).navigation();
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == ItemViewType.ITEM_FOOTER) {
View view = mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false);
return new FooterViewHolder(view);
} else {
return new KeFuViewHolder(MessageKefuItemBinding.inflate(mLayoutInflater, parent, false), mClickListener);
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof KeFuViewHolder) {
MessageKeFuEntity keFuEntity = mEntityList.get(position);
KeFuViewHolder viewHolder = (KeFuViewHolder) holder;
initKeFuViewHolder(viewHolder, keFuEntity);
} else if (holder instanceof FooterViewHolder) {
((FooterViewHolder) holder).initFooterViewHolder(mIsLoading, mIsNetworkError, mIsOver);
((FooterViewHolder) holder).initItemPadding();
}
}
@Override
public int getItemViewType(int position) {
if (position == getItemCount() - 1) {
return ItemViewType.ITEM_FOOTER;
} else {
return ItemViewType.ITEM_BODY;
}
}
@Override
public int getItemCount() {
return mEntityList == null || mEntityList.isEmpty() ? 0 : mEntityList.size() + FOOTER_ITEM_COUNT;
}
private void initKeFuViewHolder(KeFuViewHolder viewHolder, final MessageKeFuEntity keFuEntity) {
viewHolder.binding.messageKaifuItem.setBackground(ContextCompat.getDrawable(mContext, R.drawable.reuse_listview_item_style));
viewHolder.binding.messageKefuSuggestion.setBackgroundColor(ContextCompat.getColor(mContext, R.color.background));
viewHolder.binding.messageKefuSuggestion.setTextColor(ContextCompat.getColor(mContext, R.color.title));
viewHolder.binding.messageKefuName.setTextColor(ContextCompat.getColor(mContext, R.color.text_black));
viewHolder.binding.messageKefuContent.setTextColor(ContextCompat.getColor(mContext, R.color.title));
viewHolder.binding.messageKefuTime.setTextColor(ContextCompat.getColor(mContext, R.color.hint));
viewHolder.binding.copyTv.setTextColor(ContextCompat.getColor(mContext, R.color.text_subtitle));
boolean shouldShouldUserId = keFuEntity.getShowUserId() != null && keFuEntity.getShowUserId();
List<String> images = keFuEntity.getImages();
if (images != null && !images.isEmpty()) {
viewHolder.binding.messageKefuImagesContainer.removeAllViews();
viewHolder.binding.messageKefuImagesContainer.setVisibility(View.VISIBLE);
for (int i = 0; i < images.size(); i++) {
String image = images.get(i);
SimpleDraweeView draweeView = new SimpleDraweeView(mContext);
int padding = DisplayUtils.dip2px(2);
draweeView.setPadding(padding, padding, padding, padding);
FlexboxLayout.LayoutParams params = new FlexboxLayout.LayoutParams(mImageSize, mImageSize);
draweeView.setLayoutParams(params);
draweeView.setImageURI(image);
int finalI = i;
draweeView.setOnClickListener(v -> {
ARouter.getInstance().build(RouteConsts.activity.imageViewerActivity)
.withStringArrayList(EntranceConsts.KEY_URL_LIST, (ArrayList<String>) images)
.withInt(EntranceConsts.KEY_CURRENT, finalI)
.withString(EntranceConsts.KEY_ENTRANCE, "(消息中心-系统)")
.navigation();
MtaHelper.onEvent("消息中心", "系统_二级列表", "点击图片");
});
GenericDraweeHierarchy hierarchy = draweeView.getHierarchy();
RoundingParams roundingParams = RoundingParams.fromCornersRadius(18f);
hierarchy.setRoundingParams(roundingParams);
hierarchy.setPlaceholderImage(R.drawable.occupy);
viewHolder.binding.messageKefuImagesContainer.addView(draweeView);
}
viewHolder.binding.messageKefuImagesContainer.requestLayout();
} else {
viewHolder.binding.messageKefuImagesContainer.setVisibility(View.GONE);
}
ExtensionsKt.goneIf(viewHolder.binding.copyIdContainer, !shouldShouldUserId);
if (shouldShouldUserId) {
viewHolder.binding.copyIdContainer.setOnClickListener(view -> ExtensionsKt.copyTextAndToast(UserManager.getInstance().getUserId(), "已复制"));
}
viewHolder.binding.messageKefuContent.setText(Html.fromHtml(keFuEntity.getMessage()));
viewHolder.binding.messageKefuContent.setOnSpannableClickListener(spannableText -> ExtensionsKt.copyTextAndToast(spannableText, "已复制:" + spannableText));
viewHolder.setClickData(keFuEntity);
if (!TextUtils.isEmpty(keFuEntity.getSuggestion())) {
viewHolder.binding.messageKefuSuggestion.setVisibility(View.VISIBLE);
viewHolder.binding.messageKefuSuggestion.setText(keFuEntity.getSuggestion());
} else {
viewHolder.binding.messageKefuSuggestion.setVisibility(View.GONE);
}
List<LinkEntity> newLinks = keFuEntity.getNewLinks();
if (newLinks != null && !newLinks.isEmpty()) {
viewHolder.binding.messageSkipList.setVisibility(View.VISIBLE);
viewHolder.binding.messageSkipList.removeAllViews();
for (LinkEntity linkEntity : newLinks) {
if (TextUtils.isEmpty(linkEntity.getText()) && TextUtils.isEmpty(linkEntity.getTitle()))
continue;
TextView textView = getLinkChildView(viewHolder, !TextUtils.isEmpty(linkEntity.getTitle()) ? linkEntity.getTitle() : linkEntity.getText());
textView.setOnClickListener(v -> {
if (!keFuEntity.isRead()) {
mListViewModel.postMessageRead(keFuEntity.getId());
notifyDataSetChanged();
}
linkNewSkip(linkEntity);
if ("求加速回复".equals(keFuEntity.getType())) {
NewLogUtils.logMessageInformClick("", "", "", "", "求加速版本");
} else if ("求版本回复".equals(keFuEntity.getType())) {
NewLogUtils.logMessageInformClick("", "", "", "", "投票");
}
});
}
} else {
List<MessageLinkEntity> links = keFuEntity.getLinks();
if (links != null && !links.isEmpty()) {
viewHolder.binding.messageSkipList.setVisibility(View.VISIBLE);
viewHolder.binding.messageSkipList.removeAllViews();
for (MessageLinkEntity link : links) {
if (TextUtils.isEmpty(link.getQq()) &&
TextUtils.isEmpty(link.getId()) &&
TextUtils.isEmpty(link.getUrl()) &&
!"7moor".equals(link.getType()) &&
!"个人主页".equals(link.getType())) continue;
TextView textView = getLinkChildView(viewHolder, link.getDocument());
textView.setOnClickListener(v -> {
if (!keFuEntity.isRead()) {
mListViewModel.postMessageRead(keFuEntity.getId());
notifyDataSetChanged();
}
linkSkip(link);
if ("求加速回复".equals(keFuEntity.getType())) {
NewLogUtils.logMessageInformClick("", "", link.getId() == null ? "" : link.getId(), "", "求加速版本");
} else if ("求版本回复".equals(keFuEntity.getType())) {
NewLogUtils.logMessageInformClick("", "", link.getId() == null ? "" : link.getId(), "", "投票");
}
});
}
} else {
viewHolder.binding.messageSkipList.setVisibility(View.GONE);
}
}
MessageKeFuEntity.ServiceEntity serviceEntity = keFuEntity.getServiceEntity();
viewHolder.binding.messageKefuName.setText(R.string.kefu_default_name);
if (serviceEntity != null) {
String name = serviceEntity.getName();
if (!TextUtils.isEmpty(name)) {
viewHolder.binding.messageKefuName.setText(name);
}
ImageUtils.displayIcon(viewHolder.binding.messageKefuIcon, serviceEntity.getIcon());
} else {
ImageUtils.display(viewHolder.binding.messageKefuIcon, R.drawable.message_kefu_icon);
}
if (keFuEntity.getServiceEntity() != null && keFuEntity.getServiceEntity().getAuth() != null) {
ImageUtils.display(viewHolder.binding.messageUserBadge, keFuEntity.getServiceEntity().getAuth().getIcon());
viewHolder.binding.messageUserBadge.setVisibility(View.VISIBLE);
} else {
viewHolder.binding.messageUserBadge.setVisibility(View.GONE);
}
ICommentUtilsProvider commentUtilsProvider = (ICommentUtilsProvider) ARouter.getInstance().build(RouteConsts.provider.commentUtils).navigation();
if (commentUtilsProvider != null) {
commentUtilsProvider.setCommentTime(viewHolder.binding.messageKefuTime, keFuEntity.getTime());
}
if (!TextUtils.isEmpty(keFuEntity.getSuggestion())) {
StringBuffer suggest = new StringBuffer();
suggest.append("反馈原文:");
suggest.append(keFuEntity.getSuggestion());
viewHolder.binding.messageKefuSuggestion.setVisibility(View.VISIBLE);
viewHolder.binding.messageKefuSuggestion.setText(suggest);
} else {
viewHolder.binding.messageKefuSuggestion.setVisibility(View.GONE);
}
if (keFuEntity.isRead()) {
viewHolder.binding.messageKefuUnread.setVisibility(View.GONE);
} else {
viewHolder.binding.messageKefuUnread.setVisibility(View.VISIBLE);
}
viewHolder.binding.messageKefuIcon.setOnClickListener(v -> {
if (serviceEntity != null && mDirectUtils != null) {
MtaHelper.onEvent("消息中心", "系统_二级列表", "点击头像");
mDirectUtils.directToHomeActivity(mContext, serviceEntity.getId(), mEntrance, "消息中心-系统");
}
});
viewHolder.binding.messageKefuName.setOnClickListener(v -> {
if (serviceEntity != null && mDirectUtils != null) {
MtaHelper.onEvent("消息中心", "系统_二级列表", "点击名字");
mDirectUtils.directToHomeActivity(mContext, serviceEntity.getId(), mEntrance, "消息中心-系统");
}
});
viewHolder.binding.getRoot().setOnLongClickListener(v -> {
DialogHelper.showDialog(mContext,
"删除消息",
"消息删除将不可恢复,确定删除吗?",
"确定", "取消", () -> {
mListViewModel.deleteMessage(keFuEntity.getId());
}, () -> {
},
true, "消息中心", "系统列表-删除");
return false;
});
}
private TextView getLinkChildView(KeFuViewHolder viewHolder, String text) {
TextView textView = new TextView(mContext);
textView.setTextColor(mContext.getResources().getColor(R.color.theme_font));
textView.setTextSize(12F);
textView.setText(text);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT
, ViewGroup.LayoutParams.WRAP_CONTENT);
params.setMargins(0, DisplayUtils.dip2px(8F), 0, 0);
viewHolder.binding.messageSkipList.addView(textView, params);
return textView;
}
private void linkSkip(MessageLinkEntity data) {
String type = data.getType();
if (TextUtils.isEmpty(type)) {
Utils.toast(mContext, "数据请求失败");
return;
}
MtaHelper.onEvent("消息中心", "系统_二级列表", "点击链接");
if (mDirectUtils == null || mLinkDirectUtils == null || mGameDetailProvider == null) return;
switch (type) {
case "游戏":
if (!TextUtils.isEmpty(data.getId())) {
mGameDetailProvider.startGameDetailActivity(mContext, data.getId(), "", null);
}
break;
case "QQ号":
if (!TextUtils.isEmpty(data.getQq())) {
mDirectUtils.directToQqConversation(mContext, data.getQq());
}
break;
case "web":
if (!TextUtils.isEmpty(data.getUrl())) {
mDirectUtils.directToWebView(mContext, data.getUrl(), "(消息-公告)");
}
break;
case "QQ群":
if (!TextUtils.isEmpty(data.getKey())) {
mDirectUtils.directToQqGroup(mContext, data.getKey());
}
break;
case "新闻":
if (!TextUtils.isEmpty(data.getId())) {
ARouter.getInstance().build(RouteConsts.activity.newsDetailActivity)
.withString(EntranceConsts.KEY_ENTRANCE, "(消息-公告)")
.withString(EntranceConsts.KEY_NEWSID, data.getId())
.navigation();
}
break;
case "专题":
ISubjectProvider subjectProvider = (ISubjectProvider) ARouter.getInstance().build(RouteConsts.provider.subject).navigation();
if (!TextUtils.isEmpty(data.getId()) && subjectProvider != null) {
subjectProvider.startSubjectActivity(mContext, data.getId(), null, false, "(消息-客服)");
}
break;
case "7moor":
if (mContext instanceof Activity) {
// 去掉七陌支持,跳转到企点
// ImManager.startChatActivity((Activity) mContext, null, null);
mDirectUtils.directToWebView(mContext, Constants.TENCENT_QIDIAN_ADDRESS, mEntrance);
}
break;
case "问题":
if (!TextUtils.isEmpty(data.getId())) {
ARouter.getInstance().build(RouteConsts.activity.questionDetailActivity)
.withString(EntranceConsts.KEY_QUESTIONS_ID, data.getId())
.withString(EntranceConsts.KEY_ENTRANCE, BaseActivity.mergeEntranceAndPath(mEntrance, "(消息-客服)"))
.navigation();
}
break;
case "回答":
if (!TextUtils.isEmpty(data.getId())) {
mDirectUtils.directToAnswerDetail(mContext, data.getId(), mEntrance, "(消息-客服)");
}
break;
case "社区文章":
if (!TextUtils.isEmpty(data.getId()) && data.getCommunity() != null) {
mDirectUtils.directToCommunityArticle(mContext, data.getId(), data.getCommunity().getId(), mEntrance, "(消息-客服)");
}
break;
case "社区专题":
if (!TextUtils.isEmpty(data.getId()) && data.getCommunity() != null) {
mLinkDirectUtils.directToCommunityColumn(mContext, data.getCommunity(), data.getId(), mEntrance, "(消息-客服)");
}
break;
case "视频":
if (!TextUtils.isEmpty(data.getId())) {
mDirectUtils.directToVideoDetail(mContext, data.getId(), mEntrance, "系统_二级列表");
}
break;
case "安利墙评论":
if (!TextUtils.isEmpty(data.getId())) {
MtaHelper.onEvent("安利墙", "进入", "消息中心");
mDirectUtils.directToAmway(mContext, data.getId(), mEntrance, "(消息-客服)");
}
break;
case "个人主页":
mDirectUtils.directToHomeActivity(mContext, UserManager.getInstance().getUserId(), mEntrance, "(消息-客服)");
break;
case "游戏详情评论":
if (!TextUtils.isEmpty(data.getId())) {
mGameDetailProvider.startGameDetailActivity(mContext, data.getId(), "", -1, true, false, false, false, null);
}
break;
case "订单中心":
mDirectUtils.directToOrderCenter(mContext);
break;
case "订单详情":
if (!TextUtils.isEmpty(data.getId())) {
mDirectUtils.directToOrderDetail(mContext, data.getId());
}
break;
case "光能记录":
case "光能记录获取":
mDirectUtils.directToEnergyRecord(mContext, 0);
break;
case "光能记录使用":
mDirectUtils.directToEnergyRecord(mContext, 1);
break;
case "抽奖中心":
mDirectUtils.directToLotteryParadisePage(mContext);
break;
case "我的奖品":
mDirectUtils.directToMyPrizePage(mContext);
break;
case "兑换商品":
mDirectUtils.directToExchangeCommodityPage(mContext);
break;
case "中奖订单详情":
if (!TextUtils.isEmpty(data.getId()) && !TextUtils.isEmpty(data.getActivityId())) {
mDirectUtils.directToWinOrderDetail(mContext, data.getId(), data.getActivityId());
}
break;
default:
LinkEntity entity = new LinkEntity();
entity.setType(data.getType());
if (TextUtils.isEmpty(data.getId())) {
entity.setLink(data.getId());
}
if (TextUtils.isEmpty(data.getUrl())) {
entity.setLink(data.getUrl());
}
if (TextUtils.isEmpty(data.getDocument())) {
entity.setText(data.getDocument());
}
entity.setCommunity(data.getCommunity());
mLinkDirectUtils.directToLinkPage(mContext, entity, mEntrance, "(消息-客服)");
break;
}
}
private void linkNewSkip(LinkEntity data) {
String type = data.getType();
if (TextUtils.isEmpty(type)) {
Utils.toast(mContext, "数据请求失败");
return;
}
MtaHelper.onEvent("消息中心", "系统_二级列表", "点击链接");
if (mDirectUtils == null || mLinkDirectUtils == null) return;
switch (type) {
case "home":
mDirectUtils.directToHomeActivity(mContext, UserManager.getInstance().getUserId(), mEntrance, "(消息-客服)");
break;
case "7moor":
case "qidian":
mDirectUtils.directToWebView(mContext, Constants.TENCENT_QIDIAN_ADDRESS, mEntrance);
break;
case "order_center":
mDirectUtils.directToOrderCenter(mContext);
break;
case "order_detail":
if (!TextUtils.isEmpty(data.getLink())) {
mDirectUtils.directToOrderDetail(mContext, data.getLink());
}
break;
case "energy_record":
case "energy_record_get":
mDirectUtils.directToEnergyRecord(mContext, 0);
break;
case "energy_record_cost":
mDirectUtils.directToEnergyRecord(mContext, 1);
break;
case "raffle_center":
mDirectUtils.directToLotteryParadisePage(mContext);
break;
case "raffle_prize":
mDirectUtils.directToMyPrizePage(mContext);
break;
case "exchange_commodity":
mDirectUtils.directToExchangeCommodityPage(mContext);
break;
case "win_order_detail":
if (!TextUtils.isEmpty(data.getLink()) && !TextUtils.isEmpty(data.getActivityId())) {
mDirectUtils.directToWinOrderDetail(mContext, data.getLink(), data.getActivityId());
}
break;
case "game_comment_detail":
ExposureSource exposureSource = new ExposureSource("消息中心", "");
ArrayList<ExposureSource> exposureSourceList = new ArrayList<>();
exposureSourceList.add(exposureSource);
ARouter.getInstance().build(RouteConsts.activity.ratingReplyActivity)
.withString(EntranceConsts.KEY_GAMEID, data.getGameId())
.withString(EntranceConsts.KEY_COMMENTID, data.getLink())
.withString(EntranceConsts.KEY_EXPOSURE_SOURCE, GsonUtils.toJson(exposureSourceList))
.withString(EntranceConsts.KEY_ENTRANCE, "消息中心-系统消息")
.navigation();
break;
default:
mLinkDirectUtils.directToLinkPage(mContext, data, mEntrance, "(消息-客服)");
break;
}
}
}

View File

@ -0,0 +1,19 @@
package com.gh.gamecenter.message.view;
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder;
import com.gh.gamecenter.common.callback.OnListClickListener;
import com.gh.gamecenter.message.databinding.MessageKefuItemBinding;
import com.gh.gamecenter.message.entity.MessageKeFuEntity;
/**
* Created by khy on 2017/4/10.
*/
public class KeFuViewHolder extends BaseRecyclerViewHolder<MessageKeFuEntity> {
public MessageKefuItemBinding binding;
public KeFuViewHolder(MessageKefuItemBinding binding, OnListClickListener listClickListener) {
super(binding.getRoot(), listClickListener);
this.binding = binding;
this.binding.getRoot().setOnClickListener(this);
}
}

View File

@ -0,0 +1,74 @@
package com.gh.gamecenter.message.view
import android.app.Application
import com.gh.gamecenter.common.baselist.ListViewModel
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.message.entity.MessageKeFuEntity
import com.gh.gamecenter.message.retrofit.RetrofitManager
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import okhttp3.MediaType
import okhttp3.RequestBody
import okhttp3.ResponseBody
import org.json.JSONObject
class KeFuViewModel(application: Application) : ListViewModel<MessageKeFuEntity, MessageKeFuEntity>(application) {
private val mApi = RetrofitManager.getInstance().api
override fun provideDataObservable(page: Int): Observable<List<MessageKeFuEntity>> {
return mApi.getMessageKeFuData(UserManager.getInstance().userId, page)
}
override fun mergeResultLiveData() {
mResultLiveData.addSource<List<MessageKeFuEntity>>(mListLiveData) { mResultLiveData.postValue(it) }
}
fun postMessageRead(messageId: String) {
// 更新本地数据以及页面
val listData = mListLiveData.value
if (listData != null) {
for (entity in listData) {
if (messageId == entity.id) {
entity.isRead = true
mListLiveData.postValue(listData)
break
}
}
}
// 后端同步
val jsonObject = JSONObject()
jsonObject.put("type", "system_message")
val body = RequestBody.create(MediaType.parse("application/json"), jsonObject.toString())
RetrofitManager.getInstance().api.postMessageRead(UserManager.getInstance().userId, messageId, body)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<ResponseBody>() {
})
}
fun deleteMessage(messageId: String) {
mApi.deleteKaiFuMessage(UserManager.getInstance().userId, messageId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<ResponseBody>() {
override fun onResponse(response: ResponseBody?) {
val listData = mListLiveData.value
listData?.let {
val iterator = it.iterator()
while (iterator.hasNext()) {
val data = iterator.next()
if (data.id == messageId) {
it.remove(data)
mListLiveData.postValue(listData)
break
}
}
}
}
})
}
}

View File

@ -0,0 +1,42 @@
package com.gh.gamecenter.message.view
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.gh.gamecenter.common.base.activity.ToolBarActivity
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.updateStatusBarColor
import com.gh.gamecenter.message.R
class MessageActivity : ToolBarActivity() {
override fun provideNormalIntent(): Intent {
return getTargetIntent(
this,
MessageActivity::class.java,
MessageFragment::class.java,
intent?.extras
)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
updateStatusBarColor(R.color.background_white, R.color.background_white)
}
override fun onDarkModeChanged() {
super.onDarkModeChanged()
updateStatusBarColor(R.color.background_white, R.color.background_white)
}
companion object {
fun getIntent(context: Context, entrance: String): Intent {
val bundle = Bundle()
bundle.putString(EntranceConsts.KEY_ENTRANCE, entrance)
return getTargetIntent(
context,
MessageActivity::class.java,
MessageFragment::class.java, bundle
)
}
}
}

View File

@ -0,0 +1,163 @@
package com.gh.gamecenter.message.view
import android.content.Context
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.alibaba.android.arouter.launcher.ARouter
import com.gh.gamecenter.common.baselist.ListAdapter
import com.gh.gamecenter.common.callback.OnListClickListener
import com.gh.gamecenter.common.constant.ItemViewType
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.utils.DialogHelper.showDialog
import com.gh.gamecenter.common.utils.goneIf
import com.gh.gamecenter.common.utils.toBinding
import com.gh.gamecenter.common.viewholder.FooterViewHolder
import com.gh.gamecenter.feature.entity.MessageEntity
import com.gh.gamecenter.feature.entity.MessageUnreadEntity
import com.gh.gamecenter.feature.provider.IBindingAdaptersProvider
import com.gh.gamecenter.message.R
class MessageAdapter(
context: Context,
private val mClickListener: OnListClickListener,
private val mEntrance: String,
private val mViewModel: MessageNormalViewModel
) : ListAdapter<MessageEntity>(context) {
private var mUnreadEntity: MessageUnreadEntity? = null
override fun setListData(updateData: List<MessageEntity>) {
var oldSize = TOP_ITEM_COUNT
if (mEntityList != null && mEntityList.size > 0) {
oldSize += mEntityList.size
}
mEntityList = ArrayList<MessageEntity>(updateData)
if (oldSize == TOP_ITEM_COUNT || oldSize > updateData.size) {
notifyDataSetChanged()
} else {
notifyItemRangeInserted(oldSize, updateData.size + TOP_ITEM_COUNT - oldSize)
}
}
fun setMessageUnreadData(unreadEntity: MessageUnreadEntity) {
mUnreadEntity = unreadEntity
notifyItemChanged(0)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
ItemViewType.ITEM_HEADER -> MessageTopViewHolder(parent.toBinding(), mClickListener)
ItemViewType.ITEM_FOOTER -> FooterViewHolder(
mLayoutInflater.inflate(
R.layout.refresh_footerview,
parent,
false
), mClickListener
)
else -> MessageItemViewHolder(parent.toBinding(), mClickListener, "消息_一级列表")
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (getItemViewType(position)) {
ItemViewType.ITEM_HEADER -> {
val topViewHolder: MessageTopViewHolder =
holder as MessageTopViewHolder
topViewHolder.mBinding.messageInviteName.setTextColor(
ContextCompat.getColor(
mContext,
R.color.text_title
)
)
topViewHolder.mBinding.messageVoteName.setTextColor(
ContextCompat.getColor(
mContext,
R.color.text_title
)
)
topViewHolder.mBinding.messageServiceName.setTextColor(
ContextCompat.getColor(
mContext,
R.color.text_title
)
)
topViewHolder.mBinding.messageVote.background = ContextCompat.getDrawable(
mContext,
R.drawable.reuse_listview_item_style
)
topViewHolder.mBinding.messageService.background = ContextCompat.getDrawable(
mContext,
R.drawable.reuse_listview_item_style
)
topViewHolder.mBinding.messageInvite.background = ContextCompat.getDrawable(
mContext,
R.drawable.reuse_listview_item_style
)
if (mUnreadEntity != null) {
val provider = ARouter.getInstance().build(RouteConsts.provider.bindingAdapters)
.navigation() as? IBindingAdaptersProvider
provider?.setMessageUnread(topViewHolder.mBinding.unreadVote, mUnreadEntity!!.getVoteCount())
topViewHolder.mBinding.unreadVote.goneIf(mUnreadEntity!!.getVoteCount() == 0)
provider?.setMessageUnread(
topViewHolder.mBinding.unreadInvite,
mUnreadEntity!!.invited + mUnreadEntity!!.systemInvited
)
topViewHolder.mBinding.unreadInvite.goneIf(mUnreadEntity!!.invited + mUnreadEntity!!.systemInvited == 0)
provider?.setMessageUnread(topViewHolder.mBinding.unreadService, mUnreadEntity!!.service)
topViewHolder.mBinding.unreadService.goneIf(mUnreadEntity!!.service == 0)
}
}
ItemViewType.ITEM_BODY -> {
val viewHolder: MessageItemViewHolder =
holder as MessageItemViewHolder
val entity: MessageEntity = mEntityList[position - TOP_ITEM_COUNT]
viewHolder.setMessageItem(entity, mContext, mEntrance)
viewHolder.itemView.setOnLongClickListener {
showDialog(
mContext,
"删除消息",
"消息删除将不可恢复,确定删除吗?",
"确定",
"取消",
{ mViewModel.deleteMessage(entity.id) },
{},
true,
"消息中心",
"消息列表-删除"
)
false
}
}
ItemViewType.ITEM_FOOTER -> {
val footerViewHolder = holder as FooterViewHolder
footerViewHolder.initFooterViewHolder(mIsLoading, mIsNetworkError, mIsOver)
footerViewHolder.initItemPadding()
}
}
}
override fun getItemViewType(position: Int): Int {
return when (position) {
0 -> {
ItemViewType.ITEM_HEADER
}
itemCount - 1 -> {
ItemViewType.ITEM_FOOTER
}
else -> {
ItemViewType.ITEM_BODY
}
}
}
override fun getItemCount(): Int {
return if (mEntityList == null || mEntityList.isEmpty()) TOP_ITEM_COUNT else mEntityList.size + FOOTER_ITEM_COUNT + TOP_ITEM_COUNT
}
}

View File

@ -0,0 +1,137 @@
package com.gh.gamecenter.message.view
import android.os.Bundle
import android.view.View
import androidx.core.content.ContextCompat
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.RecyclerView
import com.gh.gamecenter.common.baselist.ListFragment
import com.gh.gamecenter.common.baselist.LoadStatus
import com.gh.gamecenter.common.baselist.LoadType
import com.gh.gamecenter.common.utils.ifLogin
import com.gh.gamecenter.common.utils.toDrawable
import com.gh.gamecenter.common.utils.viewModelProvider
import com.gh.gamecenter.common.view.CustomDividerItemDecoration
import com.gh.gamecenter.core.utils.MtaHelper.onEvent
import com.gh.gamecenter.feature.entity.MessageEntity
import com.gh.gamecenter.feature.entity.MessageUnreadEntity
import com.gh.gamecenter.message.HaloApp
import com.gh.gamecenter.message.R
import com.gh.gamecenter.message.utils.NewLogUtils
class MessageFragment : ListFragment<MessageEntity, MessageNormalViewModel>() {
private var mAdapter: MessageAdapter? = null
private var mUnreadViewModel: MessageUnreadViewModel? = null
private var mUnreadEntity: MessageUnreadEntity? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mUnreadViewModel = viewModelProvider()
if (savedInstanceState != null) {
mUnreadViewModel?.retry()
}
mUnreadViewModel?.unreadLiveData?.observe(this) { messageUnread: MessageUnreadEntity ->
mUnreadEntity = messageUnread
provideListAdapter().setMessageUnreadData(messageUnread)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setNavigationTitle(getString(R.string.title_message_center))
ifLogin("") {}
}
override fun provideListViewModel(): MessageNormalViewModel {
val factory: MessageNormalViewModel.Factory = MessageNormalViewModel.Factory(HaloApp.getInstance(), "default")
return ViewModelProviders.of(this, factory).get(MessageNormalViewModel::class.java)
}
override fun provideListAdapter(): MessageAdapter {
return mAdapter ?: MessageAdapter(requireContext(), this, mEntrance, mListViewModel).also { mAdapter = it }
}
override fun getItemDecoration(): RecyclerView.ItemDecoration? {
val decoration = CustomDividerItemDecoration(requireContext(), false, true, false, false)
R.drawable.divider_item_line_space_16_h_1px.toDrawable(requireContext())?.let { decoration.setDrawable(it) }
mItemDecoration = decoration
return mItemDecoration
}
override fun onLoadEmpty() {
super.onLoadDone()
mAdapter!!.loadChange(LoadStatus.LIST_OVER)
mReuseNoData!!.visibility = View.VISIBLE
mReuseNoData!!.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.transparent))
}
override fun onLoadError() {
super.onLoadDone()
mAdapter!!.loadChange(LoadStatus.LIST_FAILED)
}
override fun <LIST : Any?> onListClick(view: View?, position: Int, data: Any?) {
when (view?.id) {
R.id.footerview_item -> {
if (mAdapter!!.isNetworkError) {
mListViewModel.load(LoadType.RETRY)
}
return
}
R.id.message_vote -> {
onEvent("消息中心", "二级入口", "赞同")
NewLogUtils.logMessageInformTopIconClick(mUnreadEntity?.getVoteCount() != 0, "赞同")
startActivity(
MessageVoteActivity.getIntent(
requireContext(),
MessageNormalFragment.MESSAGE_TYPE_VOTE,
"赞同_二级列表",
mEntrance
)
)
return
}
R.id.message_invite -> {
onEvent("消息中心", "二级入口", "邀请")
NewLogUtils.logMessageInformTopIconClick(
(mUnreadEntity?.invited ?: 0) + (mUnreadEntity?.systemInvited ?: 0) != 0,
"邀请"
)
startActivity(
MessageInviteActivity.getIntent(
requireContext(),
MessageNormalFragment.MESSAGE_TYPE_INVITE,
"邀请_二级列表",
mEntrance
)
)
return
}
R.id.message_service -> {
onEvent("消息中心", "二级入口", "系统")
NewLogUtils.logMessageInformTopIconClick(mUnreadEntity?.service != 0, "系统")
startActivity(MessageKeFuActivity.getIntent(requireContext(), mEntrance))
return
}
}
val entity: MessageEntity = data as MessageEntity
val path = "我的光环-消息中心-列表"
MessageItemViewHolder.messageItemClickSkip(
view,
entity,
mEntrance,
"消息_一级列表",
path
)
if (!entity.read) {
mListViewModel.postMessageRead(entity.id, entity.type)
}
}
}

View File

@ -0,0 +1,35 @@
package com.gh.gamecenter.message.view
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.gh.gamecenter.common.base.activity.ToolBarActivity
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.updateStatusBarColor
import com.gh.gamecenter.message.R
class MessageInviteActivity : ToolBarActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
updateStatusBarColor(R.color.background_white, R.color.background_white)
}
override fun onDarkModeChanged() {
super.onDarkModeChanged()
updateStatusBarColor(R.color.background_white, R.color.background_white)
}
companion object {
fun getIntent(context: Context, messageType: String, outerInfo: String, entrance: String): Intent {
val bundle = Bundle()
bundle.putString(EntranceConsts.KEY_MESSAGE_TYPE, messageType)
bundle.putString(EntranceConsts.KEY_ENTRANCE, entrance)
bundle.putString(EntranceConsts.KEY_OUTER_INFO, outerInfo)
return getTargetIntent(
context,
MessageInviteActivity::class.java,
MessageNormalFragment::class.java, bundle
)
}
}
}

View File

@ -0,0 +1,37 @@
package com.gh.gamecenter.message.view
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.gh.gamecenter.common.base.activity.ToolBarActivity
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.updateStatusBarColor
import com.gh.gamecenter.message.R
class MessageKeFuActivity : ToolBarActivity() {
override fun provideNormalIntent(): Intent? {
return getTargetIntent(this, MessageKeFuActivity::class.java, KeFuFragment::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
updateStatusBarColor(R.color.background_white, R.color.background_white)
}
override fun onDarkModeChanged() {
super.onDarkModeChanged()
updateStatusBarColor(R.color.background_white, R.color.background_white)
}
companion object {
fun getIntent(context: Context, entrance: String): Intent {
val bundle = Bundle()
bundle.putString(EntranceConsts.KEY_ENTRANCE, entrance)
return getTargetIntent(
context,
MessageKeFuActivity::class.java,
KeFuFragment::class.java, bundle
)
}
}
}

View File

@ -0,0 +1,107 @@
package com.gh.gamecenter.message.view;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
import com.gh.gamecenter.common.baselist.ListAdapter;
import com.gh.gamecenter.common.callback.OnListClickListener;
import com.gh.gamecenter.common.constant.ItemViewType;
import com.gh.gamecenter.common.utils.DialogHelper;
import com.gh.gamecenter.common.viewholder.FooterViewHolder;
import com.gh.gamecenter.feature.entity.MessageEntity;
import com.gh.gamecenter.message.R;
import com.gh.gamecenter.message.databinding.MessageItemBinding;
/**
* Created by khy on 23/03/18.
*/
public class MessageNormalAdapter extends ListAdapter<MessageEntity> {
private OnListClickListener mClickListener;
private MessageNormalViewModel mViewModel;
private String mEntrance;
private String mOuterInfo;
public MessageNormalAdapter(Context context,
OnListClickListener clickListener,
String entrance,
String outerInfo,
MessageNormalViewModel viewModel) {
super(context);
mClickListener = clickListener;
mEntrance = entrance;
mOuterInfo = outerInfo;
mViewModel = viewModel;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
switch (viewType) {
case ItemViewType.ITEM_FOOTER:
view = mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false);
return new FooterViewHolder(view, mClickListener);
case ItemViewType.ITEM_BODY:
MessageItemBinding binding = MessageItemBinding.inflate(mLayoutInflater, parent, false);
return new MessageItemViewHolder(binding, mClickListener, mOuterInfo);
default:
return null;
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (getItemViewType(position)) {
case ItemViewType.ITEM_BODY:
MessageItemViewHolder viewHolder = (MessageItemViewHolder) holder;
MessageEntity entity = mEntityList.get(position);
entity.setRead(true);
viewHolder.setMessageItem(entity, mContext, mEntrance);
viewHolder.itemView.setOnLongClickListener(v -> {
String mtaKey;
if (MessageNormalFragment.MESSAGE_TYPE_INVITE.equals(mViewModel.getMessageType())) {
mtaKey = "邀请列表-删除";
} else {
mtaKey = "赞同列表-删除";
}
DialogHelper.showDialog(
mContext,
"删除消息",
"消息删除将不可恢复,确定删除吗?",
"确定",
"取消",
() -> mViewModel.deleteMessage(entity.getId()),
() -> {
},
true,
"消息中心",
mtaKey);
return false;
});
break;
case ItemViewType.ITEM_FOOTER:
((FooterViewHolder) holder).initFooterViewHolder(mIsLoading, mIsNetworkError, mIsOver);
((FooterViewHolder) holder).initItemPadding();
break;
}
}
@Override
public int getItemViewType(int position) {
if (position == getItemCount() - 1) {
return ItemViewType.ITEM_FOOTER;
} else {
return ItemViewType.ITEM_BODY;
}
}
@Override
public int getItemCount() {
return mEntityList == null || mEntityList.isEmpty() ? 0 : mEntityList.size() + FOOTER_ITEM_COUNT;
}
}

View File

@ -0,0 +1,110 @@
package com.gh.gamecenter.message.view;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.RecyclerView;
import com.gh.gamecenter.common.baselist.ListAdapter;
import com.gh.gamecenter.common.baselist.ListFragment;
import com.gh.gamecenter.common.baselist.LoadType;
import com.gh.gamecenter.common.constant.EntranceConsts;
import com.gh.gamecenter.common.view.CustomDividerItemDecoration;
import com.gh.gamecenter.message.HaloApp;
import com.gh.gamecenter.message.R;
import com.gh.gamecenter.feature.entity.MessageEntity;
/**
* Created by khy on 24/03/18.
* 不需要未读机制
*/
public class MessageNormalFragment extends ListFragment<MessageEntity, MessageNormalViewModel> {
public final static String MESSAGE_TYPE_VOTE = "vote";
public final static String MESSAGE_TYPE_INVITE = "invite";
private MessageNormalAdapter mAdapter;
private MessageUnreadViewModel mUnreadViewModel;
private String mMessageType;
private String mTitle;
private String mOuterInfo;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
if (getArguments() == null)
throw new NullPointerException(MessageFragment.class.getSimpleName() + " Arguments is not null");
mMessageType = getArguments().getString(EntranceConsts.KEY_MESSAGE_TYPE);
mOuterInfo = getArguments().getString(EntranceConsts.KEY_OUTER_INFO);
super.onCreate(savedInstanceState);
mUnreadViewModel = ViewModelProviders.of(this).get(MessageUnreadViewModel.class);
}
@Override
protected MessageNormalViewModel provideListViewModel() {
MessageNormalViewModel.Factory factory = new MessageNormalViewModel.Factory(HaloApp.getInstance(), mMessageType);
return ViewModelProviders.of(this, factory).get(MessageNormalViewModel.class);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (MESSAGE_TYPE_INVITE.equals(mMessageType)) {
mTitle = "邀请";
} else if (MESSAGE_TYPE_VOTE.equals(mMessageType)) {
mTitle = "赞同";
}
setNavigationTitle(mTitle);
}
@Override
protected RecyclerView.ItemDecoration getItemDecoration() {
CustomDividerItemDecoration decoration = new CustomDividerItemDecoration(requireContext(), false, false, true, false);
decoration.setDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.divider_item_line_space_16_h_1px));
mItemDecoration = decoration;
return mItemDecoration;
}
@Override
public void onLoadDone() {
super.onLoadDone();
if (MESSAGE_TYPE_INVITE.equals(mMessageType)) {
mUnreadViewModel.markRead(MessageUnreadViewModel.ReadType.INVITE);
} else {
mUnreadViewModel.markRead(MessageUnreadViewModel.ReadType.VOTE);
}
}
@Override
protected ListAdapter provideListAdapter() {
return mAdapter == null ? mAdapter = new MessageNormalAdapter(
getContext(),
this,
mEntrance,
mOuterInfo,
mListViewModel) : mAdapter;
}
@Override
public void onListClick(View view, int position, Object data) {
if (view.getId() == R.id.footerview_item) {
if (mAdapter.isNetworkError()) {
mListViewModel.load(LoadType.RETRY);
}
return;
}
MessageEntity entity = (MessageEntity) data;
String path = "我的光环-消息中心-" + mTitle;
MessageItemViewHolder.messageItemClickSkip(view, entity, mEntrance, mOuterInfo, path);
// if (!entity.getRead()) {
// mListViewModel.postMessageRead(entity.getId(), entity.getType());
// mAdapter.notifyItemChanged(position);
// }
}
}

View File

@ -0,0 +1,95 @@
package com.gh.gamecenter.message.view
import android.app.Application
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.gh.gamecenter.common.baselist.ListViewModel
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.feature.entity.MessageEntity
import com.gh.gamecenter.message.retrofit.RetrofitManager
import com.lightgame.utils.Utils
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import okhttp3.MediaType
import okhttp3.RequestBody
import okhttp3.ResponseBody
import org.json.JSONObject
class MessageNormalViewModel(
application: Application,
val messageType: String
) : ListViewModel<MessageEntity, MessageEntity>(application) {
private val mApi = RetrofitManager.getInstance().api
override fun provideDataObservable(page: Int): Observable<List<MessageEntity>> {
return mApi.getMessage(
UserManager.getInstance().userId,
messageType,
Utils.getTime(getApplication()),
page
)
}
override fun mergeResultLiveData() {
mResultLiveData.addSource<List<MessageEntity>>(mListLiveData) { mResultLiveData.postValue(it) }
}
fun deleteMessage(messageId: String) {
mApi.deleteMessage(UserManager.getInstance().userId, messageId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<ResponseBody>() {
override fun onResponse(response: ResponseBody?) {
val listData = mListLiveData.value
listData?.let {
val iterator = it.iterator()
while (iterator.hasNext()) {
val data = iterator.next()
if (data.id == messageId) {
it.remove(data)
mListLiveData.postValue(listData)
break
}
}
}
}
})
}
fun postMessageRead(messageId: String, type: String) {
// 更新本地数据以及页面
val listData = mListLiveData.value
if (listData != null) {
for (entity in listData) {
if (messageId == entity.id) {
entity.read = true
mListLiveData.postValue(listData)
break
}
}
}
// 后端同步
val jsonObject = JSONObject()
jsonObject.put("type", type)
val body = RequestBody.create(MediaType.parse("application/json"), jsonObject.toString())
mApi.postMessageRead(UserManager.getInstance().userId, messageId, body)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<ResponseBody>() {
})
}
class Factory(
private val mApplication: Application,
private val mMessageType: String
) : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return MessageNormalViewModel(mApplication, mMessageType) as T
}
}
}

View File

@ -0,0 +1,17 @@
package com.gh.gamecenter.message.view
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
import com.gh.gamecenter.common.callback.OnListClickListener
import com.gh.gamecenter.message.databinding.MessageItemTopBinding
class MessageTopViewHolder(binding: MessageItemTopBinding, listClickListener: OnListClickListener) :
BaseRecyclerViewHolder<Any>(binding.root, listClickListener) {
var mBinding: MessageItemTopBinding
init {
mBinding = binding
mBinding.messageInvite.setOnClickListener(this)
mBinding.messageService.setOnClickListener(this)
mBinding.messageVote.setOnClickListener(this)
}
}

View File

@ -0,0 +1,75 @@
package com.gh.gamecenter.message.view
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MediatorLiveData
import com.alibaba.android.arouter.launcher.ARouter
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.feature.provider.IMessageUnreadRepositoryProvider
import com.gh.gamecenter.feature.entity.MessageUnreadEntity
class MessageUnreadViewModel(application: Application) : AndroidViewModel(application) {
private val mProvider = ARouter.getInstance().build(RouteConsts.provider.messageUnreadRepository)
.navigation() as? IMessageUnreadRepositoryProvider
val unreadLiveData: MediatorLiveData<MessageUnreadEntity>? = mProvider?.getUnreadLiveData()
fun retry() {
mProvider?.loadMessageUnreadData()
}
fun markRead(readType: ReadType?) {
val cacheUnreadData = mProvider?.getUnreadLiveData()?.value ?: return
var isChange = false
when (readType) {
ReadType.SERVICE -> if (cacheUnreadData.service != 0) {
cacheUnreadData.service = 0
isChange = true
}
ReadType.VOTE -> if (cacheUnreadData.getVoteCount() != 0) {
cacheUnreadData.resetVote()
isChange = true
}
ReadType.INVITE -> if (cacheUnreadData.invited + cacheUnreadData.systemInvited != 0) {
cacheUnreadData.invited = 0
cacheUnreadData.systemInvited = 0
isChange = true
}
ReadType.DEFAULT -> if (cacheUnreadData.answer + cacheUnreadData.answerComment + cacheUnreadData.reply +
cacheUnreadData.followQuestion + cacheUnreadData.replyAnswerComment +
cacheUnreadData.communityArticleComment + cacheUnreadData.replyCommunityArticleComment != 0
) {
cacheUnreadData.answer = 0
cacheUnreadData.answerComment = 0
cacheUnreadData.reply = 0
cacheUnreadData.followQuestion = 0
cacheUnreadData.replyAnswerComment = 0
cacheUnreadData.communityArticleComment = 0
cacheUnreadData.replyCommunityArticleComment = 0
isChange = true
}
ReadType.FANS -> if (cacheUnreadData.fans != 0) {
cacheUnreadData.fans = 0
isChange = true
}
else -> {}
}
if (isChange) {
// 重新统计总数
cacheUnreadData.total = cacheUnreadData.getVoteCount() +
cacheUnreadData.service + cacheUnreadData.answer + cacheUnreadData.reply +
cacheUnreadData.followQuestion + cacheUnreadData.replyAnswerComment + cacheUnreadData.answerComment +
cacheUnreadData.systemInvited + cacheUnreadData.fans
mProvider.getUnreadLiveData()?.postValue(cacheUnreadData)
}
}
enum class ReadType {
DEFAULT, VOTE, INVITE, SERVICE, FANS
}
}

View File

@ -0,0 +1,35 @@
package com.gh.gamecenter.message.view
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.gh.gamecenter.common.base.activity.ToolBarActivity
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.updateStatusBarColor
import com.gh.gamecenter.message.R
class MessageVoteActivity : ToolBarActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
updateStatusBarColor(R.color.background_white, R.color.background_white)
}
override fun onDarkModeChanged() {
super.onDarkModeChanged()
updateStatusBarColor(R.color.background_white, R.color.background_white)
}
companion object {
fun getIntent(context: Context, messageType: String, outerInfo: String, entrance: String): Intent {
val bundle = Bundle()
bundle.putString(EntranceConsts.KEY_ENTRANCE, entrance)
bundle.putString(EntranceConsts.KEY_MESSAGE_TYPE, messageType)
bundle.putString(EntranceConsts.KEY_OUTER_INFO, outerInfo)
return getTargetIntent(
context,
MessageVoteActivity::class.java,
MessageNormalFragment::class.java, bundle
)
}
}
}

View File

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.gh.gamecenter.message">
<!-- 允许应用程序访问网络连接 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 允许应用程序获取网络信息状态 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- 允许应用程序读取电话状态 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- 允许应用程序获取当前或最近运行的应用 -->
<uses-permission android:name="android.permission.GET_TASKS" />
<application
android:name=".MessageModuleApp"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppCompatTheme"
tools:replace="android:name,android:allowBackup"
tools:targetApi="n">
<meta-data
android:name="io.sentry.auto-init"
android:value="false" />
<!-- 不让 sentry 读取系统事件 -->
<meta-data
android:name="io.sentry.breadcrumbs.system-events"
android:value="false" />
<activity
android:name=".view.MessageActivity"
android:exported="true"
android:launchMode="singleTask"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".view.MessageKeFuActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".view.MessageVoteActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".view.MessageInviteActivity"
android:exported="false"
android:screenOrientation="portrait" />
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/theme_red" />
<stroke
android:width="1dp"
android:color="@color/white" />
</shape>

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:background="@color/background_white"
android:gravity="center"
android:orientation="vertical"
android:paddingBottom="10dp">
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center_vertical"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:textColor="@color/text_title"
android:textSize="15sp"
tools:text="已赞同回答" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:background="@color/background" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</FrameLayout>

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="20dp"
android:paddingTop="10dp"
android:paddingRight="20dp"
android:paddingBottom="10dp">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/iv_icon"
style="@style/frescoCircleStyle"
android:layout_width="40dp"
android:layout_height="40dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:textColor="@color/text_title"
app:layout_constraintLeft_toRightOf="@id/iv_icon"
app:layout_constraintTop_toTopOf="parent"
tools:text="光环丧堃" />
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:textColor="@color/text_3a3a3a"
android:textSize="10sp"
app:layout_constraintLeft_toLeftOf="@id/tv_username"
app:layout_constraintTop_toBottomOf="@id/tv_username"
tools:text="2018年08月12日" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,147 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/message_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/reuse_listview_item_style"
android:paddingRight="16dp"
android:paddingBottom="16dp">
<com.gh.gamecenter.common.view.AvatarBorderView
android:id="@+id/message_user_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="7dp"
android:layout_marginLeft="7dp"
app:avatar_width="36dp"
app:border_width="0dp"
tools:layout_width="54dp"
tools:layout_height="54dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/message_unread"
android:layout_width="8dp"
android:layout_height="8dp"
android:layout_marginTop="16dp"
android:layout_marginLeft="44dp"
android:background="@drawable/shape_message_unread_hint"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/message_user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginLeft="3dp"
android:includeFontPadding="false"
android:singleLine="true"
android:textColor="@color/text_title"
android:textSize="14sp"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constrainedWidth="true"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintLeft_toRightOf="@id/message_user_icon"
app:layout_constraintRight_toLeftOf="@+id/message_user_more"
app:layout_constraintTop_toTopOf="parent"
tools:text="欢喜哥欢喜哥" />
<TextView
android:id="@+id/message_user_more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:includeFontPadding="false"
android:textColor="@color/theme_font"
android:textSize="14sp"
android:visibility="visible"
app:layout_constraintLeft_toRightOf="@id/message_user_name"
app:layout_constraintTop_toTopOf="@id/message_user_name"
app:layout_constraintRight_toLeftOf="@+id/message_command"
tools:text="等2人" />
<TextView
android:id="@+id/message_command"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:includeFontPadding="false"
android:textColor="@color/text_title"
android:textSize="14sp"
app:layout_constraintLeft_toRightOf="@id/message_user_more"
app:layout_constraintTop_toTopOf="@id/message_user_name"
app:layout_constraintRight_toRightOf="parent"
tools:text="赞同了你的回答" />
<TextView
android:id="@+id/message_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:includeFontPadding="false"
android:textColor="@color/text_subtitleDesc"
android:textSize="10sp"
app:layout_constraintLeft_toLeftOf="@id/message_user_name"
app:layout_constraintTop_toBottomOf="@id/message_user_name"
tools:text="一个小时前" />
<TextView
android:id="@+id/message_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:maxLines="3"
android:ellipsize="end"
android:textColor="@color/text_subtitle"
android:textSize="14sp"
app:layout_constraintLeft_toLeftOf="@id/message_user_name"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/message_time"
tools:text="这是一个神奇的饿回答这是一个神奇的饿回答这是一个神奇的饿回答这是一个神奇的饿回答这是一个神奇的饿回答这是一个神奇的饿回答这是一个神奇的饿回答这是一个神奇的饿回答这是一个神奇的饿回答" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/message_original"
android:layout_width="0dp"
android:layout_height="52dp"
android:layout_marginTop="10dp"
android:background="@drawable/bg_shape_space_radius_8"
app:layout_constraintLeft_toLeftOf="@id/message_user_name"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/message_content">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/message_original_icon"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_marginRight="8dp"
style="@style/frescoStyle"
app:fadeDuration="500"
app:roundedCornerRadius="4dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<TextView
android:id="@+id/message_original_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:ellipsize="end"
android:maxLines="2"
android:textColor="@color/text_subtitleDesc"
android:textSize="12sp"
tools:text="我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容"
app:layout_constrainedWidth="true"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/message_original_icon" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,203 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="8dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/message_vote"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@drawable/reuse_listview_item_style"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/message_invite"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/message_vote_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:src="@drawable/message_vote_icon"
app:layout_constraintBottom_toTopOf="@+id/message_vote_name"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<TextView
android:id="@+id/unread_vote"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="14dp"
android:layout_marginTop="10dp"
android:background="@drawable/message_unread_hint"
android:gravity="center"
android:maxLength="3"
android:minWidth="13dp"
android:minHeight="1dp"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:textColor="@color/white"
android:textSize="9dp"
android:visibility="gone"
app:layout_constraintStart_toStartOf="@+id/guideline_vote"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_vote"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<TextView
android:id="@+id/message_vote_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="16dp"
android:includeFontPadding="false"
android:text="赞同"
android:textColor="@color/text_title"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/message_vote_icon" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/message_invite"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@drawable/reuse_listview_item_style"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintLeft_toRightOf="@+id/message_vote"
app:layout_constraintRight_toLeftOf="@+id/message_service"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/message_invite_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:src="@drawable/message_invite_icon"
app:layout_constraintBottom_toTopOf="@+id/message_invite_name"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<TextView
android:id="@+id/unread_invite"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="14dp"
android:layout_marginTop="10dp"
android:background="@drawable/message_unread_hint"
android:gravity="center"
android:maxLength="3"
android:minWidth="13dp"
android:minHeight="1dp"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:textColor="@color/white"
android:textSize="9dp"
android:visibility="gone"
app:layout_constraintStart_toStartOf="@+id/guideline_invite"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_invite"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<TextView
android:id="@+id/message_invite_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="16dp"
android:includeFontPadding="false"
android:text="邀请"
android:textColor="@color/text_title"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/message_invite_icon" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/message_service"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@drawable/reuse_listview_item_style"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintLeft_toRightOf="@+id/message_invite"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/message_service_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:src="@drawable/message_service_icon"
app:layout_constraintBottom_toTopOf="@+id/message_service_name"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<TextView
android:id="@+id/unread_service"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="14dp"
android:layout_marginTop="10dp"
android:background="@drawable/message_unread_hint"
android:gravity="center"
android:maxLength="3"
android:minWidth="13dp"
android:minHeight="1dp"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:textColor="@color/white"
android:textSize="9dp"
android:visibility="gone"
app:layout_constraintStart_toStartOf="@+id/guideline_service"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_service"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<TextView
android:id="@+id/message_service_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="16dp"
android:includeFontPadding="false"
android:text="系统"
android:textColor="@color/text_title"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/message_service_icon" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,161 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:fresco="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/message_kaifu_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/reuse_listview_item_style"
android:orientation="vertical"
android:paddingTop="12dp"
android:paddingBottom="10dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="2dp">
<RelativeLayout
android:id="@+id/message_icon_container"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginRight="15dp"
tools:background="@color/btn_gray">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/message_kefu_icon"
android:layout_width="30dp"
android:layout_height="30dp"
fresco:placeholderImageScaleType="fitXY"
fresco:roundAsCircle="true" />
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/message_user_badge"
android:layout_width="13dp"
android:layout_height="13dp"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true" />
</RelativeLayout>
<TextView
android:id="@+id/message_kefu_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/message_icon_container"
android:textColor="@color/text_black"
android:textSize="12sp"
tools:text="Kris Wu" />
<TextView
android:id="@+id/message_kefu_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/message_kefu_name"
android:layout_marginTop="1dp"
android:layout_toRightOf="@id/message_icon_container"
android:textColor="@color/hint"
android:textSize="10sp"
tools:text="12-13" />
<View
android:id="@+id/message_kefu_hint"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_alignParentRight="true"
android:background="@drawable/oval_message_hint_bg"
android:visibility="gone" />
</RelativeLayout>
<View
android:id="@+id/message_kefu_unread"
android:layout_width="6dp"
android:layout_height="6dp"
android:layout_marginLeft="28dp"
android:background="@drawable/message_unread_hint" />
</RelativeLayout>
<com.gh.gamecenter.common.view.MessageSpannableTextView
android:id="@+id/message_kefu_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="10dp"
android:layout_marginRight="20dp"
android:textColor="@color/title"
android:textSize="13sp"
tools:text="Kris Wu never let you down." />
<com.google.android.flexbox.FlexboxLayout
android:id="@+id/message_kefu_images_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="18dp"
android:layout_marginTop="12dp"
android:layout_marginRight="18dp"
android:visibility="gone"
app:flexWrap="wrap" />
<TextView
android:id="@+id/message_kefu_suggestion"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="8dp"
android:layout_marginRight="20dp"
android:background="@color/background"
android:padding="11dp"
android:textColor="@color/title"
android:textSize="12sp"
android:visibility="visible"
tools:text="Kris is here to help" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/message_skip_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" />
<LinearLayout
android:id="@+id/copy_id_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginTop="10dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:visibility="gone"
tools:visibility="visible">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_id" />
<TextView
android:id="@+id/copyTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:text="@string/copy_id"
android:textColor="@color/text_subtitle"
android:textSize="10sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,7 @@
<resources>
<string name="app_name">消息中心模块</string>
<string name="title_message_center">消息中心</string>
<string name="copy_id"><u>复制ID</u></string>
<string name="kefu_default_name">光环客服</string>
<string name="request_failure_normal_hint">网络错误</string>
</resources>

View File

@ -0,0 +1,61 @@
package com.gh.gamecenter.message;
import android.app.Activity;
import android.content.Context;
import android.text.TextUtils;
import com.alibaba.android.arouter.launcher.ARouter;
import com.gh.gamecenter.common.constant.Constants;
import com.gh.gamecenter.common.constant.EntranceConsts;
import com.gh.gamecenter.common.constant.RouteConsts;
import com.gh.gamecenter.common.utils.NetworkUtils;
import com.gh.gamecenter.core.utils.CurrentActivityHolder;
import com.gh.gamecenter.core.utils.SPUtils;
import com.gh.gamecenter.login.user.UserManager;
import com.gh.gamecenter.login.utils.QuickLoginHelper;
import com.lightgame.utils.Utils;
/**
* Created by khy on 28/06/17.
*/
public class CheckLoginUtils {
public static void checkLogin(Context context, String entrance, OnLoginListener listener) {
if (!isLogin()) {
if (listener != null) Utils.toast(context, "需要登录");
if (SPUtils.getBoolean(Constants.SP_HAS_GET_PHONE_INFO) || NetworkUtils.isOpenMobileData(context)) {
startQuickLogin(context, entrance);
} else {
ARouter.getInstance().build(RouteConsts.activity.loginActivity)
.withString(EntranceConsts.KEY_ENTRANCE, entrance)
.navigation();
}
} else {
if (listener != null) {
listener.onLogin();
}
}
}
private static void startQuickLogin(Context context, String entrance) {
// 需要确保传入的 context 不为 application
if (!(context instanceof Activity)) {
context = CurrentActivityHolder.getCurrentActivity();
}
if (context != null) {
QuickLoginHelper.startLogin(context, entrance);
}
}
public static boolean isLogin() {
return !TextUtils.isEmpty(UserManager.getInstance().getToken());
}
public interface OnLoginListener {
void onLogin();
}
}

View File

@ -0,0 +1,101 @@
package com.gh.gamecenter.message
import android.os.Build
import androidx.multidex.MultiDexApplication
import com.alibaba.android.arouter.launcher.ARouter
import com.facebook.animated.giflite.GifDecoder
import com.facebook.common.logging.FLog
import com.facebook.imageformat.DefaultImageFormats
import com.facebook.imagepipeline.core.ImagePipelineConfig
import com.facebook.imagepipeline.core.ImageTranscoderType
import com.facebook.imagepipeline.core.MemoryChunkType
import com.facebook.imagepipeline.decoder.ImageDecoderConfig
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.image.EmptyDecoder
import com.gh.gamecenter.common.utils.ImageUtils.disableAnimatedImage
import com.gh.gamecenter.common.utils.ImageUtils.isFrescoInitialized
import com.gh.gamecenter.core.iinterface.IApplication
import com.gh.gamecenter.core.utils.SPUtils
import com.github.piasy.biv.BigImageViewer
import com.github.piasy.biv.loader.fresco.FrescoImageLoader
import java.util.*
class MessageModuleApp : MultiDexApplication() {
private val mApplicationList = ServiceLoader.load(IApplication::class.java, this.javaClass.classLoader)
override fun onCreate() {
super.onCreate()
initArouter()
mApp = this
for (application in mApplicationList) {
application.onCreate(this)
}
initFresco()
SPUtils.setBoolean(Constants.SP_IS_DEV_ENV, true)
}
private fun initArouter() {
if (BuildConfig.DEBUG) { // 这两行必须写在init之前否则这些配置在init过程中将无效
ARouter.openLog() // 打印日志
ARouter.openDebug() // 开启调试模式(如果在InstantRun模式下运行必须开启调试模式线上版本需要关闭,否则有安全风险)
}
ARouter.init(this) // 尽可能早推荐在Application中初始化
}
fun initFresco() {
// 初始化 Fresco(BigImageViewer 已包含Fresco)
if (!isFrescoInitialized()) {
// 在 5.0 & 5.1 设备上 disable native code原因是应用附带了 arm64 的 SO 以后在部分 5.0/5.1 设备
// 会出现找不到 arm64 so 的情况,具体可见
// https://sentry.ghzs.com/organizations/lightgame/issues/53107/
// 所以这里尝试在 5.0 & 5.1 设备上关闭 fresco 的 native 解码,应该会让 5.0 & 5.1 的设备 OOM 概率提高,但先试试效果
// 同时禁用动图
val pipelineConfigBuilder = ImagePipelineConfig.newBuilder(this)
val decodeConfigBuilder = ImageDecoderConfig.newBuilder()
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP
|| Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP_MR1
) {
pipelineConfigBuilder.setMemoryChunkType(MemoryChunkType.BUFFER_MEMORY)
.setImageTranscoderType(ImageTranscoderType.JAVA_TRANSCODER)
decodeConfigBuilder.overrideDecoder(DefaultImageFormats.GIF, GifDecoder()).build()
val manufacture = Build.MANUFACTURER.lowercase(Locale.getDefault())
// OPPO 和 VIVO 的 5.1.1 设备还会去加载 WEBP_ANIMATED 的 SO
// 实测没有发现有地方使用 WEBP_ANIMATED 的图片,这里用空占位图来替换 WEBP 动图
if ("oppo" == manufacture || "vivo" == manufacture) {
decodeConfigBuilder.overrideDecoder(
DefaultImageFormats.WEBP_ANIMATED,
EmptyDecoder()
).build()
}
pipelineConfigBuilder
.setImageDecoderConfig(decodeConfigBuilder.build())
.experiment()
.setNativeCodeDisabled(true)
// 图片仅加载静态图片
disableAnimatedImage()
}
try {
BigImageViewer.initialize(
FrescoImageLoader.with(
this,
pipelineConfigBuilder.build()
)
)
} catch (e: Throwable) {
e.printStackTrace()
}
FLog.setMinimumLoggingLevel(FLog.VERBOSE)
}
}
companion object {
private lateinit var mApp: MessageModuleApp
fun getInstance(): MessageModuleApp {
return mApp
}
}
}

View File

@ -0,0 +1,99 @@
package com.gh.gamecenter.message.provider
import android.app.Activity
import android.app.Application
import android.content.Context
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.provider.IAppProvider
import com.gh.gamecenter.core.provider.IFlavorProvider
import com.gh.gamecenter.message.HaloApp
import com.gh.gamecenter.message.R
@Route(path = RouteConsts.provider.app, name = "Application暴露服务")
class AppProviderImpl : IAppProvider {
override fun init(context: Context?) {
// Do nothing
}
override fun getAppName(): String {
return HaloApp.getInstance().getString(R.string.app_name)
}
override fun getGid(): String {
return ""
}
override fun refreshGid() {
// Do nothing
}
override fun getOaid(): String {
return ""
}
override fun getChannel(): String {
return ""
}
override fun setChannel(channel: String) {
// Do nothing
}
override fun getUserAgent(): String {
return ""
}
override fun getServerUserMark(): String {
return ""
}
override fun getDeviceRamSize(): Long {
return 0L
}
override fun getTemporaryLocalDeviceId(): String {
return ""
}
override fun isUserAcceptPrivacyPolicy(context: Context): Boolean {
return true
}
override fun put(key: String, any: Any) {
// Do nothing
}
override fun get(key: String, isRemove: Boolean): Any {
return ""
}
override fun getFlavorProvider(): IFlavorProvider {
return object : IFlavorProvider {
override fun getChannelStr(application: Application): String {
return ""
}
override fun init(application: Application, activity: Activity, activateRatio: Int) {
// do nothing
}
override fun logEvent(content: String) {
// do nothing
}
override fun logCoreEvent() {
// do nothing
}
}
}
override fun getFlavor(): String {
return "internal"
}
override fun getIsBrandNewInstall(): Boolean {
return false
}
}

View File

@ -0,0 +1,34 @@
package com.gh.gamecenter.message.provider
import android.content.Context
import android.widget.LinearLayout
import android.widget.TextView
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.provider.IBindingAdaptersProvider
@Route(path = RouteConsts.provider.bindingAdapters, name = "BindingAdapters暴露服务")
class BindingAdaptersProviderImpl : IBindingAdaptersProvider {
override fun setGameName(
view: TextView,
game: GameEntity,
isShowPlatform: Boolean,
isShowSuffix: Boolean
) {
ToastUtils.toast("调用->BindingAdaptersProviderImpl.setGameName")
}
override fun setGameTags(layout: LinearLayout, gameEntity: GameEntity) {
ToastUtils.toast("调用->BindingAdaptersProviderImpl.setGameTags")
}
override fun setMessageUnread(view: TextView, unreadCount: Int) {
ToastUtils.toast("调用->BindingAdaptersProviderImpl.setMessageUnread")
}
override fun init(context: Context?) {
// Do nothing
}
}

View File

@ -0,0 +1,35 @@
package com.gh.gamecenter.message.provider
import android.content.Context
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.provider.IBuildConfigProvider
import com.gh.gamecenter.message.BuildConfig
@Route(path = RouteConsts.provider.buildConfig, name = "BuildConfig暴露服务")
class BuildConfigImpl : IBuildConfigProvider {
override fun init(context: Context?) {
// Do nothing
}
override fun getApplicationId(): String = BuildConfig.APPLICATION_ID
override fun getVersionName(): String = BuildConfig.VERSION_NAME
override fun getExposureVersion(): String = ""
override fun isDebug(): Boolean = BuildConfig.DEBUG
override fun getApiHost(): String = BuildConfig.API_HOST
override fun getDevApiHost(): String = BuildConfig.DEV_API_HOST
override fun getNewApiHost(): String = BuildConfig.NEW_API_HOST
override fun getNewDevApiHost(): String = BuildConfig.NEW_DEV_API_HOST
override fun getVApiHost() = ""
override fun getVDevApiHost() = ""
}

View File

@ -0,0 +1,18 @@
package com.gh.gamecenter.message.provider
import android.content.Context
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.provider.ICheckLoginProvider
import com.gh.gamecenter.message.CheckLoginUtils
@Route(path = RouteConsts.provider.checkLogin, name = "CheckLoginUtils暴露服务")
class CheckLoginProviderImpl : ICheckLoginProvider {
override fun checkLogin(context: Context, entrance: String, action: (() -> Unit)?) {
CheckLoginUtils.checkLogin(context, entrance, action)
}
override fun init(context: Context?) {
// Do nothing
}
}

View File

@ -0,0 +1,22 @@
package com.gh.gamecenter.message.provider
import android.content.Context
import android.content.Intent
import android.os.Parcelable
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.feature.provider.ICommentDetailProvider
@Route(path = RouteConsts.provider.commentDetail, name = "CommentDetailActivity暴露服务")
class CommentDetailProviderImpl : ICommentDetailProvider {
override fun getIntent(context: Context, commentId: String?, message: Parcelable): Intent? {
ToastUtils.toast("调用->CommentDetailProviderImpl.getIntent")
return null
}
override fun init(context: Context?) {
// Do nothing
}
}

View File

@ -0,0 +1,19 @@
package com.gh.gamecenter.message.provider
import android.content.Context
import android.widget.TextView
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.feature.provider.ICommentUtilsProvider
@Route(path = RouteConsts.provider.commentUtils, name = "CommentUtils暴露服务")
class CommentUtilsProviderImpl : ICommentUtilsProvider {
override fun setCommentTime(textView: TextView, time: Long) {
ToastUtils.toast("调用->CommentUtilsProviderImpl.setCommentTime")
}
override fun init(context: Context?) {
// Do nothing
}
}

View File

@ -0,0 +1,93 @@
package com.gh.gamecenter.message.provider
import android.content.Context
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.provider.IConfigProvider
@Route(path = RouteConsts.provider.config, name = "Config暴露服务")
class ConfigProviderImpl : IConfigProvider {
override fun getTencentAppId(): String {
return ""
}
override fun getWechatAppId(): String {
return ""
}
override fun getWechatSecret(): String {
return ""
}
override fun getUploadLimitSize(): Long {
return 0L
}
override fun getSize(): Int {
return 0
}
override fun getRatio(): Int {
return 0
}
override fun getQuality(): Int {
return 0
}
override fun getGif(): String {
return ""
}
override fun getJpeg(): String {
return ""
}
override fun getWebp(): String {
return ""
}
override fun getGitThumb(): String {
return ""
}
override fun getGifWaterMark(): String {
return ""
}
override fun getQQ(): String {
return ""
}
override fun getQQun(): String {
return ""
}
override fun getQQunKey(): String {
return ""
}
override fun getQuickLoginAppId(): String {
return ""
}
override fun getQuickLoginAppKey(): String {
return ""
}
override fun getWeiboAppKey(): String {
return ""
}
override fun getNightModeSetting(): Boolean {
return false
}
override fun isShowPlugin(gameId: String): Boolean {
return false
}
override fun init(context: Context?) {
// Do nothing
}
}

View File

@ -0,0 +1,18 @@
package com.gh.gamecenter.message.provider
import android.content.Context
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.provider.IDataUtilsProvider
@Route(path = RouteConsts.provider.dataUtils, name = "DataUtils暴露服务")
class DataUtilsProviderImpl : IDataUtilsProvider {
override fun getDeviceCertification() {
// Do nothing
}
override fun init(context: Context?) {
// Do nothing
}
}

View File

@ -0,0 +1,26 @@
package com.gh.gamecenter.message.provider
import android.content.Context
import android.content.Intent
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.feature.provider.IGameCollectionDetailProvider
@Route(path = RouteConsts.provider.gameCollectionDetail, name = "GameCollectionDetailActivity暴露服务")
class GameCollectionDetailProviderImpl : IGameCollectionDetailProvider {
override fun getIntent(context: Context, gameCollectionId: String, isFromSquare: Boolean): Intent? {
ToastUtils.toast("调用->GameCollectionDetailProviderImpl.getIntent")
return null
}
override fun getSpecifiedCommentIntent(context: Context, gameCollectionId: String, topCommentId: String): Intent? {
ToastUtils.toast("调用->GameCollectionDetailProviderImpl.getSpecifiedCommentIntent")
return null
}
override fun init(context: Context?) {
// Do nothing
}
}

View File

@ -0,0 +1,52 @@
package com.gh.gamecenter.message.provider
import android.content.Context
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.provider.IGameDetailProvider
@Route(path = RouteConsts.provider.gameDetail, name = "GameDetailActivity暴露服务")
class GameDetailProviderImpl : IGameDetailProvider {
override fun startGameDetailActivity(
context: Context,
gameId: String,
entrance: String?,
traceEvent: ExposureEvent?
) {
ToastUtils.toast("调用->GameDetailProviderImpl.startGameDetailActivity")
}
override fun startGameDetailActivity(
context: Context,
gameEntity: GameEntity?,
entrance: String,
defaultTab: String,
isSkipGameComment: Boolean,
scrollToLibao: Boolean,
scrollToServer: Boolean,
traceEvent: ExposureEvent?
) {
ToastUtils.toast("调用->GameDetailProviderImpl.startGameDetailActivity")
}
override fun startGameDetailActivity(
context: Context,
gameId: String,
entrance: String?,
defaultTab: Int,
isSkipGameComment: Boolean,
scrollToLibao: Boolean,
openVideoStreaming: Boolean,
openPlatformWindow: Boolean,
traceEvent: ExposureEvent?
) {
ToastUtils.toast("调用->GameDetailProviderImpl.startGameDetailActivity")
}
override fun init(context: Context?) {
// Do nothing
}
}

View File

@ -0,0 +1,18 @@
package com.gh.gamecenter.message.provider
import android.content.Context
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.provider.*
@Route(path = RouteConsts.provider.logUtils, name = "LogUtils暴露服务")
class LogUtilsProviderImpl : ILogUtilsProvider {
override fun login(loginStep: String, loginType: String, entrance: String) {
// Do nothing
}
override fun init(context: Context?) {
// Do nothing
}
}

View File

@ -0,0 +1,27 @@
package com.gh.gamecenter.message.provider
import android.content.Context
import android.content.Intent
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.feature.provider.IMessageDetailProvider
@Route(path = RouteConsts.provider.messageDetail, name = "MessageDetailActivity暴露服务")
class MessageDetailProviderImpl : IMessageDetailProvider {
override fun getIntentById(
context: Context,
newsId: String,
commentNum: Int,
openSoftInput: Boolean,
entrance: String
): Intent? {
ToastUtils.toast("调用->MessageDetailProviderImpl.getIntentById")
return null
}
override fun init(context: Context?) {
// Do nothing
}
}

View File

@ -0,0 +1,26 @@
package com.gh.gamecenter.message.provider
import android.content.Context
import androidx.lifecycle.MediatorLiveData
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.feature.entity.MessageUnreadEntity
import com.gh.gamecenter.feature.provider.IMessageUnreadRepositoryProvider
@Route(path = RouteConsts.provider.messageUnreadRepository, name = "MessageUnreadRepository暴露服务")
class MessageUnreadRepositoryProviderImpl : IMessageUnreadRepositoryProvider {
override fun loadMessageUnreadData() {
ToastUtils.toast("调用->MessageUnreadRepositoryProviderImpl.loadMessageUnreadData")
}
override fun getUnreadLiveData(): MediatorLiveData<MessageUnreadEntity>? {
ToastUtils.toast("调用->MessageUnreadRepositoryProviderImpl.getUnreadLiveData")
return null
}
override fun init(context: Context?) {
// Do nothing
}
}

View File

@ -0,0 +1,65 @@
package com.gh.gamecenter.message.provider
import android.content.Context
import android.content.Intent
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.feature.provider.INewCommentDetailProvider
@Route(path = RouteConsts.provider.newCommentDetail, name = "NewCommentDetailActivity暴露服务")
class NewCommentDetailProviderImpl : INewCommentDetailProvider {
override fun getAnswerCommentIntent(
context: Context,
commentId: String,
questionId: String,
topCommentId: String,
entrance: String,
path: String
): Intent? {
ToastUtils.toast("调用->NewCommentDetailProviderImpl.getGameCollectionCommentIntent")
return null
}
override fun getArticleCommentIntent(
context: Context,
commentId: String,
communityId: String,
articleId: String,
topCommentId: String,
entrance: String,
path: String
): Intent? {
ToastUtils.toast("调用->NewCommentDetailProviderImpl.getGameCollectionCommentIntent")
return null
}
override fun getVideoCommentIntent(
context: Context,
commentId: String,
videoId: String,
topCommentId: String,
entrance: String,
path: String
): Intent? {
ToastUtils.toast("调用->NewCommentDetailProviderImpl.getVideoCommentIntent")
return null
}
override fun getGameCollectionCommentIntent(
context: Context,
commentId: String,
gameCollectionId: String,
topCommentId: String,
entrance: String,
path: String
): Intent? {
ToastUtils.toast("调用->NewCommentDetailProviderImpl.getGameCollectionCommentIntent")
return null
}
override fun init(context: Context?) {
// Do nothing
}
}

View File

@ -0,0 +1,38 @@
package com.gh.gamecenter.message.provider
import android.content.Context
import android.content.pm.PackageInfo
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.provider.IPackageUtilsProvider
@Route(path = RouteConsts.provider.packageUtils, name = "PackageUtils暴露服务")
class PackageUtilsProviderImpl : IPackageUtilsProvider {
override fun obtainProcessName(context: Context): String? {
return ""
}
override fun getGhVersionName(): String {
return ""
}
override fun getGhVersionCode(): Int {
return 0
}
override fun getInstalledPackages(context: Context, flag: Int): List<PackageInfo> {
return listOf()
}
override fun getApkSignatureByPackageName(context: Context, packageName: String): Array<String> {
return arrayOf()
}
override fun getSideLoadedInfo(): MutableMap<String, String>? {
return mutableMapOf()
}
override fun init(context: Context?) {
// Do nothing
}
}

View File

@ -0,0 +1,21 @@
package com.gh.gamecenter.message.provider
import android.content.Context
import android.content.Intent
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.feature.provider.ISimpleAnswerDetailProvider
@Route(path = RouteConsts.provider.simpleAnswerDetail, name = "SimpleAnswerDetailActivity暴露服务")
class SimpleAnswerDetailProviderImpl : ISimpleAnswerDetailProvider {
override fun getIntent(context: Context, answerId: String, entrance: String, path: String): Intent? {
ToastUtils.toast("调用->SimpleAnswerDetailProviderImpl.getIntent")
return null
}
override fun init(context: Context?) {
// Do nothing
}
}

View File

@ -0,0 +1,24 @@
package com.gh.gamecenter.message.provider
import android.content.Context
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.feature.provider.ISubjectProvider
@Route(path = RouteConsts.provider.subject, name = "SubjectActivity暴露服务")
class SubjectProviderImpl : ISubjectProvider {
override fun startSubjectActivity(
context: Context,
id: String?,
name: String?,
isOrder: Boolean,
entrance: String?
) {
ToastUtils.toast("调用->SubjectProviderImpl.startSubjectActivity")
}
override fun init(context: Context?) {
// Do nothing
}
}

View File

@ -0,0 +1,40 @@
package com.gh.gamecenter.message.provider
import android.content.Context
import android.content.Intent
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.provider.IWebProvider
@Route(path = RouteConsts.provider.webActivity, name = "WebActivity暴露服务")
class WebProviderImpl : IWebProvider {
override fun getIntent(context: Context, url: String, autoCompletionTitle: Boolean): Intent? {
return null
}
override fun getWebIntent(context: Context, title: String, url: String): Intent? {
return null
}
override fun getBindWechatIntent(context: Context): Intent? {
return null
}
override fun getSecurityCertificationIntent(context: Context): Intent? {
return null
}
override fun getQAIntent(
context: Context?,
url: String?,
title: String?,
isWebPageHandleBackPressed: Boolean,
qaType: Int
): Intent? {
return null
}
override fun init(context: Context?) {
// Do nothing
}
}

View File

@ -0,0 +1,17 @@
package com.gh.gamecenter.message
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}