Merge branch 'v2.2.0/red_package' into v2.2.0/dev

# Conflicts:
#	gradle.properties
This commit is contained in:
Max
2023-10-27 17:09:46 +08:00
94 changed files with 4675 additions and 204 deletions

View File

@@ -17,6 +17,7 @@ import androidx.multidex.MultiDex;
import com.bumptech.glide.request.target.ViewTarget;
import com.coorchice.library.utils.LogUtils;
import com.facebook.stetho.Stetho;
import com.hjq.toast.ToastUtils;
import com.mob.MobSDK;
import com.mob.moblink.MobLink;
@@ -64,6 +65,7 @@ import com.yizhuan.xchat_android_core.im.custom.bean.OpenSignInAttachment;
import com.yizhuan.xchat_android_core.initial.InitialModel;
import com.yizhuan.xchat_android_core.interceptor.NoParamsInterceptor;
import com.yizhuan.xchat_android_core.interceptor.ParamsInterceptor;
import com.yizhuan.xchat_android_core.interceptor.TimeSyncInterceptor;
import com.yizhuan.xchat_android_core.manager.IMMessageManager;
import com.yizhuan.xchat_android_core.manager.IMSystemMsgManager;
import com.yizhuan.xchat_android_core.market_verify.MarketVerifyModel;
@@ -252,6 +254,7 @@ public class XChatApplication extends BaseApp {
//fixed: Glide Exception:"You must not call setTag() on a view Glide is targeting"
ViewTarget.setTagId(R.id.tag_glide);
initStetho(context);
init(channel);
//生命周期监听
@@ -405,6 +408,7 @@ public class XChatApplication extends BaseApp {
.setBaseUrl(url)
.addInterceptors(new ParamsInterceptor(httpParams))
.addInterceptors(new NoParamsInterceptor())//注意:拦截器的添加顺序,请求的拦截顺序
.addInterceptors(new TimeSyncInterceptor())
.certificates()
.build();
//单例的model 初始化
@@ -543,4 +547,18 @@ public class XChatApplication extends BaseApp {
unregisterActivityLifecycleCallbacks(lifeCycleHelper);
}
}
/**
* 初始化Stetho网络调试
*/
private static void initStetho(Context context) {
if (Env.isDebug()) {
Stetho.initialize(
Stetho.newInitializerBuilder(context)
.enableDumpapp(Stetho.defaultDumperPluginsProvider(context))
.enableWebKitInspector(Stetho.defaultInspectorModulesProvider(context))
.build()
);
}
}
}

View File

