diff --git a/app/src/main/java/com/yizhuan/erban/application/XChatApplication.java b/app/src/main/java/com/yizhuan/erban/application/XChatApplication.java index 6438d1cd2..255ab7505 100644 --- a/app/src/main/java/com/yizhuan/erban/application/XChatApplication.java +++ b/app/src/main/java/com/yizhuan/erban/application/XChatApplication.java @@ -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() + ); + } + } } diff --git a/app/src/main/java/com/yizhuan/erban/avroom/activity/AVRoomActivity.java b/app/src/main/java/com/yizhuan/erban/avroom/activity/AVRoomActivity.java index ba0854c1a..b65cb15db 100644 --- a/app/src/main/java/com/yizhuan/erban/avroom/activity/AVRoomActivity.java +++ b/app/src/main/java/com/yizhuan/erban/avroom/activity/AVRoomActivity.java @@ -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 - 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 openDialogs; private ViewPager2 viewpager; private VerticalViewPagerAdapter mAdapter; /*********************************显示全服礼物***************************************/ @@ -511,7 +510,6 @@ public class AVRoomActivity extends BaseMvpActivity 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 { private WeakReference mReference; @@ -1433,5 +1439,4 @@ public class AVRoomActivity extends BaseMvpActivity) { + super.loadAbility(list) + // 红包处理器 + list[RedPackageHandler::class.java.simpleName] = RedPackageHandler() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/yizhuan/erban/avroom/dialog/RoomOperationDialog.java b/app/src/main/java/com/yizhuan/erban/avroom/dialog/RoomOperationDialog.java index 7e3674f47..74004e3c9 100644 --- a/app/src/main/java/com/yizhuan/erban/avroom/dialog/RoomOperationDialog.java +++ b/app/src/main/java/com/yizhuan/erban/avroom/dialog/RoomOperationDialog.java @@ -352,7 +352,6 @@ public class RoomOperationDialog extends BottomSheetDialog { } })); } - } /** diff --git a/app/src/main/java/com/yizhuan/erban/avroom/fragment/BaseRoomFragment.kt b/app/src/main/java/com/yizhuan/erban/avroom/fragment/BaseRoomFragment.kt index 0c691aa36..35954c53a 100644 --- a/app/src/main/java/com/yizhuan/erban/avroom/fragment/BaseRoomFragment.kt +++ b/app/src/main/java/com/yizhuan/erban/avroom/fragment/BaseRoomFragment.kt @@ -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?> : BaseMvpFragment(), - 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?> : * 是否開啟禮物值顯示 */ 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?> : private var atProxy: AtProxy? = null + // 房间小组件 + private var widgets: HashMap = HashMap() @CallSuper override fun onFindViews() { @@ -238,6 +248,7 @@ open class BaseRoomFragment?> : @SuppressLint("CheckResult") @CallSuper override fun initiate() { + initWidget() //如果不需要開麥,並且還沒有權限的情況下,重置狀態為需要去打開麥克風 myUid = AuthModel.get().currentUid isCloseScreen = AvRoomDataManager.get().isCloseScreen @@ -637,6 +648,7 @@ open class BaseRoomFragment?> : override fun onDestroyView() { super.onDestroyView() releaseView() + unregisterWidgets() } override fun onDestroy() { @@ -1273,10 +1285,7 @@ open class BaseRoomFragment?> : } 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?> : 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?> : 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) + } } \ No newline at end of file diff --git a/app/src/main/java/com/yizhuan/erban/avroom/fragment/HomePartyRoomFragment.java b/app/src/main/java/com/yizhuan/erban/avroom/fragment/HomePartyRoomFragment.java index 44d1c638e..d56032978 100644 --- a/app/src/main/java/com/yizhuan/erban/avroom/fragment/HomePartyRoomFragment.java +++ b/app/src/main/java/com/yizhuan/erban/avroom/fragment/HomePartyRoomFragment.java @@ -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 { .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(); } diff --git a/app/src/main/java/com/yizhuan/erban/avroom/redpackage/RedPackageHandler.kt b/app/src/main/java/com/yizhuan/erban/avroom/redpackage/RedPackageHandler.kt new file mode 100644 index 000000000..fd991d352 --- /dev/null +++ b/app/src/main/java/com/yizhuan/erban/avroom/redpackage/RedPackageHandler.kt @@ -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 = 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(bindToLifecycle()) + .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() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/yizhuan/erban/avroom/redpackage/RedPackageOpenDialog.kt b/app/src/main/java/com/yizhuan/erban/avroom/redpackage/RedPackageOpenDialog.kt index 9f2e31b2e..db3a01cc9 100644 --- a/app/src/main/java/com/yizhuan/erban/avroom/redpackage/RedPackageOpenDialog.kt +++ b/app/src/main/java/com/yizhuan/erban/avroom/redpackage/RedPackageOpenDialog.kt @@ -77,18 +77,17 @@ class RedPackageOpenDialog : BaseDialog() { @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() { 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() { } } } + TIME_OUT, REMAIN_ZERO, TIME_OUT_BACK -> { binding?.clOpened?.setBackgroundResource(R.drawable.room_red_package_get_bg) binding?.tvDiamondNum?.visibility = View.GONE diff --git a/app/src/main/java/com/yizhuan/erban/avroom/redpackage/RedPackageSendDialog.kt b/app/src/main/java/com/yizhuan/erban/avroom/redpackage/RedPackageSendDialog.kt index bb10652a4..fa86ab5fa 100644 --- a/app/src/main/java/com/yizhuan/erban/avroom/redpackage/RedPackageSendDialog.kt +++ b/app/src/main/java/com/yizhuan/erban/avroom/redpackage/RedPackageSendDialog.kt @@ -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(), 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() +// } } } diff --git a/app/src/main/java/com/yizhuan/erban/avroom/redpackage/RedPackageWidget.kt b/app/src/main/java/com/yizhuan/erban/avroom/redpackage/RedPackageWidget.kt new file mode 100644 index 000000000..87c177603 --- /dev/null +++ b/app/src/main/java/com/yizhuan/erban/avroom/redpackage/RedPackageWidget.kt @@ -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::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 { + 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() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/yizhuan/erban/avroom/redpackage/open/RedPackageOpenDialog2.kt b/app/src/main/java/com/yizhuan/erban/avroom/redpackage/open/RedPackageOpenDialog2.kt new file mode 100644 index 000000000..a237be886 --- /dev/null +++ b/app/src/main/java/com/yizhuan/erban/avroom/redpackage/open/RedPackageOpenDialog2.kt @@ -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(), 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 + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/yizhuan/erban/avroom/redpackage/open/RedPackageResultAdapter.kt b/app/src/main/java/com/yizhuan/erban/avroom/redpackage/open/RedPackageResultAdapter.kt new file mode 100644 index 000000000..a7b5b585c --- /dev/null +++ b/app/src/main/java/com/yizhuan/erban/avroom/redpackage/open/RedPackageResultAdapter.kt @@ -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(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(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(R.id.v_line).isVisible = helper.bindingAdapterPosition != itemCount - 1 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/yizhuan/erban/avroom/redpackage/send/PrivateRedPackageEditorFragment.kt b/app/src/main/java/com/yizhuan/erban/avroom/redpackage/send/PrivateRedPackageEditorFragment.kt new file mode 100644 index 000000000..2c74e6c4e --- /dev/null +++ b/app/src/main/java/com/yizhuan/erban/avroom/redpackage/send/PrivateRedPackageEditorFragment.kt @@ -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() { + + 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() + 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 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/yizhuan/erban/avroom/redpackage/send/PublicRedPackageEditorFragment.kt b/app/src/main/java/com/yizhuan/erban/avroom/redpackage/send/PublicRedPackageEditorFragment.kt new file mode 100644 index 000000000..649d357ed --- /dev/null +++ b/app/src/main/java/com/yizhuan/erban/avroom/redpackage/send/PublicRedPackageEditorFragment.kt @@ -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() { + + 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 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/yizhuan/erban/avroom/redpackage/send/RedPackageSendDialog2.kt b/app/src/main/java/com/yizhuan/erban/avroom/redpackage/send/RedPackageSendDialog2.kt new file mode 100644 index 000000000..115b6c8e4 --- /dev/null +++ b/app/src/main/java/com/yizhuan/erban/avroom/redpackage/send/RedPackageSendDialog2.kt @@ -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() { + + 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() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/yizhuan/erban/avroom/redpackage/send/RedPackageTypeItemAdapter.kt b/app/src/main/java/com/yizhuan/erban/avroom/redpackage/send/RedPackageTypeItemAdapter.kt new file mode 100644 index 000000000..60531a202 --- /dev/null +++ b/app/src/main/java/com/yizhuan/erban/avroom/redpackage/send/RedPackageTypeItemAdapter.kt @@ -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) : + BaseQuickAdapter( + 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 + ) { + 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) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/yizhuan/erban/avroom/redpackage/send/TabIndicatorAdapter.java b/app/src/main/java/com/yizhuan/erban/avroom/redpackage/send/TabIndicatorAdapter.java new file mode 100644 index 000000000..567c14d33 --- /dev/null +++ b/app/src/main/java/com/yizhuan/erban/avroom/redpackage/send/TabIndicatorAdapter.java @@ -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 mTitleList; + private Context mContext; + + public TabIndicatorAdapter(Context mContext, List 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) { + + } + } + + +} diff --git a/app/src/main/java/com/yizhuan/erban/avroom/widget/MicroView.java b/app/src/main/java/com/yizhuan/erban/avroom/widget/MicroView.java index bc33c63bf..fc57e8e8c 100644 --- a/app/src/main/java/com/yizhuan/erban/avroom/widget/MicroView.java +++ b/app/src/main/java/com/yizhuan/erban/avroom/widget/MicroView.java @@ -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), diff --git a/app/src/main/java/com/yizhuan/erban/base/BaseActivity.java b/app/src/main/java/com/yizhuan/erban/base/BaseActivity.java index 50111e60a..156b0f724 100644 --- a/app/src/main/java/com/yizhuan/erban/base/BaseActivity.java +++ b/app/src/main/java/com/yizhuan/erban/base/BaseActivity.java @@ -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); } diff --git a/app/src/main/java/com/yizhuan/erban/base/BaseDialog.kt b/app/src/main/java/com/yizhuan/erban/base/BaseDialog.kt index ab8b29fd4..62424dd55 100644 --- a/app/src/main/java/com/yizhuan/erban/base/BaseDialog.kt +++ b/app/src/main/java/com/yizhuan/erban/base/BaseDialog.kt @@ -14,6 +14,7 @@ import java.lang.reflect.ParameterizedType abstract class BaseDialog : 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 : RxDialogFragment() { override fun onDestroyView() { super.onDestroyView() _binding = null + this.onDismissListener = null } abstract fun init() diff --git a/app/src/main/java/com/yizhuan/erban/ui/setting/SettingActivity.kt b/app/src/main/java/com/yizhuan/erban/ui/setting/SettingActivity.kt index 00481a7ba..942853677 100644 --- a/app/src/main/java/com/yizhuan/erban/ui/setting/SettingActivity.kt +++ b/app/src/main/java/com/yizhuan/erban/ui/setting/SettingActivity.kt @@ -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(), View.OnClickListener { +class SettingActivity : BaseViewBindingActivity(), 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(), 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(), 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(), 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(), 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(), 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(), 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(), View. dialogManager.dismissDialog() EventBus.getDefault().unregister(this) } + + private fun debug() { +// RedPackageOpenDialog2().show(this) + } } \ No newline at end of file diff --git a/app/src/main/java/com/yizhuan/erban/view/WrapHeightViewPager.kt b/app/src/main/java/com/yizhuan/erban/view/WrapHeightViewPager.kt new file mode 100644 index 000000000..2b1523560 --- /dev/null +++ b/app/src/main/java/com/yizhuan/erban/view/WrapHeightViewPager.kt @@ -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 + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable-xxhdpi/red_package_open_bg.webp b/app/src/main/res/drawable-xxhdpi/red_package_open_bg.webp new file mode 100644 index 000000000..5910f3890 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/red_package_open_bg.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/red_package_open_btn_bg.webp b/app/src/main/res/drawable-xxhdpi/red_package_open_btn_bg.webp new file mode 100644 index 000000000..d160932f2 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/red_package_open_btn_bg.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/red_package_open_btn_get.webp b/app/src/main/res/drawable-xxhdpi/red_package_open_btn_get.webp new file mode 100644 index 000000000..47db77031 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/red_package_open_btn_get.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/red_package_result_bg.webp b/app/src/main/res/drawable-xxhdpi/red_package_result_bg.webp new file mode 100644 index 000000000..878bb2fc3 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/red_package_result_bg.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/red_package_send_bg.webp b/app/src/main/res/drawable-xxhdpi/red_package_send_bg.webp new file mode 100644 index 000000000..7c7c844ba Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/red_package_send_bg.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/red_package_send_ic.webp b/app/src/main/res/drawable-xxhdpi/red_package_send_ic.webp new file mode 100644 index 000000000..07bf608f5 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/red_package_send_ic.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/red_package_send_ic_diamond.webp b/app/src/main/res/drawable-xxhdpi/red_package_send_ic_diamond.webp new file mode 100644 index 000000000..12623a70d Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/red_package_send_ic_diamond.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/red_package_widget_bg.webp b/app/src/main/res/drawable-xxhdpi/red_package_widget_bg.webp new file mode 100644 index 000000000..5161c5206 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/red_package_widget_bg.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/red_package_widget_bg_text.webp b/app/src/main/res/drawable-xxhdpi/red_package_widget_bg_text.webp new file mode 100644 index 000000000..1836c5787 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/red_package_widget_bg_text.webp differ diff --git a/app/src/main/res/drawable/red_package_bg_balance.xml b/app/src/main/res/drawable/red_package_bg_balance.xml new file mode 100644 index 000000000..563c60cf1 --- /dev/null +++ b/app/src/main/res/drawable/red_package_bg_balance.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/red_package_bg_num.xml b/app/src/main/res/drawable/red_package_bg_num.xml new file mode 100644 index 000000000..a072c4bdf --- /dev/null +++ b/app/src/main/res/drawable/red_package_bg_num.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/red_package_bg_type_selected.xml b/app/src/main/res/drawable/red_package_bg_type_selected.xml new file mode 100644 index 000000000..a93199994 --- /dev/null +++ b/app/src/main/res/drawable/red_package_bg_type_selected.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/red_package_ic_add.xml b/app/src/main/res/drawable/red_package_ic_add.xml new file mode 100644 index 000000000..c774d9f61 --- /dev/null +++ b/app/src/main/res/drawable/red_package_ic_add.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/drawable/red_package_ic_close.xml b/app/src/main/res/drawable/red_package_ic_close.xml new file mode 100644 index 000000000..24b3ed834 --- /dev/null +++ b/app/src/main/res/drawable/red_package_ic_close.xml @@ -0,0 +1,22 @@ + + + + diff --git a/app/src/main/res/drawable/red_package_ic_help.xml b/app/src/main/res/drawable/red_package_ic_help.xml new file mode 100644 index 000000000..c3905e0c2 --- /dev/null +++ b/app/src/main/res/drawable/red_package_ic_help.xml @@ -0,0 +1,22 @@ + + + + diff --git a/app/src/main/res/drawable/red_package_open_tips_bg.xml b/app/src/main/res/drawable/red_package_open_tips_bg.xml new file mode 100644 index 000000000..5fe90e3f8 --- /dev/null +++ b/app/src/main/res/drawable/red_package_open_tips_bg.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_99292929_8.xml b/app/src/main/res/drawable/shape_99292929_8.xml new file mode 100644 index 000000000..031263cf0 --- /dev/null +++ b/app/src/main/res/drawable/shape_99292929_8.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/app/src/main/res/drawable/shape_e03654_10.xml b/app/src/main/res/drawable/shape_e03654_10.xml new file mode 100644 index 000000000..05e670a89 --- /dev/null +++ b/app/src/main/res/drawable/shape_e03654_10.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/app/src/main/res/drawable/shape_f8f8fa_8.xml b/app/src/main/res/drawable/shape_f8f8fa_8.xml new file mode 100644 index 000000000..103f85690 --- /dev/null +++ b/app/src/main/res/drawable/shape_f8f8fa_8.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/app/src/main/res/drawable/shape_g_ff6060_ff225c_26_lr.xml b/app/src/main/res/drawable/shape_g_ff6060_ff225c_26_lr.xml new file mode 100644 index 000000000..2e88e8183 --- /dev/null +++ b/app/src/main/res/drawable/shape_g_ff6060_ff225c_26_lr.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/red_package_open_dialog.xml b/app/src/main/res/layout/red_package_open_dialog.xml new file mode 100644 index 000000000..e8cd79f96 --- /dev/null +++ b/app/src/main/res/layout/red_package_open_dialog.xml @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/red_package_open_item_result.xml b/app/src/main/res/layout/red_package_open_item_result.xml new file mode 100644 index 000000000..8578bea04 --- /dev/null +++ b/app/src/main/res/layout/red_package_open_item_result.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/red_package_private_fragment.xml b/app/src/main/res/layout/red_package_private_fragment.xml new file mode 100644 index 000000000..707c0cc84 --- /dev/null +++ b/app/src/main/res/layout/red_package_private_fragment.xml @@ -0,0 +1,225 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/red_package_private_item_type.xml b/app/src/main/res/layout/red_package_private_item_type.xml new file mode 100644 index 000000000..9f321ede9 --- /dev/null +++ b/app/src/main/res/layout/red_package_private_item_type.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/red_package_public_fragment.xml b/app/src/main/res/layout/red_package_public_fragment.xml new file mode 100644 index 000000000..324223a42 --- /dev/null +++ b/app/src/main/res/layout/red_package_public_fragment.xml @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/red_package_send_dialog.xml b/app/src/main/res/layout/red_package_send_dialog.xml new file mode 100644 index 000000000..609e41862 --- /dev/null +++ b/app/src/main/res/layout/red_package_send_dialog.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/red_package_widget.xml b/app/src/main/res/layout/red_package_widget.xml new file mode 100644 index 000000000..fd29d57fc --- /dev/null +++ b/app/src/main/res/layout/red_package_widget.xml @@ -0,0 +1,43 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index eeb12a542..3e3ae8b76 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -703,5 +703,11 @@ #1E686868 #9E9EA8 #001338 - + #FF285C + #322F4D + #EBEEF5 + #94959C + #FFBF461F + #FFFFF87A + #4CFFFFFF diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f2081ca9c..d284f457c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5143,4 +5143,44 @@ 當前無需選擇解鎖禮物 選擇解鎖禮物 + 無門檻紅包 + 所有人均可參與 + 關註紅包 + 關註主播可參與 + 分享紅包 + 分享房間可參與 + 發彈幕紅包 + 發指定彈幕內容 + + 廳內紅包 + 全服紅包 + 紅包金額 + 紅包數量 + + + 搶紅包條件 + 輸入指定彈幕內容(最多10個字符) + 發紅包 + 未搶完的紅包將在12小時後返還你的錢包 + 立即生效 + 限時生效 + 恭喜發財,大吉大利! + 後開搶 + 發彈幕\n搶紅包 + 分享房間\n搶紅包 + 關注主播\n搶紅包 + 未參與活動 + 未參與活動,無法搶該紅包 + 已存入錢包,請到我的收益確認 + 已領取%s/%s個 + 搶紅包 + 請輸入彈幕內容 + 關注主播 + 对外分享房間 + 發送彈幕 + 可搶紅包 + 搶完了,下次早點來哦~ + 該紅包已失效 + ” + \ No newline at end of file diff --git a/app/src/module_mini_world/res/layout/fragment_av_room_game.xml b/app/src/module_mini_world/res/layout/fragment_av_room_game.xml index 52792c507..4c4741517 100644 --- a/app/src/module_mini_world/res/layout/fragment_av_room_game.xml +++ b/app/src/module_mini_world/res/layout/fragment_av_room_game.xml @@ -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" /> + tools:contentDescription="@string/layout_fragment_av_room_game_03" + tools:visibility="visible" /> + + + tools:contentDescription="@string/layout_fragment_av_room_game_04" + tools:visibility="visible" /> + tools:contentDescription="@string/layout_fragment_av_room_game_05" + tools:visibility="visible" /> + app:layout_constraintTop_toTopOf="parent" /> @@ -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" /> + + + tools:contentDescription="@string/layout_fragment_single_room_07" + tools:visibility="visible" /> =2.2.0版本) + case CUSTOM_MSG_SUB_RED_PACKAGE_RECEIVE_ROOM_DIAMOND2: + noticeRoomEvent(msg, RoomEvent.RECEIVE_RED_PACKAGE); + break; } break; diff --git a/core/src/main/java/com/yizhuan/xchat_android_core/im/custom/bean/CustomAttachParser.java b/core/src/main/java/com/yizhuan/xchat_android_core/im/custom/bean/CustomAttachParser.java index e5255c18c..42780e771 100644 --- a/core/src/main/java/com/yizhuan/xchat_android_core/im/custom/bean/CustomAttachParser.java +++ b/core/src/main/java/com/yizhuan/xchat_android_core/im/custom/bean/CustomAttachParser.java @@ -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: diff --git a/core/src/main/java/com/yizhuan/xchat_android_core/im/custom/bean/CustomAttachment.java b/core/src/main/java/com/yizhuan/xchat_android_core/im/custom/bean/CustomAttachment.java index ded3ced58..eb0253bf7 100644 --- a/core/src/main/java/com/yizhuan/xchat_android_core/im/custom/bean/CustomAttachment.java +++ b/core/src/main/java/com/yizhuan/xchat_android_core/im/custom/bean/CustomAttachment.java @@ -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;// 福袋礼物房间飘屏通知 diff --git a/core/src/main/java/com/yizhuan/xchat_android_core/im/custom/bean/RedPackageAttachment.java b/core/src/main/java/com/yizhuan/xchat_android_core/im/custom/bean/RedPackageAttachment.java index 69bfbb912..e48be6a34 100644 --- a/core/src/main/java/com/yizhuan/xchat_android_core/im/custom/bean/RedPackageAttachment.java +++ b/core/src/main/java/com/yizhuan/xchat_android_core/im/custom/bean/RedPackageAttachment.java @@ -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); diff --git a/core/src/main/java/com/yizhuan/xchat_android_core/interceptor/TimeSyncInterceptor.kt b/core/src/main/java/com/yizhuan/xchat_android_core/interceptor/TimeSyncInterceptor.kt new file mode 100644 index 000000000..668e30526 --- /dev/null +++ b/core/src/main/java/com/yizhuan/xchat_android_core/interceptor/TimeSyncInterceptor.kt @@ -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() + } + } +} \ No newline at end of file diff --git a/core/src/main/java/com/yizhuan/xchat_android_core/manager/AvRoomDataManager.java b/core/src/main/java/com/yizhuan/xchat_android_core/manager/AvRoomDataManager.java index 164e4be89..97ac90106 100644 --- a/core/src/main/java/com/yizhuan/xchat_android_core/manager/AvRoomDataManager.java +++ b/core/src/main/java/com/yizhuan/xchat_android_core/manager/AvRoomDataManager.java @@ -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)); } diff --git a/core/src/main/java/com/yizhuan/xchat_android_core/redpackage/RedEnvelopeVO.kt b/core/src/main/java/com/yizhuan/xchat_android_core/redpackage/RedEnvelopeVO.kt index e0f6ba12c..c1ecbe5a0 100644 --- a/core/src/main/java/com/yizhuan/xchat_android_core/redpackage/RedEnvelopeVO.kt +++ b/core/src/main/java/com/yizhuan/xchat_android_core/redpackage/RedEnvelopeVO.kt @@ -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? ) \ No newline at end of file diff --git a/core/src/main/java/com/yizhuan/xchat_android_core/redpackage/RedPackageData.kt b/core/src/main/java/com/yizhuan/xchat_android_core/redpackage/RedPackageData.kt new file mode 100644 index 000000000..7578ece37 --- /dev/null +++ b/core/src/main/java/com/yizhuan/xchat_android_core/redpackage/RedPackageData.kt @@ -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 \ No newline at end of file diff --git a/core/src/main/java/com/yizhuan/xchat_android_core/redpackage/RedPackageInfo.kt b/core/src/main/java/com/yizhuan/xchat_android_core/redpackage/RedPackageInfo.kt index c087d342f..832d42bcd 100644 --- a/core/src/main/java/com/yizhuan/xchat_android_core/redpackage/RedPackageInfo.kt +++ b/core/src/main/java/com/yizhuan/xchat_android_core/redpackage/RedPackageInfo.kt @@ -2,17 +2,19 @@ package com.yizhuan.xchat_android_core.redpackage data class RedPackageInfo( - var redEnvelopeItemVOs: List? = null, - var redEnvelopeVO: RedEnvelopeVO? = null, + var redEnvelopeItemVOs: List? = 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? ) \ No newline at end of file diff --git a/core/src/main/java/com/yizhuan/xchat_android_core/redpackage/RedPackageModel.kt b/core/src/main/java/com/yizhuan/xchat_android_core/redpackage/RedPackageModel.kt index bbaba5f67..058d5e20c 100644 --- a/core/src/main/java/com/yizhuan/xchat_android_core/redpackage/RedPackageModel.kt +++ b/core/src/main/java/com/yizhuan/xchat_android_core/redpackage/RedPackageModel.kt @@ -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 { - 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 { + return api.sendRedPackage( + goldNum, + message, + num, + roomUId, + type, + kind, + validityType, + password + ) + .io2main() + .handleStringData() } - - fun openRedPackage(redEnvelopeId: String): Single { + fun openRedPackage(redEnvelopeId: Long): Single { return api.openRedPackage(redEnvelopeId) - .io2main() - .handleBeanData() + .io2main() + .handleBeanData() } - fun getRedPackage(uid: Long): Observable { - 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 { + return api.getRedPackageData(id) + .io2main() + .handleBeanData() + .toObservable() } - fun setRedPackageSwitch(roomUid: Long):Single { + /** + * 获取房间最新可用红包 + */ + fun getLatestRoomRedPackage(uid: Long): Observable { + 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 { 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> + 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> /** * 开红包 @@ -71,7 +108,7 @@ object RedPackageModel { */ @FormUrlEncoded @POST("/red-envelope/open") - fun openRedPackage(@Field("redEnvelopeId") redEnvelopeId: String): Single> + fun openRedPackage(@Field("redEnvelopeId") redEnvelopeId: Long): Single> /** @@ -80,7 +117,18 @@ object RedPackageModel { * @return */ @GET("/room/red-envelop") - fun getRedPackage(@Query("uid") uid: Long): Single>> + fun getRedPackage( + @Query("isShowKind") isShowKind: Boolean, + @Query("uid") uid: Long + ): Single>> + + /** + * 获取红包详情 + */ + @GET("/red-envelope/get") + fun getRedPackageData( + @Query("redEnvelopeId") redEnvelopeId: Long, + ): Single> /** @@ -90,6 +138,5 @@ object RedPackageModel { fun setRedPackageSwitch(@Path("roomUid") uid: Long): Single> } - } diff --git a/core/src/main/java/com/yizhuan/xchat_android_core/redpackage/RedPackageNotifyInfo.kt b/core/src/main/java/com/yizhuan/xchat_android_core/redpackage/RedPackageNotifyInfo.kt index 7ce2d7a4b..b71239dae 100644 --- a/core/src/main/java/com/yizhuan/xchat_android_core/redpackage/RedPackageNotifyInfo.kt +++ b/core/src/main/java/com/yizhuan/xchat_android_core/redpackage/RedPackageNotifyInfo.kt @@ -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 \ No newline at end of file + /** + * 红包种类 0 旧版本 1 无门槛红包 2 关注红包 3 分享红包 4 弹幕红包 + */ + var kind: Int = 0, + + /** + * 红包可领取时间 + */ + var beginTime: Long = 0, + + /** + * 红包数量 + */ + var redEnvelopeNum: Long = 0, + + /** + * 生效类型 0 立即生效 1 限时生效 + */ + var validityType: Int?, + + ) : Serializable \ No newline at end of file diff --git a/core/src/main/java/com/yizhuan/xchat_android_core/room/core/RoomDataService.kt b/core/src/main/java/com/yizhuan/xchat_android_core/room/core/RoomDataService.kt new file mode 100644 index 000000000..63a323cf5 --- /dev/null +++ b/core/src/main/java/com/yizhuan/xchat_android_core/room/core/RoomDataService.kt @@ -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() { +} \ No newline at end of file diff --git a/core/src/main/java/com/yizhuan/xchat_android_core/support/config/Constants.kt b/core/src/main/java/com/yizhuan/xchat_android_core/support/config/Constants.kt new file mode 100644 index 000000000..9dc4339b2 --- /dev/null +++ b/core/src/main/java/com/yizhuan/xchat_android_core/support/config/Constants.kt @@ -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" +} \ No newline at end of file diff --git a/core/src/main/java/com/yizhuan/xchat_android_core/support/room/RoomAbility.kt b/core/src/main/java/com/yizhuan/xchat_android_core/support/room/RoomAbility.kt new file mode 100644 index 000000000..1005423a1 --- /dev/null +++ b/core/src/main/java/com/yizhuan/xchat_android_core/support/room/RoomAbility.kt @@ -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 { + + protected var roomContext: RoomContext? = null + + protected var roomView: RoomView? = null + + // 自定义RxLifeCycle(临时方案:后续替换为Flow) + private val lifecycleSubject = BehaviorSubject.create() + + /** + * 被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 { + return lifecycleSubject.hide() + } + + override fun bindUntilEvent(event: RoomLifecycle.Event): LifecycleTransformer { + return RxLifecycle.bindUntilEvent(lifecycleSubject, event) + } + + override fun bindToLifecycle(): LifecycleTransformer { + return RxLifecycle.bind(lifecycleSubject, ROOM_LIFECYCLE) + } + + private val ROOM_LIFECYCLE: Function = + 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") + } + } +} \ No newline at end of file diff --git a/core/src/main/java/com/yizhuan/xchat_android_core/support/room/RoomContext.kt b/core/src/main/java/com/yizhuan/xchat_android_core/support/room/RoomContext.kt new file mode 100644 index 000000000..5ff404352 --- /dev/null +++ b/core/src/main/java/com/yizhuan/xchat_android_core/support/room/RoomContext.kt @@ -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() + val contextLiveData: LiveData 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() + + 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) { + } + + /** + * 查找组件 + */ + fun 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) + } +} \ No newline at end of file diff --git a/core/src/main/java/com/yizhuan/xchat_android_core/support/room/RoomHandler.kt b/core/src/main/java/com/yizhuan/xchat_android_core/support/room/RoomHandler.kt new file mode 100644 index 000000000..c8cfbc7d5 --- /dev/null +++ b/core/src/main/java/com/yizhuan/xchat_android_core/support/room/RoomHandler.kt @@ -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() { +} \ No newline at end of file diff --git a/core/src/main/java/com/yizhuan/xchat_android_core/support/room/RoomService.kt b/core/src/main/java/com/yizhuan/xchat_android_core/support/room/RoomService.kt new file mode 100644 index 000000000..ef9ef505c --- /dev/null +++ b/core/src/main/java/com/yizhuan/xchat_android_core/support/room/RoomService.kt @@ -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() { +} \ No newline at end of file diff --git a/core/src/main/java/com/yizhuan/xchat_android_core/support/room/RoomView.kt b/core/src/main/java/com/yizhuan/xchat_android_core/support/room/RoomView.kt new file mode 100644 index 000000000..da5ee0dc2 --- /dev/null +++ b/core/src/main/java/com/yizhuan/xchat_android_core/support/room/RoomView.kt @@ -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() + } +} \ No newline at end of file diff --git a/core/src/main/java/com/yizhuan/xchat_android_core/support/room/RoomWidget.kt b/core/src/main/java/com/yizhuan/xchat_android_core/support/room/RoomWidget.kt new file mode 100644 index 000000000..67410f18e --- /dev/null +++ b/core/src/main/java/com/yizhuan/xchat_android_core/support/room/RoomWidget.kt @@ -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() +} \ No newline at end of file diff --git a/core/src/main/java/com/yizhuan/xchat_android_core/support/room/lifecycle/RoomFullLifecycleObserver.kt b/core/src/main/java/com/yizhuan/xchat_android_core/support/room/lifecycle/RoomFullLifecycleObserver.kt new file mode 100644 index 000000000..7f21d1a93 --- /dev/null +++ b/core/src/main/java/com/yizhuan/xchat_android_core/support/room/lifecycle/RoomFullLifecycleObserver.kt @@ -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) {} +} \ No newline at end of file diff --git a/core/src/main/java/com/yizhuan/xchat_android_core/support/room/lifecycle/RoomLifecycle.kt b/core/src/main/java/com/yizhuan/xchat_android_core/support/room/lifecycle/RoomLifecycle.kt new file mode 100644 index 000000000..e2d3de2eb --- /dev/null +++ b/core/src/main/java/com/yizhuan/xchat_android_core/support/room/lifecycle/RoomLifecycle.kt @@ -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; + } +} \ No newline at end of file diff --git a/core/src/main/java/com/yizhuan/xchat_android_core/support/room/lifecycle/RoomLifecycleObserver.kt b/core/src/main/java/com/yizhuan/xchat_android_core/support/room/lifecycle/RoomLifecycleObserver.kt new file mode 100644 index 000000000..27ac33f92 --- /dev/null +++ b/core/src/main/java/com/yizhuan/xchat_android_core/support/room/lifecycle/RoomLifecycleObserver.kt @@ -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) + +} \ No newline at end of file diff --git a/core/src/main/java/com/yizhuan/xchat_android_core/support/room/lifecycle/RoomLifecycleOwner.kt b/core/src/main/java/com/yizhuan/xchat_android_core/support/room/lifecycle/RoomLifecycleOwner.kt new file mode 100644 index 000000000..a8a5c007b --- /dev/null +++ b/core/src/main/java/com/yizhuan/xchat_android_core/support/room/lifecycle/RoomLifecycleOwner.kt @@ -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 +} \ No newline at end of file diff --git a/core/src/main/java/com/yizhuan/xchat_android_core/support/room/lifecycle/RoomLifecycleRegistry.kt b/core/src/main/java/com/yizhuan/xchat_android_core/support/room/lifecycle/RoomLifecycleRegistry.kt new file mode 100644 index 000000000..5663a4747 --- /dev/null +++ b/core/src/main/java/com/yizhuan/xchat_android_core/support/room/lifecycle/RoomLifecycleRegistry.kt @@ -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 = 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() + } + } +} \ No newline at end of file diff --git a/core/src/main/java/com/yizhuan/xchat_android_core/support/room/lifecycle/rx/RoomRxLifecycleEvent.java b/core/src/main/java/com/yizhuan/xchat_android_core/support/room/lifecycle/rx/RoomRxLifecycleEvent.java new file mode 100644 index 000000000..10fb6b7f3 --- /dev/null +++ b/core/src/main/java/com/yizhuan/xchat_android_core/support/room/lifecycle/rx/RoomRxLifecycleEvent.java @@ -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 +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 1870cbcfd..084476997 100644 --- a/gradle.properties +++ b/gradle.properties @@ -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 \ No newline at end of file diff --git a/library/src/main/java/com/yizhuan/xchat_android_library/net/rxnet/manager/RxNetManager.java b/library/src/main/java/com/yizhuan/xchat_android_library/net/rxnet/manager/RxNetManager.java index f3e6d6813..bcb5187d1 100644 --- a/library/src/main/java/com/yizhuan/xchat_android_library/net/rxnet/manager/RxNetManager.java +++ b/library/src/main/java/com/yizhuan/xchat_android_library/net/rxnet/manager/RxNetManager.java @@ -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) { diff --git a/library/src/module_common/java/com/yizhuan/xchat_android_library/common/application/BaseApp.java b/library/src/module_common/java/com/yizhuan/xchat_android_library/common/application/BaseApp.java index 58ee9d3b5..b8a0fa251 100644 --- a/library/src/module_common/java/com/yizhuan/xchat_android_library/common/application/BaseApp.java +++ b/library/src/module_common/java/com/yizhuan/xchat_android_library/common/application/BaseApp.java @@ -26,6 +26,7 @@ public abstract class BaseApp extends Application{ public static void init(Application application) { gContext = application; + com.chuhai.utils.AppUtils.init(application); } /** diff --git a/library/src/module_utils/java/com/chuhai/utils/ICleared.kt b/library/src/module_utils/java/com/chuhai/utils/ICleared.kt new file mode 100644 index 000000000..63273e7fc --- /dev/null +++ b/library/src/module_utils/java/com/chuhai/utils/ICleared.kt @@ -0,0 +1,14 @@ +package com.chuhai.utils + + +/** + * Created by Max on 2023/10/26 11:50 + * Desc:清除释放统一接口 + **/ +interface ICleared { + + /** + * 清除/释放 + */ + fun onCleared() {} +} \ No newline at end of file diff --git a/library/src/module_utils/java/com/chuhai/utils/log/AndroidLogPrinter.kt b/library/src/module_utils/java/com/chuhai/utils/log/AndroidLogPrinter.kt new file mode 100644 index 000000000..9a027de2c --- /dev/null +++ b/library/src/module_utils/java/com/chuhai/utils/log/AndroidLogPrinter.kt @@ -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) + } +} \ No newline at end of file diff --git a/library/src/module_utils/java/com/chuhai/utils/log/ILog.kt b/library/src/module_utils/java/com/chuhai/utils/log/ILog.kt new file mode 100644 index 000000000..8f748390d --- /dev/null +++ b/library/src/module_utils/java/com/chuhai/utils/log/ILog.kt @@ -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) + } +} \ No newline at end of file diff --git a/library/src/module_utils/java/com/chuhai/utils/log/LogPrinter.kt b/library/src/module_utils/java/com/chuhai/utils/log/LogPrinter.kt new file mode 100644 index 000000000..86f231c74 --- /dev/null +++ b/library/src/module_utils/java/com/chuhai/utils/log/LogPrinter.kt @@ -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) +} \ No newline at end of file diff --git a/library/src/module_utils/java/com/chuhai/utils/log/LogUtil.kt b/library/src/module_utils/java/com/chuhai/utils/log/LogUtil.kt new file mode 100644 index 000000000..a3ce5e7b7 --- /dev/null +++ b/library/src/module_utils/java/com/chuhai/utils/log/LogUtil.kt @@ -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) + } +} \ No newline at end of file diff --git a/library/src/module_utils/java/com/chuhai/utils/spannable/IconTextSpan.java b/library/src/module_utils/java/com/chuhai/utils/spannable/IconTextSpan.java new file mode 100644 index 000000000..994e7a798 --- /dev/null +++ b/library/src/module_utils/java/com/chuhai/utils/spannable/IconTextSpan.java @@ -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); + } +} diff --git a/library/src/module_utils/java/com/chuhai/utils/spannable/RoundBackgroundColorSpan.kt b/library/src/module_utils/java/com/chuhai/utils/spannable/RoundBackgroundColorSpan.kt new file mode 100644 index 000000000..84f2c4dbc --- /dev/null +++ b/library/src/module_utils/java/com/chuhai/utils/spannable/RoundBackgroundColorSpan.kt @@ -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) + } +} \ No newline at end of file diff --git a/library/src/module_utils/java/com/chuhai/utils/spannable/SpannableTextBuilder.kt b/library/src/module_utils/java/com/chuhai/utils/spannable/SpannableTextBuilder.kt new file mode 100644 index 000000000..39d346ef1 --- /dev/null +++ b/library/src/module_utils/java/com/chuhai/utils/spannable/SpannableTextBuilder.kt @@ -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) +} \ No newline at end of file diff --git a/library/src/module_utils/java/com/chuhai/utils/spannable/VerticalImageSpan.kt b/library/src/module_utils/java/com/chuhai/utils/spannable/VerticalImageSpan.kt new file mode 100644 index 000000000..c428c7770 --- /dev/null +++ b/library/src/module_utils/java/com/chuhai/utils/spannable/VerticalImageSpan.kt @@ -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() + } +} \ No newline at end of file diff --git a/nim_uikit/res/values/colors.xml b/nim_uikit/res/values/colors.xml index 92a781bcb..b2daa3da4 100644 --- a/nim_uikit/res/values/colors.xml +++ b/nim_uikit/res/values/colors.xml @@ -79,4 +79,6 @@ #B3333333 #FFF4F4FA + + \ No newline at end of file