@@ -16,6 +16,7 @@ import static com.yizhuan.xchat_android_core.im.custom.bean.CustomAttachment.CUS
import static com.yizhuan.xchat_android_core.im.custom.bean.CustomAttachment.CUSTOM_MSG_SUB_ROOM_PK_NOTIFY;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
@@ -38,6 +39,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.DialogFragment;
import androidx.lifecycle.LifecycleOwner;
import androidx.viewpager2.widget.ViewPager2;
import com.alibaba.fastjson.JSON;
@@ -65,7 +67,7 @@ import com.yizhuan.erban.avroom.fragment.HomePartyFragment;
import com.yizhuan.erban.avroom.fragment.InputPwdDialogFragment;
import com.yizhuan.erban.avroom.presenter.AvRoomPresenter;
import com.yizhuan.erban.avroom.presenter.HomePartyPresenter;
import com.yizhuan.erban.avroom.redpackage.RedPackageOpenDialog;
import com.yizhuan.erban.avroom.redpackage.RedPackageHandler;
import com.yizhuan.erban.avroom.view.IAvRoomView;
import com.yizhuan.erban.avroom.widget.VerticalViewPagerAdapter;
import com.yizhuan.erban.base.BaseMvpActivity;
@@ -128,7 +130,6 @@ import com.yizhuan.xchat_android_core.patriarch.event.CloseMinRoomEvent;
import com.yizhuan.xchat_android_core.patriarch.event.ImPushMsgPmLimitTimeEvent;
import com.yizhuan.xchat_android_core.patriarch.event.PmDismissAllLimitDialogEvent;
import com.yizhuan.xchat_android_core.patriarch.exception.PmRoomLimitException;
import com.yizhuan.xchat_android_core.redpackage.RedPackageModel;
import com.yizhuan.xchat_android_core.redpackage.RedPackageNotifyInfo;
import com.yizhuan.xchat_android_core.room.anotherroompk.ShowGiftDialogEvent;
import com.yizhuan.xchat_android_core.room.bean.RoomInfo;
@@ -140,6 +141,8 @@ import com.yizhuan.xchat_android_core.room.event.RoomTaskTipsEvent;
import com.yizhuan.xchat_android_core.room.pk.event.PKStateEvent;
import com.yizhuan.xchat_android_core.super_admin.util.SAdminOptUtil;
import com.yizhuan.xchat_android_core.super_admin.util.SuperAdminUtil;
import com.yizhuan.xchat_android_core.support.room.RoomContext;
import com.yizhuan.xchat_android_core.support.room.RoomView;
import com.yizhuan.xchat_android_core.treasurefairy.FairyMsgInfoBean;
import com.yizhuan.xchat_android_core.user.UserModel;
import com.yizhuan.xchat_android_core.user.bean.FirstChargeInfo;
@@ -160,10 +163,7 @@ import org.greenrobot.eventbus.ThreadMode;
import java.lang.ref.WeakReference;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@@ -180,7 +180,7 @@ import io.reactivex.disposables.Disposable;
*/
@CreatePresenter(AvRoomPresenter.class)
public class AVRoomActivity extends BaseMvpActivity<IAvRoomView, AvRoomPresenter>
implements View.OnClickListener, IAvRoomView {
implements View.OnClickListener, IAvRoomView, RoomView {
public static final int FROM_TYPE_NORMAL = 0;
public static final int FROM_TYPE_RECOMMEND = 1;
@@ -218,7 +218,6 @@ public class AVRoomActivity extends BaseMvpActivity<IAvRoomView, AvRoomPresenter
private RoomInfo mRoomInfo;
private ImageView ivHeadWear;
private MonsterDialog monsterDialog;
private List<RedPackageOpenDialog> openDialogs;
private ViewPager2 viewpager;
private VerticalViewPagerAdapter mAdapter;
/*********************************显示全服礼物***************************************/
@@ -511,7 +510,6 @@ public class AVRoomActivity extends BaseMvpActivity<IAvRoomView, AvRoomPresenter
if (giftList != null) {
giftList.clear();
}
if (!isResume) showRedPackage(AvRoomDataManager.get().mCurrentRoomInfo);
isResume = true;
}
@@ -616,9 +614,6 @@ public class AVRoomActivity extends BaseMvpActivity<IAvRoomView, AvRoomPresenter
case RoomEvent.MY_SELF_KICK_OUT_ROOM_BY_S_ADMIN:
toBack(true);
break;
case RoomEvent.RECEIVE_RED_PACKAGE:
showRedPackage(AvRoomDataManager.get().mCurrentRoomInfo);
break;
default:
}
}
@@ -680,6 +675,17 @@ public class AVRoomActivity extends BaseMvpActivity<IAvRoomView, AvRoomPresenter
RxBus.get().post(new ShowGiftDialogEvent().setGiftId(giftId));
giftId = 0;
}
/**
* TODO 临时方案后续逐步完善整个房间的RoomContext替换计划
* PS:目前房间进入的逻辑有些凌乱没有明确的生命周期看代码逻辑无论是第一次还是再次进入都会走这里所以在这里绑定View
*/
getRoomContext().onViewAttach(this);
/**
* 每次View重新创建意味着可能在房间外点了红包就重新尝试加载红包并展示
*/
tryShowSourceRedPackage();
}
private void showLiveFinishView(long uid) {
@@ -716,42 +722,25 @@ public class AVRoomActivity extends BaseMvpActivity<IAvRoomView, AvRoomPresenter
AvRoomDataManager.get().release();
}
@SuppressLint("CheckResult")
private void showRedPackage(RoomInfo roomInfo) {
if (roomInfo == null) return;
RedPackageNotifyInfo inRoomNotifyInfo = (RedPackageNotifyInfo) getIntent().getSerializableExtra("notifyInfo");
Map<String, Long> redMap = DemoCache.readRedPackage();
RedPackageModel.INSTANCE.getRedPackage(roomInfo.getUid())
.compose(bindToLifecycle())
.doFinally(() -> {
if (inRoomNotifyInfo != null && (redMap == null || !redMap.containsKey(inRoomNotifyInfo.getRedEnvelopeId()))) {
showRedPackageOpenDialog(inRoomNotifyInfo);
}
})
.filter(notifyInfo -> inRoomNotifyInfo == null || !notifyInfo.getRedEnvelopeId().equals(inRoomNotifyInfo.getRedEnvelopeId()))
.subscribe(this::showRedPackageOpenDialog);
}
private void showRedPackageOpenDialog(RedPackageNotifyInfo notifyInfo) {
if (!isResume) return;
if (openDialogs == null) {
openDialogs = new ArrayList<>();
}
RedPackageOpenDialog openDialog = RedPackageOpenDialog.Companion.newInstance(notifyInfo);
openDialog.show(this);
if (openDialog.getDialog() != null) {
openDialog.getDialog().setOnDismissListener(dialog -> openDialogs.remove(openDialog));
}
openDialogs.add(openDialog);
if (openDialogs.size() >= SHOW_RED_DIALOG_MAX) {
openDialogs.get(SHOW_RED_DIALOG_MAX - 1).dismissAllowingStateLoss();
/**
* 尝试展示来源红包(在房间外点击红包入口进入房间时)
*/
private void tryShowSourceRedPackage() {
try {
RedPackageNotifyInfo inRoomNotifyInfo = (RedPackageNotifyInfo) getIntent().getSerializableExtra("notifyInfo");
if (inRoomNotifyInfo != null) {
RedPackageHandler handler = getRoomContext().findAbility(RedPackageHandler.class.getSimpleName());
if (handler != null) {
long id = Long.parseLong(inRoomNotifyInfo.getRedEnvelopeId());
handler.tryShowRedPackage(id);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void dismissRedPackageDialog() {
if (openDialogs != null) {
openDialogs.clear();
}
if (mCurrentFragment != null) {
mCurrentFragment.dismissSendRedPackageDialog();
}
@@ -1076,7 +1065,6 @@ public class AVRoomActivity extends BaseMvpActivity<IAvRoomView, AvRoomPresenter
DemoCache.readNewUserChargeGift() == 0) {
DemoCache.saveNewUserChargeGift(1);
}
showRedPackage(AvRoomDataManager.get().mCurrentRoomInfo);
dismissLoadingDialog();
}
@@ -1401,6 +1389,24 @@ public class AVRoomActivity extends BaseMvpActivity<IAvRoomView, AvRoomPresenter
getLimitEnterRoomHelper().handleThisContext(this, event.getData(), true, this::handlePmExitRoom);
}
@Nullable
@Override
public Activity getActivity() {
return this;
}
@Nullable
@Override
public RoomContext getRoomContext() {
return RoomContext.Companion.get();
}
@NonNull
@Override
public LifecycleOwner getLifecycleOwner() {
return this;
}
private static class GiftBroadcastObserver implements Observer<BroadcastMessage> {
private WeakReference<AVRoomActivity> mReference;
@@ -1433,5 +1439,4 @@ public class AVRoomActivity extends BaseMvpActivity<IAvRoomView, AvRoomPresenter
}
}
}
}

View File

@@ -0,0 +1,19 @@
package com.yizhuan.erban.avroom.core
import com.yizhuan.erban.avroom.redpackage.RedPackageHandler
import com.yizhuan.xchat_android_core.support.room.RoomAbility
import com.yizhuan.xchat_android_core.support.room.RoomContext
/**
* Created by Max on 2023/10/26 15:41
* Desc:语音房
* @param roomId roomUid
**/
class AudioRoomContext(roomId: Long) : RoomContext(roomId) {
override fun loadAbility(list: MutableMap<String, RoomAbility>) {
super.loadAbility(list)
// 红包处理器
list[RedPackageHandler::class.java.simpleName] = RedPackageHandler()
}
}

View File

@@ -352,7 +352,6 @@ public class RoomOperationDialog extends BottomSheetDialog {
}
}));
}
}
/**

View File

@@ -21,6 +21,9 @@ import android.widget.RelativeLayout
import android.widget.TextView
import androidx.annotation.CallSuper
import androidx.core.content.ContextCompat
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.withResumed
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.chad.library.adapter.base.BaseQuickAdapter
@@ -46,7 +49,7 @@ import com.yizhuan.erban.avroom.dialog.RoomFreeGiftDialog
import com.yizhuan.erban.avroom.dialog.RoomOperationDialog
import com.yizhuan.erban.avroom.firstcharge.FirstChargeDialog
import com.yizhuan.erban.avroom.presenter.BaseRoomPresenter
import com.yizhuan.erban.avroom.redpackage.RedPackageSendDialog
import com.yizhuan.erban.avroom.redpackage.send.RedPackageSendDialog2
import com.yizhuan.erban.avroom.room_album.RoomAlbumModel
import com.yizhuan.erban.avroom.view.IBaseRoomView
import com.yizhuan.erban.avroom.widget.BottomView
@@ -84,6 +87,7 @@ import com.yizhuan.xchat_android_core.gift.bean.GiftInfo
import com.yizhuan.xchat_android_core.gift.event.RoomFreeGiftEvent
import com.yizhuan.xchat_android_core.helper.AtProxy
import com.yizhuan.xchat_android_core.home.bean.BannerInfo
import com.yizhuan.xchat_android_core.home.event.OpenRoomMessageInputEvent
import com.yizhuan.xchat_android_core.im.custom.bean.RoomFollowOwnerAttachment
import com.yizhuan.xchat_android_core.im.custom.bean.RoomFollowOwnerAttachment2
import com.yizhuan.xchat_android_core.manager.AudioEngineManager
@@ -104,6 +108,8 @@ import com.yizhuan.xchat_android_core.share.bean.SessionType
import com.yizhuan.xchat_android_core.super_admin.SaConstant
import com.yizhuan.xchat_android_core.super_admin.model.SuperAdminModel
import com.yizhuan.xchat_android_core.super_admin.util.SuperAdminUtil
import com.yizhuan.xchat_android_core.support.room.RoomView
import com.yizhuan.xchat_android_core.support.room.RoomWidget
import com.yizhuan.xchat_android_core.user.UserModel
import com.yizhuan.xchat_android_core.user.bean.BaseInfo
import com.yizhuan.xchat_android_core.user.bean.UserInfo
@@ -116,6 +122,7 @@ import com.yizhuan.xchat_android_library.rxbus.RxBus
import com.yizhuan.xchat_android_library.utils.*
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import kotlinx.coroutines.launch
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
@@ -130,7 +137,8 @@ import org.greenrobot.eventbus.ThreadMode
*/
open class BaseRoomFragment<V : IBaseRoomView?, P : BaseRoomPresenter<V>?> :
BaseMvpFragment<V?, P?>(),
View.OnClickListener, OnGiftDialogBtnClickListener, IBaseRoomView, OnMicroItemClickListener {
View.OnClickListener, OnGiftDialogBtnClickListener, IBaseRoomView, OnMicroItemClickListener,
RoomView {
private var myUid: Long = 0
protected lateinit var messageView: MessageView
protected lateinit var bottomView: BottomView
@@ -162,7 +170,7 @@ open class BaseRoomFragment<V : IBaseRoomView?, P : BaseRoomPresenter<V>?> :
* 是否開啟禮物值顯示
*/
private var showGiftValue = false
private var redPackageSendDialog: RedPackageSendDialog? = null
private var redPackageSendDialog: RedPackageSendDialog2? = null
private val mOnSoftKeyBoardChangeListener: OnSoftKeyBoardChangeListener =
object : OnSoftKeyBoardChangeListener {
override fun keyBoardShow(height: Int) {
@@ -179,6 +187,8 @@ open class BaseRoomFragment<V : IBaseRoomView?, P : BaseRoomPresenter<V>?> :
private var atProxy: AtProxy? = null
// 房间小组件
private var widgets: HashMap<String, RoomWidget> = HashMap()
@CallSuper
override fun onFindViews() {
@@ -238,6 +248,7 @@ open class BaseRoomFragment<V : IBaseRoomView?, P : BaseRoomPresenter<V>?> :
@SuppressLint("CheckResult")
@CallSuper
override fun initiate() {
initWidget()
//如果不需要開麥,並且還沒有權限的情況下,重置狀態為需要去打開麥克風
myUid = AuthModel.get().currentUid
isCloseScreen = AvRoomDataManager.get().isCloseScreen
@@ -637,6 +648,7 @@ open class BaseRoomFragment<V : IBaseRoomView?, P : BaseRoomPresenter<V>?> :
override fun onDestroyView() {
super.onDestroyView()
releaseView()
unregisterWidgets()
}
override fun onDestroy() {
@@ -1273,10 +1285,7 @@ open class BaseRoomFragment<V : IBaseRoomView?, P : BaseRoomPresenter<V>?> :
}
override fun onSendMsgBtnClick() {
inputLayout.visibility = View.VISIBLE
inputEdit.isFocusableInTouchMode = true
inputEdit.requestFocus()
KeyBoardUtils.showKeyBoard(context, inputEdit)
openMessageInput(null)
}
override fun onSendGiftBtnClick() {
@@ -1292,7 +1301,10 @@ open class BaseRoomFragment<V : IBaseRoomView?, P : BaseRoomPresenter<V>?> :
override fun onMoreBtnClick() {
val dialog = RoomOperationDialog(mContext)
dialog.setOnActionListener {
redPackageSendDialog = RedPackageSendDialog()
redPackageSendDialog = RedPackageSendDialog2()
redPackageSendDialog?.setOnDismissListener {
redPackageSendDialog = null
}
redPackageSendDialog?.show(activity)
}
dialog.show()
@@ -1302,4 +1314,55 @@ open class BaseRoomFragment<V : IBaseRoomView?, P : BaseRoomPresenter<V>?> :
RoomMsgActivity.start(mContext)
}
}
/**
* 注册组件
*/
protected fun registerWidget(name: String, widget: RoomWidget) {
widgets.put(name, widget)
widget.onStart(this)
}
/**
* 取消注册组件
*/
protected fun unregisterWidgets() {
widgets.values.forEach {
it.onStop()
}
widgets.clear()
}
override fun getLifecycleOwner(): LifecycleOwner {
return this
}
open fun initWidget() {
}
@Subscribe(threadMode = ThreadMode.MAIN)
open fun onOpenRoomMessageInputEvent(event: OpenRoomMessageInputEvent) {
lifecycleScope.launch {
lifecycle.withResumed {
view?.postDelayed({
openMessageInput(event.text)
}, 500)
}
}
}
/**
* 打开公屏输入
*/
fun openMessageInput(text: String?) {
inputLayout.visibility = View.VISIBLE
if (text != null) {
inputEdit.setText(text)
inputEdit.setSelection(inputEdit.length())
}
inputEdit.isFocusableInTouchMode = true
inputEdit.requestFocus()
KeyBoardUtils.showKeyBoard(context, inputEdit)
}
}

View File

@@ -26,6 +26,7 @@ import com.yizhuan.erban.avroom.dialog.PKScoreBoardDialog;
import com.yizhuan.erban.avroom.firstcharge.FirstChargePrizeDialog;
import com.yizhuan.erban.avroom.giftvalue.GiftValueDialogUiHelper;
import com.yizhuan.erban.avroom.presenter.HomePartyPresenter;
import com.yizhuan.erban.avroom.redpackage.RedPackageWidget;
import com.yizhuan.erban.avroom.view.IHomePartyView;
import com.yizhuan.erban.avroom.widget.PKBoardView;
import com.yizhuan.erban.base.BaseMvpActivity;
@@ -133,6 +134,12 @@ public class HomePartyRoomFragment extends BaseRoomFragment<IHomePartyView, Home
bottomView.setBottomViewListener(new PartyRoomBottomViewWrapper());
}
@Override
public void initWidget() {
super.initWidget();
registerWidget(RedPackageWidget.class.getSimpleName(), gameBinding.redPackageWidget);
}
@SuppressLint("CheckResult")
@Override
public void initiate() {
@@ -526,6 +533,11 @@ public class HomePartyRoomFragment extends BaseRoomFragment<IHomePartyView, Home
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
gameBinding.redPackageWidget.onStop();
}
@Override
public void onDestroy() {
@@ -808,7 +820,6 @@ public class HomePartyRoomFragment extends BaseRoomFragment<IHomePartyView, Home
} else {
gameBinding.ivTreasureBoxCp.setVisibility(View.GONE);
gameBinding.ivTreasureBox.setVisibility(View.VISIBLE);
GlideApp.with(BasicConfig.INSTANCE.getAppContext())
.load(GoldBoxHelper.getBoxIcon())
.error(R.drawable.icon_room_treasure_box)

View File

@@ -261,5 +261,4 @@ class SingleRoomFragment : BaseRoomFragment<ISingleRoomView?, SingleRoomPresente
event.firstChargeRewardList
).openDialog()
}
}

View File

@@ -20,6 +20,8 @@ import com.netease.nimlib.sdk.chatroom.model.EnterChatRoomResultData;
import com.netease.nimlib.sdk.util.Entry;
import com.yizhuan.erban.R;
import com.yizhuan.erban.avroom.activity.AVRoomActivity;
import com.yizhuan.erban.avroom.core.AudioRoomContext;
import com.yizhuan.xchat_android_core.support.room.RoomContext;
import com.yizhuan.erban.avroom.view.IAvRoomView;
import com.yizhuan.erban.base.BaseMvpPresenter;
import com.yizhuan.xchat_android_core.Constants;
@@ -128,6 +130,8 @@ public class AvRoomPresenter extends BaseMvpPresenter<IAvRoomView> {
.subscribe(roomQueueInfoSparseArray -> {
long uid = AuthModel.get().getCurrentUid();
AudioEngineManager.get().startRtcEngine(uid, roomInfo.getAudioSdkType());
// TODO 临时方案后续逐步完善整个房间的RoomContext替换计划
new AudioRoomContext(roomInfo.getUid()).run();
if (getMvpView() != null) {
getMvpView().enterRoomSuccess();
}

View File

@@ -0,0 +1,125 @@
package com.yizhuan.erban.avroom.redpackage
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import com.yizhuan.erban.avroom.redpackage.open.RedPackageOpenDialog2
import com.yizhuan.xchat_android_core.im.custom.bean.RedPackageAttachment
import com.yizhuan.xchat_android_core.manager.IMNetEaseManager
import com.yizhuan.xchat_android_core.manager.RoomEvent
import com.yizhuan.xchat_android_core.redpackage.RedPackageModel
import com.yizhuan.xchat_android_core.redpackage.RedPackageNotifyInfo
import com.yizhuan.xchat_android_core.support.room.RoomContext
import com.yizhuan.xchat_android_core.support.room.RoomHandler
/**
* Created by Max on 2023/10/26 11:26
* Desc:红包处理器
**/
class RedPackageHandler : RoomHandler() {
// 入口图标-数据
val iconLiveData: MutableLiveData<RedPackageNotifyInfo?> = MutableLiveData()
override fun onStart(context: RoomContext) {
super.onStart(context)
registerSignaling()
}
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
super.onStateChanged(source, event)
if (event == Lifecycle.Event.ON_RESUME) {
requestLatestRoomRedPackage()
}
}
/**
* 尝试展示红包
* PS目前这个方法仅为了解决通过房间外的红包入口进入房间后需要查询该红包展示出来。
*/
fun tryShowRedPackage(redPackageId: Long) {
val disposable = RedPackageModel.getRedPackage(redPackageId)
.compose(bindToLifecycle())
.subscribe({ item ->
if (item.state != 3 && item.state != 6 && item.state != 4) {
roomView?.getActivity().let {
RedPackageOpenDialog2.newInstance(item).show(it)
}
}
}, {})
}
/**
* 监听房间信令
*/
private fun registerSignaling() {
// TODO 临时方案不应在这里直接监听IM后续会统一分发出口
// 信令来源:新/旧版的厅内红包,非全服红包
IMNetEaseManager.get().chatRoomEventObservable
.compose<RoomEvent>(bindToLifecycle<RoomEvent>())
.filter {
it?.event == RoomEvent.RECEIVE_RED_PACKAGE && it?.chatRoomMessage?.attachment is RedPackageAttachment
}.map {
(it.chatRoomMessage.attachment as RedPackageAttachment).getRedPackageNotifyInfo()
}
.subscribe { data: RedPackageNotifyInfo ->
handleRoomRedPackage(data, true)
}
}
/**
* 查找最新的厅内红包(新/旧版的厅内红包,非全服红包)
*/
private fun requestLatestRoomRedPackage() {
logD("requestLatestRoomRedPackage()")
val roomId = roomContext?.roomId ?: return
logD("requestLatestRoomRedPackage() roomId:$roomId")
RedPackageModel.getLatestRoomRedPackage(roomId)
.compose(bindToLifecycle())
.subscribe({ data ->
logD("requestLatestRoomRedPackage() data:$data")
handleRoomRedPackage(data, false)
}, {})
}
/**
* 处理厅内红包,非全服红包
* @param isSignaling 是否来自信令?
*/
private fun handleRoomRedPackage(data: RedPackageNotifyInfo, isSignaling: Boolean) {
tryShowOpenDialog(data, isSignaling)
updateIcon(data)
}
/**
* 更新红包入口图标
*/
private fun updateIcon(data: RedPackageNotifyInfo?) {
iconLiveData.postValue(data)
}
/**
* 尝试展示红包领取弹窗
*/
private fun tryShowOpenDialog(data: RedPackageNotifyInfo, isSignaling: Boolean) {
val activity = roomView?.getActivity() ?: return
if (activity.isFinishing) {
return
}
if (!isSignaling) {
return
}
if (data.kind == 0 || (data.kind == 1 && data.validityType == 0)) {
//【旧版本-厅内红包kind=0】、【新版本-厅内无门槛-立即生效红包kind=1&validityType=0】
RedPackageOpenDialog2.newInstance(data).show(activity)
}
}
/**
* 请求刷新Icon数据已过期
*/
fun requestRefreshIcon() {
// updateIcon(null)
requestLatestRoomRedPackage()
}
}

View File

@@ -77,18 +77,17 @@ class RedPackageOpenDialog : BaseDialog<DialogRedPackageOpenBinding>() {
@SuppressLint("CheckResult")
private fun startRedPacketAnim(v: View) {
RedPackageModel.openRedPackage(redPackageNotifyInfo.redEnvelopeId)
RedPackageModel.openRedPackage(redPackageNotifyInfo.redEnvelopeId.toLong())
.subscribe({
redPackageInfo = it
if (mObjectAnimator?.isRunning == false) {
openRedPackage()
}
}
, {
binding?.ivOpen?.isEnabled = true
mObjectAnimator?.cancel()
SingleToastUtil.showToast(it.message)
})
}, {
binding?.ivOpen?.isEnabled = true
mObjectAnimator?.cancel()
SingleToastUtil.showToast(it.message)
})
mObjectAnimator = ObjectAnimator.ofFloat(v, "rotationY", 0f, 360f)
mObjectAnimator?.let {
@@ -120,31 +119,45 @@ class RedPackageOpenDialog : BaseDialog<DialogRedPackageOpenBinding>() {
binding?.clOpened?.visibility = View.VISIBLE
binding?.clRed?.visibility = View.GONE
binding?.tvNicknameOpened?.text = redPackageNotifyInfo.sendUserNick
ImageLoadUtils.loadAvatar(context, redPackageNotifyInfo.sendUserAvatar, binding?.ivAvatarOpened)
ImageLoadUtils.loadAvatar(
context,
redPackageNotifyInfo.sendUserAvatar,
binding?.ivAvatarOpened
)
packageInfo.redEnvelopeVO?.apply {
binding?.tvContentOpened?.text = message
when (packageInfo.redEnvelopeState) {
SUCCESS -> {
packageInfo.redEnvelopeItemVOs?.firstOrNull { item -> item.userVO.uid == UserUtils.getUserUid() }?.let {
it.redEnvelopeGiftItemVOs?.apply {
if (!isEmpty()) {
val adapter = RedPackageGiftAdapter(R.layout.item_red_package_gift_top)
binding?.rvGift?.visibility = View.VISIBLE
binding?.tvDiamondNum?.visibility = View.GONE
binding?.rvGift?.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
binding?.rvGift?.adapter = adapter
adapter.setNewData(this)
binding?.tvTips?.setTextColor(Color.WHITE)
binding?.tvTips?.text = MessageView.SpannableBuilder(binding?.tvTips)
.append(ResUtil.getString(R.string.avroom_redpackage_redpackageopendialog_03))
.append(it.amount, ForegroundColorSpan(Color.parseColor("#FDCD00")))
.append(ResUtil.getString(R.string.avroom_redpackage_redpackageopendialog_04))
.build()
packageInfo.redEnvelopeItemVOs?.firstOrNull { item -> item.userVO.uid == UserUtils.getUserUid() }
?.let {
it.redEnvelopeGiftItemVOs?.apply {
if (!isEmpty()) {
val adapter =
RedPackageGiftAdapter(R.layout.item_red_package_gift_top)
binding?.rvGift?.visibility = View.VISIBLE
binding?.tvDiamondNum?.visibility = View.GONE
binding?.rvGift?.layoutManager = LinearLayoutManager(
context,
RecyclerView.HORIZONTAL,
false
)
binding?.rvGift?.adapter = adapter
adapter.setNewData(this)
binding?.tvTips?.setTextColor(Color.WHITE)
binding?.tvTips?.text =
MessageView.SpannableBuilder(binding?.tvTips)
.append(ResUtil.getString(R.string.avroom_redpackage_redpackageopendialog_03))
.append(
it.amount,
ForegroundColorSpan(Color.parseColor("#FDCD00"))
)
.append(ResUtil.getString(R.string.avroom_redpackage_redpackageopendialog_04))
.build()
}
}
binding?.tvDiamondNum?.text = it.amount.substringBefore(".")
}
binding?.tvDiamondNum?.text = it.amount.substringBefore(".")
}
if (!AvRoomDataManager.get().isRoomFans){
if (!AvRoomDataManager.get().isRoomFans) {
AvRoomDataManager.get().roomUid.let {
CollectionRoomModel.get().followRoom("1", it)
.subscribe { _: String? ->
@@ -154,6 +167,7 @@ class RedPackageOpenDialog : BaseDialog<DialogRedPackageOpenBinding>() {
}
}
}
TIME_OUT, REMAIN_ZERO, TIME_OUT_BACK -> {
binding?.clOpened?.setBackgroundResource(R.drawable.room_red_package_get_bg)
binding?.tvDiamondNum?.visibility = View.GONE

View File

@@ -28,6 +28,7 @@ import com.yizhuan.xchat_android_core.pay.PayModel
import com.yizhuan.xchat_android_core.pay.event.UpdateWalletInfoEvent
import com.yizhuan.xchat_android_core.redpackage.*
import com.yizhuan.xchat_android_core.user.UserModel
import com.yizhuan.xchat_android_core.utils.LogUtils
import com.yizhuan.xchat_android_core.utils.toIntOrDef
import com.yizhuan.xchat_android_library.annatation.ActLayoutRes
import com.yizhuan.xchat_android_library.common.util.DeviceUtil
@@ -219,23 +220,24 @@ class RedPackageSendDialog : BaseDialog<DialogRedPackageSendBinding>(), GridPass
override fun onTextChanged(psw: String) {
val password = passWordFragment?.password?.password ?: ""
if (password.length == 6) {
dialogManager.showProgressDialog(context)
RedPackageModel.sendRedPackage(binding?.editGoldNum?.text.toString(),
binding?.editRedText?.text.toString().ifEmpty { "恭喜發財,大吉大利!" },
binding?.editRedNum?.text.toString(),
AvRoomDataManager.get().mCurrentRoomInfo?.uid.toString(), getRedType(), DESUtils.DESAndBase64(password))
.doOnError {
dialogManager.dismissDialog()
SingleToastUtil.showToast(it.message)
passWordFragment?.password?.clearPassword()
}
.subscribe { _ ->
PayModel.get().getWalletInfo(AuthModel.get().currentUid).subscribe()
dialogManager.dismissDialog()
SingleToastUtil.showToast("發送成功")
passWordFragment?.dismissAllowingStateLoss()
dismissAllowingStateLoss()
}
LogUtils.d("onTextChanged() editGoldNum:${binding?.editGoldNum?.text.toString()} NUM:${binding?.editRedNum?.text.toString()}")
// dialogManager.showProgressDialog(context)
// RedPackageModel.sendRedPackage(binding?.editGoldNum?.text.toString(),
// binding?.editRedText?.text.toString().ifEmpty { "恭喜發財,大吉大利!" },
// binding?.editRedNum?.text.toString(),
// AvRoomDataManager.get().mCurrentRoomInfo?.uid.toString(), getRedType(), DESUtils.DESAndBase64(password))
// .doOnError {
// dialogManager.dismissDialog()
// SingleToastUtil.showToast(it.message)
// passWordFragment?.password?.clearPassword()
// }
// .subscribe { _ ->
// PayModel.get().getWalletInfo(AuthModel.get().currentUid).subscribe()
// dialogManager.dismissDialog()
// SingleToastUtil.showToast("發送成功")
// passWordFragment?.dismissAllowingStateLoss()
// dismissAllowingStateLoss()
// }
}
}

View File

@@ -0,0 +1,243 @@
package com.yizhuan.erban.avroom.redpackage
import android.content.Context
import android.util.AttributeSet
import android.util.TypedValue
import android.view.LayoutInflater
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isVisible
import androidx.lifecycle.Observer
import com.chuhai.utils.ServiceTime
import com.chuhai.utils.ktx.singleClick
import com.yizhuan.erban.R
import com.yizhuan.erban.avroom.redpackage.open.RedPackageOpenDialog2
import com.yizhuan.erban.common.widget.dialog.DialogManager
import com.yizhuan.xchat_android_core.redpackage.RedPackageModel
import com.yizhuan.xchat_android_core.redpackage.RedPackageNotifyInfo
import com.yizhuan.xchat_android_core.support.room.RoomContext
import com.yizhuan.xchat_android_core.support.room.RoomView
import com.yizhuan.xchat_android_core.support.room.RoomWidget
import com.yizhuan.xchat_android_library.utils.SingleToastUtil
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import okhttp3.internal.toLongOrDefault
import java.text.SimpleDateFormat
import java.util.TimeZone
import java.util.concurrent.TimeUnit
/**
* Created by Max on 2023/10/24 16:37
* Desc:房间内的红包入口
**/
class RedPackageWidget : ConstraintLayout, RoomWidget {
private var roomView: RoomView? = null
private var textView: TextView? = null
private var numView: TextView? = null
private var countDownDisposable: Disposable? = null
private var data: RedPackageNotifyInfo? = null
private val redPackageHandler: RedPackageHandler?
get() = RoomContext.get()
?.findAbility<RedPackageHandler>(RedPackageHandler::class.java.simpleName)
// 临时这样实现,后续优化进度弹窗
private var dialogManager: DialogManager? = null
// 倒计时格式(分:秒)
private val mmssFormat by lazy(LazyThreadSafetyMode.NONE) {
SimpleDateFormat("mm:ss").apply {
timeZone = TimeZone.getTimeZone("GMT+00:00")
}
}
// 为了获取到RoomContext
private val contextObserver = object : Observer<RoomContext?> {
override fun onChanged(value: RoomContext?) {
if (value != null) {
RoomContext.contextLiveData.removeObserver(this)
val view = this@RedPackageWidget.roomView
if (view != null) {
init(view)
}
}
}
}
private val compositeDisposable = CompositeDisposable()
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
)
constructor(
context: Context,
attrs: AttributeSet?,
defStyleAttr: Int,
defStyleRes: Int
) : super(context, attrs, defStyleAttr, defStyleRes)
init {
// 默认不可见
this.isVisible = false
LayoutInflater.from(context)
.inflate(R.layout.red_package_widget, this, true)
textView = findViewById(R.id.tv_text)
numView = findViewById(R.id.tv_num)
this.setBackgroundResource(R.drawable.red_package_widget_bg)
singleClick {
data?.let {
tryShowRedPackage(it)
}
}
}
/**
* 开始倒计时
* @param time 开抢时间
*/
private fun startCountDown(time: Long) {
val count = time - ServiceTime.time
if (count <= 0) {
switchUI(false)
return
}
countDownDisposable =
Observable.intervalRange(0, count / 1000, 0, 1000L, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.doOnNext {
val gap = time - ServiceTime.time
if (gap >= 0) {
textView?.text = mmssFormat.format(gap)
}
}
.doOnComplete {
switchUI(false)
}
.subscribe()
}
/**
* 切换UI
* @param isCountDown 是否倒计时模式?否则为可抢红包模式
*/
private fun switchUI(isCountDown: Boolean) {
if (isCountDown) {
textView?.setBackgroundResource(R.drawable.shape_99292929_8)
textView?.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 10f)
} else {
textView?.setBackgroundResource(R.drawable.red_package_widget_bg_text)
textView?.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 9f)
textView?.setText(R.string.red_package_widget_get)
}
}
override fun onStart(roomView: RoomView) {
this.roomView = roomView
roomView.getActivity()?.let {
this.dialogManager = DialogManager(it)
this.dialogManager?.setCanceledOnClickOutside(false)
}
// TODO #临时方案 因为目前View的生命周期比RoomContext还早暂时这样拿到Context
val context = RoomContext.contextLiveData.value
if (context == null) {
RoomContext.contextLiveData.observeForever(contextObserver)
} else {
init(roomView)
}
}
private fun init(roomView: RoomView) {
val lifecycleOwner = roomView.getLifecycleOwner()
redPackageHandler?.iconLiveData?.observe(lifecycleOwner) {
loadData(it)
}
}
/**
* 更新数据
*/
private fun loadData(data: RedPackageNotifyInfo?) {
logD("loadData() data:$data")
this.data = data
stopCountDown()
if (data != null) {
val num = data.redEnvelopeNum
if (num > 0) {
if (num > 99) {
numView?.text = "99+"
} else {
numView?.text = num.toString()
}
numView?.isVisible = true
} else {
numView?.isVisible = false
}
if (data.validityType == 1) {
// 限时生效
switchUI(true)
startCountDown(data.beginTime)
} else {
// 立即生效
switchUI(false)
}
this.isVisible = true
} else {
this.isVisible = false
}
}
/**
* 尝试展示红包
*/
private fun tryShowRedPackage(data: RedPackageNotifyInfo) {
dialogManager?.context?.let {
dialogManager?.showProgressDialog(it, false)
}
val disposable = RedPackageModel.getRedPackage(data.redEnvelopeId.toLongOrDefault(0))
.subscribe({ item ->
dialogManager?.dismissDialog()
when (item.state) {
3 -> {
redPackageHandler?.requestRefreshIcon()
SingleToastUtil.showToast(R.string.red_package_result_empty_tips)
}
6 -> {
redPackageHandler?.requestRefreshIcon()
SingleToastUtil.showToast(R.string.red_package_disabled_tips)
}
else -> {
roomView?.getActivity().let {
RedPackageOpenDialog2.newInstance(item).show(it)
}
}
}
}, {
SingleToastUtil.showToast(it.message)
dialogManager?.dismissDialog()
})
compositeDisposable.add(disposable)
}
override fun onStop() {
compositeDisposable.dispose()
RoomContext.contextLiveData.removeObserver(contextObserver)
stopCountDown()
this.dialogManager?.dismissDialog()
this.dialogManager = null
this.roomView = null
}
private fun stopCountDown() {
if (countDownDisposable?.isDisposed == false) {
countDownDisposable?.dispose()
}
}
}

View File

@@ -0,0 +1,502 @@
package com.yizhuan.erban.avroom.redpackage.open
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.View
import android.view.WindowManager
import android.view.animation.AccelerateDecelerateInterpolator
import androidx.core.view.isVisible
import com.chuhai.utils.ServiceTime
import com.chuhai.utils.ktx.getColorById
import com.chuhai.utils.ktx.singleClick
import com.chuhai.utils.ktx.toStringRes
import com.chuhai.utils.log.ILog
import com.chuhai.utils.spannable.spannableBuilder
import com.trello.rxlifecycle3.android.FragmentEvent
import com.yizhuan.erban.R
import com.yizhuan.erban.base.BaseDialog
import com.yizhuan.erban.databinding.RedPackageOpenDialogBinding
import com.yizhuan.erban.ui.utils.loadAvatar
import com.yizhuan.erban.ui.widget.UserInfoDialog
import com.yizhuan.xchat_android_core.auth.AuthModel
import com.yizhuan.xchat_android_core.home.event.FollowRoomEvent
import com.yizhuan.xchat_android_core.home.event.OpenRoomMessageInputEvent
import com.yizhuan.xchat_android_core.home.event.ShareRoomEvent
import com.yizhuan.xchat_android_core.home.model.CollectionRoomModel
import com.yizhuan.xchat_android_core.manager.AvRoomDataManager
import com.yizhuan.xchat_android_core.pay.PayModel
import com.yizhuan.xchat_android_core.redpackage.*
import com.yizhuan.xchat_android_core.support.config.Constants
import com.yizhuan.xchat_android_library.annatation.ActLayoutRes
import com.yizhuan.xchat_android_library.utils.SingleToastUtil
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import org.greenrobot.eventbus.EventBus
import java.text.SimpleDateFormat
import java.util.TimeZone
import java.util.concurrent.TimeUnit
/**
* Created by Max on 2023/10/24 10:14
* Desc:领取红包
**/
@ActLayoutRes(R.layout.red_package_open_dialog)
class RedPackageOpenDialog2 : BaseDialog<RedPackageOpenDialogBinding>(), ILog {
private var btnAnimator: ObjectAnimator? = null
private var countDownDisposable: Disposable? = null
// 倒计时格式(分:秒)
private val mmssFormat by lazy(LazyThreadSafetyMode.NONE) {
SimpleDateFormat("mm:ss").apply {
timeZone = TimeZone.getTimeZone("GMT+00:00")
}
}
private val data by lazy(LazyThreadSafetyMode.NONE) {
arguments?.getSerializable(Constants.KEY_INTENT) as? RedPackageData
}
// 打开红包的结果
private var openResultRunnable: (() -> Unit)? = null
// 按钮动画是否有效(至少转过了一周期)
private var isValidOfBtnAnimator: Boolean = false
// 按钮执行函数
private var btnRunnable: (() -> Unit)? = null
// 打开红包接口请求中
private var opening = false
companion object {
/**
* 此入口来源仅限:【全服红包信令】、【厅内无门槛-立即生效红包信令】
*/
fun newInstance(data: RedPackageNotifyInfo): RedPackageOpenDialog2 {
/**
* 收到这两种信令直接展示即可,无需再次请求接口查询状态,
* 但由于信令和接口查询的数据格式不一致,所以这里做下转换,达到逻辑共用
*/
val item = RedPackageData(
amount = null,
avatar = data.sendUserAvatar,
beginTime = data.beginTime,
commissionAmount = null,
createTime = null,
duration = null,
endTime = null,
finish = true,
id = data.redEnvelopeId.toLongOrNull(),
kind = data.kind,
message = data.redEnvelopeMessage,
nick = data.sendUserNick,
num = null,
originalAmount = null,
position = null,
roomTitle = null,
roomUid = data.roomUid,
//红包状态 1 开抢中 2 过时 3 抢光了 4 抢到了 5 将要开始 6 超时已退还
state = null,
type = null,
updateTime = null,
userId = null,
validityType = data.validityType,
result = null
)
return newInstance(item)
}
fun newInstance(data: RedPackageData): RedPackageOpenDialog2 {
return RedPackageOpenDialog2().apply {
arguments = Bundle().apply {
putSerializable(Constants.KEY_INTENT, data)
}
}
}
}
override fun onStart() {
width = WindowManager.LayoutParams.MATCH_PARENT
height = WindowManager.LayoutParams.WRAP_CONTENT
super.onStart()
}
override fun init() {
val data = this.data
if (data == null) {
dismissAllowingStateLoss()
return
}
dialog?.setCanceledOnTouchOutside(false)
binding.ivClose.singleClick {
dismissAllowingStateLoss()
}
binding.layoutBtn.singleClick {
btnRunnable?.invoke()
}
loadData(data)
}
/**
* 加载红包数据
*/
private fun loadData(data: RedPackageData) {
//红包状态 1 开抢中 2 过时 3 抢光了 4 抢到了 5 将要开始 6 超时已退还
if (data.state == 4) {
loadResult(data.result)
} else {
// else简单理解为可领取状态正常情况下在RedPackageWidget点击时有异常状态过滤
loadOpen(data)
}
}
/**
* 加载可领取数据
*/
private fun loadOpen(data: RedPackageData) {
switchResultViewVisible(false)
switchOpenViewVisible(true)
binding.ivSendAvatar.loadAvatar(data.avatar)
binding.tvSendName.text = data.nick
loadTips(data)
loadBtnState(data)
}
/**
* 加载红包提示
*/
private fun loadTips(data: RedPackageData) {
binding.tvOpenTips.isVisible = false
when (data.kind) {
2 -> {
binding.tvOpenTips.spannableBuilder()
.appendText(
text = R.string.red_package_open_tips_follow.toStringRes(),
textColor = getColorById(R.color.color_FFF87A)
)
.appendText(text = R.string.red_package_open_tips_msg_end.toStringRes()).apply()
binding.tvOpenTips.isVisible = true
}
3 -> {
binding.tvOpenTips.spannableBuilder()
.appendText(
text = R.string.red_package_open_tips_share.toStringRes(),
textColor = getColorById(R.color.color_FFF87A)
)
.appendText(text = R.string.red_package_open_tips_msg_end.toStringRes()).apply()
binding.tvOpenTips.isVisible = true
}
4 -> {
binding.tvOpenTips.spannableBuilder()
.appendText(text = R.string.red_package_open_tips_msg1.toStringRes())
.appendText(
text = data.message ?: "",
textColor = getColorById(R.color.color_FFF87A)
)
.appendText(text = R.string.red_package_open_tips_msg_end.toStringRes()).apply()
binding.tvOpenTips.isVisible = true
}
else -> {
loadTips(data.message)
}
}
}
/**
* 加载按钮状态
*/
private fun loadBtnState(data: RedPackageData) {
binding.groupBtnTime.isVisible = false
binding.ivBtnGet.isVisible = false
binding.tvBtnMsg.isVisible = false
btnRunnable = null
val gap = (data.beginTime ?: 0) - ServiceTime.time
// 开始了?
val started = gap < 0
if (data.kind == 0) {
// 旧厅内红包or全服红包
switchToOpenState()
return
}
if (data.kind == 1 || data.finish == true) {
if (started) {
switchToOpenState()
} else {
startCountDown(data.beginTime ?: 0)
}
} else {
binding.tvBtnMsg.isVisible = true
if (started) {
binding.tvBtnMsg.setText(R.string.red_package_open_btn_no)
btnRunnable = {
SingleToastUtil.showToast(R.string.red_package_open_no_tips)
}
} else {
when (data.kind) {
2 -> {
binding.tvBtnMsg.setText(R.string.red_package_open_btn_follow)
btnRunnable = {
dismissAllowingStateLoss()
showUserDialog(data.userId)
}
}
3 -> {
binding.tvBtnMsg.setText(R.string.red_package_open_btn_share)
btnRunnable = {
dismissAllowingStateLoss()
EventBus.getDefault().post(ShareRoomEvent())
}
}
4 -> {
binding.tvBtnMsg.setText(R.string.red_package_open_btn_msg)
btnRunnable = {
dismissAllowingStateLoss()
EventBus.getDefault().post(OpenRoomMessageInputEvent(data.message))
}
}
else -> {
binding.tvBtnMsg.text = ""
}
}
}
}
}
/**
* 开始倒计时
* @param time 开抢时间
*/
private fun startCountDown(time: Long) {
stopCountDown()
val count = time - ServiceTime.time
if (count <= 0) {
switchToOpenState()
return
}
binding.groupBtnTime.isVisible = true
countDownDisposable =
Observable.intervalRange(0, count / 1000, 0, 1000L, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.doOnNext {
val gap = time - ServiceTime.time
if (gap >= 0) {
binding.tvBtnTime.text = mmssFormat.format(gap)
}
}
.doOnComplete {
switchToOpenState()
}
.subscribe()
}
private fun stopCountDown() {
if (countDownDisposable?.isDisposed == false) {
countDownDisposable?.dispose()
}
}
/**
* 切换为可抢状态
*/
private fun switchToOpenState() {
binding.tvBtnMsg.isVisible = false
binding.groupBtnTime.isVisible = false
binding.ivBtnGet.isVisible = true
btnRunnable = {
if (!opening) {
this.data?.let {
openRedPackage(it)
}
}
}
}
private fun loadTips(msg: String?) {
if (msg.isNullOrEmpty()) {
binding.tvOpenTips.isVisible = false
} else {
binding.tvOpenTips.text = msg
binding.tvOpenTips.isVisible = true
}
}
/**
* 打开红包
*/
private fun openRedPackage(data: RedPackageData) {
opening = true
openResultRunnable = null
startRedPacketAnim(binding.layoutBtn)
val d = RedPackageModel.openRedPackage(data.id ?: 0)
.compose(bindUntilEvent(FragmentEvent.DESTROY_VIEW)).subscribe({
if (isValidOfBtnAnimator) {
btnAnimator?.cancel()
onOpenSuccess(it)
} else {
openResultRunnable = {
onOpenSuccess(it)
}
}
}, {
if (isValidOfBtnAnimator) {
btnAnimator?.cancel()
onOpenFail(it)
} else {
openResultRunnable = {
onOpenFail(it)
}
}
})
}
/**
* 加载结果
*/
private fun loadResult(data: RedPackageInfo?) {
binding.layoutContent.setBackgroundResource(R.drawable.red_package_result_bg)
switchOpenViewVisible(false)
switchResultViewVisible(true)
binding.groupResultMoney.isVisible = false
binding.tvResultEmptyTips.isVisible = false
binding.ivResultSendAvatar.loadAvatar(data?.redEnvelopeVO?.userVO?.avatar)
binding.tvResultSendName.text = data?.redEnvelopeVO?.userVO?.nick ?: ""
val name = data?.redEnvelopeVO?.userVO?.nick ?: ""
if (name.length > 8) {
binding.tvResultSendName.text = name?.take(8) + "..的紅包"
} else {
binding.tvResultSendName.text = name + "的紅包"
}
binding.tvMessage.text = data?.redEnvelopeVO?.message
binding.tvCount.text = R.string.red_package_result_count_format.toStringRes()
.format(data?.redEnvelopeVO?.pickNum ?: 0, data?.redEnvelopeVO?.totalNum ?: 0)
val adapter = RedPackageResultAdapter()
binding.recyclerView.adapter = adapter
adapter.setNewData(data?.redEnvelopeItemVOs)
when (data?.redEnvelopeState) {
RedEnvelopeState.SUCCESS -> {
binding.tvMoney.text = data.currentUserAmount.toString().substringBefore(".")
binding.groupResultMoney.isVisible = true
}
RedEnvelopeState.TIME_OUT, RedEnvelopeState.REMAIN_ZERO, RedEnvelopeState.TIME_OUT_BACK -> {
binding.tvResultEmptyTips.isVisible = true
}
}
}
/**
* 打开红包响应成功
* @param data 结果
*/
private fun onOpenSuccess(data: RedPackageInfo) {
loadResult(data)
when (data.redEnvelopeState) {
RedEnvelopeState.SUCCESS -> {
// 下面这段是延续老代码保留的
if (!AvRoomDataManager.get().isRoomFans) {
AvRoomDataManager.get().roomUid.let {
CollectionRoomModel.get().followRoom("1", it)
.subscribe { _: String? ->
AvRoomDataManager.get().isRoomFans = true
EventBus.getDefault().post(FollowRoomEvent())
}
}
}
}
}
PayModel.get().getWalletInfo(AuthModel.get().currentUid).subscribe()
opening = false
}
/**
* 打开红包响应失败
*/
private fun onOpenFail(throwable: Throwable) {
switchToOpenState()
btnAnimator?.cancel()
SingleToastUtil.showToast(throwable.message)
opening = false
}
@SuppressLint("CheckResult")
private fun startRedPacketAnim(v: View) {
if (btnAnimator?.isRunning == true) {
btnAnimator?.cancel()
}
isValidOfBtnAnimator = false
btnAnimator = ObjectAnimator.ofFloat(v, "rotationY", 0f, 360f)
btnAnimator?.let {
it.duration = 1500
it.repeatCount = ValueAnimator.INFINITE
it.repeatMode = ValueAnimator.RESTART
it.interpolator = AccelerateDecelerateInterpolator()
it.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationRepeat(animation: Animator) {
super.onAnimationRepeat(animation)
isValidOfBtnAnimator = true
if (openResultRunnable != null) {
animation.cancel()
openResultRunnable?.invoke()
}
}
})
it.start()
}
}
private fun switchResultViewVisible(isVisible: Boolean) {
binding.groupResult.isVisible = isVisible
binding.groupResultMoney.isVisible = isVisible
binding.tvResultEmptyTips.isVisible = isVisible
}
private fun switchOpenViewVisible(isVisible: Boolean) {
binding.groupOpen.isVisible = isVisible
binding.tvOpenTips.isVisible = isVisible
}
override fun onDestroyView() {
super.onDestroyView()
btnAnimator?.cancel()
btnAnimator = null
stopCountDown()
}
override fun onPause() {
super.onPause()
dismissAllowingStateLoss()
}
private fun showUserDialog(uid: Long?) {
if (uid == null) {
return
}
activity?.let {
UserInfoDialog.showNewUserInfoDialog(
activity,
uid
)
}
}
}

View File

@@ -0,0 +1,46 @@
package com.yizhuan.erban.avroom.redpackage.open
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.core.view.isVisible
import com.chad.library.adapter.base.BaseQuickAdapter
import com.chad.library.adapter.base.BaseViewHolder
import com.yizhuan.erban.R
import com.yizhuan.erban.ui.utils.loadAvatar
import com.yizhuan.erban.utils.UserUtils
import com.yizhuan.xchat_android_core.redpackage.RedEnvelopeItemVO
import com.yizhuan.xchat_android_core.utils.subAndReplaceDot
import com.yizhuan.xchat_android_library.utils.ResUtil
import java.text.SimpleDateFormat
import java.util.Locale
/**
* Created by Max on 2023/10/27 20:24
* Desc:红包领取结果
**/
class RedPackageResultAdapter :
BaseQuickAdapter<RedEnvelopeItemVO, BaseViewHolder>(R.layout.red_package_open_item_result) {
private val dateFormat = SimpleDateFormat(
ResUtil.getString(R.string.avroom_redpackage_redpackageopenadapter_01),
Locale.CHINA
)
override fun convert(helper: BaseViewHolder, item: RedEnvelopeItemVO) {
helper.getView<ImageView>(R.id.iv_avatar).loadAvatar(item.userVO.avatar)
val isSelf = item.userVO.uid == UserUtils.getUserUid()
helper.setText(
R.id.tv_name,
if (isSelf) ResUtil.getString(R.string.avroom_redpackage_redpackageopenadapter_02) else item.userVO.nick.subAndReplaceDot(
6
)
)
helper.setText(R.id.tv_money, item.amount.substringBefore("."))
try {
helper.setText(R.id.tv_time, dateFormat.format(item.createTime))
} catch (e: Exception) {
helper.setText(R.id.tv_time, "")
}
helper.getView<View>(R.id.v_line).isVisible = helper.bindingAdapterPosition != itemCount - 1
}
}

View File

@@ -0,0 +1,263 @@
package com.yizhuan.erban.avroom.redpackage.send
import android.graphics.Color
import androidx.core.view.isVisible
import com.chuhai.utils.ktx.addDisableFilter
import com.chuhai.utils.ktx.getColorById
import com.chuhai.utils.ktx.singleClick
import com.yizhuan.erban.R
import com.yizhuan.erban.base.BaseBindingFragment
import com.yizhuan.erban.databinding.RedPackagePrivateFragmentBinding
import com.yizhuan.erban.pay.password.GiveGoldPassWordFragment
import com.yizhuan.erban.pay.widget.GridPasswordNoFocusView
import com.yizhuan.erban.ui.setting.ModifyPwdActivity
import com.yizhuan.erban.ui.widget.magicindicator.buildins.UIUtil
import com.yizhuan.erban.ui.widget.recyclerview.decoration.ColorDecoration
import com.yizhuan.xchat_android_core.auth.AuthModel
import com.yizhuan.xchat_android_core.initial.InitialModel
import com.yizhuan.xchat_android_core.manager.AvRoomDataManager
import com.yizhuan.xchat_android_core.pay.PayModel
import com.yizhuan.xchat_android_core.redpackage.RedPackageModel
import com.yizhuan.xchat_android_core.user.UserModel
import com.yizhuan.xchat_android_core.utils.toIntOrDef
import com.yizhuan.xchat_android_library.annatation.ActLayoutRes
import com.yizhuan.xchat_android_library.utils.ResUtil
import com.yizhuan.xchat_android_library.utils.SingleToastUtil
import com.yizhuan.xchat_android_library.utils.codec.DESUtils
import okhttp3.internal.toLongOrDefault
/**
* Created by Max on 2023/10/23 12:14
* Desc: 厅内红包
**/
@ActLayoutRes(R.layout.red_package_private_fragment)
class PrivateRedPackageEditorFragment : BaseBindingFragment<RedPackagePrivateFragmentBinding>() {
private var passWordFragment: GiveGoldPassWordFragment? = null
private var typeAdapter: RedPackageTypeItemAdapter? = null
// 生效时间类型 0 立即生效 1 限时生效
private var timeType = 0
override fun initiate() {
mBinding.etText.addDisableFilter(" ", "\n")
initTypeView()
updateTimeView(true)
}
override fun onSetListener() {
super.onSetListener()
mBinding.tvNow.singleClick {
updateTimeView(true)
}
mBinding.tvDelay.singleClick {
updateTimeView(false)
}
mBinding.tvSend.singleClick {
checkSend()
}
}
private fun initTypeView() {
mBinding.recyclerView.addItemDecoration(
ColorDecoration(
Color.TRANSPARENT, UIUtil.dip2px(context, 6.0), 0, false
)
)
val list = ArrayList<RedPackageTypeItemAdapter.ItemData>()
list.add(
RedPackageTypeItemAdapter.ItemData(
1,
R.string.red_package_type_unlimited_name,
R.string.red_package_type_unlimited_tips
)
)
list.add(
RedPackageTypeItemAdapter.ItemData(
2,
R.string.red_package_type_follow_name,
R.string.red_package_type_follow_tips
)
)
list.add(
RedPackageTypeItemAdapter.ItemData(
3,
R.string.red_package_type_share_name,
R.string.red_package_type_share_tips
)
)
list.add(
RedPackageTypeItemAdapter.ItemData(
4,
R.string.red_package_type_msg_name,
R.string.red_package_type_msg_tips
)
)
val adapter = RedPackageTypeItemAdapter(list)
typeAdapter = adapter
adapter.setOnItemClickListener { _, view, position ->
adapter.select(position)
updateTypeView(adapter.getSelect()?.type)
}
mBinding.recyclerView.adapter = adapter
// 默认选择第一个
adapter.select(0)
updateTypeView(adapter.getSelect()?.type)
}
/**
* 更新红包类型对应的视图
*/
private fun updateTypeView(type: Int?) {
when (type) {
1 -> {
mBinding.tvNow.isVisible = true
mBinding.tvDelay.isVisible = true
mBinding.etText.isVisible = false
}
4 -> {
mBinding.tvNow.isVisible = false
mBinding.tvDelay.isVisible = false
mBinding.etText.isVisible = true
}
else -> {
mBinding.tvNow.isVisible = false
mBinding.tvDelay.isVisible = false
mBinding.etText.isVisible = false
}
}
}
/**
* 更新时间选项
*/
private fun updateTimeView(nowOrDelay: Boolean) {
if (nowOrDelay) {
timeType = 0
mBinding.tvNow.setBackgroundResource(R.drawable.red_package_bg_type_selected)
mBinding.tvDelay.setBackgroundResource(R.drawable.shape_f8f8fa_8)
mBinding.tvNow.setTextColor(mBinding.root.context.getColorById(R.color.color_FF285C))
mBinding.tvDelay.setTextColor(mBinding.root.context.getColorById(R.color.color_767585))
} else {
timeType = 1
mBinding.tvNow.setBackgroundResource(R.drawable.shape_f8f8fa_8)
mBinding.tvDelay.setBackgroundResource(R.drawable.red_package_bg_type_selected)
mBinding.tvNow.setTextColor(mBinding.root.context.getColorById(R.color.color_767585))
mBinding.tvDelay.setTextColor(mBinding.root.context.getColorById(R.color.color_FF285C))
}
}
private fun checkSend() {
val initInfo = InitialModel.get().cacheInitInfo
if (initInfo == null) {
SingleToastUtil.showToast(ResUtil.getString(R.string.avroom_redpackage_redpackagesenddialog_01))
return
}
val typeItem = typeAdapter?.getSelect()
if (typeItem?.type == 4) {
if (mBinding.etText.text.toString().trim().isEmpty()) {
toast(R.string.red_package_msg_empty_tips)
return
}
}
val minNum = initInfo.redEnvelopeConfig.roomRedEnvelopeMinNum
val maxNum = initInfo.redEnvelopeConfig.roomRedEnvelopeMaxNum
val minGold = initInfo.redEnvelopeConfig.roomRedEnvelopeMinAmount
val maxGold = initInfo.redEnvelopeConfig.roomRedEnvelopeMaxAmount
val rate =
if (initInfo.redEnvelopeConfig.exchangeDiamondsRate == 0.0) 0.68 else initInfo.redEnvelopeConfig.exchangeDiamondsRate
val redNum = mBinding?.etNum?.text.toString().toIntOrDef()
if (redNum < minNum || redNum > maxNum) {
SingleToastUtil.showToast("紅包數量不能小於${minNum}或大於${maxNum}!")
return
}
val goldNum = mBinding?.etMoney?.text.toString().toIntOrDef()
if (goldNum % 10 != 0) {
SingleToastUtil.showToast("鉆石數必須為10的倍數!")
return
}
if (goldNum < minGold || goldNum > maxGold) {
SingleToastUtil.showToast("鉆石數量不能小於${minGold}或大於${maxGold}!")
return
}
if (goldNum.toFloat() / redNum * rate < 0.1) {//單個手氣紅包價值不低於0.1水晶
SingleToastUtil.showToast("單個紅包金額過低")
return
}
UserModel.get().cacheLoginUserInfo?.let {
if (!it.isBindPaymentPwd) {
ModifyPwdActivity.start(context, ModifyPwdActivity.PAY_PWD)
return
}
}
GiveGoldPassWordFragment.newInstance(
childFragmentManager,
mBinding?.etMoney?.text.toString()
).apply {
setListener(object : GridPasswordNoFocusView.OnPasswordChangedListener {
override fun onTextChanged(psw: String?) {
val password = passWordFragment?.password?.password ?: ""
if (password.length == 6) {
send(password)
}
}
override fun onInputFinish(psw: String?) {
}
})
passWordFragment = this
}
}
/**
* 密码输入完成
*/
private fun send(password: String) {
dialogManager.showProgressDialog(context)
val kind = typeAdapter?.getSelect()?.type ?: 1
val message = if (kind == 4) {
mBinding.etText.text.trim().toString()
} else {
null
}
val validityType = if (kind == 1) {
timeType
} else {
// 非无门槛红包 默认限时
1
}
RedPackageModel.sendRedPackage(
goldNum = mBinding.etMoney.text.trim().toString().toLongOrDefault(0),
message = message,
num = mBinding.etNum.text.trim().toString().toLongOrDefault(0),
roomUId = AvRoomDataManager.get().mCurrentRoomInfo?.uid.toString(),
type = 1,
kind = kind,
validityType = validityType,
password = DESUtils.DESAndBase64(password)
)
.compose(bindToLifecycle())
.doOnError {
dialogManager.dismissDialog()
SingleToastUtil.showToast(it.message)
passWordFragment?.password?.clearPassword()
}
.subscribe { _ ->
PayModel.get().getWalletInfo(AuthModel.get().currentUid).subscribe()
dialogManager.dismissDialog()
SingleToastUtil.showToast("發送成功")
passWordFragment?.dismissAllowingStateLoss()
(parentFragment as? RedPackageSendDialog2)?.dismissAllowingStateLoss()
}
}
override fun onDestroy() {
super.onDestroy()
passWordFragment?.dismissAllowingStateLoss()
passWordFragment = null
}
}

View File

@@ -0,0 +1,139 @@
package com.yizhuan.erban.avroom.redpackage.send
import com.chuhai.utils.ktx.addDisableFilter
import com.chuhai.utils.ktx.setOnInputChangedListener
import com.chuhai.utils.ktx.singleClick
import com.chuhai.utils.ktx.toStringRes
import com.yizhuan.erban.R
import com.yizhuan.erban.base.BaseBindingFragment
import com.yizhuan.erban.databinding.RedPackagePublicFragmentBinding
import com.yizhuan.erban.pay.password.GiveGoldPassWordFragment
import com.yizhuan.erban.pay.widget.GridPasswordNoFocusView
import com.yizhuan.erban.ui.setting.ModifyPwdActivity
import com.yizhuan.xchat_android_core.auth.AuthModel
import com.yizhuan.xchat_android_core.initial.InitialModel
import com.yizhuan.xchat_android_core.initial.bean.InitInfo
import com.yizhuan.xchat_android_core.manager.AvRoomDataManager
import com.yizhuan.xchat_android_core.pay.PayModel
import com.yizhuan.xchat_android_core.redpackage.RedPackageModel
import com.yizhuan.xchat_android_core.user.UserModel
import com.yizhuan.xchat_android_core.utils.toIntOrDef
import com.yizhuan.xchat_android_library.annatation.ActLayoutRes
import com.yizhuan.xchat_android_library.utils.SingleToastUtil
import com.yizhuan.xchat_android_library.utils.codec.DESUtils
import okhttp3.internal.toLongOrDefault
/**
* Created by Max on 2023/10/23 12:14
* Desc:全服红包
**/
@ActLayoutRes(R.layout.red_package_public_fragment)
class PublicRedPackageEditorFragment : BaseBindingFragment<RedPackagePublicFragmentBinding>() {
private var passWordFragment: GiveGoldPassWordFragment? = null
override fun initiate() {
mBinding.etText.addDisableFilter("\n")
mBinding.etText.setOnInputChangedListener {
mBinding.tvTextLength.text =
R.string.red_package_result_count_format.toStringRes().format(this, 20)
true
}
mBinding.tvSend.singleClick {
checkSend()
}
}
private fun checkSend() {
val initInfo = InitialModel.get().cacheInitInfo ?: InitInfo()
val minNum = initInfo.redEnvelopeConfig.serverRedEnvelopeMinNum
val maxNum = initInfo.redEnvelopeConfig.serverRedEnvelopeMaxNum
val minGold = initInfo.redEnvelopeConfig.serverRedEnvelopeMinAmount
val maxGold = initInfo.redEnvelopeConfig.serverRedEnvelopeMaxAmount
val rate =
if (initInfo.redEnvelopeConfig.exchangeDiamondsRate == 0.0) 0.68 else initInfo.redEnvelopeConfig.exchangeDiamondsRate
val redNum = mBinding?.etNum?.text.toString().toIntOrDef()
if (redNum < minNum || redNum > maxNum) {
SingleToastUtil.showToast("紅包數量不能小於${minNum}或大於${maxNum}!")
return
}
val goldNum = mBinding?.etMoney?.text.toString().toIntOrDef()
if (goldNum % 100 != 0) {
SingleToastUtil.showToast("鉆石數必須為100的倍數!")
return
}
if (goldNum < minGold || goldNum > maxGold) {
SingleToastUtil.showToast("鉆石數量不能小於${minGold}或大於${maxGold}!")
return
}
if (goldNum.toFloat() / redNum * rate < 0.1) {//單個手氣紅包價值不低於0.1水晶
SingleToastUtil.showToast("單個紅包金額過低")
return
}
UserModel.get().cacheLoginUserInfo?.let {
if (!it.isBindPaymentPwd) {
ModifyPwdActivity.start(context, ModifyPwdActivity.PAY_PWD)
return
}
}
GiveGoldPassWordFragment.newInstance(
childFragmentManager,
mBinding?.etMoney?.text.toString()
).apply {
setListener(object : GridPasswordNoFocusView.OnPasswordChangedListener {
override fun onTextChanged(psw: String?) {
val password = passWordFragment?.password?.password ?: ""
if (password.length == 6) {
send(password)
}
}
override fun onInputFinish(psw: String?) {
}
})
passWordFragment = this
}
}
/**
* 密码输入完成
*/
private fun send(password: String) {
dialogManager.showProgressDialog(context)
var message = mBinding.etText.text.trim().toString()
if (message.isEmpty()) {
message = R.string.red_package_msg_def.toStringRes()
}
RedPackageModel.sendRedPackage(
goldNum = mBinding.etMoney.text.trim().toString().toLongOrDefault(0),
message = message,
num = mBinding.etNum.text.trim().toString().toLongOrDefault(0),
roomUId = AvRoomDataManager.get().mCurrentRoomInfo?.uid.toString(),
type = 2,
kind = 0,
validityType = 0,
password = DESUtils.DESAndBase64(password)
)
.compose(bindToLifecycle())
.doOnError {
dialogManager.dismissDialog()
SingleToastUtil.showToast(it.message)
passWordFragment?.password?.clearPassword()
}
.subscribe { _ ->
PayModel.get().getWalletInfo(AuthModel.get().currentUid).subscribe()
dialogManager.dismissDialog()
SingleToastUtil.showToast("發送成功")
passWordFragment?.dismissAllowingStateLoss()
(parentFragment as? RedPackageSendDialog2)?.dismissAllowingStateLoss()
}
}
override fun onDestroy() {
super.onDestroy()
passWordFragment?.dismissAllowingStateLoss()
passWordFragment = null
}
}

View File

@@ -0,0 +1,127 @@
package com.yizhuan.erban.avroom.redpackage.send
import android.annotation.SuppressLint
import android.view.Gravity
import android.view.WindowManager
import com.chuhai.utils.ktx.singleClick
import com.chuhai.utils.ktx.toStringRes
import com.yizhuan.erban.R
import com.yizhuan.erban.avroom.redpackage.RedPackageEvent
import com.yizhuan.erban.base.BaseDialog
import com.yizhuan.erban.common.ViewPagerAdapter
import com.yizhuan.erban.databinding.RedPackageSendDialogBinding
import com.yizhuan.erban.ui.pay.ChargeActivity
import com.yizhuan.erban.ui.webview.CommonWebViewActivity
import com.yizhuan.erban.ui.webview.DialogWebViewActivity
import com.yizhuan.erban.ui.widget.magicindicator.ViewPagerHelper
import com.yizhuan.erban.ui.widget.magicindicator.buildins.commonnavigator.CommonNavigator
import com.yizhuan.xchat_android_core.Constants
import com.yizhuan.xchat_android_core.UriProvider
import com.yizhuan.xchat_android_core.initial.InitialModel
import com.yizhuan.xchat_android_core.pay.PayModel
import com.yizhuan.xchat_android_core.pay.event.UpdateWalletInfoEvent
import com.yizhuan.xchat_android_core.redpackage.*
import com.yizhuan.xchat_android_library.annatation.ActLayoutRes
import com.yizhuan.xchat_android_library.common.util.DeviceUtil
import com.yizhuan.xchat_android_library.utils.AppMetaDataUtil
import com.yizhuan.xchat_android_library.utils.ResUtil
import com.yizhuan.xchat_android_library.utils.SingleToastUtil
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
/**
* Created by Max on 2023/10/23 12:14
* Desc:发红包
**/
@ActLayoutRes(R.layout.red_package_send_dialog)
class RedPackageSendDialog2 : BaseDialog<RedPackageSendDialogBinding>() {
override fun onStart() {
gravity = Gravity.BOTTOM
width = WindowManager.LayoutParams.MATCH_PARENT
height = WindowManager.LayoutParams.WRAP_CONTENT
super.onStart()
}
@SuppressLint("SetTextI18n")
override fun init() {
EventBus.getDefault().register(this)
if (InitialModel.get().cacheInitInfo == null) {
InitialModel.get().init(true)
SingleToastUtil.showToast(ResUtil.getString(R.string.avroom_redpackage_redpackagesenddialog_01))
dismissAllowingStateLoss()
return
}
initView()
initEvent()
PayModel.get().currentWalletInfo?.let {
binding.tvBalance.text = it.diamondNum.toLong().toString()
}
}
private fun initView() {
val tabTitles = arrayListOf(
R.string.red_package_room.toStringRes(),
R.string.red_package_public.toStringRes()
)
val topMagicIndicatorAdapter = TabIndicatorAdapter(context, tabTitles)
topMagicIndicatorAdapter.setOnItemSelectListener {
binding.viewPager.currentItem = it
}
val commonNavigator = CommonNavigator(context)
commonNavigator.isAdjustMode = true
commonNavigator.adapter = topMagicIndicatorAdapter
binding.tabLayout.navigator = commonNavigator
val fragments =
arrayListOf(PrivateRedPackageEditorFragment(), PublicRedPackageEditorFragment())
binding.viewPager.adapter = ViewPagerAdapter(
childFragmentManager,
fragments.toList(),
null
)
ViewPagerHelper.bind(binding.tabLayout, binding.viewPager)
}
private fun initEvent() {
binding.ivHelp.singleClick {
DialogWebViewActivity.start(
context,
UriProvider.getRedPacketRule()
)
}
binding.tvBalance.singleClick {
if (AppMetaDataUtil.getChannelID() == Constants.GOOGLE) {
ChargeActivity.start(context)
} else {
CommonWebViewActivity.start(
context, UriProvider.getOfficialPay(
4,
DeviceUtil.getDeviceId(context)
)
)
}
}
}
override fun onDestroy() {
super.onDestroy()
EventBus.getDefault().unregister(this)
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onWalletInfoUpdate(event: UpdateWalletInfoEvent?) {
if (!isViewLoaded) {
return
}
binding.tvBalance.text = PayModel.get().currentWalletInfo?.diamondNum?.toString()
?: "0"
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun handleRedPackageDialog(event: RedPackageEvent?) {
dismissAllowingStateLoss()
}
}

View File

@@ -0,0 +1,76 @@
package com.yizhuan.erban.avroom.redpackage.send
import com.chad.library.adapter.base.BaseQuickAdapter
import com.chad.library.adapter.base.BaseViewHolder
import com.chuhai.utils.ktx.getColorById
import com.yizhuan.erban.R
import io.realm.internal.Keep
/**
* Created by Max on 2023/10/23 18:02
* Desc:
**/
class RedPackageTypeItemAdapter(list: List<ItemData>) :
BaseQuickAdapter<RedPackageTypeItemAdapter.ItemData, BaseViewHolder>(
R.layout.red_package_private_item_type,
list
) {
private var selectPosition = -1
@Keep
data class ItemData(
/**
* 红包种类 0 旧版本 1 无门槛红包 2 关注红包 3 分享红包 4 弹幕红包
*/
val type: Int, val name: Int, val tips: Int
)
override fun convert(helper: BaseViewHolder, item: ItemData) {
helper.setText(R.id.tv_name, item.name)
helper.setText(R.id.tv_tips, item.tips)
convertState(helper, item)
}
override fun convertPayloads(
helper: BaseViewHolder,
item: ItemData,
payloads: MutableList<Any>
) {
super.convertPayloads(helper, item, payloads)
convertState(helper, item)
}
private fun convertState(helper: BaseViewHolder, item: ItemData) {
if (helper.bindingAdapterPosition == selectPosition) {
helper.setBackgroundRes(R.id.layout_root, R.drawable.red_package_bg_type_selected)
helper.setTextColor(
R.id.tv_name,
helper.itemView.context.getColorById(R.color.color_FF285C)
)
helper.setTextColor(
R.id.tv_tips,
helper.itemView.context.getColorById(R.color.color_FF285C)
)
} else {
helper.setBackgroundRes(R.id.layout_root, R.drawable.shape_f8f8fa_8)
helper.setTextColor(
R.id.tv_name,
helper.itemView.context.getColorById(R.color.color_767585)
)
helper.setTextColor(
R.id.tv_tips,
helper.itemView.context.getColorById(R.color.color_94959C)
)
}
}
fun select(position: Int) {
this.selectPosition = position
notifyItemRangeChanged(0, itemCount, true)
}
fun getSelect(): ItemData? {
return data.getOrNull(selectPosition)
}
}

View File

@@ -0,0 +1,116 @@
package com.yizhuan.erban.avroom.redpackage.send;
import android.content.Context;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import androidx.appcompat.widget.AppCompatTextView;
import com.chuhai.utils.UiUtils;
import com.yizhuan.erban.R;
import com.yizhuan.erban.common.util.Utils;
import com.yizhuan.erban.ui.widget.magicindicator.buildins.UIUtil;
import com.yizhuan.erban.ui.widget.magicindicator.buildins.commonnavigator.abs.CommonNavigatorAdapter;
import com.yizhuan.erban.ui.widget.magicindicator.buildins.commonnavigator.abs.IPagerIndicator;
import com.yizhuan.erban.ui.widget.magicindicator.buildins.commonnavigator.abs.IPagerTitleView;
import com.yizhuan.erban.ui.widget.magicindicator.buildins.commonnavigator.indicators.LinePagerIndicator;
import java.util.List;
/**
* @author jack
* @Description
* @Date 2018/11/1
*/
public class TabIndicatorAdapter extends CommonNavigatorAdapter {
private List<String> mTitleList;
private Context mContext;
public TabIndicatorAdapter(Context mContext, List<String> mTitleList ) {
this.mTitleList = mTitleList;
this.mContext = mContext;
}
@Override
public int getCount() {
return mTitleList == null ? 0 : mTitleList.size();
}
@Override
public IPagerTitleView getTitleView(Context context, int index) {
ContactsPagerTitleView categoryPagerTitleView = new ContactsPagerTitleView(context, mTitleList.get(index));
categoryPagerTitleView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (null != mOnItemSelectListener) {
mOnItemSelectListener.onItemSelect(index);
}
}
});
return categoryPagerTitleView;
}
@Override
public IPagerIndicator getIndicator(Context context) {
LinePagerIndicator indicator = new LinePagerIndicator(context);
indicator.setMode(LinePagerIndicator.MODE_EXACTLY);
indicator.setLineHeight(UIUtil.dip2px(mContext, 4));
indicator.setRoundRadius(UIUtil.dip2px(mContext, 4));
indicator.setLineWidth(UIUtil.dip2px(mContext, 12));
indicator.setColors(context.getResources().getColor(R.color.color_FF285C));
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
lp.topMargin = UIUtil.dip2px(mContext, 2);
indicator.setLayoutParams(lp);
return indicator;
}
private OnItemSelectListener mOnItemSelectListener;
public void setOnItemSelectListener(OnItemSelectListener onItemSelectListener) {
mOnItemSelectListener = onItemSelectListener;
}
public interface OnItemSelectListener {
void onItemSelect(int position);
}
class ContactsPagerTitleView extends AppCompatTextView implements IPagerTitleView {
public ContactsPagerTitleView(Context context, String tabInfo) {
super(context);
setHeight(Utils.dip2px(getContext(), 25));
setTextSize(18);
setText(tabInfo);
setGravity(Gravity.CENTER);
}
@Override
public void onSelected(int index, int totalCount) {
// setBackgroundResource(R.drawable.shape_bg_contact_indicator_item);
setTextColor(getResources().getColor(R.color.color_FF285C));
}
@Override
public void onDeselected(int index, int totalCount) {
// setBackgroundDrawable(null);
setTextColor(getResources().getColor(R.color.color_767585));
}
@Override
public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) {
}
@Override
public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) {
}
}
}

View File

@@ -116,6 +116,9 @@ public class MicroView extends LinearLayout implements View.OnLayoutChangeListen
}
private void subMsg() {
if (isInEditMode()) {
return;
}
subscribe = IMNetEaseManager.get()
.getChatRoomEventObservable().subscribe(
roomEvent -> onReceiveRoomEvent(roomEvent),

View File

@@ -35,7 +35,6 @@ import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.style.StyleSpan;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
@@ -51,6 +50,7 @@ import androidx.fragment.app.FragmentManager;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.chuhai.utils.log.ILog;
import com.google.gson.Gson;
import com.netease.nim.uikit.common.util.log.LogUtil;
import com.netease.nimlib.sdk.NIMSDK;
@@ -70,6 +70,7 @@ import com.yizhuan.erban.avroom.activity.AVRoomActivity;
import com.yizhuan.erban.avroom.firstcharge.FirstChargeDialog;
import com.yizhuan.erban.avroom.redpackage.RedPackageGoRoomDialog;
import com.yizhuan.erban.avroom.redpackage.RedPackageOpenDialog;
import com.yizhuan.erban.avroom.redpackage.open.RedPackageOpenDialog2;
import com.yizhuan.erban.common.LoadingFragment;
import com.yizhuan.erban.common.NetworkErrorFragment;
import com.yizhuan.erban.common.NoDataFragment;
@@ -103,7 +104,6 @@ import com.yizhuan.xchat_android_core.UriProvider;
import com.yizhuan.xchat_android_core.bean.BaseProtocol;
import com.yizhuan.xchat_android_core.gift.bean.LuckyBagNoticeInfo;
import com.yizhuan.xchat_android_core.im.custom.bean.CustomAttachment;
import com.yizhuan.xchat_android_core.im.custom.bean.NotifyH5Attachment;
import com.yizhuan.xchat_android_core.im.custom.bean.NotifyH5Info;
import com.yizhuan.xchat_android_core.im.custom.bean.PlayEffectInfo;
import com.yizhuan.xchat_android_core.im.custom.bean.RoomBoxPrizeInfo;
@@ -959,7 +959,7 @@ public abstract class BaseActivity extends RxAppCompatActivity
RedPackageNotifyInfo notifyInfo = new Gson().fromJson(String.valueOf(baseProtocol.getData()), RedPackageNotifyInfo.class);
RoomInfo roomInfo = AvRoomDataManager.get().mCurrentRoomInfo;
if (roomInfo != null && roomInfo.getUid() == notifyInfo.getRoomUid() && context instanceof AVRoomActivity) {
RedPackageOpenDialog.Companion.newInstance(notifyInfo).show(BaseActivity.this);
RedPackageOpenDialog2.Companion.newInstance(notifyInfo).show(BaseActivity.this);
} else {
RedPackageGoRoomDialog.Companion.newInstance(notifyInfo).show(this);
}

View File

@@ -14,6 +14,7 @@ import java.lang.reflect.ParameterizedType
abstract class BaseDialog<T : ViewBinding> : RxDialogFragment() {
val isViewLoaded: Boolean get() = _binding != null
private var _binding: T? = null
private var onDismissListener: (() -> Unit)? = null
val binding get() = _binding!!
@@ -70,6 +71,7 @@ abstract class BaseDialog<T : ViewBinding> : RxDialogFragment() {
override fun onDestroyView() {
super.onDestroyView()
_binding = null
this.onDismissListener = null
}
abstract fun init()

View File

@@ -6,8 +6,10 @@ import android.os.Build
import android.os.Environment
import android.text.SpannableString
import android.view.View
import com.chuhai.utils.log.ILog
import com.netease.nim.uikit.StatusBarUtil
import com.tongdaxing.erban.upgrade.AppUpgradeHelper
import com.yizhuan.erban.BuildConfig
import com.yizhuan.erban.R
import com.yizhuan.erban.UIHelper
import com.yizhuan.erban.base.BaseViewBindingActivity
@@ -40,7 +42,7 @@ import java.io.File
* 设置页
* Created by wushaocheng on 2023/2/1.
*/
class SettingActivity : BaseViewBindingActivity<ActivitySettingBinding>(), View.OnClickListener {
class SettingActivity : BaseViewBindingActivity<ActivitySettingBinding>(), View.OnClickListener,ILog {
override fun init() {
EventBus.getDefault().register(this)
initWhiteTitleBar(ResUtil.getString(R.string.ui_setting_settingactivity_01))
@@ -74,6 +76,12 @@ class SettingActivity : BaseViewBindingActivity<ActivitySettingBinding>(), View.
binding.tvShieldManager.setOnClickListener(this)
binding.rlyPermission.setOnClickListener(this)
binding.rlyCheck.setOnClickListener(this)
if (BuildConfig.DEBUG) {
binding.titleBar.setOnTitleClickListener {
debug()
}
}
}
@SuppressLint("CheckResult")
@@ -103,6 +111,7 @@ class SettingActivity : BaseViewBindingActivity<ActivitySettingBinding>(), View.
BindPhoneActivity.start(this)
}
}
R.id.rly_contact_us -> UIHelper.openContactUs(this)
R.id.rly_help -> UIHelper.showUsinghelp(this)
R.id.rly_update -> startActivity(Intent(applicationContext, AboutActivity::class.java))
@@ -115,9 +124,14 @@ class SettingActivity : BaseViewBindingActivity<ActivitySettingBinding>(), View.
}
ModifyPwdActivity.start(this, ModifyPwdActivity.PAY_PWD)
}
R.id.btn_login_out -> {
val cacheLoginUserInfo = UserModel.get().cacheLoginUserInfo ?: return
if (!cacheLoginUserInfo.isBindPasswd && !SPUtils.getBoolean(SpConstants.SET_PASSWORD, false)) {
if (!cacheLoginUserInfo.isBindPasswd && !SPUtils.getBoolean(
SpConstants.SET_PASSWORD,
false
)
) {
dialogManager.showOkCancelWithTitleDialog(ResUtil.getString(R.string.set_login_password),
SpannableString(ResUtil.getString(R.string.set_login_password_tip)),
ResUtil.getString(R.string.login_fragment_adduserinfofragment_04),
@@ -126,7 +140,10 @@ class SettingActivity : BaseViewBindingActivity<ActivitySettingBinding>(), View.
override fun onCancel() {}
override fun onOk() {
SPUtils.putBoolean(SpConstants.SET_PASSWORD, true)
ModifyPwdActivity.start(this@SettingActivity, ModifyPwdActivity.LOGIN_PWD)
ModifyPwdActivity.start(
this@SettingActivity,
ModifyPwdActivity.LOGIN_PWD
)
}
})
} else {
@@ -134,10 +151,12 @@ class SettingActivity : BaseViewBindingActivity<ActivitySettingBinding>(), View.
LogoutHelper.logout()
}
}
R.id.tv_community_norms -> CommonWebViewActivity.start(
this,
UriProvider.getCommnunityNorms()
)
R.id.rly_clear_cache -> dialogManager.showOkCancelWithTitleDialog(ResUtil.getString(R.string.ui_setting_settingactivity_07),
SpannableString(ResUtil.getString(R.string.ui_setting_settingactivity_08)),
ResUtil.getString(R.string.ui_setting_settingactivity_09),
@@ -148,6 +167,7 @@ class SettingActivity : BaseViewBindingActivity<ActivitySettingBinding>(), View.
clearCache()
}
})
R.id.tv_notice_setting -> NoticeSettingActivity.start(context)
R.id.tv_shield_manager -> ShieldManageActivity.start(context)
R.id.rly_permission -> PermissionGuideActivity.start(context)
@@ -194,4 +214,8 @@ class SettingActivity : BaseViewBindingActivity<ActivitySettingBinding>(), View.
dialogManager.dismissDialog()
EventBus.getDefault().unregister(this)
}
private fun debug() {
// RedPackageOpenDialog2().show(this)
}
}

View File

@@ -0,0 +1,37 @@
package com.yizhuan.erban.view
import android.content.Context
import android.util.AttributeSet
import android.view.View
import androidx.viewpager.widget.ViewPager
/**
* Created by Max on 2022/10/10 15:30
* 高度自适应(采用最大的子页面高度)-ViewPager
*/
class WrapHeightViewPager : ViewPager {
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
var height = 0
for (i in 0 until childCount) {
val child: View = getChildAt(i)
child.measure(
widthMeasureSpec,
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
)
val h = child.measuredHeight
if (h > height) {
height = h
}
}
super.onMeasure(
widthMeasureSpec, MeasureSpec.makeMeasureSpec(
height,
MeasureSpec.EXACTLY
)
)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners
android:bottomLeftRadius="26dp"
android:topLeftRadius="26dp" />
<gradient
android:angle="0"
android:endColor="#FF225C"
android:startColor="#FF6060" />
</shape>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"
android:useLevel="false">
<corners android:radius="6dp" />
<solid android:color="#FFF44646" />
</shape>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="8dp" />
<stroke
android:width="1dp"
android:color="#FF285C" />
<solid android:color="#0FFF285C" />
</shape>

View File

@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportWidth="16"
android:viewportHeight="16">
<path
android:pathData="M8,3C8.491,3 8.889,3.398 8.889,3.889L8.888,7.111L12.111,7.111C12.602,7.111 13,7.509 13,8C13,8.491 12.602,8.889 12.111,8.889L8.888,8.888L8.889,12.111C8.889,12.602 8.491,13 8,13C7.509,13 7.111,12.602 7.111,12.111L7.111,8.888L3.889,8.889C3.398,8.889 3,8.491 3,8C3,7.509 3.398,7.111 3.889,7.111L7.111,7.111L7.111,3.889C7.111,3.398 7.509,3 8,3Z"
android:strokeWidth="0.5"
android:fillColor="#FFFFFF"
android:strokeColor="#FFFFFF"
android:fillType="evenOdd"/>
</vector>

View File

@@ -0,0 +1,22 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="28"
android:viewportHeight="28">
<path
android:pathData="M14,28C6.28,28 0,21.72 0,14C0,6.28 6.28,0 14,0C21.72,0 28,6.28 28,14C28,21.72 21.72,28 14,28ZM14,2.009C7.388,2.009 2.009,7.388 2.009,14C2.009,20.611 7.388,25.991 14,25.991C20.611,25.991 25.991,20.611 25.991,14C25.991,7.388 20.611,2.009 14,2.009Z"
android:strokeAlpha="0.8"
android:strokeWidth="1"
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:strokeColor="#00000000"
android:fillAlpha="0.8"/>
<path
android:pathData="M15.408,14.043L19.732,9.766C20.125,9.378 20.128,8.745 19.74,8.352C19.352,7.959 18.718,7.956 18.326,8.344L13.996,12.626L9.732,8.349C9.341,7.958 8.709,7.956 8.318,8.347C7.927,8.737 7.926,9.37 8.316,9.761L12.574,14.032L8.281,18.279C7.888,18.668 7.885,19.3 8.273,19.693C8.469,19.891 8.726,19.99 8.984,19.99C9.238,19.99 9.492,19.893 9.687,19.701L13.986,15.449L18.32,19.797C18.515,19.993 18.771,20.091 19.028,20.091C19.284,20.091 19.539,19.993 19.734,19.799C20.125,19.409 20.126,18.777 19.736,18.385L15.408,14.043Z"
android:strokeAlpha="0.8"
android:strokeWidth="1"
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:strokeColor="#00000000"
android:fillAlpha="0.8"/>
</vector>

View File

@@ -0,0 +1,22 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12.171,6.342C11.02,6.342 10.118,6.669 9.464,7.338C8.811,7.991 8.484,8.878 8.484,10.013L10.118,10.013C10.118,9.329 10.258,8.8 10.538,8.427C10.849,7.976 11.362,7.758 12.062,7.758C12.622,7.758 13.073,7.913 13.384,8.224C13.68,8.536 13.836,8.956 13.836,9.5C13.836,9.904 13.68,10.278 13.4,10.636L13.136,10.931C12.171,11.787 11.58,12.424 11.378,12.86C11.16,13.264 11.067,13.762 11.067,14.338L11.067,14.602L12.731,14.602L12.731,14.338C12.731,13.964 12.809,13.622 12.98,13.311C13.12,13.031 13.338,12.751 13.633,12.502C14.364,11.864 14.8,11.46 14.94,11.289C15.329,10.791 15.531,10.153 15.531,9.391C15.531,8.458 15.22,7.711 14.598,7.167C13.976,6.607 13.167,6.342 12.171,6.342ZM11.891,15.442C11.58,15.442 11.3,15.536 11.082,15.753C10.864,15.956 10.756,16.22 10.756,16.547C10.756,16.858 10.864,17.122 11.082,17.34C11.3,17.558 11.58,17.667 11.891,17.667C12.202,17.667 12.482,17.558 12.7,17.356C12.918,17.138 13.042,16.873 13.042,16.547C13.042,16.22 12.933,15.956 12.716,15.753C12.498,15.536 12.218,15.442 11.891,15.442Z"
android:strokeAlpha="0.90000004"
android:strokeWidth="1"
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:strokeColor="#00000000"
android:fillAlpha="0.90000004"/>
<path
android:pathData="M12,12m-9.333,0a9.333,9.333 0,1 1,18.667 0a9.333,9.333 0,1 1,-18.667 0"
android:strokeAlpha="0.90000004"
android:strokeWidth="1.33333333"
android:fillColor="#00000000"
android:strokeColor="#FFFFFF"
android:fillType="evenOdd"
android:fillAlpha="0.90000004"/>
</vector>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape android:shape="rectangle"
xmlns:android="http://schemas.android.com/apk/res/android">
<stroke android:width="1dp" android:color="#b3ffffff" />
<solid android:color="#66f52727" />
<corners android:radius="8dp" />
</shape>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="8dp"/>
<solid android:color="#99292929"/>
</shape>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="10dp"/>
<solid android:color="#FFE03654"/>
</shape>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="8dp"/>
<solid android:color="#F8F8FA"/>
</shape>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="26dp" />
<gradient
android:angle="0"
android:endColor="#FF225C"
android:startColor="#FF6060" />
</shape>

View File

@@ -0,0 +1,343 @@
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:background="@color/black">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layout_content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginHorizontal="24dp"
android:background="@drawable/red_package_open_bg"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="h,327:466"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/line_close_top"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.0665" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/line_send_avatar_top"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.2017" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/line_tips_top"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.4184" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/line_btn_top"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5257" />
<ImageView
android:id="@+id/iv_close"
android:layout_width="44dp"
android:layout_height="44dp"
android:scaleType="center"
android:src="@drawable/red_package_ic_close"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/line_close_top" />
<com.yizhuan.erban.common.widget.CircleImageView
android:id="@+id/iv_send_avatar"
android:layout_width="65dp"
android:layout_height="65dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/line_send_avatar_top"
tools:src="@drawable/default_avatar" />
<TextView
android:id="@+id/tv_send_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:ellipsize="end"
android:maxWidth="300dp"
android:maxLines="1"
android:textColor="@color/white"
android:textSize="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/iv_send_avatar"
tools:text="Name" />
<TextView
android:id="@+id/tv_open_tips"
android:layout_width="0dp"
android:layout_height="36dp"
android:background="@drawable/red_package_open_tips_bg"
android:gravity="center"
android:paddingHorizontal="5dp"
android:textColor="@color/white"
android:textSize="11dp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/line_tips_top"
app:layout_constraintWidth_percent="0.7064"
tools:text="恭喜发财!"
tools:visibility="visible" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layout_btn"
android:layout_width="143dp"
android:layout_height="124dp"
android:background="@drawable/red_package_open_btn_bg"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/line_btn_top">
<TextView
android:id="@+id/tv_btn_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:maxLines="1"
android:text="00:00"
android:textColor="@color/color_BF461F"
android:textSize="24dp"
app:layout_constraintBottom_toTopOf="@id/tv_btn_time_tips"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<TextView
android:id="@+id/tv_btn_time_tips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/red_package_open_btn_await"
android:textColor="@color/color_BF461F"
android:textSize="12dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_btn_time" />
<androidx.constraintlayout.widget.Group
android:id="@+id/group_btn_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="tv_btn_time,tv_btn_time_tips"
tools:visibility="visible" />
<TextView
android:id="@+id/tv_btn_msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/red_package_open_btn_msg"
android:textColor="@color/color_BF461F"
android:textSize="14dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
<ImageView
android:id="@+id/iv_btn_get"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/red_package_open_btn_get"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<!--待开启状态视图-->
<androidx.constraintlayout.widget.Group
android:id="@+id/group_open"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="layout_btn,iv_send_avatar,tv_send_name"
tools:visibility="visible" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/line_money_top"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.1909" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/line_empty_tips_top"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.236" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/line_result_send_avatar_top"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.3776" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/line_count_top"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.854" />
<ImageView
android:id="@+id/iv_money"
android:layout_width="34dp"
android:layout_height="34dp"
android:src="@drawable/red_package_send_ic_diamond"
app:layout_constraintEnd_toStartOf="@id/tv_money"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/line_money_top" />
<TextView
android:id="@+id/tv_money"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="2dp"
android:maxLines="1"
android:text="0"
android:textColor="@color/color_FF285C"
android:textSize="32dp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@id/iv_money"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/iv_money"
app:layout_constraintTop_toTopOf="@id/iv_money" />
<TextView
android:id="@+id/tv_money_tips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/red_package_result_money_tips"
android:textColor="@color/color_767585"
android:textSize="12dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_money" />
<TextView
android:id="@+id/tv_result_empty_tips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/red_package_result_empty_tips"
android:textColor="@color/color_767585"
android:textSize="12dp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/line_empty_tips_top"
tools:visibility="visible" />
<com.yizhuan.erban.common.widget.CircleImageView
android:id="@+id/iv_result_send_avatar"
android:layout_width="26dp"
android:layout_height="26dp"
app:cborder_color="@color/white"
app:cborder_width="1dp"
app:layout_constraintEnd_toStartOf="@id/tv_result_send_name"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/line_result_send_avatar_top"
tools:src="@drawable/default_avatar" />
<TextView
android:id="@+id/tv_result_send_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="3dp"
android:maxLines="1"
android:textColor="@color/white"
android:textSize="14dp"
app:layout_constraintBottom_toBottomOf="@id/iv_result_send_avatar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/iv_result_send_avatar"
app:layout_constraintTop_toTopOf="@id/iv_result_send_avatar"
tools:text="Name" />
<TextView
android:id="@+id/tv_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:layout_marginTop="3dp"
android:alpha="90"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/red_package_msg_def"
android:textColor="@color/white"
android:textSize="14dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/iv_result_send_avatar" />
<TextView
android:id="@+id/tv_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:alpha="0.8"
android:text="@string/red_package_result_count_format"
android:textColor="@color/white"
android:textSize="10dp"
app:layout_constraintEnd_toEndOf="@id/recyclerView"
app:layout_constraintTop_toBottomOf="@id/line_count_top" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="5dp"
android:background="@drawable/shape_e03654_10"
android:orientation="vertical"
android:scrollbars="none"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toTopOf="@id/tv_count"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_message"
app:layout_constraintWidth_percent="0.6819"
tools:listitem="@layout/red_package_open_item_result" />
<androidx.constraintlayout.widget.Group
android:id="@+id/group_result_money"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:constraint_referenced_ids="tv_money,tv_money_tips,iv_money"
tools:visibility="visible" />
<androidx.constraintlayout.widget.Group
android:id="@+id/group_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:constraint_referenced_ids="recyclerView,tv_count,tv_message,tv_result_send_name,iv_result_send_avatar"
tools:visibility="gone" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layout_root"
android:layout_width="match_parent"
android:layout_height="48dp"
tools:background="@color/black">
<com.yizhuan.erban.common.widget.CircleImageView
android:id="@+id/iv_avatar"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginStart="15dp"
app:cborder_color="@color/white"
app:cborder_width="1dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/default_avatar" />
<TextView
android:id="@+id/tv_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginEnd="10dp"
android:ellipsize="end"
android:textColor="@color/color_white"
android:textSize="12dp"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@id/tv_time"
app:layout_constraintEnd_toStartOf="@id/iv_money"
app:layout_constraintStart_toEndOf="@id/iv_avatar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="Name" />
<TextView
android:id="@+id/tv_time"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/color_white_60"
android:textSize="9dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/tv_name"
app:layout_constraintStart_toStartOf="@id/tv_name"
app:layout_constraintTop_toBottomOf="@id/tv_name"
tools:text="10月23日 12:00" />
<TextView
android:id="@+id/tv_money"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="15dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/white"
android:textSize="16dp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="0" />
<ImageView
android:id="@+id/iv_money"
android:layout_width="19dp"
android:layout_height="19dp"
android:layout_marginEnd="1dp"
android:src="@drawable/red_package_send_ic_diamond"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/tv_money"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/v_line"
android:layout_width="match_parent"
android:layout_height="2px"
android:layout_marginHorizontal="14dp"
android:background="@color/color_ffffff_30"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -0,0 +1,225 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingBottom="24dp">
<EditText
android:id="@+id/et_money"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginHorizontal="24dp"
android:background="@drawable/shape_f8f8fa_8"
android:gravity="end|center_vertical"
android:hint="0"
android:inputType="number"
android:paddingEnd="59dp"
android:singleLine="true"
android:textColor="@color/color_322F4D"
android:textColorHint="@color/color_B3B3C3"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/iv_money"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="9dp"
android:src="@drawable/red_package_send_ic"
app:layout_constraintBottom_toBottomOf="@id/et_money"
app:layout_constraintStart_toStartOf="@id/et_money"
app:layout_constraintTop_toTopOf="@id/et_money" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="1dp"
android:text="@string/red_package_money"
android:textColor="@color/color_322F4D"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@id/et_money"
app:layout_constraintStart_toEndOf="@id/iv_money"
app:layout_constraintTop_toTopOf="@id/et_money" />
<TextView
android:id="@+id/tv_money_unit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:text="@string/red_package_money_unit"
android:textColor="@color/color_767585"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@id/et_money"
app:layout_constraintEnd_toEndOf="@id/et_money"
app:layout_constraintTop_toTopOf="@id/et_money" />
<View
android:layout_width="1dp"
android:layout_height="26dp"
android:layout_marginEnd="14dp"
android:background="@color/color_EBEEF5"
app:layout_constraintBottom_toBottomOf="@id/et_money"
app:layout_constraintEnd_toStartOf="@id/tv_money_unit"
app:layout_constraintTop_toTopOf="@id/et_money" />
<EditText
android:id="@+id/et_num"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="12dp"
android:background="@drawable/shape_f8f8fa_8"
android:gravity="end|center_vertical"
android:hint="0"
android:inputType="number"
android:paddingEnd="59dp"
android:singleLine="true"
android:textColor="@color/color_322F4D"
android:textColorHint="@color/color_B3B3C3"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/et_money" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="@string/red_package_num"
android:textColor="@color/color_322F4D"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@id/et_num"
app:layout_constraintStart_toStartOf="@id/et_num"
app:layout_constraintTop_toTopOf="@id/et_num" />
<TextView
android:id="@+id/tv_num_unit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:text="@string/red_package_num_unit"
android:textColor="@color/color_767585"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@id/et_num"
app:layout_constraintEnd_toEndOf="@id/et_num"
app:layout_constraintTop_toTopOf="@id/et_num" />
<View
android:layout_width="1dp"
android:layout_height="26dp"
android:layout_marginEnd="14dp"
android:background="@color/color_EBEEF5"
app:layout_constraintBottom_toBottomOf="@id/et_num"
app:layout_constraintEnd_toStartOf="@id/tv_num_unit"
app:layout_constraintTop_toTopOf="@id/et_num" />
<TextView
android:id="@+id/tv_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="16dp"
android:text="@string/red_package_type"
android:textColor="@color/color_322F4D"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/et_num" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="62dp"
android:layout_marginStart="24dp"
android:layout_marginTop="8dp"
android:clipToPadding="false"
android:orientation="horizontal"
android:paddingEnd="6dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintTop_toBottomOf="@id/tv_type" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layout_parameter"
android:layout_width="match_parent"
android:layout_height="52dp"
android:layout_marginTop="12dp"
app:layout_constraintTop_toBottomOf="@id/recyclerView">
<TextView
android:id="@+id/tv_now"
android:layout_width="0dp"
android:layout_height="52dp"
android:layout_marginStart="24dp"
android:background="@drawable/shape_f8f8fa_8"
android:gravity="center"
android:text="@string/red_package_now"
android:textColor="@color/color_FF285C"
android:textSize="16sp"
app:layout_constraintEnd_toStartOf="@id/tv_delay"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_delay"
android:layout_width="0dp"
android:layout_height="52dp"
android:layout_marginStart="19dp"
android:layout_marginEnd="24dp"
android:background="@drawable/shape_f8f8fa_8"
android:gravity="center"
android:text="@string/red_package_delay"
android:textColor="@color/color_FF285C"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/tv_now"
app:layout_constraintTop_toTopOf="@id/tv_now" />
<EditText
android:id="@+id/et_text"
android:layout_width="match_parent"
android:layout_height="52dp"
android:layout_marginHorizontal="24dp"
android:background="@drawable/shape_f8f8fa_8"
android:gravity="center"
android:hint="@string/red_package_msg_hint"
android:maxLength="10"
android:singleLine="true"
android:textColor="@color/color_322F4D"
android:textColorHint="@color/color_B3B3C3"
android:textSize="16sp"
android:visibility="gone"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/tv_send"
android:layout_width="match_parent"
android:layout_height="52dp"
android:layout_marginHorizontal="40dp"
android:layout_marginTop="28dp"
android:background="@drawable/shape_g_ff6060_ff225c_26_lr"
android:gravity="center"
android:text="@string/red_package_send"
android:textColor="@color/color_FFFFFF"
android:textSize="18sp"
app:layout_constraintTop_toBottomOf="@id/layout_parameter" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/red_package_tips"
android:textColor="@color/color_94959C"
android:textSize="12sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_send" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layout_root"
android:background="@drawable/shape_f8f8fa_8"
android:layout_width="94dp"
android:layout_height="62dp">
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="@color/color_767585"
android:textSize="12dp"
app:layout_constraintBottom_toTopOf="@id/tv_tips"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="Name" />
<TextView
android:id="@+id/tv_tips"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="@color/color_94959C"
android:textSize="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_name"
tools:text="Tips" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -0,0 +1,175 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingBottom="24dp">
<EditText
android:id="@+id/et_money"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginHorizontal="24dp"
android:background="@drawable/shape_f8f8fa_8"
android:gravity="end|center_vertical"
android:hint="0"
android:inputType="number"
android:paddingEnd="59dp"
android:singleLine="true"
android:textColor="@color/color_322F4D"
android:textColorHint="@color/color_B3B3C3"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/iv_money"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="9dp"
android:src="@drawable/red_package_send_ic"
app:layout_constraintBottom_toBottomOf="@id/et_money"
app:layout_constraintStart_toStartOf="@id/et_money"
app:layout_constraintTop_toTopOf="@id/et_money" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="1dp"
android:text="@string/red_package_money"
android:textColor="@color/color_322F4D"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@id/et_money"
app:layout_constraintStart_toEndOf="@id/iv_money"
app:layout_constraintTop_toTopOf="@id/et_money" />
<TextView
android:id="@+id/tv_money_unit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:text="@string/red_package_money_unit"
android:textColor="@color/color_767585"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@id/et_money"
app:layout_constraintEnd_toEndOf="@id/et_money"
app:layout_constraintTop_toTopOf="@id/et_money" />
<View
android:layout_width="1dp"
android:layout_height="26dp"
android:layout_marginEnd="14dp"
android:background="@color/color_EBEEF5"
app:layout_constraintBottom_toBottomOf="@id/et_money"
app:layout_constraintEnd_toStartOf="@id/tv_money_unit"
app:layout_constraintTop_toTopOf="@id/et_money" />
<EditText
android:id="@+id/et_num"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="12dp"
android:background="@drawable/shape_f8f8fa_8"
android:gravity="end|center_vertical"
android:hint="0"
android:inputType="number"
android:paddingEnd="59dp"
android:singleLine="true"
android:textColor="@color/color_322F4D"
android:textColorHint="@color/color_B3B3C3"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/et_money" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="@string/red_package_num"
android:textColor="@color/color_322F4D"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@id/et_num"
app:layout_constraintStart_toStartOf="@id/et_num"
app:layout_constraintTop_toTopOf="@id/et_num" />
<TextView
android:id="@+id/tv_num_unit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:text="@string/red_package_num_unit"
android:textColor="@color/color_767585"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@id/et_num"
app:layout_constraintEnd_toEndOf="@id/et_num"
app:layout_constraintTop_toTopOf="@id/et_num" />
<View
android:layout_width="1dp"
android:layout_height="26dp"
android:layout_marginEnd="14dp"
android:background="@color/color_EBEEF5"
app:layout_constraintBottom_toBottomOf="@id/et_num"
app:layout_constraintEnd_toStartOf="@id/tv_num_unit"
app:layout_constraintTop_toTopOf="@id/et_num" />
<EditText
android:id="@+id/et_text"
android:layout_width="match_parent"
android:layout_height="76dp"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="24dp"
android:background="@drawable/shape_f8f8fa_8"
android:gravity="center"
android:maxLength="20"
android:paddingHorizontal="10dp"
android:paddingVertical="10dp"
android:hint="@string/red_package_msg_def"
android:textColorHint="@color/color_B3B3C3"
android:textColor="@color/color_322F4D"
android:textSize="16sp"
app:layout_constraintTop_toBottomOf="@id/et_num" />
<TextView
android:id="@+id/tv_text_length"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:layout_marginBottom="8dp"
android:text="0/20"
android:textColor="@color/color_94959C"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="@id/et_text"
app:layout_constraintEnd_toEndOf="@id/et_text" />
<TextView
android:id="@+id/tv_send"
android:layout_width="match_parent"
android:layout_height="52dp"
android:layout_marginHorizontal="40dp"
android:layout_marginTop="28dp"
android:gravity="center"
android:background="@drawable/shape_g_ff6060_ff225c_26_lr"
android:text="@string/red_package_send"
android:textColor="@color/color_FFFFFF"
android:textSize="18sp"
app:layout_constraintTop_toBottomOf="@id/et_text" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/red_package_tips"
android:textColor="@color/color_94959C"
android:textSize="12sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_send" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -0,0 +1,88 @@
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<View
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="@color/white"
app:layout_constraintBottom_toBottomOf="@id/viewPager"
app:layout_constraintTop_toTopOf="@id/viewPager" />
<ImageView
android:id="@+id/iv_top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:src="@drawable/red_package_send_bg"
app:layout_constraintTop_toTopOf="parent" />
<com.yizhuan.erban.ui.widget.magicindicator.MagicIndicator
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_marginHorizontal="20dp"
android:layout_marginTop="170dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/iv_top" />
<ImageView
android:id="@+id/iv_help"
android:layout_width="44dp"
android:layout_height="44dp"
android:layout_marginTop="64dp"
android:layout_marginEnd="2dp"
android:scaleType="center"
android:src="@drawable/red_package_ic_help"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/iv_top" />
<TextView
android:id="@+id/tv_balance"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_marginBottom="8dp"
android:background="@drawable/red_package_bg_balance"
android:gravity="center"
android:paddingStart="25dp"
android:paddingEnd="23dp"
android:text="0"
android:textColor="@color/white"
android:textSize="13sp"
app:layout_constraintBottom_toTopOf="@id/tab_layout"
app:layout_constraintEnd_toEndOf="parent" />
<ImageView
android:layout_width="21dp"
android:layout_height="wrap_content"
android:layout_marginEnd="2dp"
android:scaleType="center"
android:src="@drawable/red_package_ic_add"
app:layout_constraintBottom_toBottomOf="@id/tv_balance"
app:layout_constraintEnd_toEndOf="@id/tv_balance"
app:layout_constraintTop_toTopOf="@id/tv_balance" />
<ImageView
android:layout_width="20dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:scaleType="center"
android:src="@drawable/red_package_send_ic_diamond"
app:layout_constraintBottom_toBottomOf="@id/tv_balance"
app:layout_constraintStart_toStartOf="@id/tv_balance"
app:layout_constraintTop_toTopOf="@id/tv_balance" />
<com.yizhuan.erban.view.WrapHeightViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/tab_layout" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<merge 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="match_parent"
android:background="@drawable/red_package_widget_bg"
tools:layout_height="58dp"
tools:layout_width="58dp"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
<TextView
android:id="@+id/tv_text"
android:layout_width="36dp"
android:layout_height="15dp"
android:layout_marginBottom="10dp"
android:background="@drawable/red_package_widget_bg_text"
android:gravity="center"
android:includeFontPadding="false"
android:text="@string/red_package_widget_get"
android:textColor="@color/white"
android:textSize="9dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/tv_num"
android:layout_width="wrap_content"
android:layout_height="12dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="4dp"
android:background="@drawable/red_package_bg_num"
android:gravity="center"
android:includeFontPadding="false"
android:minWidth="12dp"
android:paddingHorizontal="3dp"
android:text="1"
android:textColor="@color/white"
android:textSize="9dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</merge>

View File

@@ -703,5 +703,11 @@
<color name="color_1E686868">#1E686868</color>
<color name="color_9E9EA8">#9E9EA8</color>
<color name="color_001338">#001338</color>
<color name="color_FF285C">#FF285C</color>
<color name="color_322F4D">#322F4D</color>
<color name="color_EBEEF5">#EBEEF5</color>
<color name="color_94959C">#94959C</color>
<color name="color_BF461F">#FFBF461F</color>
<color name="color_FFF87A">#FFFFF87A</color>
<color name="color_ffffff_30">#4CFFFFFF</color>
</resources>

View File

@@ -5143,4 +5143,44 @@
<string name="room_album_type_no_need_unlocked_gift_tips">當前無需選擇解鎖禮物</string>
<string name="room_album_type_choose_unlocked_gift_tips">選擇解鎖禮物</string>
<string name="red_package_type_unlimited_name">無門檻紅包</string>
<string name="red_package_type_unlimited_tips">所有人均可參與</string>
<string name="red_package_type_follow_name">關註紅包</string>
<string name="red_package_type_follow_tips">關註主播可參與</string>
<string name="red_package_type_share_name">分享紅包</string>
<string name="red_package_type_share_tips">分享房間可參與</string>
<string name="red_package_type_msg_name">發彈幕紅包</string>
<string name="red_package_type_msg_tips">發指定彈幕內容</string>
<string name="red_package_room">廳內紅包</string>
<string name="red_package_public">全服紅包</string>
<string name="red_package_money">紅包金額</string>
<string name="red_package_num">紅包數量</string>
<string name="red_package_money_unit"></string>
<string name="red_package_num_unit"></string>
<string name="red_package_type">搶紅包條件</string>
<string name="red_package_msg_hint">輸入指定彈幕內容最多10個字符</string>
<string name="red_package_send">發紅包</string>
<string name="red_package_tips">未搶完的紅包將在12小時後返還你的錢包</string>
<string name="red_package_now">立即生效</string>
<string name="red_package_delay">限時生效</string>
<string name="red_package_msg_def">恭喜發財,大吉大利!</string>
<string name="red_package_open_btn_await">後開搶</string>
<string name="red_package_open_btn_msg">發彈幕\n搶紅包</string>
<string name="red_package_open_btn_share">分享房間\n搶紅包</string>
<string name="red_package_open_btn_follow">關注主播\n搶紅包</string>
<string name="red_package_open_btn_no">未參與活動</string>
<string name="red_package_open_no_tips">未參與活動,無法搶該紅包</string>
<string name="red_package_result_money_tips">已存入錢包,請到我的收益確認</string>
<string name="red_package_result_count_format">已領取%s/%s個</string>
<string name="red_package_widget_get">搶紅包</string>
<string name="red_package_msg_empty_tips">請輸入彈幕內容</string>
<string name="red_package_open_tips_follow">關注主播</string>
<string name="red_package_open_tips_share">对外分享房間</string>
<string name="red_package_open_tips_msg1">發送彈幕</string>
<string name="red_package_open_tips_msg_end">可搶紅包</string>
<string name="red_package_result_empty_tips">搶完了,下次早點來哦~</string>
<string name="red_package_disabled_tips">該紅包已失效</string>
</resources>

View File

@@ -229,20 +229,33 @@
android:onClick="@{click}"
android:src="@drawable/ic_first_charge_enter"
android:visibility="gone"
tools:contentDescription="@string/layout_fragment_av_room_game_02" />
tools:contentDescription="@string/layout_fragment_av_room_game_02"
tools:visibility="visible" />
<ImageView
android:id="@+id/iv_radish_entrance"
android:layout_width="65dp"
android:layout_height="65dp"
android:layout_above="@id/iv_treasure_box"
android:layout_above="@id/red_package_widget"
android:layout_alignParentEnd="true"
android:layout_marginEnd="15dp"
android:layout_marginBottom="6dp"
android:onClick="@{click}"
android:src="@drawable/ic_radish_entrance"
android:visibility="gone"
tools:contentDescription="@string/layout_fragment_av_room_game_03" />
tools:contentDescription="@string/layout_fragment_av_room_game_03"
tools:visibility="visible" />
<com.yizhuan.erban.avroom.redpackage.RedPackageWidget
android:id="@+id/red_package_widget"
android:layout_width="58dp"
android:layout_height="58dp"
android:layout_above="@id/iv_treasure_box"
android:layout_alignParentEnd="true"
android:layout_marginEnd="18.5dp"
android:layout_marginBottom="6dp"
android:visibility="gone"
tools:visibility="visible" />
<ImageView
android:id="@+id/iv_treasure_box"
@@ -255,7 +268,8 @@
android:onClick="@{click}"
android:src="@drawable/icon_room_treasure_box"
android:visibility="gone"
tools:contentDescription="@string/layout_fragment_av_room_game_04" />
tools:contentDescription="@string/layout_fragment_av_room_game_04"
tools:visibility="visible" />
<ImageView
android:id="@+id/iv_queuing_micro"
@@ -269,7 +283,8 @@
android:onClick="@{click}"
android:src="@drawable/ic_dating_queuing_micro"
android:visibility="gone"
tools:contentDescription="@string/layout_fragment_av_room_game_05" />
tools:contentDescription="@string/layout_fragment_av_room_game_05"
tools:visibility="visible" />
<LinearLayout
android:layout_width="wrap_content"

View File

@@ -100,24 +100,25 @@
android:id="@+id/contribute_list"
android:layout_width="wrap_content"
android:layout_height="20dp"
android:layout_marginTop="85dp"
android:background="@drawable/bg_single_room_rank_entrance"
android:drawablePadding="@dimen/dp_2"
android:gravity="center"
android:onClick="@{click}"
android:paddingStart="@dimen/dp_8"
android:paddingEnd="@dimen/dp_4"
android:layout_marginTop="85dp"
android:text="@string/room_host_list"
android:textColor="@color/white"
android:textSize="@dimen/sp_10"
app:layout_constraintTop_toTopOf="parent"
app:drawableStartCompat="@drawable/ic_sing_room_contribute_list"
app:layout_constraintEnd_toEndOf="parent"
app:drawableStartCompat="@drawable/ic_sing_room_contribute_list" />
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_hour_rank"
android:layout_width="wrap_content"
android:layout_height="20dp"
android:layout_marginTop="@dimen/dp_8"
android:background="@drawable/bg_single_room_rank_entrance"
android:drawablePadding="@dimen/dp_2"
android:gravity="center"
@@ -126,7 +127,6 @@
android:text="@string/room_list_hour"
android:textColor="@color/white"
android:textSize="@dimen/sp_10"
android:layout_marginTop="@dimen/dp_8"
app:drawableStartCompat="@drawable/ic_single_room_rank"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/contribute_list" />
@@ -201,10 +201,20 @@
android:onClick="@{click}"
android:src="@drawable/ic_radish_entrance"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/iv_treasure_box"
tools:visibility="visible"
app:layout_constraintBottom_toTopOf="@id/red_package_widget"
app:layout_constraintEnd_toEndOf="parent"
tools:contentDescription="@string/layout_fragment_single_room_06" />
<com.yizhuan.erban.avroom.redpackage.RedPackageWidget
android:id="@+id/red_package_widget"
android:layout_width="58dp"
android:layout_height="58dp"
android:layout_marginBottom="6dp"
app:layout_constraintBottom_toTopOf="@id/iv_treasure_box"
app:layout_constraintEnd_toEndOf="@id/iv_treasure_box"
app:layout_constraintStart_toStartOf="@id/iv_treasure_box" />
<ImageView
android:id="@+id/iv_treasure_box"
android:layout_width="65dp"
@@ -216,7 +226,8 @@
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/fl_speedy_message"
app:layout_constraintEnd_toEndOf="parent"
tools:contentDescription="@string/layout_fragment_single_room_07" />
tools:contentDescription="@string/layout_fragment_single_room_07"
tools:visibility="visible" />
<com.yizhuan.erban.avroom.widget.MessageView
android:id="@+id/message_view"

View File

@@ -0,0 +1,7 @@
package com.yizhuan.xchat_android_core.home.event
/**
* Created by Max on 2023/10/27 11:56
* Desc:打开公屏输入
**/
class OpenRoomMessageInputEvent(val text: String? = null)

View File

@@ -99,6 +99,7 @@ import com.yizhuan.xchat_android_core.im.custom.bean.MonsterHuntingResultAttachm
import com.yizhuan.xchat_android_core.im.custom.bean.MonsterStatusAttachment;
import com.yizhuan.xchat_android_core.im.custom.bean.MultiGiftAttachment;
import com.yizhuan.xchat_android_core.im.custom.bean.NobleAttachment;
import com.yizhuan.xchat_android_core.im.custom.bean.RedPackageAttachment;
import com.yizhuan.xchat_android_core.im.custom.bean.RoomBoxPrizeAttachment;
import com.yizhuan.xchat_android_core.im.custom.bean.RoomFreeGiftAttachment;
import com.yizhuan.xchat_android_core.im.custom.bean.RoomFreeGiftMsgBean;
@@ -1148,12 +1149,17 @@ public final class IMNetEaseManager {
case CustomAttachment.CUSTOM_MSG_RED_PACKAGE:
switch (second) {
// 老版本厅内红包2.2.0之前)
case CUSTOM_MSG_SUB_RED_PACKAGE_RECEIVE_ROOM_DIAMOND:
noticeRoomEvent(msg, RoomEvent.RECEIVE_RED_PACKAGE);
break;
case CUSTOM_MSG_SUB_RED_PACKAGE_RECEIVE_ROOM_MSG:
addMessages(msg);
break;
// 新版厅内红包(>=2.2.0版本)
case CUSTOM_MSG_SUB_RED_PACKAGE_RECEIVE_ROOM_DIAMOND2:
noticeRoomEvent(msg, RoomEvent.RECEIVE_RED_PACKAGE);
break;
}
break;

View File

@@ -64,6 +64,7 @@ import static com.yizhuan.xchat_android_core.im.custom.bean.CustomAttachment.CUS
import static com.yizhuan.xchat_android_core.im.custom.bean.CustomAttachment.CUSTOM_MSG_SUB_PUBLIC_CHAT_HALL_GIFT;
import static com.yizhuan.xchat_android_core.im.custom.bean.CustomAttachment.CUSTOM_MSG_SUB_RED_PACKAGE_RECEIVE_ALL_DIAMOND;
import static com.yizhuan.xchat_android_core.im.custom.bean.CustomAttachment.CUSTOM_MSG_SUB_RED_PACKAGE_RECEIVE_ROOM_DIAMOND;
import static com.yizhuan.xchat_android_core.im.custom.bean.CustomAttachment.CUSTOM_MSG_SUB_RED_PACKAGE_RECEIVE_ROOM_DIAMOND2;
import static com.yizhuan.xchat_android_core.im.custom.bean.CustomAttachment.CUSTOM_MSG_SUB_RED_PACKAGE_RECEIVE_ROOM_MSG;
import static com.yizhuan.xchat_android_core.im.custom.bean.CustomAttachment.CUSTOM_MSG_SUB_TYPE_DRAW_GIFT_EFFECT;
import static com.yizhuan.xchat_android_core.im.custom.bean.CustomAttachment.CUSTOM_MSG_SUB_TYPE_RECEIVE_LUCKY_MONEY;
@@ -458,6 +459,7 @@ public class CustomAttachParser implements MsgAttachmentParser {
switch (second) {
case CUSTOM_MSG_SUB_RED_PACKAGE_RECEIVE_ROOM_DIAMOND:
case CUSTOM_MSG_SUB_RED_PACKAGE_RECEIVE_ALL_DIAMOND:
case CUSTOM_MSG_SUB_RED_PACKAGE_RECEIVE_ROOM_DIAMOND2:
attachment = new RedPackageAttachment(second);
break;
case CUSTOM_MSG_SUB_RED_PACKAGE_RECEIVE_ROOM_MSG:

View File

@@ -317,6 +317,7 @@ public class CustomAttachment implements MsgAttachment {
public static final int CUSTOM_MSG_SUB_RED_PACKAGE_RECEIVE_ROOM_DIAMOND = 602;
public static final int CUSTOM_MSG_SUB_RED_PACKAGE_RECEIVE_ALL_DIAMOND = 604;//全服红包
public static final int CUSTOM_MSG_SUB_RED_PACKAGE_RECEIVE_ROOM_MSG = 605;
public static final int CUSTOM_MSG_SUB_RED_PACKAGE_RECEIVE_ROOM_DIAMOND2 = 606;//新版本-厅内红包
//开福袋飘屏
public static final int CUSTOM_MSG_LUCKY_GIFT = 61;
public static final int CUSTOM_MSG_LUCKY_GIFT_ROOM_NOTIFY = 610;// 福袋礼物房间飘屏通知

View File

@@ -14,7 +14,7 @@ import lombok.EqualsAndHashCode;
@Data
public class RedPackageAttachment extends CustomAttachment {
private RedPackageNotifyInfo redPackageNotifyInfo;
public RedPackageNotifyInfo redPackageNotifyInfo;
public RedPackageAttachment(int second) {
super(CUSTOM_MSG_RED_PACKAGE, second);

View File

@@ -0,0 +1,57 @@
package com.yizhuan.xchat_android_core.interceptor
import com.chuhai.utils.AppUtils
import com.chuhai.utils.ServiceTime
import com.yizhuan.xchat_android_core.utils.LogUtils
import com.yizhuan.xchat_android_library.utils.NetworkUtils
import okhttp3.Headers
import okhttp3.Interceptor
import okhttp3.Response
/**
* Created by Max on 2023/10/25 14:17 AM
* Desc:服务器时间同步器
*/
class TimeSyncInterceptor : Interceptor {
private var minResponseTime = Long.MAX_VALUE
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val startTime = System.nanoTime()
val proceed = chain.proceed(request)
val lastTime = System.nanoTime() - startTime
val headers = proceed.headers
calibration(lastTime, headers)
return proceed
}
private fun calibration(responseTime: Long, headers: Headers?) {
if (headers == null) {
return
}
//如果这一次的请求响应时间小于上一次,则更新本地维护的时间
if (responseTime >= minResponseTime) {
return
}
try {
// 网络无法使用时,不能同步时间
if (!NetworkUtils.isNetworkAvailable(AppUtils.getApp())) {
return
}
// 网络响应头包含Date字段世界时间
// 利用Interceptor记录每次请求响应时间如果本次网络操作的时间小于上一次网络操作的时间则获取Date字段转换时区后更新本地
val date = headers.getDate("Date")
LogUtils.d("TimeSyncInterceptor date:$date time:${date?.time}")
date?.let {
//客户端请求过程一般大于比收到响应时间耗时所以没有简单的除2 加上去,而是直接用该时间
ServiceTime.refreshServiceTime(it.time)
minResponseTime = responseTime
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}

View File

@@ -39,6 +39,7 @@ import com.yizhuan.xchat_android_core.room.game.GameStatus;
import com.yizhuan.xchat_android_core.room.giftvalue.helper.GiftValueMrg;
import com.yizhuan.xchat_android_core.super_admin.SuperAdminDataMrg;
import com.yizhuan.xchat_android_core.super_admin.util.SuperAdminUtil;
import com.yizhuan.xchat_android_core.support.room.RoomContext;
import com.yizhuan.xchat_android_core.user.UserModel;
import com.yizhuan.xchat_android_core.utils.CurrentTimeUtils;
import com.yizhuan.xchat_android_core.utils.LogUtils;
@@ -261,6 +262,10 @@ public final class AvRoomDataManager {
kickOutRoomUids.clear();
kickOutRoomUids = null;
}
RoomContext context = RoomContext.Companion.get();
if (context != null) {
context.performStop();
}
LogUtils.d(ResUtil.getString(R.string.xchat_android_core_manager_avroomdatamanager_05));
}

View File

@@ -4,20 +4,32 @@ import com.yizhuan.xchat_android_core.community.UserVo
data class RedEnvelopeVO(
var beginTime: Long = 0,
var createTime: Long = 0,
var endTime: Long = 0,
var id: String,
var message: String,
var redEnvelopeAmount: Double = 0.0,
var roomUId: Long = 0,
/**
* 红包类型 1:房间水晶红包 2:全服水晶红包 3房间礼物红包 4:全服礼物红包
*/
var type: Int = 0,
var updateTime: Long = 0,
var userId: Long = 0,
var totalNum: String = "",
var pickNum: String = "",
var userVO: UserVo? = null
var beginTime: Long = 0,
var createTime: Long = 0,
var endTime: Long = 0,
var id: String,
var message: String,
var redEnvelopeAmount: Double = 0.0,
var roomUId: Long = 0,
/**
* 红包类型 1:房间水晶红包 2:全服水晶红包 3房间礼物红包 4:全服礼物红包
*/
var type: Int = 0,
var updateTime: Long = 0,
var userId: Long = 0,
var totalNum: Long = 0,
var pickNum: Long = 0,
var userVO: UserVo? = null,
/**
* 红包种类 0 旧版本 1 无门槛红包 2 关注红包 3 分享红包 4 弹幕红包
*/
var kind: Int = 0,
/**
* 生效类型 0 立即生效 1 限时生效
*/
var validityType: Int = 0,
/**
* 红包状态 1 开抢中 2 过时 3 抢光了 4 抢到了 5 将要开始 6 超时已退还
*/
var state: Int?
)

View File

@@ -0,0 +1,36 @@
package com.yizhuan.xchat_android_core.redpackage
import java.io.Serializable
/**
* Created by Max on 2023/10/24 16:37
* Desc:红包数据
**/
data class RedPackageData(
val amount: Double?,
val avatar: String?,
val beginTime: Long?,
val commissionAmount: Long?,
val createTime: Long?,
val duration: Long?,
val endTime: Long?,
val finish: Boolean?,
val id: Long?,
val kind: Int?,
val message: String?,
val nick: String?,
val num: Long?,
val originalAmount: Long?,
val position: String?,
val roomTitle: String?,
val roomUid: Long?,
//红包状态 1 开抢中 2 过时 3 抢光了 4 抢到了 5 将要开始 6 超时已退还
val state: Int?,
val type: String?,
val updateTime: Long?,
val userId: Long?,
val validityType: Int?,
// 结果
val result: RedPackageInfo?
) : Serializable

View File

@@ -2,17 +2,19 @@ package com.yizhuan.xchat_android_core.redpackage
data class RedPackageInfo(
var redEnvelopeItemVOs: List<RedEnvelopeItemVO>? = null,
var redEnvelopeVO: RedEnvelopeVO? = null,
var redEnvelopeItemVOs: List<RedEnvelopeItemVO>? = null,
var redEnvelopeVO: RedEnvelopeVO? = null,
/**
* ING(1, “开抢中”)
* TIME_OUT(2, “过时”)
* REMAIN_ZERO(3, “抢光了”)
* SUCCESS(4, “抢到了”)
* WILL(5, “将要开始”)
* TIME_OUT_BACK(6, “超时已退还”)
*/
var redEnvelopeState: Int = 0
/**
* ING(1, “开抢中”)
* TIME_OUT(2, “过时”)
* REMAIN_ZERO(3, “抢光了”)
* SUCCESS(4, “抢到了”)
* WILL(5, “将要开始”)
* TIME_OUT_BACK(6, “超时已退还”)
*/
var redEnvelopeState: Int = 0,
// 领取金额
var currentUserAmount: Double?
)

View File

@@ -1,7 +1,6 @@
package com.yizhuan.xchat_android_core.redpackage
import com.yizhuan.xchat_android_core.bean.response.ServiceResult
import com.yizhuan.xchat_android_core.utils.net.handleBeanData
import com.yizhuan.xchat_android_core.utils.net.handleStringData
@@ -20,32 +19,66 @@ object RedPackageModel {
api = RxNet.create(Api::class.java)
}
fun sendRedPackage(goldNum: String, message: String, num: String, roomUId: String, type: Int, password: String): Single<String> {
return api.sendRedPackage(goldNum, message, num, roomUId, type, password)
.io2main()
.handleStringData()
fun sendRedPackage(
goldNum: Long, message: String?, num: Long, roomUId: String, type: Int,
kind: Int, validityType: Int, password: String
): Single<String> {
return api.sendRedPackage(
goldNum,
message,
num,
roomUId,
type,
kind,
validityType,
password
)
.io2main()
.handleStringData()
}
fun openRedPackage(redEnvelopeId: String): Single<RedPackageInfo> {
fun openRedPackage(redEnvelopeId: Long): Single<RedPackageInfo> {
return api.openRedPackage(redEnvelopeId)
.io2main()
.handleBeanData()
.io2main()
.handleBeanData()
}
fun getRedPackage(uid: Long): Observable<RedPackageNotifyInfo> {
return api.getRedPackage(uid)
.io2main()
.handleBeanData()
.toObservable()
.flatMap { Observable.fromIterable(it) }
.map { RedPackageNotifyInfo(it.id, it.type, it.message, it.userVO?.avatar?:"", it.userVO?.nick?:"", it.roomUId, "") }
fun getRedPackage(id: Long): Observable<RedPackageData> {
return api.getRedPackageData(id)
.io2main()
.handleBeanData()
.toObservable()
}
fun setRedPackageSwitch(roomUid: Long):Single<Boolean> {
/**
* 获取房间最新可用红包
*/
fun getLatestRoomRedPackage(uid: Long): Observable<RedPackageNotifyInfo> {
return api.getRedPackage(true, uid)
.io2main()
.handleBeanData()
.toObservable().map {
val item = it.last()
RedPackageNotifyInfo(
redEnvelopeId = item.id,
redEnvelopeType = item.type,
redEnvelopeMessage = item.message,
sendUserAvatar = item.userVO?.avatar ?: "",
sendUserNick = item.userVO?.nick ?: "",
roomUid = item.roomUId,
roomTitle = "",
kind = item.kind,
beginTime = item.beginTime,
redEnvelopeNum = it.size.toLong(),
validityType = item.validityType
)
}
}
fun setRedPackageSwitch(roomUid: Long): Single<Boolean> {
return api.setRedPackageSwitch(roomUid)
.io2main()
.handleBeanData()
.io2main()
.handleBeanData()
}
private interface Api {
@@ -57,12 +90,16 @@ object RedPackageModel {
*/
@FormUrlEncoded
@POST("/red-envelope")
fun sendRedPackage(@Field("goldNum") goldNum: String,
@Field("message") message: String,
@Field("num") num: String,
@Field("roomUId") roomUId: String,
@Field("type") type: Int,
@Field("password") password: String): Single<ServiceResult<String>>
fun sendRedPackage(
@Field("goldNum") goldNum: Long,
@Field("message") message: String?,
@Field("num") num: Long,
@Field("roomUId") roomUId: String,
@Field("type") type: Int,
@Field("kind") kind: Int,
@Field("validityType") validityType: Int,
@Field("password") password: String
): Single<ServiceResult<String>>
/**
* 开红包
@@ -71,7 +108,7 @@ object RedPackageModel {
*/
@FormUrlEncoded
@POST("/red-envelope/open")
fun openRedPackage(@Field("redEnvelopeId") redEnvelopeId: String): Single<ServiceResult<RedPackageInfo>>
fun openRedPackage(@Field("redEnvelopeId") redEnvelopeId: Long): Single<ServiceResult<RedPackageInfo>>
/**
@@ -80,7 +117,18 @@ object RedPackageModel {
* @return
*/
@GET("/room/red-envelop")
fun getRedPackage(@Query("uid") uid: Long): Single<ServiceResult<List<RedEnvelopeVO>>>
fun getRedPackage(
@Query("isShowKind") isShowKind: Boolean,
@Query("uid") uid: Long
): Single<ServiceResult<List<RedEnvelopeVO>>>
/**
* 获取红包详情
*/
@GET("/red-envelope/get")
fun getRedPackageData(
@Query("redEnvelopeId") redEnvelopeId: Long,
): Single<ServiceResult<RedPackageData>>
/**
@@ -90,6 +138,5 @@ object RedPackageModel {
fun setRedPackageSwitch(@Path("roomUid") uid: Long): Single<ServiceResult<Boolean>>
}
}

View File

@@ -4,38 +4,58 @@ import java.io.Serializable
data class RedPackageNotifyInfo(
/**
* 红包 id
*/
var redEnvelopeId: String = "",
/**
* 红包 id
*/
var redEnvelopeId: String = "",
/**
* 红包类型
*/
var redEnvelopeType: Int = 0,
/**
* 红包类型 1:房间水晶红包 2:全服水晶红包 3房间礼物红包 4:全服礼物红包
*/
var redEnvelopeType: Int = 0,
/**
* 红包消息
*/
var redEnvelopeMessage: String = "",
/**
* 红包消息
*/
var redEnvelopeMessage: String = "",
/**
* 红包发送者头像
*/
var sendUserAvatar: String = "",
/**
* 红包发送者头像
*/
var sendUserAvatar: String = "",
/**
* 红包发送者昵称
*/
var sendUserNick: String = "",
/**
* 房间uid
*/
var roomUid: Long = 0,
/**
* 红包发送者昵称
*/
var sendUserNick: String = "",
/**
* 房间uid
*/
var roomUid: Long = 0,
/**
* 房间名称
*/
var roomTitle: String = ""
/**
* 房间名称
*/
var roomTitle: String = "",
) : Serializable
/**
* 红包种类 0 旧版本 1 无门槛红包 2 关注红包 3 分享红包 4 弹幕红包
*/
var kind: Int = 0,
/**
* 红包可领取时间
*/
var beginTime: Long = 0,
/**
* 红包数量
*/
var redEnvelopeNum: Long = 0,
/**
* 生效类型 0 立即生效 1 限时生效
*/
var validityType: Int?,
) : Serializable

View File

@@ -0,0 +1,10 @@
package com.yizhuan.xchat_android_core.room.core
import com.yizhuan.xchat_android_core.support.room.RoomService
/**
* Created by Max on 2023/10/26 12:36
* Desc:房间数据服务
**/
class RoomDataService : RoomService() {
}

View File

@@ -0,0 +1,9 @@
package com.yizhuan.xchat_android_core.support.config
/**
* Created by Max on 2023/10/26 22:50
* Desc:常用变量
**/
object Constants {
val KEY_INTENT = "/intent"
}

View File

@@ -0,0 +1,98 @@
package com.yizhuan.xchat_android_core.support.room
import androidx.annotation.CallSuper
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import com.chuhai.utils.log.ILog
import com.trello.rxlifecycle3.LifecycleProvider
import com.trello.rxlifecycle3.LifecycleTransformer
import com.trello.rxlifecycle3.OutsideLifecycleException
import com.trello.rxlifecycle3.RxLifecycle
import com.yizhuan.xchat_android_core.support.room.lifecycle.RoomFullLifecycleObserver
import com.yizhuan.xchat_android_core.support.room.lifecycle.RoomLifecycle
import io.reactivex.Observable
import io.reactivex.functions.Function
import io.reactivex.subjects.BehaviorSubject
/**
* Created by Max on 2023/10/26 11:03
* Desc:房间-基础能力组件
**/
abstract class RoomAbility : LifecycleEventObserver, ILog, RoomFullLifecycleObserver,
LifecycleProvider<RoomLifecycle.Event> {
protected var roomContext: RoomContext? = null
protected var roomView: RoomView? = null
// 自定义RxLifeCycle临时方案后续替换为Flow
private val lifecycleSubject = BehaviorSubject.create<RoomLifecycle.Event>()
/**
* 被RoomContext激活
*/
@CallSuper
open fun onAttach(context: RoomContext) {
this.roomContext = context
context.roomLifecycle.addObserver(this)
}
/**
* View附加状态
* @param view
*/
@CallSuper
open fun onViewAttach(view: RoomView) {
roomView?.getLifecycleOwner()?.lifecycle?.removeObserver(this)
roomView = view
view.getLifecycleOwner().lifecycle.addObserver(this)
}
@CallSuper
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
if (event == Lifecycle.Event.ON_DESTROY) {
roomView?.getLifecycleOwner()?.lifecycle?.removeObserver(this)
roomView = null
}
}
override fun onStateChanged(context: RoomContext, event: RoomLifecycle.Event) {
super.onStateChanged(context, event)
// 同步更新事件
lifecycleSubject.onNext(event)
if (event == RoomLifecycle.Event.STOP) {
roomContext?.roomLifecycle?.removeObserver(this)
roomContext = null
}
}
/**
* 下面为RxLifeCycle相关临时方案后续替换Flow
*/
override fun lifecycle(): Observable<RoomLifecycle.Event?> {
return lifecycleSubject.hide()
}
override fun <T> bindUntilEvent(event: RoomLifecycle.Event): LifecycleTransformer<T> {
return RxLifecycle.bindUntilEvent(lifecycleSubject, event)
}
override fun <T> bindToLifecycle(): LifecycleTransformer<T> {
return RxLifecycle.bind(lifecycleSubject, ROOM_LIFECYCLE)
}
private val ROOM_LIFECYCLE: Function<RoomLifecycle.Event, RoomLifecycle.Event> =
Function { lastEvent ->
when (lastEvent) {
RoomLifecycle.Event.START -> RoomLifecycle.Event.STOP
RoomLifecycle.Event.STOP -> throw OutsideLifecycleException("Cannot bind to Room lifecycle when outside of it.")
else -> throw UnsupportedOperationException("Binding to $lastEvent not yet implemented")
}
}
}

View File

@@ -0,0 +1,111 @@
package com.yizhuan.xchat_android_core.support.room
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.chuhai.utils.log.ILog
import com.yizhuan.xchat_android_core.support.room.lifecycle.RoomLifecycle
import com.yizhuan.xchat_android_core.support.room.lifecycle.RoomLifecycleRegistry
/**
* Created by Max on 2023/10/26 11:50
* Desc:一个房间
**/
abstract class RoomContext(val roomId: Long) : ILog {
/**
* 临时维护在这里,后续逐步改动
*/
companion object {
private var context: RoomContext? = null
// TODO#临时方案 目前房间的生命周期有点出乎意料未成功进房时View层都已初始化完毕所以临时开放这个LiveData供View层获取到Context
private val _contextLiveData = MutableLiveData<RoomContext?>()
val contextLiveData: LiveData<RoomContext?> get() = _contextLiveData
fun get(): RoomContext? {
return context
}
fun set(context: RoomContext?) {
this.context = context
_contextLiveData.postValue(context)
}
}
/**
* 生命周期处理
*/
val roomLifecycle: RoomLifecycleRegistry = RoomLifecycleRegistry()
/**
* 所有能力组件
*/
private val abilityList = LinkedHashMap<String, RoomAbility>()
init {
initAbility()
}
/**
* 启动(进入房间)
*/
fun performStart() {
logI("performStart()", filePrinter = true)
roomLifecycle.handleLifecycleEvent(this, RoomLifecycle.Event.START)
}
/**
* 停止(退出房间)
*/
fun performStop() {
logI("performStop()", filePrinter = true)
roomLifecycle.handleLifecycleEvent(this, RoomLifecycle.Event.STOP)
onCleared()
}
/**
* 初始化Ability
*/
private fun initAbility() {
loadAbility(abilityList)
abilityList.keys.forEach {
abilityList[it]?.onAttach(this)
}
}
/**
* 装载能力组件
*/
open fun loadAbility(list: MutableMap<String, RoomAbility>) {
}
/**
* 查找组件
*/
fun <T : RoomAbility> findAbility(key: String): T? {
return abilityList[key] as? T
}
/**
* 附加上视图了
*/
@Deprecated("临时方案后续逐步完善RoomContext替换计划到时候不会在这里实现该逻辑")
fun onViewAttach(view: RoomView) {
abilityList.keys.forEach {
abilityList[it]?.onViewAttach(view)
}
}
/**
* 激活房间(进入房间后)
*/
@Deprecated("临时方案后续逐步完善RoomContext替换计划到时候不会在这里实现该逻辑")
fun run() {
set(this)
performStart()
}
private fun onCleared() {
abilityList.clear()
set(null)
}
}

View File

@@ -0,0 +1,8 @@
package com.yizhuan.xchat_android_core.support.room
/**
* Created by Max on 2023/10/26 11:02
* Desc:房间内的业务处理器
**/
open class RoomHandler : RoomAbility() {
}

View File

@@ -0,0 +1,10 @@
package com.yizhuan.xchat_android_core.support.room
/**
* Created by Max on 2023/10/26 12:31
* Desc:房间-基础服务组件
* PS基于[RoomContext],提供整个房间的基础服务能力。(服务之间可相互提供能力,也可向下提供能力。)
*
**/
abstract class RoomService : RoomAbility() {
}

View File

@@ -0,0 +1,22 @@
package com.yizhuan.xchat_android_core.support.room
import android.app.Activity
import androidx.lifecycle.LifecycleOwner
/**
* Created by Max on 2023/10/26 15:05
* Desc:房间视图层
* TODO目前Activity和子Fragment都实现该接口感觉有些奇怪PS还没熟悉现阶段的房间视图层级关系后续梳理后整体优化
**/
interface RoomView {
fun getLifecycleOwner(): LifecycleOwner
fun getActivity(): Activity?
/**
* 获取房间上下文
*/
fun getRoomContext(): RoomContext? {
return RoomContext.get()
}
}

View File

@@ -0,0 +1,20 @@
package com.yizhuan.xchat_android_core.support.room
import com.chuhai.utils.log.ILog
/**
* Created by Max on 2023/10/26 17:12
* Desc:房间UI组件
**/
interface RoomWidget : ILog {
/**
* 组件激活
*/
fun onStart(roomView: RoomView)
/**
* 组件停止
*/
fun onStop()
}

View File

@@ -0,0 +1,34 @@
package com.yizhuan.xchat_android_core.support.room.lifecycle
import com.yizhuan.xchat_android_core.support.room.RoomContext
/**
* Created by Max on 2023/10/26 11:50
* Desc:房间生命周期事件观察者(完整)
**/
interface RoomFullLifecycleObserver : RoomLifecycleObserver {
override fun onStateChanged(context: RoomContext, event: RoomLifecycle.Event) {
when (event) {
RoomLifecycle.Event.START -> {
onStart(context)
}
RoomLifecycle.Event.STOP -> {
onStop(context)
}
}
}
/**
* 启动房间
* PS只会执行一次相当于Activity的onCreate。
*/
fun onStart(context: RoomContext) {}
/**
* 退出房间
* PS只会执行一次相当于Activity的onDestroy。
*/
fun onStop(context: RoomContext) {}
}

View File

@@ -0,0 +1,35 @@
package com.yizhuan.xchat_android_core.support.room.lifecycle
import com.chuhai.utils.ICleared
/**
* Created by Max on 2023/10/26 11:50
* Desc:房间生命周期
**/
abstract class RoomLifecycle : ICleared {
/**
* 添加生命周期监听
* PS目前是及时事件分发对于已发生状态事件不会补充分发。
*/
abstract fun addObserver(observer: RoomLifecycleObserver)
/**
* 移除生命周期监听
*/
abstract fun removeObserver(observer: RoomLifecycleObserver)
enum class Event {
/**
* 启动房间
* PS只会执行一次相当于Activity的onCreate。
*/
START,
/**
* 退出房间
* PS只会执行一次相当于Activity的onDestroy。
*/
STOP;
}
}

View File

@@ -0,0 +1,18 @@
package com.yizhuan.xchat_android_core.support.room.lifecycle
import com.yizhuan.xchat_android_core.support.room.RoomContext
/**
* Created by Max on 2023/10/26 11:50
* Desc:房间生命周期事件观察者
**/
interface RoomLifecycleObserver {
/**
* 状态改变
* @param event 新事件
*/
fun onStateChanged(context: RoomContext, event: RoomLifecycle.Event)
}

View File

@@ -0,0 +1,9 @@
package com.yizhuan.xchat_android_core.support.room.lifecycle
/**
* Created by Max on 2023/10/26 11:50
* Desc:房间生命周期持有者
**/
interface RoomLifecycleOwner {
fun getLifecycle(): RoomLifecycle
}

View File

@@ -0,0 +1,62 @@
package com.yizhuan.xchat_android_core.support.room.lifecycle
import com.chuhai.utils.log.ILog
import com.yizhuan.xchat_android_core.support.room.RoomContext
import java.util.concurrent.CopyOnWriteArrayList
/**
* Created by Max on 2023/10/26 11:50
* Desc:房间生命周期简单实现
* PS目前是及时事件分发对于已发生状态事件不会补充分发。
**/
class RoomLifecycleRegistry : RoomLifecycle(), ILog {
/**
* 生命周期监听
*/
private val observers: CopyOnWriteArrayList<RoomLifecycleObserver> = CopyOnWriteArrayList()
/**
* 当前事件
*/
private var event: Event? = null
override fun addObserver(observer: RoomLifecycleObserver) {
observers.add(observer)
}
override fun removeObserver(observer: RoomLifecycleObserver) {
observers.remove(observer)
}
/**
* 设置最新的事件
*/
fun handleLifecycleEvent(context: RoomContext, event: Event) {
logI("handleLifecycleEvent() event:$event", filePrinter = true)
if (this.event == Event.STOP) {
// 停止状态
logI("handleLifecycleEvent() 停止状态", filePrinter = true)
return
}
if (this.event == event) {
// 状态未改变
return
}
dispatchEvent(context, event)
}
/**
* 分发事件
*/
private fun dispatchEvent(context: RoomContext, event: Event) {
logI("dispatchEvent() event:$event", filePrinter = true)
this.event = event
observers.forEach {
it.onStateChanged(context, event)
}
if (event == Event.STOP) {
// 停止后,主动清空下
observers.clear()
}
}
}

View File

@@ -0,0 +1,11 @@
package com.yizhuan.xchat_android_core.support.room.lifecycle.rx;
/**
* Created by Max on 2023/10/26 11:50
* Desc:RxLifecycle 房间生命周期
**/
public enum RoomRxLifecycleEvent {
START,
STOP
}

View File

@@ -31,8 +31,8 @@ COMPILE_SDK_VERSION=33
MIN_SDK_VERSION=21
TARGET_SDK_VERSION=33
version_name=2.1.1
version_code=2110
version_name=2.2.0
version_code=2220
#systemProp.https.proxyHost=127.0.0.1
#systemProp.https.proxyPort=7890

View File

@@ -4,6 +4,7 @@ import android.content.Context;
import android.os.Build;
import android.util.Log;
import com.facebook.stetho.okhttp3.StethoInterceptor;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.yizhuan.xchat_android_library.BuildConfig;
@@ -70,6 +71,8 @@ public final class RxNetManager {
});
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
mBuilder.addInterceptor(loggingInterceptor);
mBuilder.addNetworkInterceptor(new StethoInterceptor());
}
for (Interceptor interceptor : interceptors) {

View File

@@ -26,6 +26,7 @@ public abstract class BaseApp extends Application{
public static void init(Application application) {
gContext = application;
com.chuhai.utils.AppUtils.init(application);
}
/**

View File

@@ -0,0 +1,14 @@
package com.chuhai.utils
/**
* Created by Max on 2023/10/26 11:50
* Desc:清除释放统一接口
**/
interface ICleared {
/**
* 清除/释放
*/
fun onCleared() {}
}

View File

@@ -0,0 +1,13 @@
package com.chuhai.utils.log
import android.util.Log
/**
* Created by Max on 2023/10/26 10:29
* Desc:Android日志
*/
class AndroidLogPrinter : LogPrinter {
override fun println(level: Int, tag: String, message: String) {
Log.println(level, tag, message)
}
}

View File

@@ -0,0 +1,61 @@
package com.chuhai.utils.log
/**
* Created by Max on 2023/10/26 10:29
* Desc:日志快捷使用接口
*/
interface ILog {
companion object {
/**
* 清理退出APP时调用
*/
fun onCleared() {
}
}
/**
* 默认日志Tag
*/
fun getLogTag(): String {
return this::class.java.simpleName
}
fun logI(message: String, tag: String = getLogTag(), filePrinter: Boolean = false) {
LogUtil.i(tag, message, filePrinter)
}
fun logV(message: String, tag: String = getLogTag(), filePrinter: Boolean = false) {
LogUtil.v(tag, message, filePrinter)
}
fun logW(message: String, tag: String = getLogTag(), filePrinter: Boolean = false) {
LogUtil.w(tag, message, filePrinter)
}
fun logD(message: String, tag: String = getLogTag(), filePrinter: Boolean = false) {
LogUtil.d(tag, message, filePrinter)
}
fun logE(message: String, tag: String = getLogTag(), filePrinter: Boolean = false) {
LogUtil.e(tag, message, filePrinter)
}
fun logE(
throwable: Throwable,
tag: String = getLogTag(),
filePrinter: Boolean = false
) {
LogUtil.e(tag, throwable, filePrinter)
}
fun logE(
message: String,
throwable: Throwable,
tag: String = getLogTag(),
filePrinter: Boolean = false
) {
LogUtil.e(tag, message, throwable, filePrinter)
}
}

View File

@@ -0,0 +1,14 @@
package com.chuhai.utils.log
/**
* Created by Max on 2023/10/26 10:29
* Desc: 日志打印
*/
interface LogPrinter {
/**
* 打印
* @param level 级别 [android.util.Log]
*/
fun println(level: Int, tag: String, message: String)
}

View File

@@ -0,0 +1,101 @@
package com.chuhai.utils.log
import android.util.Log
/**
* Created by Max on 2023/10/26 10:29
* Desc:日志工具
*/
object LogUtil {
private var consolePrinter: LogPrinter? = AndroidLogPrinter()
private var filePrinter: LogPrinter? = null
// 是否启动控制台打印
var consolePrinterEnabled: Boolean = true
// 是否启动文件打印
var filePrinterEnabled: Boolean = true
/**
* 设置文件打印
*/
fun setFilePrinter(filePrinter: LogPrinter) {
this.filePrinter = filePrinter
}
fun e(tag: String, message: String, filePrinter: Boolean = false) {
log(Log.ERROR, tag, message, filePrinter)
}
fun e(tag: String, throwable: Throwable, filePrinter: Boolean = false) {
val cause = Log.getStackTraceString(throwable)
if (cause.isEmpty()) {
return
}
e(tag, cause, filePrinter)
}
fun e(tag: String, message: String?, throwable: Throwable, filePrinter: Boolean = false) {
val cause = Log.getStackTraceString(throwable)
if (message == null && cause.isEmpty()) {
return
}
e(tag, message + "\t\t" + cause, filePrinter)
}
fun d(tag: String, message: String, filePrinter: Boolean = false) {
log(Log.DEBUG, tag, message, filePrinter)
}
fun i(tag: String, message: String, filePrinter: Boolean = false) {
log(Log.INFO, tag, message, filePrinter)
}
fun v(tag: String, message: String, filePrinter: Boolean = false) {
log(Log.VERBOSE, tag, message, filePrinter)
}
fun w(tag: String, message: String, filePrinter: Boolean = false) {
log(Log.WARN, tag, message, filePrinter)
}
/**
* 输出日志
*/
fun log(level: Int = Log.INFO, tag: String?, message: String?, filePrinter: Boolean = false) {
if (tag.isNullOrEmpty()) {
return
}
if (message.isNullOrEmpty()) {
return
}
// 输出控制台
logConsole(level, tag, message)
// 输出文件
if (filePrinter) {
logFile(level, tag, message)
}
}
/**
* 输出到控制台
*/
fun logConsole(level: Int = Log.INFO, tag: String, message: String) {
if (!consolePrinterEnabled) {
return
}
consolePrinter?.println(level, tag, message)
}
/**
* 输出到文件
*/
fun logFile(level: Int = Log.INFO, tag: String, message: String) {
if (!filePrinterEnabled) {
return
}
filePrinter?.println(level, tag, message)
}
}

View File

@@ -0,0 +1,156 @@
package com.chuhai.utils.spannable;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.style.ReplacementSpan;
import android.util.TypedValue;
import androidx.annotation.NonNull;
/**
* Created by Max on 2023/10/26 20:14
**/
public class IconTextSpan extends ReplacementSpan {
private Context mContext;
private int mBgColorResId; //Icon背景颜色
private String mText; //Icon内文字
private float mBgHeight; //Icon背景高度
private float mBgWidth; //Icon背景宽度
private float mRadius; //Icon圆角半径
private float mRightMargin; //右边距
private float mTextSize; //文字大小
private int mTextColorResId; //文字颜色
private Paint mBgPaint; //icon背景画笔
private Paint mTextPaint; //icon文字画笔
private int paddingHorizontal = 0;
public IconTextSpan(Context context, int bgColorResId, String text, int textColor, int mTextSize, int round, int marginRight, int paddingHorizontal) {
if (TextUtils.isEmpty(text)) {
return;
}
this.paddingHorizontal = paddingHorizontal;
//初始化默认数值
initDefaultValue(context, bgColorResId, text, textColor, mTextSize, round, marginRight);
//计算背景的宽度
this.mBgWidth = caculateBgWidth(text);
//初始化画笔
initPaint();
}
/**
* 初始化画笔
*/
private void initPaint() {
//初始化背景画笔
mBgPaint = new Paint();
mBgPaint.setColor(mBgColorResId);
mBgPaint.setStyle(Paint.Style.FILL);
mBgPaint.setAntiAlias(true);
//初始化文字画笔
mTextPaint = new TextPaint();
mTextPaint.setColor(mTextColorResId);
mTextPaint.setTextSize(mTextSize);
mTextPaint.setAntiAlias(true);
mTextPaint.setTextAlign(Paint.Align.CENTER);
}
/**
* 初始化默认数值
*
* @param context 上下文
* @param textColor 字体颜色
*/
private void initDefaultValue(Context context, int bgColorResId, String text, int textColor, int textSize, int round, int marginRight) {
this.mContext = context.getApplicationContext();
this.mBgColorResId = bgColorResId;
this.mText = text;
this.mBgHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 18f, mContext.getResources().getDisplayMetrics());
this.mRightMargin = marginRight;
this.mRadius = round;
this.mTextSize = textSize;
this.mTextColorResId = textColor;
}
/**
* 计算icon背景宽度
*
* @param text icon内文字
*/
private float caculateBgWidth(String text) {
// if (text.length() > 1) {
//多字,宽度=文字宽度+padding
Rect textRect = new Rect();
Paint paint = new Paint();
paint.setTextSize(mTextSize);
paint.getTextBounds(text, 0, text.length(), textRect);
float padding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, paddingHorizontal, mContext.getResources().getDisplayMetrics());
return textRect.width() + padding * 2;
// } else {
//单字,宽高一致为正方形
// return mBgHeight + paddingHorizontal;
// }
}
/**
* 设置右边距
* @param rightMarginDpValue 右边边距
*/
public void setRightMarginDpValue(int rightMarginDpValue) {
this.mRightMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, rightMarginDpValue, mContext.getResources().getDisplayMetrics());
}
/**
* 设置宽度,宽度=背景宽度+右边距
*/
@Override
public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
return (int) (mBgWidth + mRightMargin);
}
/**
* draw
*
* @param text 完整文本
* @param start setSpan里设置的start
* @param end setSpan里设置的start
* @param top 当前span所在行的上方y
* @param y y其实就是metric里baseline的位置
* @param bottom 当前span所在行的下方y(包含了行间距)会和下一行的top重合
* @param paint 使用此span的画笔
*/
@Override
public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
//画背景
Paint bgPaint = new Paint();
bgPaint.setColor(mBgColorResId);
bgPaint.setStyle(Paint.Style.FILL);
bgPaint.setAntiAlias(true);
Paint.FontMetrics metrics = paint.getFontMetrics();
float textHeight = metrics.descent - metrics.ascent;
//算出背景开始画的y坐标
float bgStartY = y + (textHeight - mBgHeight) / 2 + metrics.ascent;
//画背景
RectF bgRect = new RectF(x, bgStartY, x + mBgWidth , bgStartY + mBgHeight);
canvas.drawRoundRect(bgRect, mRadius, mRadius, bgPaint);
//把字画在背景中间
TextPaint textPaint = new TextPaint();
textPaint.setColor(mTextColorResId);
textPaint.setTextSize(mTextSize);
textPaint.setAntiAlias(true);
textPaint.setTextAlign(Paint.Align.CENTER); //这个只针对x有效
Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
float textRectHeight = fontMetrics.bottom - fontMetrics.top;
canvas.drawText(mText, x + mBgWidth / 2, bgStartY + (mBgHeight - textRectHeight) / 2 - fontMetrics.top, textPaint);
}
}

View File

@@ -0,0 +1,29 @@
package com.chuhai.utils.spannable
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.RectF
import android.text.style.ReplacementSpan
/**
* Created by Max on 2023/10/26 20:14
* Desc:文字 圆背景
**/
class RoundBackgroundColorSpan(var textColor: Int, var textSize: Int, var bgColor: Int, var paddingHorizontal: Int, var paddingVertical: Int, var marginHorizontal: Int,var round:Int) : ReplacementSpan() {
override fun getSize(paint: Paint, text: CharSequence?, start: Int, end: Int, fm: Paint.FontMetricsInt?): Int {
return paint.measureText(text, start, end).toInt()+(paddingHorizontal)+marginHorizontal
}
override fun draw(canvas: Canvas, text: CharSequence?, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) {
paint.color = this.textColor
paint.textSize = textSize.toFloat()
canvas.drawText(text.toString(), start, end, x + paddingHorizontal+marginHorizontal, y.toFloat()-paddingVertical, paint)
paint.color = paint.color
paint.color = this.bgColor;
val rectF = RectF(x+marginHorizontal, top.toFloat(), (paint.measureText(text.toString())) , bottom.toFloat())
canvas.drawRoundRect(rectF, round.toFloat(), round.toFloat(), paint)
}
}

View File

@@ -0,0 +1,327 @@
package com.chuhai.utils.spannable
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.TextPaint
import android.text.method.LinkMovementMethod
import android.text.style.*
import android.view.View
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import com.chuhai.utils.ktx.dp
/**
* Created by Max on 2023/10/26 20:14
* Desc:可扩展文本
**/
class SpannableTextBuilder(private val textView: TextView) {
private val spannableBuilder: SpannableStringBuilder by lazy {
SpannableStringBuilder()
}
/**
* 添加一段文本
*/
fun appendText(node: TextNode) {
val onClick: ((String) -> Unit)? = if (node.getOnClickListener() != null) {
{
node.getOnClickListener()?.invoke(node)
}
} else {
null
}
appendText(
text = node.getContent(),
textColor = node.getTextColor(),
textSize = node.getTextSize(),
backgroundColor = node.getBackgroundColor(),
underline = node.isUnderline(),
clickListener = onClick
)
}
/**
* 添加一段文本
* @param text 文本
* @param textColor 文本颜色
* @param backgroundColor 背景颜色
* @param textSize 文本大小
* @param textStyle 文本样式
* @param underline 是否有下划线
* @param clickListener 点击事件
*/
fun appendText(
text: String,
@ColorInt textColor: Int? = null,
@ColorInt backgroundColor: Int? = null,
textSize: Int? = null,
textStyle: Int? = null,
underline: Boolean? = null,
clickListener: ((String) -> Unit)? = null
): SpannableTextBuilder {
val start = spannableBuilder.length
spannableBuilder.append(text)
val end = spannableBuilder.length
// 文本颜色
if (textColor != null) {
spannableBuilder.setSpan(
ForegroundColorSpan(textColor),
start,
end,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
// 文本背景颜色
if (backgroundColor != null) {
spannableBuilder.setSpan(
BackgroundColorSpan(backgroundColor),
start,
end,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
// 文本大小
if (textSize != null) {
spannableBuilder.setSpan(
AbsoluteSizeSpan(textSize, true),
start,
end,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
// 文本样式
if (textStyle != null) {
spannableBuilder.setSpan(
StyleSpan(textStyle),
start,
end,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
// 下划线
if (underline == true) {
spannableBuilder.setSpan(UnderlineSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
// 点击事件
if (clickListener != null) {
// 设置highlightColor=Color.TRANSPARENT可以解决点击时的高亮色问题但光标的区域选中也是透明的貌似对用户体验不太好
// textView.highlightColor = Color.TRANSPARENT
textView.movementMethod = LinkMovementMethod.getInstance()
val clickableSpan = TextClickableSpan(
clickListener, text, textColor
?: textView.currentTextColor, underline ?: false
)
spannableBuilder.setSpan(clickableSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
return this
}
/**
* 添加图片
* @param drawable 图片
* @param clickListener 点击事件
*/
fun appendDrawable(
@DrawableRes drawable: Int,
clickListener: ((Int) -> Unit)?
): SpannableTextBuilder {
// 需要时再完善
val start = spannableBuilder.length
spannableBuilder.append("[icon}")
val end = spannableBuilder.length
// 图片
val imageSpan: ImageSpan = VerticalImageSpan(textView.context, drawable)
spannableBuilder.setSpan(imageSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
// 点击事件
if (clickListener != null) {
textView.movementMethod = LinkMovementMethod.getInstance()
val clickableSpan = DrawableClickableSpan(clickListener, drawable)
spannableBuilder.setSpan(clickableSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
return this
}
/**
* 添加有背景圆角的文字
* @param text 文本
* @param textColor 文本颜色
* @param backgroundColor 背景颜色
* @param paddingHorizontal 内横向边距
* @param paddingVertical 内竖向边距
* @param marginHorizontal 外横向边距
*/
fun appendTextRoundBackground(
text: String,
@ColorInt textColor: Int,
textSize: Int,
@ColorInt backgroundColor: Int,
paddingHorizontal: Int,
paddingVertical: Int,
marginHorizontal: Int,
round: Int
): SpannableTextBuilder {
val start = spannableBuilder.length
spannableBuilder.append(text)
val end = spannableBuilder.length
spannableBuilder.setSpan(
RoundBackgroundColorSpan(
textColor,
textSize,
backgroundColor,
paddingHorizontal,
paddingVertical,
marginHorizontal,
round
), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
);
return this
}
/**
* 添加有背景圆角的文字
* @param text 文本
* @param textColor 文本颜色
* @param backgroundColor 背景颜色
* @param paddingHorizontal 内横向边距
* @param paddingVertical 内竖向边距
* @param marginHorizontal 外横向边距
*/
fun appendIconTextRoundBackground(
text: String,
@ColorInt textColor: Int,
textSize: Int,
@ColorInt backgroundColor: Int,
marginRight: Int,
round: Int
): SpannableTextBuilder {
val start = spannableBuilder.length
spannableBuilder.append(text)
val end = spannableBuilder.length
spannableBuilder.setSpan(
IconTextSpan(
textView.context,
backgroundColor,
text,
textColor,
textSize,
round,
marginRight,
2.dp
),
start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
return this
}
fun build(): SpannableStringBuilder {
return spannableBuilder
}
/**
* 应用
*/
fun apply() {
textView.text = spannableBuilder
}
/**
* 文本点击
*/
class TextClickableSpan(
private val clickListener: ((String) -> Unit)? = null,
private val text: String,
private val textColor: Int,
private val underline: Boolean
) : ClickableSpan() {
override fun onClick(widget: View) {
clickListener?.invoke(text)
}
override fun updateDrawState(ds: TextPaint) {
ds.color = textColor
ds.isUnderlineText = underline
}
}
/**
* 图片点击
*/
class DrawableClickableSpan(
private val clickListener: ((Int) -> Unit)? = null,
private val drawable: Int
) : ClickableSpan() {
override fun onClick(widget: View) {
clickListener?.invoke(drawable)
}
}
interface TextNode {
/**
* 内容
*/
fun getContent(): String
/**
* 文本颜色
*/
fun getTextSize(): Int? {
return null
}
/**
* 文本颜色
*/
fun getTextColor(): Int? {
return null
}
/**
* 文本样式
*/
fun getTextStyle(): Int? {
return null
}
/**
* 背景颜色
*/
fun getBackgroundColor(): Int? {
return null
}
/**
* 是否有下划线
*/
fun isUnderline(): Boolean {
return false
}
/**
* 获取点击事件
*/
fun getOnClickListener(): ((TextNode) -> Unit)? {
return null
}
}
}
/**
* 快速构建生成器
*/
fun TextView.spannableBuilder(): SpannableTextBuilder {
return SpannableTextBuilder(this)
}

View File

@@ -0,0 +1,66 @@
package com.chuhai.utils.spannable
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.drawable.Drawable
import android.text.style.ImageSpan
/**
* Created by Max on 2023/10/26 20:14
* Desc:垂直居中的ImageSpan
**/
class VerticalImageSpan : ImageSpan {
constructor(drawable: Drawable) : super(drawable)
constructor(context: Context, resourceId: Int) : super(context, resourceId)
/**
* update the text line height
*/
override fun getSize(
paint: Paint, text: CharSequence?, start: Int, end: Int,
fontMetricsInt: Paint.FontMetricsInt?
): Int {
val drawable = drawable
val rect = drawable.bounds
if (fontMetricsInt != null) {
val fmPaint = paint.fontMetricsInt
val fontHeight = fmPaint.descent - fmPaint.ascent
val drHeight = rect.bottom - rect.top
val centerY = fmPaint.ascent + fontHeight / 2
fontMetricsInt.ascent = centerY - drHeight / 2
fontMetricsInt.top = fontMetricsInt.ascent
fontMetricsInt.bottom = centerY + drHeight / 2
fontMetricsInt.descent = fontMetricsInt.bottom
}
return rect.right
}
/**
* see detail message in android.text.TextLine
*
* @param canvas the canvas, can be null if not rendering
* @param text the text to be draw
* @param start the text start position
* @param end the text end position
* @param x the edge of the replacement closest to the leading margin
* @param top the top of the line
* @param y the baseline
* @param bottom the bottom of the line
* @param paint the work paint
*/
override fun draw(
canvas: Canvas, text: CharSequence, start: Int, end: Int,
x: Float, top: Int, y: Int, bottom: Int, paint: Paint
) {
val drawable = drawable
canvas.save()
val fmPaint = paint.fontMetricsInt
val fontHeight = fmPaint.descent - fmPaint.ascent
val centerY = y + fmPaint.descent - fontHeight / 2
val transY = centerY - (drawable.bounds.bottom - drawable.bounds.top) / 2
canvas.translate(x, transY.toFloat())
drawable.draw(canvas)
canvas.restore()
}
}

View File

@@ -79,4 +79,6 @@
<color name="text_hint_555574">#B3333333</color>
<color name="color_f4f4fa">#FFF4F4FA</color>
</resources>