diff --git a/.run/app.run.xml b/.run/app.run.xml
new file mode 100644
index 00000000..76af515f
--- /dev/null
+++ b/.run/app.run.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/my-release-key.jks b/app/my-release-key.jks
new file mode 100644
index 00000000..ec173e42
Binary files /dev/null and b/app/my-release-key.jks differ
diff --git a/app/releas/release/output-metadata.json b/app/releas/release/output-metadata.json
new file mode 100644
index 00000000..975d1e36
--- /dev/null
+++ b/app/releas/release/output-metadata.json
@@ -0,0 +1,37 @@
+{
+ "version": 3,
+ "artifactType": {
+ "type": "APK",
+ "kind": "Directory"
+ },
+ "applicationId": "com.qxcm.qxlive",
+ "variantName": "releasRelease",
+ "elements": [
+ {
+ "type": "SINGLE",
+ "filters": [],
+ "attributes": [],
+ "versionCode": 1,
+ "versionName": "1.0.0.0",
+ "outputFile": "羽声_1.0.0.0_1.apk"
+ }
+ ],
+ "elementType": "File",
+ "baselineProfiles": [
+ {
+ "minApi": 28,
+ "maxApi": 30,
+ "baselineProfiles": [
+ "baselineProfiles/1/羽声_1.0.0.0_1.dm"
+ ]
+ },
+ {
+ "minApi": 31,
+ "maxApi": 2147483647,
+ "baselineProfiles": [
+ "baselineProfiles/0/羽声_1.0.0.0_1.dm"
+ ]
+ }
+ ],
+ "minSdkVersionForDexing": 24
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/qxcm/qxlive/AppContext.java b/app/src/main/java/com/qxcm/qxlive/AppContext.java
new file mode 100644
index 00000000..b6e8f669
--- /dev/null
+++ b/app/src/main/java/com/qxcm/qxlive/AppContext.java
@@ -0,0 +1,103 @@
+package com.qxcm.qxlive;
+
+import com.alibaba.android.arouter.launcher.ARouter;
+import com.hjq.toast.ToastUtils;
+import com.xscm.moduleutil.base.CommonAppContext;
+
+
+/**
+ * Created by cxf on 2017/8/3.
+ */
+
+public class AppContext extends CommonAppContext {
+
+ private boolean mBeautyInited;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ ToastUtils.init(this);
+ ARouter.init(this);
+
+ /* mqttClient = MyMQTTClient.getInstance(this);
+ mqttClient.initialize("tcp://81.70.45.221:1883");
+ new Thread(() -> {
+ boolean connected = mqttClient.connect();
+ runOnUiThread(() -> {
+ if (connected) {
+ } else {
+ com.blankj.utilcode.util.ToastUtils.showShort("MQTT连接失败");
+ }
+ });
+ }).start();*/
+
+// ToastUtils.setInterceptor(new IToastInterceptor() {
+// @Override
+// public boolean intercept(CharSequence charSequence) {
+// return !CommonAppContext.getInstance().isFront();
+// }
+// });
+// L.setDeBug(BuildConfig.DEBUG);
+ }
+ public static void initSdk() {
+// CommonAppContext context = CommonAppContext.getInstance();
+
+// if (BuildConfig.DEBUG) {
+// L.e("应用签名:" + context.getAppSignature());
+// //L.e("facebook散列秘钥------>" + context.getFacebookHashKey());
+// }
+// //腾讯云直播鉴权url
+// String liveLicenceUrl = "https://license.vod2.myqcloud.com/license/v2/1346816652_1/v_cube.license";
+// //腾讯云直播鉴权key
+// String liveKey = "c30f209835056ba00f738a014ca4448a";
+// //腾讯云视频鉴权url
+// String ugcLicenceUrl = "https://license.vod2.myqcloud.com/license/v2/1346816652_1/v_cube.license";
+// //腾讯云视频鉴权key
+// String ugcKey = "c30f209835056ba00f738a014ca4448a";
+// TXLiveBase.getInstance().setDebug(BuildConfig.DEBUG);
+// TXLiveBase.getInstance().setLicence(context, liveLicenceUrl, liveKey, ugcLicenceUrl, ugcKey);
+// //初始化腾讯bugly
+// CrashReport.initCrashReport(context);
+// CrashReport.setAppVersion(context, CommonAppConfig.getInstance().getVersion());
+// //初始化ShareSdk
+// MobSDK.init(context);
+// MobSDK.submitPolicyGrantResult(true);
+// //初始化IM
+// ImMessageUtil.getInstance().init();
+// //初始化腾讯TPNS 移动推送
+// TpnsUtil.register(BuildConfig.DEBUG);
+// //初始化友盟统计
+// UmengUtil.init(context, BuildConfig.DEBUG);
+// //OpenInstall
+// OpenInstall.init(context);
+
+
+
+ }
+
+ /**
+ * 初始化美狐
+ */
+ public void initBeautySdk(String beautyAppId, String beautyKey) {
+// if (!TextUtils.isEmpty(beautyAppId) && !TextUtils.isEmpty(beautyKey)) {
+// if (!mBeautyInited) {
+// mBeautyInited = true;
+// if (CommonAppConfig.isYunBaoApp()) {
+// beautyAppId = DecryptUtil.decrypt(beautyAppId);
+// beautyKey = DecryptUtil.decrypt(beautyKey);
+// }
+// MHSDK.init(this, beautyAppId, beautyKey);
+// CommonAppConfig.getInstance().setMhBeautyEnable(true);
+// L.e("美狐初始化----AppId--->" + beautyAppId + "---AppKey--->" + beautyKey);
+// }
+// } else {
+// CommonAppConfig.getInstance().setMhBeautyEnable(false);
+// }
+ }
+
+ @Override
+ public void startInitSdk() {
+ initSdk();
+ }
+
+}
diff --git a/app/src/main/java/com/qxcm/qxlive/LaunchContacter.java b/app/src/main/java/com/qxcm/qxlive/LaunchContacter.java
new file mode 100644
index 00000000..f92d468a
--- /dev/null
+++ b/app/src/main/java/com/qxcm/qxlive/LaunchContacter.java
@@ -0,0 +1,20 @@
+package com.xscm.midi;
+
+import android.app.Activity;
+
+import com.xscm.moduleutil.activity.IPresenter;
+import com.xscm.moduleutil.activity.IView;
+
+public final class LaunchContacter {
+
+ public interface View extends IView {
+
+ }
+
+ public interface ILoginPre extends IPresenter {
+
+ void oauthLogin(String login_token);
+
+ void address_ip(String address_ip);
+ }
+}
diff --git a/app/src/main/java/com/qxcm/qxlive/LaunchPageActivity.java b/app/src/main/java/com/qxcm/qxlive/LaunchPageActivity.java
new file mode 100644
index 00000000..de4a7447
--- /dev/null
+++ b/app/src/main/java/com/qxcm/qxlive/LaunchPageActivity.java
@@ -0,0 +1,191 @@
+package com.qxcm.qxlive;
+
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.PersistableBundle;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.alibaba.android.arouter.launcher.ARouter;
+import com.qxcm.qxlive.databinding.ActivityLaunchPageBinding;
+import com.xscm.modulelogin.activity.ImproveInfoActivity;
+import com.xscm.moduleutil.activity.BaseAppCompatActivity;
+import com.xscm.moduleutil.activity.WebViewActivity;
+import com.xscm.moduleutil.base.AppStateListener;
+import com.xscm.moduleutil.base.AppStateManager;
+import com.xscm.moduleutil.base.CommonAppContext;
+import com.xscm.moduleutil.dialog.PolicyDialog;
+import com.xscm.moduleutil.utils.ARouteConstants;
+import com.xscm.moduleutil.utils.SpUtil;
+/**
+ APP准备启动
+ *
+ * _oo0oo_
+ * o8888888o
+ * 88" . "88
+ * (| -_- |)
+ * 0\ = /0
+ * ___/`---'\___
+ * .' \\| |// '.
+ * / \\||| : |||// \
+ * / _||||| -卍-|||||- \
+ * | | \\\ - /// | |
+ * | \_| ''\---/'' |_/ |
+ * \ .-\__ '-' ___/-. /
+ * ___'. .' /--.--\ `. .'___
+ * ."" '< `.___\_<|>_/___.' >' "".
+ * | | : `- \`.;`\ _ /`;.`/ - ` : | |
+ * \ \ `_. \_ __\ /__ _/ .-` / /
+ *=====`-.____`.___ \_____/___.-`___.-'=====
+ * `=---='
+ *
+ * 佛祖保佑 永无BUG
+ * 佛曰:
+ * 写字楼里写字间,写字间里程序员;
+ * 程序人员写程序,又拿程序换酒钱。
+ * 酒醒只在网上坐,酒醉还来网下眠;
+ * 酒醉酒醒日复日,网上网下年复年。
+ * 但愿老死电脑间,不愿鞠躬老板前;
+ * 奔驰宝马贵者趣,公交自行程序员。
+ * 别人笑我忒疯癫,我笑自己命太贱;
+ * 不见满街漂亮妹,哪个归得程序员?
+ */
+public class LaunchPageActivity extends BaseAppCompatActivity {
+ private Handler handler;
+ private PolicyDialog policyDialog;
+ private AppStateListener appStateListener;
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
+ super.onCreate(savedInstanceState, persistentState);
+
+ // 获取Application实例并设置监听器
+ CommonAppContext app = (CommonAppContext) getApplication();
+ appStateListener = AppStateManager.getInstance();
+ app.setAppStateListener(appStateListener);
+
+ if (!isTaskRoot()) {
+
+ if (SpUtil.getUnderagePassword() != null && !SpUtil.getUnderagePassword().isEmpty()) {
+// ARouter.getInstance().build(ARouteConstants.H5).withString("url", CommonAppContext.getInstance().getCurrentEnvironment().getH5Url() + "/web/index.html#/pages/feedback/teenage?id=" + SpUtil.getToken())
+// .withString("type", "1").navigation();//type==1:青少年模式
+
+ Intent intent = new Intent(this, WebViewActivity.class);
+ intent.putExtra("url", CommonAppContext.getInstance().getCurrentEnvironment().getH5Url() + "/web/index.html#/pages/feedback/teenage?id=" + SpUtil.getToken());
+ intent.putExtra("title", "1");
+ startActivity(intent);
+ } else {
+ // 如果没有设置青少年模式,应该导航到首页
+ try {
+ ARouter.getInstance().build(ARouteConstants.ME).navigation();
+ } catch (Exception e) {
+ Log.e("LaunchPageActivity", "导航到首页失败", e);
+ }
+ }
+ finish();
+ return;
+ }
+// if (shouldRestoreRoom()) {
+// ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("form", "首页").withString("roomId", CommonAppContext.getInstance().playId).navigation();
+// }
+
+ }
+
+ private boolean shouldRestoreRoom() {
+ // 检查是否应该恢复房间:
+ // 1. 应用有正在播放的房间
+ // 2. 应用应该显示房间
+ // 3. 应用是从后台恢复的(通过检查 CommonAppContext 状态)
+ return CommonAppContext.getInstance().isPlaying
+ && CommonAppContext.getInstance().isShow;
+ }
+
+ @Override
+ protected void initData() {
+ handler = new Handler();
+ // 定义一个Runnable
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ initLogin();
+ }
+ };
+ if (SpUtil.isAgreePolicy()) {
+ initLogin();
+// //延迟1.5秒执行
+// handler.postDelayed(runnable, 2500);
+ } else {
+ policyDialog = new PolicyDialog(this);
+ policyDialog.setCancelable(false);
+ policyDialog.setCanceledOnTouchOutside(false);
+ policyDialog.setPolicyClickListener(new PolicyDialog.PolicyClickListener() {
+ @Override
+ public void policyAgree() {
+ SpUtil.completeAgreePolicy();
+// CommonAppContext.getInstance().initialization();
+ initLogin();
+ }
+
+ @Override
+ public void policyExit() {
+ finish();
+ }
+ });
+ policyDialog.show();
+ }
+
+
+ }
+
+ @Override
+ protected void onDestroy() {
+ // 如果 PolicyDialog 仍在显示,则 dismiss 它
+ if (policyDialog != null && policyDialog.isShowing()) {
+ policyDialog.dismiss();
+ policyDialog = null;
+ }
+
+ // 移除所有待处理的回调以防止内存泄漏
+ if (handler != null) {
+ handler.removeCallbacksAndMessages(null);
+ }
+
+ super.onDestroy();
+ }
+
+ @Override
+ protected void initView() {
+
+ }
+
+ private void initLogin() {
+ // 在启动新 Activity 前先关闭对话框
+ if (policyDialog != null && policyDialog.isShowing()) {
+ policyDialog.dismiss();
+ policyDialog = null;
+ }
+ startActivity(new Intent(this, PasswordLoginActivity.class));
+ finish();
+
+ }
+
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.activity_launch_page;
+ }
+
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (!isTaskRoot()) {
+ finish();
+ return;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/qxcm/qxlive/LaunchPresenter.java b/app/src/main/java/com/qxcm/qxlive/LaunchPresenter.java
new file mode 100644
index 00000000..fdad3f6e
--- /dev/null
+++ b/app/src/main/java/com/qxcm/qxlive/LaunchPresenter.java
@@ -0,0 +1,99 @@
+package com.xscm.midi;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+import com.blankj.utilcode.util.ToastUtils;
+import com.xscm.modulelogin.activity.ImproveInfoActivity;
+import com.xscm.modulelogin.activity.SwitchAccountsActivity;
+import com.xscm.modulemain.activity.MainActivity;
+import com.xscm.moduleutil.base.CommonAppContext;
+import com.xscm.moduleutil.bean.UserBean;
+import com.xscm.moduleutil.http.BaseObserver;
+import com.xscm.moduleutil.presenter.BasePresenter;
+
+import org.greenrobot.eventbus.EventBus;
+
+import java.util.List;
+
+import io.reactivex.disposables.Disposable;
+
+public class LaunchPresenter extends BasePresenter implements LaunchContacter.ILoginPre {
+
+ public LaunchPresenter(LaunchContacter.View view, Context context) {
+ super(view, context);
+ }
+
+ @Override
+ public void oauthLogin(String login_token) {
+ api.oauthLogin(login_token, new BaseObserver>() {
+
+ @Override
+ public void onSubscribe(Disposable d) {
+ addDisposable(d);
+ }
+
+ @Override
+ public void onNext(List userBeans) {
+ loginSuccess(userBeans);
+ }
+ });
+ }
+
+ @Override
+ public void address_ip(String address_ip) {
+ api.address_ip(address_ip, new BaseObserver() {
+ @Override
+ public void onSubscribe(Disposable d) {
+// addDisposable(d);
+ }
+
+ @Override
+ public void onNext(String s) {
+
+ }
+ });
+ }
+
+ public void loginSuccess(List userBean) {
+ if (userBean==null) {
+ ToastUtils.showShort("登录失败请重试");
+ return;
+ }
+ if (userBean.size()==1) {
+
+ CommonAppContext.getInstance().setUser(userBean.get(0));
+
+// PreferencesUtils.putString( CommonAppContext.getInstance(), "mobile", userBean.get());
+
+ if (isViewAttach()) {
+// MvpRef.get().disLoadings();
+ }
+ if (userBean.get(0).getSex() == 0) {
+ try {
+ Intent intent = new Intent(com.blankj.utilcode.util.ActivityUtils.getTopActivity(), ImproveInfoActivity.class);
+
+ Bundle bundle = new Bundle();
+ bundle.putSerializable("userBean", userBean.get(0));
+ intent.putExtras(bundle);
+ com.blankj.utilcode.util.ActivityUtils.startActivity(intent);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ } else {
+ com.blankj.utilcode.util.ActivityUtils.startActivity(MainActivity.class);
+ }
+ }else {
+ Intent intent=new Intent(com.blankj.utilcode.util.ActivityUtils.getTopActivity(), SwitchAccountsActivity.class);
+ Bundle bundle=new Bundle();
+ bundle.putSerializable("userBean", userBean.get(0));
+ intent.putExtras(bundle);
+ com.blankj.utilcode.util.ActivityUtils.startActivity(intent);
+
+ }
+ EventBus.getDefault().post(userBean);
+// EventBus.getDefault().post(new SplashFinishEvent());
+// AppLog.setUserUniqueID(userBean.getUser_id()); // 设置您自己的账号体系ID, 并保证其唯一性 !
+ }
+}
diff --git a/app/src/main/java/com/qxcm/qxlive/PasswordLoginActivity.java b/app/src/main/java/com/qxcm/qxlive/PasswordLoginActivity.java
new file mode 100644
index 00000000..6d105f8d
--- /dev/null
+++ b/app/src/main/java/com/qxcm/qxlive/PasswordLoginActivity.java
@@ -0,0 +1,662 @@
+package com.qxcm.qxlive;
+
+import static android.view.View.VISIBLE;
+
+import android.content.Intent;
+import android.graphics.Color;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.CountDownTimer;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.text.method.PasswordTransformationMethod;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.ImageView;
+
+import androidx.annotation.Nullable;
+
+import com.alibaba.fastjson.JSON;
+import com.alipay.sdk.app.AuthTask;
+import com.blankj.utilcode.util.LogUtils;
+import com.blankj.utilcode.util.ThreadUtils;
+import com.hjq.toast.ToastUtils;
+import com.mobile.auth.gatewayauth.AuthUIConfig;
+import com.mobile.auth.gatewayauth.PhoneNumberAuthHelper;
+import com.mobile.auth.gatewayauth.PreLoginResultListener;
+import com.mobile.auth.gatewayauth.TokenResultListener;
+import com.mobile.auth.gatewayauth.model.TokenRet;
+import com.qxcm.qxlive.databinding.ActivityPasswordLoginBinding;
+import com.xscm.modulelogin.activity.ImproveInfoActivity;
+import com.xscm.modulelogin.even.LoginFinishEvent;
+import com.xscm.modulelogin.present.LoginContacter;
+import com.xscm.modulelogin.present.LoginPresenter;
+import com.xscm.modulemain.activity.MainActivity;
+import com.xscm.moduleutil.activity.BaseMvpActivity;
+import com.xscm.moduleutil.base.CommonAppContext;
+import com.xscm.moduleutil.bean.ThemeBean;
+import com.xscm.moduleutil.bean.UserBean;
+import com.xscm.moduleutil.utils.BarUtils;
+import com.xscm.moduleutil.utils.PreferencesUtils;
+import com.xscm.moduleutil.utils.logger.Logger;
+import com.tencent.mm.opensdk.modelbase.BaseResp;
+import com.tencent.mm.opensdk.modelmsg.SendAuth;
+import com.tencent.mm.opensdk.openapi.IWXAPI;
+import com.tencent.mm.opensdk.openapi.WXAPIFactory;
+
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class PasswordLoginActivity extends BaseMvpActivity implements LoginContacter.View, View.OnClickListener {
+
+ private CountDownTimer mTimer;
+ public String mobile;
+ private int type;//1:验证码登录2:密码登录
+ boolean isPasswordVisible = false;
+ public PhoneNumberAuthHelper phoneNumberAuthHelper;
+ private TokenResultListener tokenResultListener;
+ private boolean canOnePass;
+ @Override
+ protected void initData() {
+
+ }
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.activity_password_login;
+ }
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ // 检查是否已经创建过该Activity
+ if (!isTaskRoot()) {
+ Intent intent = getIntent();
+ if (intent != null) {
+ String action = intent.getAction();
+ // 如果是从Launcher启动的,并且Activity已经存在,则finish当前实例
+ if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && Intent.ACTION_MAIN.equals(action)) {
+ finish();
+ return;
+ }
+ }
+ }
+ // 新增版本检查逻辑
+ checkAppVersion();
+ super.onCreate(savedInstanceState);
+// EventBus.getDefault().register(this);
+// AppLogUtil.reportAppLog(AppLogEvent.A0101);
+ }
+ private void checkAppVersion() {
+ // 获取当前版本号
+ int currentVersionCode = 0;
+ try {
+ currentVersionCode = getPackageManager().getPackageInfo(getPackageName(), 0).versionCode;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ // 从SharedPreferences中获取上次运行的版本号
+ int lastVersionCode = PreferencesUtils.getInt(CommonAppContext.getInstance(), "last_version_code", 0);
+
+ // 如果版本号不同,说明应用已更新
+ if (currentVersionCode != lastVersionCode) {
+ // 保存当前版本号
+ PreferencesUtils.putInt(CommonAppContext.getInstance(), "last_version_code", currentVersionCode);
+
+ // 如果不是首次安装,则需要清理任务栈
+ if (lastVersionCode != 0) {
+ clearTaskAndRestart();
+ }
+ }
+ }
+
+ private void clearTaskAndRestart() {
+ // 清理所有Activity并重启应用
+ Intent intent = new Intent(this, PasswordLoginActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ startActivity(intent);
+ finish();
+ }
+
+ @Override
+ protected void onDestroy() {
+ EventBus.getDefault().unregister(this);
+ if (phoneNumberAuthHelper!=null) {
+ phoneNumberAuthHelper.hideLoginLoading();
+ //获取成功 dimiss就去登录、登录成功
+ phoneNumberAuthHelper.quitLoginPage();
+ }
+ super.onDestroy();
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void finishEvent(LoginFinishEvent event) {
+ finish();
+ }
+
+
+ @Override
+ protected void initView() {
+ super.initView();
+ MvpPre.getThemeData();
+ initQuickLogin();
+ checkOnePass();
+
+ BarUtils.setStatusBarAlpha(this, 0);
+ mBinding.edPhone.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ setUpLoginBtn();
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+
+ }
+ });
+ mBinding.edPhone.setOnFocusChangeListener(new View.
+ OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (mBinding.edPhone == null) {
+ return;
+ }
+ if (hasFocus) {
+ mBinding.relPhone.setSelected(true);
+ } else {
+ // 此处为失去焦点时的处理内容
+ mBinding.relPhone.setSelected(false);
+ }
+ }
+ });
+ mBinding.edPhone.setText(PreferencesUtils.getString(CommonAppContext.getInstance(), "mobile"));
+ if (!TextUtils.isEmpty(mobile)) {
+ mBinding.edPhone.setText(mobile);
+ }
+ mBinding.tvCodeText.setOnClickListener(this::onClick);
+ mBinding.flLogin.setOnClickListener(this::onClick);
+ mBinding.tvYhxy.setOnClickListener(this::onClick);
+ mBinding.tvYsxy.setOnClickListener(this::onClick);
+ mBinding.ivZfb.setOnClickListener(this::onClick);
+ mBinding.ivWeixin.setOnClickListener(this::onClick);
+ mBinding.tvSendCode.setOnClickListener(this::onClick);
+ mBinding.ivEye.setOnClickListener(this::onClick);
+
+ if (mBinding.tvCodeText.getText().equals("切换密码登录")){
+ type=1;
+ }else {
+ type=2;
+ }
+
+ }
+
+ private void initQuickLogin() {
+ tokenResultListener = new TokenResultListener() {
+ @Override
+ public void onTokenSuccess(String s) {
+ Logger.e("onTokenSuccess", s);
+ ThreadUtils.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ TokenRet tokenRet = null;
+ try {
+ tokenRet = JSON.parseObject(s, TokenRet.class);
+ if (tokenRet != null && ("600000").equals(tokenRet.getCode())) {
+
+ LogUtils.e("@@@",tokenRet.getToken());
+ MvpPre.oauthLoginLogin(tokenRet.getToken());
+// phoneNumberAuthHelper.hideLoginLoading();
+// //获取成功 dimiss就去登录、登录成功
+// phoneNumberAuthHelper.quitLoginPage();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onTokenFailed(String s) {
+ Logger.e("onTokenFailed", s);
+ ThreadUtils.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ phoneNumberAuthHelper.hideLoginLoading();
+ phoneNumberAuthHelper.quitLoginPage();
+ }
+ });
+ }
+ };
+
+ try {
+ phoneNumberAuthHelper = PhoneNumberAuthHelper.getInstance(getApplicationContext(), null);
+ phoneNumberAuthHelper.getReporter().setLoggerEnable(true);
+// phoneNumberAuthHelper.setAuthSDKInfo("6rdWuz058oq5OahdbFiGEybUcdahd12J83L34Uc7MrPIrxtFG+rXiwDvRcqNvjwbClbbmvMrmxKVkIysFByBsl0Qe9kqd2w8T/nhK5G6eXXlk2V9AjYCieIU+jRnjZBB+Cfechr6rCGJ2aeBARIsXcRPW7wm9WFK9euh5T+v6Pyte68yNaNdcYCll3+U4/uCEog7HygCnMIbAU+kqoPdmn2H+51YOHW+VsnsHd4w1+I3f8Tt0xLIXGM4GWnQueZ5GR46GTWiSYMy8dCIh9SPIMRyC91GosVcfGPMJSdcXqc=");
+ phoneNumberAuthHelper.setAuthSDKInfo(((CommonAppContext) getApplication()).getCurrentEnvironment().getALI_AUTH_KEY());
+ phoneNumberAuthHelper.checkEnvAvailable(2);
+
+ } catch (Throwable ignored) {
+ Logger.e("initAuthSDK", ignored);
+ }
+ }
+
+
+
+ private void checkOnePass() {
+ if (!TextUtils.isEmpty(CommonAppContext.getInstance().getToken()) && !TextUtils.isEmpty(CommonAppContext.getInstance().getUser().getTencent_im())) {
+ isRoot();
+ return;
+ }
+ showLoadings();
+ if (phoneNumberAuthHelper != null) {
+ phoneNumberAuthHelper.accelerateLoginPage(3 * 1000, new PreLoginResultListener() {
+ @Override
+ public void onTokenSuccess(String s) {
+ Logger.e("onTokenSuccess", s);
+ canOnePass = true;
+ isRoot();
+ }
+
+ @Override
+ public void onTokenFailed(String s, String s1) {
+ Logger.e("onTokenFailed", "错误信息" + s, s1);
+ isRoot();
+ }
+ });
+ } else {
+ canOnePass = false;
+ isRoot();
+ }
+ }
+
+ private void isRoot() {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ disLoadings();
+ if (!PasswordLoginActivity.this.isTaskRoot()) {
+ Intent intent = getIntent();
+ if (intent != null) {
+ String action = intent.getAction();
+ if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && Intent.ACTION_MAIN.equals(action)) {
+ finish();
+ } else {
+ goNextActivity();
+ }
+ } else {
+ goNextActivity();
+ }
+ } else {
+ goNextActivity();
+ }
+ }
+ });
+ }
+
+ private void goNextActivity() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ if (isFinishing() || isDestroyed()) {
+ return;
+ }
+ }
+ if (TextUtils.isEmpty(CommonAppContext.getInstance().getToken()) || TextUtils.isEmpty(CommonAppContext.getInstance().getUser().getTencent_im())) {
+ Logger.e("SplashEnd", "ARouters.CODE_LOGIN");
+ if (canOnePass) {
+ phoneNumberAuthHelper.setAuthListener(tokenResultListener);
+ doOnePass();
+ } else {
+
+ }
+ } else {
+ Logger.e("SplashEnd", "ARouters.MAIN");
+
+ UserBean userBean = CommonAppContext.getInstance().getUser();
+
+// TUILogin.login(getBaseContext(), CommonAppContext.getInstance().getCurrentEnvironment().getSdkAppId(), "u"+userBean.getUser_id(), userBean.getTencent_im(), new TUICallback() {
+// @Override
+// public void onError(final int code, final String desc) {
+// LogUtils.e("@@@",code,"描述:",desc);
+// }
+// @Override
+// public void onSuccess() {
+// LogUtils.e("@@@","成功");
+//
+// }
+// });
+ if (userBean.getSex() == 0) {
+ Intent intent = new Intent(this, ImproveInfoActivity.class);
+ Bundle bundle = new Bundle();
+ bundle.putSerializable("userBean", userBean);
+ intent.putExtras(bundle);
+ startActivity(intent);
+ finish();
+ } else {
+ EventBus.getDefault().post(userBean);
+ startActivity(new Intent(this, MainActivity.class));
+ finish();
+ }
+// finish();
+ }
+ }
+
+ private void doOnePass() {
+ AuthUIConfig authUIConfig = new AuthUIConfig.Builder()
+ //导航栏
+ .setNavColor(Color.TRANSPARENT)
+ .setNavReturnHidden(true)
+ .setNavHidden(true)
+ .setNavText("")
+ .setNavTextColor(Color.BLACK)
+ .setWebNavColor(Color.TRANSPARENT)
+ .setWebNavTextColor(Color.BLACK)
+ .setWebNavReturnImgPath("ic_topbar_back_dark")
+ //状态栏区
+ .setStatusBarColor(Color.TRANSPARENT)
+ .setLightColor(false)
+ .setWebViewStatusBarColor(Color.TRANSPARENT)
+ .setStatusBarUIFlag(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
+ //logo区
+ .setLogoImgPath("login_log")
+ .setLogoWidth(142)
+ .setLogoHeight(142)
+ .setLogoOffsetY(54)
+ .setLogoHidden(false)
+ .setLogoScaleType(ImageView.ScaleType.FIT_XY)
+ //Slogan
+ .setSloganText("本机号码")
+ .setSloganTextColor(Color.parseColor("#A8A8A8"))
+ .setSloganTextSize(12)
+ .setSloganOffsetY(236)
+ //掩码栏
+ .setNumberColor(Color.BLACK)
+ .setNumberSize(30)
+ .setNumFieldOffsetY(263)
+ .setNumberLayoutGravity(Gravity.CENTER_HORIZONTAL)
+ //登录按钮
+ .setLogBtnText("一键登录")
+ .setLogBtnWidth(300)
+ .setLogBtnHeight(42)
+ .setLogBtnOffsetY(325)
+ .setLogBtnTextSize(14)
+ .setLogBtnTextColor(getResources().getColor(R.color.white))
+// .setLogBtnBackgroundPath("theme_bg")
+ .setLogBtnBackgroundDrawable(getResources().getDrawable(com.xscm.moduleutil.R.mipmap.login_btn_bg))
+ //切换到其他方式
+ .setSwitchAccText("其他登录方式")
+ .setSwitchAccTextColor(Color.parseColor("#333333"))
+ .setSwitchAccTextSize(12)
+ .setSwitchOffsetY(390)
+ //协议栏
+ .setAppPrivacyOne("《用户协议》", CommonAppContext.getInstance().getCurrentEnvironment().getServerUrl()+"/api/Page/page_show?id=6")
+ .setAppPrivacyTwo("《隐私协议》", CommonAppContext.getInstance().getCurrentEnvironment().getServerUrl()+"/api/Page/page_show?id=4")
+ .setAppPrivacyColor(Color.parseColor("#000000"), Color.parseColor("#22BB79"))
+ .setPrivacyBefore("登录即代表同意")
+ .setPrivacyEnd("并授权获得号码")
+ .setPrivacyTextSize(11)
+ .setPrivacyAlertContentVerticalMargin(20)
+ .setPrivacyMargin(39)
+ .setPrivacyState(true)
+ .setCheckboxHidden(false)
+ .setCheckedImgPath("ic_agreement_selected")
+ .setUncheckedImgPath("ic_agreement_unselect")
+ .setPrivacyOffsetX(2)
+ .setVendorPrivacyPrefix("《")
+ .setVendorPrivacySuffix("》")
+ .setProtocolAction("com.qxcm.qxlive.PROTOCOL_WEBVIEW")//跳转到自定义的页面展示隐私协议
+ .setPackageName("com.qxcm.qxlive")
+ //全页面属性
+ .setAuthPageActIn("in_activity", "out_activity")
+ .setAuthPageActOut("in_activity", "out_activity")
+// .setPageBackgroundPath("log_bj")
+ .setPageBackgroundDrawable(getResources().getDrawable(com.xscm.moduleutil.R.mipmap.log_bj))
+ .create();
+ phoneNumberAuthHelper.setAuthUIConfig(authUIConfig);
+ phoneNumberAuthHelper.getLoginToken(getApplicationContext(), 5000);
+ }
+
+ @Override
+ protected LoginPresenter bindPresenter() {
+ return new LoginPresenter(this, this);
+ }
+
+
+
+ @Override
+ public void showLoadings() {
+ showLoading();
+ }
+
+ @Override
+ public void disLoadings() {
+ disLoading();
+ }
+
+ private void setUpLoginBtn() {
+ String text = mBinding.edPhone.getText().toString();
+ if (text.length() == 11) {
+ mBinding.flLogin.setEnabled(true);
+ mBinding.ivLoginBg.setAlpha(1f);
+ } else {
+ mBinding.ivLoginBg.setAlpha(0.3f);
+ mBinding.flLogin.setEnabled(false);
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+ if (id == R.id.tv_code_text) {
+// Intent intent = new Intent(this, LoginActivity.class);
+// intent.putExtra("mobile", mBinding.edPhone.getText().toString());
+// startActivity(intent);
+// finish();
+
+ if (mBinding.tvCodeText.getText().equals("切换密码登录")){
+ mBinding.rlPassCode.setVisibility(VISIBLE);
+ mBinding.rlCode.setVisibility(View.GONE);
+ mBinding.tvCodeText.setText("切换验证码登录");
+ mBinding.tvLoginText.setText("密码登录");
+ type=2;
+ }else {
+ mBinding.rlPassCode.setVisibility(View.GONE);
+ mBinding.rlCode.setVisibility(VISIBLE);
+ mBinding.tvCodeText.setText("切换密码登录");
+ mBinding.tvLoginText.setText("验证码登录");
+ type=1;
+ }
+
+
+ }
+ else if (id == R.id.fl_login) {
+ if (!mBinding.cbPrivacy.isChecked()) {
+ ToastUtils.show("请先勾选服务条款");
+ return;
+ }
+ String phone = mBinding.edPhone.getText().toString().trim();
+ if (TextUtils.isEmpty(phone)) {
+ com.blankj.utilcode.util.ToastUtils.showShort("请输入手机号");
+ return;
+ }
+ if (type==1) {
+ String code = mBinding.edPassword.getText().toString().trim();
+ if (TextUtils.isEmpty(code)) {
+ com.blankj.utilcode.util.ToastUtils.showShort("请输入验证码");
+ return;
+ }
+ MvpPre.login(phone, "", code, 1);
+ }else if (type==2) {
+ String password = mBinding.edPasswordCode.getText().toString().trim();
+ if (TextUtils.isEmpty(password)) {
+ com.blankj.utilcode.util.ToastUtils.showShort("请输入密码");
+ return;
+ }
+ MvpPre.login(phone, password, "", 2);
+ }
+
+ } else if (id == R.id.tv_yhxy) {
+ MvpPre.ysxl();
+ } else if (id == R.id.tv_ysxy) {
+ MvpPre.yhxy();
+ } else if (id == R.id.iv_zfb) {
+ if (!mBinding.cbPrivacy.isChecked()) {
+ ToastUtils.show("请先勾选服务条款");
+ return;
+ }
+ MvpPre.authorization("zfb");
+// MvpPre.authorization(SHARE_MEDIA.QQ);
+ } else if (id == R.id.iv_weixin) {
+ if (!mBinding.cbPrivacy.isChecked()) {
+ ToastUtils.show("请先勾选服务条款");
+ return;
+ }
+// MvpPre.authorization("wx");
+ wcLogin();
+ } else if (id == R.id.tv_send_code) {
+ String phone = mBinding.edPhone.getText().toString().trim();
+ if (TextUtils.isEmpty(phone)) {
+ com.blankj.utilcode.util.ToastUtils.showShort("请输入手机号");
+ return;
+ }
+ sendCodeSuccess(phone);
+ MvpPre.sendCode(phone, 1);
+ }else if (id == R.id.iv_eye) {
+
+ if (!isPasswordVisible) {
+ mBinding.edPassword.setInputType(android.text.InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
+ mBinding.ivEye.setImageResource(com.xscm.moduleutil.R.mipmap.eye_visible); // 设置按钮文本为隐藏密码
+ } else {
+ mBinding.edPassword.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ mBinding.edPassword.setTransformationMethod(PasswordTransformationMethod.getInstance());
+ mBinding.ivEye.setImageResource(com.xscm.moduleutil.R.mipmap.eye_close); // 设置按钮文本为显示密码
+ }
+ mBinding.edPassword.setSelection(mBinding.edPassword.getText().length()); // 将光标移动到文字末尾
+ isPasswordVisible = !isPasswordVisible; // 切换状态
+ }
+ }
+ private void wcLogin() {
+ //发起登陆请求前先注册微信api
+ IWXAPI api = WXAPIFactory.createWXAPI(this,CommonAppContext.getInstance().getCurrentEnvironment().getWxAppId(),true);
+ api.registerApp(CommonAppContext.getInstance().getCurrentEnvironment().getWxAppId());
+ if (!api.isWXAppInstalled()){
+ //todo 提醒未安装微信
+ com.blankj.utilcode.util.ToastUtils.showShort("请安装微信客户端");
+ return;
+ }
+ //开始发起登陆请求
+ final SendAuth.Req req = new SendAuth.Req();
+ req.scope = "snsapi_userinfo";
+ req.state = "wechat_sdk_demo_test";
+ api.sendReq(req);
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void loginEvent(BaseResp event) {
+ if (event.errCode ==BaseResp.ErrCode.ERR_OK){
+ SendAuth.Resp authResp = (SendAuth.Resp)event;
+ LogUtils.e("@@@",authResp.code);
+ MvpPre.oauthLogin(authResp.code,1);
+ }
+ }
+ private void releaseTimer() {
+ if (mTimer != null) {
+ mTimer.cancel();
+ mTimer = null;
+ }
+ }
+
+ public void sendCodeSuccess(String phoneNumber) {
+ com.blankj.utilcode.util.ToastUtils.showShort("短信验证码发送成功请注意查收");
+ mBinding.tvSendCode.setEnabled(false);
+ mBinding.tvSendCode.setAlpha(0.5f);
+ releaseTimer();
+ if (mTimer != null) {
+ mTimer.cancel();
+ }
+ mTimer = new CountDownTimer(60000L, 1000L) {
+ @Override
+ public void onTick(long millisUntilFinished) {
+ if (mBinding.tvSendCode != null) {
+ mBinding.tvSendCode.setText(String.format("重新发送(%s)", millisUntilFinished / 1000));
+ }
+ }
+
+ @Override
+ public void onFinish() {
+ mBinding.tvSendCode.setAlpha(1f);
+ mBinding.tvSendCode.setEnabled(true);
+ mBinding.tvSendCode.setText("重新发送");
+ }
+ };
+ mTimer.start();
+ }
+
+ @Override
+ public void sendCodeSuccess1(String s) {
+ LogUtils.e(s);
+ }
+
+ @Override
+ public void loginSuccess(UserBean userBean) {
+
+ }
+
+ @Override
+ public void authorizationSuccess(String s) {
+ zfbLogin(s);
+ }
+
+ @Override
+ public void ysxlSuccess(String s) {
+
+ }
+
+ @Override
+ public void getThemeData(ThemeBean themeBean) {
+
+ }
+
+ private void zfbLogin(String s) {
+ LogUtils.e("@@@",s);
+ CommonAppContext.getInstance();
+ if (!CommonAppContext.isAlipayInstalled(this)){
+ com.blankj.utilcode.util.ToastUtils.showShort("请安装支付宝客户端");
+ return;
+ }
+ String authInfo = s;
+// String authInfo = "apiname=com.alipay.account.auth&app_id=2021005152631691&app_name=yusheng&auth_type=AUTHACCOUNT&biz_type=openservice&method=alipay.open.auth.sdk.code.get&pid=2088170624624316&product_id=APP_FAST_LOGIN&scope=kuaijie&sign_type=RSA2&target_id=20141225xxxx&sign=fMcp4GtiM6rxSIeFnJCVePJKV43eXrUP86CQgiLhDHH2u%2FdN75eEvmywc2ulkm7qKRetkU9fbVZtJIqFdMJcJ9Yp%2BJI%2FF%2FpESafFR6rB2fRjiQQLGXvxmDGVMjPSxHxVtIqpZy5FDoKUSjQ2%2FILDKpu3%2F%2BtAtm2jRw1rUoMhgt0%3D";
+ Runnable authRunnable = new Runnable() {
+
+ @Override
+ public void run() {
+ // 构造AuthTask 对象
+ AuthTask authTask = new AuthTask(PasswordLoginActivity.this);
+ // 调用授权接口,获取授权结果
+ Map result = authTask.authV2(authInfo, true);
+ LogUtils.e(result);
+ if (result.get("resultStatus").equals("9000")){
+ Pattern pattern = Pattern.compile("auth_code=([^&]*)");
+ Matcher matcher = pattern.matcher(result.get("result"));
+
+ if (matcher.find()) {
+ String authCode = matcher.group(1);
+ LogUtils.e("AuthCode", authCode);
+ MvpPre.oauthLogin(authCode,2);
+ }
+ }
+ }
+ };
+ Thread authThread = new Thread(authRunnable);
+ authThread.start();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/qxcm/qxlive/presenter/BasePresenter.java b/app/src/main/java/com/qxcm/qxlive/presenter/BasePresenter.java
new file mode 100644
index 00000000..4155ddf4
--- /dev/null
+++ b/app/src/main/java/com/qxcm/qxlive/presenter/BasePresenter.java
@@ -0,0 +1,110 @@
+package com.xscm.midi.presenter;
+
+
+import android.content.Context;
+
+
+import com.xscm.moduleutil.activity.IPresenter;
+import com.xscm.moduleutil.activity.IView;
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+
+import io.reactivex.disposables.CompositeDisposable;
+import io.reactivex.disposables.Disposable;
+
+public abstract class BasePresenter implements IPresenter {
+ protected CompositeDisposable mDisposables = new CompositeDisposable();
+// private RemoteDataSource api;
+ protected Reference MvpRef;
+ protected Context mContext;
+
+ @Deprecated
+ public BasePresenter(V view) {
+ attachView(view);
+ }
+
+ public BasePresenter(V view, Context context) {
+ attachView(view);
+ mContext = context;
+ }
+
+ private void attachView(V view) {
+ MvpRef = new WeakReference(view);
+ }
+
+ protected V getView() {
+ if (MvpRef != null) {
+ return MvpRef.get();
+ }
+ return null;
+ }
+
+// protected RemoteDataSource getApi() {
+// if (api == null) {
+// api = RemoteDataSource.getInstance();
+// }
+// return api;
+// }
+
+ /**
+ * 主要用于判断IView的生命周期是否结束,防止出现内存泄露状况
+ *
+ * @return
+ */
+ public boolean isViewAttach() {
+ return MvpRef != null && MvpRef.get() != null;
+ }
+
+ @Override
+ public void detachView() {
+ cancelRequest();
+ if (MvpRef != null) {
+ MvpRef.clear();
+ MvpRef = null;
+ }
+// if (api != null) {
+// api = null;
+// }
+ unBindView();
+ }
+
+
+ public void unBindView() {
+ if (MvpRef != null) {
+ MvpRef.clear();
+ }
+ mContext=null;
+ }
+
+ /**
+ * 加入订阅对象
+ *
+ * @param disposable
+ */
+ public void addDisposable(Disposable disposable) {
+ mDisposables.add(disposable);
+ }
+
+ /**
+ * 移除订阅对象
+ *
+ * @param disposable
+ */
+ public void removeDisposable(Disposable disposable) {
+ mDisposables.remove(disposable);
+ }
+
+ /**
+ * 取消所有请求
+ */
+ public void cancelRequest() {
+ if (mDisposables != null) {
+ mDisposables.clear(); // clear时网络请求会随即cancel
+ mDisposables = null;
+ }
+ }
+
+
+
+}
diff --git a/app/src/main/java/com/qxcm/qxlive/wxapi/WXEntryActivity.java b/app/src/main/java/com/qxcm/qxlive/wxapi/WXEntryActivity.java
new file mode 100644
index 00000000..bad7147b
--- /dev/null
+++ b/app/src/main/java/com/qxcm/qxlive/wxapi/WXEntryActivity.java
@@ -0,0 +1,233 @@
+package com.xscm.midi.wxapi;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import com.blankj.utilcode.util.ToastUtils;
+import com.xscm.moduleutil.base.CommonAppContext;
+import com.tencent.mm.opensdk.modelbase.BaseReq;
+import com.tencent.mm.opensdk.modelbase.BaseResp;
+import com.tencent.mm.opensdk.openapi.IWXAPI;
+import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler;
+import com.tencent.mm.opensdk.openapi.WXAPIFactory;
+
+import org.greenrobot.eventbus.EventBus;
+
+public class WXEntryActivity extends Activity implements IWXAPIEventHandler{
+// private static String TAG = "MicroMsg.WXEntryActivity";
+//
+// private IWXAPI api;
+// private MyHandler handler;
+//
+// private static class MyHandler extends Handler {
+// private final WeakReference wxEntryActivityWeakReference;
+//
+// public MyHandler(WXEntryActivity wxEntryActivity){
+// wxEntryActivityWeakReference = new WeakReference(wxEntryActivity);
+// }
+//
+// @Override
+// public void handleMessage(Message msg) {
+// int tag = msg.what;
+// switch (tag) {
+// case 0: {
+// Bundle data = msg.getData();
+// JSONObject json = null;
+// try {
+// json = new JSONObject(data.getString("result"));
+// String openId, accessToken, refreshToken, scope;
+// openId = json.getString("openid");
+// accessToken = json.getString("access_token");
+// refreshToken = json.getString("refresh_token");
+// scope = json.getString("scope");
+//// Intent intent = new Intent(wxEntryActivityWeakReference.get(), SendToWXActivity.class);
+//// intent.putExtra("openId", openId);
+//// intent.putExtra("accessToken", accessToken);
+//// intent.putExtra("refreshToken", refreshToken);
+//// intent.putExtra("scope", scope);
+//// wxEntryActivityWeakReference.get().startActivity(intent);
+// } catch (JSONException e) {
+// Log.e(TAG, e.getMessage());
+// }
+// }
+// }
+// }
+// }
+//
+// @Override
+// public void onCreate(Bundle savedInstanceState) {
+// super.onCreate(savedInstanceState);
+//
+// api = WXAPIFactory.createWXAPI(this, CommonAppContext.getInstance().getCurrentEnvironment().getWxAppId(), false);
+// handler = new MyHandler(this);
+//
+// try {
+// Intent intent = getIntent();
+// api.handleIntent(intent, this);
+// } catch (Exception e) {
+// e.printStackTrace();
+// }
+// }
+//
+// @Override
+// protected void onNewIntent(Intent intent) {
+// super.onNewIntent(intent);
+//
+// setIntent(intent);
+// api.handleIntent(intent, this);
+// }
+//
+// @Override
+// public void onReq(BaseReq req) {
+// switch (req.getType()) {
+// case ConstantsAPI.COMMAND_GETMESSAGE_FROM_WX:
+//// goToGetMsg();
+// break;
+// case ConstantsAPI.COMMAND_SHOWMESSAGE_FROM_WX:
+//// goToShowMsg((ShowMessageFromWX.Req) req);
+// break;
+// default:
+// break;
+// }
+// finish();
+// }
+//
+// @Override
+// public void onResp(BaseResp resp) {
+// int result = 0;
+//
+// switch (resp.errCode) {
+// case BaseResp.ErrCode.ERR_OK:
+//// result = com.qxcm.moduleutil.R.string.errcode_success;
+// break;
+// case BaseResp.ErrCode.ERR_USER_CANCEL:
+//// result = R.string.errcode_cancel;
+// break;
+// case BaseResp.ErrCode.ERR_AUTH_DENIED:
+//// result = R.string.errcode_deny;
+// break;
+// case BaseResp.ErrCode.ERR_UNSUPPORT:
+//// result = R.string.errcode_unsupported;
+// break;
+// default:
+//// result = R.string.errcode_unknown;
+// break;
+// }
+//
+// Toast.makeText(this, getString(result) + ", type=" + resp.getType(), Toast.LENGTH_SHORT).show();
+//
+//
+// if (resp.getType() == ConstantsAPI.COMMAND_SUBSCRIBE_MESSAGE) {
+// SubscribeMessage.Resp subscribeMsgResp = (SubscribeMessage.Resp) resp;
+// String text = String.format("openid=%s\ntemplate_id=%s\nscene=%d\naction=%s\nreserved=%s",
+// subscribeMsgResp.openId, subscribeMsgResp.templateID, subscribeMsgResp.scene, subscribeMsgResp.action, subscribeMsgResp.reserved);
+//
+// Toast.makeText(this, text, Toast.LENGTH_LONG).show();
+// }
+//
+// if (resp.getType() == ConstantsAPI.COMMAND_LAUNCH_WX_MINIPROGRAM) {
+// WXLaunchMiniProgram.Resp launchMiniProgramResp = (WXLaunchMiniProgram.Resp) resp;
+// String text = String.format("openid=%s\nextMsg=%s\nerrStr=%s",
+// launchMiniProgramResp.openId, launchMiniProgramResp.extMsg,launchMiniProgramResp.errStr);
+//
+// Toast.makeText(this, text, Toast.LENGTH_LONG).show();
+// }
+//
+// if (resp.getType() == ConstantsAPI.COMMAND_OPEN_BUSINESS_VIEW) {
+// WXOpenBusinessView.Resp launchMiniProgramResp = (WXOpenBusinessView.Resp) resp;
+// String text = String.format("openid=%s\nextMsg=%s\nerrStr=%s\nbusinessType=%s",
+// launchMiniProgramResp.openId, launchMiniProgramResp.extMsg,launchMiniProgramResp.errStr,launchMiniProgramResp.businessType);
+//
+// Toast.makeText(this, text, Toast.LENGTH_LONG).show();
+// }
+//
+// if (resp.getType() == ConstantsAPI.COMMAND_OPEN_BUSINESS_WEBVIEW) {
+// WXOpenBusinessWebview.Resp response = (WXOpenBusinessWebview.Resp) resp;
+// String text = String.format("businessType=%d\nresultInfo=%s\nret=%d",response.businessType,response.resultInfo,response.errCode);
+//
+// Toast.makeText(this, text, Toast.LENGTH_LONG).show();
+// }
+//
+// if (resp.getType() == ConstantsAPI.COMMAND_SENDAUTH) {
+// SendAuth.Resp authResp = (SendAuth.Resp)resp;
+// final String code = authResp.code;
+//// NetworkUtil.sendWxAPI(handler, String.format("https://api.weixin.qq.com/sns/oauth2/access_token?" +
+//// "appid=%s&secret=%s&code=%s&grant_type=authorization_code", "wxd930ea5d5a258f4f",
+//// "1d6d1d57a3dd063b36d917bc0b44d964", code), NetworkUtil.GET_TOKEN);
+// }
+// finish();
+// }
+//
+//// private void goToGetMsg() {
+//// Intent intent = new Intent(this, GetFromWXActivity.class);
+//// intent.putExtras(getIntent());
+//// startActivity(intent);
+//// finish();
+//// }
+////
+//// private void goToShowMsg(ShowMessageFromWX.Req showReq) {
+//// WXMediaMessage wxMsg = showReq.message;
+//// WXAppExtendObject obj = (WXAppExtendObject) wxMsg.mediaObject;
+////
+//// StringBuffer msg = new StringBuffer();
+//// msg.append("description: ");
+//// msg.append(wxMsg.description);
+//// msg.append("\n");
+//// msg.append("extInfo: ");
+//// msg.append(obj.extInfo);
+//// msg.append("\n");
+//// msg.append("filePath: ");
+//// msg.append(obj.filePath);
+////
+//// Intent intent = new Intent(this, ShowFromWXActivity.class);
+//// intent.putExtra(Constants.ShowMsgActivity.STitle, wxMsg.title);
+//// intent.putExtra(Constants.ShowMsgActivity.SMessage, msg.toString());
+//// intent.putExtra(Constants.ShowMsgActivity.BAThumbData, wxMsg.thumbData);
+//// startActivity(intent);
+//// finish();
+//// }
+
+
+ private final String TAG = this.getClass().getSimpleName();
+ public static final String APP_ID = CommonAppContext.getInstance().getCurrentEnvironment().getWxAppId();
+ public static final String APP_SECRET = "请自己填写";
+ private IWXAPI mApi;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mApi = WXAPIFactory.createWXAPI(this, APP_ID, true);
+ mApi.handleIntent(this.getIntent(), this);
+ }
+
+ //微信发送的请求将回调到onReq方法
+ @Override
+ public void onReq(BaseReq baseReq) {
+ }
+
+ //发送到微信请求的响应结果
+ @Override
+ public void onResp(BaseResp resp) {
+ switch (resp.errCode) {
+ case BaseResp.ErrCode.ERR_OK:
+ //发送成功
+ ToastUtils.showShort("发送成功",resp.transaction);
+ EventBus.getDefault().post(resp);
+ break;
+ case BaseResp.ErrCode.ERR_USER_CANCEL:
+ //发送取消
+ ToastUtils.showShort("发送取消",resp);
+ break;
+ case BaseResp.ErrCode.ERR_AUTH_DENIED:
+ ToastUtils.showShort("发送被拒绝",resp);
+ //发送被拒绝
+ break;
+ default:
+ //发送返回
+ break;
+ }
+ finish();
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/qxcm/qxlive/wxapi/WXPayEntryActivity.java b/app/src/main/java/com/qxcm/qxlive/wxapi/WXPayEntryActivity.java
new file mode 100644
index 00000000..d12a12e2
--- /dev/null
+++ b/app/src/main/java/com/qxcm/qxlive/wxapi/WXPayEntryActivity.java
@@ -0,0 +1,71 @@
+package com.xscm.midi.wxapi;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+import com.blankj.utilcode.util.ToastUtils;
+import com.tencent.mm.opensdk.constants.ConstantsAPI;
+import com.tencent.mm.opensdk.modelbase.BaseReq;
+import com.tencent.mm.opensdk.modelbase.BaseResp;
+import com.tencent.mm.opensdk.openapi.IWXAPI;
+import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler;
+import com.tencent.mm.opensdk.openapi.WXAPIFactory;
+import com.xscm.moduleutil.base.CommonAppContext;
+import com.xscm.moduleutil.event.PayEvent;
+
+import org.greenrobot.eventbus.EventBus;
+
+public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler {
+ private IWXAPI api;
+ public static final String APP_ID = CommonAppContext.getInstance().getCurrentEnvironment().getWxAppId();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ api = WXAPIFactory.createWXAPI(this, APP_ID);
+ api.handleIntent(getIntent(), this); // 必须调用,否则无法接收回调
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ setIntent(intent);
+ api.handleIntent(intent, this);
+ }
+
+ @Override
+ public void onReq(BaseReq baseReq) {
+
+ }
+
+ @Override
+ public void onResp(BaseResp resp) {
+ // 支付结果回调(resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX)
+ if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {
+ switch (resp.errCode) {
+ case BaseResp.ErrCode.ERR_OK:
+ // 支付成功:这里需要调用后台接口确认支付状态(避免本地判断不可靠)
+
+ break;
+ case BaseResp.ErrCode.ERR_USER_CANCEL:
+ checkPayResultFromServer();
+ break;
+ default:
+ // 支付失败
+ checkPayResultFromServer();
+ break;
+ }
+ finish(); // 处理完后关闭页面
+ }
+ }
+
+ // 关键:必须从后台确认支付状态(不能仅依赖前端回调)
+ private void checkPayResultFromServer() {
+ // 调用后台接口,传入订单号查询实际支付状态
+ // 用户取消支付
+ ToastUtils.showShort("支付取消");
+ PayEvent messageEvent = new PayEvent(-2, "支付取消");
+ EventBus.getDefault().post(messageEvent);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/bg_launcher_ad_tip.xml b/app/src/main/res/drawable/bg_launcher_ad_tip.xml
new file mode 100644
index 00000000..92970da0
--- /dev/null
+++ b/app/src/main/res/drawable/bg_launcher_ad_tip.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/bg_launcher_skip.xml b/app/src/main/res/drawable/bg_launcher_skip.xml
new file mode 100644
index 00000000..456bd4be
--- /dev/null
+++ b/app/src/main/res/drawable/bg_launcher_skip.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/bg_launcher_skip_2.xml b/app/src/main/res/drawable/bg_launcher_skip_2.xml
new file mode 100644
index 00000000..411b0805
--- /dev/null
+++ b/app/src/main/res/drawable/bg_launcher_skip_2.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_topbar_back_dark.png b/app/src/main/res/drawable/ic_topbar_back_dark.png
new file mode 100644
index 00000000..f574ca9a
Binary files /dev/null and b/app/src/main/res/drawable/ic_topbar_back_dark.png differ
diff --git a/app/src/main/res/drawable/login_log.webp b/app/src/main/res/drawable/login_log.webp
new file mode 100644
index 00000000..efe68f9f
Binary files /dev/null and b/app/src/main/res/drawable/login_log.webp differ
diff --git a/app/src/main/res/layout/activity_launch_page.xml b/app/src/main/res/layout/activity_launch_page.xml
new file mode 100644
index 00000000..76372c38
--- /dev/null
+++ b/app/src/main/res/layout/activity_launch_page.xml
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_password_login.xml b/app/src/main/res/layout/activity_password_login.xml
new file mode 100644
index 00000000..0c7b2f3b
--- /dev/null
+++ b/app/src/main/res/layout/activity_password_login.xml
@@ -0,0 +1,383 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 00000000..036d09bc
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round1.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round1.xml
new file mode 100644
index 00000000..036d09bc
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round1.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 00000000..61399802
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_app.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_app.webp
new file mode 100644
index 00000000..61399802
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_app.webp differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
new file mode 100644
index 00000000..f4b0153d
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 00000000..b1273dbf
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 00000000..82f36a48
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_app.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_app.webp
new file mode 100644
index 00000000..82f36a48
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_app.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
new file mode 100644
index 00000000..e9e8c861
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 00000000..732051d4
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 00000000..c09ebc05
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_app.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_app.webp
new file mode 100644
index 00000000..c09ebc05
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_app.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
new file mode 100644
index 00000000..09bd91c1
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 00000000..936c27cc
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/screen.webp b/app/src/main/res/mipmap-xhdpi/screen.webp
new file mode 100644
index 00000000..68135279
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/screen.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 00000000..7849d4b3
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_app.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_app.webp
new file mode 100644
index 00000000..7849d4b3
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_app.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 00000000..82939eb7
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 00000000..8cceef70
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/screen.webp b/app/src/main/res/mipmap-xxhdpi/screen.webp
new file mode 100644
index 00000000..68135279
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/screen.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 00000000..c0315c91
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_app.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_app.webp
new file mode 100644
index 00000000..c0315c91
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_app.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 00000000..efe68f9f
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 00000000..da87c1ed
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/screen.webp b/app/src/main/res/mipmap-xxxhdpi/screen.webp
new file mode 100644
index 00000000..68135279
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/screen.webp differ
diff --git a/app/src/main/res/values-en-rUs/strings.xml b/app/src/main/res/values-en-rUs/strings.xml
new file mode 100644
index 00000000..ec3d88e6
--- /dev/null
+++ b/app/src/main/res/values-en-rUs/strings.xml
@@ -0,0 +1,5 @@
+
+
+ Skip
+ Click to jump to third-party applications
+
\ No newline at end of file
diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml
new file mode 100644
index 00000000..1c850379
--- /dev/null
+++ b/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 00000000..104ef913
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,32 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
+ #ff5878
+ @color/gray2
+ #00000000
+ #282828
+ #969696
+ #f5f5f5
+ #c8c8c8
+ #B4B4B4
+ #dcdcdc
+ #fffa37
+ #f0cd08
+ #FFF34D
+ #ffdd00
+ #646464
+ #ff0000
+ #FF205E
+ #169AFF
+ #e6323232
+ #1FC8F8
+ #4998F7
+ #32A0FF
+
\ No newline at end of file
diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml
new file mode 100644
index 00000000..c5d5899f
--- /dev/null
+++ b/app/src/main/res/values/ic_launcher_background.xml
@@ -0,0 +1,4 @@
+
+
+ #FFFFFF
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 00000000..b26f9828
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+
+ 跳过
+ 点击跳转第三方应用
+
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
new file mode 100644
index 00000000..bcb7b148
--- /dev/null
+++ b/app/src/main/res/values/themes.xml
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 00000000..fa0f996d
--- /dev/null
+++ b/app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 00000000..9ee9997b
--- /dev/null
+++ b/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lombok.config b/lombok.config
new file mode 100644
index 00000000..81022eac
--- /dev/null
+++ b/lombok.config
@@ -0,0 +1 @@
+lombok.anyConstructor.suppressConstructorProperties=true
\ No newline at end of file
diff --git a/moduleUtil/src/main/assets/draw_music.mp3 b/moduleUtil/src/main/assets/draw_music.mp3
new file mode 100644
index 00000000..3fe540f5
Binary files /dev/null and b/moduleUtil/src/main/assets/draw_music.mp3 differ
diff --git a/moduleUtil/src/main/assets/heart_line_31.svga b/moduleUtil/src/main/assets/heart_line_31.svga
new file mode 100644
index 00000000..e0fe8988
Binary files /dev/null and b/moduleUtil/src/main/assets/heart_line_31.svga differ
diff --git a/moduleUtil/src/main/assets/xuanz.mp3 b/moduleUtil/src/main/assets/xuanz.mp3
new file mode 100644
index 00000000..448b70e2
Binary files /dev/null and b/moduleUtil/src/main/assets/xuanz.mp3 differ
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/base/AppStateListener.java b/moduleUtil/src/main/java/com/xscm/moduleutil/base/AppStateListener.java
new file mode 100644
index 00000000..7c7dc16b
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/base/AppStateListener.java
@@ -0,0 +1,24 @@
+package com.xscm.moduleutil.base;
+
+import android.app.Activity;
+
+/**
+ *@author qx
+ *@data 2025/9/20
+ *@description: 模块之间的通讯接口
+ */
+public interface AppStateListener {
+ void onAppForeground();
+ void onAppBackground();
+ void onRoomActivityCreated(Activity roomActivity);
+ void onRoomActivityDestroyed();
+ boolean isRoomActivityActive();
+ void setFloatingWindowVisible(boolean visible);
+ boolean isFloatingWindowVisible();
+
+ // 新增方法
+ boolean shouldShowSplash();
+ void setShouldShowSplash(boolean shouldShow);
+ boolean isAppInBackground();
+ void setAppInBackground(boolean inBackground);
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/base/AppStateManager.java b/moduleUtil/src/main/java/com/xscm/moduleutil/base/AppStateManager.java
new file mode 100644
index 00000000..799b2add
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/base/AppStateManager.java
@@ -0,0 +1,112 @@
+package com.xscm.moduleutil.base;
+
+import android.app.Activity;
+
+import com.xscm.moduleutil.bean.room.RoomInfoResp;
+
+import java.lang.ref.WeakReference;
+/**
+ *@author qx
+ *@data 2025/9/20
+ *@description: 应用状态管理的单例类
+ */
+// 在 common 模块中
+public class AppStateManager implements AppStateListener {
+ private static AppStateManager instance;
+ private boolean isAppInBackground = true;
+ private boolean shouldShowSplash = true;
+ private WeakReference roomActivityRef;
+ private boolean isFloatingWindowVisible = false;
+ private boolean isRoomActivityMinimized = false;
+ private AppStateManager() {
+ // 私有构造函数
+ }
+
+ public static synchronized AppStateManager getInstance() {
+ if (instance == null) {
+ instance = new AppStateManager();
+ }
+ return instance;
+ }
+
+ @Override
+ public boolean shouldShowSplash() {
+ return shouldShowSplash;
+ }
+
+ @Override
+ public void setShouldShowSplash(boolean shouldShow) {
+ this.shouldShowSplash = shouldShow;
+ }
+
+ @Override
+ public boolean isAppInBackground() {
+ return isAppInBackground;
+ }
+
+ @Override
+ public void setAppInBackground(boolean inBackground) {
+ this.isAppInBackground = inBackground;
+ }
+
+ @Override
+ public void onRoomActivityCreated(Activity roomActivity) {
+ roomActivityRef = new WeakReference<>(roomActivity);
+ }
+
+ @Override
+ public void onRoomActivityDestroyed() {
+ roomActivityRef = null;
+ }
+
+ @Override
+ public boolean isRoomActivityActive() {
+ Activity activity = getRoomActivity();
+ return activity != null && !activity.isFinishing();
+ }
+
+ private Activity getRoomActivity() {
+ return roomActivityRef != null ? roomActivityRef.get() : null;
+ }
+
+ @Override
+ public void setFloatingWindowVisible(boolean visible) {
+ this.isFloatingWindowVisible = visible;
+ }
+
+ @Override
+ public boolean isFloatingWindowVisible() {
+ return isFloatingWindowVisible;
+ }
+
+ @Override
+ public void onAppForeground() {
+ // 应用进入前台时的处理
+ setAppInBackground(false);
+ }
+
+ @Override
+ public void onAppBackground() {
+ // 应用进入后台时的处理
+ setAppInBackground(true);
+ }
+
+ // 新增方法:设置RoomActivity为最小化状态
+ public void setRoomActivityMinimized(boolean minimized) {
+ this.isRoomActivityMinimized = minimized;
+ }
+
+ // 新增方法:检查RoomActivity是否处于最小化状态
+ public boolean isRoomActivityMinimized() {
+ return isRoomActivityMinimized;
+ }
+ private RoomInfoResp roomInfoResp;
+ public void setRoomInfo(RoomInfoResp roomInfoResp) {
+ // 处理RoomInfoResp对象
+ this.roomInfoResp = roomInfoResp;
+ }
+
+ public RoomInfoResp getRoomInfo() {
+ return roomInfoResp;
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/base/RoomManager.java b/moduleUtil/src/main/java/com/xscm/moduleutil/base/RoomManager.java
new file mode 100644
index 00000000..ce121943
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/base/RoomManager.java
@@ -0,0 +1,543 @@
+package com.xscm.moduleutil.base;
+
+import static android.app.PendingIntent.getActivity;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.TextUtils;
+
+import com.alibaba.android.arouter.launcher.ARouter;
+import com.blankj.utilcode.util.ActivityUtils;
+import com.blankj.utilcode.util.LogUtils;
+import com.blankj.utilcode.util.ToastUtils;
+import com.xscm.moduleutil.bean.room.RoomInfoResp;
+import com.xscm.moduleutil.bean.room.RoomOnline;
+import com.xscm.moduleutil.bean.room.RoomOnlineBean;
+import com.xscm.moduleutil.event.RoomOutEvent;
+import com.xscm.moduleutil.http.BaseObserver;
+import com.xscm.moduleutil.http.RetrofitClient;
+import com.xscm.moduleutil.listener.MessageListenerSingleton;
+import com.xscm.moduleutil.rtc.AgoraManager;
+import com.xscm.moduleutil.utils.ARouteConstants;
+import com.xscm.moduleutil.utils.SpUtil;
+import com.xscm.moduleutil.utils.logger.Logger;
+
+import org.greenrobot.eventbus.EventBus;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import io.reactivex.disposables.Disposable;
+
+/**
+ * 房间管理器
+ * 统一处理房间数据获取、进入房间和退出房间的逻辑
+ */
+public class RoomManager {
+ private static final String TAG = "RoomManager";
+
+ private static RoomManager instance;
+
+ // 房间数据缓存
+ private Map roomDataCache = new ConcurrentHashMap<>();
+ private Map cacheTimestamps = new ConcurrentHashMap<>();
+
+ // 缓存有效期(5分钟)
+ private static final long CACHE_DURATION = 5 * 60 * 1000;
+
+ private RoomManager() {
+ }
+
+ public static synchronized RoomManager getInstance() {
+ if (instance == null) {
+ instance = new RoomManager();
+ }
+ return instance;
+ }
+
+ /**
+ * 进入房间 - 自动获取房间数据
+ *
+ * @param context 上下文
+ * @param roomId 房间ID
+ */
+ public void enterRoom(Context context, String roomId) {
+ enterRoom(context, roomId, null, null);
+ }
+
+ /**
+ * 进入房间 - 使用密码
+ *
+ * @param context 上下文
+ * @param roomId 房间ID
+ * @param password 房间密码
+ */
+ public void enterRoom(Context context, String roomId, String password) {
+ enterRoom(context, roomId, password, null);
+ }
+
+ /**
+ * 进入房间 - 使用缓存数据
+ *
+ * @param context 上下文
+ * @param roomId 房间ID
+ * @param password 房间密码
+ * @param cachedData 缓存的房间数据
+ */
+ public void enterRoom(Context context, String roomId, String password, RoomInfoResp cachedData) {
+ if (TextUtils.isEmpty(roomId)) {
+ ToastUtils.showShort("房间ID不能为空");
+ return;
+ }
+
+ // 检查是否有有效的缓存数据
+ RoomInfoResp roomInfo = cachedData != null ? cachedData : getCachedRoomData(roomId);
+
+ if (roomInfo != null) {
+ // 使用缓存数据直接进入房间
+ navigateToRoom(context, roomId, password, roomInfo, false);
+ } else {
+ // 获取房间数据后进入房间
+ fetchRoomDataAndEnter(context, roomId, password);
+ }
+ }
+
+ /**
+ * 获取房间数据并进入房间
+ *
+ * @param context 上下文
+ * @param roomId 房间ID
+ * @param password 房间密码
+ */
+ public void fetchRoomDataAndEnter(Context context, String roomId, String password) {
+ // 显示加载提示
+ // 这里可以根据需要添加加载对话框
+ if (CommonAppContext.getInstance().isRoomJoininj){
+ return;
+ }
+ CommonAppContext.getInstance().isRoomJoininj=true;
+ // 检查是否有有效的缓存数据
+// RoomInfoResp roomInfo = getCachedRoomData(roomId);
+ // 检查是否是当前房间且用户在线
+// boolean isCurrentRoom = isCurrentRoom(roomId);
+ if (CommonAppContext.getInstance().playId == null) {
+ fetchAndJoinRoom(context, roomId, password);
+ } else {
+ if (!CommonAppContext.getInstance().playId.equals(roomId)) {
+ MessageListenerSingleton.getInstance().joinGroup(roomId);
+ exitRoom(CommonAppContext.getInstance().playId);
+ CommonAppContext.getInstance().isShow = false;
+ CommonAppContext.getInstance().isPlaying = false;
+ CommonAppContext.getInstance().isRoomJoininj=false;
+ EventBus.getDefault().post(new RoomOutEvent());
+ } else if (CommonAppContext.getInstance().lable_id.equals("6")) {
+ upInfo(context, roomId, password, true, null, true);
+ return;
+ }
+ isUserOnline(context, roomId, password, null);
+
+ }
+
+// try {
+// Thread.sleep(1000);
+// } catch (InterruptedException e) {
+// Thread.currentThread().interrupt();
+// }
+
+ // 如果是当前房间且用户在线,直接跳转到房间页面,仅更新数据
+
+
+// // 获取房间数据
+// MessageListenerSingleton.getInstance().joinGroup(roomId);
+// // 等待一段时间确保退出完成
+// try {
+// Thread.sleep(500);
+// } catch (InterruptedException e) {
+// Thread.currentThread().interrupt();
+// }
+// RetrofitClient.getInstance().roomGetIn(roomId, password, new BaseObserver() {
+//
+// @Override
+// public void onSubscribe(Disposable d) {
+// }
+//
+// @Override
+// public void onNext(RoomInfoResp resp) {
+// String appId = CommonAppContext.getInstance().getCurrentEnvironment().getSwSdkAppId();
+// String token = resp.getUser_info().getAgora_token(); // 如果启用了鉴权才需要
+// String roomId = resp.getRoom_info().getRoom_id(); // 房间 ID
+// String rtm_token=resp.getUser_info().getAgora_rtm_token();
+// SpUtil.setRtmToken(rtm_token);
+// int uid = SpUtil.getUserId(); // 0 表示由 Agora 自动生成 UID
+// boolean enableMic = false; // 是否开启麦克风
+// boolean enableJs=false; // 是否开启角色
+// if (resp.getUser_info().getPit_number()!=0){
+// enableJs=true;
+// }
+// LogUtils.e("token",token);
+// LogUtils.e("roomId:",roomId);
+//// 初始化 Agora 并加入房间
+// AgoraManager.getInstance(context)
+// .joinRoom(token, roomId, uid, enableMic,enableJs);
+// cacheRoomData(roomId, resp);
+// navigateToRoom(context, roomId, password, resp);
+// }
+// });
+
+ // 临时实现 - 直接跳转(因为缺少具体的网络请求代码)
+// navigateToRoom(context, roomId, password, null);
+ }
+
+ private void upInfo(Context context, String roomId, String password, boolean isOnline, RoomInfoResp roomInfo, boolean isCurrentRoom) {
+
+
+ if (isOnline) {
+ navigateToRoom(context, roomId, password, roomInfo, isOnline);
+ } else {
+// CommonAppContext.getInstance().isShow = false;
+// CommonAppContext.getInstance().isPlaying = false;
+// EventBus.getDefault().post(new RoomOutEvent());
+// try {
+// Thread.sleep(300);
+// } catch (InterruptedException e) {
+// Thread.currentThread().interrupt();
+// }
+ fetchAndJoinRoom(context, roomId, password);
+ }
+
+
+// if (isCurrentRoom&& isOnline) {
+// if (roomInfo != null) {
+// navigateToRoom(context, roomId, password, roomInfo);
+// } else {
+// // 即使在线,如果没有缓存数据,也需要获取数据
+// fetchAndJoinRoom(context, roomId, password);
+// }
+// return;
+// }
+
+
+ // 如果有缓存数据且用户在线,使用缓存数据进入房间
+// if (roomInfo != null && isOnline) {
+// RetrofitClient.getInstance().postRoomInfo(roomId, new BaseObserver() {
+//
+// @Override
+// public void onSubscribe(Disposable d) {
+//
+// }
+//
+// @Override
+// public void onNext(RoomInfoResp roomInfoResp) {
+//// cacheRoomData(roomId, roomInfo);
+// navigateToRoom(context, roomId, password, roomInfoResp);
+// }
+// });
+
+// cacheRoomData(roomId, roomInfo);
+// navigateToRoom(context, roomId, password, roomInfo);
+ return;
+// }
+
+ // 其他情况,获取新的房间数据并加入房间
+// fetchAndJoinRoom(context, roomId, password);
+ }
+
+ /**
+ * 获取新的房间数据并加入房间
+ *
+ * @param context 上下文
+ * @param roomId 房间ID
+ * @param password 房间密码
+ */
+ private void fetchAndJoinRoom(Context context, String roomId, String password) {
+ // 获取房间数据
+
+ // 等待一段时间确保退出完成
+ try {
+ Thread.sleep(300);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ navigateToRoom(context, roomId, password, null, false);
+
+// RetrofitClient.getInstance().roomGetIn(roomId, password, new BaseObserver() {
+//
+// @Override
+// public void onSubscribe(Disposable d) {
+// }
+//
+// @Override
+// public void onNext(RoomInfoResp resp) {
+// String appId = CommonAppContext.getInstance().getCurrentEnvironment().getSwSdkAppId();
+// String token = resp.getUser_info().getAgora_token(); // 如果启用了鉴权才需要
+// String roomId = resp.getRoom_info().getRoom_id(); // 房间 ID
+// String rtm_token=resp.getUser_info().getAgora_rtm_token();
+// SpUtil.setRtmToken(rtm_token);
+// int uid = SpUtil.getUserId(); // 0 表示由 Agora 自动生成 UID
+// boolean enableMic = false; // 是否开启麦克风
+// boolean enableJs=false; // 是否开启角色
+// if (resp.getUser_info().getPit_number()!=0){
+// enableJs=true;
+// }
+// LogUtils.e("token",token);
+// LogUtils.e("roomId:",roomId);
+//// 初始化 Agora 并加入房间
+// AgoraManager.getInstance(context)
+// .joinRoom(token, roomId, uid, enableMic,enableJs);
+// cacheRoomData(roomId, resp);
+// navigateToRoom(context, roomId, password, resp);
+// }
+// });
+ }
+
+ /**
+ * 检查是否是当前房间
+ *
+ * @param roomId 房间ID
+ * @return true表示是当前房间,false表示不是
+ */
+ private boolean isCurrentRoom(String roomId) {
+ // 这里应该实现检查是否是当前房间的逻辑
+ // 可以通过检查当前Activity或者通过全局变量等方式实现
+ // 目前返回false,需要根据实际需求实现具体逻辑
+
+ RoomInfoResp roomInfo = getCachedRoomData(roomId);
+ if (roomInfo != null) {
+ if (roomInfo.getRoom_info().getRoom_id().equals(roomId)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * 跳转到房间页面
+ *
+ * @param context 上下文
+ * @param roomId 房间ID
+ * @param password 房间密码
+ * @param roomInfo 房间信息
+ */
+ private void navigateToRoom(Context context, String roomId, String password, RoomInfoResp roomInfo, boolean isOnline) {
+ try {
+ // 构建跳转参数
+ Bundle bundle = new Bundle();
+ bundle.putString("roomId", roomId);
+ bundle.putBoolean("isOnline", isOnline);
+
+ if (!TextUtils.isEmpty(password)) {
+ bundle.putString("password", password);
+ }
+
+ if (roomInfo != null) {
+// bundle.putSerializable("roomInfo", roomInfo);
+ }
+
+ // 使用ARouter跳转到房间页面
+ ARouter.getInstance()
+ .build(ARouteConstants.ROOM_DETAILS)
+ .with(bundle)
+ .navigation(context);
+
+ } catch (Exception e) {
+ Logger.e(TAG, "跳转房间页面失败: " + e.getMessage());
+ }
+ }
+
+ /**
+ * 缓存房间数据
+ *
+ * @param roomId 房间ID
+ * @param roomInfo 房间信息
+ */
+ public void cacheRoomData(String roomId, RoomInfoResp roomInfo) {
+
+ if (TextUtils.isEmpty(roomId) || roomInfo == null) {
+ return;
+ }
+ // 清除所有现有的缓存数据
+ roomDataCache.clear();
+ cacheTimestamps.clear();
+ roomDataCache.put(roomId, roomInfo);
+ cacheTimestamps.put(roomId, System.currentTimeMillis());
+ }
+
+ /**
+ * 获取缓存的房间数据
+ *
+ * @param roomId 房间ID
+ * @return 房间信息,如果缓存无效则返回null
+ */
+ public RoomInfoResp getCachedRoomData(String roomId) {
+ if (TextUtils.isEmpty(roomId)) {
+ return null;
+ }
+
+ Long timestamp = cacheTimestamps.get(roomId);
+ if (timestamp == null) {
+ return null;
+ }
+
+ // 检查缓存是否过期
+ if (System.currentTimeMillis() - timestamp > CACHE_DURATION) {
+ // 缓存过期,清除数据
+ roomDataCache.remove(roomId);
+ cacheTimestamps.remove(roomId);
+ return null;
+ }
+
+ return roomDataCache.get(roomId);
+ }
+
+ /**
+ * 检查用户是否在线
+ *
+ * @param roomId 房间ID
+ * @return true表示用户在线,false表示不在线
+ */
+ private boolean isUserOnline(Context context, String roomId, String password, RoomInfoResp roomInfo) {
+ // 这里应该实现检查用户是否在线的逻辑
+ // 可以通过检查Agora是否还在房间中,或者通过服务端接口查询用户状态等方式实现
+ // 目前返回false,需要根据实际需求实现具体逻辑
+// boolean isCurrentRoom=isCurrentRoom(roomId);
+// try {
+// Thread.sleep(300);
+// } catch (InterruptedException e) {
+// Thread.currentThread().interrupt();
+// }
+ final boolean[] isOnline = {false};
+ RetrofitClient.getInstance().getRoomOnline(roomId, "1", "50", new BaseObserver() {
+ @Override
+ public void onSubscribe(Disposable d) {
+
+ }
+
+ @Override
+ public void onNext(RoomOnline roomOnline) {
+ try {
+ if (roomOnline != null) {
+ if (roomOnline.getOn_pit() != null) {
+ for (RoomOnlineBean roomOnlineBean : roomOnline.getOn_pit()) {
+ if (roomOnlineBean.getUser_id() == SpUtil.getUserId()) {
+ isOnline[0] = true;
+ break;
+ }
+ }
+ }
+ if (roomOnline.getOff_pit() != null) {
+ for (RoomOnlineBean roomOnlineBean : roomOnline.getOff_pit()) {
+ if (roomOnlineBean.getUser_id() == SpUtil.getUserId()) {
+ isOnline[0] = true;
+ break;
+ }
+ }
+ }
+ upInfo(context, roomId, password, isOnline[0], roomInfo, true);
+ } else {
+ isOnline[0] = false;
+ }
+ } catch (Exception e) {
+ // 捕获所有可能的异常,避免崩溃
+ e.printStackTrace();
+ isOnline[0] = false;
+ // 即使出现异常也继续执行
+ upInfo(context, roomId, password, isOnline[0], roomInfo, true);
+ }
+ }
+ });
+ return isOnline[0];
+ }
+
+ /**
+ * 清除指定房间的缓存数据
+ *
+ * @param roomId 房间ID
+ */
+ public void clearRoomCache(String roomId) {
+ if (!TextUtils.isEmpty(roomId)) {
+ roomDataCache.remove(roomId);
+ cacheTimestamps.remove(roomId);
+ }
+ }
+
+ /**
+ * 清除所有房间缓存数据
+ */
+ public void clearAllRoomCache() {
+ roomDataCache.clear();
+ cacheTimestamps.clear();
+ }
+
+ /**
+ * 退出房间
+ *
+ * @param roomId 房间ID
+ */
+ public void exitRoom(String roomId) {
+ // 清除该房间的缓存数据
+ clearRoomCache(roomId);
+
+ // 可以在这里添加其他退出房间的逻辑
+ // 例如:通知服务器用户已退出、清理房间相关资源等
+
+ RetrofitClient.getInstance().quitRoom(roomId, SpUtil.getUserId() + "", new BaseObserver() {
+ @Override
+ public void onSubscribe(Disposable d) {
+
+ }
+
+ @Override
+ public void onNext(String s) {
+
+ }
+ });
+
+ Logger.d(TAG, "退出房间: " + roomId);
+ }
+
+ /**
+ * 批量退出房间
+ *
+ * @param roomIds 房间ID列表
+ */
+ public void exitRooms(String... roomIds) {
+ if (roomIds != null) {
+ for (String roomId : roomIds) {
+ exitRoom(roomId);
+ }
+ }
+ }
+
+ /**
+ * 获取房间缓存状态
+ *
+ * @param roomId 房间ID
+ * @return 缓存状态信息
+ */
+ public String getRoomCacheStatus(String roomId) {
+ if (TextUtils.isEmpty(roomId)) {
+ return "无效的房间ID";
+ }
+
+ Long timestamp = cacheTimestamps.get(roomId);
+ if (timestamp == null) {
+ return "未缓存";
+ }
+
+ long elapsed = System.currentTimeMillis() - timestamp;
+ if (elapsed > CACHE_DURATION) {
+ return "缓存已过期";
+ }
+
+ RoomInfoResp data = roomDataCache.get(roomId);
+ if (data == null) {
+ return "缓存数据为空";
+ }
+
+ return String.format("已缓存 (%d秒前)", elapsed / 1000);
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/bean/ActivitiesPermission.java b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/ActivitiesPermission.java
new file mode 100644
index 00000000..b613ef5c
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/ActivitiesPermission.java
@@ -0,0 +1,14 @@
+package com.xscm.moduleutil.bean;
+
+import lombok.Data;
+/**
+ * @Description: 首页活动弹窗权限
+ * @Author: xscm
+ * @Date: 2021/9/27 14:05
+ */
+@Data
+public class ActivitiesPermission {
+ private int first_charge_permission;//首充权限 1:有 0:无
+ private int day_drop_permission;//天降好礼权限 1:有 0:无
+ private int n_people_permission;//新人好礼权限 1:有 0:无
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/bean/BindDetail.java b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/BindDetail.java
new file mode 100644
index 00000000..5810b82b
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/BindDetail.java
@@ -0,0 +1,22 @@
+package com.xscm.moduleutil.bean;
+
+import lombok.Data;
+/**
+ *@author qx
+ *@data 2025/9/25
+ *@description: 绑定详情
+ */
+@Data
+public class BindDetail {
+
+ private String id;
+ private String alipay_name;//支付宝姓名
+ private String alipay_account;//支付宝账户
+
+ private String bank_card_number;//银行卡号
+
+ private String bank_user_name;//姓名
+ private String bank_card;//所属行
+ private String open_bank;//开户行
+
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/bean/GiftAvatarBean.java b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/GiftAvatarBean.java
new file mode 100644
index 00000000..849f5faf
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/GiftAvatarBean.java
@@ -0,0 +1,4 @@
+package com.xscm.moduleutil.bean;
+
+public class GiftAvatarBean {
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/bean/GiftPackEvent.java b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/GiftPackEvent.java
new file mode 100644
index 00000000..203208c6
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/GiftPackEvent.java
@@ -0,0 +1,8 @@
+package com.xscm.moduleutil.bean;
+
+import lombok.Data;
+
+@Data
+public class GiftPackEvent {
+ private String bdid;
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/bean/GiftPackListCount.java b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/GiftPackListCount.java
new file mode 100644
index 00000000..51ff27d8
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/GiftPackListCount.java
@@ -0,0 +1,13 @@
+package com.xscm.moduleutil.bean;
+
+import lombok.Data;
+
+/**
+ *@author qx
+ *@data 2025/9/15
+ *@description: 背包礼物总价值
+ */
+@Data
+public class GiftPackListCount {
+ private String count;
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/bean/MqttXlhEnd.java b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/MqttXlhEnd.java
new file mode 100644
index 00000000..0d32949a
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/MqttXlhEnd.java
@@ -0,0 +1,11 @@
+package com.xscm.moduleutil.bean;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class MqttXlhEnd implements Serializable {
+ private static final long serialVersionUID = 1L;
+ private String message;
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/bean/PermissionPicBean.java b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/PermissionPicBean.java
new file mode 100644
index 00000000..d1156ab3
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/PermissionPicBean.java
@@ -0,0 +1,17 @@
+package com.xscm.moduleutil.bean;
+
+import com.stx.xhb.xbanner.entity.SimpleBannerInfo;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class PermissionPicBean extends SimpleBannerInfo {
+ private int picId;
+ private int type;//类型 1:首充、2:天降 ;3:新人
+
+ @Override
+ public Object getXBannerUrl() {
+ return picId;
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/bean/RedPackGrab.java b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/RedPackGrab.java
new file mode 100644
index 00000000..4d9807c7
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/RedPackGrab.java
@@ -0,0 +1,8 @@
+package com.xscm.moduleutil.bean;
+
+import lombok.Data;
+
+@Data
+public class RedPackGrab {
+ private int code;//1:正常抢 2:已经抢过了 3:手慢了
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/bean/RedPacketInfo.java b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/RedPacketInfo.java
new file mode 100644
index 00000000..9c4bd95e
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/RedPacketInfo.java
@@ -0,0 +1,52 @@
+package com.xscm.moduleutil.bean;
+
+import lombok.Data;
+
+/**
+ * 红包推送的对象
+ */
+@Data
+public class RedPacketInfo {
+ private int id;
+ private String remark;// 备注
+ private String password;// 口令
+ private int countdown;//0:立即开抢,其他:倒计时抢
+ private String conditions;//条件
+ private String total_amount;//红包总金额
+ private int room_id;//房间ID
+ private int type;//红包类型
+ private int total_count;//红包数量
+ private int coin_type;//币种
+ private int user_id;//用户ID
+ private String nickname;// 昵称
+ private String redpacket_id;//红包ID
+
+ private String avatar;//头像
+ private String redpacket_time;//红包消失的时间
+ private long start_time;
+
+ private boolean isAvailable;//是否可以领取
+
+ private String left_amount;//33.00",
+ private int left_count;
+ private long end_time;
+ private long createtime;
+ private String updatetime;
+ private int is_qiang;
+
+ // 获取剩余时间
+ public long remainingTime() {
+ long needTime = 0;
+ // 获取当前时间戳(毫秒)
+ long currentTimeMillis = System.currentTimeMillis() / 1000;
+ // 计算剩余时间
+ needTime = start_time - currentTimeMillis;
+ return needTime;
+ }
+
+ // 判断红包是否可以领取
+ public boolean canOpenNow() {
+ return remainingTime() <= 0;
+ }
+
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/bean/RedpacketDetail.java b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/RedpacketDetail.java
new file mode 100644
index 00000000..d4a4645c
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/RedpacketDetail.java
@@ -0,0 +1,35 @@
+package com.xscm.moduleutil.bean;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class RedpacketDetail {
+ private RedPacketInfo redpacket_info;
+
+ private List records;
+ private MyRecord my_record;
+ private boolean has_grabbed;
+ @Data
+ public static class Records {
+ private int id;
+ private int redpacket_id;
+ private int user_id;
+ private String nickname;
+ private String avatar;
+ private String amount;
+ private String createtime;
+ }
+
+ @Data
+ public static class MyRecord {
+ private int id;
+ private int redpacket_id;
+ private String nickname;
+ private String user_id;
+ private String avatar;
+ private String amount;
+ private String createtime;
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/bean/RoomUserCharmListBean.java b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/RoomUserCharmListBean.java
new file mode 100644
index 00000000..7fc4fa72
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/RoomUserCharmListBean.java
@@ -0,0 +1,21 @@
+package com.xscm.moduleutil.bean;
+
+import java.util.List;
+
+import lombok.Data;
+
+/**
+ *@author qx
+ *@data 2025/9/10
+ *@description: 魅力详情列表
+ */
+@Data
+public class RoomUserCharmListBean {
+ private int user_id;
+ private String total_price;
+ private String nickname;
+ private String avatar;
+ private String user_code;
+ private int charm;
+ private List icon ;
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/bean/SearchAll.java b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/SearchAll.java
new file mode 100644
index 00000000..8e0ec093
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/SearchAll.java
@@ -0,0 +1,11 @@
+package com.xscm.moduleutil.bean;
+
+import java.util.List;
+
+import lombok.Data;
+
+@Data
+public class SearchAll {
+ private List rooms;
+ private List users;
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/bean/XLHBean.java b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/XLHBean.java
new file mode 100644
index 00000000..896c9af7
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/XLHBean.java
@@ -0,0 +1,26 @@
+package com.xscm.moduleutil.bean;
+
+import com.xscm.moduleutil.bean.blindboxwheel.BlindBoxBean;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ *@author qx
+ *@data 2025/9/2
+ *@description: 巡乐会开始后推送的信息
+ */
+@Data
+public class XLHBean implements Serializable {
+ private static final long serialVersionUID = 1L;
+ private String text;
+ private String room_id;
+
+ private int from_type ;//100:巡乐会进度更新 101:巡乐会即将开始 102:巡乐会已经开始 103:巡乐会有人锁定了礼物 104:巡乐会结束落包
+ private BlindBoxBean.XlhData xlh_data;
+ private UserInfo FromUserInfo;
+ private String end_time;
+ private BlindBoxBean.xlhUser room_user;
+ private String gift_num;
+
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/bean/blindboxwheel/BlindBoxBean.java b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/blindboxwheel/BlindBoxBean.java
new file mode 100644
index 00000000..ec7b2885
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/blindboxwheel/BlindBoxBean.java
@@ -0,0 +1,164 @@
+package com.xscm.moduleutil.bean.blindboxwheel;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.xscm.moduleutil.bean.GiftBean;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import lombok.Data;
+
+/**
+ *@author qx
+ *@data 2025/8/27
+ *@description: 获取活动礼物列表
+ */
+@Data
+public class BlindBoxBean {
+ private String title;
+ private String rule_url;
+ private String rule;
+ private int box_price ;///每一次抽奖的价格
+ private String xlh_end_time;///巡乐会结束时间
+ private int is_xlh; ///是否开启巡乐会 0 关闭 1 开启
+ private Object xlh_data;
+ private List gift_list;
+ private String end_time;//巡乐会结束时间
+
+ private GiveGift give_homeowner_gift;//房主礼物
+ private GiveGift locking_gift;//锁定礼物
+
+
+ private xlhUser xlh_user;//巡乐会中奖用户
+ private xlhUser homeowner_user;//房主信息
+
+ public boolean isXlhDataArray() {
+ return xlh_data instanceof JsonArray || xlh_data instanceof List;
+ }
+
+ public boolean isXlhDataObject() {
+ return xlh_data instanceof JsonObject || xlh_data instanceof Map || xlh_data instanceof XlhData;
+ }
+
+ public List getXlhDataAsList() {
+ if (isXlhDataArray()) {
+ // 转换为List
+ return (List) xlh_data;
+ }
+ return new ArrayList<>();
+ }
+
+ public XlhData getXlhDataAsObject() {
+ if (isXlhDataObject()) {
+ // 如果已经是XlhData类型,直接返回
+ if (xlh_data instanceof XlhData) {
+ return (XlhData) xlh_data;
+ }
+ // 如果是Map类型(Gson解析后的LinkedTreeMap),手动转换
+ else if (xlh_data instanceof Map) {
+ Map map = (Map) xlh_data;
+ XlhData xlhData = new XlhData();
+
+ // 安全地转换各个字段
+ Object waitingStartNum = map.get("waiting_start_num");
+ if (waitingStartNum != null) {
+ xlhData.setWaiting_start_num(waitingStartNum.toString());
+ }
+
+ Object startNum = map.get("start_num");
+ if (startNum != null) {
+ xlhData.setStart_num(startNum.toString());
+ }
+
+ Object currentNum = map.get("current_num");
+ if (currentNum != null) {
+ if (currentNum instanceof Number) {
+ xlhData.setCurrent_num(((Number) currentNum).intValue());
+ } else {
+ try {
+ xlhData.setCurrent_num(Integer.parseInt(currentNum.toString()));
+ } catch (NumberFormatException e) {
+ xlhData.setCurrent_num(0);
+ }
+ }
+ }
+
+ Object status = map.get("status");
+ if (status != null) {
+ if (status instanceof Number) {
+ xlhData.setStatus(((Number) status).intValue());
+ } else {
+ try {
+ xlhData.setStatus(Integer.parseInt(status.toString()));
+ } catch (NumberFormatException e) {
+ xlhData.setStatus(0);
+ }
+ }
+ }
+ Object endTime = map.get("end_time");
+ if (endTime != null) {
+ if (endTime instanceof String){
+ xlhData.setEnd_time(endTime.toString());
+ }else {
+ xlhData.setEnd_time(endTime.toString());
+ }
+ }
+
+ return xlhData;
+ }
+ // 如果是JsonObject,也需要转换
+ else if (xlh_data instanceof JsonObject) {
+ JsonObject jsonObject = (JsonObject) xlh_data;
+ XlhData xlhData = new XlhData();
+
+ if (jsonObject.has("waiting_start_num")) {
+ xlhData.setWaiting_start_num(jsonObject.get("waiting_start_num").getAsString());
+ }
+
+ if (jsonObject.has("start_num")) {
+ xlhData.setStart_num(jsonObject.get("start_num").getAsString());
+ }
+
+ if (jsonObject.has("current_num")) {
+ xlhData.setCurrent_num(jsonObject.get("current_num").getAsInt());
+ }
+
+ if (jsonObject.has("status")) {
+ xlhData.setStatus(jsonObject.get("status").getAsInt());
+ }
+ if (jsonObject.has("end_time")){
+ xlhData.setEnd_time(jsonObject.get("end_time").getAsString());
+ }
+
+ return xlhData;
+ }
+ }
+ return null;
+ }
+ @Data
+ public static class XlhData {
+ private String waiting_start_num;//等待开始需要达到的次数
+ private String start_num;//巡乐会开启需要达到的次数
+ private int current_num;//当前已抽奖次数
+ private int status;
+ private String end_time;
+ }
+ @Data
+ public static class GiveGift {
+ private int gift_id;
+ private String gift_name;
+ private String base_image;
+ private String gift_num;
+ private String gift_price;
+ }
+
+ @Data
+ public static class xlhUser {
+ private String user_id;
+ private String nickname;
+ private String avatar;
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/bean/blindboxwheel/BlindReslutBean.java b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/blindboxwheel/BlindReslutBean.java
new file mode 100644
index 00000000..f59d4877
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/blindboxwheel/BlindReslutBean.java
@@ -0,0 +1,23 @@
+package com.xscm.moduleutil.bean.blindboxwheel;
+
+import java.util.List;
+
+import lombok.Data;
+
+/**
+ * @author qx
+ * @data 2025/8/27
+ * @description: 礼物抽奖结果
+ */
+@Data
+public class BlindReslutBean {
+ private String blind_box_turntable_id;//本次抽奖标识 Id 效果完成后用这个值推送发放
+ private List reslut_list;
+
+ @Data
+ public class ReslutList {
+ private int gift_id;//中奖礼物Id
+ private int count;//中奖礼物数量
+ }
+
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/bean/blindboxwheel/XlhDrawBean.java b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/blindboxwheel/XlhDrawBean.java
new file mode 100644
index 00000000..b17cde40
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/blindboxwheel/XlhDrawBean.java
@@ -0,0 +1,16 @@
+package com.xscm.moduleutil.bean.blindboxwheel;
+
+import lombok.Data;
+/**
+ *@author qx
+ *@data 2025/9/4
+ *@description: 巡乐会抽奖
+ */
+@Data
+public class XlhDrawBean {
+ private int gift_id;
+ private String gift_name;
+ private String base_image;
+ private String gift_price;
+ private int count ;
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/bean/room/CloseBean.java b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/room/CloseBean.java
new file mode 100644
index 00000000..686d013f
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/room/CloseBean.java
@@ -0,0 +1,19 @@
+package com.xscm.moduleutil.bean.room;
+
+import lombok.Data;
+
+// TODO: 2025/3/10 亲密关系
+@Data
+public class CloseBean {
+ private String id;//关系id
+ private String user_id;//用户id
+ private String head_picture; //用户头像
+ private String nickname;//用户昵称
+ private String sex;//性别
+ private String contact_end_time;//剩余天数
+ private String heart_value;//心动值
+ private String friend_config_id;//关系类型id
+ private String relationship_icon ;//关系类型图标
+
+
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/bean/room/Emotion.kt b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/room/Emotion.kt
new file mode 100644
index 00000000..baee14b0
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/room/Emotion.kt
@@ -0,0 +1,6 @@
+package com.xscm.moduleutil.bean.room
+
+class Emotion {
+ var type_name: String? = ""
+ var id: Int? = 0
+}
\ No newline at end of file
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/bean/room/EmotionDeatils.kt b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/room/EmotionDeatils.kt
new file mode 100644
index 00000000..a2007b0c
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/room/EmotionDeatils.kt
@@ -0,0 +1,20 @@
+package com.xscm.moduleutil.bean.room
+
+data class EmotionDeatils(
+ var id: Int? = 0,
+ var pid: Int? = 0,
+ var type_id: Int? = 0,
+ var name: String? = "",
+ var image: String? = "",
+ var animate_image : String? = "",
+ var children: List? =ArrayList (),
+)
+
+data class Children(
+ var id: Int? = 0,
+ var pid: Int? = 0,
+ var type_id: Int? = 0,
+ var name: String? = "",
+ var image: String? = "",
+ var animate_image : String? = "",
+)
\ No newline at end of file
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/bean/room/FriendUserBean.java b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/room/FriendUserBean.java
new file mode 100644
index 00000000..7d1729bd
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/room/FriendUserBean.java
@@ -0,0 +1,27 @@
+package com.xscm.moduleutil.bean.room;
+
+import java.io.Serializable;
+
+import lombok.Data;
+
+/**
+ *@author qx
+ *@data 2025/8/24
+ *@description: 结束后返回的关系数据,
+ */
+@Data
+public class FriendUserBean implements Serializable {
+ private int is_cp;//1:卡关系 0:不卡关系
+ private String user1_id;//王者位用户1id
+ private String user1_avatar;//王者位用户1头像
+ private String user1_nickname;//王者位用户1昵称
+ private String user2_id;//王者位用户2id
+ private String user2_avatar;//王者位用户2头像
+ private String user2_nickname;//王者位用户2昵称
+
+ private String heart_value;//连线值
+
+ private String heart_id;//连线值ID
+ private String relation_name;//什么关系
+
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/bean/room/RedResultBean.java b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/room/RedResultBean.java
new file mode 100644
index 00000000..2370c455
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/room/RedResultBean.java
@@ -0,0 +1,27 @@
+package com.xscm.moduleutil.bean.room;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 红包的结果集
+ */
+@Data
+public class RedResultBean {
+ private String redUserName;//发布红包的用户名称
+ private String redUserAvatar;//发布红包的用户头像
+ private String redTitle;//发布红包的备注
+ private String redJb;//中奖的金币
+ private String redyl;//已经领取的个数
+
+ private List redList;
+
+ @Data
+ public static class RedBean {
+ private String redUserName;
+ private String redUserAvatar;
+ private String redNum;
+ private String redTime;
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/bean/room/RoomConcernDean.java b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/room/RoomConcernDean.java
new file mode 100644
index 00000000..5a34226b
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/room/RoomConcernDean.java
@@ -0,0 +1,12 @@
+package com.xscm.moduleutil.bean.room;
+
+import lombok.Data;
+
+// TODO: 2025/3/12 关系表
+@Data
+public class RoomConcernDean {
+
+ private String concernName;
+ private String concernType;
+
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/bean/room/RoomHourBean.java b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/room/RoomHourBean.java
new file mode 100644
index 00000000..9c7b0a26
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/bean/room/RoomHourBean.java
@@ -0,0 +1,28 @@
+package com.xscm.moduleutil.bean.room;
+
+import lombok.Data;
+
+import java.util.List;
+/**
+ *@author qx
+ *@data 2025/9/29
+ *@description:小时榜实体类
+ */
+@Data
+public class RoomHourBean {
+ private String time_range;
+ private List lists;
+
+ @Data
+ public class RoomListBean {
+ private String room_id;
+ private String room_name;
+ private int label_id;
+ private String room_cover;
+ private int total_price;
+ private String label_icon;
+ private int xlh_status;
+ private int redpacket_status;// >0 有红包,=0 没有红包
+ }
+
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/LotteryFragment.java b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/LotteryFragment.java
new file mode 100644
index 00000000..5f8ff0d3
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/LotteryFragment.java
@@ -0,0 +1,130 @@
+package com.xscm.moduleutil.dialog;
+
+import android.os.Bundle;
+import android.view.Choreographer;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.LinearLayoutManager;
+
+import com.scwang.smartrefresh.layout.api.RefreshLayout;
+import com.scwang.smartrefresh.layout.listener.OnRefreshLoadMoreListener;
+import com.xscm.moduleutil.R;
+import com.xscm.moduleutil.base.BaseMvpDialogFragment;
+import com.xscm.moduleutil.bean.GiftBean;
+import com.xscm.moduleutil.bean.WalletBean;
+import com.xscm.moduleutil.bean.blindboxwheel.BlindBoxBean;
+import com.xscm.moduleutil.bean.blindboxwheel.BlindReslutBean;
+import com.xscm.moduleutil.bean.blindboxwheel.XlhDrawBean;
+import com.xscm.moduleutil.databinding.DialogNewRankingXlhFragmentBinding;
+import com.xscm.moduleutil.databinding.FframentDataBinding;
+import com.xscm.moduleutil.dialog.giftLottery.GiftLotteryContacts;
+import com.xscm.moduleutil.dialog.giftLottery.GiftLotteryPresenter;
+import com.xscm.moduleutil.dialog.giftLottery.GiftRecordAdapte;
+import com.xscm.moduleutil.dialog.giftLottery.NewGiftRecordAdapte;
+
+import java.util.List;
+
+public class LotteryFragment extends BaseMvpDialogFragment implements GiftLotteryContacts.View {
+ private int page=1;
+ private String roomId;
+ private int type=-1;
+ private GiftRecordAdapte giftRecordAdapte;
+
+ @Override
+ protected GiftLotteryPresenter bindPresenter() {
+ return new GiftLotteryPresenter(this,getSelfActivity());
+ }
+ public static LotteryFragment newInstance(String giftBagId,int type) {
+ Bundle args = new Bundle();
+ LotteryFragment fragment = new LotteryFragment();
+ args.putString("roomId", giftBagId);
+ args.putInt("type",type);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ protected void initData() {
+ roomId = getArguments().getString("roomId");
+ type = getArguments().getInt("type");
+ MvpPre.xlhAllRecord(roomId, "1", "20",type);
+ }
+
+ @Override
+ protected void initView() {
+
+ mBinding.smartRefreshLayout.setOnRefreshLoadMoreListener(new OnRefreshLoadMoreListener() {
+ @Override
+ public void onRefresh(@NonNull RefreshLayout refreshLayout) {
+ page = 1;
+ MvpPre.xlhAllRecord(roomId, page+"", "20",type);
+
+ }
+
+ @Override
+ public void onLoadMore(@NonNull RefreshLayout refreshLayout) {
+ page++;
+ MvpPre.xlhAllRecord(roomId, page+"", "20",type);
+ }
+ });
+
+
+ giftRecordAdapte=new GiftRecordAdapte();
+ mBinding.recyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
+ mBinding.recyclerView.setAdapter(giftRecordAdapte);
+ }
+
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.fframent_data;
+ }
+
+ @Override
+ public void getGiftListSuccess(BlindBoxBean blindBoxBean) {
+
+ }
+
+ @Override
+ public void drawGiftListSuccess(BlindReslutBean blindReslutBean) {
+
+ }
+
+ @Override
+ public void getMyRecordSuccess(List data) {
+
+ }
+
+ @Override
+ public void getAllRecordSuccess(List data) {
+
+ if (data != null){
+ if (page==1){
+ giftRecordAdapte.setNewData(data);
+ }else {
+ giftRecordAdapte.addData(data);
+ }
+ }else {
+ if (page == 1) {
+ giftRecordAdapte.setNewData(null);
+ }
+ }
+ }
+
+
+ @Override
+ public void finishRefreshLoadMore() {
+ mBinding.smartRefreshLayout.finishRefresh();
+ mBinding.smartRefreshLayout.finishLoadMore();
+ }
+
+ @Override
+ public void wallet(WalletBean walletBean) {
+
+ }
+
+ @Override
+ public void xlhChouSuccess(List data) {
+
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/NewPeopleDialog.java b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/NewPeopleDialog.java
new file mode 100644
index 00000000..f05bebaf
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/NewPeopleDialog.java
@@ -0,0 +1,368 @@
+package com.xscm.moduleutil.dialog;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Paint;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+
+import com.blankj.utilcode.util.ScreenUtils;
+import com.blankj.utilcode.util.ToastUtils;
+import com.xscm.moduleutil.R;
+import com.xscm.moduleutil.adapter.HeavenGiftAdapter;
+import com.xscm.moduleutil.bean.BaseListData;
+import com.xscm.moduleutil.bean.FirstChargeGiftBean;
+import com.xscm.moduleutil.bean.RoonGiftModel;
+import com.xscm.moduleutil.color.ThemeableDrawableUtils;
+import com.xscm.moduleutil.databinding.DialogNewPeopleBinding;
+import com.xscm.moduleutil.http.BaseObserver;
+import com.xscm.moduleutil.http.RetrofitClient;
+import com.xscm.moduleutil.utils.ColorManager;
+import com.xscm.moduleutil.widget.dialog.BaseDialog;
+import com.zhpan.bannerview.indicator.DrawableIndicator;
+import com.zhpan.indicator.base.IIndicator;
+import com.zhpan.indicator.enums.IndicatorSlideMode;
+
+import io.reactivex.disposables.Disposable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author
+ * @data
+ * @description: 新人好礼
+ */
+public class NewPeopleDialog extends BaseDialog {
+
+ HeavenGiftAdapter heavenGiftAdapter;
+ FirstChargeGiftBean firstChargeGiftBean;
+ private int type;
+
+ public NewPeopleDialog(@NonNull Context context) {
+ super(context, R.style.BaseDialogStyleH);
+ }
+
+ @Override
+ public int getLayoutId() {
+ return R.layout.dialog_new_people;
+ }
+
+ @Override
+ public void initView() {
+ setCancelable(false);
+ setCanceledOnTouchOutside(false);
+ Window window = getWindow();
+ window.setLayout((int) (ScreenUtils.getScreenWidth() * 375.f / 375), WindowManager.LayoutParams.WRAP_CONTENT);
+ mBinding.ivClose.setOnClickListener(v -> dismiss());
+
+ heavenGiftAdapter = new HeavenGiftAdapter();
+ mBinding.bannerViewPager
+ .setPageMargin(15)
+ .setAutoPlay(false)
+ .setRevealWidth(0, 0)
+ .setIndicatorVisibility(View.VISIBLE)
+ .setIndicatorView(getVectorDrawableIndicator())
+ .setIndicatorSlideMode(IndicatorSlideMode.NORMAL)
+ .setAdapter(heavenGiftAdapter)
+ .create();
+
+ mBinding.rg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(RadioGroup radioGroup, int i) {
+ if (firstChargeGiftBean == null || firstChargeGiftBean.getGift_bag().size() == 0) {
+ ToastUtils.showShort("暂无礼包");
+ return;
+ }
+
+ if (i == R.id.btn_0) {
+ List list = new ArrayList<>();
+ if (firstChargeGiftBean.getGift_bag().size() > 1) {
+ mBinding.tvTitle1.setText(firstChargeGiftBean.getGift_bag().get(0).getTitle1());
+ mBinding.tvTitle2.setText(firstChargeGiftBean.getGift_bag().get(0).getTitle2());
+ mBinding.tvTitle2.setPaintFlags(Paint.STRIKE_THRU_TEXT_FLAG);
+ mBinding.btn0.setText(firstChargeGiftBean.getGift_bag().get(0).getName());
+ list.addAll(firstChargeGiftBean.getGift_bag().get(0).getGift_list());
+ mBinding.bannerViewPager.create(baseListData(list, 4));
+ }
+ type = 1;
+ } else if (i == R.id.btn_1) {
+ List list = new ArrayList<>();
+ if (firstChargeGiftBean.getGift_bag().size() > 2) {
+ mBinding.tvTitle1.setText(firstChargeGiftBean.getGift_bag().get(1).getTitle1());
+ mBinding.tvTitle2.setText(firstChargeGiftBean.getGift_bag().get(1).getTitle2());
+ mBinding.tvTitle2.setPaintFlags(Paint.STRIKE_THRU_TEXT_FLAG);
+ mBinding.btn1.setText(firstChargeGiftBean.getGift_bag().get(1).getName());
+ list.addAll(firstChargeGiftBean.getGift_bag().get(1).getGift_list());
+ mBinding.bannerViewPager.create(baseListData(list, 4));
+ }
+ type = 2;
+ } else if (i == R.id.btn_2) {
+ List list = new ArrayList<>();
+ if (firstChargeGiftBean.getGift_bag().size() > 3) {
+ if (firstChargeGiftBean.getGift_bag().get(2) != null) {
+ mBinding.tvTitle1.setText(firstChargeGiftBean.getGift_bag().get(2).getTitle1());
+ mBinding.tvTitle2.setText(firstChargeGiftBean.getGift_bag().get(2).getTitle2());
+ mBinding.tvTitle2.setPaintFlags(Paint.STRIKE_THRU_TEXT_FLAG);
+ mBinding.btn2.setText(firstChargeGiftBean.getGift_bag().get(2).getName());
+ list.addAll(firstChargeGiftBean.getGift_bag().get(2).getGift_list());
+ mBinding.bannerViewPager.create(baseListData(list, 4));
+ type = 3;
+ }
+ }
+ } else if (i == R.id.btn_3) {
+ List list = new ArrayList<>();
+ if (firstChargeGiftBean.getGift_bag().size() >= 4) {
+ mBinding.tvTitle1.setText(firstChargeGiftBean.getGift_bag().get(3).getTitle1());
+ mBinding.tvTitle2.setText(firstChargeGiftBean.getGift_bag().get(3).getTitle2());
+ mBinding.btn3.setText(firstChargeGiftBean.getGift_bag().get(3).getName());
+ mBinding.tvTitle2.setPaintFlags(0); // 清除所有绘制标志
+ list.addAll(firstChargeGiftBean.getGift_bag().get(3).getGift_list());
+ mBinding.bannerViewPager.create(baseListData(list, 4));
+ type = 4;
+ }
+ }
+
+ }
+ });
+ mBinding.rg.setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+
+ return false;
+ }
+ });
+
+ mBinding.tvInvite.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+// RechargeDialogFragment.show(roomId, getSupportFragmentManager());
+ if (listener != null) {
+ listener.onFirstChargeConfirmed(firstChargeGiftBean, type);
+ }
+
+ }
+ });
+
+
+ }
+
+ public interface OnFirstChargeListener {
+ void onFirstChargeConfirmed(FirstChargeGiftBean giftBean, int type);
+
+ void onFirstChargeCancelled();
+ }
+
+ private OnFirstChargeListener listener;
+
+ // 设置监听器的方法
+ public void setOnFirstChargeListener(OnFirstChargeListener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ public void initData() {
+ RetrofitClient.getInstance().getNewChargeGift(new BaseObserver() {
+ @Override
+ public void onSubscribe(Disposable d) {
+
+ }
+
+ @Override
+ public void onNext(FirstChargeGiftBean firstChargeGiftBean) {
+ if (firstChargeGiftBean != null) {
+ showGift(firstChargeGiftBean);
+ }
+ }
+ });
+
+ }
+
+ private List> baseListData(List list, int chunkSize) {
+ List> baseListData = new ArrayList<>();
+ for (int i = 0; i < list.size(); i += chunkSize) {
+ BaseListData baseListData1 = new BaseListData<>();
+ baseListData1.setData(list.subList(i, Math.min(i + chunkSize, list.size())));
+ baseListData.add(baseListData1);
+ }
+ return baseListData;
+ }
+
+ private IIndicator getVectorDrawableIndicator() {
+ int dp6 = getResources().getDimensionPixelOffset(com.xscm.moduleutil.R.dimen.dp_6);
+ return new DrawableIndicator(getContext())
+ .setIndicatorGap(getResources().getDimensionPixelOffset(com.xscm.moduleutil.R.dimen.dp_2_5))
+ .setIndicatorDrawable(com.xscm.moduleutil.R.drawable.banner_indicator_nornal, com.xscm.moduleutil.R.drawable.banner_indicator_focus)
+ .setIndicatorSize(getResources().getDimensionPixelOffset(com.xscm.moduleutil.R.dimen.dp_13), dp6, getResources().getDimensionPixelOffset(com.xscm.moduleutil.R.dimen.dp_13), dp6);
+ }
+
+ private Resources getResources() {
+ return getContext().getResources();
+ }
+
+ public void showGift(FirstChargeGiftBean firstChargeGiftBean) {
+ this.firstChargeGiftBean = firstChargeGiftBean;
+ mBinding.rg.check(R.id.btn_0);
+ if (firstChargeGiftBean.getGift_bag() != null && firstChargeGiftBean.getGift_bag().size() > 0) {
+ if (firstChargeGiftBean.getGift_bag().size() >= 0) {
+ type = 1;
+ List list = new ArrayList<>();
+ mBinding.tvTitle1.setText(firstChargeGiftBean.getGift_bag().get(0).getTitle1());
+ mBinding.tvTitle2.setText(firstChargeGiftBean.getGift_bag().get(0).getTitle2());
+ mBinding.btn0.setText(firstChargeGiftBean.getGift_bag().get(0).getName());
+ list.addAll(firstChargeGiftBean.getGift_bag().get(0).getGift_list());
+ mBinding.bannerViewPager.create(baseListData(list, 4));
+
+
+ mBinding.btn1.setText(firstChargeGiftBean.getGift_bag().get(1).getName());
+ mBinding.btn2.setText(firstChargeGiftBean.getGift_bag().get(2).getName());
+ mBinding.btn3.setText(firstChargeGiftBean.getGift_bag().get(3).getName());
+
+ initGiftBagButtonStatus(firstChargeGiftBean);
+ } else if (firstChargeGiftBean.getGift_bag().size() == 2) {
+// mBinding.rg.check(R.id.btn_0);
+// mBinding.btn1.setVisibility(View.VISIBLE);
+// mBinding.btn2.setVisibility(View.INVISIBLE);
+ } else if (firstChargeGiftBean.getGift_bag().size() == 3) {
+// mBinding.rg.check(R.id.btn_0);
+// mBinding.btn1.setVisibility(View.VISIBLE);
+// mBinding.btn2.setVisibility(View.VISIBLE);
+ }
+// mBinding.rg.check(R.id.btn_0);
+ }
+
+ }
+
+ private boolean isstatus = true;
+
+ private void initGiftBagButtonStatus(FirstChargeGiftBean firstChargeGiftBean) {
+ // 1. 准备按钮列表:顺序与gift_bag中的元素顺序一一对应
+ List buttonList = Arrays.asList(
+ mBinding.btn0,
+ mBinding.btn1,
+ mBinding.btn2
+ // 未来加按钮:mBinding.btn3, mBinding.btn4...
+ );
+
+ // 2. 空安全检查:先判断核心对象/列表非空
+ if (firstChargeGiftBean != null && firstChargeGiftBean.getGift_bag() != null) {
+ List giftBagList = firstChargeGiftBean.getGift_bag();
+
+ // 3. 循环处理每个按钮
+ for (int i = 0; i < buttonList.size(); i++) {
+ RadioButton currentBtn = buttonList.get(i);
+ // 4. 索引防护:若gift_bag列表长度不足,默认按status=0处理
+ int status = (i < giftBagList.size()) ? giftBagList.get(i).getStatus() : 0;
+ // 检查是否有status=0的情况,如果有则将isStatus设为false
+ if (status == 0) {
+ isstatus = false;
+ }
+ setButtonStatus(currentBtn, status, i); // 增加索引参数
+ }
+ updateRechargeTextViewStatus(isstatus, 0);
+
+
+ } else {
+ // 5. 兜底逻辑:数据为空时,所有按钮按status=0处理
+ for (int i = 0; i < buttonList.size(); i++) {
+ setButtonStatus(buttonList.get(i), 0, i);
+ }
+ }
+
+ }
+
+ /**
+ * 工具方法:统一设置单个按钮的状态
+ *
+ * @param button 要设置的RadioButton
+ * @param status 状态值
+ * @param index 按钮索引,用于标识不同按钮
+ */
+ private void setButtonStatus(RadioButton button, int status, int index) {
+ if (button == null) return;
+
+ // 移除之前的点击监听器,避免重复设置
+ button.setOnClickListener(null);
+
+ if (status == 1) {
+ // 可用状态
+ button.setEnabled(true);
+ // button.setChecked(true);
+
+ // 恢复充值按钮状态
+ updateRechargeTextViewStatus(true, index);
+ button.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ updateRechargeTextViewStatus(true, index);
+ }
+ });
+
+ } else {
+ // status=0和其他状态:特殊处理
+ button.setEnabled(true); // 保持可点击以响应交互
+ // button.setChecked(false);
+ // 设置特殊背景(根据需求修改为你的资源)
+ button.setBackground(getResources().getDrawable(R.drawable.bf_e9));
+ button.setTextColor(getResources().getColor(R.color.colorBlack65));
+ updateRechargeTextViewStatus(false, index);
+ // 添加点击事件
+ button.setOnClickListener(v -> {
+ // 点击时再次确认是status=0状态才处理
+ button.setBackground(getResources().getDrawable(R.drawable.banner_indicator_focus));
+ // 更新充值按钮状态为不可点击
+ updateRechargeTextViewStatus(false, index);
+ });
+ }
+ }
+
+
+ /**
+ * 检查是否至少有一个元素达标(status == 1)
+ */
+ public static boolean hasAnyQualified(List giftBagList) {
+ // 空列表处理 + 任意匹配检查
+ return giftBagList != null && !giftBagList.isEmpty()
+ && giftBagList.stream()
+ .anyMatch(gift -> gift.getStatus() == 1);
+ }
+
+ /**
+ * 更新充值TextView的状态
+ *
+ * @param enabled 是否可点击
+ * @param index 关联的按钮索引
+ */
+ private void updateRechargeTextViewStatus(boolean enabled, int index) {
+ TextView rechargeTv = mBinding.tvInvite; // 假设充值按钮的id是tvRecharge
+ if (rechargeTv == null) return;
+
+ // 设置是否可点击
+ rechargeTv.setEnabled(enabled);
+
+ // 根据状态和索引设置不同背景
+ if (enabled) {
+ ThemeableDrawableUtils.setThemeableRoundedBackground(mBinding.tvInvite, ColorManager.getInstance().getPrimaryColorInt(), 53);
+ mBinding.tvInvite.setTextColor(ColorManager.getInstance().getButtonColorInt());
+
+ } else {
+
+
+ // 不可点击状态的背景
+ ThemeableDrawableUtils.setThemeableRoundedBackground(mBinding.tvInvite, ColorManager.getInstance().getButtonColorInt(), 53);
+ mBinding.tvInvite.setTextColor(getResources().getColor(R.color.colorBlack65));
+
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/RoomAuctionWebViewDialog.java b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/RoomAuctionWebViewDialog.java
new file mode 100644
index 00000000..ed1ff088
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/RoomAuctionWebViewDialog.java
@@ -0,0 +1,228 @@
+package com.xscm.moduleutil.dialog;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.webkit.JavascriptInterface;
+import android.webkit.WebSettings;
+import android.webkit.WebViewClient;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+
+import com.alibaba.android.arouter.launcher.ARouter;
+import com.blankj.utilcode.util.LogUtils;
+import com.blankj.utilcode.util.ScreenUtils;
+import com.tencent.imsdk.v2.V2TIMConversation;
+import com.tencent.mm.opensdk.modelbiz.WXOpenCustomerServiceChat;
+import com.tencent.mm.opensdk.openapi.IWXAPI;
+import com.tencent.mm.opensdk.openapi.WXAPIFactory;
+import com.tencent.qcloud.tuicore.TUIConstants;
+import com.tencent.qcloud.tuikit.tuichat.classicui.page.TUIC2CChatActivity;
+import com.tencent.qcloud.tuikit.tuichat.classicui.page.TUIGroupChatActivity;
+import com.xscm.moduleutil.R;
+import com.xscm.moduleutil.base.CommonAppContext;
+import com.xscm.moduleutil.base.RoomManager;
+import com.xscm.moduleutil.databinding.DialogRoomAuctionWebviewBinding;
+import com.xscm.moduleutil.databinding.WebViewDialogBinding;
+import com.xscm.moduleutil.utils.ARouteConstants;
+import com.xscm.moduleutil.widget.dialog.BaseDialog;
+/**
+ *@author qx
+ *@data 2025/9/24
+ *@description: 这是拍卖房的规则界面
+ */
+public class RoomAuctionWebViewDialog extends BaseDialog {
+
+ String mUrl;
+ int type;//10:天空之境 11:岁月之城 12:时空之巅
+
+ public RoomAuctionWebViewDialog(@NonNull Context context, Bundle args) {
+ super(context, R.style.BaseDialogStyleH);
+ this.mUrl = args.getString("url");
+ this.type = args.getInt("type");
+ initData1();
+ }
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (getWindow() != null) {
+ // 获取屏幕尺寸
+ android.util.DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
+ // 设置高度为屏幕高度的80%
+ android.view.WindowManager.LayoutParams params = getWindow().getAttributes();
+ params.height = (int) (displayMetrics.heightPixels * 0.9);
+ params.width = android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+ getWindow().setAttributes(params);
+ }
+ }
+ @Override
+ public int getLayoutId() {
+ return R.layout.dialog_room_auction_webview;
+ }
+
+ @Override
+ public void initView() {
+ setCancelable(true);
+ setCanceledOnTouchOutside(true);
+ Window window = getWindow();
+ assert window != null;
+ window.setGravity(Gravity.BOTTOM);
+ window.setLayout((int) (ScreenUtils.getScreenWidth() * 320.f / 375), WindowManager.LayoutParams.MATCH_PARENT);
+ mBinding.topBar.setTitle("规则");
+ mBinding.topBar.getIvBack().setOnClickListener(v -> dismiss());
+
+ }
+
+ @Override
+ public void initData() {
+
+ }
+
+
+ public void initData1() {
+// WebSettings webSettings = mBinding.webView.getSettings();
+// webSettings.setUseWideViewPort(true);
+// webSettings.setLoadWithOverviewMode(true);
+// webSettings.setJavaScriptEnabled(true);
+// webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE); //关闭webview中缓存
+// //增加JSBridge
+// mBinding.webView.addJavascriptInterface(new WebAppInterface(getContext()), "Android");
+//// mBinding.webView.addJavascriptInterface(new WebViewBridgeConfig(title), WebViewBridgeConfig.NAME);
+// webSettings.setBuiltInZoomControls(false);
+// webSettings.setSupportZoom(false);
+// webSettings.setDomStorageEnabled(true);
+// webSettings.setBlockNetworkImage(false);//解决图片不显示
+// // 启用 WebView 内容的滚动
+// mBinding.webView.setVerticalScrollBarEnabled(true);
+// mBinding.webView.setScrollbarFadingEnabled(true);
+// webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
+// webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
+// mBinding.webView.setHorizontalScrollBarEnabled(false);//水平不显示
+// mBinding.webView.setVerticalScrollBarEnabled(false); //垂直不显示
+// mBinding.webView.setWebViewClient(new WebViewClient());
+// mBinding.webView.setBackgroundColor(Color.TRANSPARENT);
+// mBinding.webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+//
+// mBinding.webView.requestFocus();
+// mBinding.webView.loadUrl(mUrl);
+
+ WebSettings webSettings = mBinding.webView.getSettings();
+ webSettings.setUseWideViewPort(true);
+ webSettings.setLoadWithOverviewMode(true);
+ webSettings.setJavaScriptEnabled(true);
+ webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE); //关闭webview中缓存
+ //增加JSBridge
+ mBinding.webView.addJavascriptInterface(new WebAppInterface(getContext()), "Android");
+// mBinding.webView.addJavascriptInterface(new WebViewBridgeConfig(title), WebViewBridgeConfig.NAME);
+ webSettings.setBuiltInZoomControls(false);
+ webSettings.setSupportZoom(false);
+ webSettings.setDomStorageEnabled(true);
+ webSettings.setBlockNetworkImage(false);//解决图片不显示
+ webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
+ webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
+ // 启用 WebView 内容的滚动,但隐藏滚动条
+ mBinding.webView.setHorizontalScrollBarEnabled(false);//水平不显示
+ mBinding.webView.setVerticalScrollBarEnabled(false); //垂直不显示滚动条
+ mBinding.webView.setWebViewClient(new WebViewClient());
+ mBinding.webView.setBackgroundColor(Color.TRANSPARENT);
+ mBinding.webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+
+ // 确保内容可以滚动
+ webSettings.setDomStorageEnabled(true);
+
+ mBinding.webView.requestFocus();
+ mBinding.webView.loadUrl(mUrl);
+ }
+
+ private Resources getResources() {
+ return getContext().getResources();
+ }
+
+ public class WebAppInterface {
+ Context mContext;
+
+ WebAppInterface(Context c) {
+ mContext = c;
+ }
+
+ // 被 H5 调用的方法
+ @JavascriptInterface
+ public void showToast(String toast) {
+ Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
+ }
+
+ @JavascriptInterface
+ public void closeWeb() {
+ LogUtils.e("value: ");
+ dismiss();
+ }
+
+ @JavascriptInterface
+ public void customerService() {
+ String appId = CommonAppContext.getInstance().getCurrentEnvironment().getWxAppId(); // 填移动应用(App)的 AppId
+ IWXAPI api = WXAPIFactory.createWXAPI(mContext, appId);
+
+// 判断当前版本是否支持拉起客服会话
+ WXOpenCustomerServiceChat.Req req = new WXOpenCustomerServiceChat.Req();
+ req.corpId = "ww1de4300858c0b461"; // 企业ID
+ req.url = "https://work.weixin.qq.com/kfid/kfcb3d23a59c188a0e7"; // 客服URL
+ api.sendReq(req);
+ }
+
+ @JavascriptInterface
+ public void jumpRoomPage(String room_id) {
+ RoomManager.getInstance().fetchRoomDataAndEnter(getContext(), room_id,"");
+
+// ARouter.getInstance().build(ARouteConstants.ROOM_DETAILS).withString("form", "首页").withString("roomId", room_id).navigation();
+ }
+ @JavascriptInterface
+ public void jumpWebPage(String objects) {
+// ARouter.getInstance().build(ARouteConstants.USER_HOME_PAGE).navigation();
+ ARouter.getInstance().build(ARouteConstants.USER_HOME_PAGE).withString("userId", objects).navigation();
+ }
+
+ @JavascriptInterface
+ public void enterGroupChat(String group_id,String cover,String guild_name) {
+ Intent intent = new Intent(mContext, TUIGroupChatActivity.class);
+ intent.putExtra(TUIConstants.TUIChat.CHAT_ID, group_id);
+ intent.putExtra(TUIConstants.TUIChat.CHAT_TYPE, V2TIMConversation.V2TIM_GROUP);
+ mContext.startActivity(intent);
+ }
+
+ @JavascriptInterface
+ public void chatWithUser(String user_id,String nickname) {
+ Intent intent = new Intent(mContext, TUIC2CChatActivity.class);
+ intent.putExtra(TUIConstants.TUIChat.CHAT_ID, user_id);
+ intent.putExtra(TUIConstants.TUIChat.CHAT_TYPE, V2TIMConversation.V2TIM_C2C);
+ mContext.startActivity(intent);
+ }
+ @JavascriptInterface
+ public void exchange(){
+ ARouter.getInstance().build(ARouteConstants.CURRENCY).navigation();
+
+ }
+
+ @JavascriptInterface
+ public void Withdrawal() {
+ ARouter.getInstance().build(ARouteConstants.WITHDRAWAL_ACTIVITY).navigation();
+ }
+
+ @JavascriptInterface
+ public void enterAuthent() {//实名认证
+ ARouter.getInstance().build(ARouteConstants.REAL_NAME_ACTIVITY2).navigation();
+ }
+
+ @JavascriptInterface
+ public void Recharge(){
+ ARouter.getInstance().build(ARouteConstants.RECHARGE_ACTIVITY).navigation();
+ }
+ }
+
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/GiftItemAdapter.java b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/GiftItemAdapter.java
new file mode 100644
index 00000000..c46b15d7
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/GiftItemAdapter.java
@@ -0,0 +1,22 @@
+package com.xscm.moduleutil.dialog.giftLottery;
+
+import com.chad.library.adapter.base.BaseQuickAdapter;
+import com.chad.library.adapter.base.BaseViewHolder;
+import com.xscm.moduleutil.R;
+import com.xscm.moduleutil.bean.blindboxwheel.XlhDrawBean;
+import com.xscm.moduleutil.utils.ImageUtils;
+
+public class GiftItemAdapter extends BaseQuickAdapter {
+ public GiftItemAdapter() {
+ super(R.layout.item_xlh_gift);
+ }
+
+ @Override
+ protected void convert(BaseViewHolder helper, XlhDrawBean item) {
+
+ helper.setText(R.id.tv_gift_name, item.getGift_name()+"x"+item.getCount());
+ ImageUtils.loadHeadCC(item.getBase_image(),helper.getView(R.id.gift_img));
+ helper.setText(R.id.tv_gift_num, item.getGift_price());
+
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/GiftLottery.java b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/GiftLottery.java
new file mode 100644
index 00000000..7240b6d6
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/GiftLottery.java
@@ -0,0 +1,27 @@
+package com.xscm.moduleutil.dialog.giftLottery;
+
+import lombok.Data;
+
+/**
+ *@author qx
+ *@data 2025/8/25
+ *@description: 盲盒抽奖的实体类
+ */
+@Data
+public class GiftLottery {
+ private String id;
+ private String icon;
+ private String number;
+ private String name;
+ private String price;
+ private boolean isSelected;
+
+ public GiftLottery(String id, String icon, String number, String name, String price, boolean isSelected) {
+ this.id = id;
+ this.icon = icon;
+ this.number = number;
+ this.name = name;
+ this.price = price;
+ this.isSelected = isSelected;
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/GiftLotteryAdapter.java b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/GiftLotteryAdapter.java
new file mode 100644
index 00000000..fb0db0b3
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/GiftLotteryAdapter.java
@@ -0,0 +1,88 @@
+package com.xscm.moduleutil.dialog.giftLottery;
+
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.os.Handler;
+import android.os.Looper;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.style.ForegroundColorSpan;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.Animation;
+import android.view.animation.ScaleAnimation;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.chad.library.adapter.base.BaseQuickAdapter;
+import com.chad.library.adapter.base.BaseViewHolder;
+import com.xscm.moduleutil.R;
+import com.xscm.moduleutil.bean.GiftBean;
+import com.xscm.moduleutil.bean.GiftBoxBean;
+import com.xscm.moduleutil.utils.ImageUtils;
+
+import java.util.List;
+import java.util.Random;
+
+/**
+ * @author qx
+ * @data 2025/8/25
+ * @description: 盲盒抽奖展示的视图
+ */
+// GiftLotteryAdapter.java
+public class GiftLotteryAdapter extends BaseQuickAdapter {
+
+
+ public GiftLotteryAdapter() {
+ super(R.layout.item_gift_lottery);
+ }
+
+ @Override
+ protected void convert(BaseViewHolder helper, GiftBean item) {
+ helper.setText(R.id.tv_gift_time, item.getCreatetime());
+ ImageUtils.loadHeadCC(item.getBase_image(),helper.getView(R.id.iv_gift_image));
+ // 使用 SpannableString 给 "x4" 设置不同颜色
+ TextView giftNameTextView = helper.getView(R.id.gift_name);
+ TextView nickNameTextView = helper.getView(R.id.tv_user_name);
+ if (giftNameTextView != null) {
+ String baseName = item.getGift_name();
+ String countText = "x"+item.getCount();
+ String fullText = baseName + countText;
+
+ SpannableStringBuilder spannable = new SpannableStringBuilder(fullText);
+
+ // 给 "x4" 部分设置颜色
+ spannable.setSpan(
+ new ForegroundColorSpan(mContext.getResources().getColor(R.color.color_C7BF62)), // 替换为实际颜色
+ baseName.length(),
+ fullText.length(),
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
+ );
+
+ giftNameTextView.setText(spannable);
+ }
+
+ if (nickNameTextView!=null){
+ nickNameTextView.setText(item.getNickname());
+ String nickName = "赠予";
+ String fullText = nickName + " " + item.getNickname();
+ SpannableStringBuilder spannable = new SpannableStringBuilder(fullText);
+
+ // 给 "x4" 部分设置颜色
+ spannable.setSpan(
+ new ForegroundColorSpan(mContext.getResources().getColor(R.color.color_C7BF62)), // 替换为实际颜色
+ 0,
+ nickName.length(),
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
+ );
+
+ nickNameTextView.setText(spannable);
+ }
+
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/GiftLotteryDialogFragment.java b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/GiftLotteryDialogFragment.java
new file mode 100644
index 00000000..20ea95dd
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/GiftLotteryDialogFragment.java
@@ -0,0 +1,253 @@
+package com.xscm.moduleutil.dialog.giftLottery;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.LinearLayoutManager;
+
+import com.scwang.smartrefresh.layout.api.RefreshLayout;
+import com.scwang.smartrefresh.layout.listener.OnRefreshLoadMoreListener;
+import com.xscm.moduleutil.R;
+import com.xscm.moduleutil.base.BaseMvpDialogFragment;
+import com.xscm.moduleutil.bean.GiftBean;
+import com.xscm.moduleutil.bean.WalletBean;
+import com.xscm.moduleutil.bean.blindboxwheel.BlindBoxBean;
+import com.xscm.moduleutil.bean.blindboxwheel.BlindReslutBean;
+import com.xscm.moduleutil.bean.blindboxwheel.XlhDrawBean;
+import com.xscm.moduleutil.databinding.DialogGiftLotteryFragmentBinding;
+import com.xscm.moduleutil.widget.pagerecyclerview.PagerGridSnapHelper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *@author qx
+ *@data 2025/8/28
+ *@description: 盲盒转盘中奖记录
+ */
+public class GiftLotteryDialogFragment extends BaseMvpDialogFragment implements GiftLotteryContacts.View{
+
+ private int page=1;
+ private String giftBagId;
+ private int type=1;
+ private GiftLotteryAdapter adapter;
+ private GiftRecordAdapte giftRecordAdapte;
+ private List data=new ArrayList<>();
+
+ @Override
+ protected GiftLotteryPresenter bindPresenter() {
+ return new GiftLotteryPresenter(this,getSelfActivity());
+ }
+
+ public static GiftLotteryDialogFragment newInstance(String giftBagId) {
+ Bundle args = new Bundle();
+ GiftLotteryDialogFragment fragment = new GiftLotteryDialogFragment();
+ args.putString("giftBagId", giftBagId);
+ fragment.setArguments(args);
+ return fragment;
+ }
+ @Nullable
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ Dialog dialog = super.onCreateDialog(savedInstanceState);
+ dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ dialog.setCancelable(true);
+ return dialog;
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ Window window = getDialog().getWindow();
+ if (window != null) {
+ // 获取屏幕高度
+ android.util.DisplayMetrics displayMetrics = new android.util.DisplayMetrics();
+ requireActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
+ int screenHeight = displayMetrics.heightPixels;
+ // 设置高度为屏幕高度的100%(全屏)
+ int heightInPx = (int) (screenHeight * 0.8);;
+ window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, heightInPx);
+ window.setBackgroundDrawableResource(android.R.color.transparent);
+
+ // 可选:设置动画样式(从底部弹出)
+ window.setWindowAnimations(R.style.CommonShowDialogBottom);
+ }
+ }
+
+ @Override
+ protected void initDialogStyle(Window window) {
+ super.initDialogStyle(window);
+ window.setGravity(Gravity.BOTTOM);
+ }
+ @Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+ giftBagId = getArguments().getString("giftBagId");
+
+ }
+
+ @Override
+ protected void initData() {
+ MvpPre.getMyRecord(giftBagId, "1", "20",type);
+
+ }
+
+
+ @Override
+ protected void initView() {
+
+ if (giftBagId.equals("10")){
+ mBinding.clRoot.setBackgroundResource(R.mipmap.tkzj);
+ mBinding.imJc.setImageResource(R.mipmap.jilu);
+ }else if (giftBagId.equals("11")){
+ mBinding.clRoot.setBackgroundResource(R.mipmap.syzc);
+ mBinding.imJc.setImageResource(R.mipmap.syzc_jl);
+ }else if (giftBagId.equals("12")){
+ mBinding.clRoot.setBackgroundResource(R.mipmap.skzj);
+ mBinding.imJc.setImageResource(R.mipmap.skzl_jl);
+ }
+
+ mBinding.smartRefreshLayout.setOnRefreshLoadMoreListener(new OnRefreshLoadMoreListener() {
+ @Override
+ public void onRefresh(@NonNull RefreshLayout refreshLayout) {
+ page = 1;
+ MvpPre.getMyRecord(giftBagId, page+"", "20",type);
+
+ }
+
+ @Override
+ public void onLoadMore(@NonNull RefreshLayout refreshLayout) {
+ page++;
+ MvpPre.getMyRecord(giftBagId, page+"", "20",type);
+ }
+ });
+
+ mBinding.textView1.setOnClickListener(this::onClick);
+ mBinding.textView2.setOnClickListener(this::onClick);
+
+ adapter=new GiftLotteryAdapter();
+ giftRecordAdapte=new GiftRecordAdapte();
+
+// PagerGridLayoutManager layoutManager = new PagerGridLayoutManager(rows, columns, PagerGridLayoutManager.VERTICAL);
+
+ dianj(1);
+ }
+
+ private void onClick(View view) {
+ int id = view.getId();
+ if (id==R.id.textView1){
+ dianj(1);
+ }else if (id==R.id.textView2){
+ dianj(2);
+ }
+ }
+ public void dianj(int type1){
+ if (type1==1) {
+ GridLayoutManager layoutManager = new GridLayoutManager(getActivity(), 3);
+
+ mBinding.recyclerView.setLayoutManager(layoutManager);
+ mBinding.recyclerView.setOnFlingListener(null);
+ // 设置滚动辅助工具
+ PagerGridSnapHelper pageSnapHelper = new PagerGridSnapHelper();
+ pageSnapHelper.attachToRecyclerView(mBinding.recyclerView);
+ mBinding.recyclerView.setAdapter(adapter);
+ type=1;
+ setTextViewStyle(mBinding.textView2, false);
+ setTextViewStyle(mBinding.textView1, true);
+ }else if (type1==2){
+ mBinding.recyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
+ mBinding.recyclerView.setAdapter(giftRecordAdapte);
+ type=2;
+ setTextViewStyle(mBinding.textView2, true);
+ setTextViewStyle(mBinding.textView1, false);
+ }
+ page=1;
+ data.clear();
+ MvpPre.getMyRecord(giftBagId, page+"", "20",type);
+ }
+
+ private void setTextViewStyle(TextView textView, boolean isSelected) {
+ if (isSelected) {
+ textView.setTextColor(getResources().getColor(R.color.white));
+ textView.setTextSize(16);
+ textView.setBackground(getResources().getDrawable(R.mipmap.tab_dy));
+ } else {
+ textView.setTextColor(getResources().getColor(R.color.color_5B5B5B));
+ textView.setTextSize(14);
+ textView.setBackgroundColor(getResources().getColor(R.color.transparent));
+ }
+ }
+
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.dialog_gift_lottery_fragment;
+ }
+
+ @Override
+ public void getGiftListSuccess(BlindBoxBean blindBoxBean) {
+
+ }
+
+ @Override
+ public void drawGiftListSuccess(BlindReslutBean blindReslutBean) {
+
+ }
+
+ @Override
+ public void getMyRecordSuccess(List data) {
+ if (data != null){
+ if (page==1){
+ adapter.setNewData(data);
+ }else {
+ adapter.addData(data);
+ }
+ }else {
+ if (page == 1) {
+ adapter.setNewData(null);
+ }
+ }
+ }
+
+ @Override
+ public void getAllRecordSuccess(List data) {
+
+ if (data != null){
+ if (page==1){
+ giftRecordAdapte.setNewData(data);
+ }else {
+ giftRecordAdapte.addData(data);
+ }
+ }else {
+ if (page == 1) {
+ giftRecordAdapte.setNewData(null);
+ }
+ }
+ }
+
+ @Override
+ public void finishRefreshLoadMore() {
+ mBinding.smartRefreshLayout.finishRefresh();
+ mBinding.smartRefreshLayout.finishLoadMore();
+ }
+
+ @Override
+ public void wallet(WalletBean walletBean) {
+
+ }
+
+ @Override
+ public void xlhChouSuccess(List data) {
+
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/GiftRecordAdapte.java b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/GiftRecordAdapte.java
new file mode 100644
index 00000000..e3a93675
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/GiftRecordAdapte.java
@@ -0,0 +1,23 @@
+package com.xscm.moduleutil.dialog.giftLottery;
+
+import com.chad.library.adapter.base.BaseQuickAdapter;
+import com.chad.library.adapter.base.BaseViewHolder;
+import com.xscm.moduleutil.R;
+import com.xscm.moduleutil.bean.GiftBean;
+import com.xscm.moduleutil.utils.ImageUtils;
+
+public class GiftRecordAdapte extends BaseQuickAdapter {
+ public GiftRecordAdapte() {
+ super(R.layout.item_gift_record);
+ }
+
+ @Override
+ protected void convert(BaseViewHolder helper, GiftBean item) {
+
+ helper.setText(R.id.tv_user_name, item.getNickname());
+ helper.setText(R.id.tv_gift_count_name,"x"+item.getCount()+" "+ item.getGift_name());
+ helper.setText(R.id.tv_time, item.getCreatetime());
+ ImageUtils.loadHeadCC(item.getBase_image(),helper.getView(R.id.iv_gift_icon));
+
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/GiftRecordAdapter.java b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/GiftRecordAdapter.java
new file mode 100644
index 00000000..4fdef4a5
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/GiftRecordAdapter.java
@@ -0,0 +1,46 @@
+package com.xscm.moduleutil.dialog.giftLottery;
+
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.style.ForegroundColorSpan;
+import android.widget.TextView;
+
+import com.chad.library.adapter.base.BaseQuickAdapter;
+import com.chad.library.adapter.base.BaseViewHolder;
+import com.xscm.moduleutil.R;
+import com.xscm.moduleutil.bean.GiftBean;
+import com.xscm.moduleutil.utils.ImageUtils;
+
+public class GiftRecordAdapter extends BaseQuickAdapter {
+
+
+ public GiftRecordAdapter() {
+ super(R.layout.item_my_record);
+ }
+
+ @Override
+ protected void convert(BaseViewHolder helper, GiftBean item) {
+ helper.setText(R.id.tv_gift_time, item.getCreatetime());
+ ImageUtils.loadHeadCC(item.getBase_image(),helper.getView(R.id.iv_gift_image));
+ // 使用 SpannableString 给 "x4" 设置不同颜色
+ TextView giftNameTextView = helper.getView(R.id.tv_gift_name);
+ if (giftNameTextView != null) {
+ String baseName = item.getGift_name();
+ String countText = "x"+item.getCount();
+ String fullText = baseName + countText;
+
+ SpannableStringBuilder spannable = new SpannableStringBuilder(fullText);
+
+ // 给 "x4" 部分设置颜色
+ spannable.setSpan(
+ new ForegroundColorSpan(mContext.getResources().getColor(R.color.color_C7BF62)), // 替换为实际颜色
+ baseName.length(),
+ fullText.length(),
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
+ );
+
+ giftNameTextView.setText(spannable);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/GiftXlhChouAdapter.java b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/GiftXlhChouAdapter.java
new file mode 100644
index 00000000..6dbaec3a
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/GiftXlhChouAdapter.java
@@ -0,0 +1,150 @@
+package com.xscm.moduleutil.dialog.giftLottery;
+
+import android.view.View;
+
+import com.chad.library.adapter.base.BaseQuickAdapter;
+import com.chad.library.adapter.base.BaseViewHolder;
+import com.xscm.moduleutil.R;
+import com.xscm.moduleutil.bean.GiftBean;
+import com.xscm.moduleutil.utils.ImageUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import lombok.Getter;
+
+@Getter
+public class GiftXlhChouAdapter extends BaseQuickAdapter {
+ private List giftLists = new ArrayList<>();
+ private int selectedPosition = -1;
+ private static final int LOOP_COUNT = 8; // 循环倍数
+
+
+ public GiftXlhChouAdapter() {
+ super(R.layout.item_xlh);
+ }
+ // 设置数据时创建循环数据
+ @Override
+ public void setNewData(List data) {
+ this.giftLists = data != null ? data : new ArrayList<>();
+ super.setNewData(createLoopData());
+ }
+
+ /**
+ * 创建循环数据
+ * @return 循环数据列表
+ */
+ private List createLoopData() {
+ List loopData = new ArrayList<>();
+ if (giftLists.isEmpty()) {
+ return loopData;
+ }
+
+ // 创建足够多的循环数据以实现循环效果
+ for (int i = 0; i < LOOP_COUNT; i++) {
+ loopData.addAll(giftLists);
+ }
+ return loopData;
+ }
+
+ @Override
+ public int getItemCount() {
+ // 如果原始数据为空,返回0
+ if (giftLists.isEmpty()) {
+ return 0;
+ }
+ // 返回循环数据的数量
+ return super.getItemCount();
+ }
+ /**
+ * 获取实际位置(将循环位置映射到原始数据位置)
+ * @param position 循环列表中的位置
+ * @return 原始数据中的实际位置
+ */
+ private int getActualPosition(int position) {
+ if (giftLists.isEmpty()) {
+ return 0;
+ }
+ return position % giftLists.size();
+ }
+ @Override
+ protected void convert(BaseViewHolder helper, GiftBean item) {
+ // 获取实际位置
+ int actualPosition = getActualPosition(helper.getAdapterPosition());
+ GiftBean actualItem = giftLists.get(actualPosition);
+
+ helper.setText(R.id.tv_gift_name, actualItem.getGift_name());
+ helper.setText(R.id.tv_gift_pic, actualItem.getGift_price());
+ ImageUtils.loadHeadCC(actualItem.getBase_image(), helper.getView(R.id.iv_gift_image));
+ // 处理选中状态
+ View selectedIcon = helper.getView(R.id.selected_icon);
+ // 处理选中状态
+ if (selectedIcon != null) {
+ // 检查当前item是否为选中位置
+ if (actualPosition == selectedPosition) {
+ selectedIcon.setVisibility(View.GONE);
+ helper.setBackgroundRes(R.id.ll_bg,R.mipmap.ke_bg);
+
+ } else {
+ helper.setBackgroundRes(R.id.ll_bg,R.mipmap.xlh_cj_item);
+ selectedIcon.setVisibility(View.GONE);
+ }
+ }
+ }
+ /**
+ * 设置选中位置并更新UI
+ * @param position 选中的位置
+ */
+ public void setSelectedPosition(int position) {
+ int previousPosition = selectedPosition;
+ selectedPosition = position;
+
+ if (previousPosition >= 0) {
+ notifyItemsByActualPosition(previousPosition, false);
+ }
+
+ if (selectedPosition >= 0) {
+ notifyItemsByActualPosition(selectedPosition, true);
+ }
+ }
+ /**
+ * 根据实际位置通知所有匹配的item更新
+ * @param actualPosition 原始数据中的位置
+ * @param isSelected 是否选中
+ */
+ private void notifyItemsByActualPosition(int actualPosition, boolean isSelected) {
+ if (giftLists.isEmpty() || actualPosition >= giftLists.size()) {
+ return;
+ }
+
+ // 通知所有匹配该实际位置的item更新
+ for (int i = 0; i < getItemCount(); i++) {
+ if (getActualPosition(i) == actualPosition) {
+ notifyItemChanged(i);
+ }
+ }
+ }
+ /**
+ * 清除选中状态
+ */
+ public void clearSelection() {
+ int previousPosition = selectedPosition;
+ selectedPosition = -1;
+
+ if (previousPosition >= 0) {
+ notifyItemsByActualPosition(previousPosition, false);
+ }
+ }
+
+ /**
+ * 获取原始数据大小
+ * @return 原始数据大小
+ */
+ public int getOriginalDataSize() {
+ List loopData = new ArrayList<>();
+ for (int i = 0; i < LOOP_COUNT; i++) {
+ loopData.addAll(giftLists);
+ }
+ return loopData.size();
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/LuckyFragment.java b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/LuckyFragment.java
new file mode 100644
index 00000000..cc40a300
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/LuckyFragment.java
@@ -0,0 +1,124 @@
+package com.xscm.moduleutil.dialog.giftLottery;
+
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.LinearLayoutManager;
+
+import com.scwang.smartrefresh.layout.api.RefreshLayout;
+import com.scwang.smartrefresh.layout.listener.OnRefreshLoadMoreListener;
+import com.xscm.moduleutil.R;
+import com.xscm.moduleutil.base.BaseMvpDialogFragment;
+import com.xscm.moduleutil.bean.GiftBean;
+import com.xscm.moduleutil.bean.WalletBean;
+import com.xscm.moduleutil.bean.blindboxwheel.BlindBoxBean;
+import com.xscm.moduleutil.bean.blindboxwheel.BlindReslutBean;
+import com.xscm.moduleutil.bean.blindboxwheel.XlhDrawBean;
+import com.xscm.moduleutil.databinding.FframentDataBinding;
+
+import java.util.List;
+
+public class LuckyFragment extends BaseMvpDialogFragment implements GiftLotteryContacts.View {
+ private int page=1;
+ private String roomId;
+ private int type=-1;
+ private NewGiftRecordAdapte giftRecordAdapte;
+
+ @Override
+ protected GiftLotteryPresenter bindPresenter() {
+ return new GiftLotteryPresenter(this,getSelfActivity());
+ }
+ public static LuckyFragment newInstance(String giftBagId, int type) {
+ Bundle args = new Bundle();
+ LuckyFragment fragment = new LuckyFragment();
+ args.putString("roomId", giftBagId);
+ args.putInt("type",type);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ protected void initData() {
+ roomId = getArguments().getString("roomId");
+ type = getArguments().getInt("type");
+ MvpPre.xlhAllRecord(roomId, "1", "20",type);
+ }
+
+ @Override
+ protected void initView() {
+
+ mBinding.smartRefreshLayout.setOnRefreshLoadMoreListener(new OnRefreshLoadMoreListener() {
+ @Override
+ public void onRefresh(@NonNull RefreshLayout refreshLayout) {
+ page = 1;
+ MvpPre.xlhAllRecord(roomId, page+"", "20",type);
+
+ }
+
+ @Override
+ public void onLoadMore(@NonNull RefreshLayout refreshLayout) {
+ page++;
+ MvpPre.xlhAllRecord(roomId, page+"", "20",type);
+ }
+ });
+
+
+ giftRecordAdapte=new NewGiftRecordAdapte();
+ mBinding.recyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
+ mBinding.recyclerView.setAdapter(giftRecordAdapte);
+ }
+
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.fframent_data;
+ }
+
+ @Override
+ public void getGiftListSuccess(BlindBoxBean blindBoxBean) {
+
+ }
+
+ @Override
+ public void drawGiftListSuccess(BlindReslutBean blindReslutBean) {
+
+ }
+
+ @Override
+ public void getMyRecordSuccess(List data) {
+
+ }
+
+ @Override
+ public void getAllRecordSuccess(List data) {
+
+ if (data != null){
+ if (page==1){
+ giftRecordAdapte.setNewData(data);
+ }else {
+ giftRecordAdapte.addData(data);
+ }
+ }else {
+ if (page == 1) {
+ giftRecordAdapte.setNewData(null);
+ }
+ }
+ }
+
+
+ @Override
+ public void finishRefreshLoadMore() {
+ mBinding.smartRefreshLayout.finishRefresh();
+ mBinding.smartRefreshLayout.finishLoadMore();
+ }
+
+ @Override
+ public void wallet(WalletBean walletBean) {
+
+ }
+
+ @Override
+ public void xlhChouSuccess(List data) {
+
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/NewGiftRecordAdapte.java b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/NewGiftRecordAdapte.java
new file mode 100644
index 00000000..72138f93
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/NewGiftRecordAdapte.java
@@ -0,0 +1,23 @@
+package com.xscm.moduleutil.dialog.giftLottery;
+
+import com.chad.library.adapter.base.BaseQuickAdapter;
+import com.chad.library.adapter.base.BaseViewHolder;
+import com.xscm.moduleutil.R;
+import com.xscm.moduleutil.bean.GiftBean;
+import com.xscm.moduleutil.utils.ImageUtils;
+
+public class NewGiftRecordAdapte extends BaseQuickAdapter {
+ public NewGiftRecordAdapte() {
+ super(R.layout.item_gift_record_new);
+ }
+
+ @Override
+ protected void convert(BaseViewHolder helper, GiftBean item) {
+ helper.setText(R.id.tv_issue,item.getPeriods());
+ helper.setText(R.id.tv_user_name, item.getNickname());
+ helper.setText(R.id.tv_gift_count_name, item.getGift_name());
+ helper.setText(R.id.tv_time, item.getCreatetime());
+ ImageUtils.loadHeadCC(item.getBase_image(),helper.getView(R.id.iv_gift_icon));
+
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/NewXlhRankingDialog.java b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/NewXlhRankingDialog.java
new file mode 100644
index 00000000..c698d1bf
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/NewXlhRankingDialog.java
@@ -0,0 +1,174 @@
+package com.xscm.moduleutil.dialog.giftLottery;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.RadioGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+
+import com.xscm.moduleutil.R;
+import com.xscm.moduleutil.adapter.MyPagerAdapter;
+import com.xscm.moduleutil.base.BaseMvpDialogFragment;
+import com.xscm.moduleutil.bean.GiftBean;
+import com.xscm.moduleutil.bean.WalletBean;
+import com.xscm.moduleutil.bean.blindboxwheel.BlindBoxBean;
+import com.xscm.moduleutil.bean.blindboxwheel.BlindReslutBean;
+import com.xscm.moduleutil.bean.blindboxwheel.XlhDrawBean;
+import com.xscm.moduleutil.databinding.DialogNewRankingXlhFragmentBinding;
+import com.xscm.moduleutil.dialog.LotteryFragment;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *@author qx
+ *@data 2025/9/4
+ *@description:巡乐会榜单
+ */
+ public class NewXlhRankingDialog extends BaseMvpDialogFragment implements GiftLotteryContacts.View{
+ private String roomId;
+
+ private MyPagerAdapter pagerAdapter;
+ private List fragmentList;
+ private List titleList = new ArrayList();
+
+ @Override
+ protected GiftLotteryPresenter bindPresenter() {
+ return new GiftLotteryPresenter(this,getSelfActivity());
+ }
+
+ public static NewXlhRankingDialog newInstance(String giftBagId) {
+ Bundle args = new Bundle();
+ NewXlhRankingDialog fragment = new NewXlhRankingDialog();
+ args.putString("roomId", giftBagId);
+ fragment.setArguments(args);
+ return fragment;
+ }
+ @Nullable
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ Dialog dialog = super.onCreateDialog(savedInstanceState);
+ dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ dialog.setCancelable(true);
+ return dialog;
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ Window window = getDialog().getWindow();
+ if (window != null) {
+ // 获取屏幕高度
+ android.util.DisplayMetrics displayMetrics = new android.util.DisplayMetrics();
+ requireActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
+ int screenHeight = displayMetrics.heightPixels;
+ // 设置高度为屏幕高度的100%(全屏)
+ int heightInPx = (int) (screenHeight * 0.8);;
+ window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, heightInPx);
+ window.setBackgroundDrawableResource(android.R.color.transparent);
+
+ // 可选:设置动画样式(从底部弹出)
+ window.setWindowAnimations(R.style.CommonShowDialogBottom);
+ }
+ }
+
+ @Override
+ protected void initDialogStyle(Window window) {
+ super.initDialogStyle(window);
+ window.setGravity(Gravity.BOTTOM);
+ }
+ @Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+
+
+ }
+
+ @Override
+ protected void initData() {
+ roomId = getArguments().getString("roomId");
+ // MvpPre.xlhAllRecord(roomId, "1", "20");
+ // 初始化Fragment列表
+ initFragments();
+ initViewPager();
+ }
+
+ // 初始化Fragment列表
+ private void initFragments() {
+ fragmentList = new ArrayList<>();
+ fragmentList.add(new LotteryFragment().newInstance(roomId,1)); // 第1页:抽奖榜单
+ fragmentList.add(new LuckyFragment().newInstance(roomId,2)); // 第1页:抽奖榜单
+
+ }
+
+ // 初始化ViewPager
+ private void initViewPager() {
+ titleList.add("");
+ titleList.add("");
+ FragmentManager childFragmentManager = getChildFragmentManager();
+ pagerAdapter = new MyPagerAdapter(childFragmentManager, fragmentList,titleList );
+ mBinding.ivViewPager.setAdapter(pagerAdapter);
+ mBinding.ivViewPager.setCurrentItem(0); // 默认显示第1页
+ }
+ @Override
+ protected void initView() {
+ mBinding.rbBtn.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(RadioGroup group, int checkedId) {
+ if (checkedId==R.id.radio_all){
+ mBinding.ivViewPager.setCurrentItem(0);
+ }else {
+ mBinding.ivViewPager.setCurrentItem(1);
+ }
+ }
+ });
+ }
+
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.dialog_new_ranking_xlh_fragment;
+ }
+
+ @Override
+ public void getGiftListSuccess(BlindBoxBean blindBoxBean) {
+
+ }
+
+ @Override
+ public void drawGiftListSuccess(BlindReslutBean blindReslutBean) {
+
+ }
+
+ @Override
+ public void getMyRecordSuccess(List data) {
+ }
+
+ @Override
+ public void getAllRecordSuccess(List data) {
+
+
+ }
+
+ @Override
+ public void finishRefreshLoadMore() {
+
+ }
+
+ @Override
+ public void wallet(WalletBean walletBean) {
+
+ }
+
+ @Override
+ public void xlhChouSuccess(List data) {
+
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/PrizePoolAdapter.java b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/PrizePoolAdapter.java
new file mode 100644
index 00000000..c3333398
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/PrizePoolAdapter.java
@@ -0,0 +1,61 @@
+package com.xscm.moduleutil.dialog.giftLottery;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.chad.library.adapter.base.BaseQuickAdapter;
+import com.chad.library.adapter.base.BaseViewHolder;
+import com.xscm.moduleutil.R;
+import com.xscm.moduleutil.adapter.MyBaseAdapter;
+import com.xscm.moduleutil.bean.GiftBean;
+import com.xscm.moduleutil.utils.ImageUtils;
+/**
+ *@author qx
+ *@data 2025/8/28
+ *@description: 盲盒转盘的奖池适配器
+ */
+public class PrizePoolAdapter extends BaseQuickAdapter {
+ private Context context;
+ private int type;
+
+ public PrizePoolAdapter(int type) {
+ super(R.layout.item_prize_pool);
+ this.type = type;
+ }
+
+
+
+
+ @Override
+ protected void convert(BaseViewHolder helper, GiftBean item) {
+
+ if (type == 10 || type == 12){
+ helper.setImageResource(R.id.iv_prize_pool,R.mipmap.tkzj_z);
+ }else {
+ helper.setImageResource(R.id.iv_prize_pool,R.mipmap.xlh_hd);
+ }
+
+ helper.setText(R.id.tv_gift_name, item.getGift_name());
+ helper.setText(R.id.tv_gift_pic, item.getGift_price());
+ ImageUtils.loadHeadCC(item.getBase_image(),helper.getView(R.id.iv_gift_image));
+ }
+
+
+ public static class ViewHolder {
+ private ImageView imGiftImage;
+ private TextView tv_gift_name;
+ private TextView tv_gift_price;
+
+ public ViewHolder(View convertView) {
+ imGiftImage = convertView.findViewById(R.id.iv_gift_image);
+ tv_gift_name = convertView.findViewById(R.id.tv_gift_name);
+ tv_gift_price = convertView.findViewById(R.id.tv_gift_pic);
+
+ }
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/PrizePoolDialog.java b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/PrizePoolDialog.java
new file mode 100644
index 00000000..aff88a46
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/PrizePoolDialog.java
@@ -0,0 +1,117 @@
+package com.xscm.moduleutil.dialog.giftLottery;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.GridLayoutManager;
+
+import com.xscm.moduleutil.R;
+import com.xscm.moduleutil.bean.GiftBean;
+import com.xscm.moduleutil.databinding.DialogPrizePoolBinding;
+import com.xscm.moduleutil.widget.dialog.BaseDialog;
+
+import java.util.List;
+
+/**
+ * @author qx
+ * @data 2025/8/28
+ * @description: 盲盒转盘奖池弹窗
+ */
+public class PrizePoolDialog extends BaseDialog {
+ private Context mContext;
+ private List gift_list;
+ private int type;
+
+ public PrizePoolDialog(@NonNull Context context) {
+ super(context);
+ this.mContext = context;
+ // 设置对话框从底部弹出并紧贴底部
+ if (getWindow() != null) {
+ getWindow().setGravity(android.view.Gravity.BOTTOM);
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (getWindow() != null) {
+ // 获取屏幕尺寸
+ android.util.DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
+ // 设置高度为屏幕高度的80%
+ android.view.WindowManager.LayoutParams params = getWindow().getAttributes();
+ params.height = (int) (displayMetrics.heightPixels * 0.7);
+ params.width = android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+ getWindow().setAttributes(params);
+ }
+ }
+
+
+ @Override
+ public int getLayoutId() {
+ return R.layout.dialog_prize_pool;
+ }
+
+ @Override
+ public void initView() {
+ }
+
+
+ @Override
+ public void initData() {
+
+ }
+
+ private void showEmptyState() {
+ // 显示空状态或加载中提示
+ // mBinding.tvEmpty.setVisibility(View.VISIBLE);
+ // mBinding.tvEmpty.setText("暂无奖池数据");
+ }
+
+ // 提供更新数据的方法
+ public void updateData(List newData, int type) {
+ if (type == 10) {
+ mBinding.clPrize.setBackgroundResource(R.mipmap.tkzj);
+ mBinding.imJc.setImageResource(R.mipmap.jiangc);
+ } else if (type == 11) {
+ mBinding.clPrize.setBackgroundResource(R.mipmap.syzc);
+ mBinding.imJc.setImageResource(R.mipmap.syzc_jc);
+ } else if (type == 12) {
+ mBinding.clPrize.setBackgroundResource(R.mipmap.skzj);
+ mBinding.imJc.setImageResource(R.mipmap.skzl_jc);
+ }else if (type == 13){
+ mBinding.clPrize.setBackgroundResource(R.mipmap.xlh);
+ mBinding.imJc.setImageResource(R.mipmap.xlh_jc);
+ }
+
+ // 根据屏幕密度调整行数和列数
+ int rows, columns;
+ float density = mContext.getResources().getDisplayMetrics().density;
+
+ if (density <= 2.0) { // 低密度屏幕(如mdpi, hdpi)
+ rows = 4;
+ columns = 3;
+ } else if (density <= 3.0) { // 中密度屏幕(如xhdpi)
+ rows = 4;
+ columns = 3;
+ } else { // 高密度屏幕(如xxhdpi, xxxhdpi)
+ rows = 4;
+ columns = 3;
+ }
+
+ if (newData != null && !newData.isEmpty()) {
+ this.gift_list = newData;
+ if (mBinding != null && mContext != null) {
+ PrizePoolAdapter prizePoolAdapter = new PrizePoolAdapter(type);
+ GridLayoutManager layoutManager = new GridLayoutManager(getContext(), 3);
+// PagerGridLayoutManager layoutManager = new PagerGridLayoutManager(rows, columns, PagerGridLayoutManager.VERTICAL);
+ mBinding.gvGift.setLayoutManager(layoutManager);
+// mBinding.gvGift.setOnFlingListener(null);
+ // 设置滚动辅助工具
+// PagerGridSnapHelper pageSnapHelper = new PagerGridSnapHelper();
+// pageSnapHelper.attachToRecyclerView(mBinding.gvGift);
+ mBinding.gvGift.setAdapter(prizePoolAdapter);
+ prizePoolAdapter.setNewData(gift_list);
+ }
+ }
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/TourClubDialogFragment.java b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/TourClubDialogFragment.java
new file mode 100644
index 00000000..0872f8bf
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/TourClubDialogFragment.java
@@ -0,0 +1,1116 @@
+package com.xscm.moduleutil.dialog.giftLottery;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.graphics.Typeface;
+import android.os.Bundle;
+import android.os.CountDownTimer;
+import android.os.Handler;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.LinearSnapHelper;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.alibaba.android.arouter.launcher.ARouter;
+import com.blankj.utilcode.util.GsonUtils;
+import com.xscm.moduleutil.R;
+import com.xscm.moduleutil.base.BaseMvpDialogFragment;
+import com.xscm.moduleutil.bean.GiftBean;
+import com.xscm.moduleutil.bean.MqttXlhEnd;
+import com.xscm.moduleutil.bean.RoomMessageEvent;
+import com.xscm.moduleutil.bean.WalletBean;
+import com.xscm.moduleutil.bean.XLHBean;
+import com.xscm.moduleutil.bean.blindboxwheel.BlindBoxBean;
+import com.xscm.moduleutil.bean.blindboxwheel.BlindReslutBean;
+import com.xscm.moduleutil.bean.blindboxwheel.XlhDrawBean;
+import com.xscm.moduleutil.databinding.FragmentTourClubDialogBinding;
+import com.xscm.moduleutil.dialog.RechargeDialogFragment;
+import com.xscm.moduleutil.dialog.WebViewDialog;
+import com.xscm.moduleutil.event.MqttBean;
+import com.xscm.moduleutil.utils.ARouteConstants;
+import com.xscm.moduleutil.utils.ImageUtils;
+import com.xscm.moduleutil.widget.CenterScrollHelper;
+import com.xscm.moduleutil.widget.EqualSpaceItemDecoration;
+
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author qx
+ * @data 2025/9/15
+ * @description: 玄镜之旅
+ */
+public class TourClubDialogFragment extends BaseMvpDialogFragment implements GiftLotteryContacts.View {
+ private String roomId;
+ private List giftLists = new ArrayList<>();
+ private String getRule_url;
+ private CountDownTimer mCountDownTimer;
+ private long endTime; // 服务器返回的结束时间戳
+ private String num;
+ private NewXlhRankingDialog newXlhRankingDialog;
+ private XlhRecordDialog xlhRecordDialog;
+ private XlhObtainDialog xlhObtainDialog;
+
+ private GiftXlhChouAdapter giftXlhChouAdapter;
+ private CenterScrollHelper scrollHelper;
+
+ private static final int ITEM_COUNT = 24;
+ private static final int ROTATION_COUNT = 3;
+
+
+ private Handler handler = new Handler();
+ private boolean isLotteryRunning = false;
+ private int scrollSpeed = 10; // 滚动速度
+ private int totalScrollTime = 5000; // 总滚动时间,单位:毫秒
+ private int targetPosition;
+
+ private boolean isDrawing;//是否正在抽奖
+
+
+ // 添加自动滚动相关的成员变量
+ private Handler autoScrollHandler = new Handler();
+ private Runnable autoScrollRunnable;
+ private boolean isAutoScrolling = false;
+ private static final int AUTO_SCROLL_DELAY = 1000; // 1秒滚动一个item
+ private boolean vCheckbox=false;
+
+ @Override
+ protected GiftLotteryPresenter bindPresenter() {
+ return new GiftLotteryPresenter(this, getActivity());
+ }
+
+ public static TourClubDialogFragment newInstance(String roomId) {
+ TourClubDialogFragment dialog = new TourClubDialogFragment();
+ Bundle args = new Bundle();
+ args.putString("roomId", roomId);
+ dialog.setArguments(args);
+ return dialog;
+ }
+
+ @Nullable
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ Dialog dialog = super.onCreateDialog(savedInstanceState);
+ dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ dialog.setCancelable(true);
+ if (!EventBus.getDefault().isRegistered(this))
+ EventBus.getDefault().register(this);
+ return dialog;
+ }
+
+ @Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+ roomId = getArguments().getString("roomId");
+ }
+
+ @Override
+ protected void initData() {
+ MvpPre.xlh(roomId);
+ MvpPre.wallet();
+ }
+
+ @Override
+ protected void initDialogStyle(Window window) {
+ super.initDialogStyle(window);
+ window.setGravity(Gravity.BOTTOM);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ Window window = getDialog().getWindow();
+ if (window != null) {
+ // 获取屏幕高度
+ android.util.DisplayMetrics displayMetrics = new android.util.DisplayMetrics();
+ requireActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
+ int screenHeight = displayMetrics.heightPixels;
+ // 设置高度为屏幕高度的100%(全屏)
+ int heightInPx = (int) (screenHeight * 0.85);
+ window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, heightInPx);
+ window.setBackgroundDrawableResource(android.R.color.transparent);
+
+ // 可选:设置动画样式(从底部弹出)
+ window.setWindowAnimations(R.style.CommonShowDialogBottom);
+ }
+ }
+
+ @Override
+ protected void initView() {
+ mBinding.tvJc.setOnClickListener(this::onClick);
+ mBinding.llOne.setOnClickListener(this::onClick);
+ mBinding.llTen.setOnClickListener(this::onClick);
+ mBinding.llHundred.setOnClickListener(this::onClick);
+ mBinding.tvGz.setOnClickListener(this::onClick);
+ mBinding.tvBd.setOnClickListener(this::onClick);
+ mBinding.tvJl.setOnClickListener(this::onClick);
+ mBinding.exchangeLayout.setOnClickListener(this::onClick);
+ mBinding.vCheckbox.setOnClickListener(this::onClick);
+ giftXlhChouAdapter = new GiftXlhChouAdapter();
+ // 使用 LinearLayoutManager 横向滚动
+ LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false);
+ mBinding.recycleView.setLayoutManager(layoutManager);
+ mBinding.recycleView.setOnFlingListener(null);
+
+ // 设置滚动辅助工具
+ LinearSnapHelper snapHelper = new LinearSnapHelper();
+ snapHelper.attachToRecyclerView(mBinding.recycleView);
+
+ // 添加 ItemDecoration 实现等宽分布
+ int spacing = getResources().getDimensionPixelSize(R.dimen.dp_15); // 根据需要调整间距
+ mBinding.recycleView.addItemDecoration(new EqualSpaceItemDecoration(3, spacing));
+
+ mBinding.recycleView.setAdapter(giftXlhChouAdapter);
+ // 初始化滚动帮助类
+ scrollHelper = new CenterScrollHelper(mBinding.recycleView);
+
+ // 初始化时滚动到中间位置,确保有足够的前后数据
+ mBinding.recycleView.post(() -> {
+ if (giftXlhChouAdapter.getOriginalDataSize() > 0) {
+ int middlePosition = giftXlhChouAdapter.getItemCount() / 2;
+ mBinding.recycleView.scrollToPosition(middlePosition);
+ }
+ });
+ mBinding.tvNumber.setText("x0");
+ mBinding.tvNumber.setTypeface(android.graphics.Typeface.create("semibold", Typeface.NORMAL));
+ }
+
+ // 添加自动滚动相关的方法
+ private void startAutoScroll() {
+ // 如果已经在自动滚动或数据为空,则不启动
+ if (isAutoScrolling || giftLists == null || giftLists.isEmpty()) {
+ return;
+ }
+
+ isAutoScrolling = true;
+ autoScrollRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (isAutoScrolling && mBinding != null && mBinding.recycleView != null) {
+ // 获取当前显示的第一个可见item位置
+ LinearLayoutManager layoutManager = (LinearLayoutManager) mBinding.recycleView.getLayoutManager();
+ if (layoutManager != null) {
+ int currentPosition = layoutManager.findFirstVisibleItemPosition();
+
+ // 如果没有完全可见的item,使用第一个可见item
+ if (currentPosition == RecyclerView.NO_POSITION) {
+ currentPosition = layoutManager.findFirstVisibleItemPosition();
+ }
+
+ // 如果还是没有有效的position,从0开始
+ if (currentPosition == RecyclerView.NO_POSITION) {
+ currentPosition = 0;
+ }
+ // 计算下一个位置
+ int nextPosition = currentPosition + 1;
+
+ // 如果到达末尾,回到开始位置
+ if (nextPosition >= giftXlhChouAdapter.getItemCount()) {
+ nextPosition = 0;
+ }
+
+ // 平滑滚动到下一个位置
+ mBinding.recycleView.smoothScrollToPosition(nextPosition);
+ }
+
+ // 1秒后继续滚动
+ autoScrollHandler.postDelayed(this, AUTO_SCROLL_DELAY);
+ }
+ }
+ };
+
+ // 开始自动滚动
+ autoScrollHandler.postDelayed(autoScrollRunnable, AUTO_SCROLL_DELAY);
+ }
+
+ private void stopAutoScroll() {
+ isAutoScrolling = false;
+ if (autoScrollRunnable != null) {
+ autoScrollHandler.removeCallbacks(autoScrollRunnable);
+ autoScrollRunnable = null;
+ }
+ }
+
+ // 在抽奖结束后重新启动自动滚动
+ private void resumeAutoScrollAfterLottery() {
+ // 延迟一段时间后再启动自动滚动,让用户看到抽奖结果
+ mBinding.recycleView.postDelayed(() -> {
+ if (isVisible() && !isDrawing) {
+ startAutoScroll();
+ }
+ }, 3000); // 3秒后重新开始自动滚动
+ }
+
+
+ private void onClick(View view) {
+ int id = view.getId();
+ if (id == R.id.tv_jc) {
+ if (giftLists != null && !giftLists.isEmpty()) {
+ PrizePoolDialog prizePoolDialog = new PrizePoolDialog(getActivity());
+ prizePoolDialog.updateData(giftLists, 13);
+ prizePoolDialog.show();
+ } else {
+ com.hjq.toast.ToastUtils.show("奖池数据加载中,请稍后再试");
+ }
+ } else if (id == R.id.ll_one) {
+ if (!isDrawing) {
+ isDrawing = true;
+ prepareForNewLottery();
+ num = "1";
+ MvpPre.xlhChou(roomId, num);
+ } else {
+// com.hjq.toast.ToastUtils.show("正在抽奖中...");
+ }
+
+ } else if (id == R.id.ll_ten) {
+ if (!isDrawing) {
+ isDrawing = true;
+ prepareForNewLottery();
+ num = "10";
+ MvpPre.xlhChou(roomId, num);
+ } else {
+// com.hjq.toast.ToastUtils.show("正在抽奖中...");
+ }
+
+ } else if (id == R.id.ll_hundred) {
+ if (!isDrawing) {
+ isDrawing = true;
+ prepareForNewLottery();
+ num = "100";
+ MvpPre.xlhChou(roomId, num);
+ } else {
+// com.hjq.toast.ToastUtils.show("正在抽奖中...");
+ }
+
+ } else if (id == R.id.tv_gz) {
+ Bundle bundle = new Bundle();
+ bundle.putString("url", getRule_url);
+ bundle.putInt("type", 13);
+ WebViewDialog dialog = new WebViewDialog(getActivity(), bundle);
+ dialog.show();
+ } else if (id == R.id.tv_bd) {
+ // 如果当前dialog存在且正在显示,先关闭
+ if (newXlhRankingDialog != null && newXlhRankingDialog.isVisible()) {
+ newXlhRankingDialog.dismiss();
+ }
+
+ newXlhRankingDialog = NewXlhRankingDialog.newInstance(roomId);
+ newXlhRankingDialog.show(getChildFragmentManager(), "newXlhRankingDialog");
+ } else if (id == R.id.tv_jl) {
+ // 如果当前dialog存在且正在显示,先关闭
+ if (xlhRecordDialog != null && xlhRecordDialog.isVisible()) {
+ xlhRecordDialog.dismiss();
+ }
+
+ xlhRecordDialog = XlhRecordDialog.newInstance(roomId);
+ xlhRecordDialog.show(getChildFragmentManager(), "XlhRecordDialog");
+ } else if (id == R.id.exchange_layout) {//钻石兑换
+// ARouter.getInstance().build(ARouteConstants.CURRENCY).navigation();
+ RechargeDialogFragment.show(roomId, null, getActivity().getSupportFragmentManager(),"0","0");
+ }else if (id==R.id.v_checkbox){
+ mBinding.vCheckbox.setSelected(!mBinding.vCheckbox.isSelected());
+ vCheckbox=mBinding.vCheckbox.isSelected();
+ }
+ }
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.fragment_tour_club_dialog;
+ }
+
+ @Override
+ public void getGiftListSuccess(BlindBoxBean blindBoxBean) {
+ if (blindBoxBean != null) {
+
+ getRule_url = blindBoxBean.getRule_url();
+ upTitle(blindBoxBean.getBox_price());
+ giftLists = blindBoxBean.getGift_list();
+// 初始滚动到中间位置,避免边界问题
+// mBinding.recycleView.scrollToPosition(Integer.MAX_VALUE / 2 - (Integer.MAX_VALUE / 2) % giftLists.size());
+
+ // 获取结束时间并启动倒计时
+ if (blindBoxBean.getXlh_end_time() != null && !blindBoxBean.getXlh_end_time().isEmpty()) {
+ try {
+ endTime = Long.parseLong(blindBoxBean.getXlh_end_time());
+ mBinding.qxDjs.setEndTime(endTime);
+ // 假设 end_time 是时间戳字符串
+// startCountdown();
+ } catch (NumberFormatException e) {
+ // 如果不是时间戳,可能是日期字符串,需要相应解析
+ // 例如:2025-08-26 19:10:47
+ // 可以使用 SimpleDateFormat 解析
+ e.printStackTrace();
+ }
+ }
+
+ if (blindBoxBean.getGive_homeowner_gift() != null) {
+// ImageUtils.loadHeadCC(blindBoxBean.getGive_homeowner_gift().getBase_image(), mBinding.giveIm);
+ mBinding.gvFzlw.setModel(blindBoxBean.getGive_homeowner_gift());
+ }
+ if (blindBoxBean.getLocking_gift() != null) {
+// ImageUtils.loadHeadCC(blindBoxBean.getLocking_gift().getBase_image(), mBinding.giftImg);
+// mBinding.tvGiftName.setText(blindBoxBean.getLocking_gift().getGift_name());
+// mBinding.tvGiftPrice.setText(blindBoxBean.getLocking_gift().getGift_price());
+// mBinding.tvGiftCount.setText(blindBoxBean.getLocking_gift().getGift_num());
+ mBinding.tvNumber.setText("x"+(blindBoxBean.getLocking_gift().getGift_num()!=null ?blindBoxBean.getLocking_gift().getGift_num():"0"));
+ mBinding.gvSdlw.setModel(blindBoxBean.getLocking_gift());
+ mBinding.gvSdlw.setIsLockGift(true);
+ mBinding.gvSdlw.stopAnimation();
+ mBinding.gvSdlw.startAnimation();
+ }
+
+ if (blindBoxBean.getXlh_user() != null) {
+// ImageUtils.loadHeadCC(blindBoxBean.getXlh_user().getAvatar(), mBinding.userPic);
+// mBinding.userName.setText(blindBoxBean.getXlh_user().getNickname());
+
+ mBinding.gvXyz.setModel(blindBoxBean.getXlh_user());
+ }
+ mBinding.gvXyz.setIsLuckUser(true);
+ if (blindBoxBean.getHomeowner_user()!=null){
+ mBinding.gvFz.setModel(blindBoxBean.getHomeowner_user());
+
+ }
+ mBinding.gvFz.setIsLuckUser(false);
+ giftXlhChouAdapter.setNewData(giftLists);
+// // 数据加载完成后启动自动滚动
+// if (giftLists != null && !giftLists.isEmpty()) {
+// mBinding.recycleView.post(() -> {
+// // 确保在UI线程中启动自动滚动
+// startAutoScroll();
+// });
+// }
+
+ }else {
+ isDrawing = false;
+ }
+ }
+
+ int mboxPrice;
+
+ // TODO: 2025/9/4 设置底部按钮文字
+ private void upTitle(int boxPrice) {
+ this.mboxPrice = boxPrice;
+ mBinding.tvOne.setText(boxPrice + "币一次");
+ mBinding.tvTen.setText((boxPrice * 10) + "币十次");
+ mBinding.tvHundred.setText((boxPrice * 100) + "币百次");
+ }
+
+ // TODO: 2025/8/29 接收im推送过来的消息
+// @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onMusicPlay(RoomMessageEvent message) {
+ if (message.getMsgType() == 1057) {
+// UpView(message.getText().getXlh_data());
+ endTime = Long.parseLong(message.getText().getEnd_time() != null ? message.getText().getEnd_time() : "0");
+// startCountdown();
+// mBinding.tvGiftCount.setText(message.getText().getGift_num() != null ? message.getText().getGift_num() : "0");
+// mBinding.userName.setText(message.getText().getFromUserInfo().getNickname() != null ? message.getText().getFromUserInfo().getNickname() : "");
+// ImageUtils.loadHeadCC(message.getText().getFromUserInfo().getAvatar(), mBinding.userPic);
+ BlindBoxBean.xlhUser xlhUser = new BlindBoxBean.xlhUser();
+ xlhUser.setAvatar(message.getText().getFromUserInfo().getAvatar());
+ xlhUser.setNickname(message.getText().getFromUserInfo().getNickname());
+ mBinding.gvXyz.setModel(xlhUser);
+ mBinding.qxDjs.setEndTime(endTime);
+ mBinding.tvNumber.setText("x"+(message.getText().getGift_num() != null ? message.getText().getGift_num() : "0"));
+ isDrawing=false;
+ }
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onMessageReceived(RoomMessageEvent event) {
+ onMusicPlay( event);
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onMessageReceived(MqttXlhEnd event) {
+ XLHBean xlhBean = GsonUtils.fromJson(event.getMessage(), XLHBean.class);
+ if (xlhBean.getFrom_type()==103){
+ endTime = Long.parseLong(xlhBean.getEnd_time() != null ? xlhBean.getEnd_time() : "0");
+ BlindBoxBean.xlhUser xlhUser = new BlindBoxBean.xlhUser();
+ xlhUser.setAvatar(xlhBean.getFromUserInfo().getAvatar());
+ xlhUser.setNickname(xlhBean.getFromUserInfo().getNickname());
+ mBinding.gvXyz.setModel(xlhUser);
+ mBinding.qxDjs.setEndTime(endTime);
+ mBinding.tvNumber.setText("x"+(xlhBean.getGift_num() != null ? xlhBean.getGift_num() : "0"));
+ isDrawing=false;
+ if (xlhBean.getRoom_user()!=null){
+ mBinding.gvFz.setModel(xlhBean.getRoom_user());
+ }
+ mBinding.gvFz.setIsLuckUser(false);
+ }else if (xlhBean.getFrom_type()==104){
+ dismiss();
+ }
+
+ }
+
+ /**
+ * 启动倒计时
+ */
+ private void startCountdown() {
+ // 先释放之前的倒计时器
+ releaseCountDownTimer();
+
+ // 获取当前时间
+ long currentTime = System.currentTimeMillis() / 1000; // 转换为秒
+ long countdownTime = endTime - currentTime; // 计算剩余时间(秒)
+
+ // 如果倒计时时间已经结束,显示00:00
+ if (countdownTime <= 0) {
+ updateCountdownDisplay(0, 0);
+ return;
+ }
+
+ // 确保不超过最大时间59:59 (3599秒)
+ countdownTime = Math.min(countdownTime, 3599);
+
+ mCountDownTimer = new CountDownTimer(countdownTime * 1000, 1000) {
+ @Override
+ public void onTick(long millisUntilFinished) {
+ long seconds = millisUntilFinished / 1000;
+ long minutes = seconds / 60;
+ long remainingSeconds = seconds % 60;
+ updateCountdownDisplay((int) minutes, (int) remainingSeconds);
+ }
+
+ @Override
+ public void onFinish() {
+ updateCountdownDisplay(0, 0);
+ }
+ };
+
+ mCountDownTimer.start();
+ }
+
+ /**
+ * 更新倒计时显示
+ *
+ * @param minutes 分钟数
+ * @param seconds 秒数
+ */
+ private void updateCountdownDisplay(int minutes, int seconds) {
+ // 假设布局中有以下TextView:
+ // mBinding.tvMinutes 十位分钟
+ // mBinding.tvMinutesUnit 个位分钟
+ // mBinding.tvSeconds 十位秒数
+ // mBinding.tvSecondsUnit 个位秒数
+
+ // 分解分钟数
+ int minutesTens = minutes / 10; // 十位分钟
+ int minutesUnits = minutes % 10; // 个位分钟
+
+ // 分解秒数
+ int secondsTens = seconds / 10; // 十位秒数
+ int secondsUnits = seconds % 10; // 个位秒数
+
+ // 更新UI显示(根据你的实际布局调整)
+ mBinding.tvTime1.setText(String.valueOf(minutesTens));
+ mBinding.tvTime2.setText(String.valueOf(minutesUnits));
+ mBinding.tvTime3.setText(String.valueOf(secondsTens));
+ mBinding.tvTime4.setText(String.valueOf(secondsUnits));
+
+ // 或者如果是一个整体的显示:
+ // mBinding.tvCountdown.setText(String.format("%02d:%02d", minutes, seconds));
+ }
+
+ /**
+ * 释放倒计时器资源
+ */
+ private void releaseCountDownTimer() {
+ if (mCountDownTimer != null) {
+ mCountDownTimer.cancel();
+ mCountDownTimer = null;
+ }
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ releaseCountDownTimer();
+ // 清理所有待执行的任务
+ clearPendingTasks();
+
+ // 清理滚动帮助类
+ if (scrollHelper != null) {
+ scrollHelper = null;
+ }
+
+ // 清理适配器引用
+ if (giftXlhChouAdapter != null) {
+ giftXlhChouAdapter = null;
+ }
+
+ if (EventBus.getDefault().isRegistered(this)) {
+ EventBus.getDefault().unregister(this);
+ }
+
+ if (xlhRecordDialog != null && xlhRecordDialog.isVisible()) {
+ xlhRecordDialog.dismiss();
+ }
+
+ // 如果当前dialog存在且正在显示,先关闭
+ if (newXlhRankingDialog != null && newXlhRankingDialog.isVisible()) {
+ newXlhRankingDialog.dismiss();
+ }
+
+ if (xlhObtainDialog != null && xlhObtainDialog.isShowing()) {
+ xlhObtainDialog.dismiss();
+ }
+
+ stopAutoScroll();
+ autoScrollHandler.removeCallbacksAndMessages(null);
+ }
+
+
+ @Override
+ public void drawGiftListSuccess(BlindReslutBean blindReslutBean) {
+
+ }
+
+ @Override
+ public void getMyRecordSuccess(List data) {
+
+ }
+
+ @Override
+ public void getAllRecordSuccess(List data) {
+
+ }
+
+ @Override
+ public void finishRefreshLoadMore() {
+
+ }
+
+ private int icon;
+
+ @Override
+ public void wallet(WalletBean walletBean) {
+ if (walletBean != null) {
+ icon = (int) Double.parseDouble(
+ (walletBean.getCoin() != null && !walletBean.getCoin().isEmpty())
+ ? walletBean.getCoin()
+ : "0"
+ );
+ mBinding.tvIcon.setText(walletBean.getCoin()!=null && !walletBean.getCoin().isEmpty() ?walletBean.getCoin():"0");
+ showView();
+ }
+ }
+
+ private void showView() {
+ if (icon>=mboxPrice){
+ mBinding.llOne.setClickable( true);
+ }else {
+ mBinding.llOne.setClickable( false);
+ }
+
+ if (icon>=mboxPrice*10){
+ mBinding.llTen.setClickable( true);
+ }else {
+ mBinding.llTen.setClickable( false);
+ }
+ if (icon>=mboxPrice*100){
+ mBinding.llHundred.setClickable( true);
+ }else {
+ mBinding.llHundred.setClickable( false);
+ }
+ }
+
+ private Runnable pendingLotteryRunnable;
+ private Runnable pendingCenteringRunnable;
+
+ @Override
+ public void xlhChouSuccess(List data) {
+ if (data != null) {
+ if (vCheckbox){
+ showResultDialog(data);
+ }else {
+// showResultDialog(data);
+
+// 取消之前可能存在的任务
+ clearPendingTasks();
+ // 抽奖完成后执行动画滚动
+ pendingLotteryRunnable = new Runnable() {
+ @Override
+ public void run() {
+ // 清理之前的状态
+ if (giftXlhChouAdapter != null) {
+ giftXlhChouAdapter.clearSelection();
+ }
+ int winningPosition = findHighestValueWinningPosition(data);//这是获取到的中奖位置下标
+ if (winningPosition != -1) {
+ if (scrollHelper == null) {
+ scrollHelper = new CenterScrollHelper(mBinding.recycleView);
+ }
+
+ // 计算在循环列表中的目标位置(滚动几圈后停在目标位置)
+ int loopCount = 4; // 滚动4圈
+ int originalSize = giftLists.size();///这是列表的总大小
+ // 计算目标在循环列表中的位置(确保在中间区域)
+ ///这是计算总圈数的大小
+ int middleBaseIndex = (loopCount * originalSize);
+ ///这里是展示在中奖的位置,加上总圈数的大小,
+ int targetLoopIndex = middleBaseIndex + (winningPosition % originalSize);
+
+ // 使用scrollWithCircles方法执行带动画的滚动(带完成回调)
+ scrollHelper.scrollWithCircles(
+ targetLoopIndex, // 在循环列表中的位置
+ loopCount, // 滚动圈数
+ 1000, // 每个item滚动时间200ms(控制速度)
+ originalSize, // 原始数据大小
+ () -> { // 滚动完成回调
+ // 滚动完成后更新选中状态(使用原始位置)
+ if (giftXlhChouAdapter != null) {
+ giftXlhChouAdapter.setSelectedPosition(winningPosition);
+ }
+ // 滚动完成后延迟一小段时间再居中,确保UI更新完成
+ pendingCenteringRunnable = new Runnable() {
+ @Override
+ public void run() {
+ // 手动将选中项居中
+ centerSelectedItem(winningPosition, originalSize);
+
+ // 显示结果对话框
+ getActivity().runOnUiThread(() -> {
+ scrollHelper = null;
+ showResultDialog(data);
+ // 抽奖结束后重新启动自动滚动
+// resumeAutoScrollAfterLottery();
+ });
+ }
+ };
+ mBinding.recycleView.postDelayed(pendingCenteringRunnable, 1000);
+ }
+ );
+
+ } else {
+ // 如果没有找到中奖位置,直接显示对话框
+ showResultDialog(data);
+ // 抽奖结束后重新启动自动滚动
+// resumeAutoScrollAfterLottery();
+ }
+ }
+ };
+ mBinding.recycleView.postDelayed(pendingLotteryRunnable, 1000);
+
+ }
+ }else {
+ isDrawing=false;
+ // 抽奖失败也重新启动自动滚动
+// resumeAutoScrollAfterLottery();
+ }
+ MvpPre.wallet();
+
+ }
+
+
+ /**
+ * 为新的抽奖做准备,清理之前的状态
+ */
+ private void prepareForNewLottery() {
+
+ // 停止自动滚动
+ stopAutoScroll();
+
+ // 取消之前可能存在的任务
+ clearPendingTasks();
+
+ // 清除之前的选择状态
+ if (giftXlhChouAdapter != null) {
+ giftXlhChouAdapter.clearSelection();
+ }
+
+ // 重置滚动帮助类
+ if (scrollHelper != null) {
+ scrollHelper.reset();
+ }
+
+ // 停止可能正在进行的滚动
+ mBinding.recycleView.stopScroll();
+
+ // 清理可能存在的回调
+ if (mBinding.recycleView.getHandler() != null) {
+ mBinding.recycleView.getHandler().removeCallbacksAndMessages(null);
+ }
+
+ // 重新初始化滚动帮助类
+ scrollHelper = new CenterScrollHelper(mBinding.recycleView);
+ }
+
+ /**
+ * 清除所有待执行的任务
+ */
+ private void clearPendingTasks() {
+ // 取消抽奖动画任务
+ if (pendingLotteryRunnable != null) {
+ mBinding.recycleView.removeCallbacks(pendingLotteryRunnable);
+ pendingLotteryRunnable = null;
+ }
+
+ // 取消居中任务
+ if (pendingCenteringRunnable != null) {
+ mBinding.recycleView.removeCallbacks(pendingCenteringRunnable);
+ pendingCenteringRunnable = null;
+ }
+
+ // 停止可能正在进行的滚动
+ mBinding.recycleView.stopScroll();
+
+ // 清理handler中的所有消息
+ if (mBinding.recycleView.getHandler() != null) {
+ mBinding.recycleView.getHandler().removeCallbacksAndMessages(null);
+ }
+ }
+
+ private void startLotteryAnimation(int targetIndex) {
+ // 确保适配器有数据
+ if (giftXlhChouAdapter == null || giftXlhChouAdapter.getOriginalDataSize() <= 0) {
+ showResultDialogWithoutAnimation(targetIndex);
+ return;
+ }
+
+ LinearLayoutManager layoutManager = (LinearLayoutManager) mBinding.recycleView.getLayoutManager();
+ if (layoutManager == null) {
+ showResultDialogWithoutAnimation(targetIndex);
+ return;
+ }
+
+ // 获取当前第一个可见的item位置
+ int firstVisiblePosition = layoutManager.findFirstVisibleItemPosition();
+ if (firstVisiblePosition == RecyclerView.NO_POSITION) {
+ firstVisiblePosition = 0;
+ }
+
+ // 获取当前在原始数据中的实际位置
+ int currentActualPosition = firstVisiblePosition % giftXlhChouAdapter.getOriginalDataSize();
+
+ // 计算需要滚动的总item数
+ // 滚动指定圈数 + 从当前位置到目标位置的距离
+ int itemsToScroll = ROTATION_COUNT * giftXlhChouAdapter.getOriginalDataSize()
+ + (targetIndex - currentActualPosition + giftXlhChouAdapter.getOriginalDataSize())
+ % giftXlhChouAdapter.getOriginalDataSize();
+
+ // 计算目标位置
+ int targetPosition = firstVisiblePosition + itemsToScroll;
+
+ // 确保目标位置在合理范围内
+ int maxPosition = giftXlhChouAdapter.getItemCount() - 1;
+ if (targetPosition > maxPosition) {
+ targetPosition = maxPosition;
+ }
+
+ // 确保目标位置有效
+ if (targetPosition >= 0) {
+ try {
+ SmoothScroller smoothScroller = new SmoothScroller(getActivity(), targetPosition);
+ layoutManager.startSmoothScroll(smoothScroller);
+ } catch (IllegalArgumentException e) {
+ // 如果仍然出现异常,使用备选方案
+ showResultDialogWithoutAnimation(targetIndex);
+ }
+ } else {
+ showResultDialogWithoutAnimation(targetIndex);
+ }
+ }
+
+
+ private void showResultDialogWithoutAnimation(int targetIndex) {
+ // 直接设置选中状态
+ if (giftXlhChouAdapter != null) {
+ giftXlhChouAdapter.setSelectedPosition(targetIndex);
+ }
+
+ // 延迟显示结果对话框
+ mBinding.recycleView.postDelayed(() -> {
+ // 创建假的XlhDrawBean数据用于显示结果
+ List fakeData = new ArrayList<>();
+ // 这里根据需要构造假数据
+ showResultDialog(fakeData);
+ }, 300);
+ }
+
+
+ private class SmoothScroller extends androidx.recyclerview.widget.LinearSmoothScroller {
+
+ private int targetPosition;
+
+ public SmoothScroller(android.content.Context context, int targetPosition) {
+ super(context);
+ this.targetPosition = targetPosition;
+ }
+
+ @Override
+ protected int calculateTimeForScrolling(int dx) {
+ // 自定义滚动时间,实现速度变化
+ float distance = Math.abs(dx);
+ float speed = 1f;
+ if (distance < 100) {
+ speed = 0.3f; // 慢
+ } else if (distance < 300) {
+ speed = 1f; // 快
+ } else {
+ speed = 0.3f; // 慢
+ }
+ return (int) (distance / speed);
+ }
+
+ @Override
+ public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int snapPreference) {
+ // 将目标项移动到屏幕中心
+ return (boxStart + (boxEnd - boxStart) / 2) - (viewStart + (viewEnd - viewStart) / 2);
+ }
+
+ @Override
+ public int getTargetPosition() {
+ // 添加更严格的边界检查
+ if (giftXlhChouAdapter != null && targetPosition >= 0 && targetPosition < giftXlhChouAdapter.getItemCount()) {
+ return targetPosition;
+ }
+ // 如果超出范围,返回一个安全的位置
+ if (giftXlhChouAdapter != null && giftXlhChouAdapter.getItemCount() > 0) {
+ return Math.max(0, Math.min(targetPosition, giftXlhChouAdapter.getItemCount() - 1));
+ }
+ return 0; // 返回默认位置
+ }
+ }
+
+ /**
+ * 将指定的原始位置item居中显示
+ *
+ * @param originalPosition 原始数据中的位置
+ * @param originalSize 原始数据大小
+ */
+ // 新增优化的居中方法
+ private void centerSelectedItem(int originalPosition, int originalSize) {
+ LinearLayoutManager layoutManager = (LinearLayoutManager) mBinding.recycleView.getLayoutManager();
+ if (layoutManager == null) return;
+
+ // 计算出需要滚动到的目标位置(确保在中间可见区域)
+ int targetPosition = originalPosition + 4 * originalSize;
+
+ // 先滚动到目标位置附近
+ mBinding.recycleView.scrollToPosition(targetPosition);
+
+ // 使用post确保布局完成后进行精确居中
+ mBinding.recycleView.post(() -> {
+ View targetView = layoutManager.findViewByPosition(targetPosition);
+ if (targetView == null) {
+ // 如果目标view不可见,使用smoothScrollToPosition
+ mBinding.recycleView.smoothScrollToPosition(targetPosition);
+ // 再次尝试居中
+ mBinding.recycleView.postDelayed(() -> {
+ View refreshedView = layoutManager.findViewByPosition(targetPosition);
+ if (refreshedView != null) {
+ performCentering(layoutManager, refreshedView);
+ }
+ }, 50);
+ return;
+ }
+
+ performCentering(layoutManager, targetView);
+ });
+ }
+
+ // 执行实际的居中操作
+ private void performCentering(LinearLayoutManager layoutManager, View targetView) {
+ // 计算RecyclerView的中心点
+ int recyclerViewWidth = mBinding.recycleView.getWidth();
+ int recyclerViewCenter = recyclerViewWidth / 2;
+
+ // 计算目标view的中心点
+ int targetViewLeft = targetView.getLeft();
+ int targetViewWidth = targetView.getWidth();
+ int targetViewCenter = targetViewLeft + targetViewWidth / 2;
+
+ // 计算需要滚动的偏移量
+ int offset = targetViewCenter - recyclerViewCenter;
+
+ // 精确滚动使目标view居中
+ mBinding.recycleView.scrollBy(offset, 0);
+ }
+
+ /**
+ * 直接滚动到指定位置并居中
+ *
+ * @param targetPosition 目标位置
+ */
+ private void directScrollToCenter(int targetPosition) {
+ if (mBinding.recycleView.getLayoutManager() == null) return;
+
+ mBinding.recycleView.post(() -> {
+ int screenWidth = mBinding.recycleView.getWidth();
+ if (screenWidth <= 0) return;
+
+ // 计算item宽度
+ int itemWidth = screenWidth / 3;
+
+ // 计算目标位置的左边缘
+ int targetLeft = targetPosition * itemWidth;
+
+ // 计算使item居中需要滚动到的位置
+ int targetScrollX = targetLeft - (screenWidth - itemWidth) / 2;
+
+ // 直接滚动到目标位置
+ mBinding.recycleView.scrollBy(
+ targetScrollX - mBinding.recycleView.computeHorizontalScrollOffset(),
+ 0
+ );
+ });
+ }
+
+
+ /**
+ * 滚动到指定位置并使该位置的item居中显示
+ *
+ * @param selectedPosition 选中的原始位置
+ */
+ private void scrollToCenterWithSelected(int selectedPosition) {
+ if (mBinding.recycleView.getLayoutManager() == null) return;
+
+ int screenWidth = mBinding.recycleView.getWidth();
+ if (screenWidth <= 0) return;
+
+ // 计算item宽度
+ int itemWidth = screenWidth / 3;
+
+ // 计算要滚动到的中心位置(使selectedPosition的item居中)
+ int middlePositionInLoop = (giftXlhChouAdapter.getItemCount() / 2 / giftXlhChouAdapter.getOriginalDataSize())
+ * giftXlhChouAdapter.getOriginalDataSize() + selectedPosition;
+
+ // 滚动到目标位置
+ mBinding.recycleView.smoothScrollToPosition(middlePositionInLoop);
+
+ // 额外确保居中
+ mBinding.recycleView.postDelayed(() -> {
+ if (scrollHelper != null) {
+ scrollHelper.scrollToCenter(middlePositionInLoop);
+ }
+ }, 300);
+ }
+
+ /**
+ * 显示抽奖结果对话框
+ *
+ * @param data 中奖数据
+ */
+ private void showResultDialog(List data) {
+ isDrawing = false;
+ // 创建并显示对话框
+ // 关闭之前可能存在的对话框
+// if (xlhObtainDialog != null && xlhObtainDialog.isShowing()) {
+// xlhObtainDialog.dismiss();
+// }
+ // 创建并显示对话框
+ xlhObtainDialog = new XlhObtainDialog(getActivity());
+ xlhObtainDialog.setOnGiftItemClickListener(new XlhObtainDialog.OnGiftItemClickListener() {
+
+ @Override
+ public void onPlayAgainClick() {
+ isDrawing = true;
+ // 处理再玩一次点击事件
+ MvpPre.xlhChou(roomId, num);
+ }
+
+ @Override
+ public void onCloseClick() {
+ // 处理关闭点击事件
+ if (xlhObtainDialog != null && xlhObtainDialog.isShowing()) {
+ isDrawing = false;
+ xlhObtainDialog.dismiss();
+ }
+ xlhObtainDialog = null;
+ }
+ });
+
+ xlhObtainDialog.show();
+ xlhObtainDialog.setGiftList(data);
+ }
+
+ /**
+ * 根据中奖结果找到最高价值奖品在giftLists中的位置
+ *
+ * @param data 中奖结果列表
+ * @return 最高价值奖品在giftLists中的下标,如果没有找到则返回-1
+ */
+ private int findHighestValueWinningPosition(List data) {
+ if (data == null || data.isEmpty() || giftLists == null || giftLists.isEmpty()) {
+ return -1;
+ }
+
+ int targetPosition = -1;
+ int highestPrice = -1;
+
+ // 遍历giftLists查找中奖项目
+ for (int i = 0; i < giftLists.size(); i++) {
+ GiftBean giftBean = giftLists.get(i);
+
+ // 在中奖数据中查找匹配的礼品
+ for (XlhDrawBean xlhDrawBean : data) {
+ // 比较gift_id是否匹配
+ if (giftBean.getGift_id().equals(String.valueOf(xlhDrawBean.getGift_id()))) {
+ // 解析价格,处理可能的NumberFormatException
+ try {
+ int currentPrice = Integer.parseInt(giftBean.getGift_price());
+ // 如果当前价格更高,则更新目标位置和最高价格
+ if (currentPrice > highestPrice) {
+ highestPrice = currentPrice;
+ targetPosition = i;
+ }
+ } catch (NumberFormatException e) {
+ // 如果价格解析失败,使用默认值0进行比较
+ if (highestPrice <= 0) {
+ highestPrice = 0;
+ targetPosition = i;
+ }
+ }
+ break; // 找到匹配项后跳出内层循环
+ }
+ }
+ }
+
+ return targetPosition;
+ }
+
+ /**
+ * 精确将指定位置的item滚动到中心
+ *
+ * @param position 目标位置
+ */
+ private void preciseScrollToCenter(int position) {
+ if (mBinding.recycleView.getLayoutManager() == null) return;
+
+ int screenWidth = mBinding.recycleView.getWidth();
+ if (screenWidth <= 0) return;
+
+ // 计算item宽度
+ int itemWidth = screenWidth / 3;
+
+ // 计算屏幕中心位置
+ int screenCenter = screenWidth / 2;
+
+ // 计算目标item的中心位置(在当前可见区域中)
+ int targetItemCenter = (mBinding.recycleView.computeHorizontalScrollOffset() / itemWidth) * itemWidth
+ + position * itemWidth + itemWidth / 2;
+
+ // 计算需要滚动的距离使目标item居中
+ int scrollDistance = targetItemCenter - screenCenter;
+
+ // 获取当前滚动位置
+ int currentScrollX = mBinding.recycleView.computeHorizontalScrollOffset();
+
+ // 执行滚动使选中项居中
+ mBinding.recycleView.smoothScrollBy(scrollDistance - currentScrollX, 0);
+
+ }
+
+
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/XlhObtainDialog.java b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/XlhObtainDialog.java
new file mode 100644
index 00000000..9c5d8f31
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/XlhObtainDialog.java
@@ -0,0 +1,109 @@
+package com.xscm.moduleutil.dialog.giftLottery;
+
+import android.content.Context;
+import android.view.Gravity;
+import android.view.Window;
+import android.view.WindowManager;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.GridLayoutManager;
+
+import com.blankj.utilcode.util.ScreenUtils;
+import com.xscm.moduleutil.R;
+import com.xscm.moduleutil.bean.blindboxwheel.XlhDrawBean;
+import com.xscm.moduleutil.databinding.DialogHeavenGiftBinding;
+import com.xscm.moduleutil.databinding.DialogXlhObtainBinding;
+import com.xscm.moduleutil.widget.dialog.BaseDialog;
+
+import java.util.List;
+
+/**
+ * @author qx
+ * @data 2025/9/2
+ * @description: 巡乐会恭喜或得礼弹窗
+ */
+public class XlhObtainDialog extends BaseDialog {
+
+ public interface OnGiftItemClickListener {
+
+ void onPlayAgainClick();
+
+ void onCloseClick();
+ }
+
+ private GiftItemAdapter mAdapter;
+ private OnGiftItemClickListener mListener;
+ private List mGiftList;
+
+ public XlhObtainDialog(@NonNull Context context) {
+ super(context, R.style.BaseDialogStyleH);
+ }
+
+ public XlhObtainDialog(@NonNull Context context, List giftList) {
+ super(context, R.style.BaseDialogStyleH);
+ this.mGiftList = giftList;
+ }
+
+ @Override
+ public int getLayoutId() {
+ return R.layout.dialog_xlh_obtain;
+ }
+
+ @Override
+ public void initView() {
+ setCancelable(false);
+ setCanceledOnTouchOutside(false);
+ Window window = getWindow();
+ // 设置对话框在屏幕中央显示
+ window.setGravity(Gravity.CENTER);
+ // 去掉背景阴影
+ window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+
+ // 设置窗口背景为透明
+ window.setBackgroundDrawableResource(android.R.color.transparent);
+ window.setLayout((int) (ScreenUtils.getScreenWidth() * 375.f / 375), WindowManager.LayoutParams.WRAP_CONTENT);
+ // 设置点击事件
+ mBinding.xlhClose.setOnClickListener(v -> {
+ if (mListener != null) {
+ mListener.onCloseClick();
+ }
+ dismiss();
+ });
+
+ mBinding.ivAgain.setOnClickListener(v -> {
+ if (mListener != null) {
+ mListener.onPlayAgainClick();
+ }
+ dismiss();
+ });
+ initRecyclerView();
+ }
+ private void initRecyclerView() {
+ mAdapter = new GiftItemAdapter();
+ mBinding.rvHead.setLayoutManager(new GridLayoutManager(getContext(), 3));
+ mBinding.rvHead.setAdapter(mAdapter);
+
+ }
+
+ /**
+ * 设置礼物数据
+ */
+ public void setGiftList(List giftList) {
+ this.mGiftList = giftList;
+ if (mAdapter != null) {
+ mAdapter.setNewData(giftList);
+ }
+ }
+
+ /**
+ * 设置点击回调监听器
+ */
+ public void setOnGiftItemClickListener(OnGiftItemClickListener listener) {
+ this.mListener = listener;
+ }
+
+ @Override
+ public void initData() {
+
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/XlhRankingDialog.java b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/XlhRankingDialog.java
new file mode 100644
index 00000000..32bca684
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/XlhRankingDialog.java
@@ -0,0 +1,176 @@
+package com.xscm.moduleutil.dialog.giftLottery;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.LinearLayoutManager;
+
+import com.scwang.smartrefresh.layout.api.RefreshLayout;
+import com.scwang.smartrefresh.layout.listener.OnRefreshLoadMoreListener;
+import com.xscm.moduleutil.R;
+import com.xscm.moduleutil.base.BaseMvpDialogFragment;
+import com.xscm.moduleutil.bean.GiftBean;
+import com.xscm.moduleutil.bean.WalletBean;
+import com.xscm.moduleutil.bean.blindboxwheel.BlindBoxBean;
+import com.xscm.moduleutil.bean.blindboxwheel.BlindReslutBean;
+import com.xscm.moduleutil.bean.blindboxwheel.XlhDrawBean;
+import com.xscm.moduleutil.databinding.DialogGiftLotteryFragmentBinding;
+import com.xscm.moduleutil.databinding.DialogRankingXlhFragmentBinding;
+import com.xscm.moduleutil.widget.pagerecyclerview.PagerGridSnapHelper;
+
+import java.util.ArrayList;
+import java.util.List;
+/**
+ *@author qx
+ *@data 2025/9/4
+ *@description:巡乐会榜单
+ */
+public class XlhRankingDialog extends BaseMvpDialogFragment implements GiftLotteryContacts.View{
+ private int page=1;
+ private String roomId;
+ private GiftRecordAdapte giftRecordAdapte;
+
+ @Override
+ protected GiftLotteryPresenter bindPresenter() {
+ return new GiftLotteryPresenter(this,getSelfActivity());
+ }
+
+ public static XlhRankingDialog newInstance(String giftBagId,int type) {
+ Bundle args = new Bundle();
+ XlhRankingDialog fragment = new XlhRankingDialog();
+ args.putString("roomId", giftBagId);
+ fragment.setArguments(args);
+ return fragment;
+ }
+ @Nullable
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ Dialog dialog = super.onCreateDialog(savedInstanceState);
+ dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ dialog.setCancelable(true);
+ return dialog;
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ Window window = getDialog().getWindow();
+ if (window != null) {
+ // 获取屏幕高度
+ android.util.DisplayMetrics displayMetrics = new android.util.DisplayMetrics();
+ requireActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
+ int screenHeight = displayMetrics.heightPixels;
+ // 设置高度为屏幕高度的100%(全屏)
+ int heightInPx = (int) (screenHeight * 0.8);;
+ window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, heightInPx);
+ window.setBackgroundDrawableResource(android.R.color.transparent);
+
+ // 可选:设置动画样式(从底部弹出)
+ window.setWindowAnimations(R.style.CommonShowDialogBottom);
+ }
+ }
+
+ @Override
+ protected void initDialogStyle(Window window) {
+ super.initDialogStyle(window);
+ window.setGravity(Gravity.BOTTOM);
+ }
+ @Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+ roomId = getArguments().getString("roomId");
+
+ }
+
+ @Override
+ protected void initData() {
+ MvpPre.xlhAllRecord(roomId, "1", "20",1);
+
+ }
+
+
+ @Override
+ protected void initView() {
+
+ mBinding.smartRefreshLayout.setOnRefreshLoadMoreListener(new OnRefreshLoadMoreListener() {
+ @Override
+ public void onRefresh(@NonNull RefreshLayout refreshLayout) {
+ page = 1;
+ MvpPre.xlhAllRecord(roomId, page+"", "20",1);
+
+ }
+
+ @Override
+ public void onLoadMore(@NonNull RefreshLayout refreshLayout) {
+ page++;
+ MvpPre.xlhAllRecord(roomId, page+"", "20",1);
+ }
+ });
+
+
+ giftRecordAdapte=new GiftRecordAdapte();
+ mBinding.recyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
+ mBinding.recyclerView.setAdapter(giftRecordAdapte);
+ }
+
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.dialog_ranking_xlh_fragment;
+ }
+
+ @Override
+ public void getGiftListSuccess(BlindBoxBean blindBoxBean) {
+
+ }
+
+ @Override
+ public void drawGiftListSuccess(BlindReslutBean blindReslutBean) {
+
+ }
+
+ @Override
+ public void getMyRecordSuccess(List data) {
+ }
+
+ @Override
+ public void getAllRecordSuccess(List data) {
+
+ if (data != null){
+ if (page==1){
+ giftRecordAdapte.setNewData(data);
+ }else {
+ giftRecordAdapte.addData(data);
+ }
+ }else {
+ if (page == 1) {
+ giftRecordAdapte.setNewData(null);
+ }
+ }
+ }
+
+ @Override
+ public void finishRefreshLoadMore() {
+ mBinding.smartRefreshLayout.finishRefresh();
+ mBinding.smartRefreshLayout.finishLoadMore();
+ }
+
+ @Override
+ public void wallet(WalletBean walletBean) {
+
+ }
+
+ @Override
+ public void xlhChouSuccess(List data) {
+
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/XlhRecordDialog.java b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/XlhRecordDialog.java
new file mode 100644
index 00000000..9425492c
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/dialog/giftLottery/XlhRecordDialog.java
@@ -0,0 +1,182 @@
+package com.xscm.moduleutil.dialog.giftLottery;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.LinearLayoutManager;
+
+import com.scwang.smartrefresh.layout.api.RefreshLayout;
+import com.scwang.smartrefresh.layout.listener.OnRefreshLoadMoreListener;
+import com.xscm.moduleutil.R;
+import com.xscm.moduleutil.base.BaseMvpDialogFragment;
+import com.xscm.moduleutil.bean.GiftBean;
+import com.xscm.moduleutil.bean.WalletBean;
+import com.xscm.moduleutil.bean.blindboxwheel.BlindBoxBean;
+import com.xscm.moduleutil.bean.blindboxwheel.BlindReslutBean;
+import com.xscm.moduleutil.bean.blindboxwheel.XlhDrawBean;
+import com.xscm.moduleutil.databinding.DialogGiftLotteryFragmentBinding;
+import com.xscm.moduleutil.databinding.DialogXlhRecordFragmentBinding;
+import com.xscm.moduleutil.widget.pagerecyclerview.PagerGridSnapHelper;
+
+import java.util.ArrayList;
+import java.util.List;
+/**
+ *@author qx
+ *@data 2025/9/4
+ *@description:巡乐会记录
+ */
+public class XlhRecordDialog extends BaseMvpDialogFragment implements GiftLotteryContacts.View{
+
+ private int page=1;
+ private String roomId;
+ private GiftRecordAdapter adapter;
+
+ @Override
+ protected GiftLotteryPresenter bindPresenter() {
+ return new GiftLotteryPresenter(this,getSelfActivity());
+ }
+
+ public static XlhRecordDialog newInstance(String roomId) {
+ Bundle args = new Bundle();
+ XlhRecordDialog fragment = new XlhRecordDialog();
+ args.putString("roomId", roomId);
+ fragment.setArguments(args);
+ return fragment;
+ }
+ @Nullable
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ Dialog dialog = super.onCreateDialog(savedInstanceState);
+ dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ dialog.setCancelable(true);
+ return dialog;
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ Window window = getDialog().getWindow();
+ if (window != null) {
+ // 获取屏幕高度
+ android.util.DisplayMetrics displayMetrics = new android.util.DisplayMetrics();
+ requireActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
+ int screenHeight = displayMetrics.heightPixels;
+ // 设置高度为屏幕高度的100%(全屏)
+ int heightInPx = (int) (screenHeight * 0.8);;
+ window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, heightInPx);
+ window.setBackgroundDrawableResource(android.R.color.transparent);
+
+ // 可选:设置动画样式(从底部弹出)
+ window.setWindowAnimations(R.style.CommonShowDialogBottom);
+ }
+ }
+
+ @Override
+ protected void initDialogStyle(Window window) {
+ super.initDialogStyle(window);
+ window.setGravity(Gravity.BOTTOM);
+ }
+ @Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+ roomId = getArguments().getString("roomId");
+
+ }
+
+ @Override
+ protected void initData() {
+ MvpPre.xlhMyRecord(roomId, "1", "20");
+
+ }
+
+
+ @Override
+ protected void initView() {
+
+
+ mBinding.smartRefreshLayout.setOnRefreshLoadMoreListener(new OnRefreshLoadMoreListener() {
+ @Override
+ public void onRefresh(@NonNull RefreshLayout refreshLayout) {
+ page = 1;
+ MvpPre.xlhMyRecord(roomId, page+"", "20");
+
+ }
+
+ @Override
+ public void onLoadMore(@NonNull RefreshLayout refreshLayout) {
+ page++;
+ MvpPre.xlhMyRecord(roomId, page+"", "20");
+ }
+ });
+ adapter=new GiftRecordAdapter();
+ GridLayoutManager layoutManager = new GridLayoutManager(getActivity(), 3);
+
+ mBinding.recyclerView.setLayoutManager(layoutManager);
+ mBinding.recyclerView.setOnFlingListener(null);
+ // 设置滚动辅助工具
+ PagerGridSnapHelper pageSnapHelper = new PagerGridSnapHelper();
+ pageSnapHelper.attachToRecyclerView(mBinding.recyclerView);
+ mBinding.recyclerView.setAdapter(adapter);
+ }
+
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.dialog_xlh_record_fragment;
+ }
+
+ @Override
+ public void getGiftListSuccess(BlindBoxBean blindBoxBean) {
+
+ }
+
+ @Override
+ public void drawGiftListSuccess(BlindReslutBean blindReslutBean) {
+
+ }
+
+ @Override
+ public void getMyRecordSuccess(List data) {
+ if (data != null){
+ if (page==1){
+ adapter.setNewData(data);
+ }else {
+ adapter.addData(data);
+ }
+ }else {
+ if (page == 1) {
+ adapter.setNewData(null);
+ }
+ }
+ }
+
+ @Override
+ public void getAllRecordSuccess(List data) {
+
+ }
+
+ @Override
+ public void finishRefreshLoadMore() {
+ mBinding.smartRefreshLayout.finishRefresh();
+ mBinding.smartRefreshLayout.finishLoadMore();
+ }
+
+ @Override
+ public void wallet(WalletBean walletBean) {
+
+ }
+
+ @Override
+ public void xlhChouSuccess(List data) {
+
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/event/FloatingScreenEvent.java b/moduleUtil/src/main/java/com/xscm/moduleutil/event/FloatingScreenEvent.java
new file mode 100644
index 00000000..91ceb698
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/event/FloatingScreenEvent.java
@@ -0,0 +1,17 @@
+package com.xscm.moduleutil.event;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ *@author qx
+ *@data 2025/9/22
+ *@description: 关闭飘屏
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class FloatingScreenEvent {
+ private boolean floatingScreen;
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/event/HourlyBean.java b/moduleUtil/src/main/java/com/xscm/moduleutil/event/HourlyBean.java
new file mode 100644
index 00000000..8b9015c9
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/event/HourlyBean.java
@@ -0,0 +1,17 @@
+package com.xscm.moduleutil.event;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 小时榜飘屏
+ */
+@Data
+public class HourlyBean implements Serializable {
+ private static final long serialVersionUID = 1L;
+ private String room_id;
+ private String room_name;
+ private String text;
+ private String rank_number;
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/event/LotteryEvent.java b/moduleUtil/src/main/java/com/xscm/moduleutil/event/LotteryEvent.java
new file mode 100644
index 00000000..403733a2
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/event/LotteryEvent.java
@@ -0,0 +1,37 @@
+package com.xscm.moduleutil.event;
+
+/**
+ *@author qx
+ *@data 2025/8/28
+ *@description: 这是创建一枚举,根据类型的不同,创建不同的弹窗
+ */
+public enum LotteryEvent {
+
+ MIRROR_SKY("10"), // 天空之境
+ CITY_TIME("11"), //岁月之城
+ PINNACLE_TIME("12"); // 时光之巅
+
+ private final String type;
+
+ LotteryEvent(String type) {
+ this.type = type;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ // 根据giftBagId字符串判断类型
+ public static LotteryEvent fromLotteryEvent(String giftBagId) {
+ if (giftBagId.equals("10")) {
+ return MIRROR_SKY;
+ }
+ if (giftBagId.equals("11")) {
+ return CITY_TIME;
+ }
+ if (giftBagId.equals("12")) {
+ return PINNACLE_TIME;
+ }
+ return MIRROR_SKY;
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/event/QXRoomSeatViewType.java b/moduleUtil/src/main/java/com/xscm/moduleutil/event/QXRoomSeatViewType.java
new file mode 100644
index 00000000..376f4c7b
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/event/QXRoomSeatViewType.java
@@ -0,0 +1,76 @@
+package com.xscm.moduleutil.event;
+
+public enum QXRoomSeatViewType {
+ /**
+ * 无类型
+ */
+ NONE(0, "无类型"),
+
+ /**
+ * 普通麦位(二卡八麦)
+ */
+ NORMAL(1, "点唱"),
+
+ KTV(3,"K歌"),
+
+ /**
+ * 拍卖麦位
+ */
+ AUCTION(2, "拍卖麦位"),
+
+
+
+ /**
+ * 小黑屋麦位
+ */
+ CABIN(6, "小黑屋麦位"),
+
+ /**
+ * 交友房麦位
+ */
+ FRIEND(7, "交友房麦位");
+
+ private final int value;
+ private final String description;
+
+ QXRoomSeatViewType(int value, String description) {
+ this.value = value;
+ this.description = description;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+ public static QXRoomSeatViewType fromLotteryEvent(int value) {
+ if (value==1) {
+ return NORMAL;
+ }
+ if (value==2) {
+ return AUCTION;
+ }
+ if (value==3) {
+ return KTV;
+ }
+
+ if (value==6){
+ return CABIN;
+ }
+ if (value==7){
+ return FRIEND;
+ }
+ return NONE;
+ }
+
+ @Override
+ public String toString() {
+ return "QXRoomSeatViewType{" +
+ "value=" + value +
+ ", description='" + description + '\'' +
+ '}';
+ }
+
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/event/RedBean.java b/moduleUtil/src/main/java/com/xscm/moduleutil/event/RedBean.java
new file mode 100644
index 00000000..aadd00ae
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/event/RedBean.java
@@ -0,0 +1,14 @@
+package com.xscm.moduleutil.event;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class RedBean implements Serializable {
+ private static final long serialVersionUID = 1L;
+ private String room_id;
+ private String room_name;
+ private String text;
+ private String nickname;
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/event/RedEnvelopeStatus.java b/moduleUtil/src/main/java/com/xscm/moduleutil/event/RedEnvelopeStatus.java
new file mode 100644
index 00000000..df40bce8
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/event/RedEnvelopeStatus.java
@@ -0,0 +1,17 @@
+package com.xscm.moduleutil.event;
+/**
+ * 红包打开状态
+ */
+public enum RedEnvelopeStatus {
+
+ /// 打开红包
+ QXRedBagDrawTypeOpen,
+ /// 仅倒计时
+ QXRedBagDrawTypeTimeDown,
+ /// 仅收藏房间
+ QXRedBagDrawTypeCollect,
+ /// 手慢了被领完了
+ QXRedBagDrawTypeFinished,
+ /// 发送评论领红包
+ QXRedBagDrawTypePwdSend,
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/http/ApiResponseCallback.java b/moduleUtil/src/main/java/com/xscm/moduleutil/http/ApiResponseCallback.java
new file mode 100644
index 00000000..4bb637ef
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/http/ApiResponseCallback.java
@@ -0,0 +1,134 @@
+package com.xscm.moduleutil.http;
+
+import android.content.Context;
+import android.widget.Toast;
+
+import com.blankj.utilcode.util.LogUtils;
+import com.blankj.utilcode.util.ToastUtils;
+import com.xscm.moduleutil.base.CommonAppContext;
+
+import org.greenrobot.eventbus.EventBus;
+
+import java.io.IOException;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+/**
+ * 通用的API响应处理回调类
+ * 统一处理所有接口的响应和错误情况
+ */
+public abstract class ApiResponseCallback implements Callback> {
+ private Context mContext;
+
+ // 构造方法,传入上下文用于显示提示
+ public ApiResponseCallback(Context context) {
+ this.mContext = context;
+ }
+
+ @Override
+ public void onResponse(Call> call, Response> response) {
+ // 统一处理HTTP响应
+ if (response.isSuccessful()) {
+ // 处理200-299范围内的HTTP状态码
+ BaseModel body = response.body();
+
+ if (body != null) {
+ // 根据code值进行不同处理
+ switch (body.getCode()) {
+ case 1: // 接口返回成功
+ // 业务成功,回调给具体实现
+ // 即使data为null也调用onSuccess,由具体实现决定如何处理null值
+ onSuccess(body.getData());
+ break;
+ case 0: // 接口请求成功但数据错误
+ // 显示错误信息
+// String errorMsg = body.getMsg() != null ? body.getMsg() : "操作失败,请重试";
+// showToast(errorMsg);
+ onFailure(new Exception(body.getMsg()));
+ break;
+ case 301: // 登录失效
+ // 显示错误信息并退出应用
+// String loginErrorMsg = body.getMsg() != null ? body.getMsg() : "登录已失效,请重新登录";
+ showToast(body.getMsg());
+
+ try {
+ // 发送退出登录事件
+// EventBus.getDefault().post(new com.xscm.moduleutil.event.LogOutEvent());
+
+ // 清除登录信息
+ CommonAppContext.getInstance().clearLoginInfo();
+
+ // 跳转到登录页面
+// android.content.Intent intent = new android.content.Intent(CommonAppContext.getInstance(), Class.forName("com.xscm.midi.LaunchPageActivity"));
+// intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK | android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK);
+// CommonAppContext.getInstance().startActivity(intent);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ onFailure(new Exception(body.getMsg()));
+ break;
+ default:
+ // 其他错误情况
+ String defaultErrorMsg = body.getMsg() != null ? body.getMsg() : "未知错误";
+ showToast(defaultErrorMsg);
+ onFailure(new Exception(defaultErrorMsg));
+ break;
+ }
+ } else {
+ // 响应体为空的情况
+ String errorMsg = "获取数据失败,请重试";
+ showToast(errorMsg);
+ onFailure(new Exception(errorMsg));
+ }
+ } else {
+ // 处理HTTP错误状态码
+ String errorInfo;
+ try {
+ if (response.errorBody() != null) {
+ errorInfo = response.errorBody().string();
+ // 可以在这里统一解析错误响应体
+ } else {
+ errorInfo = "请求失败,状态码:" + response.code();
+ }
+ } catch (IOException e) {
+ errorInfo = "请求失败,状态码:" + response.code();
+ e.printStackTrace();
+ }
+ showToast("");
+ onFailure(new Exception(errorInfo));
+ }
+ }
+
+ @Override
+ public void onFailure(Call> call, Throwable t) {
+ // 统一处理网络异常
+ String errorMsg;
+ if (t instanceof IOException) {
+// errorMsg = "网络异常,请检查网络连接";
+ } else {
+// errorMsg = "请求处理失败,请重试";
+ }
+ showToast("");
+ // 回调给具体实现处理
+ onFailure(t);
+ }
+
+ // 显示提示信息
+ private void showToast(String message) {
+ if (mContext != null) {
+ Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ // 业务成功时的回调,由具体接口实现
+ public abstract void onSuccess(T data);
+
+ // 错误时的回调,可选实现
+ public void onFailure(Throwable t) {
+ // 可以留空,由子类选择性实现
+ LogUtils.e("接口错误:",t);
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/http/BusinessAwareConverterFactory.java b/moduleUtil/src/main/java/com/xscm/moduleutil/http/BusinessAwareConverterFactory.java
new file mode 100644
index 00000000..f41332de
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/http/BusinessAwareConverterFactory.java
@@ -0,0 +1,74 @@
+package com.xscm.moduleutil.http;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+
+import com.blankj.utilcode.util.ToastUtils;
+import com.xscm.moduleutil.base.CommonAppContext;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import okhttp3.ResponseBody;
+import retrofit2.Converter;
+import retrofit2.Retrofit;
+import retrofit2.converter.gson.GsonConverterFactory;
+
+public class BusinessAwareConverterFactory extends Converter.Factory {
+ private final GsonConverterFactory originalFactory;
+ private final Context context;
+
+ public BusinessAwareConverterFactory(Context context) {
+ this.context = context;
+ this.originalFactory = GsonConverterFactory.create();
+ }
+
+ @Override
+ public Converter responseBodyConverter(Type type,
+ Annotation[] annotations,
+ Retrofit retrofit) {
+ final Converter delegate =
+ originalFactory.responseBodyConverter(type, annotations, retrofit);
+
+ return new Converter() {
+ @Override
+ public Object convert(ResponseBody value) throws IOException {
+ // 先读取响应字符串检查业务状态码
+ String responseString = value.string();
+ try {
+ JSONObject jsonObject = new JSONObject(responseString);
+ int code = jsonObject.getInt("code");
+ String msg = jsonObject.getString("msg");
+
+ if (code == 301) {
+ handleForceLogout();
+ ToastUtils.showShort(msg);
+ }
+
+ // 重新构建 ResponseBody 供原始转换器使用
+ ResponseBody newValue = ResponseBody.create(value.contentType(), responseString);
+ return delegate.convert(newValue);
+
+ } catch (JSONException e) {
+ throw new IOException("");
+ }
+ }
+
+ private void handleForceLogout() {
+ new Handler(Looper.getMainLooper()).post(() -> {
+ try {
+ CommonAppContext.getInstance().clearLoginInfo();
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ });
+ }
+ };
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/rtc/AgoraIsOPen.java b/moduleUtil/src/main/java/com/xscm/moduleutil/rtc/AgoraIsOPen.java
new file mode 100644
index 00000000..ece2b682
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/rtc/AgoraIsOPen.java
@@ -0,0 +1,13 @@
+package com.xscm.moduleutil.rtc;
+
+import lombok.Data;
+
+/**
+ *@author qx
+ *@data 2025/9/18
+ *@description: 加入声网返回
+ */
+@Data
+public class AgoraIsOPen {
+ private boolean isOpen;
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/service/IMConnectionService.java b/moduleUtil/src/main/java/com/xscm/moduleutil/service/IMConnectionService.java
new file mode 100644
index 00000000..c24d6b35
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/service/IMConnectionService.java
@@ -0,0 +1,124 @@
+package com.xscm.moduleutil.service;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Build;
+import android.os.IBinder;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.blankj.utilcode.util.LogUtils;
+import com.blankj.utilcode.util.ToastUtils;
+import com.tencent.imsdk.v2.V2TIMManager;
+import com.tencent.imsdk.v2.V2TIMSDKListener;
+import com.tencent.imsdk.v2.V2TIMUserFullInfo;
+import com.xscm.moduleutil.R;
+import com.xscm.moduleutil.base.CommonAppContext;
+import com.xscm.moduleutil.http.RetrofitClient;
+
+public class IMConnectionService extends Service {
+ private static final String TAG = "IMConnectionService";
+ private static final int NOTIFICATION_ID = 2;
+ private static final String CHANNEL_ID = "im_connection_channel";
+
+ private final V2TIMSDKListener imSdkListener = new V2TIMSDKListener() {
+ @Override
+ public void onConnecting() {
+ Log.d(TAG, "IM connecting...");
+ }
+
+ @Override
+ public void onConnectSuccess() {//重连成功
+ Log.d(TAG, "IM connect success");
+ if (CommonAppContext.getInstance().playId != null) {
+ LogUtils.e("@@@", ""+CommonAppContext.getInstance().playId);
+ RetrofitClient.getInstance().roomUserReconnect(CommonAppContext.getInstance().playId);
+ }
+ }
+
+ @Override
+ public void onConnectFailed(int code, String error) {
+ Log.e(TAG, "IM connect failed, code: " + code + ", error: " + error);
+ }
+
+ @Override
+ public void onKickedOffline() {
+ Log.w(TAG, "IM kicked offline");
+ if (CommonAppContext.getInstance().playId != null) {
+ ToastUtils.showShort("您的账号已被挤下线");
+ try {
+ CommonAppContext.getInstance().clearLoginInfo();
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+
+ @Override
+ public void onUserSigExpired() {
+ Log.w(TAG, "IM user sig expired");
+ }
+
+ @Override
+ public void onSelfInfoUpdated(V2TIMUserFullInfo info) {
+ Log.d(TAG, "IM self info updated");
+ }
+
+ };
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ startForegroundService();
+ V2TIMManager.getInstance().addIMSDKListener(imSdkListener);
+ Log.d(TAG, "IMConnectionService created and listener registered");
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ return START_STICKY; // 服务被杀死后会自动重启
+ }
+
+ @Nullable
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ private void startForegroundService() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ // 创建通知渠道
+ NotificationChannel channel = new NotificationChannel(
+ CHANNEL_ID,
+ "IM Connection Service",
+ NotificationManager.IMPORTANCE_LOW
+ );
+ NotificationManager manager = getSystemService(NotificationManager.class);
+ if (manager != null) {
+ manager.createNotificationChannel(channel);
+ }
+
+ // 创建通知
+ Notification notification = new Notification.Builder(this, CHANNEL_ID)
+ .setContentTitle("IM连接服务")
+ .setContentText("保持IM连接活跃")
+ .setSmallIcon(R.mipmap.default_avatar)
+ .setOngoing(true)
+ .build();
+
+ startForeground(NOTIFICATION_ID, notification);
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ V2TIMManager.getInstance().removeIMSDKListener(imSdkListener);
+ Log.d(TAG, "IMConnectionService destroyed and listener unregistered");
+ }
+}
\ No newline at end of file
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/service/MqttConnect.java b/moduleUtil/src/main/java/com/xscm/moduleutil/service/MqttConnect.java
new file mode 100644
index 00000000..34427f00
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/service/MqttConnect.java
@@ -0,0 +1,221 @@
+package com.xscm.moduleutil.service;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.blankj.utilcode.util.LogUtils;
+import com.google.android.gms.common.api.Api;
+import com.hjq.toast.ToastUtils;
+import com.xscm.moduleutil.utils.logger.DataLogger;
+
+import org.eclipse.paho.client.mqttv3.MqttClient;
+import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
+import org.eclipse.paho.client.mqttv3.MqttDeliveryToken;
+import org.eclipse.paho.client.mqttv3.MqttException;
+import org.eclipse.paho.client.mqttv3.MqttMessage;
+import org.eclipse.paho.client.mqttv3.MqttTopic;
+import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
+
+import java.util.ArrayList;
+
+public class MqttConnect {
+ private String HOST;
+ private String Tag = "MQTT";
+ private String clientId = "";
+ private static MqttClient mqttClient = null;
+ private Context context;
+
+ // 订阅主题
+ public static String shutdown = "";
+ public static String update_app = "";
+ public static String qx_hour_ranking = "";
+
+ public static String qx_redpacket_arrive="";//红包飘屏的主题
+ Handler handler = new Handler(Looper.getMainLooper());
+ String[] topic;
+ int[] qos = {1,2,3,0,0,0,0,0,0,0,0,0,0}; // 消息质量
+ private static MqttConnect instance;
+
+ public MqttConnect(Context context, String host, String clientId) {
+ this.HOST = host;
+ this.context = context;
+ this.clientId = clientId;
+
+ // 这里是你自己需要订阅的主题
+ shutdown = "qx_room_topic"; // 关机
+ update_app = "qx_xunlehui"; // 发送更新APP
+// qx_hour_ranking = "qx_hour_ranking";
+ qx_hour_ranking = "qx_hour_ranking";
+ qx_redpacket_arrive="qx_redpacket_arrive";
+
+ ArrayList topicList = new ArrayList<>();
+ topicList.add(shutdown);
+ topicList.add(update_app);
+ topicList.add(qx_hour_ranking);
+ topicList.add(qx_redpacket_arrive);
+ topic = topicList.toArray(new String[0]);
+ }
+
+ /**
+ * 单列模式,只能实例化一次
+ * @param context
+ * @param host
+ * @param clientId
+ * @return
+ */
+ public static synchronized MqttConnect getInstance(Context context, String host, String clientId) {
+ if (instance == null) {
+ instance = new MqttConnect(context, host, clientId);
+ }
+ return instance;
+ }
+
+ /**
+ * 客户端connect连接mqtt服务器
+ **/
+ public void mqttClient()
+ {
+// close();
+// handler.postDelayed(new Runnable() {
+// @Override
+// public void run() {
+ try {
+// uiTip("MQTT开始连接");
+ MqttConnectOptions options = mqttConnectOptions();
+ mqttClient.setCallback(new MqttInitCallback(context, HOST, clientId));
+ mqttClient.connect(options);
+// sub(topic,qos);
+ sub(shutdown);
+ sub(update_app);
+ sub(qx_hour_ranking);
+ sub(qx_redpacket_arrive);
+// uiTip("MQTT连接成功");
+ }catch (MqttException e){
+// uiTip("MQTT连接失败,准备重连。。。:"+e.getMessage());
+ handler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ Log.e(Tag,"开始重连。。。");
+ mqttClient();
+ }
+ },3000);
+ }
+// }
+// },200);
+ }
+
+ /**
+ * 在主线程弹出消息
+ * @param msg
+ */
+ private void uiTip(String msg){
+ Log.d(Tag,msg);
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+// Toast.makeText(context.getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
+ LogUtils.e("mqtt","连接成功");
+ ToastUtils.show(msg);
+ }
+ });
+ }
+
+ /**
+ * MQTT连接参数设置
+ */
+ private MqttConnectOptions mqttConnectOptions()
+ throws MqttException {
+ mqttClient = new MqttClient(HOST, clientId, new MemoryPersistence());
+ MqttConnectOptions options = new MqttConnectOptions();
+ options.setUserName("public");
+ options.setConnectionTimeout(10);
+ options.setCleanSession(true);
+ options.setConnectionTimeout(10);
+ options.setKeepAliveInterval(10);
+
+ return options;
+ }
+
+
+ /**
+ * 关闭MQTT连接
+ */
+ public void close(){
+ if(mqttClient != null && mqttClient.isConnected()){
+ try {
+ mqttClient.close();
+ mqttClient.disconnect();
+ mqttClient = null;
+ } catch (MqttException e) {
+ Log.e(Tag,"关闭MQTT连接报错:"+e.getMessage());
+// ToastUtils.show("关闭MQTT连接报错");
+ }
+ }else {
+ Log.d(Tag,"Mqtt已关闭");
+ }
+ }
+
+ /**
+ * 向某个主题发布消息 默认qos:1
+ */
+ public static void pub(String topic, String msg) throws MqttException {
+ MqttMessage mqttMessage = new MqttMessage();
+ mqttMessage.setPayload(msg.getBytes());
+ MqttTopic mqttTopic = mqttClient.getTopic(topic);
+ MqttDeliveryToken token = mqttTopic.publish(mqttMessage);
+ token.waitForCompletion();
+ }
+
+ /**
+ * 向某个主题发布消息
+ *
+ * @param topic: 发布的主题
+ * @param msg: 发布的消息
+ * @param qos: 消息质量 Qos:0、1、2
+ */
+ public void pub(String topic, String msg, int qos) throws MqttException {
+ MqttMessage mqttMessage = new MqttMessage();
+ mqttMessage.setQos(qos);
+ mqttMessage.setPayload(msg.getBytes());
+ MqttTopic mqttTopic = mqttClient.getTopic(topic);
+ MqttDeliveryToken token = mqttTopic.publish(mqttMessage);
+ token.waitForCompletion();
+ }
+
+ /**
+ * 订阅某一个主题 ,此方法默认的的Qos等级为:1
+ *
+ * @param topic 主题
+ */
+ public void sub(String topic){
+ try {
+ mqttClient.subscribe(topic);
+ } catch (MqttException e) {
+ Log.e(Tag,"MQTT主题订阅失败:" + e.getMessage());
+// uiTip("MQTT主题订阅失败");
+ }
+ }
+
+ /**
+ * 订阅某一个主题,可携带Qos
+ *
+ * @param topic 所要订阅的主题
+ * @param qos
+ * 消息质量:0最多发送一次,不保证消息能够到达接收端,也不负责重发
+ * 1至少发送一次,确保消息能够到达接收端,但可能会导致消息重复
+ * 2确保消息恰好被接收一次
+ */
+ public void sub(String[] topic, int[] qos){
+ try {
+ mqttClient.subscribe(topic, qos);
+ }catch (MqttException e){
+ Log.e(Tag,"订阅主题失败:"+e.getMessage());
+ }
+ }
+
+
+// 原文链接:https://blog.csdn.net/Fyx1987496919/article/details/140516525
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/service/MqttInitCallback.java b/moduleUtil/src/main/java/com/xscm/moduleutil/service/MqttInitCallback.java
new file mode 100644
index 00000000..9db19811
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/service/MqttInitCallback.java
@@ -0,0 +1,234 @@
+package com.xscm.moduleutil.service;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.blankj.utilcode.util.GsonUtils;
+import com.blankj.utilcode.util.LogUtils;
+import com.hjq.toast.ToastUtils;
+import com.orhanobut.logger.Logger;
+import com.xscm.moduleutil.bean.MqttXlhEnd;
+import com.xscm.moduleutil.bean.XLHBean;
+import com.xscm.moduleutil.event.HourlyBean;
+import com.xscm.moduleutil.event.RedBean;
+import com.xscm.moduleutil.event.RoomGiftRunable;
+import com.xscm.moduleutil.utils.SpUtil;
+
+import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
+import org.eclipse.paho.client.mqttv3.MqttCallback;
+import org.eclipse.paho.client.mqttv3.MqttMessage;
+import org.greenrobot.eventbus.EventBus;
+
+import java.util.List;
+
+public class MqttInitCallback implements MqttCallback {
+ private String Tag = "MqttInitCallback";
+ private String HOST;
+ private String clientId = "";
+ private MqttConnect mqttConnect = null;
+ private Context context = null;
+
+ MqttInitCallback(Context context, String host, String clientId) {
+ this.context = context;
+ this.HOST = host;
+ this.clientId = clientId;
+ }
+
+ /**
+ * 连接丢失
+ */
+ @Override
+ public void connectionLost(Throwable cause) {
+ Log.d(Tag, "mqtt连接断开,执行重连");
+// ToastUtils.show("mqtt连接断开,执行重连");
+ mqttConnect = MqttConnect.getInstance(context, HOST, clientId);
+ mqttConnect.mqttClient();
+ }
+
+ /**
+ * subscribe订阅后得到的消息会执行到这里
+ */
+ @Override
+ public void messageArrived(String topic, MqttMessage message) {
+// Log.d(Tag,"topic");
+// Log.d(Tag,topic);
+
+ String messageStr = message.toString();
+ Logger.e("MQTT", "收到的消息", "主题:" + topic + " 收到的消息:" + messageStr);
+ if (topic.equals("qx_room_topic")) {
+// ToastUtils.show("收到礼物飘屏");
+ receiveMessage(topic, messageStr);
+ } else if (topic.equals("qx_xunlehui")) {
+// ToastUtils.show("收到轮盘飘屏");
+ receiveXlhMessage(messageStr);
+ } else if (topic.equals("qx_hour_ranking")) {
+ receiveQXHourRanking(topic, messageStr);
+ } else if (topic.equals("qx_redpacket_arrive")) {
+ receiveRed(topic, messageStr);
+ }
+ }
+
+ private void receiveRed(String topic, String messageStr) {
+ try {
+ JSONObject jsonObject = JSON.parseObject(messageStr);
+ String message = jsonObject.getString("msg");
+ // 将事件处理放到主线程执行
+ new Handler(Looper.getMainLooper()).post(() -> {
+ processRedMessage(topic, message);
+ });
+ } catch (Exception e) {
+ Log.e("MQTT", "解析MQTT消息异常", e);
+ }
+ }
+
+ private void processRedMessage(String topic, String message) {
+ try {
+ // 如果 data 是集合字符串
+ // 解析为集合
+ RedBean dataList = JSON.parseObject(message, RedBean.class);
+
+ // 在主线程处理集合数据
+ new Handler(Looper.getMainLooper()).post(() -> {
+ processDataRed(dataList);
+ });
+ } catch (Exception e) {
+ Log.e("MQTT", "解析MQTT消息异常", e);
+ }
+ }
+
+ private void processDataRed(RedBean dataList) {
+ // 遍历集合并发送每个元素
+// for (HourlyBean dataItem : dataList) {
+// EventBus.getDefault().post(dataItem);
+// }
+
+ // 或者发送整个集合
+ EventBus.getDefault().post(dataList);
+ }
+
+ private void receiveQXHourRanking(String topic, String data) {
+ try {
+ JSONObject jsonObject = JSON.parseObject(data);
+ int type = jsonObject.getIntValue("type");
+ String message = jsonObject.getString("msg");
+
+ // 将事件处理放到主线程执行
+ new Handler(Looper.getMainLooper()).post(() -> {
+ processMessage(topic, message);
+ });
+ } catch (Exception e) {
+ Log.e("MQTT", "解析MQTT消息异常", e);
+// ToastUtils.show("收到礼物飘屏,解析异常");
+ }
+ }
+
+ private void processMessage(String topic, String data) {
+ try {
+ // 如果 data 是集合字符串
+ if (isJsonArray(data)) {
+ // 解析为集合
+ List dataList = JSON.parseArray(data, HourlyBean.class);
+
+ // 在主线程处理集合数据
+ new Handler(Looper.getMainLooper()).post(() -> {
+ processDataList(dataList);
+ });
+ }
+ } catch (Exception e) {
+ Log.e("MQTT", "解析MQTT消息异常", e);
+ }
+ }
+
+ // 处理集合数据
+ private void processDataList(List dataList) {
+ // 遍历集合并发送每个元素
+// for (HourlyBean dataItem : dataList) {
+// EventBus.getDefault().post(dataItem);
+// }
+
+ // 或者发送整个集合
+ EventBus.getDefault().post(dataList);
+ }
+
+ // 判断是否为 JSON 数组
+ private boolean isJsonArray(String jsonString) {
+ try {
+ return JSON.parseArray(jsonString) != null;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ private void receiveMessage(String topic, String data) {
+ try {
+ JSONObject jsonObject = JSON.parseObject(data);
+ int type = jsonObject.getIntValue("type");
+ String message = jsonObject.getString("msg");
+
+ // 将事件处理放到主线程执行
+ new Handler(Looper.getMainLooper()).post(() -> {
+ processMessageType(type, message);
+ });
+ } catch (Exception e) {
+ Log.e("MQTT", "解析MQTT消息异常", e);
+// ToastUtils.show("收到礼物飘屏,解析异常");
+ }
+ }
+
+ private void processMessageType(int type, String message) {
+ switch (type) {
+ case 5019://推送所有人-横幅礼物通知
+ new RoomGiftRunable(message).run();
+ break;
+ case 8000:
+// XLHBean xlhBean= GsonUtils.fromJson(message, XLHBean.class);
+// if (xlhBean!=null && xlhBean.getRoom_id()!=null && SpUtil.getMyRoomId()!=null) {
+// if (xlhBean.getRoom_id().equals(SpUtil.getMyRoomId())) {
+// if (xlhBean.getFrom_type()==3) {
+ LogUtils.e("MQTT", "收到消息" + message);
+
+ MqttXlhEnd mqttXlhEnd = new MqttXlhEnd();
+ mqttXlhEnd.setMessage(message);
+ EventBus.getDefault().post(mqttXlhEnd);
+
+// }
+// }
+// }
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void receiveXlhMessage(String messageStr) {
+ try {
+ JSONObject jsonObject = JSON.parseObject(messageStr);
+ int type = jsonObject.getIntValue("type");
+ String message = jsonObject.getString("msg");
+ XLHBean xlhBean = JSON.parseObject(message, XLHBean.class);
+ // 将事件处理放到主线程执行
+ new Handler(Looper.getMainLooper()).post(() -> {
+ processMessageType(type, message);
+ EventBus.getDefault().post(xlhBean);
+ });
+ } catch (Exception e) {
+ Log.e("MQTT", "解析MQTT消息异常", e);
+// ToastUtils.show("收到轮盘飘屏,解析异常");
+ }
+ }
+
+ /**
+ * publish发布成功后会执行到这里
+ */
+ @Override
+ public void deliveryComplete(IMqttDeliveryToken token) {
+
+ }
+
+
+// 原文链接:https://blog.csdn.net/Fyx1987496919/article/details/140516525
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/utils/ClickUtils.java b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/ClickUtils.java
new file mode 100644
index 00000000..ca420428
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/ClickUtils.java
@@ -0,0 +1,19 @@
+package com.xscm.moduleutil.utils;
+/**
+ *@author qx
+ *@data 2025/9/10
+ *@description: 防止重复点击的工具类
+ */
+public class ClickUtils {
+ private static final long CLICK_INTERVAL = 1000; // 1000ms内不允许重复点击
+ private static long lastClickTime = 0;
+
+ public static boolean isFastDoubleClick() {
+ long currentTime = System.currentTimeMillis();
+ if (currentTime - lastClickTime < CLICK_INTERVAL) {
+ return true;
+ }
+ lastClickTime = currentTime;
+ return false;
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/utils/CrashHandler.java b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/CrashHandler.java
new file mode 100644
index 00000000..98b608d4
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/CrashHandler.java
@@ -0,0 +1,41 @@
+package com.xscm.moduleutil.utils;
+
+import android.content.Context;
+import android.util.Log;
+
+import com.alibaba.android.arouter.launcher.ARouter;
+
+public class CrashHandler implements Thread.UncaughtExceptionHandler {
+ private static CrashHandler instance;
+ private Thread.UncaughtExceptionHandler defaultHandler;
+
+ private CrashHandler(Context context) {
+ defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
+ }
+
+ public static void init(Context context) {
+ if (instance == null) {
+ instance = new CrashHandler(context);
+ Thread.setDefaultUncaughtExceptionHandler(instance);
+ }
+ }
+
+ @Override
+ public void uncaughtException(Thread t, Throwable e) {
+ // 记录崩溃日志
+ Log.e("CrashHandler", "未捕获异常: " + e.getMessage());
+ // 简单处理空指针
+ if (e instanceof NullPointerException) {
+ // 重启应用或跳转错误页
+ restartApp();
+ } else {
+ // 交给系统默认处理
+ defaultHandler.uncaughtException(t, e);
+ }
+ }
+
+ private void restartApp() {
+ // 实现应用重启逻辑
+ ARouter.getInstance().build(ARouteConstants.ME).navigation();
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/utils/IMServiceManager.java b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/IMServiceManager.java
new file mode 100644
index 00000000..c7fd461c
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/IMServiceManager.java
@@ -0,0 +1,58 @@
+package com.xscm.moduleutil.utils;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+
+import com.xscm.moduleutil.service.IMConnectionService;
+
+public class IMServiceManager {
+ private static IMServiceManager instance;
+ private boolean isServiceStarted = false;
+
+ private IMServiceManager() {
+ }
+
+ public static synchronized IMServiceManager getInstance() {
+ if (instance == null) {
+ instance = new IMServiceManager();
+ }
+ return instance;
+ }
+
+ public void startIMService(Context context) {
+ if (isServiceStarted) {
+ return;
+ }
+
+ Intent serviceIntent = new Intent(context, IMConnectionService.class);
+ try {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ context.startForegroundService(serviceIntent);
+ } else {
+ context.startService(serviceIntent);
+ }
+ isServiceStarted = true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void stopIMService(Context context) {
+ if (!isServiceStarted) {
+ return;
+ }
+
+ Intent serviceIntent = new Intent(context, IMConnectionService.class);
+ try {
+ context.stopService(serviceIntent);
+ isServiceStarted = false;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public boolean isServiceStarted() {
+ return isServiceStarted;
+ }
+}
\ No newline at end of file
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/utils/MemoryOptimizationUtils.java b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/MemoryOptimizationUtils.java
new file mode 100644
index 00000000..56ac405a
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/MemoryOptimizationUtils.java
@@ -0,0 +1,105 @@
+package com.xscm.moduleutil.utils;
+
+import android.content.Context;
+
+import com.blankj.utilcode.util.LogUtils;
+import com.bumptech.glide.Glide;
+import com.orhanobut.logger.Logger;
+
+public class MemoryOptimizationUtils {
+ private static final String TAG = "MemoryOptimization";
+
+ /**
+ * 检查内存状态
+ */
+ public static boolean isMemoryLow() {
+ Runtime runtime = Runtime.getRuntime();
+ long usedMemory = runtime.totalMemory() - runtime.freeMemory();
+ long maxMemory = runtime.maxMemory();
+ double memoryUsage = (double) usedMemory / maxMemory;
+
+ LogUtils.d(TAG, "Memory usage: " + (memoryUsage * 100) + "%");
+
+ // 内存使用超过85%认为是低内存
+ return memoryUsage > 0.85;
+ }
+ private static long lastGCTime = 0;
+ private static final long MIN_GC_INTERVAL = 5000; // 5秒最小间隔
+
+ /**
+ * 强制进行垃圾回收
+ */
+ public static void forceGC() {
+ long currentTime = System.currentTimeMillis();
+
+ // 避免频繁调用GC
+ if (currentTime - lastGCTime < MIN_GC_INTERVAL) {
+ Logger.d(TAG, "Skipping GC, too frequent");
+ return;
+ }
+
+ lastGCTime = currentTime;
+
+ // 使用异步方式调用GC
+ new Thread(() -> {
+ try {
+ // 在后台线程执行GC
+ System.gc();
+ Thread.sleep(100); // 给GC一些时间
+ Runtime.getRuntime().runFinalization();
+ Logger.d(TAG, "Garbage collection completed");
+ } catch (Exception e) {
+ Logger.e(TAG, "Error during GC: " + e.getMessage());
+ }
+ }).start();
+ }
+
+ /**
+ * 清理图片缓存
+ */
+ public static void clearImageCache(Context context) {
+ try {
+ // 清理Glide缓存
+ Glide.get(context).clearMemory();
+
+ // 在后台线程清理磁盘缓存
+ new Thread(() -> {
+ try {
+ Glide.get(context).clearDiskCache();
+ } catch (Exception e) {
+ LogUtils.e(TAG, "Error clearing Glide disk cache: " + e.getMessage());
+ }
+ }).start();
+ } catch (Exception e) {
+ LogUtils.e(TAG, "Error clearing image cache: " + e.getMessage());
+ }
+ }
+ /**
+ * 获取当前内存使用情况
+ */
+ public static String getMemoryInfo() {
+ Runtime runtime = Runtime.getRuntime();
+ long maxMemory = runtime.maxMemory();
+ long totalMemory = runtime.totalMemory();
+ long freeMemory = runtime.freeMemory();
+ long usedMemory = totalMemory - freeMemory;
+
+ return String.format("Max: %d MB, Total: %d MB, Used: %d MB, Free: %d MB",
+ maxMemory / (1024 * 1024),
+ totalMemory / (1024 * 1024),
+ usedMemory / (1024 * 1024),
+ freeMemory / (1024 * 1024));
+ }
+ /**
+ * 清理SVGA缓存
+ */
+ public static void clearSVGACache() {
+ try {
+ // 如果SVGA库提供了清理缓存的方法,调用它
+ // SVGAParser.clearCache(); // 假设有这样的方法
+ } catch (Exception e) {
+ LogUtils.e(TAG, "Error clearing SVGA cache: " + e.getMessage());
+ }
+ }
+}
+
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/utils/QXRedPacketManager.java b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/QXRedPacketManager.java
new file mode 100644
index 00000000..2e57a881
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/QXRedPacketManager.java
@@ -0,0 +1,281 @@
+package com.xscm.moduleutil.utils;
+
+import android.os.Handler;
+import android.os.Looper;
+import com.blankj.utilcode.util.LogUtils;
+import com.xscm.moduleutil.bean.RedPacketInfo;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 红包管理器单例类
+ */
+public class QXRedPacketManager {
+ private static QXRedPacketManager instance;
+ private final Map redPackets;
+ private Handler checkTimerHandler;
+ private Runnable checkTimerRunnable;
+
+ // 私有构造函数,防止外部实例化
+ private QXRedPacketManager() {
+ this.redPackets = new ConcurrentHashMap<>();
+ }
+
+ /**
+ * 获取单例实例
+ *
+ * @return QXRedPacketManager 单例
+ */
+ public static QXRedPacketManager getInstance() {
+ if (instance == null) {
+ synchronized (QXRedPacketManager.class) {
+ if (instance == null) {
+ instance = new QXRedPacketManager();
+ }
+ }
+ }
+ return instance;
+ }
+ public List getSortedUserListLambda(Map userMap) {
+ List redPacketInfoList = new ArrayList<>(userMap.values());
+ redPacketInfoList.sort((user1, user2) -> Long.compare(user1.getStart_time(), user2.getStart_time()));
+ return redPacketInfoList;
+ }
+
+ /**
+ * 添加红包列表
+ *
+ * @param redPackets 红包模型列表
+ */
+ public void addRedPackets(List redPackets) {
+ if (redPackets == null || redPackets.isEmpty()) {
+ return;
+ }
+
+ for (RedPacketInfo model : redPackets) {
+ this.redPackets.put(model.getRedpacket_id(), model);
+ }
+
+ // 在添加数据后启动定时器(如果尚未启动)
+ startCheckTimer();
+ if (this.delegate != null && this.delegate instanceof QXRedPacketManagerDelegate) {
+ ((QXRedPacketManagerDelegate) this.delegate).onRedPacketsAdded(redPackets, this.redPackets.size());
+ }
+ }
+
+ /**
+ * 添加单个红包
+ *
+ * @param redPacket 红包模型
+ */
+ public void addRedPacket(RedPacketInfo redPacket) {
+ if (redPacket == null || redPacket.getRedpacket_id() == null) {
+ return;
+ }
+
+ this.redPackets.put(redPacket.getRedpacket_id(), redPacket);
+
+ // 在添加数据后启动定时器(如果尚未启动)
+ startCheckTimer();
+ if (this.delegate != null && this.delegate instanceof QXRedPacketManagerDelegate) {
+ ((QXRedPacketManagerDelegate) this.delegate).onRedPacketAdded(redPacket, this.redPackets.size());
+ }
+ }
+
+ /**
+ * 移除红包
+ *
+ * @param packetId 红包ID
+ */
+ public void removeRedPacket(String packetId) {
+ this.redPackets.remove(packetId);
+
+ if (this.delegate != null && this.delegate instanceof QXRedPacketManagerDelegate) {
+ ((QXRedPacketManagerDelegate) this.delegate).onRedPacketRemoved(packetId, this.redPackets.size());
+ }
+ }
+
+ /**
+ * 获取所有红包
+ *
+ * @return 红包列表
+ */
+ public List getAllRedPackets() {
+ return getSortedUserListLambda(redPackets);
+ }
+
+ /**
+ * 根据ID获取红包
+ *
+ * @param packetId 红包ID
+ * @return 红包模型
+ */
+ public RedPacketInfo getRedPacket(String packetId) {
+ return this.redPackets.get(packetId);
+ }
+
+ /**
+ * 开始检查定时器
+ */
+ public void startCheckTimer() {
+ // 如果定时器已经在运行,直接返回
+ if (checkTimerRunnable != null && checkTimerHandler != null) {
+ return;
+ }
+ if (checkTimerRunnable == null) {
+ checkTimerRunnable = new Runnable() {
+ @Override
+ public void run() {
+ checkAndUpdateRedPackets();
+ }
+ };
+
+ checkTimerHandler = new Handler(Looper.getMainLooper());
+ checkTimerHandler.post(checkTimerRunnable);
+ }
+ }
+
+ /**
+ * 检查并更新红包状态
+ */
+ private void checkAndUpdateRedPackets() {
+ // 添加空值检查
+ if (this.redPackets == null || this.redPackets.isEmpty()) {
+ return;
+ }
+ List packets = getAllRedPackets();
+
+ for (RedPacketInfo packet : packets) {
+ long packetTime = packet.remainingTime();
+ LogUtils.e("红包剩余时间:" + packet.getRedpacket_time());
+ long redpacketTime = 0;
+ try {
+ if (packet.getRedpacket_time() != null) {
+ redpacketTime = Long.parseLong(packet.getRedpacket_time());
+ }
+ } catch (NumberFormatException e) {
+ LogUtils.e("红包时间格式错误: " + packet.getRedpacket_time());
+ }
+ if (packetTime <= -redpacketTime) {
+
+ removeRedPacket(packet.getRedpacket_id());
+ }
+ if (packet.getCountdown()==0){
+ continue;
+ }
+
+ if (this.delegate != null && this.delegate instanceof QXRedPacketManagerDelegate) {
+ ((QXRedPacketManagerDelegate) this.delegate).didUpdateRedPacketTime(packet, packetTime);
+ }
+
+ boolean wasAvailable = packet.isAvailable();
+ packet.setAvailable(packet.canOpenNow());
+
+ // 状态发生变化时通知
+ if (wasAvailable != packet.isAvailable()) {
+ if (this.delegate != null && this.delegate instanceof QXRedPacketManagerDelegate) {
+ ((QXRedPacketManagerDelegate) this.delegate).onRedPacketUpdated(packet, this.redPackets.size());
+ }
+ }
+ }
+
+ // 继续执行定时任务
+ // 修复:增加空值检查避免 NullPointerException
+ if (checkTimerHandler != null && checkTimerRunnable != null) {
+ // 继续执行定时任务
+ checkTimerHandler.postDelayed(checkTimerRunnable, 1000);
+ }
+ }
+
+ /**
+ * 移除所有红包
+ */
+ public void removeAllRedPackets() {
+ this.redPackets.clear();
+ endCheckTimer();
+ }
+
+ /**
+ * 结束检查定时器
+ */
+ public void endCheckTimer() {
+ if (checkTimerHandler != null) {
+ checkTimerHandler.removeCallbacks(checkTimerRunnable);
+ checkTimerHandler = null;
+ checkTimerRunnable = null;
+ }
+ }
+
+ /**
+ * 销毁红包信息
+ */
+ public void destroyRedpacketInfo() {
+ removeAllRedPackets();
+ endCheckTimer();
+ this.delegate = null;
+ }
+
+ /**
+ * 委托接口
+ */
+ public interface QXRedPacketManagerDelegate {
+ /**
+ * 添加红包列表回调
+ *
+ * @param redPackets 红包列表
+ * @param remainingCount 剩余数量
+ */
+ void onRedPacketsAdded(List redPackets, int remainingCount);
+
+ /**
+ * 添加单个红包回调
+ *
+ @param redPacket 红包模型
+ * @param remainingCount 剩余数量
+ */
+ void onRedPacketAdded(RedPacketInfo redPacket, int remainingCount);
+
+ /**
+ * 移除红包回调
+ *
+ * @param packetId 红包ID
+ * @param remainingCount 剩余数量
+ */
+ void onRedPacketRemoved(String packetId, int remainingCount);
+
+ /**
+ * 更新红包状态回调
+ *
+ * @param packet 红包模型
+ * @param remainingCount 剩余数量
+ */
+ void onRedPacketUpdated(RedPacketInfo packet, int remainingCount);
+
+ /**
+ * 更新红包时间回调
+ *
+ * @param packet 红包模型
+ * @param packetTime 红包剩余时间
+ */
+ void didUpdateRedPacketTime(RedPacketInfo packet, long packetTime);
+ }
+
+ /**
+ * -- SETTER --
+ * 设置委托对象
+ *
+ *
+ * -- GETTER --
+ * 获取委托对象
+ *
+ @param delegate 委托对象
+ * @return 委托对象
+ */
+ @Getter
+ @Setter
+ private QXRedPacketManagerDelegate delegate;
+}
+
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/utils/TextViewUtils.java b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/TextViewUtils.java
new file mode 100644
index 00000000..d25a5321
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/TextViewUtils.java
@@ -0,0 +1,171 @@
+package com.xscm.moduleutil.utils;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.os.Build;
+import android.text.Html;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.method.LinkMovementMethod;
+import android.text.style.BackgroundColorSpan;
+import android.text.style.ClickableSpan;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.RelativeSizeSpan;
+import android.text.style.StrikethroughSpan;
+import android.text.style.StyleSpan;
+import android.text.style.UnderlineSpan;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.NonNull;
+
+/**
+ * TextView 富文本工具类(Java实现)
+ * 支持HTML解析、部分文本样式、点击事件等功能
+ */
+public class TextViewUtils {
+
+ /**
+ * 显示HTML格式文本
+ * @param textView 目标TextView
+ * @param htmlContent HTML内容字符串
+ */
+ public static void setHtmlText(TextView textView, String htmlContent) {
+ setHtmlText(textView, htmlContent, true);
+ }
+
+ /**
+ * 显示HTML格式文本(可控制链接点击)
+ * @param textView 目标TextView
+ * @param htmlContent HTML内容字符串
+ * @param enableLinks 是否启用链接点击
+ */
+ public static void setHtmlText(TextView textView, String htmlContent, boolean enableLinks) {
+ if (textView == null || htmlContent == null) return;
+
+ // 处理不同Android版本的HTML解析
+ CharSequence spannedText;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ spannedText = Html.fromHtml(htmlContent, Html.FROM_HTML_MODE_COMPACT);
+ } else {
+ // 兼容Android N以下版本
+ spannedText = Html.fromHtml(htmlContent);
+ }
+
+ textView.setText(spannedText);
+
+ // 启用链接点击功能
+ if (enableLinks) {
+ textView.setMovementMethod(LinkMovementMethod.getInstance());
+ textView.setHighlightColor(Color.TRANSPARENT); // 去除点击高亮
+ }
+ }
+
+ /**
+ * 给部分文本设置样式
+ * @param textView 目标TextView
+ * @param fullText 完整文本
+ * @param targetText 需要设置样式的子文本
+ * @param spans 样式集合(可传入多个)
+ */
+ public static void setPartialStyle(TextView textView, String fullText,
+ String targetText, Object... spans) {
+ if (textView == null || fullText == null || targetText == null) return;
+
+ int startIndex = fullText.indexOf(targetText);
+ if (startIndex == -1) {
+ textView.setText(fullText);
+ return;
+ }
+
+ int endIndex = startIndex + targetText.length();
+ SpannableString spannable = new SpannableString(fullText);
+
+ // 应用所有样式
+ for (Object span : spans) {
+ spannable.setSpan(span, startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ textView.setText(spannable);
+ }
+
+ /**
+ * 设置可点击文本
+ * @param textView 目标TextView
+ * @param fullText 完整文本
+ * @param clickText 可点击的子文本
+ * @param linkColor 链接颜色
+ * @param isUnderline 是否显示下划线
+ * @param listener 点击事件监听器
+ */
+ public static void setClickableText(TextView textView, String fullText, String clickText,
+ @ColorInt int linkColor, boolean isUnderline,
+ OnClickableTextListener listener) {
+ if (textView == null || fullText == null || clickText == null || listener == null) return;
+
+ int startIndex = fullText.indexOf(clickText);
+ if (startIndex == -1) {
+ textView.setText(fullText);
+ return;
+ }
+
+ int endIndex = startIndex + clickText.length();
+ SpannableString spannable = new SpannableString(fullText);
+
+ // 创建可点击样式
+ ClickableSpan clickableSpan = new ClickableSpan() {
+ @Override
+ public void onClick(@NonNull View widget) {
+ listener.onClick();
+ }
+
+ @Override
+ public void updateDrawState(@NonNull android.text.TextPaint ds) {
+ super.updateDrawState(ds);
+ ds.setColor(linkColor);
+ ds.setUnderlineText(isUnderline);
+ }
+ };
+
+ spannable.setSpan(clickableSpan, startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ textView.setText(spannable);
+ textView.setMovementMethod(LinkMovementMethod.getInstance());
+ textView.setHighlightColor(Color.TRANSPARENT);
+ }
+
+ // 快捷创建样式的工具方法
+ public static StyleSpan createBoldSpan() {
+ return new StyleSpan(Typeface.BOLD);
+ }
+
+ public static StyleSpan createItalicSpan() {
+ return new StyleSpan(Typeface.ITALIC);
+ }
+
+ public static ForegroundColorSpan createTextColorSpan(@ColorInt int color) {
+ return new ForegroundColorSpan(color);
+ }
+
+ public static BackgroundColorSpan createBgColorSpan(@ColorInt int color) {
+ return new BackgroundColorSpan(color);
+ }
+
+ public static UnderlineSpan createUnderlineSpan() {
+ return new UnderlineSpan();
+ }
+
+ public static StrikethroughSpan createStrikethroughSpan() {
+ return new StrikethroughSpan();
+ }
+
+ public static RelativeSizeSpan createTextSizeSpan(float proportion) {
+ return new RelativeSizeSpan(proportion);
+ }
+
+ /**
+ * 可点击文本的监听器接口
+ */
+ public interface OnClickableTextListener {
+ void onClick();
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/utils/cos/CosUploadManager.java b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/cos/CosUploadManager.java
new file mode 100644
index 00000000..c3d916c1
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/cos/CosUploadManager.java
@@ -0,0 +1,146 @@
+package com.xscm.moduleutil.utils.cos;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import androidx.annotation.Nullable;
+import com.blankj.utilcode.util.LogUtils;
+import com.tencent.cos.xml.CosXmlService;
+import com.tencent.cos.xml.CosXmlServiceConfig;
+import com.tencent.cos.xml.exception.CosXmlClientException;
+import com.tencent.cos.xml.exception.CosXmlServiceException;
+import com.tencent.cos.xml.listener.CosXmlResultListener;
+import com.tencent.cos.xml.model.CosXmlRequest;
+import com.tencent.cos.xml.model.CosXmlResult;
+import com.tencent.cos.xml.model.object.PutObjectRequest;
+import com.tencent.cos.xml.transfer.COSXMLUploadTask;
+import com.tencent.cos.xml.transfer.TransferConfig;
+import com.tencent.cos.xml.transfer.TransferManager;
+import com.tencent.qcloud.core.auth.SessionQCloudCredentials;
+import com.xscm.moduleutil.http.BaseObserver;
+import com.xscm.moduleutil.http.RetrofitClient;
+import com.xscm.moduleutil.utils.oss.OSSOperUtils;
+import io.reactivex.disposables.Disposable;
+import org.jetbrains.annotations.NotNull;
+
+
+/**
+ * com.xscm.moduleutil.utils.cos
+ * qx
+ * 2025/10/23
+ */
+public class CosUploadManager {
+ private static volatile CosUploadManager instance;
+ private Context context;
+ private Handler mainHandler;
+ // 私有构造函数,防止外部实例化
+ private CosUploadManager() {
+ mainHandler = new Handler(Looper.getMainLooper());
+ }
+
+ // 双重检查锁定获取单例实例
+ public static CosUploadManager getInstance() {
+ if (instance == null) {
+ synchronized (CosUploadManager.class) {
+ if (instance == null) {
+ instance = new CosUploadManager();
+ }
+ }
+ }
+ return instance;
+ }
+ public void init(Context context) {
+ this.context = context.getApplicationContext();
+ }
+ public void upParameters( String objectKey, String localPath, UploadCallback callback) {
+ // 确保已初始化
+ if (context == null) {
+ callback.onFailure(new IllegalStateException("CosUploadManager not initialized with context"));
+ return;
+ }
+ RetrofitClient.getInstance().getTempKey(new BaseObserver() {
+ @Override
+ public void onSubscribe(@NotNull Disposable disposable) {
+
+ }
+
+ @Override
+ public void onNext(@NotNull TempKeyBean tempKeyBean) {
+ if (tempKeyBean != null){
+ upCosData(tempKeyBean, tempKeyBean.getBucket(), objectKey, localPath, callback);
+ }
+ }
+ });
+ }
+
+ public void upCosData(TempKeyBean tempKeyBean, String bucketName, String objectKey, String localFilePath, UploadCallback callback){
+ // 获取临时密钥(业务层控制获取的方式)
+ String tmpSecretId = tempKeyBean.getCredentials().getTmpSecretId(); // 临时密钥 SecretId
+ String tmpSecretKey = tempKeyBean.getCredentials().getTmpSecretKey(); // 临时密钥 SecretKey
+ String sessionToken = tempKeyBean.getCredentials().getSessionToken(); // 临时密钥 Token
+ long expiredTime = tempKeyBean.getExpiredTime();//临时密钥有效截止时间戳,单位是秒
+// 建议返回服务器时间作为签名的开始时间,避免由于用户手机本地时间偏差过大导致请求过期
+ long startTime = tempKeyBean.getStartTime(); //临时密钥有效起始时间,单位是秒
+ // 存储桶所在地域简称,例如广州地区是 ap-guangzhou
+ String region = tempKeyBean.getRegion();
+
+ SessionQCloudCredentials sessionQCloudCredentials = new SessionQCloudCredentials(tmpSecretId, tmpSecretKey,
+ sessionToken, startTime, expiredTime);
+ // 创建 CosXmlServiceConfig 对象,根据需要修改默认的配置参数
+ CosXmlServiceConfig serviceConfig = new CosXmlServiceConfig.Builder()
+ .setRegion(region)
+ .isHttps(true) // 使用 HTTPS 请求, 默认为 HTTP 请求
+ .builder();
+ CosXmlService cosXmlService = new CosXmlService(context, serviceConfig);
+
+ // 任何 CosXmlRequest 都支持这种方式,例如上传 PutObjectRequest、下载 GetObjectRequest、删除 DeleteObjectRequest 等
+// 以下用上传进行示例
+ PutObjectRequest putRequest = new PutObjectRequest(bucketName, objectKey, localFilePath);
+// sessionQCloudCredentials 为第一步“初始化密钥”中获取到的单次临时密钥
+ putRequest.setCredential(sessionQCloudCredentials);
+// 初始化 TransferConfig,这里使用默认配置,如果需要定制,请参考 SDK 接口文档
+ TransferConfig transferConfig = new TransferConfig.Builder().build();
+// 初始化 TransferManager
+ TransferManager transferManager = new TransferManager(cosXmlService, transferConfig);
+ COSXMLUploadTask uploadTask = transferManager.upload(putRequest, null);
+ uploadTask.setCosXmlResultListener(new CosXmlResultListener() {
+
+ @Override
+ public void onSuccess(CosXmlRequest cosXmlRequest, CosXmlResult cosXmlResult) {
+ COSXMLUploadTask.COSXMLUploadTaskResult uploadResult =
+ (COSXMLUploadTask.COSXMLUploadTaskResult) cosXmlResult;
+ LogUtils.e("@@@1", "上传成功", "描述:", "文件ID" + uploadResult);
+ // 如果有回调,则调用成功回调
+ if (callback != null) {
+ // 构造文件访问URL
+ String url =uploadResult.accessUrl;
+ mainHandler.post(() -> callback.onSuccess(url));
+ }
+ }
+
+ @Override
+ public void onFail(CosXmlRequest cosXmlRequest, @Nullable @org.jetbrains.annotations.Nullable CosXmlClientException e, @Nullable @org.jetbrains.annotations.Nullable CosXmlServiceException e1) {
+ // 切换到主线程执行回调
+ mainHandler.post(() -> {
+ if (e != null) {
+ LogUtils.e("CosUpload", "上传失败", e);
+ if (callback != null) {
+ callback.onFailure(e);
+ }
+ } else {
+ LogUtils.e("CosUpload", "上传失败", e1);
+ if (callback != null) {
+ callback.onFailure(e1);
+ }
+ }
+ });
+ }
+ });
+ }
+
+ // 上传回调接口
+ public interface UploadCallback {
+ void onSuccess(String url); // 上传成功,返回访问URL
+ void onFailure(Exception e); // 上传失败
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/utils/cos/FilePathHelpe.java b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/cos/FilePathHelpe.java
new file mode 100644
index 00000000..86d67981
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/cos/FilePathHelpe.java
@@ -0,0 +1,216 @@
+package com.xscm.moduleutil.utils.cos;
+
+import android.annotation.SuppressLint;
+import android.content.ContentUris;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Environment;
+import android.provider.DocumentsContract;
+import android.provider.MediaStore;
+import android.util.Log;
+import androidx.annotation.RequiresApi;
+
+/**
+ * com.xscm.moduleutil.utils.cos
+ * qx
+ * 2025/10/23
+ */
+public class FilePathHelpe {
+
+ public static String getPathFromUri(Context context, Uri uri) {
+
+ String scheme = uri.getScheme();
+ Log.d("TAG", scheme);
+ if (scheme.equalsIgnoreCase("content")) {
+ return getPathFromMediaUri(context, uri);
+ } else if (scheme.equalsIgnoreCase("file")){
+ return getPathFromFileUri(context, uri);
+ }
+ return "";
+ }
+
+
+ private static String getPathFromMediaUri(Context context, Uri uri) {
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ return getKitKatPathFromMediaUri(context, uri);
+ } else {
+ return getImagePathFromMediaUri(context, uri, null);
+ }
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.KITKAT)
+ private static String getKitKatPathFromMediaUri(Context context, Uri uri) {
+
+ String imagePath = "";
+ if (DocumentsContract.isDocumentUri(context, uri)) {
+ String docId = DocumentsContract.getDocumentId(uri);
+ if ("com.android.providers.media.documents".equals(uri.getAuthority())) {
+ //Log.d(TAG, uri.toString());
+ String id = docId.split(":")[1];
+ String selection = MediaStore.Images.Media._ID + "=" + id;
+
+ imagePath = getImagePathFromMediaUri(context, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);
+ } else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) {
+ //Log.d(TAG, uri.toString());
+ Uri contentUri = ContentUris.withAppendedId(
+ Uri.parse("content://downloads/public_downloads"),
+ Long.valueOf(docId));
+ imagePath = getImagePathFromMediaUri(context, contentUri, null);
+ }
+ } else if ("content".equalsIgnoreCase(uri.getScheme())) {
+ //Log.d(TAG, "content: " + uri.toString());
+ imagePath = getImagePathFromMediaUri(context, uri, null);
+ }
+ return imagePath;
+ }
+
+ @SuppressLint("Range")
+ private static String getImagePathFromMediaUri(Context context, Uri uri, String selection) {
+ String path = null;
+ Cursor cursor = context.getContentResolver().query(uri, null, selection, null, null);
+ if (cursor != null) {
+ if (cursor.moveToFirst()) {
+ //path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
+ path = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.DATA));
+ }
+
+ cursor.close();
+ }
+ return path;
+ }
+
+ private static String getPathFromFileUri(Context context, Uri uri) {
+
+ return uri.getPath();
+ }
+
+
+ public static String getPathBeforeKitKat(Context context, Uri uri) {
+ if ("content".equalsIgnoreCase(uri.getScheme())) {
+ String[] projection = { MediaStore.MediaColumns.DATA };
+ Cursor cursor = null;
+ try {
+ cursor = context.getContentResolver().query(uri, projection,null, null, null);
+ int column_index = cursor.getColumnIndexOrThrow("_data");
+ if (cursor.moveToFirst()) {
+ return cursor.getString(column_index);
+ }
+ } catch (Exception e) {
+
+ } finally {
+ if(cursor != null)
+ cursor.close();
+ }
+ } else if ("file".equalsIgnoreCase(uri.getScheme())) {
+ return uri.getPath();
+ }
+
+ return null;
+ }
+
+ @SuppressLint("NewApi")
+ public static String getPathAfterKitKat(Context context, Uri uri) {
+
+ if (DocumentsContract.isDocumentUri(context, uri)) {
+ // ExternalStorageProvider
+ if (isExternalStorageDocument(uri)) {
+ final String docId = DocumentsContract.getDocumentId(uri);
+ final String[] split = docId.split(":");
+ final String type = split[0];
+
+ if ("primary".equalsIgnoreCase(type)) {
+ return Environment.getExternalStorageDirectory() + "/" + split[1];
+ }
+
+ // TODO handle non-primary volumes
+ }
+ // DownloadsProvider
+ else if (isDownloadsDocument(uri)) {
+
+ final String id = DocumentsContract.getDocumentId(uri);
+ final Uri contentUri = ContentUris.withAppendedId(
+ Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
+
+ return getDataColumn(context, contentUri, null, null);
+ }
+ // MediaProvider
+ else if (isMediaDocument(uri)) {
+ final String docId = DocumentsContract.getDocumentId(uri);
+ final String[] split = docId.split(":");
+ final String type = split[0];
+
+ Uri contentUri = null;
+ if ("image".equals(type)) {
+ contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+ } else if ("video".equals(type)) {
+ contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
+ } else if ("audio".equals(type)) {
+ contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
+ }
+
+ final String selection = "_id=?";
+ final String[] selectionArgs = new String[] { split[1] };
+
+ return getDataColumn(context, contentUri, selection, selectionArgs);
+ }
+ }
+ // MediaStore (and general)
+ else if ("content".equalsIgnoreCase(uri.getScheme())) {
+ return getDataColumn(context, uri, null, null);
+ }
+ // File
+ else if ("file".equalsIgnoreCase(uri.getScheme())) {
+ return uri.getPath();
+ }
+
+ return null;
+ }
+
+
+ public static String getDataColumn(Context context, Uri uri, String selection,
+ String[] selectionArgs) {
+
+ Cursor cursor = null;
+ final String column = "_data";
+ final String[] projection = { column };
+
+ try {
+ cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
+ null);
+ if (cursor != null && cursor.moveToFirst()) {
+ final int column_index = cursor.getColumnIndexOrThrow(column);
+ return cursor.getString(column_index);
+ }
+ } finally {
+ if (cursor != null)
+ cursor.close();
+ }
+ return null;
+ }
+
+
+ public static boolean isExternalStorageDocument(Uri uri) {
+ return "com.android.externalstorage.documents".equals(uri.getAuthority());
+ }
+
+
+ public static boolean isDownloadsDocument(Uri uri) {
+ return "com.android.providers.downloads.documents".equals(uri.getAuthority());
+ }
+
+
+ public static boolean isMediaDocument(Uri uri) {
+ return "com.android.providers.media.documents".equals(uri.getAuthority());
+ }
+
+ public static String getPath(Context context, Uri uri) {
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ return getPathAfterKitKat(context, uri);
+ }
+ return getPathBeforeKitKat(context, uri);
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/utils/cos/RemoteCOSSigner.java b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/cos/RemoteCOSSigner.java
new file mode 100644
index 00000000..fc0affc0
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/cos/RemoteCOSSigner.java
@@ -0,0 +1,158 @@
+package com.xscm.moduleutil.utils.cos;
+
+import android.text.TextUtils;
+import com.tencent.qcloud.core.auth.QCloudCredentials;
+import com.tencent.qcloud.core.auth.QCloudSigner;
+import com.tencent.qcloud.core.common.QCloudClientException;
+import com.tencent.qcloud.core.common.QCloudServiceException;
+import com.tencent.qcloud.core.http.QCloudHttpClient;
+import com.tencent.qcloud.core.http.QCloudHttpRequest;
+import com.tencent.qcloud.core.http.RequestBodySerializer;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.net.URL;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * com.xscm.moduleutil.utils.cos
+ * qx
+ * 2025/10/23
+ */
+public class RemoteCOSSigner implements QCloudSigner {
+
+ private URL requestSignUrl;
+
+ public RemoteCOSSigner(URL url) {
+
+ requestSignUrl = url;
+ }
+
+ /**
+ * @param request 即为发送到 CSP 服务端的请求,您需要根据这个 HTTP 请求的参数来计算签名,并给其添加 Authorization header
+ * @param credentials 空字段,请不要使用
+ * @throws QCloudClientException 您可以在处理过程中抛出异常
+ */
+ @Override
+ public void sign(QCloudHttpRequest request, QCloudCredentials credentials) throws QCloudClientException {
+
+ /**
+ * 获取计算签名所需字段
+ */
+ URL url = request.url();
+ String method = request.method();
+ String host = url.getHost();
+ String schema = url.getProtocol();
+ String path = url.getPath();
+ Map headers = getHeaderMap(request.headers());
+ Map params = getQueryMap(url.getQuery());
+
+
+ String signFieldJson = null;
+ try {
+ signFieldJson = signField2Json(method, schema, host, path, headers, params);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ throw new QCloudClientException("sign field transfer to json failed");
+ }
+
+
+ /**
+ * 向您自己的服务端请求签名
+ */
+ QCloudHttpRequest httpRequest = new QCloudHttpRequest.Builder()
+ .method("PUT")
+ .url(requestSignUrl)
+ .body(RequestBodySerializer.string(null, signFieldJson))
+ .build();
+
+ String response = null;
+ try {
+ response = QCloudHttpClient.getDefault().resolveRequest(httpRequest).executeNow().content();
+ } catch (QCloudServiceException e) {
+ e.printStackTrace();
+ throw new QCloudClientException(e);
+ }
+
+ String sign = null;
+ try {
+ sign = getSignFromResponse(response);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ throw new QCloudClientException("parse response failed");
+ }
+
+ /**
+ * 给请求设置 Authorization Header
+ */
+ if (TextUtils.isEmpty(sign)) {
+ throw new QCloudClientException("get sign from server failed!!!");
+ }
+ request.addHeader("Authorization", sign);
+ }
+
+ private Map getHeaderMap(Map> multiValuesHeaders) {
+
+ Map header = new HashMap<>();
+ for (Map.Entry> entry : multiValuesHeaders.entrySet()) {
+
+ if (entry.getValue().size() > 0) {
+ header.put(entry.getKey(), entry.getValue().get(0));
+ }
+ }
+
+ return header;
+ }
+
+ private Map getQueryMap(String query)
+ {
+
+ Map map = new HashMap<>();
+ if (TextUtils.isEmpty(query)) {
+ return map;
+ }
+
+ String[] params = query.split("&");
+ for (String param : params)
+ {
+ String[] paramKeyValue = param.split("=");
+ if (paramKeyValue.length >= 2) {
+ String name = paramKeyValue[0];
+ String value = paramKeyValue[1];
+ map.put(name, value);
+ }
+ }
+ return map;
+ }
+
+ /**
+ * 将签名需要的字段转化为 json 字符串
+ *
+ * @return
+ */
+ private String signField2Json(String method, String schema, String host, String path,
+ Map headers, Map params) throws JSONException {
+
+ JSONObject signJson = new JSONObject();
+ signJson.put("method", method);
+ signJson.put("schema", schema);
+ signJson.put("host", host);
+ signJson.put("path", path);
+
+ JSONObject headersJSON = new JSONObject(headers);
+ signJson.put("headers", headersJSON);
+
+ JSONObject paramsJSON = new JSONObject(params);
+ signJson.put("params", paramsJSON);
+
+ return signJson.toString();
+ }
+
+ private String getSignFromResponse(String response) throws JSONException {
+
+ JSONObject jsonObject = new JSONObject(response);
+ return jsonObject.optString("sign");
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/utils/cos/RemoteStorage.java b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/cos/RemoteStorage.java
new file mode 100644
index 00000000..943edac6
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/cos/RemoteStorage.java
@@ -0,0 +1,132 @@
+package com.xscm.moduleutil.utils.cos;
+
+import android.content.Context;
+import com.tencent.cos.xml.CosXmlService;
+import com.tencent.cos.xml.CosXmlServiceConfig;
+import com.tencent.cos.xml.exception.CosXmlClientException;
+import com.tencent.cos.xml.exception.CosXmlServiceException;
+import com.tencent.cos.xml.listener.CosXmlProgressListener;
+import com.tencent.cos.xml.model.bucket.PutBucketRequest;
+import com.tencent.cos.xml.model.bucket.PutBucketResult;
+import com.tencent.cos.xml.model.object.PutObjectRequest;
+import com.tencent.cos.xml.model.object.PutObjectResult;
+import com.tencent.cos.xml.model.service.GetServiceRequest;
+import com.tencent.cos.xml.model.service.GetServiceResult;
+import com.tencent.cos.xml.transfer.UploadService;
+import com.tencent.qcloud.core.auth.QCloudSigner;
+import com.xscm.moduleutil.base.CommonAppContext;
+import com.xscm.moduleutil.utils.SpUtil;
+import com.xscm.moduleutil.widget.Constants;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * com.xscm.moduleutil.utils.cos
+ * qx
+ * 2025/10/23
+ */
+public class RemoteStorage {
+
+ private int MULTIPART_UPLOAD_SIZE = 1024 * 2;
+
+ private CosXmlService cosXmlService;
+ private boolean isHttps;
+ private String appid;
+ private String region;
+
+
+ public RemoteStorage(Context context, String appid, String region, String hostFormat) {
+
+ isHttps = false;
+ this.appid = appid;
+ this.region = region;
+
+ /**
+ * 初始化配置
+ */
+ CosXmlServiceConfig cosXmlServiceConfig = new CosXmlServiceConfig.Builder()
+ .isHttps(isHttps)
+ .setAppidAndRegion(appid, region) // appid 和 region 均可以为空
+ .setDebuggable(true)
+ .setBucketInPath(false) // 将 Bucket 放在 URL 的 Path 中
+ .setHostFormat(hostFormat) // 私有云需要设置主域名
+ .builder();
+
+ /**
+ * 私有云暂时不支持临时密钥进行签名,如果直接在客户端直接使用永久密钥会有安全性问题,因此这里采用
+ * 服务端直接下发签名的方式来进行鉴权。
+ */
+ URL url = null; // 您的服务端签名的 URL 地址
+ try {
+ url = new URL(CommonAppContext.getInstance().getCurrentEnvironment().getServerUrl()+ Constants.GET_TEMP_KEY+"?"+ SpUtil.getToken());
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ }
+ QCloudSigner cosSigner = new RemoteCOSSigner(url);
+
+ cosXmlService = new CosXmlService(context, cosXmlServiceConfig, cosSigner);
+ }
+
+
+ /**
+ * 上传文件
+ *
+ * @param bucketName bucket 名称
+ * @param cosPath 上传到 COS 的路径
+ * @param localPath 需要上传文件的本地路径
+ * @param progressListener 进度监听器
+ *
+ * @return 本次上传的 id,可以通过这个 id 来取消上传
+ */
+ public UploadService.UploadServiceResult uploadFile(String bucketName, String cosPath, String localPath, CosXmlProgressListener progressListener)
+ throws CosXmlServiceException, CosXmlClientException {
+
+ UploadService.ResumeData resumeData = new UploadService.ResumeData();
+ resumeData.sliceSize = MULTIPART_UPLOAD_SIZE; // 分片上传的大小
+ resumeData.cosPath = cosPath;
+ resumeData.bucket = bucketName;
+ resumeData.srcPath = localPath;
+
+ /**
+ * 上传服务类,这个类封装了 {@link CosXmlService} 几个上传相关的接口,通过使用该接口,您可以更加方便的上传文件。
+ * 注意,每次上传都要初始化一个新的 {@link CosXmlService} 对象。
+ */
+ final UploadService uploadService = new UploadService(cosXmlService, resumeData);
+ uploadService.setProgressListener(progressListener);
+ return uploadService.upload();
+ }
+
+ public PutObjectResult simpleUploadFile(String bucketName, String cosPath, String localPath, CosXmlProgressListener progressListener)
+ throws CosXmlServiceException, CosXmlClientException {
+
+ PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, cosPath, localPath);
+ putObjectRequest.setProgressListener(progressListener);
+
+ return cosXmlService.putObject(putObjectRequest);
+ }
+ /**
+ * 列出所有的 bucket
+ */
+ public GetServiceResult getService() throws CosXmlServiceException, CosXmlClientException {
+
+ GetServiceRequest getServiceRequest = new GetServiceRequest();
+ getServiceRequest.setRequestHeaders("x-cos-meta-bucket", "BucketName", false);
+
+ return cosXmlService.getService(getServiceRequest);
+ }
+
+
+ /**
+ * 创建 bucket
+ *
+ * @param bucketName bucket 名称
+ */
+ public PutBucketResult putBucket(String bucketName) throws CosXmlServiceException, CosXmlClientException {
+
+ PutBucketRequest putBucketRequest = new PutBucketRequest(bucketName);
+
+ return cosXmlService.putBucket(putBucketRequest);
+ }
+
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/utils/cos/TaskFactory.java b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/cos/TaskFactory.java
new file mode 100644
index 00000000..1ffec683
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/cos/TaskFactory.java
@@ -0,0 +1,239 @@
+package com.xscm.moduleutil.utils.cos;
+
+import android.content.Context;
+import android.os.AsyncTask;
+import android.widget.Toast;
+import com.tencent.cos.xml.exception.CosXmlClientException;
+import com.tencent.cos.xml.exception.CosXmlServiceException;
+import com.tencent.cos.xml.listener.CosXmlProgressListener;
+import com.tencent.cos.xml.model.bucket.PutBucketResult;
+import com.tencent.cos.xml.model.object.PutObjectResult;
+import com.tencent.cos.xml.model.service.GetServiceResult;
+import com.tencent.cos.xml.model.tag.ListAllMyBuckets;
+import com.tencent.cos.xml.transfer.UploadService;
+import com.tencent.qcloud.core.logger.QCloudLogger;
+
+import java.util.List;
+
+/**
+ * com.xscm.moduleutil.utils.cos
+ * qx
+ * 2025/10/23
+ */
+public class TaskFactory {
+
+ private static TaskFactory instance;
+
+ private TaskFactory() {}
+
+ public static TaskFactory getInstance() {
+
+ if (instance == null) {
+ synchronized (TaskFactory.class) {
+ if (instance == null) {
+ instance = new TaskFactory();
+ }
+ }
+ }
+
+ return instance;
+ }
+
+ public GetServiceTask createGetServiceTask(Context context, RemoteStorage remoteStorage) {
+
+ return new GetServiceTask(context, remoteStorage);
+ }
+
+ public PutBucketTask createPutBucketTask(Context context, RemoteStorage remoteStorage, String bucketName) {
+
+ return new PutBucketTask(context, remoteStorage, bucketName);
+ }
+
+ public PutObjectTask createPutObjectTask(Context context, RemoteStorage remoteStorage, String bucket,
+ String srcPath, String dstPath) {
+
+ return new PutObjectTask(context, remoteStorage, bucket, srcPath, dstPath);
+ }
+
+
+
+ public SimplePutObjectTask createSimplePutObjectTask(Context context, RemoteStorage remoteStorage, String bucket,
+ String srcPath, String dstPath) {
+
+ return new SimplePutObjectTask(context, remoteStorage, bucket, srcPath, dstPath);
+ }
+
+
+ public class GetServiceTask extends AsyncTask {
+
+ Context context;
+ RemoteStorage remoteStorage ;
+
+ public GetServiceTask(Context context, RemoteStorage remoteStorage) {
+ this.remoteStorage = remoteStorage;
+ this.context = context;
+ }
+
+ @Override
+ protected GetServiceResult doInBackground(Void ... voids) {
+ try {
+ return remoteStorage.getService();
+ } catch (CosXmlServiceException e) {
+ e.printStackTrace();
+ } catch (CosXmlClientException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ protected void onPostExecute(GetServiceResult getServiceResult) {
+
+ if (getServiceResult != null && getServiceResult.listAllMyBuckets != null) {
+ List buckets = getServiceResult.listAllMyBuckets.buckets;
+ Toast.makeText(context, buckets.toString(), Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(context, "GetService failed", Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+
+ public class PutBucketTask extends AsyncTask {
+
+ RemoteStorage remoteStorage ;
+ String bucketName;
+ Context context;
+
+ public PutBucketTask(Context context, RemoteStorage remoteStorage, String bucketName) {
+ this.context = context;
+ this.remoteStorage = remoteStorage;
+ this.bucketName = bucketName;
+ }
+
+ @Override
+ protected PutBucketResult doInBackground(Void ... voids) {
+ try {
+ return remoteStorage.putBucket(bucketName);
+ } catch (CosXmlServiceException e) {
+ e.printStackTrace();
+ } catch (CosXmlClientException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(PutBucketResult putBucketResult) {
+
+ if (putBucketResult != null) {
+ Toast.makeText(context, putBucketResult.printResult(), Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+
+ static class PutObjectTask extends AsyncTask {
+
+ Context context;
+ RemoteStorage remoteStorage;
+ String bucket;
+ String srcPath;
+ String dstPath;
+
+ public PutObjectTask(Context context, RemoteStorage remoteStorage, String bucket, String srcPath, String dstPath) {
+
+ this.context = context;
+ this.remoteStorage = remoteStorage;
+ this.bucket = bucket;
+ this.srcPath = srcPath;
+ this.dstPath = dstPath;
+ }
+
+ @Override
+ protected UploadService.UploadServiceResult doInBackground(Void... voids) {
+ try {
+ return remoteStorage.uploadFile(bucket, dstPath, srcPath, new CosXmlProgressListener() {
+ @Override
+ public void onProgress(long progress, long total) {
+ publishProgress((int) ((progress/ (float) total) * 100));
+ }
+ });
+ } catch (CosXmlServiceException e) {
+ e.printStackTrace();
+ } catch (CosXmlClientException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+
+ @Override
+ protected void onProgressUpdate(Integer... values) {
+
+ QCloudLogger.i("upload", "progress " + values[0]);
+ }
+
+
+ @Override
+ protected void onPostExecute(UploadService.UploadServiceResult uploadServiceResult) {
+
+ if (uploadServiceResult != null) {
+ Toast.makeText(context, uploadServiceResult.printResult(), Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+
+ static class SimplePutObjectTask extends AsyncTask {
+
+ Context context;
+ RemoteStorage remoteStorage;
+ String bucket;
+ String srcPath;
+ String dstPath;
+
+ public SimplePutObjectTask(Context context, RemoteStorage remoteStorage, String bucket, String srcPath, String dstPath) {
+
+ this.context = context;
+ this.remoteStorage = remoteStorage;
+ this.bucket = bucket;
+ this.srcPath = srcPath;
+ this.dstPath = dstPath;
+ }
+
+ @Override
+ protected PutObjectResult doInBackground(Void... voids) {
+ try {
+ return remoteStorage.simpleUploadFile(bucket, dstPath, srcPath, new CosXmlProgressListener() {
+ @Override
+ public void onProgress(long progress, long total) {
+ publishProgress((int) ((progress/ (float) total) * 100));
+ }
+ });
+ } catch (CosXmlServiceException e) {
+ e.printStackTrace();
+ } catch (CosXmlClientException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+
+ @Override
+ protected void onProgressUpdate(Integer... values) {
+
+ QCloudLogger.i("upload", "progress " + values[0]);
+ }
+
+
+ @Override
+ protected void onPostExecute(PutObjectResult putObjectResult) {
+
+ if (putObjectResult != null) {
+ Toast.makeText(context, putObjectResult.printResult(), Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+}
+
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/utils/cos/TempKeyBean.kt b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/cos/TempKeyBean.kt
new file mode 100644
index 00000000..8ac6cd6f
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/cos/TempKeyBean.kt
@@ -0,0 +1,21 @@
+package com.xscm.moduleutil.utils.cos
+
+/**
+ *com.xscm.moduleutil.utils.cos
+ *qx
+ *2025/10/23
+ *
+ */
+data class TempKeyBean (
+ var startTime:Long = 0,
+ var expiredTime:Long = 0,
+ var region:String = "",
+ var bucket:String = "",
+ var credentials : Credentials = Credentials(),
+)
+
+data class Credentials(
+ var sessionToken : String="",
+ var tmpSecretId : String="",
+ var tmpSecretKey : String=""
+)
\ No newline at end of file
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/utils/logger/LogInterceptor.java b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/logger/LogInterceptor.java
new file mode 100644
index 00000000..6c466c2f
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/logger/LogInterceptor.java
@@ -0,0 +1,276 @@
+package com.xscm.moduleutil.utils.logger;
+
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.TimeUnit;
+
+import okhttp3.Interceptor;
+import okhttp3.MediaType;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+import okhttp3.internal.http.HttpHeaders;
+import okio.Buffer;
+import okio.BufferedSource;
+
+/**
+ * OkHttp 日志拦截器,用于打印请求和响应详情
+ */
+public class LogInterceptor implements Interceptor {
+ private static final String TAG = "NetworkLog";
+ private static final Charset UTF8 = StandardCharsets.UTF_8;
+
+ // 日志开关(可根据debug/release环境动态设置)
+ private boolean isLogEnabled = true;
+ // 是否打印请求体
+ private boolean logRequestBody = true;
+ // 是否打印响应体
+
+ private boolean logResponseBody = true;
+ // 最大日志长度(避免过大的响应体导致日志刷屏)
+ private int maxLogLength = 2048;
+
+ public LogInterceptor() {
+ }
+
+ // 配置方法
+ public LogInterceptor setLogEnabled(boolean enabled) {
+ isLogEnabled = enabled;
+ return this;
+ }
+
+ public LogInterceptor setLogRequestBody(boolean logRequestBody) {
+ this.logRequestBody = logRequestBody;
+ return this;
+ }
+
+ public LogInterceptor setLogResponseBody(boolean logResponseBody) {
+ this.logResponseBody = logResponseBody;
+ return this;
+ }
+
+ public LogInterceptor setMaxLogLength(int maxLogLength) {
+ this.maxLogLength = maxLogLength;
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Response intercept(@NonNull Chain chain) throws IOException {
+ if (!isLogEnabled) {
+ return chain.proceed(chain.request());
+ }
+
+ Request request = chain.request();
+ // 打印请求日志
+ logRequest(request);
+
+ // 记录请求开始时间,用于计算耗时
+ long startNs = System.nanoTime();
+ Response response;
+ try {
+ response = chain.proceed(request);
+ } catch (Exception e) {
+ // 打印请求异常
+ Log.e(TAG, "请求失败: " + e.getMessage());
+ throw e;
+ }
+ // 计算请求耗时
+ long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
+
+ // 打印响应日志
+ logResponse(response, tookMs);
+
+ return response;
+ }
+
+ /**
+ * 打印请求日志
+ */
+ private void logRequest(Request request) {
+ try {
+ StringBuilder log = new StringBuilder();
+ log.append("\n==================== 请求开始 ====================\n");
+
+ // 请求行: 方法 + URL
+ log.append(String.format("方法: %s URL: %s\n", request.method(), request.url()));
+
+ // 请求头
+ log.append("请求头:\n");
+ for (String name : request.headers().names()) {
+ // 脱敏敏感头信息(如Authorization、Cookie等)
+ String value = isSensitiveHeader(name) ? "***" : request.headers().get(name);
+ log.append(String.format(" %s: %s\n", name, value));
+ }
+
+ // 请求体
+ if (logRequestBody && request.body() != null) {
+ RequestBody requestBody = request.body();
+ if (requestBody.contentLength() > 0) {
+ log.append("请求体:\n");
+
+ // 复制请求体(避免原请求体被消耗)
+ Buffer buffer = new Buffer();
+ requestBody.writeTo(buffer);
+ Charset charset = UTF8;
+ MediaType contentType = requestBody.contentType();
+ if (contentType != null) {
+ charset = contentType.charset(UTF8);
+ }
+
+ // 读取请求体内容
+ String body = buffer.readString(charset);
+ // 格式化JSON(如果是JSON类型)
+ if (isJson(contentType)) {
+ body = formatJson(body);
+ }
+ // 截断过长的日志
+ log.append(truncateLog(body)).append("\n");
+ }
+ }
+
+ log.append("==================== 请求结束 ====================\n");
+ Log.d(TAG, log.toString());
+ } catch (Exception e) {
+ Log.e(TAG, "打印请求日志失败: " + e.getMessage());
+ }
+ }
+
+ /**
+ * 打印响应日志
+ */
+ private void logResponse(Response response, long tookMs) {
+ try {
+ StringBuilder log = new StringBuilder();
+ log.append("\n==================== 响应开始 ====================\n");
+
+ // 响应行: 状态码 + 消息 + 耗时
+ log.append(String.format("状态码: %d 消息: %s 耗时: %dms\n",
+ response.code(), response.message(), tookMs));
+
+ // 响应头
+ log.append("响应头:\n");
+ for (String name : response.headers().names()) {
+ log.append(String.format(" %s: %s\n", name, response.headers().get(name)));
+ }
+
+ // 响应体
+ if (logResponseBody && HttpHeaders.hasBody(response)) {
+ ResponseBody responseBody = response.body();
+ if (responseBody != null) {
+ BufferedSource source = responseBody.source();
+ source.request(Long.MAX_VALUE); // 读取整个响应体
+ Buffer buffer = source.buffer();
+
+ Charset charset = UTF8;
+ MediaType contentType = responseBody.contentType();
+ if (contentType != null) {
+ charset = contentType.charset(UTF8);
+ }
+
+ // 读取响应体内容
+ String body = buffer.clone().readString(charset);
+ // 格式化JSON
+ if (isJson(contentType)) {
+ body = formatJson(body);
+ }
+ // 截断过长的日志
+ log.append("响应体:\n").append(truncateLog(body)).append("\n");
+ }
+ }
+
+ log.append("==================== 响应结束 ====================\n");
+ Log.d(TAG, log.toString());
+ } catch (Exception e) {
+ Log.e(TAG, "打印响应日志失败: " + e.getMessage());
+ }
+ }
+
+ /**
+ * 判断是否为JSON类型
+ */
+ private boolean isJson(MediaType mediaType) {
+ if (mediaType == null) return false;
+ return mediaType.type().equals("application") &&
+ mediaType.subtype().equals("json");
+ }
+
+ /**
+ * 格式化JSON字符串(增强可读性)
+ */
+ private String formatJson(String json) {
+ try {
+ // 简单格式化(可根据需要使用更复杂的JSON格式化库)
+ StringBuilder formatted = new StringBuilder();
+ int indent = 0;
+ boolean inQuotes = false;
+ char lastChar = ' ';
+
+ for (char c : json.toCharArray()) {
+ if (c == '"' && lastChar != '\\') {
+ inQuotes = !inQuotes;
+ }
+
+ if (!inQuotes) {
+ switch (c) {
+ case '{':
+ case '[':
+ formatted.append(c).append("\n");
+ indent += 4;
+ formatted.append(" ".repeat(indent));
+ break;
+ case '}':
+ case ']':
+ formatted.append("\n");
+ indent -= 4;
+ formatted.append(" ".repeat(indent)).append(c);
+ break;
+ case ',':
+ formatted.append(c).append("\n").append(" ".repeat(indent));
+ break;
+ case ':':
+ formatted.append(" : ");
+ break;
+ default:
+ formatted.append(c);
+ break;
+ }
+ } else {
+ formatted.append(c);
+ }
+ lastChar = c;
+ }
+ return formatted.toString();
+ } catch (Exception e) {
+ // 格式化失败时返回原始字符串
+ return json;
+ }
+ }
+
+ /**
+ * 截断过长的日志
+ */
+ private String truncateLog(String log) {
+ if (log.length() <= maxLogLength) {
+ return log;
+ }
+ return log.substring(0, maxLogLength) + "\n...[日志过长,已截断]...";
+ }
+
+ /**
+ * 判断是否为敏感头信息(需要脱敏)
+ */
+ private boolean isSensitiveHeader(String headerName) {
+ String lowerHeader = headerName.toLowerCase();
+ return lowerHeader.contains("authorization") ||
+ lowerHeader.contains("cookie") ||
+ lowerHeader.contains("token") ||
+ lowerHeader.contains("secret");
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/utils/roomview/GiftDisplayManager.java b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/roomview/GiftDisplayManager.java
new file mode 100644
index 00000000..996cbba6
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/roomview/GiftDisplayManager.java
@@ -0,0 +1,233 @@
+package com.xscm.moduleutil.utils.roomview;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import com.xscm.moduleutil.bean.GiftBean;
+import com.xscm.moduleutil.bean.RoonGiftModel;
+
+import java.lang.ref.WeakReference;
+import java.util.*;
+
+public class GiftDisplayManager {
+ private static GiftDisplayManager instance;
+
+ private WeakReference containerRef;
+ private List displayViews;
+ private Queue giftQueue;
+ private Map accumulatedGifts;
+ private boolean isProcessingQueue = false;
+
+ private Handler mainHandler = new Handler(Looper.getMainLooper());
+
+ public static GiftDisplayManager getInstance() {
+ if (instance == null) {
+ synchronized (GiftDisplayManager.class) {
+ if (instance == null) {
+ instance = new GiftDisplayManager();
+ }
+ }
+ }
+ return instance;
+ }
+
+ private GiftDisplayManager() {
+ displayViews = new ArrayList<>();
+ giftQueue = new LinkedList<>();
+ accumulatedGifts = new HashMap<>();
+ }
+
+ public void setupDisplayView(ViewGroup container) {
+ this.containerRef = new WeakReference<>(container);
+ createDisplayViews();
+ }
+
+ private void createDisplayViews() {
+ if (displayViews.size() > 0 || containerRef == null || containerRef.get() == null) {
+ return;
+ }
+
+ ViewGroup container = containerRef.get();
+ int viewHeight = dpToPx(40);
+ int spacing = dpToPx(10);
+ int topMargin = dpToPx(100);
+ int width = dpToPx(270);
+
+ for (int i = 0; i < 3; i++) {
+ int y = topMargin + (viewHeight + spacing) * i;
+
+ GiftDisplayView displayView = new GiftDisplayView(container.getContext());
+ FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(width, viewHeight);
+ params.setMargins(0, y, 0, 0);
+ displayView.setLayoutParams(params);
+ displayView.setTag(1000 + i);
+
+ final int finalI = i;
+ displayView.setGiftAnimationListener(view -> {
+ Log.d("GiftDisplayManager", "Gift animation ended on view: " + finalI);
+ onGiftAnimationEnd(view);
+ });
+
+ container.addView(displayView);
+ displayViews.add(displayView);
+
+ Log.d("GiftDisplayManager", "Created display view " + i);
+ }
+ }
+
+ public void receiveGift(GiftBean gift) {
+ if (gift == null) return;
+
+ Log.d("GiftDisplayManager", "Received gift: " + gift.getSenderName() +
+ " - " + gift.getGift_name() + " x" + gift.getNumber());
+
+ mainHandler.post(() -> internalReceiveGift(gift));
+ }
+
+ private void internalReceiveGift(GiftBean gift) {
+ // 查找正在显示的同类型礼物
+ GiftDisplayView displayingView = findDisplayingViewForGift(gift);
+
+ if (displayingView != null) {
+ // 找到正在显示的视图,直接累加
+ String key = gift.getGiftKey();
+ GiftBean accumulatedGift = accumulatedGifts.get(key);
+ if (accumulatedGift != null) {
+ accumulatedGift.setNumber(accumulatedGift.getNumber() + gift.getNumber());
+ displayingView.updateGiftCount(accumulatedGift.getNumber());
+ Log.d("GiftDisplayManager", "Gift accumulated: " + gift.getGift_name() +
+ " x" + accumulatedGift.getNumber());
+ }
+ } else {
+ // 新礼物,检查是否可以立即显示
+ GiftDisplayView availableView = findAvailableDisplayView();
+ if (availableView != null) {
+ // 有可用视图,立即显示
+ String key = gift.getGiftKey();
+ accumulatedGifts.put(key, gift.clone());
+ availableView.showGift(gift);
+ Log.d("GiftDisplayManager", "Immediately display gift on view: " + availableView.getTag());
+ } else {
+ // 没有可用视图,加入队列
+ giftQueue.offer(gift);
+ Log.d("GiftDisplayManager", "Added to queue, current queue size: " + giftQueue.size());
+ }
+ }
+
+ // 处理队列
+ processGiftQueue();
+ }
+
+ private GiftDisplayView findDisplayingViewForGift(GiftBean gift) {
+ for (GiftDisplayView view : displayViews) {
+ if (view.isAnimating() && view.getCurrentGift() != null &&
+ view.getCurrentGift().isSameGiftFromSameSender(gift)) {
+ return view;
+ }
+ }
+ return null;
+ }
+
+ private GiftDisplayView findAvailableDisplayView() {
+ for (GiftDisplayView view : displayViews) {
+ if (!view.isAnimating()) {
+ return view;
+ }
+ }
+ return null;
+ }
+
+ private void processGiftQueue() {
+ if (isProcessingQueue) {
+ return;
+ }
+
+ isProcessingQueue = true;
+
+ // 循环处理队列直到队列为空或没有可用视图
+ while (!giftQueue.isEmpty()) {
+ GiftDisplayView availableView = findAvailableDisplayView();
+ if (availableView == null) {
+ break;
+ }
+
+ GiftBean gift = giftQueue.poll();
+ if (gift == null) continue;
+
+ // 检查是否已经有同类型礼物在显示
+ GiftDisplayView displayingView = findDisplayingViewForGift(gift);
+ if (displayingView == null) {
+ String key = gift.getGiftKey();
+ accumulatedGifts.put(key, gift.clone());
+ availableView.showGift(gift);
+ Log.d("GiftDisplayManager", "Display gift from queue: " + gift.getGift_name());
+ } else {
+ // 如果已经在显示,累加到现有视图
+ String key = gift.getGiftKey();
+ GiftBean accumulatedGift = accumulatedGifts.get(key);
+ if (accumulatedGift != null) {
+ accumulatedGift.setNumber(accumulatedGift.getNumber() + gift.getNumber());
+ displayingView.updateGiftCount(accumulatedGift.getNumber());
+ Log.d("GiftDisplayManager", "Queue gift accumulated to existing: " +
+ gift.getNickname() + " x" + accumulatedGift.getNumber());
+ }
+ }
+ }
+
+ isProcessingQueue = false;
+
+ // 打印队列状态
+ if (!giftQueue.isEmpty()) {
+ Log.d("GiftDisplayManager", "Still " + giftQueue.size() + " gifts waiting in queue");
+ }
+ }
+
+ private void onGiftAnimationEnd(GiftDisplayView view) {
+ Log.d("GiftDisplayManager", "Gift animation end on view: " + view.getTag());
+
+ // 从累加记录中移除
+ if (view.getCurrentGift() != null) {
+ String key = view.getCurrentGift().getGiftKey();
+ accumulatedGifts.remove(key);
+ Log.d("GiftDisplayManager", "Removed accumulated record: " + key);
+ }
+
+ // 延迟一下再处理队列,确保视图状态完全重置
+ mainHandler.postDelayed(this::processGiftQueue, 100);
+ }
+
+ public void clearAll() {
+ Log.d("GiftDisplayManager", "Clear all gifts and queue");
+
+ for (GiftDisplayView view : displayViews) {
+ view.finishAnimationImmediately();
+ }
+ containerRef.clear();
+ displayViews.clear();
+ giftQueue.clear();
+ accumulatedGifts.clear();
+ isProcessingQueue = false;
+ }
+
+ // 调试方法
+ public void printDebugInfo() {
+ Log.d("GiftDisplayManager", "=== Gift Display Manager Status ===");
+ Log.d("GiftDisplayManager", "Queue size: " + giftQueue.size());
+ Log.d("GiftDisplayManager", "Accumulated records: " + accumulatedGifts.size());
+
+ for (int i = 0; i < displayViews.size(); i++) {
+ GiftDisplayView view = displayViews.get(i);
+ Log.d("GiftDisplayManager", "View " + i + ": Animating=" + view.isAnimating() +
+ ", Gift=" + (view.getCurrentGift() != null ? view.getCurrentGift().getGift_name() : "None"));
+ }
+ Log.d("GiftDisplayManager", "===================================");
+ }
+
+ private int dpToPx(int dp) {
+ if (containerRef == null || containerRef.get() == null) return dp;
+ float density = containerRef.get().getResources().getDisplayMetrics().density;
+ return Math.round(dp * density);
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/utils/roomview/GiftDisplayView.java b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/roomview/GiftDisplayView.java
new file mode 100644
index 00000000..37d8baff
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/roomview/GiftDisplayView.java
@@ -0,0 +1,263 @@
+package com.xscm.moduleutil.utils.roomview;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.graphics.Color;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import com.xscm.moduleutil.R;
+import com.xscm.moduleutil.bean.GiftBean;
+import com.xscm.moduleutil.utils.ImageUtils;
+
+import java.util.Random;
+
+public class GiftDisplayView extends FrameLayout {
+ private ImageView avatarImageView;
+ private TextView senderTextView;
+ private TextView giftTextView;
+ private TextView countTextView;
+ private ImageView giftImageView;
+ private LinearLayout ll;
+
+ private GiftBean currentGift;
+ private boolean isAnimating = false;
+ private GiftAnimationListener listener;
+ private Handler handler = new Handler();
+ private Runnable hideRunnable;
+
+ public interface GiftAnimationListener {
+ void onGiftAnimationEnd(GiftDisplayView view);
+ }
+
+ public GiftDisplayView(Context context) {
+ super(context);
+ initView();
+ }
+
+ public GiftDisplayView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initView();
+ }
+
+ private void initView() {
+ LayoutInflater.from(getContext()).inflate(R.layout.gift_display_layout, this, true);
+
+ avatarImageView = findViewById(R.id.iv_avatar);
+ senderTextView = findViewById(R.id.tv_sender);
+ giftTextView = findViewById(R.id.tv_gift);
+ countTextView = findViewById(R.id.tv_count);
+ giftImageView = findViewById(R.id.iv_gift);
+// ll = findViewById(R.id.ll);
+
+// setBackgroundResource(R.drawable.gift_background);
+ setAlpha(0f);
+ }
+
+ public void showGift(GiftBean gift) {
+ if (isAnimating) {
+ Log.w("GiftDisplayView", "View is animating, cannot show new gift");
+ return;
+ }
+
+ this.currentGift = gift;
+ this.isAnimating = true;
+
+ Log.d("GiftDisplayView", "Start showing gift: " + gift.getGift_name());
+
+ // 更新UI
+ updateUIWithGift(gift);
+
+ // 重置位置
+ setTranslationX(-getWidth());
+ setAlpha(1f);
+
+ // 从左往右进入动画
+ animate()
+ .translationX(0)
+ .setDuration(500)
+ .setInterpolator(new AccelerateDecelerateInterpolator())
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ Log.d("GiftDisplayView", "Enter animation completed: " + gift.getGift_name());
+ startHideTimer();
+ }
+ })
+ .start();
+ }
+
+ private void updateUIWithGift(GiftBean gift) {
+ if (gift == null) return;
+
+ senderTextView.setText(gift.getNickname()!=null ? gift.getNickname() : "未知用户");
+ // 更新发送者名称
+// senderTextView.setText(gift.getSenderName() != null ? gift.getSenderName() : "未知用户");
+
+ // 更新礼物信息
+ giftTextView.setText("送给 "+(gift.getSenderName() != null ? gift.getSenderName() : "未知用户") + (gift.getGift_name() != null ? gift.getGift_name() : "礼物"));
+
+ // 更新礼物数量
+ countTextView.setText("x" + gift.getNumber());
+
+ // 加载头像图片(这里可以使用Glide、Picasso等图片加载库)
+ loadAvatarImage(gift.getUserAvatar());
+
+ // 加载礼物图片
+ loadGiftImage(gift.getBase_image());
+
+ Log.d("GiftDisplayView", "Update UI: " + gift.getSenderName() + " - " +
+ gift.getGift_name() + " x" + gift.getNumber());
+ }
+
+ private void loadAvatarImage(String avatarUrl) {
+ if (avatarUrl != null && !avatarUrl.isEmpty()) {
+ // 使用图片加载库,例如:
+ // Glide.with(getContext()).load(avatarUrl).into(avatarImageView);
+
+ // 临时用颜色代替
+// avatarImageView.setBackgroundColor(getRandomColor());
+ ImageUtils.loadHeadCC(avatarUrl, avatarImageView);
+ } else {
+ avatarImageView.setBackgroundColor(Color.LTGRAY);
+ }
+ }
+
+ private void loadGiftImage(String giftImageUrl) {
+ if (giftImageUrl != null && !giftImageUrl.isEmpty()) {
+ // 使用图片加载库
+ // Glide.with(getContext()).load(giftImageUrl).into(giftImageView);
+
+ // 临时用颜色代替
+// giftImageView.setBackgroundColor(getRandomColor());
+ ImageUtils.loadHeadCC(giftImageUrl, giftImageView);
+ } else {
+ giftImageView.setBackgroundColor(Color.parseColor("#FFA500"));
+ }
+ }
+
+ private int getRandomColor() {
+ Random random = new Random();
+ return Color.argb(255, random.nextInt(256), random.nextInt(256), random.nextInt(256));
+ }
+
+ public void updateGiftCount(int count) {
+ if (!isAnimating) {
+ Log.w("GiftDisplayView", "View is not animating, cannot update count");
+ return;
+ }
+
+ Log.d("GiftDisplayView", "Update gift count: " + count);
+
+ // 更新数量显示
+ countTextView.setText("x" + count);
+
+ // 数量更新动画
+ countTextView.animate()
+ .scaleX(1.5f)
+ .scaleY(1.5f)
+ .setDuration(200)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ countTextView.animate()
+ .scaleX(1f)
+ .scaleY(1f)
+ .setDuration(200)
+ .start();
+ }
+ })
+ .start();
+
+ // 重置计时器
+ resetHideTimer();
+ }
+
+ private void startHideTimer() {
+ // 移除之前的任务
+ if (hideRunnable != null) {
+ handler.removeCallbacks(hideRunnable);
+ }
+
+ hideRunnable = this::hideAnimation;
+ handler.postDelayed(hideRunnable, 3000);
+ }
+
+ private void resetHideTimer() {
+ startHideTimer();
+ }
+
+ private void hideAnimation() {
+ if (!isAnimating) {
+ return;
+ }
+
+ Log.d("GiftDisplayView", "Start hide animation: " + currentGift.getGift_name());
+
+ // 从右往左消失动画
+ animate()
+ .translationX(-getWidth())
+ .alpha(0f)
+ .setDuration(500)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ Log.d("GiftDisplayView", "Hide animation completed: " + currentGift.getGift_name());
+ isAnimating = false;
+
+ if (listener != null) {
+ listener.onGiftAnimationEnd(GiftDisplayView.this);
+ }
+
+ currentGift = null;
+ }
+ })
+ .start();
+ }
+
+ public void finishAnimationImmediately() {
+ Log.d("GiftDisplayView", "Finish animation immediately");
+
+ // 移除计时任务
+ if (hideRunnable != null) {
+ handler.removeCallbacks(hideRunnable);
+ hideRunnable = null;
+ }
+
+ // 清除动画
+ clearAnimation();
+ animate().cancel();
+
+ isAnimating = false;
+ currentGift = null;
+ setAlpha(0f);
+ setTranslationX(-getWidth());
+ }
+
+ public boolean isAnimating() {
+ return isAnimating;
+ }
+
+ public GiftBean getCurrentGift() {
+ return currentGift;
+ }
+
+ public void setGiftAnimationListener(GiftAnimationListener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (hideRunnable != null) {
+ handler.removeCallbacks(hideRunnable);
+ }
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/view/CustomDialogView.java b/moduleUtil/src/main/java/com/xscm/moduleutil/view/CustomDialogView.java
new file mode 100644
index 00000000..a1f1abac
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/view/CustomDialogView.java
@@ -0,0 +1,44 @@
+package com.xscm.moduleutil.view;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.core.content.ContextCompat;
+import com.xscm.moduleutil.R;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * 这是抢红包的自定义view
+ */
+public class CustomDialogView extends ConstraintLayout {
+ public CustomDialogView(@NonNull @NotNull Context context) {
+ super(context);
+ init();
+ }
+
+ public CustomDialogView(@NonNull @NotNull Context context, @Nullable @org.jetbrains.annotations.Nullable AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public CustomDialogView(@NonNull @NotNull Context context, @Nullable @org.jetbrains.annotations.Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+ }
+
+ public CustomDialogView(@NonNull @NotNull Context context, @Nullable @org.jetbrains.annotations.Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init();
+ }
+
+ private void init() {
+ // 初始化视图
+ // 设置背景色
+ setBackground(ContextCompat.getDrawable(getContext(), R.drawable.bg_red_16_envel));
+
+
+ }
+
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/view/QXMeetGiftView.java b/moduleUtil/src/main/java/com/xscm/moduleutil/view/QXMeetGiftView.java
new file mode 100644
index 00000000..74df3d21
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/view/QXMeetGiftView.java
@@ -0,0 +1,262 @@
+package com.xscm.moduleutil.view;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.RotateAnimation;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.bumptech.glide.Glide;
+import com.xscm.moduleutil.R;
+import com.xscm.moduleutil.bean.GiftBean;
+import com.xscm.moduleutil.bean.blindboxwheel.BlindBoxBean;
+
+public class QXMeetGiftView extends RelativeLayout {
+
+ private TextView giftNameLabel;
+ private ImageView giftPriceBgView;
+ private Button giftCoin;
+ private ImageView bgImageView;
+ private ImageView giftBgImageView;
+ private ImageView giftImageView;
+
+ private boolean isLockGift;
+ private Object model; // 这里用 Object 代替 QXDrawGiftModel
+
+ public QXMeetGiftView(Context context) {
+ super(context);
+ initSubviews(context);
+ }
+
+ public QXMeetGiftView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ initSubviews(context);
+ }
+
+ public QXMeetGiftView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initSubviews(context);
+ }
+
+ private void initSubviews(Context context) {
+ // 创建背景图片视图
+ bgImageView = new ImageView(context);
+ bgImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
+ bgImageView.setImageResource(R.drawable.ac_left_gift_bg);
+ LayoutParams bgParams = new LayoutParams(
+ LayoutParams.MATCH_PARENT,
+ LayoutParams.MATCH_PARENT
+ );
+ addView(bgImageView, bgParams);
+
+ // 创建礼物图片视图(圆形)
+ giftImageView = new ImageView(context);
+ giftImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
+ LayoutParams giftImageParams = new LayoutParams(
+ dpToPx(48), // 固定宽度
+ dpToPx(62) // 固定高度
+ );
+ giftImageParams.addRule(CENTER_IN_PARENT);
+ giftImageParams.topMargin = dpToPx(10);
+ addView(giftImageView, giftImageParams);
+
+ // 创建礼物背景光效视图
+ giftBgImageView = new ImageView(context);
+ giftBgImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
+ giftBgImageView.setImageResource(R.drawable.ac_lock_gift_light_bg);
+ giftBgImageView.setVisibility(View.GONE); // 初始隐藏
+ LayoutParams giftBgParams = new LayoutParams(
+ LayoutParams.MATCH_PARENT,
+ LayoutParams.MATCH_PARENT
+ );
+ addView(giftBgImageView, giftBgParams);
+
+
+
+ // 创建价格背景视图
+ giftPriceBgView = new ImageView(context);
+ giftPriceBgView.setImageResource(R.drawable.ac_meet_gift_name_bg);
+ LayoutParams priceBgParams = new LayoutParams(
+ LayoutParams.MATCH_PARENT,
+ dpToPx(15)
+ );
+ priceBgParams.addRule(CENTER_HORIZONTAL);
+ priceBgParams.addRule(ALIGN_PARENT_BOTTOM);
+ priceBgParams.bottomMargin = dpToPx(10); // 在名称标签上方
+ addView(giftPriceBgView, priceBgParams);
+
+
+ // 创建金币按钮
+ giftCoin = new Button(context);
+ giftCoin.setTextColor(0xFFC7BF62); // 使用直接的颜色值
+ // 设置按钮图标
+ Drawable coinDrawable = getResources().getDrawable(R.mipmap.jinb);
+ coinDrawable.setBounds(0, 0, dpToPx(1), dpToPx(1));
+ giftCoin.setCompoundDrawables(coinDrawable, null, null, null);
+ giftCoin.setTextSize(10);
+ giftCoin.setBackgroundColor(0x00000000); // 透明背景
+ LayoutParams coinParams = new LayoutParams(
+ LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT
+ );
+ coinParams.addRule(CENTER_HORIZONTAL);
+ coinParams.addRule(ALIGN_PARENT_BOTTOM);
+ coinParams.bottomMargin = -dpToPx(2);
+ addView(giftCoin, coinParams);
+
+ // 创建礼物名称标签
+ giftNameLabel = new TextView(context);
+ giftNameLabel.setTextColor(0xFFFFFFFF);
+ giftNameLabel.setTextSize(12);
+ giftNameLabel.setGravity(android.view.Gravity.CENTER);
+ giftNameLabel.setSingleLine(true); // 设置为单行显示
+ giftNameLabel.setEllipsize(android.text.TextUtils.TruncateAt.END); // 超出部分用省略号表示
+ LayoutParams nameParams = new LayoutParams(
+ LayoutParams.MATCH_PARENT,
+ LayoutParams.WRAP_CONTENT
+ );
+ nameParams.addRule(CENTER_HORIZONTAL);
+ nameParams.addRule(ALIGN_PARENT_BOTTOM);
+ nameParams.bottomMargin = dpToPx(1);
+ addView(giftNameLabel, nameParams);
+
+ // 调整视图层级 - 确保正确的层级关系
+ // 按添加顺序已经确定层级,最晚添加的在最上层
+ }
+
+ public void setIsLockGift(boolean isLockGift) {
+ this.isLockGift = isLockGift;
+
+ // 设置背景图片
+ int bgResource = isLockGift ?
+ R.drawable.ac_lock_gift_bg : R.drawable.ac_left_gift_bg;
+ bgImageView.setImageResource(bgResource);
+
+ // 显示/隐藏光效背景
+ giftBgImageView.setVisibility(isLockGift ? View.VISIBLE : View.GONE);
+
+ if (isLockGift) {
+ // 重新设置礼物图片的约束
+ LayoutParams params = (LayoutParams) giftImageView.getLayoutParams();
+ params.width = dpToPx(36);
+ params.height = dpToPx(36);
+ params.addRule(CENTER_IN_PARENT);
+ params.setMargins(0, 0, 0, 0);
+ giftImageView.setLayoutParams(params);
+ } else {
+ // 恢复原始约束
+ LayoutParams params = (LayoutParams) giftImageView.getLayoutParams();
+ params.width = LayoutParams.MATCH_PARENT;
+ params.height = LayoutParams.WRAP_CONTENT;
+ params.addRule(CENTER_IN_PARENT);
+ params.setMargins(dpToPx(6), dpToPx(6), dpToPx(6), 0);
+ giftImageView.setLayoutParams(params);
+ }
+ }
+
+ public void setModel(BlindBoxBean.GiveGift model) {
+ this.model = model;
+ // 这里需要根据您的 QXDrawGiftModel 类来实现具体逻辑
+
+ if (model instanceof BlindBoxBean.GiveGift) {
+ BlindBoxBean.GiveGift giftModel = (BlindBoxBean.GiveGift) model;
+
+ // 使用图片加载库加载图片
+ Glide.with(getContext()).load(giftModel.getBase_image()).into(giftImageView);
+
+ giftNameLabel.setText(giftModel.getGift_name());
+ giftCoin.setText(giftModel.getGift_price());
+ }
+ }
+
+ public void startAnimation() {
+ // 礼物图片顺时针旋转动画
+ RotateAnimation rotateAnimation = new RotateAnimation(
+ 0, 360, // 从 0 度旋转到 360 度
+ Animation.RELATIVE_TO_SELF, 0.5f,
+ Animation.RELATIVE_TO_SELF, 0.5f
+ );
+ rotateAnimation.setDuration(3000); // 3 秒
+ rotateAnimation.setRepeatCount(Animation.INFINITE);
+ rotateAnimation.setRepeatMode(Animation.RESTART);
+ rotateAnimation.setInterpolator(getContext(), android.R.anim.linear_interpolator);
+ giftImageView.startAnimation(rotateAnimation);
+
+ // 光效背景逆时针旋转动画
+ RotateAnimation rotateAnimation1 = new RotateAnimation(
+ 0, -360, // 从 0 度旋转到 -360 度
+ Animation.RELATIVE_TO_SELF, 0.5f,
+ Animation.RELATIVE_TO_SELF, 0.5f
+ );
+ rotateAnimation1.setDuration(3000); // 3 秒
+ rotateAnimation1.setRepeatCount(Animation.INFINITE);
+ rotateAnimation1.setRepeatMode(Animation.RESTART);
+ rotateAnimation1.setInterpolator(getContext(), android.R.anim.linear_interpolator);
+ giftBgImageView.startAnimation(rotateAnimation1);
+ }
+
+ public void resetAnimation() {
+ giftImageView.clearAnimation();
+ giftBgImageView.clearAnimation();
+ }
+
+ public void stopAnimation() {
+ giftImageView.clearAnimation();
+ giftBgImageView.clearAnimation();
+ }
+
+ // 辅助方法:dp 转 px
+ private int dpToPx(int dp) {
+ float density = getResources().getDisplayMetrics().density;
+ return Math.round(dp * density);
+ }
+
+ // 缩放宽度方法(对应 ScaleWidth)
+ private int scaleWidth(int value) {
+ // 这里需要根据您的缩放逻辑实现
+ // 通常可以根据屏幕密度进行缩放
+ float scale = getResources().getDisplayMetrics().density;
+ return (int) (value * scale);
+ }
+
+ // Getter 方法
+ public TextView getGiftNameLabel() {
+ return giftNameLabel;
+ }
+
+ public ImageView getGiftPriceBgView() {
+ return giftPriceBgView;
+ }
+
+ public Button getGiftCoin() {
+ return giftCoin;
+ }
+
+ public ImageView getBgImageView() {
+ return bgImageView;
+ }
+
+ public ImageView getGiftBgImageView() {
+ return giftBgImageView;
+ }
+
+ public ImageView getGiftImageView() {
+ return giftImageView;
+ }
+
+ public boolean isLockGift() {
+ return isLockGift;
+ }
+
+ public Object getModel() {
+ return model;
+ }
+}
\ No newline at end of file
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/view/QXMeetUserView.java b/moduleUtil/src/main/java/com/xscm/moduleutil/view/QXMeetUserView.java
new file mode 100644
index 00000000..daa86c91
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/view/QXMeetUserView.java
@@ -0,0 +1,332 @@
+package com.xscm.moduleutil.view;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.GradientDrawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.xscm.moduleutil.R;
+import com.xscm.moduleutil.bean.blindboxwheel.BlindBoxBean;
+import com.xscm.moduleutil.utils.ImageUtils;
+import com.xscm.moduleutil.widget.GifAvatarOvalView;
+
+public class QXMeetUserView extends RelativeLayout {
+ private static final String TAG = "QXMeetUserView";
+
+ private GifAvatarOvalView headerImageView; // 头像
+ private ImageView dressImageView; // 装饰图
+ private TextView tagLabel; // 标签
+ private TextView nameLabel; // 名称
+
+ // 布局属性变量
+ private int avatarSize; // 头像大小
+ private int dressSize; // 装饰图大小
+ private float nameTextSize; // 名字字体大小
+ private int nameTextColor; // 名字颜色
+ private float tagTextSize; // 标签字体大小
+ private int tagTextColor; // 标签颜色
+ private int tagMargin; // 标签与头像的水平间距
+
+ // 布局常量(dp)
+ private static final int DEFAULT_AVATAR_SIZE = 60; // 默认头像大小
+ private static final int DEFAULT_DRESS_SIZE = 70; // 默认装饰图大小
+ private static final float DEFAULT_NAME_TEXT_SIZE = 12; // 默认名字字体大小
+ private static final int DEFAULT_NAME_TEXT_COLOR = 0xFFFFFFFF; // 默认名字颜色
+ private static final float DEFAULT_TAG_TEXT_SIZE = 12; // 默认标签字体大小
+ private static final int DEFAULT_TAG_TEXT_COLOR = 0xFFFFE554; // 默认标签颜色
+ private static final int DEFAULT_TAG_MARGIN = 0; // 默认标签间距
+ private static final int NAME_MARGIN_TOP = 3; // 名字与标签间距
+
+ public QXMeetUserView(Context context) {
+ super(context);
+ initAttrs(null);
+ initSubviews(context);
+ }
+
+ public QXMeetUserView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ initAttrs(attrs);
+ initSubviews(context);
+ }
+
+ public QXMeetUserView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initAttrs(attrs);
+ initSubviews(context);
+ }
+
+ // 初始化自定义属性
+ private void initAttrs(AttributeSet attrs) {
+ if (attrs == null) {
+ // 设置默认值
+ avatarSize = dpToPx(DEFAULT_AVATAR_SIZE);
+ dressSize = dpToPx(DEFAULT_DRESS_SIZE);
+ nameTextSize = DEFAULT_NAME_TEXT_SIZE;
+ nameTextColor = DEFAULT_NAME_TEXT_COLOR;
+ tagTextSize = DEFAULT_TAG_TEXT_SIZE;
+ tagTextColor = DEFAULT_TAG_TEXT_COLOR;
+ tagMargin = dpToPx(DEFAULT_TAG_MARGIN);
+ return;
+ }
+
+ // 从XML获取属性
+ TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.QXMeetUserView);
+ avatarSize = ta.getDimensionPixelSize(R.styleable.QXMeetUserView_avatarSize, dpToPx(DEFAULT_AVATAR_SIZE));
+ dressSize = ta.getDimensionPixelSize(R.styleable.QXMeetUserView_dressSize, dpToPx(DEFAULT_DRESS_SIZE));
+ nameTextSize = ta.getDimension(R.styleable.QXMeetUserView_nameTextSize, DEFAULT_NAME_TEXT_SIZE);
+ nameTextColor = ta.getColor(R.styleable.QXMeetUserView_nameTextColor, DEFAULT_NAME_TEXT_COLOR);
+ tagTextSize = ta.getDimension(R.styleable.QXMeetUserView_tagTextSize, DEFAULT_TAG_TEXT_SIZE);
+ tagTextColor = ta.getColor(R.styleable.QXMeetUserView_tagTextColor, DEFAULT_TAG_TEXT_COLOR);
+ tagMargin = ta.getDimensionPixelSize(R.styleable.QXMeetUserView_tagMargin, dpToPx(DEFAULT_TAG_MARGIN));
+ ta.recycle();
+
+ // 日志输出属性值,便于调试
+ Log.d(TAG, "属性初始化 - 头像大小: " + avatarSize + ", 装饰大小: " + dressSize);
+ }
+
+ private void initSubviews(Context context) {
+ setClipChildren(false);
+ setClipToPadding(false);
+ setWillNotDraw(false);
+ // 为整个视图添加背景色,便于调试视图范围
+ // setBackgroundColor(0x0A000000); // 极浅灰色背景
+
+ // 2. 头像(中间层)
+ headerImageView = new GifAvatarOvalView(context);
+ headerImageView.setId(View.generateViewId());
+ headerImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
+ // 设置强制背景色,确保即使资源加载失败也能看到
+ // headerImageView.setBackgroundColor(0x33FF0000); // 半透明红色
+
+ // 尝试加载默认头像资源
+ try {
+ int resId = R.mipmap.default_avatar;
+ // headerImageView.setImageResource(resId);
+ Log.d(TAG, "尝试加载默认头像资源: " + resId);
+ } catch (Exception e) {
+ Log.e(TAG, "默认头像资源加载失败: " + e.getMessage());
+ }
+
+ LayoutParams headerParams = new LayoutParams(avatarSize, avatarSize);
+ headerParams.addRule(CENTER_IN_PARENT); // 头像在父容器居中
+ addView(headerImageView, headerParams);
+ Log.d(TAG, "头像已添加到视图,大小: " + avatarSize + "x" + avatarSize);
+
+
+
+ // 1. 装饰图(底层)- 优先初始化确保在最底层
+ dressImageView = new ImageView(context);
+ dressImageView.setId(View.generateViewId());
+ dressImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
+ // 设置强制背景色,确保即使资源加载失败也能看到
+ /// dressImageView.setBackgroundColor(0x330000FF); // 半透明蓝色
+
+ // 尝试加载装饰图资源
+ try {
+ int resId = R.mipmap.xlh_image;
+ dressImageView.setImageResource(resId);
+ Log.d(TAG, "尝试加载装饰图资源: " + resId);
+ } catch (Exception e) {
+ Log.e(TAG, "装饰图资源加载失败: " + e.getMessage());
+ }
+
+ LayoutParams dressParams = new LayoutParams(dressSize, dressSize);
+ dressParams.addRule(CENTER_HORIZONTAL);
+ dressParams.topMargin = dpToPx(5); // 稍微向下移动一点,确保可见
+ addView(dressImageView, dressParams);
+ Log.d(TAG, "装饰图已添加到视图,大小: " + dressSize + "x" + dressSize);
+
+
+ // 3. 标签(顶层,与头像底部平齐)
+ tagLabel = new TextView(context);
+ tagLabel.setId(View.generateViewId());
+ tagLabel.setTextColor(tagTextColor);
+ tagLabel.setTextSize(tagTextSize);
+ tagLabel.setGravity(Gravity.CENTER);
+ tagLabel.setText("房主");
+ tagLabel.setBackground(getRoundedRectBackground(0xFF8D6F28, dpToPx(8)));
+ tagLabel.setPadding(dpToPx(6), 0, dpToPx(6), 0);
+
+ LayoutParams tagParams = new LayoutParams(LayoutParams.WRAP_CONTENT, dpToPx(16));
+ tagParams.addRule(CENTER_HORIZONTAL);
+ addView(tagLabel, tagParams);
+
+ // 4. 名字(顶层,在标签下方)
+ nameLabel = new TextView(context);
+ nameLabel.setId(View.generateViewId());
+ nameLabel.setTextColor(nameTextColor);
+ nameLabel.setTextSize(nameTextSize);
+ nameLabel.setText("虚位以待");
+ nameLabel.setGravity(Gravity.CENTER);
+ nameLabel.setSingleLine(true);
+
+ LayoutParams nameParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+ nameParams.addRule(CENTER_HORIZONTAL);
+ nameParams.addRule(BELOW, tagLabel.getId());
+ nameParams.topMargin = dpToPx(NAME_MARGIN_TOP);
+ addView(nameLabel, nameParams);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // 强制设置最小宽高,确保视图可见
+ int minWidth = Math.max(avatarSize, dressSize);
+ int minHeight = dressSize + dpToPx(40); // 装饰图高度 + 标签和名字的高度
+
+ int width = resolveSizeAndState(minWidth, widthMeasureSpec, 0);
+ int height = resolveSizeAndState(minHeight, heightMeasureSpec, 0);
+
+ setMeasuredDimension(width, height);
+
+ // 测量子视图
+ measureChildren(
+ MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
+ );
+
+ Log.d(TAG, "onMeasure - 视图大小: " + width + "x" + height);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+
+ // 确保装饰图正确布局
+ if (dressImageView != null) {
+ int dressLeft = (getWidth() - dressSize) / 2;
+ int dressTop = dpToPx(5); // 顶部留出一点空间
+ dressImageView.layout(
+ dressLeft,
+ dressTop,
+ dressLeft + dressSize,
+ dressTop + dressSize
+ );
+ Log.d(TAG, "装饰图布局位置: " + dressLeft + "," + dressTop + "," +
+ (dressLeft + dressSize) + "," + (dressTop + dressSize));
+ }
+
+ // 确保头像正确布局
+ if (headerImageView != null) {
+ int avatarLeft = (getWidth() - avatarSize) / 2;
+ int avatarTop = (dressSize - avatarSize) / 2 + dpToPx(5); // 居中显示在装饰图上
+ headerImageView.layout(
+ avatarLeft,
+ avatarTop,
+ avatarLeft + avatarSize,
+ avatarTop + avatarSize
+ );
+ Log.d(TAG, "头像布局位置: " + avatarLeft + "," + avatarTop + "," +
+ (avatarLeft + avatarSize) + "," + (avatarTop + avatarSize));
+ }
+
+ // 标签布局(与头像底部平齐)
+ if (headerImageView != null && tagLabel != null) {
+ int avatarBottom = headerImageView.getBottom();
+ int tagTop = avatarBottom + tagMargin;
+ int tagLeft = (getWidth() - tagLabel.getMeasuredWidth()) / 2;
+
+ tagLabel.layout(
+ tagLeft,
+ tagTop,
+ tagLeft + tagLabel.getMeasuredWidth(),
+ tagTop + tagLabel.getMeasuredHeight()
+ );
+ }
+
+ // 名字布局(在标签下方)
+ if (tagLabel != null && nameLabel != null) {
+ int tagBottom = tagLabel.getBottom();
+ int nameTop = tagBottom + dpToPx(NAME_MARGIN_TOP);
+ int nameLeft = (getWidth() - nameLabel.getMeasuredWidth()) / 2;
+
+ nameLabel.layout(
+ nameLeft,
+ nameTop,
+ nameLeft + nameLabel.getMeasuredWidth(),
+ nameTop + nameLabel.getMeasuredHeight()
+ );
+ }
+ }
+
+ // 以下方法保持不变
+ public void setIsLuckUser(boolean isLuckUser) {
+ if (tagLabel == null) return;
+
+ if (isLuckUser) {
+ tagLabel.setTextColor(0xFFFFFFFF);
+ tagLabel.setBackground(getRoundedRectBackground(0xFF6C49E4, dpToPx(8)));
+ tagLabel.setText("幸运者");
+ } else {
+ tagLabel.setTextColor(tagTextColor);
+ tagLabel.setBackground(getRoundedRectBackground(0xFF8D6F28, dpToPx(8)));
+ tagLabel.setText("房主");
+ }
+ }
+
+ public void setModel(BlindBoxBean.xlhUser model) {
+
+ if (headerImageView == null || nameLabel == null) return;
+
+ if (model != null) {
+ try {
+ if (model.getAvatar().toString()!=""){
+ ImageUtils.loadHeadCC(model.getAvatar(), headerImageView);
+ }else {
+ int resId = R.mipmap.default_avatar;
+ headerImageView.setImageResource(resId);
+ }
+
+ Log.d(TAG, "加载用户头像: " + model.getAvatar());
+ } catch (Exception e) {
+ int resId = R.mipmap.default_avatar;
+ headerImageView.setImageResource(resId);
+ Log.e(TAG, "加载用户头像失败: " + e.getMessage());
+ //headerImageView.setBackgroundColor(0x33FF0000); // 保持红色背景以便识别
+ }
+ nameLabel.setText(model.getNickname() != null ? model.getNickname() : "虚位以待");
+ } else {
+ resetView();
+ }
+ }
+
+ public void resetView() {
+ if (headerImageView != null) {
+ try {
+ headerImageView.setImageResource(R.mipmap.default_avatar);
+ } catch (Exception e) {
+ Log.e(TAG, "重置头像失败: " + e.getMessage());
+ // headerImageView.setBackgroundColor(0x33FF0000);
+ }
+ }
+ if (nameLabel != null) {
+ nameLabel.setText("虚位以待");
+ }
+ setIsLuckUser(false);
+ }
+
+ private GradientDrawable getRoundedRectBackground(int color, float radius) {
+ GradientDrawable drawable = new GradientDrawable();
+ drawable.setColor(color);
+ drawable.setCornerRadius(radius);
+ return drawable;
+ }
+
+ private int dpToPx(int dp) {
+ float density = getResources().getDisplayMetrics().density;
+ return Math.round(dp * density);
+ }
+
+ // Getter方法
+ public ImageView getHeaderImageView() { return headerImageView; }
+ public ImageView getDressImageView() { return dressImageView; }
+ public TextView getTagLabel() { return tagLabel; }
+ public TextView getNameLabel() { return nameLabel; }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/view/QXRedBagSendView.java b/moduleUtil/src/main/java/com/xscm/moduleutil/view/QXRedBagSendView.java
new file mode 100644
index 00000000..3c924550
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/view/QXRedBagSendView.java
@@ -0,0 +1,860 @@
+package com.xscm.moduleutil.view;
+
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Color;
+import android.text.InputType;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.DecelerateInterpolator;
+import android.webkit.WebView;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+import com.xscm.moduleutil.R;
+
+public class QXRedBagSendView extends FrameLayout {
+
+ // Properties
+ private String redBagType = "1";
+ private String redBagContentType = "1";
+ private String redBagTime = "0";
+ private int currentPage = 0;
+ private boolean isFromRule = false;
+
+ // UI Components
+ private LinearLayout mainContainer;
+ private ImageView bgImageView;
+ private TextView titleLabel;
+ private Button helpBtn;
+ private Button backBtn;
+ private Button closeBtn;
+ private Button nextBtn;
+ private Button commitBtn;
+
+ private LinearLayout firstContentView;
+ private Button normalRedBagBtn;
+ private Button pwdRedBagBtn;
+ private LinearLayout firstPwdView;
+ private EditText pwdTextField;
+ private LinearLayout firstTimeView;
+ private View scrollBgView;
+ private Button coinRedBagBtn;
+ private Button diamondRedBagBtn;
+ private Button selectedRedBagTimeBtn;
+
+ private LinearLayout nextContentView;
+ private TextView moneyLabel;
+ private TextView moneyUnitLabel;
+ private EditText moneyTextField;
+ private EditText countTextField;
+ private EditText remarkTextField;
+ private Button noDrawAuthBtn;
+ private Button collectDrawAuthBtn;
+ private Button upSeatDrawAuthBtn;
+
+ private LinearLayout ruleContentView;
+ private WebView webView;
+
+ private final int[] timeArray = {0, 1, 2, 5, 10};
+ private final int[] drawAuthArray = {0, 1, 2};
+
+ public QXRedBagSendView(Context context) {
+ super(context);
+ init(context);
+ }
+
+ public QXRedBagSendView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ public QXRedBagSendView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init(context);
+ }
+
+ private void init(Context context) {
+ setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ ));
+ setBackgroundColor(Color.parseColor("#80000000"));
+
+ initMainContainer(context);
+ initTitleBar(context);
+ initContentArea(context);
+ initButtons(context);
+
+ // 默认选择普通红包
+ selectedRedBagTypeAction(normalRedBagBtn);
+ }
+
+ private void initMainContainer(Context context) {
+ mainContainer = new LinearLayout(context);
+ mainContainer.setOrientation(LinearLayout.VERTICAL);
+ LayoutParams containerParams = new LayoutParams(
+ dpToPx(345),
+ dpToPx(454)
+ );
+ containerParams.gravity = Gravity.CENTER;
+ mainContainer.setLayoutParams(containerParams);
+
+ // 背景图片
+ bgImageView = new ImageView(context);
+ bgImageView.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ ));
+ // 设置背景资源,需要你在res/drawable中添加对应的图片
+ bgImageView.setBackgroundResource(R.mipmap.red_en);
+// bgImageView.setBackgroundColor(Color.parseColor("#FFD700")); // 临时颜色
+
+ // 将背景图片添加到主容器
+ mainContainer.addView(bgImageView);
+
+ addView(mainContainer);
+ }
+
+ private void initTitleBar(Context context) {
+ // 标题栏容器
+ LinearLayout titleContainer = new LinearLayout(context);
+ titleContainer.setOrientation(LinearLayout.HORIZONTAL);
+ titleContainer.setGravity(Gravity.CENTER_VERTICAL);
+ LinearLayout.LayoutParams titleContainerParams = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT
+ );
+ titleContainerParams.setMargins(0, dpToPx(15), 0, 0);
+ titleContainer.setLayoutParams(titleContainerParams);
+
+ // 帮助按钮
+ helpBtn = createIconButton(context);
+ helpBtn.setBackgroundColor(Color.TRANSPARENT);
+ helpBtn.setOnClickListener(v -> helpAction());
+ titleContainer.addView(helpBtn);
+
+ // 返回按钮
+ backBtn = createIconButton(context);
+ backBtn.setBackgroundColor(Color.TRANSPARENT);
+ backBtn.setOnClickListener(v -> backAction());
+ backBtn.setVisibility(View.GONE);
+ titleContainer.addView(backBtn);
+
+ // 标题
+ titleLabel = new TextView(context);
+ titleLabel.setText("直播间红包");
+ titleLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
+ titleLabel.setTextColor(Color.WHITE);
+ titleLabel.setTypeface(titleLabel.getTypeface(), android.graphics.Typeface.BOLD);
+ LinearLayout.LayoutParams titleParams = new LinearLayout.LayoutParams(
+ 0, ViewGroup.LayoutParams.WRAP_CONTENT, 1
+ );
+ titleLabel.setLayoutParams(titleParams);
+ titleLabel.setGravity(Gravity.CENTER);
+ titleContainer.addView(titleLabel);
+
+ // 关闭按钮
+ closeBtn = createIconButton(context);
+ closeBtn.setBackgroundColor(Color.TRANSPARENT);
+ closeBtn.setOnClickListener(v -> closeAction());
+ titleContainer.addView(closeBtn);
+
+ // 将标题栏添加到主容器(在背景图片之上)
+ mainContainer.addView(titleContainer);
+ }
+
+ private void initContentArea(Context context) {
+ // 内容区域容器
+ LinearLayout contentArea = new LinearLayout(context);
+ contentArea.setOrientation(LinearLayout.VERTICAL);
+ LinearLayout.LayoutParams contentParams = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ 0, 1
+ );
+ contentParams.setMargins(dpToPx(15), dpToPx(15), dpToPx(15), dpToPx(15));
+ contentArea.setLayoutParams(contentParams);
+
+ initFirstContentView(context, contentArea);
+ initNextContentView(context, contentArea);
+ initRuleView(context, contentArea);
+
+ mainContainer.addView(contentArea);
+ }
+
+ private void initButtons(Context context) {
+ // 按钮容器
+ LinearLayout buttonContainer = new LinearLayout(context);
+ buttonContainer.setOrientation(LinearLayout.VERTICAL);
+ LinearLayout.LayoutParams buttonParams = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT
+ );
+ buttonParams.setMargins(dpToPx(15), 0, dpToPx(15), dpToPx(15));
+ buttonContainer.setLayoutParams(buttonParams);
+
+ // 下一步按钮
+ nextBtn = createActionButton(context, "下一步");
+ nextBtn.setOnClickListener(v -> nextAction());
+ buttonContainer.addView(nextBtn);
+
+ // 提交按钮
+ commitBtn = createActionButton(context, "发红包");
+ commitBtn.setOnClickListener(v -> commitAction());
+ commitBtn.setVisibility(View.GONE);
+ buttonContainer.addView(commitBtn);
+
+ mainContainer.addView(buttonContainer);
+ }
+
+ private void initFirstContentView(Context context, ViewGroup parent) {
+ firstContentView = new LinearLayout(context);
+ firstContentView.setOrientation(LinearLayout.VERTICAL);
+ firstContentView.setLayoutParams(new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ ));
+ parent.addView(firstContentView);
+
+ // 顶部视图 - 参与领取限制
+ LinearLayout firstTopView = createCardView(context);
+ firstTopView.setOrientation(LinearLayout.VERTICAL);
+ LinearLayout.LayoutParams topParams = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ dpToPx(88)
+ );
+ topParams.bottomMargin = dpToPx(12);
+ firstTopView.setLayoutParams(topParams);
+ firstContentView.addView(firstTopView);
+
+ TextView topTitleLabel = createTitleLabel(context, "参与领取限制");
+ firstTopView.addView(topTitleLabel);
+
+ LinearLayout typeButtonContainer = new LinearLayout(context);
+ typeButtonContainer.setLayoutParams(new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT
+ ));
+ typeButtonContainer.setWeightSum(2);
+ firstTopView.addView(typeButtonContainer);
+
+ normalRedBagBtn = createTypeButton(context, "普通红包");
+ normalRedBagBtn.setOnClickListener(v -> selectedRedBagTypeAction(normalRedBagBtn));
+ typeButtonContainer.addView(normalRedBagBtn);
+
+ pwdRedBagBtn = createTypeButton(context, "口令红包");
+ pwdRedBagBtn.setOnClickListener(v -> selectedRedBagTypeAction(pwdRedBagBtn));
+ typeButtonContainer.addView(pwdRedBagBtn);
+
+ // 口令输入视图
+ firstPwdView = createInputCard(context, "口令", "请输入口令");
+ pwdTextField = (EditText) ((LinearLayout) firstPwdView).getChildAt(1);
+ firstContentView.addView(firstPwdView);
+
+ // 时间选择视图
+ firstTimeView = createCardView(context);
+ firstTimeView.setOrientation(LinearLayout.VERTICAL);
+ LinearLayout.LayoutParams timeParams = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ dpToPx(88)
+ );
+ timeParams.bottomMargin = dpToPx(12);
+ firstTimeView.setLayoutParams(timeParams);
+ firstContentView.addView(firstTimeView);
+
+ TextView timeTitleLabel = createTitleLabel(context, "开奖倒计时");
+ firstTimeView.addView(timeTitleLabel);
+
+ LinearLayout timeButtonContainer = new LinearLayout(context);
+ timeButtonContainer.setLayoutParams(new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT
+ ));
+ firstTimeView.addView(timeButtonContainer);
+
+ // 创建时间选择按钮
+ for (int time : timeArray) {
+ Button timeBtn = createTimeButton(context, time);
+ timeBtn.setOnClickListener(v -> redBagTimeAction(timeBtn));
+ if (time == 0) {
+ timeBtn.setSelected(true);
+ selectedRedBagTimeBtn = timeBtn;
+ }
+ timeButtonContainer.addView(timeBtn);
+ }
+
+ // 红包类型选择
+ LinearLayout bottomView = createCardView(context);
+ bottomView.setOrientation(LinearLayout.HORIZONTAL);
+ bottomView.setGravity(Gravity.CENTER_VERTICAL);
+ LinearLayout.LayoutParams bottomParams = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ dpToPx(48)
+ );
+ bottomView.setLayoutParams(bottomParams);
+ firstContentView.addView(bottomView);
+
+ TextView redBagTypeLabel = createTitleLabel(context, "红包类型");
+ bottomView.addView(redBagTypeLabel);
+
+ // 红包类型切换容器
+ RelativeLayout typeSwitchContainer = new RelativeLayout(context);
+ LinearLayout.LayoutParams switchParams = new LinearLayout.LayoutParams(
+ dpToPx(130),
+ dpToPx(26)
+ );
+ switchParams.gravity = Gravity.CENTER_VERTICAL;
+ typeSwitchContainer.setLayoutParams(switchParams);
+ typeSwitchContainer.setBackgroundColor(Color.parseColor("#BA230A"));
+ typeSwitchContainer.setPadding(dpToPx(2), dpToPx(2), dpToPx(2), dpToPx(2));
+ bottomView.addView(typeSwitchContainer);
+
+ scrollBgView = new View(context);
+ RelativeLayout.LayoutParams scrollParams = new RelativeLayout.LayoutParams(
+ dpToPx(63),
+ ViewGroup.LayoutParams.MATCH_PARENT
+ );
+ scrollBgView.setLayoutParams(scrollParams);
+ scrollBgView.setBackgroundColor(Color.parseColor("#FDE8A3"));
+ typeSwitchContainer.addView(scrollBgView);
+
+ coinRedBagBtn = createContentTypeButton(context, "金币红包");
+ coinRedBagBtn.setSelected(true);
+ coinRedBagBtn.setOnClickListener(v -> redBagContentTypeAction(coinRedBagBtn));
+ typeSwitchContainer.addView(coinRedBagBtn);
+
+ diamondRedBagBtn = createContentTypeButton(context, "钻石红包");
+ diamondRedBagBtn.setOnClickListener(v -> redBagContentTypeAction(diamondRedBagBtn));
+ typeSwitchContainer.addView(diamondRedBagBtn);
+ }
+
+ private void initNextContentView(Context context, ViewGroup parent) {
+ nextContentView = new LinearLayout(context);
+ nextContentView.setOrientation(LinearLayout.VERTICAL);
+ nextContentView.setLayoutParams(new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ ));
+ nextContentView.setVisibility(View.GONE);
+ parent.addView(nextContentView);
+
+ // 可用余额标签
+ moneyLabel = new TextView(context);
+ moneyLabel.setText("-金币可用");
+ moneyLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
+ moneyLabel.setTextColor(Color.WHITE);
+ LinearLayout.LayoutParams moneyLabelParams = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT
+ );
+ moneyLabelParams.gravity = Gravity.END;
+ moneyLabel.setLayoutParams(moneyLabelParams);
+ nextContentView.addView(moneyLabel);
+
+ // 金额输入
+ LinearLayout moneyBgView = createInputCard(context, "金额", "请输入红包金额");
+ moneyTextField = (EditText) moneyBgView.getChildAt(1);
+
+ moneyUnitLabel = new TextView(context);
+ moneyUnitLabel.setText("金币");
+ moneyUnitLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
+ moneyUnitLabel.setTextColor(Color.parseColor("#666666"));
+ LinearLayout.LayoutParams unitParams = new LinearLayout.LayoutParams(
+ dpToPx(30),
+ ViewGroup.LayoutParams.WRAP_CONTENT
+ );
+ moneyUnitLabel.setLayoutParams(unitParams);
+ moneyBgView.addView(moneyUnitLabel);
+
+ nextContentView.addView(moneyBgView);
+
+ // 个数输入
+ LinearLayout countBgView = createInputCard(context, "个数", "请输入红包数量");
+ countTextField = (EditText) countBgView.getChildAt(1);
+
+ TextView countUnitLabel = new TextView(context);
+ countUnitLabel.setText("个");
+ countUnitLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
+ countUnitLabel.setTextColor(Color.parseColor("#666666"));
+ countUnitLabel.setLayoutParams(unitParams);
+ countBgView.addView(countUnitLabel);
+
+ nextContentView.addView(countBgView);
+
+ // 领取条件
+ LinearLayout drawAuthBgView = createCardView(context);
+ drawAuthBgView.setOrientation(LinearLayout.VERTICAL);
+ LinearLayout.LayoutParams authParams = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ dpToPx(88)
+ );
+ authParams.bottomMargin = dpToPx(12);
+ drawAuthBgView.setLayoutParams(authParams);
+ nextContentView.addView(drawAuthBgView);
+
+ TextView authTitleLabel = createTitleLabel(context, "条件");
+ drawAuthBgView.addView(authTitleLabel);
+
+ LinearLayout authButtonContainer = new LinearLayout(context);
+ authButtonContainer.setLayoutParams(new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT
+ ));
+ drawAuthBgView.addView(authButtonContainer);
+
+ // 创建领取条件按钮
+ noDrawAuthBtn = createAuthButton(context, "无");
+ noDrawAuthBtn.setSelected(true);
+ noDrawAuthBtn.setOnClickListener(v -> drawAuthAction(noDrawAuthBtn));
+ authButtonContainer.addView(noDrawAuthBtn);
+
+ collectDrawAuthBtn = createAuthButton(context, "收藏房间");
+ collectDrawAuthBtn.setOnClickListener(v -> drawAuthAction(collectDrawAuthBtn));
+ authButtonContainer.addView(collectDrawAuthBtn);
+
+ upSeatDrawAuthBtn = createAuthButton(context, "仅麦上用户");
+ upSeatDrawAuthBtn.setOnClickListener(v -> drawAuthAction(upSeatDrawAuthBtn));
+ authButtonContainer.addView(upSeatDrawAuthBtn);
+
+ // 备注输入
+ LinearLayout remarkBgView = createInputCard(context, "备注", "请输入备注");
+ remarkTextField = (EditText) remarkBgView.getChildAt(1);
+ nextContentView.addView(remarkBgView);
+ }
+
+ private void initRuleView(Context context, ViewGroup parent) {
+ ruleContentView = new LinearLayout(context);
+ ruleContentView.setOrientation(LinearLayout.VERTICAL);
+ ruleContentView.setLayoutParams(new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ ));
+ ruleContentView.setBackgroundColor(Color.WHITE);
+ ruleContentView.setPadding(dpToPx(15), dpToPx(15), dpToPx(15), dpToPx(15));
+ ruleContentView.setVisibility(View.GONE);
+ parent.addView(ruleContentView);
+
+ webView = new WebView(context);
+ webView.setLayoutParams(new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ ));
+ ruleContentView.addView(webView);
+ }
+
+ // 工具方法
+ private Button createIconButton(Context context) {
+ Button button = new Button(context);
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
+ dpToPx(40),
+ dpToPx(40)
+ );
+ button.setLayoutParams(params);
+ return button;
+ }
+
+ private Button createActionButton(Context context, String text) {
+ Button button = new Button(context);
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ dpToPx(44)
+ );
+ button.setLayoutParams(params);
+ button.setText(text);
+ button.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
+ button.setTextColor(Color.parseColor("#F35248"));
+ button.setTypeface(button.getTypeface(), android.graphics.Typeface.BOLD);
+ button.setBackgroundColor(Color.parseColor("#FDE8A3")); // 临时背景色
+ return button;
+ }
+
+ private LinearLayout createCardView(Context context) {
+ LinearLayout card = new LinearLayout(context);
+ card.setBackgroundColor(Color.WHITE);
+ card.setPadding(dpToPx(15), dpToPx(11), dpToPx(15), dpToPx(5));
+ return card;
+ }
+
+ private TextView createTitleLabel(Context context, String text) {
+ TextView label = new TextView(context);
+ label.setText(text);
+ label.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
+ label.setTextColor(Color.parseColor("#666666"));
+ label.setTypeface(label.getTypeface(), android.graphics.Typeface.BOLD);
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ dpToPx(26)
+ );
+ label.setLayoutParams(params);
+ return label;
+ }
+
+ private Button createTypeButton(Context context, String text) {
+ Button button = new Button(context);
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
+ 0, dpToPx(36), 1
+ );
+ params.setMargins(0, 0, dpToPx(10), 0);
+ button.setLayoutParams(params);
+ button.setText(text);
+ button.setTextSize(TypedValue.COMPLEX_UNIT_SP, 15);
+ button.setTextColor(Color.WHITE);
+ button.setTypeface(button.getTypeface(), android.graphics.Typeface.BOLD);
+ button.setBackgroundColor(Color.parseColor("#FF9999")); // 临时背景色
+ return button;
+ }
+
+ private LinearLayout createInputCard(Context context, String title, String hint) {
+ LinearLayout card = new LinearLayout(context);
+ card.setOrientation(LinearLayout.HORIZONTAL);
+ card.setGravity(Gravity.CENTER_VERTICAL);
+ LinearLayout.LayoutParams cardParams = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ dpToPx(48)
+ );
+ cardParams.bottomMargin = dpToPx(12);
+ card.setLayoutParams(cardParams);
+ card.setBackgroundColor(Color.WHITE);
+ card.setPadding(dpToPx(15), 0, dpToPx(15), 0);
+
+ TextView titleLabel = createTitleLabel(context, title);
+ card.addView(titleLabel);
+
+ EditText editText = new EditText(context);
+ LinearLayout.LayoutParams editParams = new LinearLayout.LayoutParams(
+ 0, ViewGroup.LayoutParams.MATCH_PARENT, 1
+ );
+ editParams.setMargins(dpToPx(15), 0, dpToPx(15), 0);
+ editText.setLayoutParams(editParams);
+ editText.setHint(hint);
+ editText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
+ editText.setTextColor(Color.parseColor("#666666"));
+ editText.setGravity(Gravity.END);
+ editText.setBackground(null);
+ editText.setInputType(InputType.TYPE_CLASS_TEXT);
+ card.addView(editText);
+
+ return card;
+ }
+
+ private Button createTimeButton(Context context, int minutes) {
+ Button button = new Button(context);
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
+ dpToPx(49), dpToPx(36)
+ );
+ params.setMargins(0, 0, dpToPx(10), 0);
+ button.setLayoutParams(params);
+
+ String text = minutes == 0 ? "立刻" : minutes + "分钟";
+ button.setText(text);
+ button.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
+ button.setTypeface(button.getTypeface(), android.graphics.Typeface.BOLD);
+ button.setBackgroundColor(Color.parseColor("#EEEEEE")); // 临时背景色
+ return button;
+ }
+
+ private Button createContentTypeButton(Context context, String text) {
+ Button button = new Button(context);
+ RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
+ dpToPx(63), ViewGroup.LayoutParams.MATCH_PARENT
+ );
+ button.setLayoutParams(params);
+ button.setText(text);
+ button.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
+ button.setBackgroundColor(Color.TRANSPARENT);
+ button.setTextColor(Color.parseColor("#FFC9C7"));
+ return button;
+ }
+
+ private Button createAuthButton(Context context, String text) {
+ Button button = new Button(context);
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
+ 0, dpToPx(36), 1
+ );
+ params.setMargins(0, 0, dpToPx(10), 0);
+ button.setLayoutParams(params);
+ button.setText(text);
+ button.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
+ button.setTypeface(button.getTypeface(), android.graphics.Typeface.BOLD);
+ button.setBackgroundColor(Color.parseColor("#EEEEEE")); // 临时背景色
+ return button;
+ }
+
+ private int dpToPx(int dp) {
+ return (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ dp,
+ getResources().getDisplayMetrics()
+ );
+ }
+
+ // Action Methods
+ private void nextAction() {
+ currentPage = 1;
+ moneyUnitLabel.setText(redBagContentType.equals("1") ? "金币" : "钻石");
+
+ backBtn.setVisibility(View.GONE);
+ helpBtn.setVisibility(View.VISIBLE);
+ nextBtn.setVisibility(View.GONE);
+ commitBtn.setVisibility(View.VISIBLE);
+
+ switchContentView(firstContentView, nextContentView);
+ }
+
+ private void commitAction() {
+ // 实现发红包逻辑
+ }
+
+ private void helpAction() {
+ isFromRule = true;
+ webView.loadUrl("http://www.baidu.com");
+
+ backBtn.setVisibility(View.VISIBLE);
+ helpBtn.setVisibility(View.GONE);
+ nextBtn.setVisibility(View.GONE);
+ commitBtn.setVisibility(View.GONE);
+
+ View currentView = currentPage == 1 ? nextContentView : firstContentView;
+ switchContentView(currentView, ruleContentView);
+ }
+
+ private void closeAction() {
+ hide();
+ }
+
+ private void backAction() {
+ backBtn.setVisibility(View.GONE);
+ helpBtn.setVisibility(View.VISIBLE);
+
+ View currentView = currentPage == 1 ? nextContentView : firstContentView;
+
+ nextBtn.setVisibility(currentPage == 0 ? View.VISIBLE : View.GONE);
+ commitBtn.setVisibility(currentPage == 1 ? View.VISIBLE : View.GONE);
+
+ switchContentView(ruleContentView, currentView);
+ isFromRule = false;
+ }
+
+ private void selectedRedBagTypeAction(Button sender) {
+ if (sender.isSelected()) return;
+
+ if (sender == normalRedBagBtn) {
+ pwdRedBagBtn.setSelected(false);
+ normalRedBagBtn.setSelected(true);
+ firstPwdView.setVisibility(View.GONE);
+
+ // 调整时间视图位置
+ LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) firstTimeView.getLayoutParams();
+ params.topMargin = 0;
+ params.bottomMargin = dpToPx(12);
+ firstTimeView.setLayoutParams(params);
+
+ redBagType = "1";
+ } else {
+ pwdRedBagBtn.setSelected(true);
+ normalRedBagBtn.setSelected(false);
+ firstPwdView.setVisibility(View.VISIBLE);
+
+ // 调整时间视图位置
+ LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) firstTimeView.getLayoutParams();
+ params.topMargin = dpToPx(60);
+ params.bottomMargin = dpToPx(12);
+ firstTimeView.setLayoutParams(params);
+
+ redBagType = "2";
+ }
+
+ // 更新按钮背景色
+ updateButtonSelection(normalRedBagBtn, sender == normalRedBagBtn);
+ updateButtonSelection(pwdRedBagBtn, sender == pwdRedBagBtn);
+ }
+
+ private void redBagContentTypeAction(Button sender) {
+ if (sender.isSelected()) return;
+
+ if (sender == coinRedBagBtn) {
+ diamondRedBagBtn.setSelected(false);
+ coinRedBagBtn.setSelected(true);
+ redBagContentType = "1";
+ animateSwitchBg(true);
+ } else {
+ coinRedBagBtn.setSelected(false);
+ diamondRedBagBtn.setSelected(true);
+ redBagContentType = "2";
+ animateSwitchBg(false);
+ }
+
+ // 更新按钮文字颜色
+ updateContentTypeButtonColor(coinRedBagBtn, coinRedBagBtn.isSelected());
+ updateContentTypeButtonColor(diamondRedBagBtn, diamondRedBagBtn.isSelected());
+ }
+
+ private void drawAuthAction(Button sender) {
+ if (sender == noDrawAuthBtn) {
+ noDrawAuthBtn.setSelected(true);
+ collectDrawAuthBtn.setSelected(false);
+ upSeatDrawAuthBtn.setSelected(false);
+ } else if (sender == collectDrawAuthBtn) {
+ collectDrawAuthBtn.setSelected(!collectDrawAuthBtn.isSelected());
+ if (upSeatDrawAuthBtn.isSelected() || collectDrawAuthBtn.isSelected()) {
+ noDrawAuthBtn.setSelected(false);
+ } else {
+ noDrawAuthBtn.setSelected(true);
+ }
+ } else if (sender == upSeatDrawAuthBtn) {
+ noDrawAuthBtn.setSelected(false);
+ upSeatDrawAuthBtn.setSelected(!upSeatDrawAuthBtn.isSelected());
+ if (upSeatDrawAuthBtn.isSelected() || collectDrawAuthBtn.isSelected()) {
+ noDrawAuthBtn.setSelected(false);
+ } else {
+ noDrawAuthBtn.setSelected(true);
+ }
+ }
+
+ // 更新按钮背景色
+ updateAuthButtonSelection(noDrawAuthBtn, noDrawAuthBtn.isSelected());
+ updateAuthButtonSelection(collectDrawAuthBtn, collectDrawAuthBtn.isSelected());
+ updateAuthButtonSelection(upSeatDrawAuthBtn, upSeatDrawAuthBtn.isSelected());
+ }
+
+ private void redBagTimeAction(Button sender) {
+ if (sender.isSelected()) return;
+
+ if (selectedRedBagTimeBtn != null) {
+ selectedRedBagTimeBtn.setSelected(false);
+ updateTimeButtonSelection(selectedRedBagTimeBtn, false);
+ }
+
+ sender.setSelected(true);
+ selectedRedBagTimeBtn = sender;
+ int minutes = 0;
+ try {
+ String text = sender.getText().toString();
+ if (text.equals("立刻")) {
+ minutes = 0;
+ } else {
+ minutes = Integer.parseInt(text.replace("分钟", ""));
+ }
+ } catch (Exception e) {
+ minutes = 0;
+ }
+ redBagTime = String.valueOf(minutes * 60);
+
+ updateTimeButtonSelection(sender, true);
+ }
+
+ // Helper Methods
+ private void switchContentView(View hideView, View showView) {
+ hideView.setVisibility(View.GONE);
+ showView.setVisibility(View.VISIBLE);
+ }
+
+ private void updateButtonSelection(Button button, boolean selected) {
+ button.setBackgroundColor(selected ?
+ Color.parseColor("#FF5555") : Color.parseColor("#FF9999"));
+ }
+
+ private void updateTimeButtonSelection(Button button, boolean selected) {
+ button.setBackgroundColor(selected ?
+ Color.parseColor("#FF5555") : Color.parseColor("#EEEEEE"));
+ }
+
+ private void updateAuthButtonSelection(Button button, boolean selected) {
+ button.setBackgroundColor(selected ?
+ Color.parseColor("#FF5555") : Color.parseColor("#EEEEEE"));
+ }
+
+ private void updateContentTypeButtonColor(Button button, boolean selected) {
+ button.setTextColor(selected ?
+ Color.parseColor("#D01717") : Color.parseColor("#FFC9C7"));
+ }
+
+ private void animateSwitchBg(boolean toLeft) {
+ int targetX = toLeft ? 0 : dpToPx(63);
+
+ ValueAnimator animator = ValueAnimator.ofInt(
+ ((RelativeLayout.LayoutParams) scrollBgView.getLayoutParams()).leftMargin,
+ targetX
+ );
+ animator.addUpdateListener(animation -> {
+ int value = (int) animation.getAnimatedValue();
+ RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) scrollBgView.getLayoutParams();
+ params.leftMargin = value;
+ scrollBgView.setLayoutParams(params);
+ });
+ animator.setDuration(300);
+ animator.setInterpolator(new DecelerateInterpolator());
+ animator.start();
+ }
+
+ // Public Methods
+ public void showInView(ViewGroup parent) {
+ parent.addView(this);
+
+ // 添加入场动画
+ mainContainer.setTranslationY(-getHeight());
+ mainContainer.animate()
+ .translationY(0)
+ .setDuration(300)
+ .setInterpolator(new DecelerateInterpolator())
+ .start();
+ }
+
+ public void hide() {
+ // 添加退场动画
+ mainContainer.animate()
+ .translationY(getHeight())
+ .setDuration(300)
+ .setInterpolator(new DecelerateInterpolator())
+ .withEndAction(() -> {
+ ViewGroup parent = (ViewGroup) getParent();
+ if (parent != null) {
+ parent.removeView(QXRedBagSendView.this);
+ }
+ })
+ .start();
+ }
+
+ // Getter methods
+ public String getRedBagType() {
+ return redBagType;
+ }
+
+ public String getRedBagContentType() {
+ return redBagContentType;
+ }
+
+ public String getRedBagTime() {
+ return redBagTime;
+ }
+
+ public String getPassword() {
+ return pwdTextField.getText().toString();
+ }
+
+ public String getMoney() {
+ return moneyTextField.getText().toString();
+ }
+
+ public String getCount() {
+ return countTextField.getText().toString();
+ }
+
+ public String getRemark() {
+ return remarkTextField.getText().toString();
+ }
+
+ public int getDrawAuth() {
+ if (noDrawAuthBtn.isSelected()) return 0;
+ if (collectDrawAuthBtn.isSelected()) return 1;
+ if (upSeatDrawAuthBtn.isSelected()) return 2;
+ return 0;
+ }
+}
\ No newline at end of file
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/view/QXTimeDownView.java b/moduleUtil/src/main/java/com/xscm/moduleutil/view/QXTimeDownView.java
new file mode 100644
index 00000000..5743c9db
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/view/QXTimeDownView.java
@@ -0,0 +1,268 @@
+package com.xscm.moduleutil.view;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.ScaleAnimation;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.xscm.moduleutil.R;
+
+public class QXTimeDownView extends FrameLayout {
+
+ private ImageView bgImageView;
+ private TextView titleLabel;
+ private TextView timeLabel;
+ private TextView bigTimeLabel;
+
+ private long endTime;
+ private long startTime;
+ private Handler timerHandler;
+ private Runnable timerRunnable;
+
+ private TimeDownDelegate delegate;
+
+ public interface TimeDownDelegate {
+ void timeDownStartAnimation();
+ void timeDownUpdateAnimationWithTime(long time);
+ void timeDownStopAnimation();
+ void timeDownDidFinished();
+ }
+
+ public QXTimeDownView(Context context) {
+ super(context);
+ initSubviews(context);
+ }
+
+ public QXTimeDownView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ initSubviews(context);
+ }
+
+ public QXTimeDownView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initSubviews(context);
+ }
+
+ private void initSubviews(Context context) {
+ // 背景图片(最大的圆圈)
+ bgImageView = new ImageView(context);
+ bgImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
+ bgImageView.setImageResource(R.drawable.ac_time_down_bg);
+ LayoutParams bgParams = new LayoutParams(
+ LayoutParams.MATCH_PARENT,
+ LayoutParams.MATCH_PARENT
+ );
+ bgParams.gravity = Gravity.CENTER;
+ addView(bgImageView, bgParams);
+
+ // 时间标签(居中显示在背景图上)
+ timeLabel = new TextView(context);
+ timeLabel.setTextSize(16);
+ timeLabel.setTextColor(0xFFFFECA7);
+ timeLabel.setGravity(Gravity.CENTER);
+ timeLabel.setTypeface(android.graphics.Typeface.create("sans-serif-condensed", android.graphics.Typeface.NORMAL));
+ LayoutParams timeParams = new LayoutParams(
+ LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT
+ );
+ timeParams.gravity = Gravity.CENTER;
+ timeParams.topMargin = dpToPx(-10);
+ addView(timeLabel, timeParams);
+
+ // 标题标签(显示在时间标签下方)
+ titleLabel = new TextView(context);
+ titleLabel.setTextSize(12);
+ titleLabel.setTextColor(0xFFFFECA7);
+ titleLabel.setText("倒计时");
+ titleLabel.setGravity(Gravity.CENTER);
+ LayoutParams titleParams = new LayoutParams(
+ LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT
+ );
+ titleParams.gravity = Gravity.CENTER;
+ titleParams.topMargin = dpToPx(12); // 在timeLabel下方一定距离
+ addView(titleLabel, titleParams);
+
+ // 大时间标签(初始隐藏,用于最后30秒显示)
+ bigTimeLabel = new TextView(context);
+ bigTimeLabel.setTextSize(20);
+ bigTimeLabel.setText("-");
+ bigTimeLabel.setTextColor(0xFFFFECA7);
+ bigTimeLabel.setGravity(Gravity.CENTER);
+ bigTimeLabel.setVisibility(View.GONE);
+ bigTimeLabel.setTypeface(Typeface.create("semibold", Typeface.NORMAL));
+ LayoutParams bigTimeParams = new LayoutParams(
+ LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT
+ );
+ bigTimeParams.gravity = Gravity.CENTER;
+ addView(bigTimeLabel, bigTimeParams);
+
+ // 初始化定时器
+ timerHandler = new Handler(Looper.getMainLooper());
+ }
+
+ public void setEndTime(long endTime) {
+ this.endTime = endTime;
+
+ // 获取当前时间(秒)
+ long currentTime = System.currentTimeMillis() / 1000;
+ this.startTime = endTime - currentTime;
+
+ if (this.startTime <= 0) {
+ // 时间错误不进行倒计时
+ showBigTimeLabel("0");
+ return;
+ }
+
+ stopTimer();
+ startTimer();
+ }
+
+ private void startTimer() {
+ timerRunnable = new Runnable() {
+ @Override
+ public void run() {
+ startTime--;
+
+ long min = (startTime % 3600) / 60;
+ long second = startTime % 60;
+
+ if (startTime <= 30) {
+ // 最后30秒显示大数字
+ showBigTimeLabel(String.valueOf(startTime));
+
+ if (startTime == 30) {
+ if (delegate != null) {
+ delegate.timeDownStartAnimation();
+ }
+ } else {
+ if (delegate != null) {
+ delegate.timeDownUpdateAnimationWithTime(startTime);
+ }
+ }
+
+ // 弹性动画
+ performScaleAnimation();
+ } else {
+ // 正常倒计时显示
+ showNormalTime(String.format("%02d:%02d", min, second));
+
+ if (delegate != null) {
+ delegate.timeDownStopAnimation();
+ }
+ }
+
+ if (startTime <= 0) {
+ stopTimer();
+ showBigTimeLabel("0");
+
+ if (delegate != null) {
+ delegate.timeDownDidFinished();
+ delegate.timeDownStopAnimation();
+ }
+ } else {
+ // 继续下一次计时
+ timerHandler.postDelayed(this, 1000);
+ }
+ }
+ };
+
+ // 立即开始第一次执行
+ timerHandler.post(timerRunnable);
+ }
+
+ public void stopTimer() {
+ if (timerHandler != null && timerRunnable != null) {
+ timerHandler.removeCallbacks(timerRunnable);
+ timerRunnable = null;
+ }
+ }
+
+ private void showBigTimeLabel(String text) {
+ bigTimeLabel.setVisibility(View.VISIBLE);
+ timeLabel.setVisibility(View.GONE);
+ titleLabel.setVisibility(View.GONE);
+ bigTimeLabel.setText(text);
+ }
+
+ private void showNormalTime(String timeText) {
+ bigTimeLabel.setVisibility(View.GONE);
+ timeLabel.setVisibility(View.VISIBLE);
+ titleLabel.setVisibility(View.VISIBLE);
+ timeLabel.setText(timeText);
+ }
+
+ private void performScaleAnimation() {
+ ScaleAnimation scaleAnimation = new ScaleAnimation(
+ 1.0f, 1.5f, // X轴从1.0缩放到1.5
+ 1.0f, 1.5f, // Y轴从1.0缩放到1.5
+ ScaleAnimation.RELATIVE_TO_SELF, 0.5f, // 缩放中心X
+ ScaleAnimation.RELATIVE_TO_SELF, 0.5f // 缩放中心Y
+ );
+
+ scaleAnimation.setDuration(600);
+ scaleAnimation.setFillAfter(false);
+
+ bigTimeLabel.startAnimation(scaleAnimation);
+ }
+
+ public void reset() {
+ stopTimer();
+ showNormalTime("00:00");
+ bigTimeLabel.setVisibility(View.GONE);
+ timeLabel.setVisibility(View.VISIBLE);
+ titleLabel.setVisibility(View.VISIBLE);
+ }
+
+ // 辅助方法:dp 转 px
+ private int dpToPx(int dp) {
+ float density = getResources().getDisplayMetrics().density;
+ return Math.round(dp * density);
+ }
+
+ // Getter 和 Setter 方法
+ public void setDelegate(TimeDownDelegate delegate) {
+ this.delegate = delegate;
+ }
+
+ public long getEndTime() {
+ return endTime;
+ }
+
+ public long getStartTime() {
+ return startTime;
+ }
+
+ public TextView getTitleLabel() {
+ return titleLabel;
+ }
+
+ public TextView getTimeLabel() {
+ return timeLabel;
+ }
+
+ public TextView getBigTimeLabel() {
+ return bigTimeLabel;
+ }
+
+ public ImageView getBgImageView() {
+ return bgImageView;
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ stopTimer();
+ }
+}
\ No newline at end of file
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/view/RichTextView.kt b/moduleUtil/src/main/java/com/xscm/moduleutil/view/RichTextView.kt
new file mode 100644
index 00000000..b9165e11
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/view/RichTextView.kt
@@ -0,0 +1,207 @@
+package com.xscm.moduleutil.view
+
+import android.content.Context
+import android.graphics.Color
+import android.graphics.Typeface
+import android.text.Html
+import android.text.Spannable
+import android.text.SpannableStringBuilder
+import android.text.TextPaint
+import android.text.method.LinkMovementMethod
+import android.text.style.*
+import android.util.Log
+import android.util.AttributeSet
+import android.view.View
+import android.widget.TextView
+import androidx.annotation.ColorInt
+import androidx.annotation.StringRes
+
+/**
+ * 封装了富文本展示功能的自定义TextView
+ * 支持HTML格式、自定义样式文本和点击事件
+ */
+class RichTextView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : androidx.appcompat.widget.AppCompatTextView(context, attrs, defStyleAttr) {
+
+ // 富文本构建器
+ private val spannableBuilder = SpannableStringBuilder()
+
+ init {
+ // 初始化配置
+ movementMethod = LinkMovementMethod.getInstance()
+ highlightColor = Color.TRANSPARENT // 移除点击高亮
+ }
+
+ /**
+ * 设置HTML格式的富文本
+ */
+ fun setHtmlText(html: String) {
+ val spanned = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
+ Html.fromHtml(html, Html.FROM_HTML_MODE_COMPACT)
+ } else {
+ @Suppress("DEPRECATION")
+ Html.fromHtml(html)
+ }
+ text = spanned
+ }
+
+ /**
+ * 从资源文件设置HTML富文本
+ */
+ fun setHtmlText(@StringRes resId: Int) {
+ setHtmlText(context.getString(resId))
+ }
+
+ /**
+ * 开始构建富文本
+ */
+ fun beginBuild(): RichTextBuilder {
+ spannableBuilder.clear()
+ return RichTextBuilder()
+ }
+
+ /**
+ * 富文本构建器
+ * 支持链式调用添加各种样式的文本
+ */
+ inner class RichTextBuilder {
+
+ /**
+ * 添加普通文本
+ */
+ fun addText(text: String): RichTextBuilder {
+ spannableBuilder.append(text)
+ return this
+ }
+
+ /**
+ * 添加普通文本(从资源文件)
+ */
+ fun addText(@StringRes resId: Int): RichTextBuilder {
+ return addText(context.getString(resId))
+ }
+
+ /**
+ * 添加加粗文本
+ */
+ fun addBoldText(text: String): RichTextBuilder {
+ return addStyledText(text, StyleSpan(Typeface.BOLD))
+ }
+
+ /**
+ * 添加斜体文本
+ */
+ fun addItalicText(text: String): RichTextBuilder {
+ return addStyledText(text, StyleSpan(Typeface.ITALIC))
+ }
+
+ /**
+ * 添加下划线文本
+ */
+ fun addUnderlineText(text: String): RichTextBuilder {
+ return addStyledText(text, UnderlineSpan())
+ }
+
+ /**
+ * 添加删除线文本
+ */
+ fun addStrikethroughText(text: String): RichTextBuilder {
+ return addStyledText(text, StrikethroughSpan())
+ }
+
+ /**
+ * 添加指定颜色的文本
+ */
+ fun addColoredText(text: String, @ColorInt color: Int): RichTextBuilder {
+ return addStyledText(text, ForegroundColorSpan(color))
+ }
+
+ /**
+ * 添加指定背景色的文本
+ */
+ fun addBackgroundColoredText(text: String, @ColorInt color: Int): RichTextBuilder {
+ return addStyledText(text, BackgroundColorSpan(color))
+ }
+
+ /**
+ * 添加指定大小的文本
+ * @param proportion 相对于默认大小的比例
+ */
+ fun addSizedText(text: String, proportion: Float): RichTextBuilder {
+ return addStyledText(text, RelativeSizeSpan(proportion))
+ }
+
+ /**
+ * 添加带有点击事件的文本
+ */
+ fun addClickableText(
+ text: String,
+ @ColorInt linkColor: Int = Color.BLUE,
+ isUnderline: Boolean = false,
+ onClick: () -> Unit
+ ): RichTextBuilder {
+ val start = spannableBuilder.length
+ spannableBuilder.append(text)
+ val end = spannableBuilder.length
+
+ val clickableSpan = object : ClickableSpan() {
+ override fun onClick(widget: View) {
+ onClick.invoke()
+ }
+
+ override fun updateDrawState(ds: TextPaint) {
+ super.updateDrawState(ds)
+ ds.color = linkColor
+ ds.isUnderlineText = isUnderline
+ }
+ }
+
+ spannableBuilder.setSpan(
+ clickableSpan,
+ start,
+ end,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
+ )
+
+ return this
+ }
+
+ /**
+ * 添加换行
+ */
+ fun addLineBreak(): RichTextBuilder {
+ spannableBuilder.append("\n")
+ return this
+ }
+
+ /**
+ * 添加自定义样式的文本
+ */
+ fun addStyledText(text: String, vararg spans: Any): RichTextBuilder {
+ val start = spannableBuilder.length
+ spannableBuilder.append(text)
+ val end = spannableBuilder.length
+
+ spans.forEach { span ->
+ spannableBuilder.setSpan(
+ span,
+ start,
+ end,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
+ )
+ }
+
+ return this
+ }
+
+ /**
+ * 完成构建并应用到TextView
+ */
+ fun build() {
+ text = spannableBuilder
+ }
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/widget/CenterScrollHelper.java b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/CenterScrollHelper.java
new file mode 100644
index 00000000..198c749e
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/CenterScrollHelper.java
@@ -0,0 +1,271 @@
+package com.xscm.moduleutil.widget;
+
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.LinearSmoothScroller;
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * @Author lxj$
+ * @Time $ 2025-9-4 21:04:34$
+ * @Description 自定义滚动辅助类$
+ */
+public class CenterScrollHelper {
+ private RecyclerView recyclerView;
+
+ public CenterScrollHelper(RecyclerView recyclerView) {
+ this.recyclerView = recyclerView;
+ }
+
+ /**
+ * 循环滚动指定圈数后停在目标位置
+ * @param targetPosition 目标位置
+ * @param circles 滚动圈数
+ * @param durationPerItem 每个item滚动的持续时间(控制速度)
+ * @param adapterSize 适配器总大小
+ */
+ public void scrollWithCircles(int targetPosition, int circles, int durationPerItem, int adapterSize) {
+ scrollWithCircles(targetPosition, circles, durationPerItem, adapterSize, null);
+ }
+
+ /**
+ * 循环滚动指定圈数后停在目标位置(带回调)
+ * @param targetPosition 目标位置
+ * @param circles 滚动圈数
+ * @param durationPerItem 每个item滚动的持续时间(控制速度)
+ * @param adapterSize 适配器总大小
+ * @param onComplete 滚动完成回调
+ */
+ public void scrollWithCircles(int targetPosition, int circles, int durationPerItem,
+ int adapterSize, Runnable onComplete) {
+ if (recyclerView.getLayoutManager() == null) {
+ if (onComplete != null) onComplete.run();
+ return;
+ }
+
+ // 计算总滚动位置(多圈+目标位置)
+ int totalItems = circles * adapterSize + targetPosition;
+
+
+ // 使用LinearSmoothScroller进行平滑滚动
+ LinearSmoothScroller smoothScroller = new LinearSmoothScroller(recyclerView.getContext()) {
+ private static final float ACCELERATION = 0.5f; // 加速度
+ private static final float DECELERATION = 0.8f; // 减速度
+
+ @Override
+ protected int calculateTimeForScrolling(int dx) {
+ // 使用缓动函数计算时间,实现从慢到快再到慢的效果
+// return calculateEasingTime(dx, durationPerItem);
+
+// // 简单线性时间计算,确保滚动速度一致
+// int screenWidth = recyclerView.getWidth();
+// if (screenWidth <= 0) {
+// return durationPerItem * 3;
+// }
+// int itemWidth = screenWidth / 3;
+// int items = Math.max(1, dx / itemWidth);
+// return durationPerItem * items;
+
+ // 使用缓动函数计算时间,实现从慢到快再到慢的效果
+ return calculateEasingTime(dx, durationPerItem);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ // 滚动停止后确保目标位置居中
+// scrollToCenter(targetPosition);
+ // 执行完成回调
+ if (onComplete != null) {
+ recyclerView.post(onComplete);
+ }
+ }
+ };
+
+ smoothScroller.setTargetPosition(totalItems);
+ recyclerView.getLayoutManager().startSmoothScroll(smoothScroller);
+ }
+
+ // 在 CenterScrollHelper 类中添加
+ public void reset() {
+ // 清理可能存在的回调或状态
+ if (recyclerView != null) {
+ recyclerView.stopScroll();
+ recyclerView.getHandler().removeCallbacksAndMessages(null);
+ }
+ }
+
+ /**
+ * 使用缓动函数计算滚动时间,实现加速减速效果
+ * @param dx 滚动距离
+ * @param baseDurationPerItem 基础时间
+ * @return 计算后的时间
+ */
+ private int calculateEasingTime(int dx, int baseDurationPerItem) {
+ if (dx <= 0) return 0;
+
+ // 获取屏幕宽度作为参考
+ int screenWidth = recyclerView.getWidth();
+ if (screenWidth <= 0) {
+ return baseDurationPerItem * 3; // 默认值
+ }
+
+ // 计算item宽度(假设每屏3个item)
+ int itemWidth = screenWidth / 3;
+ // 计算滚动的item数量
+ int items = Math.max(1, dx / itemWidth);
+
+ // 使用easeInOutQuad缓动函数实现先慢中快后慢的效果
+ double progress = Math.min(1.0, (double) items / 100); // 假设100个item为完整过程
+
+ // easeInOutQuad缓动函数
+ double easeProgress;
+ if (progress < 0.5) {
+ easeProgress = 2 * progress * progress; // 先慢后快
+ } else {
+ easeProgress = 1 - Math.pow(-2 * progress + 2, 2) / 2; // 后慢
+ }
+
+ // 计算时间:开始慢(500ms),后来快(50ms)
+ int minDuration = 1000; // 最快速度
+ int maxDuration = 2000; // 最慢速度
+ int calculatedTime = (int) (maxDuration - (maxDuration - minDuration) * easeProgress);
+
+ return Math.max(minDuration, calculatedTime);
+ }
+
+ /**
+ * 将指定位置的item精确居中显示
+ * @param position 需要居中的位置(在循环列表中的实际位置)
+ * @param originalSize 原始数据大小
+ */
+ public void scrollToCenter(int position, int originalSize) {
+ if (recyclerView.getLayoutManager() == null) return;
+
+ int screenWidth = recyclerView.getWidth();
+ if (screenWidth <= 0) return;
+
+ // 计算item宽度(假设每个item等宽)
+ int itemWidth = screenWidth / 3; // 每屏显示3个item
+
+ // 计算使item居中需要滚动的总距离
+ int targetScrollX = position * itemWidth - (screenWidth - itemWidth) / 2;
+
+ // 获取当前滚动位置
+ int currentScrollX = recyclerView.computeHorizontalScrollOffset();
+
+ // 计算需要滚动的距离
+ int scrollDistance = targetScrollX - currentScrollX;
+
+ // 执行滚动
+ recyclerView.smoothScrollBy(scrollDistance, 0);
+ }
+
+ /**
+ * 将指定位置的item精确居中显示(简化版)
+ * @param position 需要居中的位置
+ */
+ public void scrollToCenter(int position) {
+ if (recyclerView.getLayoutManager() == null) return;
+
+ int screenWidth = recyclerView.getWidth();
+ if (screenWidth <= 0) return;
+
+ // 计算item宽度
+ int itemWidth = screenWidth / 3;
+
+ // 计算目标位置的左边缘
+ int targetLeft = position * itemWidth;
+
+ // 计算使item居中需要滚动到的位置
+ int targetScrollX = targetLeft - (screenWidth - itemWidth) / 2;
+
+ // 获取当前滚动位置
+ int currentScrollX = recyclerView.computeHorizontalScrollOffset();
+
+ // 计算需要滚动的距离
+ int scrollDistance = targetScrollX - currentScrollX;
+
+ // 如果距离很小,就不需要滚动了
+ if (Math.abs(scrollDistance) > 5) {
+ // 执行滚动
+ recyclerView.smoothScrollBy(scrollDistance, 0);
+ }
+ }
+ /**
+ * 将指定位置的item居中显示(支持循环)
+ */
+ public void centerItem(int targetPosition, int adapterSize) {
+ if (recyclerView.getLayoutManager() == null) return;
+
+ LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
+ int screenWidth = recyclerView.getWidth();
+ if (screenWidth <= 0) return;
+
+ int itemWidth = screenWidth / 3; // 每个item占屏幕1/3
+
+ // 计算需要滚动的距离使item居中
+ // 这里需要根据实际的item宽度和屏幕宽度来计算居中位置
+ int targetScrollX = targetPosition * itemWidth - (screenWidth - itemWidth) / 2;
+ int currentScrollX = recyclerView.computeHorizontalScrollOffset();
+ int scrollDistance = targetScrollX - currentScrollX;
+
+ // 使用smoothScrollBy确保平滑滚动到居中位置
+ recyclerView.smoothScrollBy(scrollDistance, 0);
+ }
+ /**
+ * 使用更复杂的缓动函数计算滚动时间
+ * @param dx 滚动距离
+ * @param baseDurationPerItem 基础时间
+ * @return 计算后的时间
+ */
+ private int calculateAdvancedEasingTime(int dx, int baseDurationPerItem) {
+ if (dx <= 0) return 0;
+
+ int screenWidth = recyclerView.getWidth();
+ if (screenWidth <= 0) {
+ return baseDurationPerItem * 3;
+ }
+
+ int itemWidth = screenWidth / 3;
+ int totalItems = dx / itemWidth;
+
+ // 分段处理:开始慢,中间快,结束慢
+ int totalTime = 0;
+ for (int i = 0; i < totalItems; i++) {
+ // 计算当前位置的进度 (0-1)
+ double progress = (double) i / Math.max(1, totalItems);
+
+ // 使用贝塞尔缓动函数:慢-快-慢
+ double easedProgress = bezierEasing(progress, 0.25, 0.1, 0.25, 1.0);
+
+ // 根据进度调整时间
+ int minTime = 30; // 最快时间
+ int maxTime = baseDurationPerItem * 2; // 最慢时间
+ int itemTime = (int) (maxTime - (maxTime - minTime) * easedProgress);
+
+ totalTime += itemTime;
+ }
+
+ return Math.max(100, totalTime); // 至少100ms
+ }
+
+ /**
+ * 贝塞尔缓动函数
+ * @param t 时间进度 (0-1)
+ * @param x1 控制点1 x
+ * @param y1 控制点1 y
+ * @param x2 控制点2 x
+ * @param y2 控制点2 y
+ * @return 缓动后的值
+ */
+ private double bezierEasing(double t, double x1, double y1, double x2, double y2) {
+ // 简化的贝塞尔曲线计算
+ // 这里使用一个近似算法
+ double a = 1 - t;
+ return 3 * a * a * t * y1 + 3 * a * t * t * y2 + t * t * t;
+ }
+
+
+}
+
+
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/widget/DropHourlView.java b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/DropHourlView.java
new file mode 100644
index 00000000..e290cf27
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/DropHourlView.java
@@ -0,0 +1,285 @@
+package com.xscm.moduleutil.widget;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewAnimationUtils;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import com.blankj.utilcode.util.ScreenUtils;
+import com.xscm.moduleutil.utils.BarUtils;
+
+/**
+ * 描述:小时榜的显示视图
+ */
+public class DropHourlView extends LinearLayout {
+
+ private int rightMargin = 0;
+ private float lastX, lastY;
+ private int screenWidth;
+ private int screenHeight; // 添加屏幕高度变量
+
+ public DropHourlView(Context context) {
+ super(context);
+ init();
+ }
+
+ public DropHourlView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public DropHourlView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+ }
+
+
+ void init() {
+ // 初始化屏幕尺寸
+ screenWidth = ScreenUtils.getScreenWidth();
+ screenHeight = ScreenUtils.getScreenHeight();
+
+ post(new Runnable() {
+ @Override
+ public void run() {
+ //设置初始位置
+ int sh = ScreenUtils.getScreenHeight();
+ int sw = ScreenUtils.getScreenWidth()-200;
+// setBackgroundResource(R.drawable.bg_home_drop_view);
+ int y = (int) (0.5f * sh) - getHeight();
+ // 确保Y坐标不会超出屏幕范围
+ y = Math.max(0, Math.min(y, sh - getHeight()));
+ int x = sw - getWidth();
+ setTranslationX(x);
+ setTranslationY(y);
+ }
+ });
+
+ updateSize();
+ mStatusBarHeight = BarUtils.getStatusBarHeight();
+ }
+ /**
+ * 更新屏幕尺寸信息
+ */
+ protected void updateSize() {
+ ViewGroup viewGroup = (ViewGroup) getParent();
+ if (viewGroup != null) {
+ mScreenWidth = viewGroup.getWidth();
+ mScreenHeight = viewGroup.getHeight();
+ } else {
+ // 如果父视图为空,使用屏幕的实际宽度和高度
+ mScreenWidth = getResources().getDisplayMetrics().widthPixels;
+ mScreenHeight = getResources().getDisplayMetrics().heightPixels;
+ }
+ }
+
+ boolean starDrap = false;
+ float X1;
+ float X2;
+ float Y1;
+ float Y2;
+ // 记录视图初始位置
+ private float originalX;
+ private float originalY;
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ if (starDrap) return true;
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ X1 = event.getRawX();
+ Y1 = event.getRawY();
+ // 记录视图当前位置
+ originalX = getTranslationX();
+ originalY = getTranslationY();
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ X2 = event.getRawX();
+ Y2 = event.getRawY();
+ Action(X1, X2, Y1, Y2);
+ break;
+
+
+ }
+ return starDrap;
+ }
+
+ String TAG = "DropHourlView";
+
+ public boolean Action(float X1, float X2, float Y1, float Y2) {
+ float ComparedX = X2 - X1;//第二次的X坐标的位置减去第一次X坐标的位置,代表X坐标上的变化情况
+ float ComparedY = Y2 - Y1;//同理
+ //当X坐标的变化量的绝对值大于Y坐标的变化量的绝对值,以X坐标的变化情况作为判断依据
+ //上下左右的判断,都在一条直线上,但手指的操作不可能划直线,所有选择变化量大的方向上的量
+ //作为判断依据
+ if (Math.abs(ComparedX) > 30 || Math.abs(ComparedY) > 30) {
+ Log.i(TAG, "Action: 拖动");
+ starDrap = true;
+// setBackgroundResource(R.drawable.bg_home_drop_view);
+ return true;
+ } else {
+ starDrap = false;
+ return false;
+ }
+ }
+ private float mOriginalRawX;
+ private float mOriginalRawY;
+ private float mOriginalX;
+ private float mOriginalY;
+ protected int mScreenWidth;
+ private int mScreenHeight;
+ private int mStatusBarHeight;
+
+ private void updateViewPosition(MotionEvent event) {
+ // 计算新的Y位置
+ float desY = mOriginalY + event.getRawY() - mOriginalRawY;
+
+ // 限制Y位置不超出屏幕边界
+ if (desY < mStatusBarHeight) {
+ desY = mStatusBarHeight;
+ }
+ if (desY > mScreenHeight - getHeight()) {
+ desY = mScreenHeight - getHeight();
+ }
+
+ // 计算新的X位置
+ float desX = mOriginalX + event.getRawX() - mOriginalRawX;
+
+ // 限制X位置不超出屏幕边界
+ if (desX < 0) {
+ desX = 0;
+ }
+ if (desX > mScreenWidth - getWidth()) {
+ desX = mScreenWidth - getWidth();
+ }
+
+ // 设置视图的新位置
+ setX(desX);
+ setY(desY);
+ }
+ private void changeOriginalTouchParams(MotionEvent event) {
+ mOriginalX = getX();//getX()相对于控件X坐标的距离
+ mOriginalY = getY();
+ mOriginalRawX = event.getRawX();//getRawX()指控件在屏幕上的X坐标
+ mOriginalRawY = event.getRawY();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (event == null) {
+ return false;
+ }
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ changeOriginalTouchParams(event);
+ updateSize(); // 添加这行确保尺寸是最新的
+ // ... 其他现有代码 ...
+
+ break;
+ case MotionEvent.ACTION_MOVE:
+
+ updateViewPosition(event); // 使用更新后的带边界检查的方法
+
+// setBackgroundResource(R.drawable.bg_home_drop_view);
+ // 使用屏幕绝对坐标计算新位置
+// float newX = originalX + (event.getRawX() - X1);
+// float newY = originalY + (event.getRawY() - Y1);
+//
+// // 限制X和Y坐标在屏幕范围内
+// newX = Math.max(0, Math.min(newX, screenWidth - getWidth()));
+// newY = Math.max(0, Math.min(newY, screenHeight - getHeight()));
+//
+// setTranslationX(newX);
+// setTranslationY(newY);
+// X2 = event.getRawX();
+ break;
+ case MotionEvent.ACTION_UP:
+ starDrap = false;
+ int sw = ScreenUtils.getScreenWidth();
+ Log.i(TAG, "onTouchEvent: " + sw + "," + X2);
+ boolean isR = getTranslationX() + getWidth() / 2 >= sw / 2;//贴边方向
+
+ // 获取当前Y坐标
+ float currentY = getTranslationY();
+
+ // 创建X轴和Y轴的动画
+ ObjectAnimator animX = ObjectAnimator.ofFloat(this, "translationX", isR ? sw - getWidth() : 0f).setDuration(200);
+ // Y轴保持当前位置,但确保在屏幕范围内
+ currentY = Math.max(0, Math.min(currentY, screenHeight - getHeight()));
+ ObjectAnimator animY = ObjectAnimator.ofFloat(this, "translationY", currentY).setDuration(200);
+
+ animX.start();
+ animY.start();
+
+ break;
+
+ }
+
+ return true;
+ }
+
+
+ public void doRevealAnimation(View mPuppet, boolean flag) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ int[] vLocation = new int[2];
+ getLocationInWindow(vLocation);
+ int centerX = vLocation[0] + getMeasuredWidth() / 2;
+ int centerY = vLocation[1] + getMeasuredHeight() / 2;
+
+ int height = ScreenUtils.getScreenHeight();
+ int width = ScreenUtils.getScreenWidth();
+ int maxRradius = (int) Math.hypot(height, width);
+ Log.e("hei", maxRradius + "");
+
+ if (flag) {
+ mPuppet.setVisibility(VISIBLE);
+ Animator animator = ViewAnimationUtils.createCircularReveal(mPuppet, centerX, centerY, maxRradius, 0);
+ animator.setDuration(600);
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mPuppet.setVisibility(View.GONE);
+ }
+ });
+ animator.start();
+ flag = false;
+ } else {
+ Animator animator = ViewAnimationUtils.createCircularReveal(mPuppet, centerX, centerY, 0, maxRradius);
+ animator.setDuration(1000);
+ animator.addListener(new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mPuppet.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+
+ }
+ });
+ animator.start();
+ flag = true;
+ }
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/widget/DropRedView.java b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/DropRedView.java
new file mode 100644
index 00000000..30117f0e
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/DropRedView.java
@@ -0,0 +1,286 @@
+package com.xscm.moduleutil.widget;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewAnimationUtils;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import com.blankj.utilcode.util.ScreenUtils;
+import com.xscm.moduleutil.utils.BarUtils;
+
+/**
+ * 这是红包入口悬浮框
+ */
+public class DropRedView extends LinearLayout {
+
+ private int rightMargin = 0;
+ private float lastX, lastY;
+ private int screenWidth;
+ private int screenHeight; // 添加屏幕高度变量
+
+ public DropRedView(Context context) {
+ super(context);
+ init();
+ }
+
+ public DropRedView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public DropRedView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+ }
+
+
+ void init() {
+ // 初始化屏幕尺寸
+ screenWidth = ScreenUtils.getScreenWidth();
+ screenHeight = ScreenUtils.getScreenHeight();
+
+ post(new Runnable() {
+ @Override
+ public void run() {
+ //设置初始位置
+ int sh = ScreenUtils.getScreenHeight();
+ int sw = ScreenUtils.getScreenWidth();
+// setBackgroundResource(R.drawable.bg_home_drop_view);
+ int y = (int) (0.5f * sh) - getHeight();
+ // 确保Y坐标不会超出屏幕范围
+ y = Math.max(0, Math.min(y, sh - getHeight()));
+// int x = sw - getWidth();//这是靠右边展示的
+ int x=20 ;//这里这只一小的数值,就是靠左展示的
+ setTranslationX(x);
+ setTranslationY(y);
+ }
+ });
+
+ updateSize();
+ mStatusBarHeight = BarUtils.getStatusBarHeight();
+ }
+ /**
+ * 更新屏幕尺寸信息
+ */
+ protected void updateSize() {
+ ViewGroup viewGroup = (ViewGroup) getParent();
+ if (viewGroup != null) {
+ mScreenWidth = viewGroup.getWidth();
+ mScreenHeight = viewGroup.getHeight();
+ } else {
+ // 如果父视图为空,使用屏幕的实际宽度和高度
+ mScreenWidth = getResources().getDisplayMetrics().widthPixels;
+ mScreenHeight = getResources().getDisplayMetrics().heightPixels;
+ }
+ }
+
+ boolean starDrap = false;
+ float X1;
+ float X2;
+ float Y1;
+ float Y2;
+ // 记录视图初始位置
+ private float originalX;
+ private float originalY;
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ if (starDrap) return true;
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ X1 = event.getRawX();
+ Y1 = event.getRawY();
+ // 记录视图当前位置
+ originalX = getTranslationX();
+ originalY = getTranslationY();
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ X2 = event.getRawX();
+ Y2 = event.getRawY();
+ Action(X1, X2, Y1, Y2);
+ break;
+
+
+ }
+ return starDrap;
+ }
+
+ String TAG = "DropHourlView";
+
+ public boolean Action(float X1, float X2, float Y1, float Y2) {
+ float ComparedX = X2 - X1;//第二次的X坐标的位置减去第一次X坐标的位置,代表X坐标上的变化情况
+ float ComparedY = Y2 - Y1;//同理
+ //当X坐标的变化量的绝对值大于Y坐标的变化量的绝对值,以X坐标的变化情况作为判断依据
+ //上下左右的判断,都在一条直线上,但手指的操作不可能划直线,所有选择变化量大的方向上的量
+ //作为判断依据
+ if (Math.abs(ComparedX) > 30 || Math.abs(ComparedY) > 30) {
+ Log.i(TAG, "Action: 拖动");
+ starDrap = true;
+// setBackgroundResource(R.drawable.bg_home_drop_view);
+ return true;
+ } else {
+ starDrap = false;
+ return false;
+ }
+ }
+ private float mOriginalRawX;
+ private float mOriginalRawY;
+ private float mOriginalX;
+ private float mOriginalY;
+ protected int mScreenWidth;
+ private int mScreenHeight;
+ private int mStatusBarHeight;
+
+ private void updateViewPosition(MotionEvent event) {
+ // 计算新的Y位置
+ float desY = mOriginalY + event.getRawY() - mOriginalRawY;
+
+ // 限制Y位置不超出屏幕边界
+ if (desY < mStatusBarHeight) {
+ desY = mStatusBarHeight;
+ }
+ if (desY > mScreenHeight - getHeight()) {
+ desY = mScreenHeight - getHeight();
+ }
+
+ // 计算新的X位置
+ float desX = mOriginalX + event.getRawX() - mOriginalRawX;
+
+ // 限制X位置不超出屏幕边界
+ if (desX < 0) {
+ desX = 0;
+ }
+ if (desX > mScreenWidth - getWidth()) {
+ desX = mScreenWidth - getWidth();
+ }
+
+ // 设置视图的新位置
+ setX(desX);
+ setY(desY);
+ }
+ private void changeOriginalTouchParams(MotionEvent event) {
+ mOriginalX = getX();//getX()相对于控件X坐标的距离
+ mOriginalY = getY();
+ mOriginalRawX = event.getRawX();//getRawX()指控件在屏幕上的X坐标
+ mOriginalRawY = event.getRawY();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (event == null) {
+ return false;
+ }
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ changeOriginalTouchParams(event);
+ updateSize(); // 添加这行确保尺寸是最新的
+ // ... 其他现有代码 ...
+
+ break;
+ case MotionEvent.ACTION_MOVE:
+
+ updateViewPosition(event); // 使用更新后的带边界检查的方法
+
+// setBackgroundResource(R.drawable.bg_home_drop_view);
+ // 使用屏幕绝对坐标计算新位置
+// float newX = originalX + (event.getRawX() - X1);
+// float newY = originalY + (event.getRawY() - Y1);
+//
+// // 限制X和Y坐标在屏幕范围内
+// newX = Math.max(0, Math.min(newX, screenWidth - getWidth()));
+// newY = Math.max(0, Math.min(newY, screenHeight - getHeight()));
+//
+// setTranslationX(newX);
+// setTranslationY(newY);
+// X2 = event.getRawX();
+ break;
+ case MotionEvent.ACTION_UP:
+ starDrap = false;
+ int sw = ScreenUtils.getScreenWidth();
+ Log.i(TAG, "onTouchEvent: " + sw + "," + X2);
+ boolean isR = getTranslationX() + getWidth() / 2 >= sw / 2;//贴边方向
+
+ // 获取当前Y坐标
+ float currentY = getTranslationY();
+
+ // 创建X轴和Y轴的动画
+ ObjectAnimator animX = ObjectAnimator.ofFloat(this, "translationX", isR ? sw - getWidth() : 0f).setDuration(200);
+ // Y轴保持当前位置,但确保在屏幕范围内
+ currentY = Math.max(0, Math.min(currentY, screenHeight - getHeight()));
+ ObjectAnimator animY = ObjectAnimator.ofFloat(this, "translationY", currentY).setDuration(200);
+
+ animX.start();
+ animY.start();
+
+ break;
+
+ }
+
+ return true;
+ }
+
+
+ public void doRevealAnimation(View mPuppet, boolean flag) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ int[] vLocation = new int[2];
+ getLocationInWindow(vLocation);
+ int centerX = vLocation[0] + getMeasuredWidth() / 2;
+ int centerY = vLocation[1] + getMeasuredHeight() / 2;
+
+ int height = ScreenUtils.getScreenHeight();
+ int width = ScreenUtils.getScreenWidth();
+ int maxRradius = (int) Math.hypot(height, width);
+ Log.e("hei", maxRradius + "");
+
+ if (flag) {
+ mPuppet.setVisibility(VISIBLE);
+ Animator animator = ViewAnimationUtils.createCircularReveal(mPuppet, centerX, centerY, maxRradius, 0);
+ animator.setDuration(600);
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mPuppet.setVisibility(View.GONE);
+ }
+ });
+ animator.start();
+ flag = false;
+ } else {
+ Animator animator = ViewAnimationUtils.createCircularReveal(mPuppet, centerX, centerY, 0, maxRradius);
+ animator.setDuration(1000);
+ animator.addListener(new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mPuppet.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+
+ }
+ });
+ animator.start();
+ flag = true;
+ }
+ }
+ }
+
+
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/widget/DropViewRoom.java b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/DropViewRoom.java
new file mode 100644
index 00000000..2dd470b3
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/DropViewRoom.java
@@ -0,0 +1,184 @@
+package com.xscm.moduleutil.widget;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewAnimationUtils;
+import android.widget.LinearLayout;
+
+import com.blankj.utilcode.util.ScreenUtils;
+
+public class DropViewRoom extends LinearLayout {
+
+ private int rightMargin = 0;
+ private float lastX, lastY;
+ private int screenWidth;
+
+ public DropViewRoom(Context context) {
+ super(context);
+ init();
+ }
+
+ public DropViewRoom(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public DropViewRoom(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+ }
+
+
+ void init() {
+
+ post(new Runnable() {
+ @Override
+ public void run() {
+ //设置初始位置
+ int sh = ScreenUtils.getScreenHeight();
+ int sw = ScreenUtils.getScreenWidth()-100;
+// setBackgroundResource(R.drawable.bg_home_drop_view);
+ int y = (int) (0.5f * sh) - getHeight();
+ int x = sw - getWidth();
+ setTranslationX(x);
+ setTranslationY(y);
+ }
+ });
+ }
+
+
+ boolean starDrap = false;
+ float X1;
+ float X2;
+ float Y1;
+ float Y2;
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ if (starDrap) return true;
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ X1 = event.getX();
+ Y1 = event.getY();
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ X2 = event.getX();//当手指抬起时,再次获取屏幕位置的X值
+ Y2 = event.getY();//同理
+ Action(X1, X2, Y1, Y2);
+ break;
+
+
+ }
+ return starDrap;
+ }
+
+ String TAG = "DropView";
+
+ public boolean Action(float X1, float X2, float Y1, float Y2) {
+ float ComparedX = X2 - X1;//第二次的X坐标的位置减去第一次X坐标的位置,代表X坐标上的变化情况
+ float ComparedY = Y2 - Y1;//同理
+ //当X坐标的变化量的绝对值大于Y坐标的变化量的绝对值,以X坐标的变化情况作为判断依据
+ //上下左右的判断,都在一条直线上,但手指的操作不可能划直线,所有选择变化量大的方向上的量
+ //作为判断依据
+ if (Math.abs(ComparedX) > 30 || Math.abs(ComparedY) > 30) {
+ Log.i(TAG, "Action: 拖动");
+ starDrap = true;
+// setBackgroundResource(R.drawable.bg_home_drop_view);
+ return true;
+ } else {
+ starDrap = false;
+ return false;
+ }
+ }
+
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ switch (event.getAction()) {
+
+ case MotionEvent.ACTION_MOVE:
+// setBackgroundResource(R.drawable.bg_home_drop_view);
+ setTranslationX(getX() + (event.getX() - X1));
+ setTranslationY(getY() + (event.getY() - Y1));
+ X2 = event.getX();
+ break;
+ case MotionEvent.ACTION_UP:
+ starDrap = false;
+ int sw = ScreenUtils.getScreenWidth();
+ Log.i(TAG, "onTouchEvent: " + sw + "," + X2);
+ boolean isR = getTranslationX() + getWidth() / 2 >= sw / 2;//贴边方向
+ ObjectAnimator anim = ObjectAnimator.ofFloat(this, "translationX", isR ? sw - getWidth()+10 : 0f).setDuration(200);
+ anim.start();
+
+ break;
+
+ }
+
+ return true;
+ }
+
+
+ public void doRevealAnimation(View mPuppet, boolean flag) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ int[] vLocation = new int[2];
+ getLocationInWindow(vLocation);
+ int centerX = vLocation[0] + getMeasuredWidth() / 2;
+ int centerY = vLocation[1] + getMeasuredHeight() / 2;
+
+ int height = ScreenUtils.getScreenHeight();
+ int width = ScreenUtils.getScreenWidth();
+ int maxRradius = (int) Math.hypot(height, width);
+ Log.e("hei", maxRradius + "");
+
+ if (flag) {
+ mPuppet.setVisibility(VISIBLE);
+ Animator animator = ViewAnimationUtils.createCircularReveal(mPuppet, centerX, centerY, maxRradius, 0);
+ animator.setDuration(600);
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mPuppet.setVisibility(View.GONE);
+ }
+ });
+ animator.start();
+ flag = false;
+ } else {
+ Animator animator = ViewAnimationUtils.createCircularReveal(mPuppet, centerX, centerY, 0, maxRradius);
+ animator.setDuration(1000);
+ animator.addListener(new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mPuppet.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+
+ }
+ });
+ animator.start();
+ flag = true;
+ }
+ }
+ }
+
+
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/widget/EqualSpaceItemDecoration.java b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/EqualSpaceItemDecoration.java
new file mode 100644
index 00000000..234425ec
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/EqualSpaceItemDecoration.java
@@ -0,0 +1,51 @@
+package com.xscm.moduleutil.widget;
+
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * @Author lxj$
+ * @Time $ 2025-9-4 20:32:52$
+ * @Description 自定义的,让在item平分屏幕宽度$
+ */
+public class EqualSpaceItemDecoration extends RecyclerView.ItemDecoration {
+ private int spanCount;
+ private int spacing;
+
+ public EqualSpaceItemDecoration(int spanCount, int spacing) {
+ this.spanCount = spanCount;
+ this.spacing = spacing;
+ }
+
+ @Override
+ public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
+ int position = parent.getChildAdapterPosition(view);
+
+ // 设置间距
+ outRect.left = spacing / 2;
+ outRect.right = spacing / 2;
+ }
+
+ @Override
+ public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
+ super.onDraw(c, parent, state);
+
+ // 确保每个item宽度为屏幕的1/3
+ int childCount = parent.getChildCount();
+ int screenWidth = parent.getWidth();
+ int itemWidth = screenWidth / spanCount;
+
+ for (int i = 0; i < childCount; i++) {
+ View child = parent.getChildAt(i);
+ ViewGroup.LayoutParams params = child.getLayoutParams();
+ params.width = itemWidth;
+ child.setLayoutParams(params);
+ }
+ }
+}
+
+
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/widget/FakeNinePatchDrawable.kt b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/FakeNinePatchDrawable.kt
new file mode 100644
index 00000000..d102ad51
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/FakeNinePatchDrawable.kt
@@ -0,0 +1,96 @@
+package com.xscm.modulemain
+
+import android.graphics.*
+import android.graphics.drawable.Drawable
+
+/**
+ * 这是设置图片设置拉伸区域
+ */
+class FakeNinePatchDrawable(
+ private val bitmap: Bitmap,
+ // 定义拉伸区域的坐标(相对图片的百分比,范围 0~1)
+ private val left: Float, // 左边界(左侧不拉伸区域的宽度比例)
+ private val top: Float, // 上边界(上侧不拉伸区域的高度比例)
+ private val right: Float, // 右边界(右侧不拉伸区域的宽度比例)
+ private val bottom: Float // 下边界(下侧不拉伸区域的高度比例)
+) : Drawable() {
+
+ private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
+ private val rect = Rect() // 绘制区域
+
+ override fun draw(canvas: Canvas) {
+ val bounds = bounds // 视图的实际尺寸(需要填充的区域)
+ val w = bitmap.width
+ val h = bitmap.height
+
+ // 计算分割点(像素值)
+ val splitLeft = (w * left).toInt()
+ val splitTop = (h * top).toInt()
+ val splitRight = (w * right).toInt()
+ val splitBottom = (h * bottom).toInt()
+
+ // 1. 绘制四个角(不拉伸)
+ // 左上
+ rect.set(0, 0, splitLeft, splitTop)
+ canvas.drawBitmap(bitmap, Rect(0, 0, splitLeft, splitTop), rect, paint)
+ // 右上
+ rect.set(bounds.width() - (w - splitRight), 0, bounds.width(), splitTop)
+ canvas.drawBitmap(bitmap, Rect(splitRight, 0, w, splitTop), rect, paint)
+ // 左下
+ rect.set(0, bounds.height() - (h - splitBottom), splitLeft, bounds.height())
+ canvas.drawBitmap(bitmap, Rect(0, splitBottom, splitLeft, h), rect, paint)
+ // 右下
+ rect.set(
+ bounds.width() - (w - splitRight),
+ bounds.height() - (h - splitBottom),
+ bounds.width(),
+ bounds.height()
+ )
+ canvas.drawBitmap(bitmap, Rect(splitRight, splitBottom, w, h), rect, paint)
+
+ // 2. 绘制四条边(单向拉伸)
+ // 上边(水平拉伸)
+ rect.set(splitLeft, 0, bounds.width() - (w - splitRight), splitTop)
+ canvas.drawBitmap(bitmap, Rect(splitLeft, 0, splitRight, splitTop), rect, paint)
+ // 下边(水平拉伸)
+ rect.set(
+ splitLeft,
+ bounds.height() - (h - splitBottom),
+ bounds.width() - (w - splitRight),
+ bounds.height()
+ )
+ canvas.drawBitmap(bitmap, Rect(splitLeft, splitBottom, splitRight, h), rect, paint)
+ // 左边(垂直拉伸)
+ rect.set(0, splitTop, splitLeft, bounds.height() - (h - splitBottom))
+ canvas.drawBitmap(bitmap, Rect(0, splitTop, splitLeft, splitBottom), rect, paint)
+ // 右边(垂直拉伸)
+ rect.set(
+ bounds.width() - (w - splitRight),
+ splitTop,
+ bounds.width(),
+ bounds.height() - (h - splitBottom)
+ )
+ canvas.drawBitmap(bitmap, Rect(splitRight, splitTop, w, splitBottom), rect, paint)
+
+ // 3. 绘制中间区域(双向拉伸)
+ rect.set(
+ splitLeft,
+ splitTop,
+ bounds.width() - (w - splitRight),
+ bounds.height() - (h - splitBottom)
+ )
+ canvas.drawBitmap(bitmap, Rect(splitLeft, splitTop, splitRight, splitBottom), rect, paint)
+ }
+
+ override fun setAlpha(alpha: Int) {
+ paint.alpha = alpha
+ invalidateSelf()
+ }
+
+ override fun getOpacity(): Int = PixelFormat.TRANSLUCENT
+ override fun getAlpha(): Int = paint.alpha
+ override fun setColorFilter(colorFilter: ColorFilter?) {
+ paint.colorFilter = colorFilter
+ invalidateSelf()
+ }
+}
\ No newline at end of file
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/widget/GiftAnimView.java b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/GiftAnimView.java
new file mode 100644
index 00000000..eecb70e2
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/GiftAnimView.java
@@ -0,0 +1,513 @@
+package com.xscm.moduleutil.widget;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.blankj.utilcode.util.LogUtils;
+import com.opensource.svgaplayer.SVGACallback;
+import com.opensource.svgaplayer.SVGADrawable;
+import com.opensource.svgaplayer.SVGAImageView;
+import com.opensource.svgaplayer.SVGAParser;
+import com.opensource.svgaplayer.SVGAVideoEntity;
+import com.tencent.qgame.animplayer.AnimConfig;
+import com.tencent.qgame.animplayer.AnimView;
+import com.tencent.qgame.animplayer.inter.IAnimListener;
+import com.xscm.moduleutil.bean.GiftBean;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.locks.ReentrantLock;
+
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+
+public class GiftAnimView extends FrameLayout implements GiftSvgaView.OnAnimationListener {
+ private AnimView playerMp4View;
+ private GiftSvgaView svgaView;
+ private GiftBean playModel;
+ private boolean isLoadEffect = false;
+ private boolean isShow = true;//是否开启特效
+ private ReentrantLock lock = new ReentrantLock();
+ private List giftArray = new ArrayList<>();
+ public ExecutorService queue = Executors.newSingleThreadExecutor();
+ private Context mContext;
+ private boolean isOnece;
+ // 添加带Context参数的构造函数
+
+ // 添加带Context和AttributeSet参数的构造函数(解决XML inflate问题的关键)
+ public GiftAnimView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ this.mContext = context;
+ init();
+ }
+
+ // 添加带Context、AttributeSet和 defStyleAttr 参数的构造函数(更完整的实现)
+ public GiftAnimView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ this.mContext = context;
+ init();
+ }
+ public void setQueue(ExecutorService queue) {
+ this.queue = queue;
+ }
+
+ public GiftAnimView(Context context) {
+ super(context);
+ this.mContext = context;
+ init();
+ }
+
+ private void init() {
+ isLoadEffect = false;
+
+ // 初始化SVGA视图
+ svgaView = new GiftSvgaView(getContext());
+ addView(svgaView);
+
+ playerMp4View = new AnimView(getContext());
+
+ addView(playerMp4View);
+
+ // 设置布局参数 - 在Android中通常使用LayoutParams
+ LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+ playerMp4View.setLoop(1);
+ playerMp4View.setLayoutParams(params);
+ playerMp4View.setAnimListener(new IAnimListener() {
+ @Override
+ public void onVideoDestroy() {
+ LogUtils.e("onVideoDestroy");
+ }
+
+ @Override
+ public void onVideoComplete() {
+ LogUtils.e("onVideoComplete");
+ post(() -> {
+ if (playerMp4View!=null) {
+ playerMp4View.setVisibility(View.GONE);
+ if (isOnece){
+ return;
+ }
+ // 通知播放完成
+ notifyPlaybackComplete();
+ loadStartSVGAPlayer();
+ }
+ });
+
+ }
+
+ @Override
+ public void onVideoRender(int i, @Nullable AnimConfig animConfig) {
+// LogUtils.e("onVideoRender", i, animConfig);
+ }
+
+ @Override
+ public void onVideoStart() {
+ LogUtils.e("onVideoStart");
+ }
+
+ @Override
+ public boolean onVideoConfigReady(@NonNull AnimConfig animConfig) {
+ LogUtils.e("onVideoConfigReady", animConfig);
+ return true;
+ }
+
+ @Override
+ public void onFailed(int i, @Nullable String s) {
+ LogUtils.e("onFailed", s);
+ }
+ });
+ svgaView.setDidFinishedDisplay(this);
+ svgaView.setDidStartAnimation(this);
+ }
+
+ public void displayEffectView1(List stringList){
+ queue.execute(new Runnable() {
+ @Override
+ public void run() {
+ /// 如果play_image不存在return
+ if (stringList == null || stringList.isEmpty()) {
+ return;
+ }
+
+
+ /// 锁住list
+ lock.lock();
+ try {
+ /// 添加礼物进list
+ giftArray.addAll(stringList);
+
+ /// 如果没有在加载则开始加载
+ if (!isLoadEffect) {
+ /// 更改加载状态标记
+ isLoadEffect = true;
+ loadStartSVGAPlayer();
+ }
+ } finally {
+ /// 解锁
+ lock.unlock();
+ }
+ }
+ });
+ }
+
+ /// 礼物特效进来 gift为礼物实体类
+ public void displayEffectView(final String gift) {
+ queue.execute(new Runnable() {
+ @Override
+ public void run() {
+ /// 如果play_image不存在return
+ if (gift == null || gift.isEmpty()) {
+ return;
+ }
+
+ /// 将play_image链接转为小写
+ String playImage = gift;
+ String pathExtension = getFileExtension(playImage).toLowerCase();
+
+ /// 判定礼物的后缀是否为svga或者mp4如果非这两种 则return
+ if (!("svga".equals(pathExtension) || "mp4".equals(pathExtension))) {
+ return;
+ }
+
+ /// 锁住list
+ lock.lock();
+ try {
+ /// 添加礼物进list
+ giftArray.add(gift);
+
+ /// 如果没有在加载则开始加载
+ if (!isLoadEffect) {
+ /// 更改加载状态标记
+ isLoadEffect = true;
+ loadStartSVGAPlayer();
+ }
+ } finally {
+ /// 解锁
+ lock.unlock();
+ }
+ }
+ });
+ }
+
+ public void previewEffectWith(String playImage){
+ this.isOnece=true;
+ if (playImage.endsWith("mp4")) {
+ downloadAndPlay(getContext(), playImage, new DownloadCallback() {
+ @Override
+ public void onSuccess(File file) {
+ post(() -> {
+ playerMp4View.setVisibility(View.VISIBLE);
+ svgaView.setVisibility(View.GONE);
+ playerMp4View.startPlay(file);
+ });
+ }
+
+ @Override
+ public void onFailure(Exception e) {
+ LogUtils.e("MP4下载或播放失败: " + e.getMessage());
+ // 处理失败情况,继续播放下一个
+ }
+ });
+ } else if (playImage.endsWith("svga")) {
+// File file = downloadAndPlay(getContext(), playImage);
+ post(() -> {
+ playerMp4View.setVisibility(View.GONE);
+ svgaView.setVisibility(View.VISIBLE);
+ svgaView.loadSVGAPlayerWith(playImage, false);
+ });
+ }
+ }
+
+ public void openOrCloseEffectViewWith(boolean isShow) {
+ this.isShow = isShow;
+ removeSvgaQueueData();
+
+ playerMp4View.stopPlay();
+ playerMp4View.setVisibility(View.GONE);
+ setVisibility(isShow ? View.VISIBLE : View.GONE);
+ }
+
+ public void stopPlay() {
+ removeSvgaQueueData();
+
+ playerMp4View.stopPlay();
+ playerMp4View.setVisibility(View.GONE);
+ }
+
+
+ private void loadStartSVGAPlayer() {
+ if (!isShow) {
+ /// isshow 为是否开启特效 如果未开启则return
+ return;
+ }
+
+ String giftModel = null;
+
+ /// list加锁
+ lock.lock();
+ try {
+ /// 如果list长度大于0
+ if (!giftArray.isEmpty()) {
+ /// gift的实体类则赋值,取list中的第一个数据
+ giftModel = giftArray.get(0);
+ /// 移除list的第一条数据
+ giftArray.remove(0);
+ isLoadEffect = true;
+ } else {
+ isLoadEffect = false;
+ }
+ } finally {
+ /// 解锁
+ lock.unlock();
+ }
+
+ if (isLoadEffect && giftModel != null && !TextUtils.isEmpty(giftModel)) {
+ String finalGiftModel = giftModel;
+ post(new Runnable() {
+ @Override
+ public void run() {
+ String playImage = finalGiftModel;
+ if (playImage.endsWith("mp4")) {
+ downloadAndPlay(getContext(), playImage, new DownloadCallback() {
+ @Override
+ public void onSuccess(File file) {
+ post(() -> {
+ playerMp4View.setVisibility(View.VISIBLE);
+ svgaView.setVisibility(View.GONE);
+ playerMp4View.startPlay(file);
+ });
+ }
+
+ @Override
+ public void onFailure(Exception e) {
+ LogUtils.e("MP4下载或播放失败: " + e.getMessage());
+ // 处理失败情况,继续播放下一个
+ post(() -> {
+ lock.lock();
+ try {
+ isLoadEffect = false;
+ } finally {
+ lock.unlock();
+ }
+ loadStartSVGAPlayer();
+ });
+ }
+ });
+ } else if (playImage.endsWith("svga")) {
+// File file = downloadAndPlay(getContext(), playImage);
+ post(() -> {
+ playerMp4View.setVisibility(View.GONE);
+ svgaView.setVisibility(View.VISIBLE);
+ svgaView.loadSVGAPlayerWith(finalGiftModel, false);
+ });
+ } else {
+ lock.lock();
+ try {
+ isLoadEffect = false;
+ } finally {
+ lock.unlock();
+ }
+ loadStartSVGAPlayer();
+ // 直接播放缓存文件
+ }
+
+ }
+ });
+ }
+ }
+
+
+ public void downloadAndPlay(Context context, String playImage, DownloadCallback callback) {
+ String fileName = playImage.substring(playImage.lastIndexOf("/"));
+ String filePath = context.getCacheDir().getAbsolutePath() + fileName;
+
+ LogUtils.e("@@@@@filePath: " + filePath.toString());
+
+ File file = new File(filePath);
+
+ if (!file.exists()) {
+ LogUtils.e("无缓存");
+ // 使用OkHttp进行下载
+ OkHttpClient client = new OkHttpClient();
+ Request request = new Request.Builder()
+ .url(playImage)
+ .build();
+
+ client.newCall(request).enqueue(new Callback() {
+ @Override
+ public void onFailure(Call call, IOException e) {
+ LogUtils.e("MP4下载失败: " + e.toString());
+ // 在主线程中回调失败
+ post(() -> {
+ if (callback != null) {
+ callback.onFailure(e);
+ }
+ });
+ }
+
+ @Override
+ public void onResponse(Call call, Response response) throws IOException {
+ if (response.isSuccessful()) {
+ try (ResponseBody responseBody = response.body()) {
+ if (responseBody != null) {
+ File downloadedFile = new File(filePath);
+ FileOutputStream fos = new FileOutputStream(downloadedFile);
+ fos.write(responseBody.bytes());
+ fos.close();
+
+ // 在主线程中回调成功
+ post(() -> {
+ if (callback != null) {
+ callback.onSuccess(downloadedFile);
+ }
+ });
+ } else {
+ // 在主线程中回调失败
+ post(() -> {
+ if (callback != null) {
+ callback.onFailure(new IOException("Response body is null"));
+ }
+ });
+ }
+ } catch (Exception e) {
+ LogUtils.e("MP4文件保存失败: " + e.getMessage());
+ // 在主线程中回调失败
+ post(() -> {
+ if (callback != null) {
+ callback.onFailure(e);
+ }
+ });
+ }
+ } else {
+ LogUtils.e("MP4下载响应失败");
+ // 在主线程中回调失败
+ post(() -> {
+ if (callback != null) {
+ callback.onFailure(new IOException("Response not successful: " + response.code()));
+ }
+ });
+ }
+ }
+ });
+ } else {
+ // 文件已存在,直接回调成功
+ if (callback != null) {
+ callback.onSuccess(file);
+ }
+ }
+ }
+
+ // 添加回调接口
+ public interface DownloadCallback {
+ void onSuccess(File file);
+
+ void onFailure(Exception e);
+ }
+
+
+ public void destroyEffectView() {
+ removeSvgaQueueData();
+ svgaView.stopEffectSvgaPlay();
+ playerMp4View.stopPlay();
+ // 清理监听器
+ clearPlaybackCompleteListeners();
+ removeView(playerMp4View);
+ removeView(svgaView);
+ svgaView = null;
+ playerMp4View = null;
+ playModel = null;
+
+ if (queue != null) {
+ queue.shutdown();
+ queue = null;
+ }
+ }
+
+ private void removeSvgaQueueData() {
+ lock.lock();
+ try {
+ giftArray.clear();
+ isLoadEffect = false;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ private String getFileExtension(String url) {
+ if (url != null && url.contains(".")) {
+ return url.substring(url.lastIndexOf(".") + 1);
+ }
+ return "";
+ }
+
+ @Override
+ public void onStartAnimation(GiftSvgaView view) {
+
+ }
+
+ @Override
+ public void onFinishedDisplay(GiftSvgaView view) {
+ post(() -> {
+ if (svgaView!=null) {
+ svgaView.setVisibility(View.GONE);
+ }
+ if (isOnece){
+ return;
+ }
+ // 通知播放完成
+ notifyPlaybackComplete();
+ loadStartSVGAPlayer();
+ });
+ }
+
+ // 在 GiftAnimView 类中添加播放完成监听接口
+ public interface OnPlaybackCompleteListener {
+ void onPlaybackComplete();
+ }
+
+ // 在 GiftAnimView 类中添加成员变量
+ private List playbackCompleteListeners = new ArrayList<>();
+
+ // 在 GiftAnimView 类中添加监听器管理方法
+ public void addOnPlaybackCompleteListener(OnPlaybackCompleteListener listener) {
+ if (listener != null && !playbackCompleteListeners.contains(listener)) {
+ playbackCompleteListeners.add(listener);
+ }
+ }
+
+ public void removeOnPlaybackCompleteListener(OnPlaybackCompleteListener listener) {
+ if (listener != null) {
+ playbackCompleteListeners.remove(listener);
+ }
+ }
+
+ public void clearPlaybackCompleteListeners() {
+ playbackCompleteListeners.clear();
+ }
+
+ // 触发播放完成回调的方法
+ private void notifyPlaybackComplete() {
+ for (OnPlaybackCompleteListener listener : new ArrayList<>(playbackCompleteListeners)) {
+ if (listener != null) {
+ listener.onPlaybackComplete();
+ }
+ }
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/widget/GiftCardView.java b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/GiftCardView.java
new file mode 100644
index 00000000..1534c06b
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/GiftCardView.java
@@ -0,0 +1,306 @@
+package com.xscm.moduleutil.widget;
+
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.LayerDrawable;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.core.content.ContextCompat;
+
+import com.google.android.material.card.MaterialCardView;
+import com.xscm.moduleutil.R;
+import com.xscm.moduleutil.bean.GiftBean;
+import com.xscm.moduleutil.utils.ImageUtils;
+
+/**
+ * 礼物卡片控件 - 支持选中效果和自定义样式
+ */
+public class GiftCardView extends FrameLayout {
+
+ private ImageView mIconImageView;
+ private TextView mNameTextView;
+ private TextView mCountTextView;
+ private TextView mResultTextView;
+
+ private boolean isSelected = false;
+ private Drawable selectedBackground;
+ private Drawable normalBackground;
+
+ // 添加GiftBean数据引用
+ private GiftBean giftBean;
+
+ private ObjectAnimator pulseAnimator;
+
+ public GiftCardView(Context context) {
+ this(context, null);
+ }
+
+ public GiftCardView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public GiftCardView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initViews();
+ initAttributes(attrs);
+ }
+
+ private void initViews() {
+ LayoutInflater.from(getContext()).inflate(R.layout.view_gift_card, this, true);
+
+ mIconImageView = findViewById(R.id.img_gift_icon);
+ mNameTextView = findViewById(R.id.tv_gift_name);
+ mCountTextView = findViewById(R.id.tv_gift_count);
+ mResultTextView= findViewById(R.id.result);
+
+ // 设置默认点击事件
+ setOnClickListener(v -> {
+ if (getOnGiftClickListener() != null) {
+ getOnGiftClickListener().onGiftClick(this);
+ }
+ });
+ }
+
+ private void initAttributes(AttributeSet attrs) {
+ TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.GiftCardView);
+
+ try {
+ // 获取自定义属性
+ String name = typedArray.getString(R.styleable.GiftCardView_giftName);
+ String count = typedArray.getString(R.styleable.GiftCardView_giftCount);
+ int iconResId = typedArray.getResourceId(R.styleable.GiftCardView_giftIcon, 0);
+
+ // 设置默认值
+ if (name != null) {
+ setName(name);
+ }
+ if (count != null) {
+ setCount(count);
+ }
+ if (iconResId != 0) {
+ setIcon("");
+ }
+
+ // 获取选中状态相关属性
+ selectedBackground = typedArray.getDrawable(R.styleable.GiftCardView_selectedBackground);
+ normalBackground = typedArray.getDrawable(R.styleable.GiftCardView_normalBackground);
+
+ // 如果没有设置选中背景,则使用默认的选中效果
+// if (selectedBackground == null) {
+// selectedBackground = ContextCompat.getDrawable(getContext(), R.mipmap.tkzj_x);
+// }
+// if (normalBackground == null) {
+// normalBackground = ContextCompat.getDrawable(getContext(), R.mipmap.tkzj_w);
+// }
+
+ // 设置默认背景
+ if (normalBackground != null) {
+ setBackground(normalBackground);
+ }
+
+ } finally {
+ typedArray.recycle();
+ }
+ }
+ /**
+ * 绑定GiftBean数据
+ */
+ public void bindGiftData(GiftBean giftBean) {
+ this.giftBean = giftBean;
+
+ if (giftBean != null) {
+ // 设置礼物图标
+ if (giftBean.getBase_image() != null && !giftBean.getBase_image().isEmpty()) {
+ setIcon(giftBean.getBase_image());
+ // 如果是资源ID
+
+ } else {
+ setIcon("");
+ }
+
+ // 设置礼物名称
+ setName(giftBean.getGift_name() != null ? giftBean.getGift_name() : "未知礼物");
+
+ // 设置礼物数量
+ setCount(giftBean.getGift_price() != null ? giftBean.getGift_price() : "0");
+ setmResultTextView(giftBean.getCount());
+ }
+ }
+
+ /**
+ * 获取绑定的GiftBean数据
+ */
+ public GiftBean getGiftBean() {
+ return giftBean;
+ }
+ /**
+ * 设置礼物图标(URL)
+ */
+ public void setIcon(String url) {
+ if (mIconImageView != null) {
+ if (url != null && !url.isEmpty()) {
+ ImageUtils.loadHeadCC(url, mIconImageView);
+ } else {
+ mIconImageView.setImageResource(R.mipmap.ic_launcher);
+ }
+ }
+ }
+ private android.animation.AnimatorSet animatorSet;
+
+ public void startPulseAnimationWithLayer() {
+ if (pulseAnimator != null && pulseAnimator.isRunning()) {
+ pulseAnimator.cancel();
+ }
+ pulseAnimator = ObjectAnimator.ofFloat(this, "scaleX", 0.9f, 1.1f);
+ pulseAnimator.setDuration(500);
+ pulseAnimator.setRepeatCount(ObjectAnimator.INFINITE);
+ pulseAnimator.setRepeatMode(ObjectAnimator.REVERSE);
+ ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(this, "scaleY", 0.9f, 1.1f);
+ scaleYAnimator.setDuration(500);
+ scaleYAnimator.setRepeatCount(ObjectAnimator.INFINITE);
+ scaleYAnimator.setRepeatMode(ObjectAnimator.REVERSE);
+
+ // 组合动画
+ animatorSet = new android.animation.AnimatorSet();
+ animatorSet.playTogether(pulseAnimator, scaleYAnimator);
+ animatorSet.start();
+ }
+
+ public void stopPulseAnimationWithLayer() {
+ if (pulseAnimator != null) {
+ pulseAnimator.cancel();
+ }
+
+ // 停止组合动画
+ if (animatorSet != null && animatorSet.isRunning()) {
+ animatorSet.cancel();
+ }
+
+ // 清除引用
+ animatorSet = null;
+ pulseAnimator = null;
+
+ // 重置缩放
+ this.setScaleX(1f);
+ this.setScaleY(1f);
+
+ // 强制重新绘制
+ this.invalidate();
+ }
+ /**
+ * 设置礼物名称
+ */
+ public void setName(String name) {
+ if (mNameTextView != null) {
+ mNameTextView.setText(name);
+ }
+ }
+
+ /**
+ * 设置礼物数量
+ */
+ public void setCount(String count) {
+ if (mCountTextView != null) {
+ mCountTextView.setText(count);
+ }
+ }
+
+ /**
+ * 设置选中状态
+ */
+ public void setSelected(boolean selected) {
+ isSelected = selected;
+
+ if (selected) {
+ setBackground(selectedBackground);
+ // 可以添加额外的选中效果
+// setElevation(8f); // 提高阴影
+ // 添加发光效果
+// addGlowEffect();
+ } else {
+ setBackground(normalBackground);
+// setElevation(4f); // 恢复默认阴影
+// removeGlowEffect();
+ }
+ }
+
+ public void setmResultTextView(int num){
+ if (mResultTextView!=null) {
+ mResultTextView.setText("x" + num);
+ }
+ }
+
+ public void setVisibilitymResultTextView(boolean isVisible){
+ if (mResultTextView!=null) {
+ mResultTextView.setVisibility(isVisible?View.VISIBLE:View.GONE);
+ }
+ }
+
+ /**
+ * 添加发光效果
+ */
+ private void addGlowEffect() {
+ // 使用LayerDrawable创建发光效果
+ GradientDrawable glow = new GradientDrawable();
+ glow.setColor(Color.TRANSPARENT);
+ glow.setStroke(8, Color.argb(150, 255, 255, 255));
+ glow.setCornerRadius(16f);
+
+ LayerDrawable layerDrawable = new LayerDrawable(new Drawable[]{glow, getBackground()});
+ setBackground(layerDrawable);
+ }
+
+ /**
+ * 移除发光效果
+ */
+ private void removeGlowEffect() {
+ setBackground(normalBackground);
+ }
+
+ /**
+ * 获取当前是否选中
+ */
+ public boolean isSelected() {
+ return isSelected;
+ }
+
+ /**
+ * 切换选中状态
+ */
+ public void toggleSelected() {
+ setSelected(!isSelected);
+ }
+
+ /**
+ * 设置点击监听器
+ */
+ public void setOnGiftClickListener(OnGiftClickListener listener) {
+ this.onGiftClickListener = listener;
+ }
+
+ /**
+ * 获取点击监听器
+ */
+ public OnGiftClickListener getOnGiftClickListener() {
+ return onGiftClickListener;
+ }
+
+ /**
+ * 点击监听器接口
+ */
+ public interface OnGiftClickListener {
+ void onGiftClick(GiftCardView giftCardView);
+ }
+
+ private OnGiftClickListener onGiftClickListener;
+}
+
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/widget/GiftSvgaView.java b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/GiftSvgaView.java
new file mode 100644
index 00000000..ab999753
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/GiftSvgaView.java
@@ -0,0 +1,285 @@
+package com.xscm.moduleutil.widget;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.opensource.svgaplayer.SVGACallback;
+import com.opensource.svgaplayer.SVGADrawable;
+import com.opensource.svgaplayer.SVGAImageView;
+import com.opensource.svgaplayer.SVGAParser;
+import com.opensource.svgaplayer.SVGAVideoEntity;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+public class GiftSvgaView extends FrameLayout implements SVGACallback {
+ private SVGAImageView player;
+ private SVGAParser parser;
+ private boolean isAutoPlay = true;
+
+ // 回调接口
+ public interface OnAnimationListener {
+ void onStartAnimation(GiftSvgaView view);
+ void onFinishedDisplay(GiftSvgaView view);
+ }
+
+ private OnAnimationListener didStartAnimation;
+ private OnAnimationListener didFinishedDisplay;
+
+ public GiftSvgaView(Context context) {
+ this(context, null);
+ }
+
+ public GiftSvgaView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public GiftSvgaView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initializeData(true);
+ }
+
+ public GiftSvgaView(Context context, boolean isAutoPlay) {
+ super(context);
+ initializeData(isAutoPlay);
+ }
+
+ private void initializeData(boolean isAutoPlay) {
+ this.isAutoPlay = isAutoPlay;
+ setBackgroundColor(Color.TRANSPARENT); // clearColor
+ setClickable(false); // userInteractionEnabled = NO
+ initPlayer();
+ }
+
+ private void initPlayer() {
+ player = new SVGAImageView(getContext());
+ player.setBackgroundColor(Color.TRANSPARENT);
+ player.setScaleType(ImageView.ScaleType.CENTER_CROP); // UIViewContentModeScaleAspectFill
+ // 如果需要 ScaleAspectFit,使用 ImageView.ScaleType.FIT_CENTER
+
+ // 设置布局参数 - 填满父视图
+ LayoutParams params = new LayoutParams(
+ LayoutParams.MATCH_PARENT,
+ LayoutParams.MATCH_PARENT
+ );
+ addView(player, params);
+
+ // 初始化解析器
+ parser = new SVGAParser(getContext());
+ player.setCallback(this);
+ }
+
+ public void loadSVGAPlayerWith(String loadPath) {
+ loadSVGAPlayerWith(loadPath, false);
+ }
+
+ public void loadSVGAPlayerWith(String loadPath, boolean inBundle) {
+ loadSVGAPlayerWith(loadPath, inBundle, 1);
+ }
+
+ public void loadSVGAPlayerWith(String loadPath, boolean inBundle, int loop) {
+ if (loadPath == null || loadPath.isEmpty()) {
+ if (didFinishedDisplay != null) {
+ didFinishedDisplay.onFinishedDisplay(this);
+ }
+ return;
+ }
+
+ if (player == null) {
+ initPlayer();
+ }
+
+ stopEffectSvgaPlay();
+ player.setLoops(loop);
+
+ if (loadPath.startsWith("https:") || loadPath.startsWith("http:")) {
+ // URL加载
+ try {
+ parser.parse(new URL(loadPath), new SVGAParser.ParseCompletion() {
+ @Override
+ public void onComplete(@NotNull SVGAVideoEntity videoItem) {
+ SVGADrawable drawable = new SVGADrawable(videoItem);
+ player.setImageDrawable(drawable);
+
+ if (isAutoPlay) {
+ player.startAnimation();
+ if (didStartAnimation != null) {
+ didStartAnimation.onStartAnimation(GiftSvgaView.this);
+ }
+ }
+ }
+
+ @Override
+ public void onError() {
+ if (didFinishedDisplay != null) {
+ didFinishedDisplay.onFinishedDisplay(GiftSvgaView.this);
+ }
+ }
+ });
+ } catch (Exception e) {
+ e.printStackTrace();
+ if (didFinishedDisplay != null) {
+ didFinishedDisplay.onFinishedDisplay(this);
+ }
+ }
+ } else if (inBundle) {
+ // 从Assets加载
+ try {
+ parser.parse(loadPath, new SVGAParser.ParseCompletion() {
+ @Override
+ public void onComplete(@NotNull SVGAVideoEntity videoItem) {
+ SVGADrawable drawable = new SVGADrawable(videoItem);
+ player.setImageDrawable(drawable);
+
+ if (isAutoPlay) {
+ player.startAnimation();
+ if (didStartAnimation != null) {
+ didStartAnimation.onStartAnimation(GiftSvgaView.this);
+ }
+ }
+ }
+
+ @Override
+ public void onError() {
+ if (didFinishedDisplay != null) {
+ didFinishedDisplay.onFinishedDisplay(GiftSvgaView.this);
+ }
+ }
+ });
+ } catch (Exception e) {
+ e.printStackTrace();
+ if (didFinishedDisplay != null) {
+ didFinishedDisplay.onFinishedDisplay(this);
+ }
+ }
+ } else {
+ // 从文件路径加载
+ try {
+ File file = new File(loadPath);
+ if (!file.exists() || file.length() < 4) {
+ if (didFinishedDisplay != null) {
+ didFinishedDisplay.onFinishedDisplay(this);
+ }
+ return;
+ }
+
+ InputStream inputStream = new FileInputStream(file);
+ parser.parse(inputStream, loadPath, new SVGAParser.ParseCompletion() {
+ @Override
+ public void onComplete(@NotNull SVGAVideoEntity videoItem) {
+ SVGADrawable drawable = new SVGADrawable(videoItem);
+ player.setImageDrawable(drawable);
+
+ if (isAutoPlay) {
+ player.startAnimation();
+ if (didStartAnimation != null) {
+ didStartAnimation.onStartAnimation(GiftSvgaView.this);
+ }
+ }
+ }
+
+ @Override
+ public void onError() {
+ if (didFinishedDisplay != null) {
+ didFinishedDisplay.onFinishedDisplay(GiftSvgaView.this);
+ }
+ }
+ }, true);
+ } catch (IOException e) {
+ e.printStackTrace();
+ if (didFinishedDisplay != null) {
+ didFinishedDisplay.onFinishedDisplay(this);
+ }
+ }
+ }
+ }
+
+ // Public方法
+ public void startEffectSvgaPlay() {
+ if (player != null && player.getDrawable() != null) {
+ player.startAnimation();
+ }
+ }
+
+ public void pauseEffectSvgaPlay() {
+ if (player != null) {
+ player.pauseAnimation();
+ }
+ }
+
+ public void stopEffectSvgaPlay() {
+ if (player != null) {
+ player.stopAnimation();
+ player.clearAnimation();
+ player.setImageDrawable( null);
+ }
+ }
+
+ public void destroySvga() {
+ stopEffectSvgaPlay();
+ if (player != null) {
+ removeView(player);
+ player = null;
+ }
+ parser = null;
+ }
+
+ // SVGACallback接口实现
+ @Override
+ public void onPause() {
+ // 暂停回调
+ }
+
+ @Override
+ public void onFinished() {
+ if (didFinishedDisplay != null) {
+ didFinishedDisplay.onFinishedDisplay(this);
+ }
+ }
+
+ @Override
+ public void onRepeat() {
+ // 重复回调
+ }
+
+ @Override
+ public void onStep(int frame, double percentage) {
+ // 步骤回调
+ }
+
+ // Getter和Setter方法
+ public void setDidStartAnimation(OnAnimationListener listener) {
+ this.didStartAnimation = listener;
+ }
+
+ public void setDidFinishedDisplay(OnAnimationListener listener) {
+ this.didFinishedDisplay = listener;
+ if (player != null) {
+ player.setCallback(this);
+ }
+ }
+
+ public boolean isAutoPlay() {
+ return isAutoPlay;
+ }
+
+ public void setAutoPlay(boolean autoPlay) {
+ isAutoPlay = autoPlay;
+ }
+
+ public SVGAImageView getPlayer() {
+ return player;
+ }
+
+ public SVGAParser getParser() {
+ return parser;
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/widget/QXGiftDriftView.java b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/QXGiftDriftView.java
new file mode 100644
index 00000000..8dc87683
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/QXGiftDriftView.java
@@ -0,0 +1,273 @@
+package com.xscm.moduleutil.widget;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.graphics.Color;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.TranslateAnimation;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.xscm.moduleutil.R;
+import com.xscm.moduleutil.event.MqttBean;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class QXGiftDriftView extends ViewGroup {
+
+ private static QXGiftDriftView instance;
+
+ private ImageView bgImageView;
+ private TextView titleLabel;
+ private ImageView giftImageView;
+ private TextView countLabel;
+ private boolean isPlaying=false;
+ private boolean isClose;
+
+ private List dataArray = new ArrayList<>();
+ private MqttBean.ListBean model;
+
+ private Context context;
+ private int screenWidth;
+
+ public static QXGiftDriftView getInstance(Context context) {
+ if (instance == null) {
+ instance = new QXGiftDriftView(context);
+ }
+ return instance;
+ }
+
+ private QXGiftDriftView(Context context) {
+ super(context);
+ this.context = context;
+
+ // 获取屏幕宽度
+ screenWidth = context.getResources().getDisplayMetrics().widthPixels;
+
+ // 初始化视图
+ initSubviews();
+
+ // 从SharedPreferences读取设置
+ SharedPreferences prefs = context.getSharedPreferences("AppPrefs", Context.MODE_PRIVATE);
+ isClose = prefs.getBoolean("kIsCloseDrifPop", false);
+ }
+
+ private void initSubviews() {
+ // 设置视图尺寸
+ int width = scaleWidth(316);
+ int height = scaleWidth(50);
+ setLayoutParams(new LayoutParams(width, height));
+
+ // 背景图片
+ bgImageView = new ImageView(context);
+ bgImageView.setImageResource(R.mipmap.gift_p_b);
+ addView(bgImageView);
+
+ // 标题标签
+ titleLabel = new TextView(context);
+ titleLabel.setTextSize(14);
+ titleLabel.setTextColor(Color.WHITE);
+ addView(titleLabel);
+
+ // 礼物图片
+ giftImageView = new ImageView(context);
+ giftImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
+ addView(giftImageView);
+
+ // 数量标签
+ countLabel = new TextView(context);
+ countLabel.setTextSize(14);
+ countLabel.setTextColor(Color.WHITE);
+ addView(countLabel);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ // 布局子视图
+ int width = getWidth();
+ int height = getHeight();
+
+ // 背景图片填满整个视图
+ bgImageView.layout(0, 0, width, height);
+
+ // 标题标签居中偏左
+ int titleWidth = titleLabel.getMeasuredWidth();
+ int titleHeight = titleLabel.getMeasuredHeight();
+ int titleLeft = (width - titleWidth) / 2 - scaleWidth(30);
+ int titleTop = (height - titleHeight) / 2;
+ titleLabel.layout(titleLeft, titleTop, titleLeft + titleWidth, titleTop + titleHeight);
+
+ // 礼物图片在标题右侧
+ int giftSize = scaleWidth(20);
+ int giftLeft = titleLeft + titleWidth + scaleWidth(5);
+ int giftTop = titleTop + (titleHeight - giftSize) / 2;
+ giftImageView.layout(giftLeft, giftTop, giftLeft + giftSize, giftTop + giftSize);
+
+ // 数量标签在礼物图片右侧
+ int countWidth = countLabel.getMeasuredWidth();
+ int countLeft = giftLeft + giftSize;
+ countLabel.layout(countLeft, titleTop, countLeft + countWidth, titleTop + titleHeight);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int width = scaleWidth(316);
+ int height = scaleWidth(50);
+ setMeasuredDimension(width, height);
+
+ // 测量子视图
+ measureChildren(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ public void addGiftModel(MqttBean.ListBean model) {
+ if (isClose) {
+ return;
+ }
+ dataArray.add(model);
+ giftAction();
+ }
+
+ public void addGiftModelList(List list) {
+ dataArray.addAll(list);
+ giftAction();
+ }
+
+ private void giftAction() {
+ if (isPlaying) {
+ return;
+ }
+
+ if (dataArray.isEmpty()) {
+ return;
+ }
+
+ isPlaying = true;
+ model = dataArray.get(0);
+
+ // 添加到窗口(这里需要根据实际情况获取根布局)
+ ViewGroup rootView = (ViewGroup) ((Activity) context).getWindow().getDecorView();
+ rootView.addView(QXGiftDriftView.getInstance( context));
+
+ // 设置初始位置(屏幕右侧外)
+ setX(screenWidth);
+
+ // 进入动画
+ TranslateAnimation enterAnim = new TranslateAnimation(
+ Animation.ABSOLUTE, screenWidth,
+ Animation.ABSOLUTE, (screenWidth - scaleWidth(316)) / 2,
+ Animation.ABSOLUTE, 0,
+ Animation.ABSOLUTE, 0
+ );
+ enterAnim.setDuration(1500);
+ enterAnim.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {}
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ // 停留后退出
+ postDelayed(() -> {
+ TranslateAnimation exitAnim = new TranslateAnimation(
+ Animation.ABSOLUTE, (screenWidth - scaleWidth(316)) / 2,
+ Animation.ABSOLUTE, -screenWidth,
+ Animation.ABSOLUTE, 0,
+ Animation.ABSOLUTE, 0
+ );
+ exitAnim.setDuration(2000);
+ exitAnim.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {}
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ // 移除视图并处理下一个
+ ViewGroup rootView = (ViewGroup) getParent();
+ if (rootView != null) {
+ rootView.removeView(QXGiftDriftView.getInstance( context));
+ }
+
+ if (!dataArray.isEmpty()) {
+ dataArray.remove(0);
+ }
+ isPlaying = false;
+
+ if (!dataArray.isEmpty()) {
+ giftAction();
+ }
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {}
+ });
+ startAnimation(exitAnim);
+ }, 1000); // 停留1秒
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {}
+ });
+ startAnimation(enterAnim);
+ }
+
+ public void setModel(MqttBean.ListBean model) {
+ this.model = model;
+
+ String text = String.format("%s送给%s", model.getFromUserName(), model.getToUserName());
+ // 这里需要使用SpannableString来实现富文本效果
+ // 简化处理,直接设置文本
+ titleLabel.setText(text);
+
+ // 加载图片 - 使用图片加载库如Glide
+ // Glide.with(context).load(model.getGiftPicture()).into(giftImageView);
+
+ countLabel.setText(String.format("X%s", model.getNumber()));
+ }
+
+ public void drifPopIsClose(boolean isClose) {
+ this.isClose = isClose;
+ setVisibility(isClose ? View.GONE : View.VISIBLE);
+
+ SharedPreferences prefs = context.getSharedPreferences("AppPrefs", Context.MODE_PRIVATE);
+ prefs.edit().putBoolean("kIsCloseDrifPop", isClose).apply();
+
+ if (isClose) {
+ ViewGroup rootView = (ViewGroup) getParent();
+ if (rootView != null) {
+ rootView.removeView(this);
+ }
+ dataArray.clear();
+ isPlaying = false;
+ }
+ }
+
+ private int scaleWidth(int width) {
+ // 根据屏幕密度进行缩放
+ float density = context.getResources().getDisplayMetrics().density;
+ return (int) (width * density);
+ }
+
+ // QXGiftScrollModel 类(需要单独定义)
+ public static class QXGiftScrollModel {
+ private String fromUserName;
+ private String toUserName;
+ private String giftPicture;
+ private String number;
+
+ // getters and setters
+ public String getFromUserName() { return fromUserName; }
+ public void setFromUserName(String fromUserName) { this.fromUserName = fromUserName; }
+
+ public String getToUserName() { return toUserName; }
+ public void setToUserName(String toUserName) { this.toUserName = toUserName; }
+
+ public String getGiftPicture() { return giftPicture; }
+ public void setGiftPicture(String giftPicture) { this.giftPicture = giftPicture; }
+
+ public String getNumber() { return number; }
+ public void setNumber(String number) { this.number = number; }
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/widget/QXGiftPlayerManager.java b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/QXGiftPlayerManager.java
new file mode 100644
index 00000000..8fbcbcc8
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/QXGiftPlayerManager.java
@@ -0,0 +1,144 @@
+package com.xscm.moduleutil.widget;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import com.xscm.moduleutil.bean.GiftBean;
+
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+public class QXGiftPlayerManager {
+ private static QXGiftPlayerManager instance;
+ private View bgEffectView;
+
+ private GiftAnimView fullEffectView;
+ private GiftAnimView chatEffectView;
+ private Context context;
+
+ private QXGiftPlayerManager(Context context) {
+ this.context = context.getApplicationContext();
+ }
+
+ public static synchronized QXGiftPlayerManager getInstance(Context context) {
+ if (instance == null) {
+ instance = new QXGiftPlayerManager(context);
+ }
+ return instance;
+ }
+ public View getDefaultBgEffectView() {
+ if (bgEffectView == null) {
+ initBgEffectView();
+ }
+ return bgEffectView;
+ }
+
+ public GiftAnimView getDefaultFullEffectView() {
+ if (fullEffectView == null) {
+ initFullEffectView();
+ }
+ return fullEffectView;
+ }
+
+ public GiftAnimView getDefaultChatEffectView() {
+ if (chatEffectView == null) {
+ initChatEffectView();
+ }
+ return chatEffectView;
+ }
+
+ private void initBgEffectView() {
+ bgEffectView = new FrameLayout(context);
+ bgEffectView.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ ));
+ bgEffectView.setBackgroundColor(0x00000000);
+ bgEffectView.setClickable(false); // userInteractionEnabled = NO
+
+ // 添加全屏特效视图和聊天特效视图
+ ((ViewGroup) bgEffectView).addView(getDefaultFullEffectView());
+ ((ViewGroup) bgEffectView).addView(getDefaultChatEffectView());
+ }
+ public void displayFullEffectView(String gift) {
+ getDefaultFullEffectView().displayEffectView(gift);
+ }
+
+ public void displayFullEffectView1(List stringList){
+ getDefaultFullEffectView().displayEffectView1(stringList);
+ }
+
+ public void displayChatEffectView(String gift) {
+ getDefaultChatEffectView().displayEffectView(gift);
+ }
+
+
+
+ public void openOrCloseEffectViewWith(boolean isShow) {
+ getDefaultFullEffectView().openOrCloseEffectViewWith(isShow);
+ getDefaultChatEffectView().openOrCloseEffectViewWith(isShow);
+ }
+
+ public void destroyEffectSvga() {
+ if (fullEffectView != null) {
+ fullEffectView.destroyEffectView();
+ if (bgEffectView != null) {
+ ((ViewGroup) bgEffectView).removeView(fullEffectView);
+ }
+ fullEffectView = null;
+ }
+
+ if (chatEffectView != null) {
+ chatEffectView.destroyEffectView();
+ if (bgEffectView != null) {
+ ((ViewGroup) bgEffectView).removeView(chatEffectView);
+ }
+ chatEffectView = null;
+ }
+
+ if (bgEffectView != null) {
+ bgEffectView = null;
+ }
+ }
+
+ public void stopPlay() {
+ if (fullEffectView != null) {
+ fullEffectView.stopPlay();
+ }
+ if (chatEffectView != null) {
+ chatEffectView.stopPlay();
+ }
+ }
+
+ private void initFullEffectView() {
+ fullEffectView = new GiftAnimView(context);
+ fullEffectView.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ ));
+ // 创建专用线程池替代GCD队列
+ ExecutorService queue = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue(),
+ Executors.defaultThreadFactory());
+ fullEffectView.setQueue(queue);
+ }
+
+ private void initChatEffectView() {
+ chatEffectView = new GiftAnimView(context);
+ chatEffectView.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ ));
+ // 创建专用线程池替代GCD队列
+ ExecutorService queue = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue(),
+ Executors.defaultThreadFactory());
+ chatEffectView.setQueue(queue);
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/widget/RoomSingWheatView.java b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/RoomSingWheatView.java
new file mode 100644
index 00000000..c0ef584d
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/RoomSingWheatView.java
@@ -0,0 +1,169 @@
+package com.xscm.moduleutil.widget;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.opensource.svgaplayer.SVGAImageView;
+import com.xscm.moduleutil.R;
+import com.xscm.moduleutil.bean.room.RoomPitBean;
+import com.xscm.moduleutil.utils.ImageUtils;
+
+public class RoomSingWheatView extends LinearLayout {
+
+ public ImageView mRiv;
+ public ImageView mIvGift;
+ public WheatCharmView mCharmView;
+ public TextView mTvName;
+ public ImageView mIvSex;
+ public AvatarFrameView mIvFrame;
+ public AvatarFrameView mIvRipple;
+ public ExpressionImgView mIvFace;
+ public ImageView mIvShutup;
+ public TextView tvTime;
+ public TextView mTvNo;
+
+ public TextView tv_time_pk;
+
+ public RoomPitBean pitBean;//麦位数据
+ public String roomId;//房间id
+
+ public static final String WHEAT_BOSS = "8";//老板位
+ public static final String WHEAT_HOST = "9";//主持位
+
+ public float oX;
+ public float oY;
+
+ boolean closePhone = false;//自己麦位关闭话筒,用于判断声纹显示
+
+ public String pitNumber;
+ public int pitImageVId;
+
+// public ImageView iv_on_line;
+ private boolean showGiftAnim = true;//显示麦位动画
+ private ImageView iv_tag_type;
+
+ private TextView tv_zhul;
+
+ public RoomSingWheatView(@NonNull Context context) {
+ this(context, null);
+ }
+
+ public RoomSingWheatView(@NonNull Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public RoomSingWheatView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initView(context);
+ }
+
+ private void initView(Context context) {
+ // 确保布局被正确加载
+ inflate(context, getLayoutId(), this);
+
+ // 初始化所有视图组件
+ mRiv = findViewById(R.id.riv);
+ mIvGift = findViewById(R.id.iv_gift);
+ mCharmView = findViewById(R.id.charm_view);
+ mTvName = findViewById(R.id.tv_name);
+ mIvSex = findViewById(R.id.iv_sex);
+ mIvFrame = findViewById(R.id.iv_frame);
+ mIvRipple = findViewById(R.id.iv_ripple);
+ mIvFace = findViewById(R.id.iv_face);
+ mIvShutup = findViewById(R.id.iv_shutup);
+ tvTime = findViewById(R.id.tv_time);
+ tv_time_pk = findViewById(R.id.tv_time_pk);
+ mTvNo = findViewById(R.id.tv_no);
+// iv_on_line = findViewById(R.id.iv_online);
+ iv_tag_type = findViewById(R.id.iv_tag_type);
+ tv_zhul = findViewById(R.id.tv_zhul);
+
+ // 设置初始位置
+ if (mIvGift != null) {
+ oX = mIvGift.getX();
+ oY = mIvGift.getY();
+ }
+ }
+
+ protected int getLayoutId() {
+ return R.layout.room_view_sing_wheat;
+ }
+
+ public void setData(RoomPitBean bean) {
+ this.pitBean = bean;
+ if (bean == null) return;
+
+ // 添加空值检查,防止NPE
+ if (mTvName == null) {
+ // 可能布局未正确加载,尝试重新初始化
+ initView(getContext());
+ if (mTvName == null) {
+ // 如果仍然为null,记录日志并返回
+ android.util.Log.e("RoomSingWheatView", "mTvName is still null after re-initialization");
+ return;
+ }
+ }
+
+ if (isOn()) {
+ //开启声浪
+ mIvRipple.startLoopingSvga("ripple3695.svga");
+ mIvRipple.setVisibility(VISIBLE);
+ mTvName.setText(bean.getNickname());
+ ImageUtils.loadHeadCC(bean.getAvatar(), mRiv);
+
+ if (TextUtils.isEmpty(pitBean.getDress())) {
+ if (mIvFrame != null) mIvFrame.setVisibility(INVISIBLE);
+ } else {
+ if (mIvFrame != null) {
+ mIvFrame.setVisibility(VISIBLE);
+ mIvFrame.setSource(pitBean.getDress(), 3);
+ }
+ }
+ } else {
+ String pitText = "-1".equals(pitNumber) ? "" :
+ "9".equals(pitNumber) ? "主持位" :
+ "10".equals(pitNumber) ? "嘉宾位" :
+ pitNumber + "号麦位";
+ mTvName.setText(pitText);
+
+ if (mIvFrame != null) mIvFrame.setVisibility(INVISIBLE);
+ if (mIvFace != null) mIvFace.remove();
+ //停止声浪
+ mIvRipple.stopSvga();
+ mIvRipple.setVisibility(GONE);
+ }
+
+ // 更新魅力值视图
+ if (mCharmView != null) {
+ if (pitBean.getNickname() == null || pitBean.getNickname().isEmpty()) {
+ mCharmView.setVisibility(GONE);
+ } else {
+ mCharmView.setVisibility(VISIBLE);
+ }
+ }
+
+ // 更新PK状态
+ if (tv_time_pk != null) {
+ if (pitBean.is_pk() && pitBean.getUser_id() != null &&
+ !pitBean.getUser_id().equals("0") && !pitBean.getUser_id().isEmpty()) {
+ tv_time_pk.setVisibility(VISIBLE);
+ if (mCharmView != null) mCharmView.setVisibility(GONE);
+ } else {
+ tv_time_pk.setVisibility(GONE);
+ if (mCharmView != null) mCharmView.setVisibility(VISIBLE);
+ }
+ }
+ }
+
+ private boolean isOn() {
+ return pitBean != null && !TextUtils.isEmpty(pitBean.getUser_id()) && !"0".equals(pitBean.getUser_id());
+ }
+}
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/widget/SharedViewModel.java b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/SharedViewModel.java
new file mode 100644
index 00000000..9b85527c
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/SharedViewModel.java
@@ -0,0 +1,70 @@
+package com.xscm.moduleutil.widget;
+
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.ViewModel;
+
+import com.xscm.moduleutil.bean.room.RoomInfoResp;
+import com.xscm.moduleutil.event.QXRoomSeatViewType;
+
+// 在 common 模块中或相应模块中
+public class SharedViewModel extends ViewModel {
+ // 给roomFragment传递数据
+ private final MutableLiveData dataForFragment = new MutableLiveData<>();
+ private final MutableLiveData fragmentReady = new MutableLiveData<>(false);
+ private MutableLiveData seatViewTypeData = new MutableLiveData<>();
+ //给子fragment传递数据
+ private MutableLiveData childFragmentData = new MutableLiveData<>();
+
+ // 为子Fragment设置数据的方法
+ public void setChildFragmentData(RoomInfoResp data) {
+ childFragmentData.setValue(data);
+ }
+
+ // 获取子Fragment数据的LiveData
+ public LiveData getChildFragmentData() {
+ return childFragmentData;
+ }
+ public void setSeatViewType(QXRoomSeatViewType type) {
+ seatViewTypeData.setValue(type);
+ }
+
+ public LiveData getSeatViewType() {
+ return seatViewTypeData;
+ }
+ public LiveData getDataForFragment() {
+ return dataForFragment;
+ }
+
+ public void setDataForFragment(RoomInfoResp data) {
+ dataForFragment.setValue(data);
+ }
+
+ public LiveData getFragmentReady() {
+ return fragmentReady;
+ }
+
+ public void setFragmentReady(boolean ready) {
+ fragmentReady.setValue(ready);
+ }
+
+ // 清除数据,避免重复接收
+
+ // 清理所有数据的方法
+ public void clearAllData() {
+ dataForFragment.setValue(null);
+ childFragmentData.setValue(null);
+ seatViewTypeData.setValue(null);
+ fragmentReady.setValue(false);
+ }
+
+ // 清理子Fragment数据
+ public void clearChildFragmentData() {
+ childFragmentData.setValue(null);
+ }
+
+ // 清理主Fragment数据
+ public void clearFragmentData() {
+ dataForFragment.setValue(null);
+ }
+}
\ No newline at end of file
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/widget/img/BubbleBackgroundHelper.java b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/img/BubbleBackgroundHelper.java
new file mode 100644
index 00000000..0d53a0f2
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/img/BubbleBackgroundHelper.java
@@ -0,0 +1,137 @@
+package com.xscm.moduleutil.widget.img;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.NinePatchDrawable;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.target.CustomTarget;
+import com.bumptech.glide.request.transition.Transition;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+/**
+ * Created by xscm on 2021/5/27.
+ * 创建类似.9图效果的拉伸背景
+ */
+public class BubbleBackgroundHelper {
+
+ /**
+ * 创建类似.9图效果的拉伸背景
+ */
+ public static Drawable createStretchableBubble(Context context, Bitmap originalBitmap,
+ int leftBorder, int topBorder,
+ int rightBorder, int bottomBorder) {
+ try {
+ // 将 Bitmap 转换为 NinePatchDrawable
+ byte[] chunk = createNinePatchChunk(
+ originalBitmap.getWidth(),
+ originalBitmap.getHeight(),
+ leftBorder, topBorder, rightBorder, bottomBorder
+ );
+
+ NinePatchDrawable drawable = new NinePatchDrawable(
+ context.getResources(),
+ originalBitmap,
+ chunk,
+ new Rect(),
+ null
+ );
+
+ return drawable;
+ } catch (Exception e) {
+ e.printStackTrace();
+ // 如果失败,返回原始 BitmapDrawable
+ return new BitmapDrawable(context.getResources(), originalBitmap);
+ }
+ }
+
+ /**
+ * 创建 NinePatch 的 chunk 数据
+ */
+ private static byte[] createNinePatchChunk(int bitmapWidth, int bitmapHeight,
+ int leftBorder, int topBorder,
+ int rightBorder, int bottomBorder) {
+ ByteBuffer buffer = ByteBuffer.allocate(84).order(ByteOrder.nativeOrder());
+
+ // 写入魔数
+ buffer.put((byte) 0x01);
+ buffer.put((byte) 0x02);
+ buffer.put((byte) 0x02);
+ buffer.put((byte) 0x02);
+
+ // 水平拉伸区域数量
+ buffer.putInt(1);
+ // 垂直拉伸区域数量
+ buffer.putInt(1);
+
+ // 颜色数量
+ buffer.putInt(0);
+
+ // 跳过填充
+ buffer.putInt(0);
+ buffer.putInt(0);
+
+ // 水平拉伸区域
+ buffer.putInt(leftBorder);
+ buffer.putInt(bitmapWidth - rightBorder);
+
+ // 垂直拉伸区域
+ buffer.putInt(topBorder);
+ buffer.putInt(bitmapHeight - bottomBorder);
+
+ // 填充剩余部分
+ for (int i = 0; i < 9; i++) {
+ buffer.putInt(0);
+ }
+
+ return buffer.array();
+ }
+
+ /**
+ * 加载网络图片并创建拉伸背景
+ */
+ public static void loadBubbleBackground(Context context, String imageUrl,
+ OnBubbleBackgroundLoadedListener listener) {
+ Glide.with(context)
+ .asBitmap()
+ .load(imageUrl)
+ .into(new CustomTarget() {
+ @Override
+ public void onResourceReady(@NonNull Bitmap resource,
+ @Nullable Transition super Bitmap> transition) {
+ // 假设气泡的四个角都是 30px(根据你的图片调整)
+ int borderSize = 10;
+ Drawable bubbleDrawable = createStretchableBubble(
+ context, resource,
+ borderSize, borderSize, borderSize, borderSize
+ );
+
+ if (listener != null) {
+ listener.onBackgroundLoaded(bubbleDrawable);
+ }
+ }
+
+ @Override
+ public void onLoadCleared(@Nullable Drawable placeholder) {
+ // 清理资源
+ }
+
+ @Override
+ public void onLoadFailed(@Nullable Drawable errorDrawable) {
+ if (listener != null) {
+ listener.onLoadFailed();
+ }
+ }
+ });
+ }
+
+ public interface OnBubbleBackgroundLoadedListener {
+ void onBackgroundLoaded(Drawable bubbleDrawable);
+ void onLoadFailed();
+ }
+}
\ No newline at end of file
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/widget/room/PassRoomException.kt b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/room/PassRoomException.kt
new file mode 100644
index 00000000..cea4259d
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/widget/room/PassRoomException.kt
@@ -0,0 +1,8 @@
+package com.xscm.moduleutil.widget.room
+
+import java.io.IOException
+
+/**
+ * 自定义异常信息显示
+ */
+data class PassRoomException(var msg: String,var code: Int) : IOException()
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/anim/slide_in_right.xml b/moduleUtil/src/main/res/anim/slide_in_right.xml
new file mode 100644
index 00000000..c301a7ac
--- /dev/null
+++ b/moduleUtil/src/main/res/anim/slide_in_right.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/anim/slide_out_right.xml b/moduleUtil/src/main/res/anim/slide_out_right.xml
new file mode 100644
index 00000000..3bebee9f
--- /dev/null
+++ b/moduleUtil/src/main/res/anim/slide_out_right.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/ac_left_gift_bg.png b/moduleUtil/src/main/res/drawable-xxxhdpi/ac_left_gift_bg.png
new file mode 100644
index 00000000..5f0eff08
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/ac_left_gift_bg.png differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/ac_lock_gift_bg.png b/moduleUtil/src/main/res/drawable-xxxhdpi/ac_lock_gift_bg.png
new file mode 100644
index 00000000..b67b241c
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/ac_lock_gift_bg.png differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/ac_lock_gift_light_bg.png b/moduleUtil/src/main/res/drawable-xxxhdpi/ac_lock_gift_light_bg.png
new file mode 100644
index 00000000..f3258ccd
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/ac_lock_gift_light_bg.png differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/ac_meet_gift_name_bg.png b/moduleUtil/src/main/res/drawable-xxxhdpi/ac_meet_gift_name_bg.png
new file mode 100644
index 00000000..ff17a584
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/ac_meet_gift_name_bg.png differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/ac_time_down_bg.png b/moduleUtil/src/main/res/drawable-xxxhdpi/ac_time_down_bg.png
new file mode 100644
index 00000000..ec5d9a47
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/ac_time_down_bg.png differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/bg_room_gift.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/bg_room_gift.webp
new file mode 100644
index 00000000..3adf2979
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/bg_room_gift.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/custom.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/custom.webp
new file mode 100644
index 00000000..a3a0d522
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/custom.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/detail_icon_go.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/detail_icon_go.webp
new file mode 100644
index 00000000..a1139bb3
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/detail_icon_go.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/gift_background.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/gift_background.webp
new file mode 100644
index 00000000..4d8a8a26
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/gift_background.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/group.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/group.webp
new file mode 100644
index 00000000..50bb34d6
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/group.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/ic_agreement_selected.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/ic_agreement_selected.webp
new file mode 100644
index 00000000..c9f19032
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/ic_agreement_selected.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/ic_agreement_unselect.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/ic_agreement_unselect.webp
new file mode 100644
index 00000000..971318af
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/ic_agreement_unselect.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/icon_btn_radio_0.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/icon_btn_radio_0.webp
new file mode 100644
index 00000000..236dd396
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/icon_btn_radio_0.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/icon_btn_radio_1.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/icon_btn_radio_1.webp
new file mode 100644
index 00000000..a9318d80
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/icon_btn_radio_1.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/icon_pay_select.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/icon_pay_select.webp
new file mode 100644
index 00000000..2080b80f
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/icon_pay_select.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/icon_pay_unselect.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/icon_pay_unselect.webp
new file mode 100644
index 00000000..971318af
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/icon_pay_unselect.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/image_yq.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/image_yq.webp
new file mode 100644
index 00000000..fef15712
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/image_yq.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/img_emperor.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/img_emperor.webp
new file mode 100644
index 00000000..e7d0b1c0
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/img_emperor.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/me_avatar_bg.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/me_avatar_bg.webp
new file mode 100644
index 00000000..8ed29c18
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/me_avatar_bg.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/meinfo_skill_star_normal.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/meinfo_skill_star_normal.webp
new file mode 100644
index 00000000..36875453
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/meinfo_skill_star_normal.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/meinfo_skill_star_selected.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/meinfo_skill_star_selected.webp
new file mode 100644
index 00000000..65d03df2
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/meinfo_skill_star_selected.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/phonetic.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/phonetic.webp
new file mode 100644
index 00000000..6a279dee
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/phonetic.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/red_bag_btn_bg_nor.png b/moduleUtil/src/main/res/drawable-xxxhdpi/red_bag_btn_bg_nor.png
new file mode 100644
index 00000000..b3a3f653
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/red_bag_btn_bg_nor.png differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/red_bag_btn_bg_sel.png b/moduleUtil/src/main/res/drawable-xxxhdpi/red_bag_btn_bg_sel.png
new file mode 100644
index 00000000..c2ada8d3
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/red_bag_btn_bg_sel.png differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/red_bag_next_btn_bg.png b/moduleUtil/src/main/res/drawable-xxxhdpi/red_bag_next_btn_bg.png
new file mode 100644
index 00000000..7a7b494c
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/red_bag_next_btn_bg.png differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/red_bag_time_btn_bg_nor.png b/moduleUtil/src/main/res/drawable-xxxhdpi/red_bag_time_btn_bg_nor.png
new file mode 100644
index 00000000..d2372a8a
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/red_bag_time_btn_bg_nor.png differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/red_bag_time_btn_bg_sel.png b/moduleUtil/src/main/res/drawable-xxxhdpi/red_bag_time_btn_bg_sel.png
new file mode 100644
index 00000000..ace098aa
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/red_bag_time_btn_bg_sel.png differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/room_ic_wheat_default.png b/moduleUtil/src/main/res/drawable-xxxhdpi/room_ic_wheat_default.png
new file mode 100644
index 00000000..d59d2839
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/room_ic_wheat_default.png differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/room_redbag_back.png b/moduleUtil/src/main/res/drawable-xxxhdpi/room_redbag_back.png
new file mode 100644
index 00000000..91d3bfab
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/room_redbag_back.png differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/room_redbag_help.png b/moduleUtil/src/main/res/drawable-xxxhdpi/room_redbag_help.png
new file mode 100644
index 00000000..eb049052
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/room_redbag_help.png differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00000.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00000.webp
new file mode 100644
index 00000000..4088c105
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00000.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00001.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00001.webp
new file mode 100644
index 00000000..aa3bde0a
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00001.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00002.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00002.webp
new file mode 100644
index 00000000..56bdcc98
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00002.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00003.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00003.webp
new file mode 100644
index 00000000..cb880fe5
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00003.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00004.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00004.webp
new file mode 100644
index 00000000..ab5c1d8c
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00004.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00005.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00005.webp
new file mode 100644
index 00000000..55b807ab
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00005.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00006.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00006.webp
new file mode 100644
index 00000000..dc4e840e
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00006.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00007.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00007.webp
new file mode 100644
index 00000000..c72edb32
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00007.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00008.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00008.webp
new file mode 100644
index 00000000..409e1d1a
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00008.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00009.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00009.webp
new file mode 100644
index 00000000..a7a94fa3
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00009.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00010.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00010.webp
new file mode 100644
index 00000000..d3d05d4c
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00010.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00011.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00011.webp
new file mode 100644
index 00000000..feb52131
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00011.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00012.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00012.webp
new file mode 100644
index 00000000..270a972a
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00012.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00013.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00013.webp
new file mode 100644
index 00000000..1f933aa5
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00013.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00014.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00014.webp
new file mode 100644
index 00000000..90491828
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00014.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00015.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00015.webp
new file mode 100644
index 00000000..3f6d9ac8
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00015.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00016.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00016.webp
new file mode 100644
index 00000000..aea576dd
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00016.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00017.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00017.webp
new file mode 100644
index 00000000..9c34b78f
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00017.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00018.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00018.webp
new file mode 100644
index 00000000..86c96e3a
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00018.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00019.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00019.webp
new file mode 100644
index 00000000..d7c94f63
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00019.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00020.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00020.webp
new file mode 100644
index 00000000..8a651102
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00020.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00021.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00021.webp
new file mode 100644
index 00000000..f968efbe
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00021.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00022.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00022.webp
new file mode 100644
index 00000000..8c25a86a
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00022.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00023.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00023.webp
new file mode 100644
index 00000000..63cb1fdb
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00023.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00024.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00024.webp
new file mode 100644
index 00000000..54a777f7
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00024.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00025.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00025.webp
new file mode 100644
index 00000000..78404573
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00025.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00026.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00026.webp
new file mode 100644
index 00000000..0fd1c003
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00026.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00027.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00027.webp
new file mode 100644
index 00000000..da9a70ab
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00027.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00028.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00028.webp
new file mode 100644
index 00000000..b6931469
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00028.webp differ
diff --git a/moduleUtil/src/main/res/drawable-xxxhdpi/star_00029.webp b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00029.webp
new file mode 100644
index 00000000..c4728498
Binary files /dev/null and b/moduleUtil/src/main/res/drawable-xxxhdpi/star_00029.webp differ
diff --git a/moduleUtil/src/main/res/drawable/bf_e9.xml b/moduleUtil/src/main/res/drawable/bf_e9.xml
new file mode 100644
index 00000000..b1f1451b
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/bf_e9.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/drawable/bg_bttom_fff.xml b/moduleUtil/src/main/res/drawable/bg_bttom_fff.xml
new file mode 100644
index 00000000..4786b09d
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/bg_bttom_fff.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/drawable/bg_person.xml b/moduleUtil/src/main/res/drawable/bg_person.xml
new file mode 100644
index 00000000..a7b3cf23
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/bg_person.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/moduleUtil/src/main/res/drawable/bg_r10_bott.xml b/moduleUtil/src/main/res/drawable/bg_r10_bott.xml
new file mode 100644
index 00000000..bfc5aca7
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/bg_r10_bott.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
diff --git a/moduleUtil/src/main/res/drawable/bg_r395_ba230a.xml b/moduleUtil/src/main/res/drawable/bg_r395_ba230a.xml
new file mode 100644
index 00000000..1f0158ba
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/bg_r395_ba230a.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/drawable/bg_r40_fe8ec8.xml b/moduleUtil/src/main/res/drawable/bg_r40_fe8ec8.xml
new file mode 100644
index 00000000..edc69fe3
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/bg_r40_fe8ec8.xml
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/drawable/bg_r45_2d449f.xml b/moduleUtil/src/main/res/drawable/bg_r45_2d449f.xml
new file mode 100644
index 00000000..85342633
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/bg_r45_2d449f.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/drawable/bg_r45_8c3ca2.xml b/moduleUtil/src/main/res/drawable/bg_r45_8c3ca2.xml
new file mode 100644
index 00000000..6895316c
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/bg_r45_8c3ca2.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/drawable/bg_r45_a27f40.xml b/moduleUtil/src/main/res/drawable/bg_r45_a27f40.xml
new file mode 100644
index 00000000..c5aa4f84
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/bg_r45_a27f40.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/drawable/bg_r4_eff2f8.xml b/moduleUtil/src/main/res/drawable/bg_r4_eff2f8.xml
new file mode 100644
index 00000000..d16b5c8f
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/bg_r4_eff2f8.xml
@@ -0,0 +1,15 @@
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/drawable/bg_r6_000ff.xml b/moduleUtil/src/main/res/drawable/bg_r6_000ff.xml
new file mode 100644
index 00000000..cbc129ed
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/bg_r6_000ff.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/drawable/bg_r8_c51a0c.xml b/moduleUtil/src/main/res/drawable/bg_r8_c51a0c.xml
new file mode 100644
index 00000000..1542f872
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/bg_r8_c51a0c.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/drawable/bg_r8_fff.xml b/moduleUtil/src/main/res/drawable/bg_r8_fff.xml
new file mode 100644
index 00000000..f20c57a9
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/bg_r8_fff.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/drawable/bg_r8_tm.xml b/moduleUtil/src/main/res/drawable/bg_r8_tm.xml
new file mode 100644
index 00000000..1f053f32
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/bg_r8_tm.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/drawable/bg_radio_selector.xml b/moduleUtil/src/main/res/drawable/bg_radio_selector.xml
new file mode 100644
index 00000000..db0541ff
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/bg_radio_selector.xml
@@ -0,0 +1,33 @@
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/drawable/bg_red_16_envel.xml b/moduleUtil/src/main/res/drawable/bg_red_16_envel.xml
new file mode 100644
index 00000000..31ac67ae
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/bg_red_16_envel.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+ - #4d000000
+ - 0
+ - 4
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/drawable/bg_red_xian.xml b/moduleUtil/src/main/res/drawable/bg_red_xian.xml
new file mode 100644
index 00000000..5e2b9079
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/bg_red_xian.xml
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/drawable/bg_round_corner.xml b/moduleUtil/src/main/res/drawable/bg_round_corner.xml
new file mode 100644
index 00000000..298a6173
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/bg_round_corner.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/moduleUtil/src/main/res/drawable/bg_rounded_top_white.xml b/moduleUtil/src/main/res/drawable/bg_rounded_top_white.xml
new file mode 100644
index 00000000..857b7690
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/bg_rounded_top_white.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/drawable/bg_xlh_huod_bj.xml b/moduleUtil/src/main/res/drawable/bg_xlh_huod_bj.xml
new file mode 100644
index 00000000..f94de2fc
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/bg_xlh_huod_bj.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/drawable/circle_background.xml b/moduleUtil/src/main/res/drawable/circle_background.xml
new file mode 100644
index 00000000..e93365e2
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/circle_background.xml
@@ -0,0 +1,5 @@
+
+
+
+
diff --git a/moduleUtil/src/main/res/drawable/ease_row_pubilc_sys_bg.xml b/moduleUtil/src/main/res/drawable/ease_row_pubilc_sys_bg.xml
new file mode 100644
index 00000000..b2f611f8
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/ease_row_pubilc_sys_bg.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/drawable/home_bbar_xz.xml b/moduleUtil/src/main/res/drawable/home_bbar_xz.xml
new file mode 100644
index 00000000..218841d6
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/home_bbar_xz.xml
@@ -0,0 +1,20 @@
+
+
+ -
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
diff --git a/moduleUtil/src/main/res/drawable/jiaoy_tv_x.xml b/moduleUtil/src/main/res/drawable/jiaoy_tv_x.xml
new file mode 100644
index 00000000..0ace894e
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/jiaoy_tv_x.xml
@@ -0,0 +1,8 @@
+
+
+
+ - #4a400c7c
+ - 0
+ - 2
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/drawable/rd_btn_new_p.xml b/moduleUtil/src/main/res/drawable/rd_btn_new_p.xml
new file mode 100644
index 00000000..7544729a
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/rd_btn_new_p.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/drawable/selector_red_bag_condition_button.xml b/moduleUtil/src/main/res/drawable/selector_red_bag_condition_button.xml
new file mode 100644
index 00000000..eb92e4ab
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/selector_red_bag_condition_button.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/moduleUtil/src/main/res/drawable/selector_red_bag_radio_button.xml b/moduleUtil/src/main/res/drawable/selector_red_bag_radio_button.xml
new file mode 100644
index 00000000..14fa7986
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/selector_red_bag_radio_button.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/moduleUtil/src/main/res/drawable/selector_red_bag_type_button.xml b/moduleUtil/src/main/res/drawable/selector_red_bag_type_button.xml
new file mode 100644
index 00000000..c4c485e9
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/selector_red_bag_type_button.xml
@@ -0,0 +1,17 @@
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
diff --git a/moduleUtil/src/main/res/drawable/syzc_background.xml b/moduleUtil/src/main/res/drawable/syzc_background.xml
new file mode 100644
index 00000000..1f5753b1
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/syzc_background.xml
@@ -0,0 +1,5 @@
+
+
+
+
diff --git a/moduleUtil/src/main/res/drawable/text_color_radio_selector.xml b/moduleUtil/src/main/res/drawable/text_color_radio_selector.xml
new file mode 100644
index 00000000..e0d8d6e3
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/text_color_radio_selector.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/font/semibold.otf b/moduleUtil/src/main/res/font/semibold.otf
new file mode 100644
index 00000000..5f94c235
Binary files /dev/null and b/moduleUtil/src/main/res/font/semibold.otf differ
diff --git a/moduleUtil/src/main/res/layout/dialog_city_time.xml b/moduleUtil/src/main/res/layout/dialog_city_time.xml
new file mode 100644
index 00000000..725aa5bd
--- /dev/null
+++ b/moduleUtil/src/main/res/layout/dialog_city_time.xml
@@ -0,0 +1,480 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/layout/dialog_gift_lottery_fragment.xml b/moduleUtil/src/main/res/layout/dialog_gift_lottery_fragment.xml
new file mode 100644
index 00000000..552697f0
--- /dev/null
+++ b/moduleUtil/src/main/res/layout/dialog_gift_lottery_fragment.xml
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/moduleUtil/src/main/res/layout/dialog_mirroe_sky.xml b/moduleUtil/src/main/res/layout/dialog_mirroe_sky.xml
new file mode 100644
index 00000000..b234b1a1
--- /dev/null
+++ b/moduleUtil/src/main/res/layout/dialog_mirroe_sky.xml
@@ -0,0 +1,514 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/layout/dialog_new_people.xml b/moduleUtil/src/main/res/layout/dialog_new_people.xml
new file mode 100644
index 00000000..9da9e959
--- /dev/null
+++ b/moduleUtil/src/main/res/layout/dialog_new_people.xml
@@ -0,0 +1,176 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/layout/dialog_new_ranking_xlh_fragment.xml b/moduleUtil/src/main/res/layout/dialog_new_ranking_xlh_fragment.xml
new file mode 100644
index 00000000..aa14412a
--- /dev/null
+++ b/moduleUtil/src/main/res/layout/dialog_new_ranking_xlh_fragment.xml
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/moduleUtil/src/main/res/layout/dialog_pinnacle_time.xml b/moduleUtil/src/main/res/layout/dialog_pinnacle_time.xml
new file mode 100644
index 00000000..fda57903
--- /dev/null
+++ b/moduleUtil/src/main/res/layout/dialog_pinnacle_time.xml
@@ -0,0 +1,480 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/layout/dialog_prize_pool.xml b/moduleUtil/src/main/res/layout/dialog_prize_pool.xml
new file mode 100644
index 00000000..e96932ef
--- /dev/null
+++ b/moduleUtil/src/main/res/layout/dialog_prize_pool.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/layout/dialog_ranking_xlh_fragment.xml b/moduleUtil/src/main/res/layout/dialog_ranking_xlh_fragment.xml
new file mode 100644
index 00000000..6ff9c6e7
--- /dev/null
+++ b/moduleUtil/src/main/res/layout/dialog_ranking_xlh_fragment.xml
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/moduleUtil/src/main/res/layout/dialog_room_auction_webview.xml b/moduleUtil/src/main/res/layout/dialog_room_auction_webview.xml
new file mode 100644
index 00000000..1422b931
--- /dev/null
+++ b/moduleUtil/src/main/res/layout/dialog_room_auction_webview.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/layout/dialog_xlh_obtain.xml b/moduleUtil/src/main/res/layout/dialog_xlh_obtain.xml
new file mode 100644
index 00000000..1fd778de
--- /dev/null
+++ b/moduleUtil/src/main/res/layout/dialog_xlh_obtain.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/layout/dialog_xlh_record_fragment.xml b/moduleUtil/src/main/res/layout/dialog_xlh_record_fragment.xml
new file mode 100644
index 00000000..dfe54bf7
--- /dev/null
+++ b/moduleUtil/src/main/res/layout/dialog_xlh_record_fragment.xml
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/moduleUtil/src/main/res/layout/fframent_data.xml b/moduleUtil/src/main/res/layout/fframent_data.xml
new file mode 100644
index 00000000..1bbec3d8
--- /dev/null
+++ b/moduleUtil/src/main/res/layout/fframent_data.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/moduleUtil/src/main/res/layout/fragment_tour_club_dialog.xml b/moduleUtil/src/main/res/layout/fragment_tour_club_dialog.xml
new file mode 100644
index 00000000..93a59995
--- /dev/null
+++ b/moduleUtil/src/main/res/layout/fragment_tour_club_dialog.xml
@@ -0,0 +1,592 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/layout/gift_display_layout.xml b/moduleUtil/src/main/res/layout/gift_display_layout.xml
new file mode 100644
index 00000000..684d5458
--- /dev/null
+++ b/moduleUtil/src/main/res/layout/gift_display_layout.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/layout/item_gift_lottery.xml b/moduleUtil/src/main/res/layout/item_gift_lottery.xml
new file mode 100644
index 00000000..9d7f7932
--- /dev/null
+++ b/moduleUtil/src/main/res/layout/item_gift_lottery.xml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/layout/item_gift_record.xml b/moduleUtil/src/main/res/layout/item_gift_record.xml
new file mode 100644
index 00000000..35cf732f
--- /dev/null
+++ b/moduleUtil/src/main/res/layout/item_gift_record.xml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/moduleUtil/src/main/res/layout/item_gift_record_new.xml b/moduleUtil/src/main/res/layout/item_gift_record_new.xml
new file mode 100644
index 00000000..85fd8775
--- /dev/null
+++ b/moduleUtil/src/main/res/layout/item_gift_record_new.xml
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/moduleUtil/src/main/res/layout/item_hourly_floating.xml b/moduleUtil/src/main/res/layout/item_hourly_floating.xml
new file mode 100644
index 00000000..fbeb8521
--- /dev/null
+++ b/moduleUtil/src/main/res/layout/item_hourly_floating.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/layout/item_my_record.xml b/moduleUtil/src/main/res/layout/item_my_record.xml
new file mode 100644
index 00000000..4ebd8bcc
--- /dev/null
+++ b/moduleUtil/src/main/res/layout/item_my_record.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/layout/item_piaoping_red.xml b/moduleUtil/src/main/res/layout/item_piaoping_red.xml
new file mode 100644
index 00000000..80257898
--- /dev/null
+++ b/moduleUtil/src/main/res/layout/item_piaoping_red.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/layout/item_piaoping_xlh.xml b/moduleUtil/src/main/res/layout/item_piaoping_xlh.xml
new file mode 100644
index 00000000..4ec4a57d
--- /dev/null
+++ b/moduleUtil/src/main/res/layout/item_piaoping_xlh.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/layout/item_prize_pool.xml b/moduleUtil/src/main/res/layout/item_prize_pool.xml
new file mode 100644
index 00000000..345892b8
--- /dev/null
+++ b/moduleUtil/src/main/res/layout/item_prize_pool.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/layout/item_xlh.xml b/moduleUtil/src/main/res/layout/item_xlh.xml
new file mode 100644
index 00000000..a82c0dad
--- /dev/null
+++ b/moduleUtil/src/main/res/layout/item_xlh.xml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/layout/item_xlh_gift.xml b/moduleUtil/src/main/res/layout/item_xlh_gift.xml
new file mode 100644
index 00000000..04a84153
--- /dev/null
+++ b/moduleUtil/src/main/res/layout/item_xlh_gift.xml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleUtil/src/main/res/layout/view_gift_card.xml b/moduleUtil/src/main/res/layout/view_gift_card.xml
new file mode 100644
index 00000000..4355f518
--- /dev/null
+++ b/moduleUtil/src/main/res/layout/view_gift_card.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/moduleUtil/src/main/res/mipmap-hdpi/default_avatar.webp b/moduleUtil/src/main/res/mipmap-hdpi/default_avatar.webp
new file mode 100644
index 00000000..61399802
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-hdpi/default_avatar.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-hdpi/gift_t.webp b/moduleUtil/src/main/res/mipmap-hdpi/gift_t.webp
new file mode 100644
index 00000000..14b024b2
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-hdpi/gift_t.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-hdpi/ic_launcher_app.webp b/moduleUtil/src/main/res/mipmap-hdpi/ic_launcher_app.webp
new file mode 100644
index 00000000..61399802
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-hdpi/ic_launcher_app.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-hdpi/ic_launcher_round1.webp b/moduleUtil/src/main/res/mipmap-hdpi/ic_launcher_round1.webp
new file mode 100644
index 00000000..61399802
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-hdpi/ic_launcher_round1.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-hdpi/room_ic_wheat_charm.webp b/moduleUtil/src/main/res/mipmap-hdpi/room_ic_wheat_charm.webp
new file mode 100644
index 00000000..cc58b528
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-hdpi/room_ic_wheat_charm.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-hdpi/room_status.webp b/moduleUtil/src/main/res/mipmap-hdpi/room_status.webp
new file mode 100644
index 00000000..a8ea3386
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-hdpi/room_status.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/accompany_off.webp b/moduleUtil/src/main/res/mipmap-mdpi/accompany_off.webp
new file mode 100644
index 00000000..ffae9ab9
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/accompany_off.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/delte.webp b/moduleUtil/src/main/res/mipmap-mdpi/delte.webp
new file mode 100644
index 00000000..2210eb4d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/delte.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/gb.webp b/moduleUtil/src/main/res/mipmap-mdpi/gb.webp
new file mode 100644
index 00000000..9b2af7b2
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/gb.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/hua.webp b/moduleUtil/src/main/res/mipmap-mdpi/hua.webp
new file mode 100644
index 00000000..8de3a964
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/hua.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/huif.webp b/moduleUtil/src/main/res/mipmap-mdpi/huif.webp
new file mode 100644
index 00000000..28bb0147
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/huif.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/huyu.webp b/moduleUtil/src/main/res/mipmap-mdpi/huyu.webp
new file mode 100644
index 00000000..0b68fc40
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/huyu.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/ic_auction.webp b/moduleUtil/src/main/res/mipmap-mdpi/ic_auction.webp
new file mode 100644
index 00000000..b1045491
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/ic_auction.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/ic_bg_image.webp b/moduleUtil/src/main/res/mipmap-mdpi/ic_bg_image.webp
new file mode 100644
index 00000000..ac44c756
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/ic_bg_image.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/ic_bg_music.webp b/moduleUtil/src/main/res/mipmap-mdpi/ic_bg_music.webp
new file mode 100644
index 00000000..fe3441c2
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/ic_bg_music.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/ic_boy.webp b/moduleUtil/src/main/res/mipmap-mdpi/ic_boy.webp
new file mode 100644
index 00000000..22c1a340
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/ic_boy.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/ic_clear_message.webp b/moduleUtil/src/main/res/mipmap-mdpi/ic_clear_message.webp
new file mode 100644
index 00000000..45675e70
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/ic_clear_message.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/ic_close_effects.webp b/moduleUtil/src/main/res/mipmap-mdpi/ic_close_effects.webp
new file mode 100644
index 00000000..e587fb3e
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/ic_close_effects.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/ic_close_floating_screen.webp b/moduleUtil/src/main/res/mipmap-mdpi/ic_close_floating_screen.webp
new file mode 100644
index 00000000..5c786a0c
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/ic_close_floating_screen.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/ic_compere.webp b/moduleUtil/src/main/res/mipmap-mdpi/ic_compere.webp
new file mode 100644
index 00000000..34c5098d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/ic_compere.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/ic_girl.webp b/moduleUtil/src/main/res/mipmap-mdpi/ic_girl.webp
new file mode 100644
index 00000000..515ae034
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/ic_girl.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/ic_jiaoy.webp b/moduleUtil/src/main/res/mipmap-mdpi/ic_jiaoy.webp
new file mode 100644
index 00000000..f2e0a207
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/ic_jiaoy.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/ic_launcher_app.webp b/moduleUtil/src/main/res/mipmap-mdpi/ic_launcher_app.webp
new file mode 100644
index 00000000..82f36a48
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/ic_launcher_app.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/ic_leave.webp b/moduleUtil/src/main/res/mipmap-mdpi/ic_leave.webp
new file mode 100644
index 00000000..9f555bab
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/ic_leave.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/ic_my_dress.webp b/moduleUtil/src/main/res/mipmap-mdpi/ic_my_dress.webp
new file mode 100644
index 00000000..ed171f2d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/ic_my_dress.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/ic_open_effects.webp b/moduleUtil/src/main/res/mipmap-mdpi/ic_open_effects.webp
new file mode 100644
index 00000000..13c22894
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/ic_open_effects.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/ic_open_floating_screen.webp b/moduleUtil/src/main/res/mipmap-mdpi/ic_open_floating_screen.webp
new file mode 100644
index 00000000..cd553b43
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/ic_open_floating_screen.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/ic_order_mic.webp b/moduleUtil/src/main/res/mipmap-mdpi/ic_order_mic.webp
new file mode 100644
index 00000000..3ee14f45
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/ic_order_mic.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/ic_order_mic_selected.webp b/moduleUtil/src/main/res/mipmap-mdpi/ic_order_mic_selected.webp
new file mode 100644
index 00000000..40a5b593
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/ic_order_mic_selected.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/ic_report.webp b/moduleUtil/src/main/res/mipmap-mdpi/ic_report.webp
new file mode 100644
index 00000000..59ebbcc9
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/ic_report.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/ic_room_setting.webp b/moduleUtil/src/main/res/mipmap-mdpi/ic_room_setting.webp
new file mode 100644
index 00000000..86d41826
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/ic_room_setting.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/ic_share.webp b/moduleUtil/src/main/res/mipmap-mdpi/ic_share.webp
new file mode 100644
index 00000000..a75f3408
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/ic_share.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/ic_sing.webp b/moduleUtil/src/main/res/mipmap-mdpi/ic_sing.webp
new file mode 100644
index 00000000..9d1e3cc5
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/ic_sing.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/ic_subsidy.webp b/moduleUtil/src/main/res/mipmap-mdpi/ic_subsidy.webp
new file mode 100644
index 00000000..eafadf83
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/ic_subsidy.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/ic_welcome.webp b/moduleUtil/src/main/res/mipmap-mdpi/ic_welcome.webp
new file mode 100644
index 00000000..1f30f3e6
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/ic_welcome.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/icon_arrow_right_3.webp b/moduleUtil/src/main/res/mipmap-mdpi/icon_arrow_right_3.webp
new file mode 100644
index 00000000..ce83dcb2
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/icon_arrow_right_3.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/muisc_set_up.webp b/moduleUtil/src/main/res/mipmap-mdpi/muisc_set_up.webp
new file mode 100644
index 00000000..c79df0b7
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/muisc_set_up.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/rank_h.webp b/moduleUtil/src/main/res/mipmap-mdpi/rank_h.webp
new file mode 100644
index 00000000..442aa08a
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/rank_h.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/red_tx.png b/moduleUtil/src/main/res/mipmap-mdpi/red_tx.png
new file mode 100644
index 00000000..d303fcd6
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/red_tx.png differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/rewar_1.webp b/moduleUtil/src/main/res/mipmap-mdpi/rewar_1.webp
new file mode 100644
index 00000000..fca43f9e
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/rewar_1.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/rewar_2.webp b/moduleUtil/src/main/res/mipmap-mdpi/rewar_2.webp
new file mode 100644
index 00000000..0f2b9254
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/rewar_2.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/rewar_3.webp b/moduleUtil/src/main/res/mipmap-mdpi/rewar_3.webp
new file mode 100644
index 00000000..34f98097
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/rewar_3.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/room_cd_jb.webp b/moduleUtil/src/main/res/mipmap-mdpi/room_cd_jb.webp
new file mode 100644
index 00000000..5bad1879
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/room_cd_jb.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/room_gift.webp b/moduleUtil/src/main/res/mipmap-mdpi/room_gift.webp
new file mode 100644
index 00000000..91306732
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/room_gift.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/room_message.webp b/moduleUtil/src/main/res/mipmap-mdpi/room_message.webp
new file mode 100644
index 00000000..ec5429b1
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/room_message.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/room_microphone.webp b/moduleUtil/src/main/res/mipmap-mdpi/room_microphone.webp
new file mode 100644
index 00000000..96c0f339
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/room_microphone.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/room_microphone_off.webp b/moduleUtil/src/main/res/mipmap-mdpi/room_microphone_off.webp
new file mode 100644
index 00000000..b1f1c6f2
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/room_microphone_off.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/room_mis.webp b/moduleUtil/src/main/res/mipmap-mdpi/room_mis.webp
new file mode 100644
index 00000000..9b0f4f24
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/room_mis.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/room_notice.webp b/moduleUtil/src/main/res/mipmap-mdpi/room_notice.webp
new file mode 100644
index 00000000..2edf343f
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/room_notice.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/room_pk.webp b/moduleUtil/src/main/res/mipmap-mdpi/room_pk.webp
new file mode 100644
index 00000000..aad4e468
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/room_pk.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/room_ranking_ist.webp b/moduleUtil/src/main/res/mipmap-mdpi/room_ranking_ist.webp
new file mode 100644
index 00000000..e9dd4f4c
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/room_ranking_ist.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/room_sett.webp b/moduleUtil/src/main/res/mipmap-mdpi/room_sett.webp
new file mode 100644
index 00000000..3f226bae
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/room_sett.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/room_voice_g.webp b/moduleUtil/src/main/res/mipmap-mdpi/room_voice_g.webp
new file mode 100644
index 00000000..56d441a0
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/room_voice_g.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/room_voice_kg.webp b/moduleUtil/src/main/res/mipmap-mdpi/room_voice_kg.webp
new file mode 100644
index 00000000..8908f4b6
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/room_voice_kg.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/room_xd.webp b/moduleUtil/src/main/res/mipmap-mdpi/room_xd.webp
new file mode 100644
index 00000000..f050e45b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/room_xd.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/task_lq.webp b/moduleUtil/src/main/res/mipmap-mdpi/task_lq.webp
new file mode 100644
index 00000000..753c16a3
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/task_lq.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/task_ylq.webp b/moduleUtil/src/main/res/mipmap-mdpi/task_ylq.webp
new file mode 100644
index 00000000..18aff363
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/task_ylq.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/text_bj.webp b/moduleUtil/src/main/res/mipmap-mdpi/text_bj.webp
new file mode 100644
index 00000000..64ddb59f
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/text_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-mdpi/tiaoyint.webp b/moduleUtil/src/main/res/mipmap-mdpi/tiaoyint.webp
new file mode 100644
index 00000000..f39ccb40
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-mdpi/tiaoyint.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xhdpi/ganga.webp b/moduleUtil/src/main/res/mipmap-xhdpi/ganga.webp
new file mode 100644
index 00000000..b772f675
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xhdpi/ganga.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xhdpi/gift_show_b.webp b/moduleUtil/src/main/res/mipmap-xhdpi/gift_show_b.webp
new file mode 100644
index 00000000..04c9dec0
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xhdpi/gift_show_b.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xhdpi/hourly_d.png b/moduleUtil/src/main/res/mipmap-xhdpi/hourly_d.png
new file mode 100644
index 00000000..b3c8c528
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xhdpi/hourly_d.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xhdpi/hourly_xlh_sta.webp b/moduleUtil/src/main/res/mipmap-xhdpi/hourly_xlh_sta.webp
new file mode 100644
index 00000000..b35e97ea
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xhdpi/hourly_xlh_sta.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xhdpi/hourly_xlh_status.png b/moduleUtil/src/main/res/mipmap-xhdpi/hourly_xlh_status.png
new file mode 100644
index 00000000..551d1088
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xhdpi/hourly_xlh_status.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xhdpi/huanhu.webp b/moduleUtil/src/main/res/mipmap-xhdpi/huanhu.webp
new file mode 100644
index 00000000..abb77754
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xhdpi/huanhu.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xhdpi/ic_launcher_app.webp b/moduleUtil/src/main/res/mipmap-xhdpi/ic_launcher_app.webp
new file mode 100644
index 00000000..c09ebc05
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xhdpi/ic_launcher_app.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xhdpi/jianjiao.webp b/moduleUtil/src/main/res/mipmap-xhdpi/jianjiao.webp
new file mode 100644
index 00000000..3309681c
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xhdpi/jianjiao.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xhdpi/momoda.webp b/moduleUtil/src/main/res/mipmap-xhdpi/momoda.webp
new file mode 100644
index 00000000..e61f4e51
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xhdpi/momoda.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xhdpi/rank_im_t1.webp b/moduleUtil/src/main/res/mipmap-xhdpi/rank_im_t1.webp
new file mode 100644
index 00000000..e5f5eab0
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xhdpi/rank_im_t1.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xhdpi/rank_im_t2.webp b/moduleUtil/src/main/res/mipmap-xhdpi/rank_im_t2.webp
new file mode 100644
index 00000000..40ef2ba1
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xhdpi/rank_im_t2.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xhdpi/rank_im_t3.webp b/moduleUtil/src/main/res/mipmap-xhdpi/rank_im_t3.webp
new file mode 100644
index 00000000..a47b6047
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xhdpi/rank_im_t3.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xhdpi/room_gift_wheat_bg.webp b/moduleUtil/src/main/res/mipmap-xhdpi/room_gift_wheat_bg.webp
new file mode 100644
index 00000000..04c9dec0
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xhdpi/room_gift_wheat_bg.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xhdpi/room_settr.webp b/moduleUtil/src/main/res/mipmap-xhdpi/room_settr.webp
new file mode 100644
index 00000000..834f841f
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xhdpi/room_settr.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xhdpi/top1.png b/moduleUtil/src/main/res/mipmap-xhdpi/top1.png
new file mode 100644
index 00000000..d72b1dd6
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xhdpi/top1.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xhdpi/top2.png b/moduleUtil/src/main/res/mipmap-xhdpi/top2.png
new file mode 100644
index 00000000..1f22194b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xhdpi/top2.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xhdpi/top3.png b/moduleUtil/src/main/res/mipmap-xhdpi/top3.png
new file mode 100644
index 00000000..1722d49b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xhdpi/top3.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xhdpi/xiaosheng.webp b/moduleUtil/src/main/res/mipmap-xhdpi/xiaosheng.webp
new file mode 100644
index 00000000..1e246980
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xhdpi/xiaosheng.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxhdpi/dcl.png b/moduleUtil/src/main/res/mipmap-xxhdpi/dcl.png
new file mode 100644
index 00000000..c481a850
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxhdpi/dcl.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxhdpi/dcl2.png b/moduleUtil/src/main/res/mipmap-xxhdpi/dcl2.png
new file mode 100644
index 00000000..2e39dad4
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxhdpi/dcl2.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxhdpi/dcl3.png b/moduleUtil/src/main/res/mipmap-xxhdpi/dcl3.png
new file mode 100644
index 00000000..3b8f44c0
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxhdpi/dcl3.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxhdpi/dcl4.png b/moduleUtil/src/main/res/mipmap-xxhdpi/dcl4.png
new file mode 100644
index 00000000..fd420efa
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxhdpi/dcl4.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxhdpi/dcl5.png b/moduleUtil/src/main/res/mipmap-xxhdpi/dcl5.png
new file mode 100644
index 00000000..3b8f44c0
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxhdpi/dcl5.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxhdpi/dcl6.png b/moduleUtil/src/main/res/mipmap-xxhdpi/dcl6.png
new file mode 100644
index 00000000..2e39dad4
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxhdpi/dcl6.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxhdpi/eye_close.webp b/moduleUtil/src/main/res/mipmap-xxhdpi/eye_close.webp
new file mode 100644
index 00000000..850273ed
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxhdpi/eye_close.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxhdpi/eye_visible.webp b/moduleUtil/src/main/res/mipmap-xxhdpi/eye_visible.webp
new file mode 100644
index 00000000..81150180
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxhdpi/eye_visible.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxhdpi/hourly_djs.png b/moduleUtil/src/main/res/mipmap-xxhdpi/hourly_djs.png
new file mode 100644
index 00000000..9cde5882
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxhdpi/hourly_djs.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxhdpi/hourly_num.png b/moduleUtil/src/main/res/mipmap-xxhdpi/hourly_num.png
new file mode 100644
index 00000000..dc600c80
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxhdpi/hourly_num.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxhdpi/hourly_top_bj.webp b/moduleUtil/src/main/res/mipmap-xxhdpi/hourly_top_bj.webp
new file mode 100644
index 00000000..a367eb04
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxhdpi/hourly_top_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxhdpi/hourly_wh.png b/moduleUtil/src/main/res/mipmap-xxhdpi/hourly_wh.png
new file mode 100644
index 00000000..dff1bf39
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxhdpi/hourly_wh.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxhdpi/ic_launcher_app.webp b/moduleUtil/src/main/res/mipmap-xxhdpi/ic_launcher_app.webp
new file mode 100644
index 00000000..7849d4b3
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxhdpi/ic_launcher_app.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxhdpi/ic_room_huangguan.webp b/moduleUtil/src/main/res/mipmap-xxhdpi/ic_room_huangguan.webp
new file mode 100644
index 00000000..5fa331b2
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxhdpi/ic_room_huangguan.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxhdpi/ic_room_xq_wno_female.webp b/moduleUtil/src/main/res/mipmap-xxhdpi/ic_room_xq_wno_female.webp
new file mode 100644
index 00000000..58804bca
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxhdpi/ic_room_xq_wno_female.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxhdpi/ic_room_xq_wno_male.webp b/moduleUtil/src/main/res/mipmap-xxhdpi/ic_room_xq_wno_male.webp
new file mode 100644
index 00000000..01069739
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxhdpi/ic_room_xq_wno_male.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxhdpi/room_ic_owner_offline.webp b/moduleUtil/src/main/res/mipmap-xxhdpi/room_ic_owner_offline.webp
new file mode 100644
index 00000000..d645a27d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxhdpi/room_ic_owner_offline.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxhdpi/zyx.png b/moduleUtil/src/main/res/mipmap-xxhdpi/zyx.png
new file mode 100644
index 00000000..a0e9f949
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxhdpi/zyx.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/a1img_9.9.png b/moduleUtil/src/main/res/mipmap-xxxhdpi/a1img_9.9.png
new file mode 100644
index 00000000..abc75f54
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/a1img_9.9.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/action_js.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/action_js.webp
new file mode 100644
index 00000000..a30458a3
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/action_js.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/activity_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/activity_bj.webp
new file mode 100644
index 00000000..c2d9fe9b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/activity_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/app_name_bg.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/app_name_bg.webp
new file mode 100644
index 00000000..a952911d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/app_name_bg.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/auction.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/auction.webp
new file mode 100644
index 00000000..5edc9eab
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/auction.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_1.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_1.webp
new file mode 100644
index 00000000..ebaf0615
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_1.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_2.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_2.webp
new file mode 100644
index 00000000..a3101467
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_2.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_3.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_3.webp
new file mode 100644
index 00000000..e688a168
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_3.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_bj1.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_bj1.webp
new file mode 100644
index 00000000..eec53932
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_bj1.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_bj2.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_bj2.webp
new file mode 100644
index 00000000..cccf29a1
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_bj2.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_bj3.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_bj3.webp
new file mode 100644
index 00000000..fd77580d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_bj3.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_cg.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_cg.webp
new file mode 100644
index 00000000..cafc32f3
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_cg.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_jt.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_jt.webp
new file mode 100644
index 00000000..4585f42a
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_jt.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_sb.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_sb.webp
new file mode 100644
index 00000000..65086f4e
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_sb.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_za.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_za.webp
new file mode 100644
index 00000000..60eefdee
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/auction_za.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/bangdan.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/bangdan.webp
new file mode 100644
index 00000000..818630c1
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/bangdan.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/bangdan3.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/bangdan3.webp
new file mode 100644
index 00000000..2b3bf05a
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/bangdan3.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/bangdan4.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/bangdan4.webp
new file mode 100644
index 00000000..9d7f5cf5
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/bangdan4.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/become.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/become.webp
new file mode 100644
index 00000000..55c5a95a
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/become.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/bg_wz.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/bg_wz.webp
new file mode 100644
index 00000000..0fe5aa56
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/bg_wz.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/bj.webp
new file mode 100644
index 00000000..3019eb27
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/but_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/but_bj.webp
new file mode 100644
index 00000000..dfee162c
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/but_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/cabin_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/cabin_bj.webp
new file mode 100644
index 00000000..53f67b9b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/cabin_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/cancel.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/cancel.webp
new file mode 100644
index 00000000..b9228a62
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/cancel.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/changpian.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/changpian.webp
new file mode 100644
index 00000000..289ff838
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/changpian.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/check_boy.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/check_boy.webp
new file mode 100644
index 00000000..dfb80355
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/check_boy.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/check_boy1.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/check_boy1.webp
new file mode 100644
index 00000000..bce15728
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/check_boy1.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/check_girl.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/check_girl.webp
new file mode 100644
index 00000000..e2ba2bb7
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/check_girl.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/check_girl1.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/check_girl1.webp
new file mode 100644
index 00000000..842e888e
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/check_girl1.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/chou_w.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/chou_w.webp
new file mode 100644
index 00000000..b0e2fa8d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/chou_w.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/chou_x.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/chou_x.webp
new file mode 100644
index 00000000..b0aa5846
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/chou_x.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/clogs_car.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/clogs_car.webp
new file mode 100644
index 00000000..875f2d71
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/clogs_car.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/collect.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/collect.webp
new file mode 100644
index 00000000..c4c3aadd
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/collect.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/collected.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/collected.webp
new file mode 100644
index 00000000..0ed830da
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/collected.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/common_et_clear_icon.png b/moduleUtil/src/main/res/mipmap-xxxhdpi/common_et_clear_icon.png
new file mode 100644
index 00000000..3eeefacc
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/common_et_clear_icon.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/common_switch_login_icon.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/common_switch_login_icon.webp
new file mode 100644
index 00000000..c7480337
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/common_switch_login_icon.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/concern_dialog.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/concern_dialog.webp
new file mode 100644
index 00000000..031026e8
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/concern_dialog.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/copy.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/copy.webp
new file mode 100644
index 00000000..6a1a85b1
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/copy.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/cp_room_tb.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/cp_room_tb.webp
new file mode 100644
index 00000000..d59192ca
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/cp_room_tb.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/cr_album.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/cr_album.webp
new file mode 100644
index 00000000..1a674e4d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/cr_album.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/create_album.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/create_album.webp
new file mode 100644
index 00000000..225766e0
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/create_album.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/cz_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/cz_bj.webp
new file mode 100644
index 00000000..66d193de
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/cz_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/dashu.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/dashu.webp
new file mode 100644
index 00000000..12a0f88f
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/dashu.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/dele_foot.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/dele_foot.webp
new file mode 100644
index 00000000..f326809a
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/dele_foot.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/dengj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/dengj.webp
new file mode 100644
index 00000000..1189c8ff
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/dengj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/dengt.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/dengt.webp
new file mode 100644
index 00000000..f624729a
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/dengt.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/diany.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/diany.webp
new file mode 100644
index 00000000..a9cb0517
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/diany.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/dianyf.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/dianyf.webp
new file mode 100644
index 00000000..7e60b10c
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/dianyf.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/dongtai_hudong_dianzan.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/dongtai_hudong_dianzan.webp
new file mode 100644
index 00000000..1cb80c50
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/dongtai_hudong_dianzan.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/dongtai_hudong_pinglun.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/dongtai_hudong_pinglun.webp
new file mode 100644
index 00000000..08b961ad
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/dongtai_hudong_pinglun.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/dongtai_hudong_yidianzan.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/dongtai_hudong_yidianzan.webp
new file mode 100644
index 00000000..b242ee1d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/dongtai_hudong_yidianzan.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/f_h.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/f_h.webp
new file mode 100644
index 00000000..b74ef255
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/f_h.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/fail.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/fail.webp
new file mode 100644
index 00000000..dd8ad198
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/fail.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/fangz.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/fangz.webp
new file mode 100644
index 00000000..59faaf67
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/fangz.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/firsh_bj.png b/moduleUtil/src/main/res/mipmap-xxxhdpi/firsh_bj.png
new file mode 100644
index 00000000..3fdac73e
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/firsh_bj.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/frb_bg.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/frb_bg.webp
new file mode 100644
index 00000000..ac968447
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/frb_bg.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/gg.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/gg.webp
new file mode 100644
index 00000000..ee52746f
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/gg.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_bj.webp
new file mode 100644
index 00000000..c628282c
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_mh.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_mh.webp
new file mode 100644
index 00000000..79d98890
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_mh.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_name_skzd.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_name_skzd.webp
new file mode 100644
index 00000000..bb85a639
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_name_skzd.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_name_syzc.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_name_syzc.webp
new file mode 100644
index 00000000..040838b6
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_name_syzc.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_name_tkzj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_name_tkzj.webp
new file mode 100644
index 00000000..43d21726
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_name_tkzj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_p_b.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_p_b.webp
new file mode 100644
index 00000000..7d7c2ece
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_p_b.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_sjzd.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_sjzd.webp
new file mode 100644
index 00000000..eec4af4d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_sjzd.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_syzc.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_syzc.webp
new file mode 100644
index 00000000..dfe9f091
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_syzc.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_tkzj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_tkzj.webp
new file mode 100644
index 00000000..c6538d39
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_tkzj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_wf.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_wf.webp
new file mode 100644
index 00000000..244b99d7
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/gift_wf.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/gly.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/gly.webp
new file mode 100644
index 00000000..1dc62ed5
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/gly.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/go_cz.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/go_cz.webp
new file mode 100644
index 00000000..956deec1
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/go_cz.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/go_lock.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/go_lock.webp
new file mode 100644
index 00000000..b3e9cd0f
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/go_lock.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/go_sl.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/go_sl.webp
new file mode 100644
index 00000000..28218bfc
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/go_sl.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/go_wc.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/go_wc.webp
new file mode 100644
index 00000000..79c7010d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/go_wc.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/go_yq.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/go_yq.webp
new file mode 100644
index 00000000..985175b9
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/go_yq.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/gongxiang.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/gongxiang.webp
new file mode 100644
index 00000000..dc2ba12f
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/gongxiang.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/gsui.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/gsui.webp
new file mode 100644
index 00000000..86bebf1a
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/gsui.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/guanx.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/guanx.webp
new file mode 100644
index 00000000..11bd9646
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/guanx.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/guanxiw_z.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/guanxiw_z.webp
new file mode 100644
index 00000000..1c0a5980
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/guanxiw_z.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/gux_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/gux_bj.webp
new file mode 100644
index 00000000..7dd2e722
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/gux_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/guxi_k.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/guxi_k.webp
new file mode 100644
index 00000000..c4937018
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/guxi_k.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/guxi_w.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/guxi_w.webp
new file mode 100644
index 00000000..01ca56fb
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/guxi_w.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/gx_xz_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/gx_xz_bj.webp
new file mode 100644
index 00000000..c9cbbf0b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/gx_xz_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/gz.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/gz.webp
new file mode 100644
index 00000000..e111ba06
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/gz.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/h_bj_b.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/h_bj_b.webp
new file mode 100644
index 00000000..2f292a74
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/h_bj_b.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/heab_t.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/heab_t.webp
new file mode 100644
index 00000000..4af1ceb7
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/heab_t.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/head_cc.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/head_cc.webp
new file mode 100644
index 00000000..9cfc7257
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/head_cc.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/headline_b.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/headline_b.webp
new file mode 100644
index 00000000..f1a97e0b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/headline_b.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/headline_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/headline_bj.webp
new file mode 100644
index 00000000..bb3573b5
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/headline_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/heave_b.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/heave_b.webp
new file mode 100644
index 00000000..b84fbedf
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/heave_b.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/heaven_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/heaven_bj.webp
new file mode 100644
index 00000000..03262c1b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/heaven_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/hg.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/hg.webp
new file mode 100644
index 00000000..d0c4287e
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/hg.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/home_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/home_bj.webp
new file mode 100644
index 00000000..8f7d78ea
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/home_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/home_gg.png b/moduleUtil/src/main/res/mipmap-xxxhdpi/home_gg.png
new file mode 100644
index 00000000..0b807d74
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/home_gg.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/home_phb.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/home_phb.webp
new file mode 100644
index 00000000..2ab555ec
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/home_phb.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/home_rm.png b/moduleUtil/src/main/res/mipmap-xxxhdpi/home_rm.png
new file mode 100644
index 00000000..cb6d6c3c
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/home_rm.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/home_xx.png b/moduleUtil/src/main/res/mipmap-xxxhdpi/home_xx.png
new file mode 100644
index 00000000..90703037
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/home_xx.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/hourl_top1.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/hourl_top1.webp
new file mode 100644
index 00000000..f4a43622
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/hourl_top1.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/hourl_top2.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/hourl_top2.webp
new file mode 100644
index 00000000..b01dab61
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/hourl_top2.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/hourl_top3.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/hourl_top3.webp
new file mode 100644
index 00000000..e68d1a9a
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/hourl_top3.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/ic_home_rank.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/ic_home_rank.webp
new file mode 100644
index 00000000..c544a856
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/ic_home_rank.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/ic_launcher_app.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/ic_launcher_app.webp
new file mode 100644
index 00000000..c0315c91
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/ic_launcher_app.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/ic_paidui_home.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/ic_paidui_home.webp
new file mode 100644
index 00000000..4b67ea34
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/ic_paidui_home.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/ic_question_mark.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/ic_question_mark.webp
new file mode 100644
index 00000000..6b5e021c
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/ic_question_mark.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/ic_topbar_back_dark.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/ic_topbar_back_dark.webp
new file mode 100644
index 00000000..7aac55e1
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/ic_topbar_back_dark.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_login_code.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_login_code.webp
new file mode 100644
index 00000000..5db27fc4
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_login_code.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_login_lick.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_login_lick.webp
new file mode 100644
index 00000000..9d31198b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_login_lick.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_login_user_new.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_login_user_new.webp
new file mode 100644
index 00000000..347cc2e8
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_login_user_new.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_me_trend_select.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_me_trend_select.webp
new file mode 100644
index 00000000..a6637fdd
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_me_trend_select.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_me_trend_unselect.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_me_trend_unselect.webp
new file mode 100644
index 00000000..349e97a9
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_me_trend_unselect.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_my_select.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_my_select.webp
new file mode 100644
index 00000000..15423695
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_my_select.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_my_un_select.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_my_un_select.webp
new file mode 100644
index 00000000..5000a9eb
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_my_un_select.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_news_select.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_news_select.webp
new file mode 100644
index 00000000..25768a61
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_news_select.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_news_un_select.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_news_un_select.webp
new file mode 100644
index 00000000..35f680cd
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/icon_news_un_select.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/im_delete.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/im_delete.webp
new file mode 100644
index 00000000..ea57092d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/im_delete.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/im_share.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/im_share.webp
new file mode 100644
index 00000000..b5d2694b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/im_share.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/im_transfer.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/im_transfer.webp
new file mode 100644
index 00000000..8581d276
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/im_transfer.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/im_zs.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/im_zs.webp
new file mode 100644
index 00000000..e13530ce
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/im_zs.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/imh_app_update.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/imh_app_update.webp
new file mode 100644
index 00000000..acf0e0e2
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/imh_app_update.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/index_close_youth.png b/moduleUtil/src/main/res/mipmap-xxxhdpi/index_close_youth.png
new file mode 100644
index 00000000..278f3182
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/index_close_youth.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/index_icon_search.png b/moduleUtil/src/main/res/mipmap-xxxhdpi/index_icon_search.png
new file mode 100644
index 00000000..7f222921
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/index_icon_search.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/index_img_room_mask.png b/moduleUtil/src/main/res/mipmap-xxxhdpi/index_img_room_mask.png
new file mode 100644
index 00000000..cf146e60
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/index_img_room_mask.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/index_level_search_o.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/index_level_search_o.webp
new file mode 100644
index 00000000..9734a821
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/index_level_search_o.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/index_youth.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/index_youth.webp
new file mode 100644
index 00000000..af24f6c9
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/index_youth.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/invite_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/invite_bj.webp
new file mode 100644
index 00000000..27cb815d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/invite_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/invite_k.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/invite_k.webp
new file mode 100644
index 00000000..336b5dc8
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/invite_k.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/invite_tx.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/invite_tx.webp
new file mode 100644
index 00000000..0251c1f4
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/invite_tx.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/jb.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/jb.webp
new file mode 100644
index 00000000..cd12a3ea
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/jb.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/jiangc.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiangc.webp
new file mode 100644
index 00000000..4a13bb4b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiangc.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/jiao_k_l.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiao_k_l.webp
new file mode 100644
index 00000000..37a3a2d5
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiao_k_l.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/jiao_k_r.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiao_k_r.webp
new file mode 100644
index 00000000..2154ed6f
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiao_k_r.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/jiao_kgx.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiao_kgx.webp
new file mode 100644
index 00000000..a1aad020
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiao_kgx.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/jiao_p1.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiao_p1.webp
new file mode 100644
index 00000000..d96a0c33
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiao_p1.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/jiao_p2.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiao_p2.webp
new file mode 100644
index 00000000..b506e34b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiao_p2.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_bj.webp
new file mode 100644
index 00000000..95e46080
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_djs.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_djs.webp
new file mode 100644
index 00000000..251fc02d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_djs.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_js.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_js.webp
new file mode 100644
index 00000000..af580280
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_js.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_ks.png b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_ks.png
new file mode 100644
index 00000000..6d9f64f2
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_ks.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_n.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_n.webp
new file mode 100644
index 00000000..9b029c83
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_n.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_tv_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_tv_bj.webp
new file mode 100644
index 00000000..2f5fd4b8
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_tv_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_x.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_x.webp
new file mode 100644
index 00000000..0e9902b6
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_x.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_xian.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_xian.webp
new file mode 100644
index 00000000..0209677d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_xian.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_yc.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_yc.webp
new file mode 100644
index 00000000..13070563
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiaoy_yc.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/jijang.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/jijang.webp
new file mode 100644
index 00000000..04cfe68b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/jijang.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/jiks.png b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiks.png
new file mode 100644
index 00000000..6e8ff70b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/jiks.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/jilu.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/jilu.webp
new file mode 100644
index 00000000..50137360
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/jilu.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/jinb_tg.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/jinb_tg.webp
new file mode 100644
index 00000000..12b2e89f
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/jinb_tg.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/jt_right.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/jt_right.webp
new file mode 100644
index 00000000..341d5269
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/jt_right.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/jub.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/jub.webp
new file mode 100644
index 00000000..c3bf738f
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/jub.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/jxz.png b/moduleUtil/src/main/res/mipmap-xxxhdpi/jxz.png
new file mode 100644
index 00000000..f7185e21
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/jxz.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/kagx.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/kagx.webp
new file mode 100644
index 00000000..c4cec0b5
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/kagx.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/ke_bg.png b/moduleUtil/src/main/res/mipmap-xxxhdpi/ke_bg.png
new file mode 100644
index 00000000..261800f6
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/ke_bg.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/ktv.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/ktv.webp
new file mode 100644
index 00000000..1b383a78
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/ktv.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/laoren.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/laoren.webp
new file mode 100644
index 00000000..a39a62d4
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/laoren.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/liang.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/liang.webp
new file mode 100644
index 00000000..5ee2b37e
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/liang.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/lianj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/lianj.webp
new file mode 100644
index 00000000..cce9e08d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/lianj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/line9.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/line9.webp
new file mode 100644
index 00000000..54ef84e2
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/line9.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/liwu.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/liwu.webp
new file mode 100644
index 00000000..be62c908
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/liwu.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/lock.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/lock.webp
new file mode 100644
index 00000000..bd69e94d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/lock.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/log_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/log_bj.webp
new file mode 100644
index 00000000..9d5f9333
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/log_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/login_btn_bg.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/login_btn_bg.webp
new file mode 100644
index 00000000..b524c499
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/login_btn_bg.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/login_log.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/login_log.webp
new file mode 100644
index 00000000..8175b1ea
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/login_log.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/login_title.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/login_title.webp
new file mode 100644
index 00000000..76799570
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/login_title.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/me_edit.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/me_edit.webp
new file mode 100644
index 00000000..b79e252d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/me_edit.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/me_my_bag.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/me_my_bag.webp
new file mode 100644
index 00000000..e5698b10
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/me_my_bag.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/me_show_store.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/me_show_store.webp
new file mode 100644
index 00000000..209a73f6
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/me_show_store.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/me_test.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/me_test.webp
new file mode 100644
index 00000000..08de7ab4
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/me_test.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/me_union_icon.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/me_union_icon.webp
new file mode 100644
index 00000000..400697eb
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/me_union_icon.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/me_wallet.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/me_wallet.webp
new file mode 100644
index 00000000..4d37de33
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/me_wallet.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/me_wallet_icon.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/me_wallet_icon.webp
new file mode 100644
index 00000000..fe1f5e90
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/me_wallet_icon.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/mess.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/mess.webp
new file mode 100644
index 00000000..e76953dd
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/mess.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/mony_cz.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/mony_cz.webp
new file mode 100644
index 00000000..5b355957
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/mony_cz.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/mony_tx.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/mony_tx.webp
new file mode 100644
index 00000000..23a13b6f
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/mony_tx.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/mu_yc.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/mu_yc.webp
new file mode 100644
index 00000000..7c417722
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/mu_yc.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/muis_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/muis_bj.webp
new file mode 100644
index 00000000..76506a79
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/muis_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/mx.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/mx.webp
new file mode 100644
index 00000000..ff949f9b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/mx.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/my_bag_item.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/my_bag_item.webp
new file mode 100644
index 00000000..cba558b0
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/my_bag_item.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/my_dan.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/my_dan.webp
new file mode 100644
index 00000000..d428212f
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/my_dan.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/my_home.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/my_home.webp
new file mode 100644
index 00000000..730a924e
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/my_home.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/nan.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/nan.webp
new file mode 100644
index 00000000..866d3d8c
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/nan.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/new_people_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/new_people_bj.webp
new file mode 100644
index 00000000..d8fc7169
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/new_people_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/no_follow.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/no_follow.webp
new file mode 100644
index 00000000..1c7d873d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/no_follow.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/not_liang.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/not_liang.webp
new file mode 100644
index 00000000..1e09ab4a
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/not_liang.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/not_unlocked.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/not_unlocked.webp
new file mode 100644
index 00000000..aa446544
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/not_unlocked.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/notifi.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/notifi.webp
new file mode 100644
index 00000000..3da47995
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/notifi.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/num_1.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/num_1.webp
new file mode 100644
index 00000000..ef97d4b7
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/num_1.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/num_11.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/num_11.webp
new file mode 100644
index 00000000..3f951d58
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/num_11.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/num_2.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/num_2.webp
new file mode 100644
index 00000000..c6fed70b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/num_2.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/num_22.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/num_22.webp
new file mode 100644
index 00000000..efea944c
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/num_22.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/nv.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/nv.webp
new file mode 100644
index 00000000..dd55717b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/nv.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/nvhai.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/nvhai.webp
new file mode 100644
index 00000000..033f24b2
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/nvhai.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/paih.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/paih.webp
new file mode 100644
index 00000000..d2913890
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/paih.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/paimai.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/paimai.webp
new file mode 100644
index 00000000..fac399fe
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/paimai.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/personality.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/personality.webp
new file mode 100644
index 00000000..d86ef41a
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/personality.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/ping.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/ping.webp
new file mode 100644
index 00000000..b97d579b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/ping.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_djs.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_djs.webp
new file mode 100644
index 00000000..263333f8
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_djs.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_left.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_left.webp
new file mode 100644
index 00000000..aad55a84
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_left.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_pj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_pj.webp
new file mode 100644
index 00000000..765739cf
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_pj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_right.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_right.webp
new file mode 100644
index 00000000..2367df67
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_right.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_sb.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_sb.webp
new file mode 100644
index 00000000..1325eb36
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_sb.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_sl.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_sl.webp
new file mode 100644
index 00000000..26b36570
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_sl.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_stop.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_stop.webp
new file mode 100644
index 00000000..a30458a3
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_stop.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_time_f.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_time_f.webp
new file mode 100644
index 00000000..66c601f3
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_time_f.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_time_t.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_time_t.webp
new file mode 100644
index 00000000..dd0af59a
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_time_t.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_vs.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_vs.webp
new file mode 100644
index 00000000..bc28b73b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/pk_vs.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/pp.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/pp.webp
new file mode 100644
index 00000000..8e6cbbcd
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/pp.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/qgrml.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/qgrml.webp
new file mode 100644
index 00000000..9dd4de0b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/qgrml.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/qipaokang.9.png b/moduleUtil/src/main/res/mipmap-xxxhdpi/qipaokang.9.png
new file mode 100644
index 00000000..0950ca89
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/qipaokang.9.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/qm_b.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/qm_b.webp
new file mode 100644
index 00000000..3adf2979
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/qm_b.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/qq.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/qq.webp
new file mode 100644
index 00000000..f3b9ac96
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/qq.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/quan.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/quan.webp
new file mode 100644
index 00000000..defd3071
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/quan.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/re_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/re_bj.webp
new file mode 100644
index 00000000..b0632b19
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/re_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/real_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/real_bj.webp
new file mode 100644
index 00000000..b1c55fb7
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/real_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/real_img.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/real_img.webp
new file mode 100644
index 00000000..ae4b5932
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/real_img.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/real_q.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/real_q.webp
new file mode 100644
index 00000000..cea6852f
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/real_q.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/red.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/red.webp
new file mode 100644
index 00000000..cfd2db08
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/red.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/red_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/red_bj.webp
new file mode 100644
index 00000000..25c06b8e
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/red_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/red_bj_h.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/red_bj_h.webp
new file mode 100644
index 00000000..ba61aafc
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/red_bj_h.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/red_bott_b.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/red_bott_b.webp
new file mode 100644
index 00000000..0791be61
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/red_bott_b.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/red_en.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/red_en.webp
new file mode 100644
index 00000000..c011c4ef
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/red_en.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/red_f_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/red_f_bj.webp
new file mode 100644
index 00000000..684d841e
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/red_f_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/red_im_bot.png b/moduleUtil/src/main/res/mipmap-xxxhdpi/red_im_bot.png
new file mode 100644
index 00000000..698dc672
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/red_im_bot.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/red_k.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/red_k.webp
new file mode 100644
index 00000000..99b829b9
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/red_k.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/red_k_top.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/red_k_top.webp
new file mode 100644
index 00000000..d6803626
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/red_k_top.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/red_kl_b.png b/moduleUtil/src/main/res/mipmap-xxxhdpi/red_kl_b.png
new file mode 100644
index 00000000..0ee0af4a
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/red_kl_b.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/red_liet_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/red_liet_bj.webp
new file mode 100644
index 00000000..af216fdc
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/red_liet_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/red_pp.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/red_pp.webp
new file mode 100644
index 00000000..0ebd0eb9
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/red_pp.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/red_text.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/red_text.webp
new file mode 100644
index 00000000..0b3276e1
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/red_text.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/red_top_b.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/red_top_b.webp
new file mode 100644
index 00000000..485b7a10
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/red_top_b.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/regit_t.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/regit_t.webp
new file mode 100644
index 00000000..b95bab6a
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/regit_t.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/release_n.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/release_n.webp
new file mode 100644
index 00000000..3ed7a01d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/release_n.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/ren.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/ren.webp
new file mode 100644
index 00000000..cee8c465
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/ren.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/renz.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/renz.webp
new file mode 100644
index 00000000..ca06ab05
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/renz.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/reyi.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/reyi.webp
new file mode 100644
index 00000000..0ce226b0
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/reyi.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_auction_jp.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_auction_jp.webp
new file mode 100644
index 00000000..f6fdd2b1
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_auction_jp.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_bj.webp
new file mode 100644
index 00000000..91cf3329
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_bt.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_bt.webp
new file mode 100644
index 00000000..b027ccde
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_bt.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_charm_b.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_charm_b.webp
new file mode 100644
index 00000000..79a55612
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_charm_b.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_colse.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_colse.webp
new file mode 100644
index 00000000..f10a725c
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_colse.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_dialog_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_dialog_bj.webp
new file mode 100644
index 00000000..45c92b6b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_dialog_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_dian.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_dian.webp
new file mode 100644
index 00000000..fa35a01e
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_dian.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_gb.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_gb.webp
new file mode 100644
index 00000000..1467981c
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_gb.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_gift_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_gift_bj.webp
new file mode 100644
index 00000000..c628282c
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_gift_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_gift_bjx.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_gift_bjx.webp
new file mode 100644
index 00000000..51ca52a5
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_gift_bjx.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_gift_select_bg.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_gift_select_bg.webp
new file mode 100644
index 00000000..bd0004ac
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_gift_select_bg.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_gz.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_gz.webp
new file mode 100644
index 00000000..84af9343
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_gz.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_ic_banner_point_normal.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_ic_banner_point_normal.webp
new file mode 100644
index 00000000..6002a882
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_ic_banner_point_normal.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_ic_banner_point_select.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_ic_banner_point_select.webp
new file mode 100644
index 00000000..94306aa9
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_ic_banner_point_select.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_ic_wheat_default.png b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_ic_wheat_default.png
new file mode 100644
index 00000000..d59d2839
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_ic_wheat_default.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_ic_wheat_default_suo.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_ic_wheat_default_suo.webp
new file mode 100644
index 00000000..6d12875a
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_ic_wheat_default_suo.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_jb.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_jb.webp
new file mode 100644
index 00000000..08c955bb
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_jb.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_kg.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_kg.webp
new file mode 100644
index 00000000..8875e40a
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_kg.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_lh.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_lh.webp
new file mode 100644
index 00000000..60965d15
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_lh.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_lt.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_lt.webp
new file mode 100644
index 00000000..27a3c7f6
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_lt.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_music_win_list.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_music_win_list.webp
new file mode 100644
index 00000000..31c4f9ef
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_music_win_list.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_music_win_next.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_music_win_next.webp
new file mode 100644
index 00000000..c3c2dffb
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_music_win_next.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_music_win_puase.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_music_win_puase.webp
new file mode 100644
index 00000000..ed0f8a4b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_music_win_puase.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_music_win_start.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_music_win_start.webp
new file mode 100644
index 00000000..f3d0b6cf
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_music_win_start.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_mx.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_mx.webp
new file mode 100644
index 00000000..8dfea5f7
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_mx.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_rank_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_rank_bj.webp
new file mode 100644
index 00000000..3447677c
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_rank_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_rig_jt.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_rig_jt.webp
new file mode 100644
index 00000000..9f475029
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_rig_jt.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_sl.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_sl.webp
new file mode 100644
index 00000000..9f1a3d3e
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_sl.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_sound_effects.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_sound_effects.webp
new file mode 100644
index 00000000..251c076b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_sound_effects.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_t.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_t.webp
new file mode 100644
index 00000000..0b50cafb
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_t.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_user_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_user_bj.webp
new file mode 100644
index 00000000..3adf2979
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_user_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_wheat_feeding.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_wheat_feeding.webp
new file mode 100644
index 00000000..1e956c54
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_wheat_feeding.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_wheat_feeding_up.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_wheat_feeding_up.webp
new file mode 100644
index 00000000..e9ede2d6
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_wheat_feeding_up.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_xsb.png b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_xsb.png
new file mode 100644
index 00000000..3ceede08
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_xsb.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/room_ygz.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_ygz.webp
new file mode 100644
index 00000000..fd473b39
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/room_ygz.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/sanwei.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/sanwei.webp
new file mode 100644
index 00000000..1b0a6c64
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/sanwei.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/sect_false.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/sect_false.webp
new file mode 100644
index 00000000..3a42c305
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/sect_false.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/sect_true.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/sect_true.webp
new file mode 100644
index 00000000..6ccbe04b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/sect_true.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/setting.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/setting.webp
new file mode 100644
index 00000000..86f69071
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/setting.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/setting_t.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/setting_t.webp
new file mode 100644
index 00000000..2a019455
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/setting_t.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/shanc.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/shanc.webp
new file mode 100644
index 00000000..8256a61d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/shanc.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/shanchu.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/shanchu.webp
new file mode 100644
index 00000000..f50c0d80
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/shanchu.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/shequ_dongtai_gengduo.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/shequ_dongtai_gengduo.webp
new file mode 100644
index 00000000..d4e4015d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/shequ_dongtai_gengduo.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/shij.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/shij.webp
new file mode 100644
index 00000000..cba0e884
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/shij.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/shouc.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/shouc.webp
new file mode 100644
index 00000000..8a5d43ad
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/shouc.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/shouchl.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/shouchl.webp
new file mode 100644
index 00000000..4a20746c
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/shouchl.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/shousz.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/shousz.webp
new file mode 100644
index 00000000..ede77127
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/shousz.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/shouyi.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/shouyi.webp
new file mode 100644
index 00000000..8d26cfeb
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/shouyi.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/showszc.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/showszc.webp
new file mode 100644
index 00000000..541ee95e
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/showszc.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/sign_icon_wechat.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/sign_icon_wechat.webp
new file mode 100644
index 00000000..fe8b9013
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/sign_icon_wechat.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/sign_icon_wechat_q.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/sign_icon_wechat_q.webp
new file mode 100644
index 00000000..fbe8ecb7
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/sign_icon_wechat_q.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/sign_icon_zfb.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/sign_icon_zfb.webp
new file mode 100644
index 00000000..e2862f7d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/sign_icon_zfb.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/six.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/six.webp
new file mode 100644
index 00000000..07b28ae5
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/six.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/skzd_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/skzd_bj.webp
new file mode 100644
index 00000000..f8d593df
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/skzd_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/skzd_left_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/skzd_left_bj.webp
new file mode 100644
index 00000000..d5d7ccba
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/skzd_left_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/skzd_rigth_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/skzd_rigth_bj.webp
new file mode 100644
index 00000000..0ab9ae11
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/skzd_rigth_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/skzd_w.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/skzd_w.webp
new file mode 100644
index 00000000..564c4259
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/skzd_w.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/skzd_x.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/skzd_x.webp
new file mode 100644
index 00000000..cc806e17
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/skzd_x.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/skzj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/skzj.webp
new file mode 100644
index 00000000..dc70661c
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/skzj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/skzj_gz.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/skzj_gz.webp
new file mode 100644
index 00000000..1a9cfaad
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/skzj_gz.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/skzj_z_b.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/skzj_z_b.webp
new file mode 100644
index 00000000..8f7d7d6d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/skzj_z_b.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/skzl_jc.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/skzl_jc.webp
new file mode 100644
index 00000000..93433487
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/skzl_jc.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/skzl_jl.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/skzl_jl.webp
new file mode 100644
index 00000000..f6291869
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/skzl_jl.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/suij.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/suij.webp
new file mode 100644
index 00000000..177bc89d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/suij.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/suo.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/suo.webp
new file mode 100644
index 00000000..41f580e5
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/suo.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/suound_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/suound_bj.webp
new file mode 100644
index 00000000..0d08ad47
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/suound_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/suound_bjs.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/suound_bjs.webp
new file mode 100644
index 00000000..19d8459c
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/suound_bjs.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/sure.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/sure.webp
new file mode 100644
index 00000000..86217d8b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/sure.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc.webp
new file mode 100644
index 00000000..06af9abc
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_bj.webp
new file mode 100644
index 00000000..e27fda4c
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_gz.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_gz.webp
new file mode 100644
index 00000000..2d80d856
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_gz.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_jc.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_jc.webp
new file mode 100644
index 00000000..640140fb
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_jc.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_jl.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_jl.webp
new file mode 100644
index 00000000..ef8996f1
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_jl.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_left_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_left_bj.webp
new file mode 100644
index 00000000..0da9b78c
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_left_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_rigth_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_rigth_bj.webp
new file mode 100644
index 00000000..b1a7bdcf
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_rigth_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_w.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_w.webp
new file mode 100644
index 00000000..5110cb5d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_w.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_x.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_x.webp
new file mode 100644
index 00000000..9a08b477
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_x.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_z_b.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_z_b.webp
new file mode 100644
index 00000000..7959a1bd
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/syzc_z_b.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/tab_dy.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/tab_dy.webp
new file mode 100644
index 00000000..9102569d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/tab_dy.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/tab_main_media_selected.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/tab_main_media_selected.webp
new file mode 100644
index 00000000..29a4483d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/tab_main_media_selected.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/tab_main_media_unselected.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/tab_main_media_unselected.webp
new file mode 100644
index 00000000..2d65556f
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/tab_main_media_unselected.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/tab_x.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/tab_x.webp
new file mode 100644
index 00000000..9d2b7c5d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/tab_x.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/task_cj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/task_cj.webp
new file mode 100644
index 00000000..1315c87b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/task_cj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/task_gj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/task_gj.webp
new file mode 100644
index 00000000..723922e0
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/task_gj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/task_gz.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/task_gz.webp
new file mode 100644
index 00000000..10cbc807
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/task_gz.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/task_lock.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/task_lock.webp
new file mode 100644
index 00000000..c4852c8e
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/task_lock.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/task_t.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/task_t.webp
new file mode 100644
index 00000000..5c5371f4
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/task_t.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/tianjhl.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/tianjhl.webp
new file mode 100644
index 00000000..08b52b5d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/tianjhl.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/time_b.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/time_b.webp
new file mode 100644
index 00000000..79e85080
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/time_b.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/tk_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/tk_bj.webp
new file mode 100644
index 00000000..0744ae2d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/tk_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzi_rigth_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzi_rigth_bj.webp
new file mode 100644
index 00000000..5e7faaf5
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzi_rigth_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzj.webp
new file mode 100644
index 00000000..cd6dd28b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzj_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzj_bj.webp
new file mode 100644
index 00000000..afe150e3
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzj_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzj_gz.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzj_gz.webp
new file mode 100644
index 00000000..d8c34662
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzj_gz.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzj_left_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzj_left_bj.webp
new file mode 100644
index 00000000..390ff4f7
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzj_left_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzj_w.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzj_w.webp
new file mode 100644
index 00000000..51352986
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzj_w.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzj_x.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzj_x.webp
new file mode 100644
index 00000000..872b78ac
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzj_x.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzj_z.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzj_z.webp
new file mode 100644
index 00000000..76d2e1fd
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzj_z.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzj_z_b.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzj_z_b.webp
new file mode 100644
index 00000000..70c9876c
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/tkzj_z_b.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/tour_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/tour_bj.webp
new file mode 100644
index 00000000..ff54a7eb
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/tour_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/tour_zj_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/tour_zj_bj.webp
new file mode 100644
index 00000000..a39af3ef
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/tour_zj_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/tt_c.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/tt_c.webp
new file mode 100644
index 00000000..12af8619
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/tt_c.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/tt_q.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/tt_q.webp
new file mode 100644
index 00000000..7bf025a2
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/tt_q.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/tt_yc.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/tt_yc.webp
new file mode 100644
index 00000000..471ed970
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/tt_yc.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/tx_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/tx_bj.webp
new file mode 100644
index 00000000..bc190f45
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/tx_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/unlocked.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/unlocked.webp
new file mode 100644
index 00000000..29b4b62b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/unlocked.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/up_t.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/up_t.webp
new file mode 100644
index 00000000..6586d052
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/up_t.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/up_x.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/up_x.webp
new file mode 100644
index 00000000..44a90f67
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/up_x.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/user_wait_header.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/user_wait_header.webp
new file mode 100644
index 00000000..4ebd9be0
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/user_wait_header.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/victory.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/victory.webp
new file mode 100644
index 00000000..d33bd74b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/victory.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/wheat_consent.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/wheat_consent.webp
new file mode 100644
index 00000000..235efc57
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/wheat_consent.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/wheat_refuse.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/wheat_refuse.webp
new file mode 100644
index 00000000..89edc558
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/wheat_refuse.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/wx.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/wx.webp
new file mode 100644
index 00000000..4f2734f4
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/wx.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/wx_zf.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/wx_zf.webp
new file mode 100644
index 00000000..6c0de896
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/wx_zf.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xiaohei_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/xiaohei_bj.webp
new file mode 100644
index 00000000..2a5689ca
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xiaohei_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xiaox_b.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/xiaox_b.webp
new file mode 100644
index 00000000..3af35bf5
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xiaox_b.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xiaox_bq.png b/moduleUtil/src/main/res/mipmap-xxxhdpi/xiaox_bq.png
new file mode 100644
index 00000000..d5954974
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xiaox_bq.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xinr.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/xinr.webp
new file mode 100644
index 00000000..2b4f79a8
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xinr.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xinrhl.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/xinrhl.webp
new file mode 100644
index 00000000..0a4ebde8
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xinrhl.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh.webp
new file mode 100644
index 00000000..e89bdacb
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_bd.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_bd.webp
new file mode 100644
index 00000000..bd7133eb
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_bd.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_cj_item.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_cj_item.webp
new file mode 100644
index 00000000..264a3b93
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_cj_item.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_cj_w.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_cj_w.webp
new file mode 100644
index 00000000..b0030dd9
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_cj_w.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_g_2.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_g_2.webp
new file mode 100644
index 00000000..69320e62
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_g_2.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_gb.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_gb.webp
new file mode 100644
index 00000000..94454f12
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_gb.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_gift_user.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_gift_user.webp
new file mode 100644
index 00000000..30001228
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_gift_user.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_gz.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_gz.webp
new file mode 100644
index 00000000..5a60ebb8
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_gz.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_hd.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_hd.webp
new file mode 100644
index 00000000..d63edac6
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_hd.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_image.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_image.webp
new file mode 100644
index 00000000..b455740a
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_image.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_jc.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_jc.webp
new file mode 100644
index 00000000..1aa64b34
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_jc.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_jjks.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_jjks.webp
new file mode 100644
index 00000000..cee22de9
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_jjks.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_jl.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_jl.webp
new file mode 100644
index 00000000..50137360
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_jl.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_num.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_num.webp
new file mode 100644
index 00000000..070afb5a
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_num.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_ob.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_ob.webp
new file mode 100644
index 00000000..1b8887f8
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_ob.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_rk_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_rk_bj.webp
new file mode 100644
index 00000000..99889a0b
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_rk_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_xj_x.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_xj_x.webp
new file mode 100644
index 00000000..b30ef5c0
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_xj_x.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_xz.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_xz.webp
new file mode 100644
index 00000000..04e6f2e9
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_xz.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_zl.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_zl.webp
new file mode 100644
index 00000000..8fab8929
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_zl.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_zsks.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_zsks.webp
new file mode 100644
index 00000000..459306b7
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlh_zsks.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xlhxd.png b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlhxd.png
new file mode 100644
index 00000000..ab3a55f2
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xlhxd.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xr_ykj.png b/moduleUtil/src/main/res/mipmap-xxxhdpi/xr_ykj.png
new file mode 100644
index 00000000..e7cc646d
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xr_ykj.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xr_ykj_xz.png b/moduleUtil/src/main/res/mipmap-xxxhdpi/xr_ykj_xz.png
new file mode 100644
index 00000000..3d129abb
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xr_ykj_xz.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xt.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/xt.webp
new file mode 100644
index 00000000..ab834ed1
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xt.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/xuni.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/xuni.webp
new file mode 100644
index 00000000..034a2f00
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/xuni.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/y_w.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/y_w.webp
new file mode 100644
index 00000000..192cf3d8
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/y_w.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/y_won.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/y_won.webp
new file mode 100644
index 00000000..533810a6
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/y_won.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/yic.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/yic.webp
new file mode 100644
index 00000000..9a523315
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/yic.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/yigz.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/yigz.webp
new file mode 100644
index 00000000..2e188b85
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/yigz.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/yishouc.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/yishouc.webp
new file mode 100644
index 00000000..0ed830da
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/yishouc.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/ylq.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/ylq.webp
new file mode 100644
index 00000000..7fd4f7d0
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/ylq.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/yq_pk.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/yq_pk.webp
new file mode 100644
index 00000000..6557646e
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/yq_pk.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/yuansheng.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/yuansheng.webp
new file mode 100644
index 00000000..a8c274fb
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/yuansheng.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/yujie.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/yujie.webp
new file mode 100644
index 00000000..d2cadf77
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/yujie.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/za_bj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/za_bj.webp
new file mode 100644
index 00000000..8ddf1aad
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/za_bj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/za_ljjp.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/za_ljjp.webp
new file mode 100644
index 00000000..79712dd3
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/za_ljjp.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/za_m.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/za_m.webp
new file mode 100644
index 00000000..5ba2ef23
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/za_m.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/za_maiw.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/za_maiw.webp
new file mode 100644
index 00000000..7be20d17
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/za_maiw.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/za_maiw_b.png b/moduleUtil/src/main/res/mipmap-xxxhdpi/za_maiw_b.png
new file mode 100644
index 00000000..8036144e
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/za_maiw_b.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/za_maiw_o.png b/moduleUtil/src/main/res/mipmap-xxxhdpi/za_maiw_o.png
new file mode 100644
index 00000000..988c3d18
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/za_maiw_o.png differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/za_p.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/za_p.webp
new file mode 100644
index 00000000..43c7014a
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/za_p.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/za_s.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/za_s.webp
new file mode 100644
index 00000000..e747ce50
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/za_s.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/za_t.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/za_t.webp
new file mode 100644
index 00000000..893f253c
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/za_t.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/zc.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/zc.webp
new file mode 100644
index 00000000..d3a80df0
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/zc.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/zhaop.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/zhaop.webp
new file mode 100644
index 00000000..9011e26e
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/zhaop.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/zhensgh.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/zhensgh.webp
new file mode 100644
index 00000000..c039642e
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/zhensgh.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/zhid.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/zhid.webp
new file mode 100644
index 00000000..0fa24ed2
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/zhid.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/zs.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/zs.webp
new file mode 100644
index 00000000..5fd2f553
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/zs.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/zs_tb.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/zs_tb.webp
new file mode 100644
index 00000000..ebffa171
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/zs_tb.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/zubj.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/zubj.webp
new file mode 100644
index 00000000..481cd1d1
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/zubj.webp differ
diff --git a/moduleUtil/src/main/res/mipmap-xxxhdpi/zus_tg.webp b/moduleUtil/src/main/res/mipmap-xxxhdpi/zus_tg.webp
new file mode 100644
index 00000000..24bd6f99
Binary files /dev/null and b/moduleUtil/src/main/res/mipmap-xxxhdpi/zus_tg.webp differ
diff --git a/moduleUtil/src/main/res/raw/red_packet_come.MP3 b/moduleUtil/src/main/res/raw/red_packet_come.MP3
new file mode 100644
index 00000000..88db62da
Binary files /dev/null and b/moduleUtil/src/main/res/raw/red_packet_come.MP3 differ
diff --git a/moduleUtil/src/main/res/values-v23/themes.xml b/moduleUtil/src/main/res/values-v23/themes.xml
new file mode 100644
index 00000000..5f66de92
--- /dev/null
+++ b/moduleUtil/src/main/res/values-v23/themes.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleroom/src/main/java/com/example/moduleroom/activity/RedResultActivity.java b/moduleroom/src/main/java/com/example/moduleroom/activity/RedResultActivity.java
new file mode 100644
index 00000000..a01a3023
--- /dev/null
+++ b/moduleroom/src/main/java/com/example/moduleroom/activity/RedResultActivity.java
@@ -0,0 +1,113 @@
+package com.example.moduleroom.activity;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import com.alibaba.android.arouter.facade.annotation.Autowired;
+import com.alibaba.android.arouter.facade.annotation.Route;
+import com.example.moduleroom.R;
+import com.example.moduleroom.adapter.RedAdapter;
+import com.example.moduleroom.contacts.RedEnvelopesContacts;
+import com.example.moduleroom.databinding.FragmentRedBinding;
+import com.example.moduleroom.fragment.RedViewModel;
+import com.example.moduleroom.presenter.RedEnvelopesPresenter;
+import com.scwang.smartrefresh.layout.api.RefreshLayout;
+import com.scwang.smartrefresh.layout.listener.OnRefreshLoadMoreListener;
+import com.xscm.moduleutil.activity.BaseMvpActivity;
+import com.xscm.moduleutil.base.BaseMvpFragment;
+import com.xscm.moduleutil.bean.RedpacketDetail;
+import com.xscm.moduleutil.utils.ARouteConstants;
+import com.xscm.moduleutil.utils.ImageUtils;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author qx
+ * @data 2025/9/29
+ * @description:红包最终的展示页面
+ */
+@Route(path = ARouteConstants.ROOM_RED_RESULT)
+public class RedResultActivity extends BaseMvpActivity implements RedEnvelopesContacts.View {
+
+ private RedViewModel mViewModel;
+ private RedAdapter redAdapter;
+ private int page = 1;
+ private String redpacketId;
+
+// public static RedResultActivity newInstance() {
+// return new RedResultActivity();
+// }
+
+
+
+ @Override
+ protected void initData() {
+ redpacketId = getIntent().getStringExtra("redpacketId");
+ if (redpacketId == null || redpacketId.isEmpty()) {
+ // 处理红包ID为空的情况
+ return;
+ }
+ if (MvpPre==null){
+ MvpPre = new RedEnvelopesPresenter(this, this);
+ }
+ MvpPre.getRedpacketDetail(redpacketId);
+ }
+
+ @Override
+ protected RedEnvelopesPresenter bindPresenter() {
+ return new RedEnvelopesPresenter(this, this);
+ }
+
+ @Override
+ protected void initView() {
+ mBinding.recyclerView.setLayoutManager(new LinearLayoutManager(this));
+ redAdapter = new RedAdapter();
+ mBinding.recyclerView.setAdapter(redAdapter);
+ // 确保最后一项完全可见
+ mBinding.recyclerView.setClipToPadding(false);
+ mBinding.recyclerView.setPadding(
+ 0,
+ getResources().getDimensionPixelSize(com.xscm.moduleutil.R.dimen.dp_12),
+ 0,
+ getResources().getDimensionPixelSize(com.xscm.moduleutil.R.dimen.dp_12)
+ );
+ mBinding.smartRefreshLayout.setOnRefreshLoadMoreListener(new OnRefreshLoadMoreListener() {
+
+ @Override
+ public void onRefresh(@NonNull @NotNull RefreshLayout refreshLayout) {
+ page = 1;
+// MvpPre.getRoomHourRanking(page+"", "20");
+ }
+
+ @Override
+ public void onLoadMore(@NonNull @NotNull RefreshLayout refreshLayout) {
+ page++;
+// MvpPre.getRoomHourRanking(page+"", "20");
+ }
+ });
+ }
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.fragment_red;
+ }
+
+ @Override
+ public void redPacketDetail(RedpacketDetail redpacketDetail) {
+ // 检查 Activity 是否已经销毁
+ if (isFinishing() || isDestroyed()) {
+ return;
+ }
+ if (redpacketDetail != null) {
+ ImageUtils.loadHeadCC(redpacketDetail.getRedpacket_info().getAvatar(), mBinding.userAvatar);
+ mBinding.userName.setText(redpacketDetail.getRedpacket_info().getNickname());
+ mBinding.tvRedTitle.setText(redpacketDetail.getRedpacket_info().getRemark());
+ mBinding.tvJb.setText(redpacketDetail.getRedpacket_info().getCoin_type() == 1 ? "金币" : "钻石");
+ if (redpacketDetail.getMy_record() != null) {
+ mBinding.tvRedJb.setText(redpacketDetail.getMy_record().getAmount());
+ }else {
+ mBinding.tvRedJb.setText("0.00");
+ }
+ mBinding.tvLq.setText("已领取"+redpacketDetail.getRecords().size() + "/" + redpacketDetail.getRedpacket_info().getTotal_count());
+ redAdapter.setNewData(redpacketDetail.getRecords());
+ }
+ }
+}
\ No newline at end of file
diff --git a/moduleroom/src/main/java/com/example/moduleroom/activity/RoomActivity.kt b/moduleroom/src/main/java/com/example/moduleroom/activity/RoomActivity.kt
new file mode 100644
index 00000000..df7a3a0c
--- /dev/null
+++ b/moduleroom/src/main/java/com/example/moduleroom/activity/RoomActivity.kt
@@ -0,0 +1,4516 @@
+package com.example.moduleroom.activity
+
+import android.Manifest
+import android.annotation.SuppressLint
+import android.app.ActivityManager
+import android.app.AlertDialog
+import android.content.Context
+import android.content.DialogInterface
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.res.Configuration
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.os.*
+import android.text.Spannable
+import android.text.SpannableStringBuilder
+import android.text.TextUtils
+import android.text.style.ForegroundColorSpan
+import android.util.Log
+import android.view.*
+import android.view.inputmethod.InputMethodManager
+import android.widget.Button
+import android.widget.EditText
+import android.widget.FrameLayout
+import android.widget.ImageView
+import androidx.activity.OnBackPressedCallback
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.core.content.ContextCompat
+import androidx.fragment.app.DialogFragment
+import androidx.fragment.app.Fragment
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.alibaba.android.arouter.facade.annotation.Autowired
+import com.alibaba.android.arouter.facade.annotation.Route
+import com.alibaba.android.arouter.launcher.ARouter
+import com.blankj.utilcode.util.GsonUtils
+import com.blankj.utilcode.util.LogUtils
+import com.blankj.utilcode.util.ThreadUtils
+import com.blankj.utilcode.util.TimeUtils
+import com.chad.library.adapter.base.BaseQuickAdapter
+import com.example.moduleroom.R
+import com.example.moduleroom.contacts.RoomContacts
+import com.example.moduleroom.databinding.ActivityRoomBinding
+import com.example.moduleroom.dialog.*
+import com.example.moduleroom.dialog.ExitRoomBottomSheet.OnOptionSelectedListener
+import com.example.moduleroom.fragment.*
+import com.example.moduleroom.presenter.RoomPresenter
+import com.example.moduleroom.service.ForegroundService
+import com.example.moduleroom.service.RoomPlayService
+import com.hjq.toast.ToastUtils
+import com.liulishuo.okdownload.OkDownloadProvider
+import com.orhanobut.logger.Logger
+import com.petterp.floatingx.assist.helper.FxScopeHelper
+import com.petterp.floatingx.listener.control.IFxControl
+import com.tencent.imsdk.v2.V2TIMManager
+import com.tencent.imsdk.v2.V2TIMSDKListener
+import com.tencent.imsdk.v2.V2TIMUserFullInfo
+import com.tencent.imsdk.v2.V2TIMValueCallback
+import com.xscm.moduleutil.activity.BaseMvpActivity
+import com.xscm.moduleutil.adapter.CommonPageAdapter
+import com.xscm.moduleutil.adapter.LikeUserAdapter
+import com.xscm.moduleutil.base.AppStateListener
+import com.xscm.moduleutil.base.AppStateManager
+import com.xscm.moduleutil.base.CommonAppContext
+import com.xscm.moduleutil.base.RoomManager
+import com.xscm.moduleutil.bean.*
+import com.xscm.moduleutil.bean.RoomMessageEvent.T
+import com.xscm.moduleutil.bean.RoomMessageEvent.text
+import com.xscm.moduleutil.bean.room.*
+import com.xscm.moduleutil.bean.room.FriendInfo.HeartList
+import com.xscm.moduleutil.bean.room.RoomAuction.AuctionListBean
+import com.xscm.moduleutil.bean.room.RoomAuction.AuctionUserBean
+import com.xscm.moduleutil.color.ThemeableDrawableUtils
+import com.xscm.moduleutil.dialog.ConfirmDialog
+import com.xscm.moduleutil.dialog.RechargeDialogFragment
+import com.xscm.moduleutil.dialog.giftLottery.GiftLotteryDialog
+import com.xscm.moduleutil.dialog.giftLottery.TourClubDialogFragment
+import com.xscm.moduleutil.event.*
+import com.xscm.moduleutil.event.RoomWheatEvent
+import com.xscm.moduleutil.http.BaseObserver
+import com.xscm.moduleutil.http.RetrofitClient
+import com.xscm.moduleutil.interfaces.OnMusicItemClickListener
+import com.xscm.moduleutil.listener.MessageListenerSingleton
+import com.xscm.moduleutil.listener.MessageListenerSingleton.OnMessageReceivedListener
+import com.xscm.moduleutil.rtc.AgoraManager
+import com.xscm.moduleutil.rtc.MusicPlayBean
+import com.xscm.moduleutil.service.MyRoomSingleton
+import com.xscm.moduleutil.utils.*
+import com.xscm.moduleutil.utils.roomview.GiftDisplayManager
+import com.xscm.moduleutil.widget.*
+import com.xscm.moduleutil.widget.ViewUtils.OnViewCreatedListener
+import com.xscm.moduleutil.widget.floatingView.Floa
+import io.agora.musiccontentcenter.Music
+import io.reactivex.disposables.Disposable
+import org.greenrobot.eventbus.EventBus
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+import pub.devrel.easypermissions.AppSettingsDialog
+import pub.devrel.easypermissions.EasyPermissions
+import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks
+import java.lang.ref.WeakReference
+import java.nio.charset.StandardCharsets
+import java.util.*
+import java.util.stream.Collectors
+
+@Route(path = ARouteConstants.ROOM_DETAILS)
+class RoomActivity : BaseMvpActivity(),
+ RoomContacts.View, PermissionCallbacks, OnMessageReceivedListener, QXRedPacketManager.QXRedPacketManagerDelegate {
+ private var roomFragment: RoomFragment? = null
+ var commonPageAdapter: CommonPageAdapter? = null
+ private var mRoomBean: RoomBean? = null
+
+ //房主信息
+ private var mRoomOwnerBean: RoomOwnerBean? = null
+
+ //房间用户信息
+ private var mRoomUserBean: RoomUserBean? = null
+ private var mPitList: List = ArrayList()
+
+ @JvmField
+ @Autowired
+ var password: String? = null
+
+ @JvmField
+ @Autowired
+ var roomId: String? = null
+
+ @JvmField
+ @Autowired
+ var mRoomInfoResp: RoomInfoResp? = null
+
+ @JvmField
+ @Autowired
+ var taskId: String? = null
+
+ var likeUserAdapter: LikeUserAdapter? = null
+ var permissions: Array = arrayOf(Manifest.permission.RECORD_AUDIO)
+ private var isSave = false //活动是否将被系统回收
+
+ private var musicWindowControl: IFxControl? = null
+ private var customMusicFloatingView: CustomMusicFloatingView? = null
+ private var number = 0
+
+ private var floatingMagnetView: Floa? = null
+ private var fullScreenContainer: FrameLayout? = null
+ private var isFullScreen = false
+ private var ivExitFullscreen: ImageView? = null
+ var ivQuan: ImageView? = null
+ private var imYc = false
+
+ @JvmField
+ @Autowired
+ var isOnline: Boolean = false
+
+ private var silentCountDownTimer: SilentCountDownTimer? = null
+ private var circularProgress: CircularProgressView? = null
+ private var publicScreenFragment: PublicScreenEaseChatFragment? = null // 添加成员变量
+
+ // 添加成员变量
+ private var isLayoutAdjusted = false
+
+ // 存储当前显示的弹框引用
+ private val activeDialogs: MutableList = ArrayList()
+ private val activeDialogFragments: MutableList = ArrayList()
+
+ private var isMinimized = false
+ private var appStateListener: AppStateListener? = null
+
+ private var qxRedPacketManager: QXRedPacketManager? = null
+
+
+ // 添加弹框到管理列表
+ fun addActiveDialog(dialog: DialogInterface) {
+ activeDialogs.add(dialog)
+ }
+
+ fun addActiveDialogFragment(dialogFragment: Fragment) {
+ activeDialogFragments.add(dialogFragment)
+ }
+
+
+
+ private fun resumeRoomFromMinimize() {
+ // 从最小化状态恢复房间
+ isMinimized = false
+ clearMinimizeState()
+
+ // 恢复房间状态
+ resumeRoomState()
+
+ // 确保UI正确显示
+ if (mBinding != null) {
+ // 恢复UI状态
+ }
+ }
+
+ private var bgEffectView: View? = null
+
+ private fun setupEffectView() {
+ bgEffectView = null
+ if (bgEffectView == null) {
+ // 获取单例管理器
+ val manager = QXGiftPlayerManager.getInstance(applicationContext)
+
+ // 获取背景特效视图并添加到布局中
+ bgEffectView = manager.defaultBgEffectView
+ // 找到 mBinding.svgaGift 的父容器
+ val parent = mBinding!!.svgaGift.parent
+ if (parent is ViewGroup) {
+ val parentViewGroup = parent
+ // 检查 bgEffectView 是否已经有父视图
+ val currentParent = bgEffectView?.getParent()
+ if (currentParent != null && currentParent is ViewGroup) {
+ // 如果已经有父视图,先从父视图中移除
+ currentParent.removeView(bgEffectView)
+ }
+
+ // 确保 bgEffectView 不为 null 并且没有父视图后再添加
+ if (bgEffectView != null) {
+ // 将 bgEffectView 添加为 mBinding.svgaGift 的兄弟视图
+ // 添加到 mBinding.svgaGift 的父容器中,位置在 mBinding.svgaGift 之前
+ parentViewGroup.addView(
+ bgEffectView,
+ parentViewGroup.indexOfChild(mBinding!!.svgaGift)
+ )
+ }
+
+ // 设置布局参数 - 填满父视图
+ val params = FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT
+ )
+ bgEffectView?.setLayoutParams(params)
+ } else {
+ LogUtils.e("mBinding.svgaGift 没有有效的父容器")
+ return
+ }
+ // 获取全屏特效视图
+ val fullEffectView = manager.defaultFullEffectView
+
+ // 设置全屏特效视图的布局参数 - 居中并设置尺寸
+ val fullParams = FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ )
+ fullParams.gravity = Gravity.CENTER
+ fullEffectView.layoutParams = fullParams
+
+ // 获取聊天特效视图
+ val chatEffectView = manager.defaultChatEffectView
+
+ // 设置聊天特效视图的布局参数 - 底部居中并设置尺寸
+ val chatParams = FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ )
+ chatParams.gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
+ chatEffectView.layoutParams = chatParams
+ }
+
+ // 从SharedPreferences获取是否关闭特效的设置
+ val isClose = SpUtil.getOpenEffect() != 1
+ QXGiftPlayerManager.getInstance(applicationContext).openOrCloseEffectViewWith(!isClose)
+ }
+
+ override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
+ if (keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_DOWN) {
+ // 拦截返回键,显示退出对话框而不是直接退出
+ showExitRoomDialog()
+ return true
+ }
+ return false
+ }
+
+ // 添加一个标记,用于判断用户是否主动离开应用
+ private var userLeaving = false
+
+ override fun onUserLeaveHint() {
+ super.onUserLeaveHint()
+ LogUtils.e("RoomActivity", "onUserLeaveHint")
+ // 当用户主动离开应用时(例如按下Home键),设置标记
+ userLeaving = true
+ }
+
+ override fun onPause() {
+ super.onPause()
+ LogUtils.e("RoomActivity", "onPause")
+
+ if (mRoomInfoResp != null) {
+ if (mRoomInfoResp!!.room_info.type_id != "6") {
+ // 只有在用户主动离开应用时才执行最小化操作
+ if (!userLeaving) {
+ // 保持Activity alive,不调用finish()
+
+ minimizeToBackground()
+
+ userLeaving = false // 重置标记
+ }
+ }
+ userLeaving = true
+ }
+ }
+
+ fun tob() {
+ val stub = mBinding!!.roomTop.stubButtons2
+ stub.visibility = View.VISIBLE
+ val imActionJs = mBinding!!.roomTop.imActionJs.findViewById(R.id.im_action_js)
+ val imActionYs = mBinding!!.roomTop.imActionYs.findViewById(R.id.im_action_ys)
+ if (imActionJs != null && imActionYs != null) {
+ imActionJs.setOnClickListener { dialogEnd() }
+ imActionYs.setOnClickListener {
+ MvpPre!!.auctionDelay(
+ SpUtil.getauctionId()
+ )
+ }
+ }
+ }
+
+ fun upTop() {
+ mBinding!!.roomTop.stubButtons2.visibility = View.GONE
+ }
+
+ private fun dialogEnd() {
+ // 创建并显示确认对话框
+ ConfirmDialog(
+ this,
+ "提示",
+ "您确定要结束本次拍卖吗?",
+ "确认",
+ "取消",
+ { v: View? ->
+ // 点击“确认”按钮时执行删除操作
+ MvpPre!!.auctionEnd(SpUtil.getauctionId(), roomId)
+ },
+ { v: View? -> }, false, 0
+ ).show()
+ }
+
+ fun upVisibility(visible: Boolean) {
+ mBinding!!.roomTop.imActionJs.visibility =
+ if (visible) View.VISIBLE else View.INVISIBLE
+ mBinding!!.roomTop.imActionYs.visibility =
+ if (visible) View.VISIBLE else View.INVISIBLE
+ }
+
+ fun upJs(visible: Boolean) {
+ mBinding!!.roomTop.imActionJs.visibility =
+ if (visible) View.VISIBLE else View.INVISIBLE
+ }
+
+ fun upYs(visible: Boolean) {
+ mBinding!!.roomTop.imActionYs.visibility =
+ if (visible) View.VISIBLE else View.INVISIBLE
+ }
+
+ /** 最小化 */
+ private fun showExitRoomDialog() {
+ if (mRoomInfoResp!!.room_info.type_id.equals("6")) {
+ val bottomSheet = ExitRoomBottomSheet.newInstance(false, true, true);
+ bottomSheet.setOnOptionSelectedListener(object : OnOptionSelectedListener {
+ override fun onMinimize() {
+ }
+
+ override fun onExitRoom() {
+ // 调用退出房间方法
+// MvpPre.quitRoom(roomId, SpUtil.getUserId() + "");
+
+ // 真正退出房间
+ // 调用退出房间方法
+ MessageListenerSingleton.quitGroup(roomId);
+ quit();
+ if (mRoomInfoResp!!.getRoom_info()
+ .getLabel_id() != null && mRoomInfoResp!!.getRoom_info().getLabel_id()
+ .equals("5")
+ ) {
+ jiaR();
+ return;
+ } else {
+ performExitRoom(1);
+ }
+// performExitRoom(1)
+ }
+
+ override fun onCancel() {
+ // 用户点击取消,不做任何事
+ }
+ })
+
+ return
+ }
+
+ val bottomSheet = ExitRoomBottomSheet.newInstance()
+ bottomSheet.setOnOptionSelectedListener(object : OnOptionSelectedListener {
+ override fun onMinimize() {
+ // 处理最小化逻辑,比如不销毁 Activity,仅移至后台
+ minimizeToBackground()
+ }
+
+ override fun onExitRoom() {
+ performExitRoom(1)
+ }
+
+ override fun onCancel() {
+ // 用户点击取消,不做任何事
+ }
+ })
+ bottomSheet.show(supportFragmentManager, "ExitRoomBottomSheet")
+ addActiveDialogFragment(bottomSheet)
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ val currentFragment =
+ supportFragmentManager.findFragmentById(R.id.vp_room_pager) // 替换为你实际的容器 ID
+ if (currentFragment is RoomCabinFragment) {
+ currentFragment.onConfigurationChanged(newConfig)
+ initPublicScreenFragment()
+ }
+ }
+
+ fun clearData() {
+ publicScreenFragment!!.someMethod()
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ fun roomInfoEvent(surfaceView: SurfaceEvent) {
+ if (mRoomInfoResp!!.room_info.type_id == "6") { //判断是否是电影房
+ floatingMagnetView = findViewById(R.id.flaoat) //电影房
+ if (mRoomInfoResp!!.user_info.is_room_owner != 1) { //判断是不是房主,1:是 如何是,不展示 0不是,展示布局
+ if (surfaceView.type != 1) {
+ val container =
+ floatingMagnetView?.findViewById(R.id.fl_screenshare)
+ // mBinding.flaoat.setVisibility(GONE);//展示或不展示
+ if (surfaceView == null) {
+ runOnUiThread {
+ mBinding!!.flaoat.visibility = View.GONE
+ container?.removeAllViews()
+ }
+ } else {
+ runOnUiThread {
+ mBinding!!.flaoat.visibility = View.VISIBLE
+ container?.removeAllViews()
+ container?.addView(surfaceView.surfaceView)
+ }
+ }
+ } else {
+ floatingMagnetView?.setVisibility(View.GONE)
+ }
+ }
+ }
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ fun hideInput(event: RoomInputHideEvent) {
+ if (event.hide) {
+ mBinding!!.vpRoomPager.isScrollContainer = false
+ } else {
+ mBinding!!.vpRoomPager.isScrollContainer = true
+ }
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ fun roomInfoEvent(messageEvent: ColoseCardEvent?) {
+ mBinding!!.flaoat.visibility = View.GONE
+ exitFullScreen()
+ }
+
+ /**
+ * 释放当前房间
+ */
+ private fun releaseRoom() {
+ AgoraManager.getInstance(this).cleanup()
+ CommonAppContext.getInstance().isPlaying = false
+ CommonAppContext.getInstance().isShow = false
+ QXGiftPlayerManager.getInstance(applicationContext).destroyEffectSvga()
+ cleanupResources()
+ }
+ private lateinit var giftManager: GiftDisplayManager
+ private val testHandler = Handler()
+ private var testRunnable: Runnable? = null
+
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ // 在super.onCreate之前设置主题以避免闪白屏
+ setTheme(com.xscm.moduleutil.R.style.BaseAppTheme) // 设置你的主主题
+ super.onCreate(savedInstanceState)
+ // // 进入房间10s后检查是否显示提示上麦对话框
+ LogUtils.e("RoomActivity", "onCreate")
+
+ isSave = false
+ sDestroied = false
+ isMinimized = false
+ overridePendingTransition(0, 0) // 关闭转场动画
+ startKeepLiveService() //保活
+
+ window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN)
+ sActivityRef = WeakReference(this)
+
+
+ // 检查是否有保存的最小化状态
+ checkAndRestoreMinimizeState()
+ // 获取传递的房间数据
+ // 在子线程中执行网络请求
+ performNetworkRequestsAsync()
+ roomFragment = RoomFragment.newInstance()
+ supportFragmentManager
+ .beginTransaction()
+ .replace(R.id.vp_room_pager, roomFragment!!)
+ .commitAllowingStateLoss()
+ // 使用新的 OnBackPressedDispatcher API 来处理返回事件
+ if (onBackPressedDispatcher != null) {
+ onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
+ override fun handleOnBackPressed() {
+ // 拦截返回键,显示退出对话框而不是直接退出
+ showExitRoomDialog()
+ }
+ })
+ }
+
+ // 获取Application实例并设置监听器
+ val app = application as CommonAppContext
+ appStateListener = AppStateManager.getInstance()
+ app.setAppStateListener(appStateListener)
+
+ // 通知RoomActivity已创建
+ if (appStateListener != null) {
+ appStateListener?.onRoomActivityCreated(this)
+ }
+
+
+ // 处理房间数据
+// handleRoomData();
+ SpUtil.saveMyRoomId(roomId)
+ // 检查是否从最小化状态恢复
+ if (isMinimized) {
+ // 恢复房间状态
+ resumeRoomState()
+ }
+
+ V2TIMManager.getInstance().addIMSDKListener(imSdkListener);
+ // 在 RoomActivity 中获取单例实例
+ // 在onCreate中初始化红包管理器实例,以便在整个Activity生命周期中使用
+ qxRedPacketManager = QXRedPacketManager.getInstance()
+ // 获取单例实例并设置委托
+ qxRedPacketManager!!.setDelegate(this);
+
+
+ // 初始化礼物管理器
+ giftManager = GiftDisplayManager.getInstance()
+ giftManager.setupDisplayView(mBinding!!.giftContainer)
+
+ }
+
+
+ private val imSdkListener = object : V2TIMSDKListener() {
+ override fun onConnecting() {}
+
+ override fun onConnectSuccess() { //重连成功
+ if (CommonAppContext.getInstance().playId != null) {
+ LogUtils.e("@@@", "重连成功")
+ LogUtils.e("@@@", "" + CommonAppContext.getInstance().playId)
+ RetrofitClient.getInstance().roomUserReconnect(CommonAppContext.getInstance().playId)
+ }
+ }
+
+ override fun onConnectFailed(code: Int, error: String?) {
+ LogUtils.e("@@@", "断开连接")
+ CommonAppContext.getInstance().onConnectFailed = true
+ }
+
+ override fun onKickedOffline() {
+ // queren1();
+ if (CommonAppContext.getInstance().playId != null) {
+ ToastUtils.show("您的账号已被挤下线")
+ try {
+ CommonAppContext.getInstance().clearLoginInfo()
+ } catch (e: ClassNotFoundException) {
+ throw RuntimeException(e)
+ }
+ }
+ }
+
+ override fun onUserSigExpired() {}
+
+ override fun onSelfInfoUpdated(info: V2TIMUserFullInfo?) {}
+ }
+ private fun checkAndRestoreMinimizeState() {
+ val prefs = getSharedPreferences("room_minimize_state", Context.MODE_PRIVATE)
+ var isMinimized = prefs.getBoolean("is_minimized", false)
+
+ if (isMinimized) {
+ isMinimized = true
+ // 检查最小化时间,如果太久可能需要重新登录
+ val minimizeTime = prefs.getLong(PREF_MINIMIZED_TIME, 0)
+ val currentTime = System.currentTimeMillis()
+
+ // 如果最小化超过一定时间(如30分钟),可能需要重新验证
+ if (currentTime - minimizeTime > 30 * 60 * 1000) {
+ // 清理过期的最小化状态
+ clearMinimizeState()
+ isMinimized = false
+ }
+ } else {
+ isMinimized = false
+ }
+ }
+
+ /**
+ * 在子线程中执行网络请求,避免阻塞主线程
+ */
+ private fun performNetworkRequestsAsync() {
+ ThreadUtils.executeByIo(object : ThreadUtils.SimpleTask() {
+ @Throws(Throwable::class)
+ override fun doInBackground(): Void? {
+ // 在后台线程执行网络请求前的准备工作
+ // 例如:检查缓存、预处理数据等
+// prepareNetworkRequest();
+ return null
+ }
+
+ override fun onSuccess(result: Void?) {
+ runOnUiThread {
+ // 使用Handler确保在主线程中调用
+// MvpPre.getRoomIn(roomId, password);
+ if (mRoomInfoResp == null) {
+ // 使用Handler确保在主线程中调用
+ MvpPre!!.getRoomIn(roomId, password)
+
+ }
+ MvpPre!!.getRoomOnline(roomId, "1", "10")
+ }
+ }
+
+
+ override fun onFail(e: Throwable) {
+ LogUtils.e("Network request preparation failed: " + e.message)
+ // 即使准备失败,也尝试执行网络请求
+ runOnUiThread {
+ // MvpPre.getRoomIn(roomId, password);
+ // 检查是否已经有房间信息,如果有则不需要再次获取
+ if (mRoomInfoResp == null) {
+ MvpPre!!.getRoomIn(roomId, password)
+ }
+ MvpPre!!.getRoomOnline(roomId, "1", "10")
+ }
+ }
+ })
+ }
+
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ fun onRoomTaskEvent(event: RoomTaskEvent?) {
+ if (taskId != null && taskId != "9") { //这是每日任务完成发送私聊信息的事件
+ RetrofitClient.getInstance().dailyTasksComplete(taskId, object : BaseObserver() {
+ override fun onSubscribe(d: Disposable) {
+ }
+
+ override fun onNext(roomSingleton: RoomSingleton) {
+ number++
+ // 这里处理请求结果
+ if (roomSingleton.is_completed == 1) {
+ // 任务完成,可以做一些后续操作
+ taskId = null
+ }
+ }
+ })
+ }
+ }
+
+ // TODO: 发红包
+ fun redDialogView() {
+ RedBagSendDialog(this, roomId).show()
+ }
+
+ var redEnvelopesFragment: RedEnvelopesFragment? = null
+ var redListDialog: RedListDialog? = null
+ var redPacketInfo: RedPacketInfo? = null
+
+ override fun initView() {
+ super.initView()
+ floatingMagnetView = findViewById(R.id.flaoat)
+ ivQuan = findViewById(R.id.iv_quan)
+ fullScreenContainer = findViewById(R.id.fullscreen_container) // 自定义全屏容器
+ ivExitFullscreen = findViewById(R.id.iv_exit_fullscreen)
+ ivQuan?.setOnClickListener(View.OnClickListener { v: View? -> toggleFullScreen() })
+ ivExitFullscreen?.setOnClickListener(View.OnClickListener { v: View? -> exitFullScreen() })
+
+ LogUtils.e("lxj", "开始时间:" + TimeUtils.date2String(Date()))
+
+ if (taskId != null) {
+ if (taskId == "9") {
+ MyRoomSingleton.getInstance().onEnterRoom(taskId)
+ }
+ }
+ circularProgress = mBinding!!.giftShowProgress
+ circularProgress!!.progress = 0
+ mBinding!!.giftShowLayout.setOnClickListener {
+ onGiftGiveProgressClcik()
+ LogUtils.e("xj", "onSubscribe2222")
+ }
+
+ ThemeableDrawableUtils.setThemeableRoundedBackground(
+ mBinding!!.roomTop.btnFollow,
+ ColorManager.getInstance().primaryColorInt,
+ 53
+ )
+ mBinding!!.roomTop.btnFollow.setTextColor(ColorManager.getInstance().buttonColorInt)
+ initPublicScreenFragment()
+
+ // stub = mBinding.roomTop.stubButtons.getViewStub();
+
+ // 为透明 View 设置触摸监听
+ mBinding!!.roomTop.rlTop.setOnTouchListener { v, event ->
+ // 将触摸事件透传给下层的 View
+ false // 返回 false,表示不拦截事件
+ }
+
+ val layoutParams = mBinding!!.roomTop.root.layoutParams
+ layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT // 使用你定义的getWidth方法
+ layoutParams.height = SystemUtils.getWidth(74) // 示例高度
+ mBinding!!.roomTop.root.layoutParams = layoutParams
+
+ mBinding!!.xlhIm.setOnClickListener {
+ val fm = supportFragmentManager
+ if (fm != null && !fm.isDestroyed) {
+ val newDialog = TourClubDialogFragment.newInstance(
+ roomId
+ )
+ newDialog.show(fm, "TourClubDialogFragment")
+ }
+ }
+
+ mBinding!!.clXsb.visibility = View.GONE
+ mBinding!!.tvXlh.setOnClickListener { view ->
+ val fragment = HourlyChartDialog.newInstance()
+ fragment.show(supportFragmentManager, "HourlyChartDialog")
+ }
+
+ mBinding!!.drvRed.visibility = View.GONE
+ mBinding!!.redBj.setOnClickListener {
+ if(qxRedPacketManager!!.getAllRedPackets().size==1){
+ redPacketInfo = qxRedPacketManager!!.getAllRedPackets().get(0)
+ if (qxRedPacketManager!!.getAllRedPackets().get(0)!=null && qxRedPacketManager!!.getAllRedPackets().get(0).is_qiang==1){
+ ARouter.getInstance().build(ARouteConstants.ROOM_RED_RESULT).withString("redpacketId", qxRedPacketManager!!.getAllRedPackets().get(0).getRedpacket_id()).navigation();
+ }else {
+ redEnvelopesFragment = RedEnvelopesFragment(this@RoomActivity)
+ redEnvelopesFragment!!.setIsCollectedRoom(mRoomUserBean!!.is_collect == 1)
+ redEnvelopesFragment!!.setFromToComment(false)
+ redEnvelopesFragment!!.setRedPacket(qxRedPacketManager!!.getAllRedPackets().get(0))
+ redEnvelopesFragment!!.show()
+ }
+ return@setOnClickListener
+ }
+
+ redListDialog = RedListDialog(this)
+ redListDialog!!.setOnRedPacketClickListener(object : RedListDialog.OnRedPacketClickListener {
+
+ override fun onRedPacketClick(redPacketInfos: RedPacketInfo?, position: Int) {
+ redPacketInfo = redPacketInfos
+ if (redPacketInfos!=null && redPacketInfos.is_qiang==1){
+ ARouter.getInstance().build(ARouteConstants.ROOM_RED_RESULT).withString("redpacketId", redPacketInfos.getRedpacket_id()).navigation();
+ }else {
+ redEnvelopesFragment = RedEnvelopesFragment(this@RoomActivity)
+ redEnvelopesFragment!!.setIsCollectedRoom(mRoomUserBean!!.is_collect == 1)
+ redEnvelopesFragment!!.setFromToComment(false)
+ redEnvelopesFragment!!.setRedPacket(redPacketInfos)
+ redEnvelopesFragment!!.show()
+ redListDialog!!.dismiss()
+ }
+ }
+ })
+ redListDialog!!.show();
+ }
+ }
+
+ private fun onGiftGiveProgressClcik() {
+ if (giftGiveEvent == null || giftGiveEvent!!.roonGiftModel == null) {
+ return
+ }
+
+ if (giftGiveEvent!!.auction_id != null && !giftGiveEvent!!.auction_id.isEmpty()) {
+ RetrofitClient.getInstance().roomAuctionJoin(
+ giftGiveEvent!!.getAuction_id(),
+ giftGiveEvent!!.getUserId(),
+ giftGiveEvent!!.getRoonGiftModel().gift_id,
+ giftGiveEvent!!.getNum(),
+ "1",
+ object : BaseObserver() {
+ override fun onSubscribe(d: Disposable) {
+// showGiftGiveProgress();
+ }
+
+ override fun onNext(auctionListBean: AuctionListBean) {
+ if (auctionListBean == null) {
+ // 处理空响应
+ Log.e("RoomActivity", "AuctionListBean is null")
+ hideGiftGiveProgress()
+ return
+ }
+ showGiftGiveProgress()
+ }
+
+ override fun onError(e: Throwable) {
+ super.onError(e)
+ val msg = e.message
+ if (!TextUtils.isEmpty(msg) && msg!!.contains("当前余额不足")) {
+ ToastUtils.show("当前余额不足,请充值")
+ ThreadUtils.runOnUiThreadDelayed({
+ val fragment = RechargeDialogFragment.show(
+ roomId, null,
+ supportFragmentManager,"",""
+ )
+ if (fragment != null) {
+ addActiveDialogFragment(fragment) // 添加到管理列表
+ }
+ }, 1400)
+ }
+ hideGiftGiveProgress()
+ }
+ })
+ } else {
+ RetrofitClient.getInstance().roomGift(
+ giftGiveEvent!!.getRoom_id(),
+ giftGiveEvent!!.getRoonGiftModel().gift_id,
+ giftGiveEvent!!.getNum(),
+ giftGiveEvent!!.getUserId(),
+ "1",
+ giftGiveEvent!!.getPit(),
+ giftGiveEvent!!.heart_id,
+ object : BaseObserver() {
+ override fun onSubscribe(d: Disposable) {
+// showGiftGiveProgress();
+// LogUtils.e("xj", "onSubscribe");
+ }
+
+ override fun onNext(s: String) {
+ showGiftGiveProgress()
+ }
+
+ override fun onError(e: Throwable) {
+ super.onError(e)
+ val msg = e.message
+ if (!TextUtils.isEmpty(msg) && msg!!.contains("当前余额不足")) {
+ ToastUtils.show("当前余额不足,请充值")
+ ThreadUtils.runOnUiThreadDelayed({
+ val fragment = RechargeDialogFragment.show(
+ roomId, null,
+ supportFragmentManager,"",""
+ )
+ if (fragment != null) {
+ addActiveDialogFragment(fragment) // 添加到管理列表
+ }
+ }, 1400)
+ }
+ hideGiftGiveProgress()
+ }
+ })
+ }
+ }
+
+ private var giftGiveEvent: RoomGiftGiveEvent? = null
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ fun roomGiveGiftEvent(event: RoomGiftGiveEvent?) {
+ if (isFinishing || event == null || event.roonGiftModel == null) {
+ return
+ }
+ giftGiveEvent = event
+ showGiftGiveProgress()
+ }
+
+ private var giftProgress = 0
+ private var giftCountTimer: CountDownTimer? = null
+
+ private fun startGiftProgressTime() {
+ giftProgress = 0
+ if (giftCountTimer != null) {
+ giftCountTimer!!.cancel()
+ }
+ LogUtils.e("xj2", "onSubscribe")
+ giftCountTimer = object : CountDownTimer((1000 * 10).toLong(), 50) {
+ override fun onTick(millisUntilFinished: Long) {
+ if (!isFinishing) {
+ circularProgress!!.progress = 1000 - (millisUntilFinished / 10).toInt()
+ }
+ }
+
+ override fun onFinish() {
+ circularProgress!!.progress = 1000
+ hideGiftGiveProgress()
+ }
+ }
+ giftCountTimer?.start()
+ }
+
+ private fun showGiftGiveProgress() {
+ ImageUtils.loadImageView(
+ giftGiveEvent!!.roonGiftModel.base_image,
+ mBinding!!.giftShowProgressImg
+ )
+ circularProgress!!.progress = 1000 // 显示进度条,2025年7月19日11:23:37将这个从下面的方法提起到这里,
+ startGiftProgressTime()
+ mBinding!!.giftShowLayout.visibility = View.VISIBLE
+ }
+
+ private fun hideGiftGiveProgress() {
+ mBinding!!.giftShowLayout.visibility = View.GONE
+ if (giftCountTimer != null) {
+ giftCountTimer!!.cancel()
+ giftCountTimer = null
+ }
+ }
+
+ private fun toggleFullScreen() {
+ if (isFullScreen) {
+ exitFullScreen()
+ } else {
+// enterFullScreen();
+
+ // 修改为横屏展示模式而不是全屏模式
+
+ enterLandscapeMode()
+ }
+ }
+
+
+ private fun enterLandscapeMode() {
+ isFullScreen = true
+
+ // 设置横屏
+ requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
+
+ if (floatingMagnetView != null) {
+ // 修改Floa组件的布局参数,使其在横屏时占据更大区域
+ val layoutParams = floatingMagnetView!!.layoutParams
+ if (layoutParams is ConstraintLayout.LayoutParams) {
+ val params = layoutParams
+ params.width = ConstraintLayout.LayoutParams.MATCH_CONSTRAINT
+ params.height = ConstraintLayout.LayoutParams.MATCH_CONSTRAINT
+// params.horizontalBias = 0.5f
+// params.verticalBias = 0.5f
+ floatingMagnetView!!.layoutParams = params
+ } else {
+ // 如果不是ConstraintLayout.LayoutParams,创建新的
+ val params = ConstraintLayout.LayoutParams(
+ ConstraintLayout.LayoutParams.MATCH_CONSTRAINT,
+ ConstraintLayout.LayoutParams.MATCH_CONSTRAINT
+ )
+ params.topToTop = ConstraintLayout.LayoutParams.PARENT_ID
+ params.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID
+ params.startToStart = ConstraintLayout.LayoutParams.PARENT_ID
+ params.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID
+ floatingMagnetView!!.layoutParams = params
+ }
+
+ // 可以调整内部fl_screenshare的布局参数
+ val flScreenshare = floatingMagnetView!!.findViewById(R.id.fl_screenshare)
+ val screenParams = flScreenshare.layoutParams
+ if (screenParams !is FrameLayout.LayoutParams) {
+ // 如果不是FrameLayout.LayoutParams,创建新的
+ val newScreenParams = FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT
+ )
+ flScreenshare.layoutParams = newScreenParams
+ } else {
+ val newScreenParams = screenParams
+ newScreenParams.width = FrameLayout.LayoutParams.MATCH_PARENT
+ newScreenParams.height = FrameLayout.LayoutParams.MATCH_PARENT
+ flScreenshare.layoutParams = newScreenParams
+ }
+
+ // 显示退出按钮
+ ivQuan!!.visibility = View.VISIBLE
+ }
+ }
+
+ private fun enterFullScreen() {
+ isFullScreen = true
+
+
+ // 隐藏系统UI
+ val decorView = window.decorView
+ decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_FULLSCREEN
+ or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
+
+ // 设置横屏
+ requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
+ // 找到 fl_screenshare 并移到全屏容器中
+ val fl_screenshare = findViewById(R.id.fl_screenshare)
+ if (fl_screenshare != null) {
+// // 先从当前父容器中移除
+// ViewParent parent = fl_screenshare.getParent();
+// if (parent != null && parent instanceof ViewGroup) {
+// ((ViewGroup) parent).removeView(fl_screenshare);
+// }
+//
+// // 添加到全屏容器
+// fullScreenContainer.addView(fl_screenshare);
+
+ safelyMoveViewToParent(fl_screenshare, floatingMagnetView)
+ // 显示全屏容器
+ fullScreenContainer!!.visibility = View.VISIBLE
+ floatingMagnetView!!.visibility = View.GONE
+ ivExitFullscreen!!.visibility = View.VISIBLE // 显示退出按钮
+ }
+ }
+
+ private fun exitFullScreen() {
+ isFullScreen = false
+
+ // 设置回竖屏
+ requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
+
+ if (floatingMagnetView != null) {
+ // 恢复原始尺寸
+ val params = ConstraintLayout.LayoutParams(
+ resources.getDimensionPixelSize(com.xscm.moduleutil.R.dimen.dp_240),
+ resources.getDimensionPixelSize(com.xscm.moduleutil.R.dimen.dp_135)
+ )
+ params.topToTop = ConstraintLayout.LayoutParams.PARENT_ID
+ params.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID
+ params.startToStart = ConstraintLayout.LayoutParams.PARENT_ID
+ params.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID
+ floatingMagnetView!!.layoutParams = params
+
+ // 恢复内部fl_screenshare的布局参数
+ val flScreenshare = floatingMagnetView!!.findViewById(R.id.fl_screenshare)
+ val screenParams = FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT
+ )
+ flScreenshare.layoutParams = screenParams
+ }
+
+ // 隐藏退出按钮
+ ivExitFullscreen!!.visibility = View.GONE
+ }
+
+ override fun initData() {
+ if (!EasyPermissions.hasPermissions(this, *permissions)) {
+ EasyPermissions.requestPermissions(
+ this, "请开启录音使用权限",
+ 1, *permissions
+ )
+ }
+
+ mBinding!!.roomTop.btnFollow.setOnClickListener { view: View ->
+ this.onClick(
+ view
+ )
+ }
+ mBinding!!.roomTop.btnNotice.setOnClickListener { view: View ->
+ this.onClick(
+ view
+ )
+ }
+ mBinding!!.roomTop.btnRanking.setOnClickListener { view: View ->
+ this.onClick(
+ view
+ )
+ }
+ mBinding!!.roomTop.btnCloseLive.setOnClickListener { view: View ->
+ this.onClick(
+ view
+ )
+ }
+ mBinding!!.roomTop.tvNum.setOnClickListener { view: View ->
+ this.onClick(
+ view
+ )
+ }
+ mBinding!!.roomTop.rl.setOnClickListener { view: View ->
+ this.onClick(
+ view
+ )
+ }
+ mBinding!!.ivSoundEffects.setOnClickListener { view: View ->
+ this.onClick(
+ view
+ )
+ }
+ mBinding!!.ivWheatFeeding.setOnClickListener { view: View ->
+ this.onClick(
+ view
+ )
+ }
+ mBinding!!.clFirstCharge.setOnClickListener { view: View ->
+ this.onClick(
+ view
+ )
+ }
+ mBinding!!.roomTop.userRecyclerView.layoutManager = LinearLayoutManager(
+ this, LinearLayoutManager.HORIZONTAL, false
+ )
+ likeUserAdapter = LikeUserAdapter()
+ mBinding!!.roomTop.userRecyclerView.adapter = likeUserAdapter
+ likeUserAdapter!!.onItemClickListener =
+ BaseQuickAdapter.OnItemClickListener { adapter, view, position ->
+ // RoomOnlineDialogFragment.show(roomId, "", mRoomUserBean, mRoomInfoResp, getSupportFragmentManager());
+ val fragment = RoomOnlineDialogFragment.show(
+ roomId, "", mRoomUserBean, mRoomInfoResp,
+ supportFragmentManager
+ )
+ if (fragment != null) {
+ addActiveDialogFragment(fragment) // 添加到管理列表
+ }
+ }
+
+
+ // PublicScreenEaseChatFragment fragment = PublicScreenEaseChatFragment.newInstance();
+// getSupportFragmentManager().beginTransaction().replace(R.id.ease_container, fragment).commitAllowingStateLoss();
+ mBinding!!.ivChat.setOnClickListener { view: View ->
+ this.onClick(
+ view
+ )
+ }
+
+ mBinding!!.ivEmoji.setOnClickListener { view: View ->
+ this.onClick(
+ view
+ )
+ }
+
+ mBinding!!.llInput.setOnClickListener { view: View ->
+ this.onClick(
+ view
+ )
+ }
+ mBinding!!.rlMisc.setOnClickListener { view: View ->
+ this.onClick(
+ view
+ )
+ }
+ mBinding!!.rlGift.setOnClickListener { view: View ->
+ this.onClick(
+ view
+ )
+ }
+ mBinding!!.rlMic.setOnClickListener { view: View ->
+ this.onClick(
+ view
+ )
+ }
+ mBinding!!.rlSett.setOnClickListener { view: View ->
+ this.onClick(
+ view
+ )
+ }
+ mBinding!!.rlVoive.setOnClickListener { view: View ->
+ this.onClick(
+ view
+ )
+ }
+ mBinding!!.rlMore.setOnClickListener { view: View ->
+ this.onClick(
+ view
+ )
+ }
+ mBinding!!.rlMessage.setOnClickListener { view: View ->
+ this.onClick(
+ view
+ )
+ }
+
+ // SpUtil.saveMyRoomId(roomId);
+// MvpPre.getRoomIn(roomId, password);
+// MvpPre.getRoomOnline(roomId, "1", "10");
+ mBinding!!.inputMenu1.performClick()
+
+ V2TIMManager.getConversationManager()
+ .getTotalUnreadMessageCount(object : V2TIMValueCallback {
+ override fun onSuccess(aLong: Long) {
+ if (aLong == 0L) {
+ mBinding!!.ivMessageDot.visibility = View.GONE
+ mBinding!!.ivMessageDot.text = "0"
+ } else {
+ mBinding!!.ivMessageDot.visibility = View.VISIBLE
+ }
+ mBinding!!.ivMessageDot.text = aLong.toString()
+ }
+
+ override fun onError(code: Int, desc: String) {
+ }
+ })
+
+ mBinding!!.roomTop.root.isClickable = false
+ mBinding!!.roomTop.root.setOnClickListener {
+ // ToastUtils.showShort("点击了房间,事件穿透");
+ }
+
+// mBinding!!.clXsb.setOnClickListener {
+// val fragment = HourlyChartDialog.newInstance()
+// fragment.show(supportFragmentManager,"HourlyChartDialog")
+// if (fragment != null) {
+// addActiveDialogFragment(fragment) // 添加到管理列表
+// }
+// }
+ }
+
+
+ private fun initPublicScreenFragment() {
+ // 检查是否已经存在 Fragment 实例(例如在配置更改后)
+ publicScreenFragment = supportFragmentManager
+ .findFragmentById(R.id.ease_container) as PublicScreenEaseChatFragment?
+
+ // 如果不存在,则创建新的实例
+ if (publicScreenFragment == null) {
+ publicScreenFragment = PublicScreenEaseChatFragment.newInstance(roomId)
+ supportFragmentManager.beginTransaction()
+ .replace(R.id.ease_container, publicScreenFragment!!)
+ .commitAllowingStateLoss()
+ }
+ }
+
+ // 在类成员变量中添加
+ private val roomSwitchHandler = Handler(Looper.getMainLooper())
+ private var roomSwitchRunnable: Runnable? = null
+ private var pendingRoomId: String? = null
+ private var lastSwitchedRoomId = ""
+
+ fun roomInfoEvent(messageEvent: RoomMessageEvent?) {
+ if (messageEvent == null) return
+ if (roomFragment == null) {
+ roomFragment = RoomFragment.newInstance()
+ supportFragmentManager
+ .beginTransaction()
+ .replace(R.id.vp_room_pager, roomFragment!!)
+ .commitAllowingStateLoss()
+ }
+
+ val msgType = messageEvent.msgType
+ val text = messageEvent.text
+
+ if (msgType == 1005) {
+ LogUtils.e("@@@@" + "EventBusnujm2" + "playQueue.size()====" + messageEvent.text.giftInfo)
+ val playQueue = Arrays.asList(
+ *messageEvent.text.giftInfo.play_image.split(",".toRegex())
+ .dropLastWhile { it.isEmpty() }.toTypedArray()
+ )
+ QXGiftPlayerManager.getInstance(this).displayFullEffectView1(playQueue)
+ if (messageEvent!!.text.giftInfo!=null) {
+ var giftBean = messageEvent.text.giftInfo
+ giftBean.nickname= messageEvent.text.fromUserInfo.nickname
+ giftBean.userAvatar=messageEvent.text.fromUserInfo.avatar
+ giftBean.senderName=messageEvent.text.toUserInfo.nickname
+ giftBean.senderAvatarUrl=messageEvent.text.toUserInfo.avatar
+ giftBean.number=messageEvent.text.gift_num.toInt()
+ giftManager.receiveGift(giftBean)
+ }
+ hand1005(messageEvent, text)
+ } else if (msgType == 123) {
+ EventBus.getDefault().post(RoomSettingEvent())
+ } else if (msgType == 1014) {
+ handleMsgType1014(messageEvent, text)
+ } else if (msgType == 1013) {
+ handleMsgType1013(messageEvent, text)
+ } else if (msgType == 1012) {
+ handleMsgType1012()
+ } else if (msgType == 124) {
+ handleMsgType124(messageEvent, text)
+ } else if (msgType == 1003) {
+ handleMsgType1003(messageEvent, text)
+ } else if (msgType == 1004) {
+ handleMsgType1004(messageEvent, text)
+ } else if (msgType == 1022) {
+ handleMsgType1022(messageEvent, text)
+ } else if (msgType == 1023) {
+ handleMsgType1023(messageEvent, text)
+ } else if (msgType == 1024) {
+ handleMsgType1024(messageEvent, text)
+ } else if (msgType == 1025) {
+ roomFragment!!.handleAuctionMessageEvent(messageEvent)
+ } else if (msgType == 1026) {
+ roomFragment!!.handleAuctionMessageEvent(messageEvent)
+ } else if (msgType == 1027) {
+ roomFragment!!.handleAuctionMessageEvent(messageEvent)
+ } else if (msgType == 1020) {
+ handleMsgType1020(messageEvent, text)
+ } else if (msgType == 1011) {
+ handleMsgType1011(messageEvent, text)
+ } else if (msgType == 1001) {
+ handleMsgType1001()
+ } else if (msgType == 1002) {
+ handleMsgType1002()
+ } else if (msgType == 1029) {
+ handleMsgType1029(messageEvent, text)
+ } else if (msgType == 1021) {
+ handleMsgType1021(messageEvent, text)
+ } else if (msgType == 1036) {
+ handleMsgType1036(messageEvent, text)
+ } else if (msgType == 1049) {
+ handleMsgType1049(messageEvent, text)
+ } else if (msgType == 1050) {
+ handleMsgType1050(messageEvent, text)
+ } else if (msgType == 1051) {
+ handleMsgType1051(messageEvent, text)
+ } else if (msgType == 1052) {
+ roomFragment!!.upCabinFragment(text.time_day)
+ } else if (msgType == 1053) {
+ handleMsgType1053(messageEvent, text)
+ } else if (msgType == 1054) {
+ handleMsgType1054(messageEvent, text)
+ } else if (msgType == 1055) {
+ handleMsgType1055(messageEvent)
+ } else if (msgType == 1035) {
+ handleMsgType1035(messageEvent, text)
+ } else if (msgType == 1030 || msgType == 1031 || msgType == 1032 || msgType == 1033 || msgType == 1015 || msgType == 1037) {
+ roomFragment!!.SingSongEvent(messageEvent)
+ if (msgType == 1032 || msgType == 1033) {
+ setRoleType(3, -11)
+ }
+ } else if (msgType == 125) {
+ handleMsgType125(messageEvent, text)
+ } else if (msgType == 1006) {
+ handleMsgType1006()
+ } else if (msgType == 1007) {
+ handleMsgType1007()
+ } else if (msgType == 1017) {
+ handleMsgType1017()
+ } else if (msgType == 1018) {
+ handleMsgType1018()
+ } else if (msgType == 126) {
+ handleMsgType126(messageEvent, text)
+ } else if (msgType == 1034) {
+ handleMsgType1034(messageEvent, text)
+ } else if (msgType == 1016) {
+ handleMsgType1016(messageEvent, text)
+ } else if (msgType == 1039) {
+ handleMsgType1039(messageEvent, text)
+ } else if (msgType == 1028) {
+ roomFragment!!.handleMsgType1028(messageEvent)
+ } else if (msgType == 1058) {
+ var userId = messageEvent!!.text!!.user_id!!
+ LogUtils.e("messageEvent!!.text.type" + messageEvent!!.text.type)
+ CommonAppContext.getInstance().onlineMap.set(
+ userId?.toString() ?: "",
+ messageEvent!!.text.type
+ )
+ if (mRoomInfoResp != null && mRoomInfoResp!!.room_info != null) {
+ if (mRoomInfoResp!!.room_info.type_id == "1" || mRoomInfoResp!!.room_info.type_id == "3" ||
+ mRoomInfoResp!!.room_info.type_id == "4" || mRoomInfoResp!!.room_info.type_id == "8"
+ ) {
+ if (mRoomInfoResp!!.room_info.label_id == "1") {
+// roomFragment!!.SingSongEvent(messageEvent)
+ } else {
+ roomFragment!!.KtvFragmentEvent(messageEvent)
+ }
+ } else if (mRoomInfoResp!!.room_info.type_id == "2") {
+ roomFragment!!.handleAuctionMessageEvent(messageEvent)
+ } else if (mRoomInfoResp!!.room_info.type_id == "7") {
+ roomFragment!!.friendshipRoomFragmentEvent(messageEvent)
+ }
+ }
+
+ CommonAppContext.getInstance().getOnlineMap()
+ } else if (msgType == 1059) { // 1059清除个人魅力 1058在离线
+ if (mRoomInfoResp != null && mRoomInfoResp!!.room_info != null) {
+ if (mRoomInfoResp!!.room_info.type_id == "1" || mRoomInfoResp!!.room_info.type_id == "3" ||
+ mRoomInfoResp!!.room_info.type_id == "4" || mRoomInfoResp!!.room_info.type_id == "8"
+ ) {
+ if (mRoomInfoResp!!.room_info.label_id == "1") {
+ roomFragment!!.SingSongEvent(messageEvent)
+ } else {
+ roomFragment!!.KtvFragmentEvent(messageEvent)
+ }
+ } else if (mRoomInfoResp!!.room_info.type_id == "2") {
+ roomFragment!!.handleAuctionMessageEvent(messageEvent)
+ } else if (mRoomInfoResp!!.room_info.type_id == "7") {
+ roomFragment!!.friendshipRoomFragmentEvent(messageEvent)
+ }
+ }
+ } else if (msgType == 1056) { // 1056 抽奖结果
+ val dialog = supportFragmentManager
+ .findFragmentByTag("GiftLotteryDialog") as GiftLotteryDialog?
+ if (dialog != null && dialog.isVisible) {
+ dialog.UpView(messageEvent.text.xlh_data)
+ } else {
+ EventBus.getDefault().post(messageEvent.text.xlh_data)
+ }
+
+ if (messageEvent.text.xlh_data != null) {
+ if (messageEvent.text.xlh_data.status == 1) {
+ mBinding!!.xlhRk.visibility = View.VISIBLE
+ xlhDjs(messageEvent.text.xlh_data.end_time)
+ } else {
+ mBinding!!.xlhRk.visibility = View.INVISIBLE
+ releaseCountDownTimer1()
+ }
+ }
+ } else if (msgType == 1057) {
+ val existingFragment =
+ supportFragmentManager.findFragmentByTag("TourClubDialogFragment") as TourClubDialogFragment?
+ if (existingFragment != null && existingFragment.isVisible) {
+ existingFragment.onMusicPlay(messageEvent)
+ } else {
+ LogUtils.e("TourClubDialogFragment", "巡乐会界面未打开")
+ EventBus.getDefault().post(messageEvent)
+ }
+ xlhDjs(messageEvent.text.end_time)
+
+ } else if (msgType == 1060) {
+ qxRedPacketManager!!.addRedPacket(messageEvent.text.redpacketInfo)
+ }else if (msgType == 1061){
+ qxRedPacketManager!!.removeRedPacket(messageEvent.text.redpacket_id)
+ }
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ fun onMessageEvent(messageEvent: MqttXlhEnd) {
+ val xlhBean: XLHBean = GsonUtils.fromJson(messageEvent.message, XLHBean::class.java)
+ if(xlhBean.from_type==100){
+ if (xlhBean.xlh_data != null) {
+ if (xlhBean.xlh_data.status == 1) {
+ mBinding!!.xlhRk.visibility = View.VISIBLE
+ xlhDjs(xlhBean.xlh_data.end_time)
+ } else {
+ mBinding!!.xlhRk.visibility = View.INVISIBLE
+ releaseCountDownTimer1()
+ }
+ }
+ }else if(xlhBean.from_type==103){
+ val existingFragment =
+ supportFragmentManager.findFragmentByTag("TourClubDialogFragment") as TourClubDialogFragment?
+ if (existingFragment != null && existingFragment.isVisible) {
+ existingFragment.onMessageReceived(messageEvent)
+ } else {
+ LogUtils.e("TourClubDialogFragment", "巡乐会界面未打开")
+// EventBus.getDefault().post(messageEvent)
+ }
+ xlhDjs(xlhBean.end_time)
+ }else if(xlhBean.from_type==104){
+
+ }
+ }
+
+ private var endTime: Long = 0
+
+ private fun xlhDjs(endTimeStr: String?) {
+ // 获取结束时间并启动倒计时
+ if (endTimeStr != null && !endTimeStr.isEmpty()) {
+ try {
+ // 假设 end_time 是时间戳字符串
+ endTime = endTimeStr.toLong()
+ countDownTime(endTime)
+ } catch (e: NumberFormatException) {
+ // 如果不是时间戳,可能是日期字符串,需要相应解析
+ // 例如:2025-08-26 19:10:47
+ // 可以使用 SimpleDateFormat 解析
+ e.printStackTrace()
+ }
+ }
+ }
+
+ fun countDownTime(time: Long) {
+ try {
+ if (time <= 0) {
+ setTime(0)
+
+ releaseCountDownTimer1()
+ return
+ }
+ releaseCountDownTimer1()
+ // 获取当前时间的毫秒值
+ val currentTime = System.currentTimeMillis() / 1000L
+ // 计算倒计时的总秒数
+ val countDownTime = (time - currentTime)
+ Logger.d("@@@", "countDownTime = $countDownTime")
+ if (countDownTime <= 0) {
+ setTime(0)
+ releaseCountDownTimer1()
+ return
+ }
+ mCountDownTimer2 = object : CountDownTimer(countDownTime * 1000L, 1000L) {
+ override fun onTick(millisUntilFinished: Long) {
+ val time1 = (millisUntilFinished / 1000).toInt()
+ setTime(time1)
+ // mBinding.tvDjs.setText(time1 + "");
+ if (time1 == 0) {
+ mBinding!!.xlhRk.visibility = View.INVISIBLE
+ }
+ }
+
+ override fun onFinish() {
+ setTime(0)
+ }
+ }
+ mCountDownTimer2?.start()
+ } catch (e: Exception) {
+ Logger.e("countDownTime", e)
+ }
+ }
+
+ private fun formatTime(totalSeconds: Int): String {
+ val minutes = (totalSeconds % 3600) / 60
+ val seconds = totalSeconds % 60
+
+ return String.format("%02d:%02d", minutes, seconds)
+ }
+
+ @SuppressLint("DefaultLocale")
+ fun setTime(seconds: Int) {
+ if (seconds == 0) {
+ mBinding!!.xlhRk.visibility = View.INVISIBLE
+ return
+ }
+ val formattedTime = formatTime(seconds)
+ mBinding!!.tvDjs.text = "倒计时$formattedTime"
+ }
+
+ // TODO: 2025/3/19 释放倒计时器
+ private fun releaseCountDownTimer1() {
+ if (mCountDownTimer2 != null) {
+ mCountDownTimer2!!.cancel()
+ mCountDownTimer2 = null
+ }
+ }
+
+ private val pitMap: MutableMap = HashMap()
+
+ fun handleMsgType1039(messageEvent: RoomMessageEvent, text: T?) {
+ if (mRoomInfoResp == null || mRoomInfoResp!!.room_info == null) {
+ return
+ }
+
+ val pitList = mRoomInfoResp!!.room_info.pit_list
+ if (pitList != null && !pitList.isEmpty()) {
+// pitMap.clear() // 避免数据累积
+// for (roomPitBean in pitList) {
+// pitMap[roomPitBean.pit_number] = roomPitBean
+// }
+
+ if (text == null) {
+ return
+ }
+
+ val fromPitNumber = text.from_pit_number
+ val toPitNumber = text.to_pit_number
+
+ if (!TextUtils.isEmpty(fromPitNumber) && !TextUtils.isEmpty(toPitNumber)) {
+ performSwitchMic(fromPitNumber, toPitNumber, messageEvent)
+ }
+ // 执行换麦逻辑
+ }
+ }
+
+ private fun performSwitchMic(
+ fromPitNumber: String,
+ toPitNumber: String,
+ messageEvent: RoomMessageEvent
+ ) {
+ mRoomInfoResp!!.user_info.pit_number = Integer.parseInt(toPitNumber)
+
+
+// if (messageEvent.text.user_id .equals(SpUtil.getUserId().toString()) && "9" == toPitNumber) {
+// if (customMusicFloatingView != null) {
+// customMusicFloatingView!!.destroy()
+// AgoraManager.getInstance(this@RoomActivity).desMusic()
+// isMusic = false
+// }
+// mBinding!!.roomTop.rl.visibility = View.GONE
+// ivSoundEffects(false)
+// }
+
+ if ("9" == toPitNumber && messageEvent.text.user_id.equals(SpUtil.getUserId().toString())) {
+ mBinding!!.roomTop.rl.visibility = View.VISIBLE
+ ivSoundEffects(true)
+ } else {
+ if (customMusicFloatingView != null) {
+ customMusicFloatingView!!.destroy()
+ AgoraManager.getInstance(this@RoomActivity).desMusic()
+ isMusic = false
+ }
+ mBinding!!.roomTop.rl.visibility = View.GONE
+ ivSoundEffects(false)
+ }
+// if (TextUtils.isEmpty(fromPitNumber) || TextUtils.isEmpty(toPitNumber)) {
+// return
+// }
+////
+// if (mRoomInfoResp == null || mRoomInfoResp!!.room_info == null) {
+// LogUtils.e("Room info is null")
+// return
+// }
+////
+// val pitList = mRoomInfoResp!!.room_info.pit_list
+// if (pitList == null || pitList.isEmpty()) {
+// LogUtils.e("pit_list is null or empty")
+// return
+// }
+////
+// var fromBean: RoomPitBean? = null
+// var toBean: RoomPitBean? = null
+// val pitListCopy: MutableList = ArrayList()
+//
+// for (bean in pitList) {
+// if (bean == null) continue
+// if (fromPitNumber == bean.pit_number) {
+// fromBean = bean
+// pitListCopy.add(bean)
+// } else if (toPitNumber == bean.pit_number) {
+// toBean = bean
+// pitListCopy.add(bean)
+// }
+// }
+//
+// if (fromBean == null || toBean == null) {
+// LogUtils.e("Cannot find pit number: from=$fromPitNumber, to=$toPitNumber")
+// return
+// }
+//
+// // 交换 pit_number
+// val temp = fromBean.pit_number
+// fromBean.pit_number = toBean.pit_number
+// toBean.pit_number = temp
+//
+// // 构造新的 pitList
+// val newPitList: MutableList = ArrayList()
+// for (bean in pitList) {
+// if (!pitListCopy.contains(bean)) {
+// newPitList.add(bean)
+// }
+// }
+// newPitList.add(fromBean)
+// newPitList.add(toBean)
+//// 排序 - 更简洁的 Kotlin 写法
+// newPitList.sortWith(compareBy {
+// try {
+// it.pit_number.toInt()
+// } catch (e: NumberFormatException) {
+// it.pit_number
+// }
+// })
+//
+// mRoomInfoResp!!.room_info.pit_list = newPitList
+//
+// // 更新当前用户 pit_number
+// if (mRoomInfoResp!!.user_info != null) {
+// val currentUserId = SpUtil.getUserId().toString()
+// val fromUserId = fromBean.user_id
+// val toUserId = toBean.user_id
+//
+// if (fromUserId != null && fromUserId == currentUserId) {
+// try {
+// mRoomInfoResp!!.user_info.pit_number = fromBean.pit_number.toInt()
+// } catch (e: NumberFormatException) {
+// LogUtils.e("Invalid pit number: " + toBean.pit_number)
+// }
+// } else if (toUserId != null && toUserId == currentUserId) {
+// try {
+// mRoomInfoResp!!.user_info.pit_number = fromBean.pit_number.toInt()
+// } catch (e: NumberFormatException) {
+// LogUtils.e("Invalid pit number: " + toBean.pit_number)
+// }
+// }
+// }
+
+ dispatchRoomEvent(messageEvent)
+ }
+
+ private fun dispatchRoomEvent(messageEvent: RoomMessageEvent) {
+ if (mRoomInfoResp == null || mRoomInfoResp!!.room_info == null) return
+ roomFragment!!.upRoomInfoData(mRoomInfoResp)
+ val typeId = mRoomInfoResp!!.room_info.type_id
+ if ("2" == typeId) {
+
+ roomFragment!!.handleAuctionMessageEvent(messageEvent)
+ } else if ("3" == typeId || "4" == typeId || "1" == typeId || "8" == typeId) {
+ val labelId = mRoomInfoResp!!.room_info.label_id
+ if ("2" == labelId) {
+ roomFragment!!.KtvFragmentEvent(messageEvent)
+ } else if ("1" == labelId) {
+ roomFragment!!.SingSongEvent(messageEvent)
+ }
+ } else if ("7" == typeId) {
+ roomFragment!!.friendshipRoomFragmentEvent(messageEvent)
+ }
+ if (mRoomInfoResp!!.user_info.pit_number == 9) {
+ mBinding!!.roomTop.rl.visibility = View.VISIBLE
+ ivSoundEffects(true)
+ }
+ }
+
+ private fun hand1005(messageEvent: RoomMessageEvent, text: T?) {
+ if (text == null || mRoomInfoResp == null || mRoomInfoResp!!.room_info == null) return
+
+ val giftInfo = text.giftInfo
+ val toUserInfo = text.toUserInfo
+ // if (giftInfo == null || toUserInfo == null) return;
+ val pitList = mRoomInfoResp!!.room_info.pit_list ?: return
+
+
+ // roomFragment.updateSeatViewExchangedWithPitArray(mRoomInfoResp);
+ val typeId = mRoomInfoResp!!.room_info.type_id
+ if ("2" == typeId) {
+ roomFragment!!.handleAuctionMessageEvent(messageEvent)
+ return
+ } else if ("1" == typeId || "4" == typeId || "3" == typeId || "8" == typeId) {
+ val labelId = mRoomInfoResp!!.room_info.label_id
+ if ("2" == labelId) {
+ roomFragment!!.KtvFragmentEvent(messageEvent)
+ return
+ } else if ("1" == labelId) {
+ roomFragment!!.SingSongEvent(messageEvent)
+ return
+ }
+ } else if ("7" == typeId) { //交友房
+ roomFragment!!.friendshipRoomFragmentEvent(messageEvent)
+ return
+ }
+ }
+
+ private fun handleMsgType1014(messageEvent: RoomMessageEvent, text: T?) {
+ if (text == null) return
+
+ val roomSettingEvent = RoomSettingEvent()
+ roomSettingEvent.roomId = messageEvent.roomId
+ roomSettingEvent.room_up_pit_type = text.room_up_pit_type
+ roomSettingEvent.type = messageEvent.msgType
+
+ if (mRoomBean != null) {
+ mRoomBean!!.room_up_pit_type = text.room_up_pit_type.toString() + ""
+ EventBus.getDefault().post(mRoomBean)
+ }
+
+ EventBus.getDefault().post(roomSettingEvent)
+ }
+
+ private fun handleMsgType1013(messageEvent: RoomMessageEvent, text: T?) {
+ if (text == null || mRoomUserBean == null) return
+
+ val userId2 = SpUtil.getUserId()
+
+ if (text.action == 1 && mRoomUserBean!!.user_id == userId2.toString() + "" && mRoomUserBean!!.pit_number == 9) {
+ queren(text.fromUserInfo.nickname)
+ } else if (text.action == 4 && text.fromUserInfo.user_id == userId2) {
+ queren1(text.fromUserInfo.nickname)
+ } else {
+ roomFragment?.KtvFragmentEvent(messageEvent)
+ }
+ }
+
+ private fun handleMsgType1012() {
+ if (customMusicFloatingView != null) {
+ customMusicFloatingView!!.destroy()
+ }
+ AgoraManager.getInstance(this@RoomActivity).desMusic()
+
+ // stub.setVisibility(View.GONE);
+ if ((mRoomInfoResp!!.room_info.type_id == "1" || mRoomInfoResp!!.room_info.type_id == "4" ||
+ mRoomInfoResp!!.room_info.type_id == "3" || mRoomInfoResp!!.room_info.type_id == "8") &&
+ mRoomInfoResp!!.room_info.label_id == "2"
+ ) {
+ for (roomPitBean in mRoomInfoResp!!.song_pit_list) {
+ AgoraManager.getInstance(this@RoomActivity).ClientRole(false)
+ ivWheatFeeding(com.xscm.moduleutil.R.mipmap.room_wheat_feeding)
+ mBinding!!.rlMic.visibility = View.GONE
+ }
+ if (mRoomInfoResp!!.song_user_info != null && mRoomInfoResp!!.song_user_info.user_id != null) {
+ if (mRoomInfoResp!!.song_user_info.user_id == SpUtil.getUserId().toString() + "") {
+ AgoraManager.getInstance(this@RoomActivity).ClientRole(false)
+ ivWheatFeeding(com.xscm.moduleutil.R.mipmap.room_wheat_feeding)
+ mBinding!!.rlMic.visibility = View.GONE
+ }
+ }
+ }
+ CommonAppContext.getInstance().onlineMap.clear()
+ MvpPre!!.postRoomInfo(roomId)
+ }
+
+ private fun handleMsgType124(messageEvent: RoomMessageEvent, text: T?) {
+ if (text == null) return
+
+ try {
+ val parsedText = GsonUtils.fromJson(
+ text.text,
+ text::class.java
+ )
+ val musicPlayBean = MusicPlayBean()
+ musicPlayBean.position = parsedText.position
+ EventBus.getDefault().post(musicPlayBean)
+ } catch (e: Exception) {
+ // Handle exception
+ }
+ }
+
+ private fun handleMsgType1003(messageEvent: RoomMessageEvent, text: T?) {
+ if (text == null || mRoomInfoResp == null || mRoomInfoResp!!.room_info == null) return
+ if (roomFragment == null) {
+ roomFragment = RoomFragment.newInstance()
+ supportFragmentManager
+ .beginTransaction()
+ .replace(R.id.vp_room_pager, roomFragment!!)
+ .commitAllowingStateLoss()
+ }
+
+ val fromUserInfo = text.fromUserInfo ?: return
+
+ val pitNumber = text.pit_number
+ val userId = fromUserInfo.user_id
+ val currentUserId = SpUtil.getUserId()
+
+ if ("9" == pitNumber && userId == currentUserId) {
+ mBinding!!.roomTop.rl.visibility = View.VISIBLE
+ ivSoundEffects(true)
+ }
+
+ if (userId == currentUserId) {
+ aBoolean = false
+ ivWheatFeeding(com.xscm.moduleutil.R.mipmap.room_wheat_feeding_up)
+ setBoolean(aBoolean)
+ if (mRoomInfoResp!!.user_info != null) {
+ mRoomInfoResp!!.user_info.pit_number =
+ pitNumber?.toInt() ?: -1
+ }
+ setRoleType(3, pitNumber!!.toInt())
+ switchMic(2)
+ }
+
+
+ val typeId = mRoomInfoResp!!.room_info.type_id
+ if ("2" == typeId) {
+ if ("9" == pitNumber) {
+ mRoomInfoResp!!.room_info.pit_list[0] = getPitBean(messageEvent)
+ if (mRoomInfoResp!!.user_info != null) {
+ mRoomInfoResp!!.user_info.pit_number = pitNumber.toInt()
+ }
+ }
+ roomFragment!!.upRoomInfoData(mRoomInfoResp)
+ roomFragment!!.handleAuctionMessageEvent(messageEvent)
+ } else if ("3" == typeId || "4" == typeId || "1" == typeId || "8" == typeId) {
+ val labelId = mRoomInfoResp!!.room_info.label_id
+ if ("2" == labelId) {
+ roomFragment!!.KtvFragmentEvent(messageEvent)
+ } else if ("1" == labelId) {
+ mRoomInfoResp!!.room_info.pit_list.set(pitNumber.toInt() - 1, getPitBean(messageEvent))
+ roomFragment!!.upRoomInfoData(mRoomInfoResp)
+ roomFragment!!.SingSongEvent(messageEvent)
+ }
+ } else if ("7" == typeId) {
+ mBinding!!.rlMore.visibility = View.GONE
+ mBinding!!.rlMisc.visibility = View.GONE
+
+ roomFragment!!.friendshipRoomFragmentEvent(messageEvent)
+ } else {
+ roomFragment!!.updateSeatViewExchangedWithPitArray(mRoomInfoResp)
+ }
+
+ // if (pitNumber.equals("9") && mRoomInfoResp.getUser_info().getUser_id().equals(SpUtil.getUserId()+"")) {
+// ivSoundEffects(true);
+// } else {
+// ivSoundEffects(false);
+// }
+ }
+
+ private fun handleMsgType1004(messageEvent: RoomMessageEvent, text: T?) {
+ if (text == null || mRoomInfoResp == null || mRoomInfoResp!!.room_info == null) return
+
+ val fromUserInfo = text.fromUserInfo ?: return
+
+ val pitNumber = text.pit_number
+ val userId = fromUserInfo.user_id
+ val currentUserId = SpUtil.getUserId()
+
+ if (userId == currentUserId) {
+ aBoolean = true
+ ivWheatFeeding(com.xscm.moduleutil.R.mipmap.room_wheat_feeding)
+ setBoolean(aBoolean)
+ if (mRoomInfoResp!!.user_info != null) {
+ mRoomInfoResp!!.user_info.pit_number = 0
+ }
+ setRoleType(0, 0)
+ switchMic(2)
+ }
+
+ if (pitNumber == "9") {
+ ivSoundEffects(false)
+ }
+
+ if (userId == currentUserId && "9" == pitNumber) {
+ if (customMusicFloatingView != null) {
+ customMusicFloatingView!!.destroy()
+ AgoraManager.getInstance(this@RoomActivity).desMusic()
+ isMusic = false
+ }
+ mBinding!!.roomTop.rl.visibility = View.GONE
+ ivSoundEffects(false)
+ }
+
+ val typeId = mRoomInfoResp!!.room_info.type_id
+ if ("2" == typeId) {
+ if ("9" == pitNumber) {
+ if (userId == currentUserId) {
+ mRoomInfoResp!!.room_info.pit_list[0] = getPitBean2(messageEvent, "9")
+ if (mRoomInfoResp!!.user_info != null) {
+ mRoomInfoResp!!.user_info.pit_number = 0
+ }
+ }
+ } else if ("888" == pitNumber) {
+ mRoomInfoResp!!.room_auction = null
+ if (userId == currentUserId) {
+ setRoleType(0, 0)
+ switchMic(2)
+ }
+ }
+ roomFragment!!.upRoomInfoData(mRoomInfoResp)
+ roomFragment!!.handleAuctionMessageEvent(messageEvent)
+ } else if ("3" == typeId || "4" == typeId || "1" == typeId || "8" == typeId) {
+ val labelId = mRoomInfoResp!!.room_info.label_id
+ if ("2" == labelId) {
+ roomFragment!!.KtvFragmentEvent(messageEvent)
+ } else if ("1" == labelId) {
+ mRoomInfoResp!!.room_info.pit_list.set(pitNumber.toInt() - 1, getPitBean2(messageEvent, pitNumber))
+ roomFragment!!.upRoomInfoData(mRoomInfoResp)
+ roomFragment!!.SingSongEvent(messageEvent)
+ if (mRoomInfoResp!!.user_info.user_id == SpUtil.getUserId().toString() + "") {
+ ivSoundEffects(false)
+ }
+ }
+ } else {
+ roomFragment!!.friendshipRoomFragmentEvent(messageEvent)
+ }
+ }
+
+ private fun handleMsgType1022(messageEvent: RoomMessageEvent, text: T?) {
+ if (text == null || mRoomInfoResp == null || mRoomInfoResp!!.room_info == null) return
+
+ val pitNumber = text.pit_number
+ val userId = text.fromUserInfo.user_id
+ val currentUserId = SpUtil.getUserId()
+
+ if (text.type == 1) {
+ if ("888" == pitNumber) {
+ var type = -1
+ if ("2" == mRoomInfoResp!!.room_info.type_id) {
+ type = if ("1" == mRoomInfoResp!!.room_info.label_id) 1 else 2
+ }
+ if (userId == currentUserId) {
+ setRoleType(3, 888)
+ switchMic(2)
+ }
+ }
+ } else if (text.type == 2) {
+ if ("9" == pitNumber) {
+ setRoleType(0, 0)
+ switchMic(2)
+ } else if ("888" == pitNumber) {
+ mRoomInfoResp!!.room_auction = RoomAuction()
+ if (userId == currentUserId) {
+ setRoleType(0, 0)
+ switchMic(2)
+ }
+ }
+ }
+
+ roomFragment!!.upRoomInfoData(mRoomInfoResp)
+ roomFragment!!.handleAuctionMessageEvent(messageEvent)
+ }
+
+ private fun handleMsgType1023(messageEvent: RoomMessageEvent, text: T?) {
+ if (text == null || mRoomInfoResp == null || mRoomInfoResp!!.room_auction == null) return
+
+ mRoomInfoResp!!.room_auction.auction_user = text.auction_user
+ SpUtil.setAuctionId(text.auction_user.auction_id)
+ roomFragment!!.upRoomInfoData(mRoomInfoResp)
+ roomFragment!!.handleAuctionMessageEvent(messageEvent)
+ // roomFragment.updateSeatViewExchangedWithPitArray(mRoomInfoResp);
+ }
+
+ private fun handleMsgType1024(messageEvent: RoomMessageEvent, text: T?) {
+ if (text == null || mRoomInfoResp == null || mRoomInfoResp!!.room_auction == null) return
+ if (mRoomInfoResp!!.room_auction.auction_list != null) {
+ mRoomInfoResp!!.room_auction.auction_list.clear()
+ }
+ if (mRoomInfoResp!!.room_auction.auction_list != null && text.auction_list != null) {
+ mRoomInfoResp!!.room_auction.auction_list.addAll(text.auction_list)
+ } else {
+ mRoomInfoResp!!.room_auction.auction_list = ArrayList()
+ // mRoomInfoResp.getRoom_auction().getAuction_list().addAll(text.getAuction_list());
+ }
+ roomFragment!!.upRoomInfoData(mRoomInfoResp)
+ roomFragment!!.handleAuctionMessageEvent(messageEvent)
+ // roomFragment.updateSeatViewExchangedWithPitArray(mRoomInfoResp);
+ }
+
+ private fun handleMsgType1020(messageEvent: RoomMessageEvent, text: T?) {
+ if (text == null) return
+
+ mRoomBean = text.roomInfo
+ EventBus.getDefault().post(mRoomBean)
+ changeBackgroundColor(mRoomBean?.getRoom_background())
+ }
+
+ private fun handleMsgType1011(messageEvent: RoomMessageEvent, text: T?) {
+ if (text == null || text.fromUserInfo == null) return
+
+ if (text.fromUserInfo.user_id == SpUtil.getUserId()) {
+ LogUtils.e("退出房间")
+ MvpPre!!.quitRoom(roomId, SpUtil.getUserId().toString() + "")
+ }
+ }
+
+ private fun handleMsgType1001() {
+ number++
+ mBinding!!.roomTop.tvNum.text = number.toString() + ""
+ }
+
+ private fun handleMsgType1002() {
+ number--
+ if (number < 0) {
+ number = 0
+ }
+ mBinding!!.roomTop.tvNum.text = number.toString() + ""
+ }
+
+ private fun handleMsgType1029(messageEvent: RoomMessageEvent, text: T?) {
+ if (text == null || mRoomInfoResp == null || mRoomInfoResp!!.user_info == null) return
+
+ if (mRoomInfoResp!!.user_info.pit_number == 9 && mRoomInfoResp!!.user_info.user_id == SpUtil.getUserId()
+ .toString() + ""
+ ) {
+ querenPk(text.text, text.pkId)
+ }
+ }
+
+ private fun handleMsgType1021(messageEvent: RoomMessageEvent, text: T) {
+ if (mRoomInfoResp == null || mRoomInfoResp!!.room_info == null) return
+
+ val typeId = mRoomInfoResp!!.room_info.type_id
+ if ("2" == typeId) {
+ roomFragment!!.handleAuctionMessageEvent(messageEvent)
+ } else if ("1" == typeId || "4" == typeId || "3" == typeId || "8" == typeId) {
+ val labelId = mRoomInfoResp!!.room_info.label_id
+ if ("2" == labelId) {
+ roomFragment!!.KtvFragmentEvent(messageEvent)
+ } else {
+ updateCharmForAllPitBeans("")
+ roomFragment!!.upRoomInfoData(mRoomInfoResp)
+ roomFragment!!.SingSongEvent(messageEvent)
+ }
+ } else {
+ updateCharmForAllPitBeans("")
+ roomFragment!!.upFriendList(mRoomInfoResp!!.room_info.pit_list)
+ }
+ }
+
+ private fun updateCharmForAllPitBeans(charm: String) {
+ if (mRoomInfoResp == null || mRoomInfoResp!!.room_info == null) return
+
+ val pitList = mRoomInfoResp!!.room_info.pit_list ?: return
+
+ for (roomPitBean in pitList) {
+ roomPitBean.charm = charm
+ try {
+ pitList[roomPitBean.pit_number.toInt() - 1] = roomPitBean
+ } catch (e: NumberFormatException) {
+ // Handle exception
+ }
+ }
+ }
+
+ private fun handleMsgType1036(messageEvent: RoomMessageEvent, text: T?) {
+ if (text == null) return
+
+ if (text.room_id == roomId) {
+ val onlineNumber = text.online_number.toString() + ""
+ if (mBinding!!.roomTop.tvNum.text.toString() != onlineNumber) {
+ mBinding!!.roomTop.tvNum.text = onlineNumber
+ }
+ }
+ }
+
+ private fun handleMsgType1049(messageEvent: RoomMessageEvent, text: T?) {
+ if (text == null) return
+
+ val endTime = if (text.end_time != null) text.end_time.toLong() else 0
+ if (text.step != 3) {
+ roomFragment!!.updateFriendshipState(text.step, text.friend_id, endTime, null)
+ } else {
+ roomFragment!!.updateFriendshipState(text.step, text.friend_id, 0, text.friend_user)
+ }
+ }
+
+ private fun handleMsgType1050(messageEvent: RoomMessageEvent, text: T?) {
+ if (text == null) return
+
+ roomFragment!!.friendTimeDelayWithTime(text.end_time.toLong())
+ }
+
+ private fun handleMsgType1051(messageEvent: RoomMessageEvent, text: T?) {
+ if (text == null) return
+
+ if (text.user1_id == null || text.user2_id == null) {
+ return
+ }
+ if (roomFragment != null) {
+ roomFragment!!.clearDialog()
+ }
+ val friend_user = getFriendUserBean(messageEvent)
+ FriendsDialogFragment.show(friend_user, supportFragmentManager)
+
+
+ if (text.user1_id == SpUtil.getUserId()
+ .toString() + "" || text.user2_id == SpUtil.getUserId().toString() + ""
+ ) {
+ if (roomSwitchRunnable != null) {
+ roomSwitchHandler.removeCallbacks(roomSwitchRunnable!!)
+ }
+
+ pendingRoomId = text.room_id
+ if (pendingRoomId == null) {
+ return
+ }
+ LogUtils.e("需要加入的房间id:pendingRoomId: $pendingRoomId")
+ roomSwitchRunnable = Runnable {
+ if (!isFinishing && !isDestroyed) {
+ if (pendingRoomId != lastSwitchedRoomId) {
+ MvpPre!!.quitRoom2(messageEvent.roomId, SpUtil.getUserId().toString() + "")
+ quit()
+ MessageListenerSingleton.quitGroup(messageEvent.roomId)
+ MessageListenerSingleton.getInstance().joinGroup(pendingRoomId)
+ MvpPre!!.getRoomIn(pendingRoomId, "")
+ lastSwitchedRoomId = pendingRoomId!!
+ AgoraManager.getInstance(OkDownloadProvider.context).lastRoomId =
+ messageEvent.roomId
+// RoomManager.getInstance().fetchRoomDataAndEnter(this,pendingRoomId, "")
+ }
+ }
+ pendingRoomId = null
+ }
+
+ roomSwitchHandler.postDelayed(roomSwitchRunnable!!, 3000)
+ }
+ }
+
+ //退出房间
+ fun quitRoomAll(roomId: String?) {
+ MvpPre!!.quitRoom(roomId, SpUtil.getUserId().toString() + "")
+ }
+
+ private fun handleMsgType1053(messageEvent: RoomMessageEvent, text: T?) {
+ if (text == null || text.list == null || text.list.isEmpty()) return
+ val pitArr: MutableList = ArrayList()
+ for (j in text.list.indices) {
+ val roomPitBean = getRoomPitBean(messageEvent, j)
+ pitArr.add(roomPitBean)
+ }
+
+ roomFragment!!.friendSeatDidChanged(pitArr)
+ pitArr.clear()
+ }
+
+ private fun handleMsgType1054(messageEvent: RoomMessageEvent, text: T?) {
+ if (text == null || text.list == null) return
+
+ val heartList = text.list.stream()
+ .map { item: UserInfo ->
+ val heartList1 = HeartList()
+ heartList1.heartId = item.heartId
+ heartList1.heartNum = item.heartNum
+ heartList1
+ }
+ .collect(Collectors.toList())
+
+ roomFragment!!.friendHeartNumberDidChanged(heartList)
+ }
+
+ private fun handleMsgType1055(messageEvent: RoomMessageEvent) {
+ if (!isFinishing && !isDestroyed) {
+ MvpPre!!.quitRoom2(messageEvent.roomId, SpUtil.getUserId().toString() + "")
+ quit()
+ jiaR()
+ }
+ }
+
+ private fun handleMsgType1035(messageEvent: RoomMessageEvent, text: T?) {
+ if (text == null || mRoomInfoResp == null || mRoomInfoResp!!.room_info == null) return
+
+ val fromUserInfo = text.fromUserInfo ?: return
+
+ val typeId = mRoomInfoResp!!.room_info.type_id
+ if ("2" == typeId) {
+ roomFragment!!.handleAuctionMessageEvent(messageEvent)
+ } else if ("1" == typeId || "3" == typeId || "4" == typeId || "8" == typeId) {
+ val labelId = mRoomInfoResp!!.room_info.label_id
+ if ("2" == labelId) {
+ roomFragment!!.KtvFragmentEvent(messageEvent)
+ } else {
+// updatePitBeanForUser(fromUserInfo);
+ roomFragment!!.upRoomInfoData(updatePitBeanForUser(fromUserInfo))
+ roomFragment!!.SingSongEvent(messageEvent)
+ }
+ } else {
+ roomFragment!!.upRoomInfoData(updatePitBeanForUser(fromUserInfo))
+ roomFragment!!.friendshipRoomFragmentEvent(messageEvent)
+ }
+ }
+
+ private fun updatePitBeanForUser(fromUserInfo: UserInfo): RoomInfoResp? {
+ if (mRoomInfoResp == null || mRoomInfoResp!!.room_info == null) return mRoomInfoResp
+
+ val pitList = mRoomInfoResp!!.room_info.pit_list ?: return mRoomInfoResp
+
+ for (pitBean in pitList) {
+ if (pitBean.user_id == fromUserInfo.user_id.toString() + "") {
+ pitBean.charm = fromUserInfo.charm
+ pitBean.avatar = fromUserInfo.avatar
+ pitBean.nickname = fromUserInfo.nickname
+ pitBean.sex = fromUserInfo.sex.toString() + ""
+ pitBean.dress = fromUserInfo.dress
+ }
+ }
+ return mRoomInfoResp
+ }
+
+ private fun handleMsgType125(messageEvent: RoomMessageEvent, text: T?) {
+ if (text == null) return
+
+ try {
+ val parsedText = GsonUtils.fromJson(
+ text.text,
+ text::class.java
+ )
+ if (parsedText.is_mute == 1) {
+ AgoraManager.getInstance(this).ClientRole(false)
+ AgoraManager.getInstance(this).muteLocalAudioStreamEx(false, SpUtil.getUserId())
+ } else {
+ AgoraManager.getInstance(this).ClientRole(true)
+ AgoraManager.getInstance(this).muteLocalAudioStreamEx(true, SpUtil.getUserId())
+ }
+ } catch (e: Exception) {
+ // Handle exception
+ }
+ }
+
+ private fun handleMsgType1007() {
+ if (mRoomInfoResp != null && mRoomInfoResp!!.user_info != null) {
+ mRoomInfoResp!!.user_info.is_host = 1
+ roomFragment!!.upRoomInfoData(mRoomInfoResp)
+ // roomFragment.updateSeatViewExchangedWithPitArray(mRoomInfoResp);
+ }
+ }
+
+ private fun handleMsgType1006() {
+ if (mRoomInfoResp != null && mRoomInfoResp!!.user_info != null) {
+ mRoomInfoResp!!.user_info.is_management = 1
+ roomFragment!!.upRoomInfoData(mRoomInfoResp)
+ }
+ }
+
+ private fun handleMsgType1018() {
+ if (mRoomInfoResp != null && mRoomInfoResp!!.user_info != null) {
+ mRoomInfoResp!!.user_info.is_host = 0
+ roomFragment!!.upRoomInfoData(mRoomInfoResp)
+ // roomFragment.updateSeatViewExchangedWithPitArray(mRoomInfoResp);
+ }
+ }
+
+ private fun handleMsgType1017() {
+ if (mRoomInfoResp != null && mRoomInfoResp!!.user_info != null) {
+ mRoomInfoResp!!.user_info.is_management = 0
+ roomFragment!!.upRoomInfoData(mRoomInfoResp)
+ }
+ }
+
+ private fun handleMsgType126(messageEvent: RoomMessageEvent, text: T?) {
+ if (text == null) return
+
+ AgoraManager.getInstance(this)
+ .setLocalAudioEnabled(false, text.fromUserInfo.user_id.toString() + "")
+ }
+
+ private fun handleMsgType1034(messageEvent: RoomMessageEvent, text: T?) {
+ if (text == null) return
+
+ val count = text.count
+ if (count == 0) {
+ tvFirst(SpannableStringBuilder("0人排队"))
+ return
+ }
+
+ val countText = count.toString()
+ val fullText = countText + "人排队"
+
+ val spannableStringBuilder = SpannableStringBuilder(fullText)
+ spannableStringBuilder.setSpan(
+ ForegroundColorSpan(
+ ContextCompat.getColor(
+ this,
+ com.xscm.moduleutil.R.color.color_C7BF62
+ )
+ ),
+ 0,
+ countText.length,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
+ )
+ tvFirst(spannableStringBuilder)
+ }
+
+ private fun handleMsgType1016(messageEvent: RoomMessageEvent, text: T?) {
+ if (text == null || mRoomInfoResp == null || mRoomInfoResp!!.user_info == null) return
+
+ if (text.fromUserInfo.user_id == SpUtil.getUserId()) {
+ if (text.is_mute_pit == 1) {
+ switchMic(2)
+ }
+ mRoomInfoResp!!.user_info.is_mute_pit = text.is_mute_pit.toString()
+ mRoomInfoResp!!.user_info.is_mute = text.is_mute.toString()
+ }
+ }
+
+ // TODO: 2025/6/30 上麦,麦位变化
+ private fun getPitBean(messageEvent: RoomMessageEvent): RoomPitBean {
+ val pitBean = RoomPitBean()
+ pitBean.pit_number = messageEvent.text.pit_number
+ pitBean.user_id = messageEvent.text.fromUserInfo.user_id.toString() + ""
+ pitBean.avatar = messageEvent.text.fromUserInfo.avatar
+ pitBean.nickname = messageEvent.text.fromUserInfo.nickname
+ pitBean.sex = messageEvent.text.fromUserInfo.sex.toString() + ""
+ pitBean.charm = messageEvent.text.fromUserInfo.charm
+
+ return pitBean
+ }
+
+ // TODO: 2025/6/30 下麦麦位变化
+ private fun getPitBean2(messageEvent: RoomMessageEvent?, number: String): RoomPitBean {
+ val pitBean = RoomPitBean()
+ pitBean.pit_number = if (messageEvent != null) messageEvent.text.pit_number else number
+ pitBean.user_id = ""
+ pitBean.avatar = ""
+ pitBean.nickname = ""
+ pitBean.sex = ""
+ pitBean.charm = ""
+ pitBean.is_pm = 1
+ return pitBean
+ }
+
+ // TODO: 2025/8/29 排麦位上麦
+ private fun getPitBean3(messageEvent: RoomMessageEvent): AuctionUserBean {
+ val roomAuction = AuctionUserBean()
+ roomAuction.user_id = messageEvent.text.fromUserInfo.user_id.toString() + ""
+ roomAuction.avatar = messageEvent.text.fromUserInfo.avatar
+ roomAuction.nickname = messageEvent.text.fromUserInfo.nickname
+ roomAuction.sex = messageEvent.text.fromUserInfo.sex.toString() + ""
+ roomAuction.charm = messageEvent.text.fromUserInfo.charm
+
+ return roomAuction
+ }
+
+ fun mus() {
+ if (mRoomInfoResp!!.user_info.is_mute == "1") {
+ ToastUtils.show("您已经被禁言")
+ } else {
+ switchMic(1)
+ }
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ fun onMessageEvent(messageEvent: UnreadCountEvent) {
+ if (messageEvent.aLong == 0L) {
+ mBinding!!.ivMessageDot.visibility = View.GONE
+ } else {
+ mBinding!!.ivMessageDot.visibility = View.VISIBLE
+ mBinding!!.ivMessageDot.text = messageEvent.aLong.toString()
+ }
+ }
+
+ private fun querenPk(text: String, pk_id: String) {
+ // 创建并显示确认对话框
+ val dialog = ConfirmDialog(
+ this,
+ "温馨提示",
+ text,
+ "确认",
+ "拒绝",
+ { v: View? ->
+ // 点击“确认”按钮时执行删除操作
+ MvpPre!!.acceptPk(pk_id, "1")
+ },
+ { v: View? ->
+ MvpPre!!.acceptPk(pk_id, "2")
+ }, true, 10
+ )
+ dialog.show()
+ addActiveDialog(dialog)
+ }
+
+ /**
+ * 修改背景图
+ *
+ * @param bjId
+ */
+ fun changeBackgroundColor(bjId: String?) {
+ // 假设你的Activity有一个根布局,例如 ConstraintLayout
+ ImageUtils.loadImageWithCache(this, bjId, mBinding!!.ivBg)
+ }
+
+ fun changeBackground(bjId: Int) {
+ mBinding!!.ivBg.setImageDrawable(resources.getDrawable(bjId))
+ }
+
+ fun setvisibTop(`is`: Boolean) {
+ mBinding!!.roomTop.root.visibility =
+ if (`is`) View.VISIBLE else View.GONE
+ }
+
+ fun setDiany(`is`: Boolean) {
+// mBinding.flaoat.setVisibility(is? VISIBLE : GONE);
+ }
+
+ fun setOnlineNumber(number: Int) {
+ mBinding!!.roomTop.tvNum.text = number.toString() + ""
+ }
+
+ fun setUserInfo() {
+ mRoomInfoResp!!.user_info.is_collect = 1
+ mBinding!!.roomTop.btnFollow.background =
+ resources.getDrawable(com.xscm.moduleutil.R.mipmap.yishouc)
+ mBinding!!.roomTop.btnFollow.text = ""
+ }
+
+ /**
+ * 特效设置
+ */
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ fun setEffectSwitch(event: EffectEvent) {
+ if (event.isEffectOn) { //特效开启
+ QXGiftPlayerManager.getInstance(this).openOrCloseEffectViewWith(true)
+ mBinding!!.svgaGift.visibility = View.VISIBLE
+ } else {
+// mBinding.svgaGift.closeEffect();
+// mBinding.svgaGift.closeEffect();
+ QXGiftPlayerManager.getInstance(this).openOrCloseEffectViewWith(false)
+ mBinding!!.svgaGift.visibility = View.GONE
+ }
+ }
+
+ /**
+ * 坐骑进场特效
+ *
+ * @param roomJoinMountModel
+ */
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ fun roomJoinMount(roomJoinMountModel: RoomJoinMountModel) {
+ if (roomId != roomJoinMountModel.room_id) {
+ return
+ }
+ if (roomJoinMountModel.show_type != 1) {
+ val gift = GiftBean()
+ gift.gift_id = ""
+ gift.play_image = roomJoinMountModel.ride_url
+ QXGiftPlayerManager.getInstance(this).displayFullEffectView(roomJoinMountModel.ride_url)
+ // mBinding.svgaZuoji.setSource(roomJoinMountModel.getRide_url(), 2);
+ }
+ }
+
+ private var isSwith = false
+ private var voive = false //声音开关
+ private var canSend = true
+ private var aBoolean = true //上下麦按钮
+ private var mCountDownTimer: CountDownTimer? = null
+ private var mCountDownTimer2: CountDownTimer? = null
+
+ private fun onClick(view: View) {
+ val id = view.id
+ if (id == R.id.btn_follow) {
+ MvpPre!!.userGuanz(mRoomInfoResp!!.room_info.room_id, "2")
+ } else if (id == R.id.btn_notice) {
+// RoomTipsView.show(this,view,"公告","公告内容");
+ val roomNoticeDialogFragment = RoomNoticeDialogFragment(this)
+ roomNoticeDialogFragment.setTitle("公告")
+ roomNoticeDialogFragment.setContent(mRoomBean!!.room_intro)
+ roomNoticeDialogFragment.show()
+ addActiveDialog(roomNoticeDialogFragment)
+ } else if (id == R.id.btn_ranking) { //排行榜
+// RoomChartsFragment.newInstance(roomId).show(getSupportFragmentManager(), "RoomChartsFragment");
+ val fragment = RoomChartsFragment.newInstance(roomId, mRoomInfoResp)
+ fragment.show(supportFragmentManager, "RoomChartsFragment")
+ addActiveDialogFragment(fragment)
+ } else if (id == R.id.btn_close_live) { //退出房间
+ showExitRoomDialog()
+ } else if (id == R.id.tv_num) {
+// RoomOnlineDialogFragment.show(roomId, "", mRoomUserBean, mRoomInfoResp, getSupportFragmentManager());
+ val fragment = RoomOnlineDialogFragment.show(
+ roomId, "", mRoomUserBean, mRoomInfoResp,
+ supportFragmentManager
+ )
+ if (fragment != null) {
+ addActiveDialogFragment(fragment) // 添加到管理列表
+ }
+ } else if (id == R.id.rl) {
+ MvpPre!!.clearUserCharm(roomId, "")
+ } else if (id == R.id.ll_input) {
+ mBinding!!.llInput.visibility = View.GONE
+ mBinding!!.inputMenu1.dismiss()
+ }else if (id==R.id.iv_emoji){
+ val emotionPickerDialog = EmotionPickerDialog(this)
+ emotionPickerDialog.setOnEmotionSelectedListener { emotion ->
+ // 处理选中的表情
+ if (publicScreenFragment!=null){
+ publicScreenFragment!!.sendChatEmoji(emotion)
+ }
+ }
+ emotionPickerDialog.show()
+ }
+ else if (id == R.id.iv_chat) {
+ if (mRoomInfoResp!!.user_info.is_mute == "1") {
+ ToastUtils.show("您已经被禁言")
+ } else {
+ // mBinding.llInput.setVisibility(View.VISIBLE);
+// mBinding.inputMenu1.bringToFront(); // 强制将该 View 置于最上层
+// mBinding.inputMenu1.show();
+
+ dialogDismiss(1)
+ }
+ } else if (id == R.id.rl_mic) {
+ if (mRoomInfoResp!!.user_info.is_mute_pit != null && mRoomInfoResp!!.user_info.is_mute_pit == "1") {
+ ToastUtils.show("您被禁麦了")
+ } else {
+ if (isSwith) {
+ switchMic(1)
+ } else {
+ switchMic(2)
+ }
+ }
+ } else if (id == R.id.rl_sett) { //房间设置
+// RoomSettingFragment.show(mRoomInfoResp, getSupportFragmentManager());
+ if (mRoomInfoResp == null) {
+ com.blankj.utilcode.util.ToastUtils.showShort("请稍后再试")
+ return
+ }
+ val fragment = RoomSettingFragment.show(
+ mRoomInfoResp,
+ supportFragmentManager
+ )
+ if (fragment != null) {
+ addActiveDialogFragment(fragment) // 添加到管理列表
+ }
+ } else if (id == R.id.rl_voive) {
+ if (voive) {
+ mBinding!!.imVoive.setImageResource(com.xscm.moduleutil.R.mipmap.room_voice_g)
+ voive = false
+ } else {
+ mBinding!!.imVoive.setImageResource(com.xscm.moduleutil.R.mipmap.room_voice_kg)
+ voive = true
+ }
+ AgoraManager.getInstance(this).muteSpeaker(!voive)
+ } else if (id == R.id.rl_more) { //点击PK
+// RoomPkDialogFragment.newInstance(roomId, SpUtil.getUserId() + "", mRoomInfoResp.getRoom_info().getIs_pk()).show(getSupportFragmentManager(), "RoomPkDialogFragment");
+ val fragment = RoomPkDialogFragment.newInstance(
+ roomId,
+ SpUtil.getUserId().toString() + "",
+ mRoomInfoResp!!.room_info.is_pk
+ )
+ fragment.show(supportFragmentManager, "RoomPkDialogFragment")
+ addActiveDialogFragment(fragment) // 添加到管理列表
+ } else if (id == R.id.rl_message) {
+// RoomMessageDialogFragment.show(getSupportFragmentManager());
+ val fragment = RoomMessageDialogFragment.show(
+ supportFragmentManager
+ )
+ if (fragment != null) {
+ addActiveDialogFragment(fragment) // 添加到管理列表
+ }
+ } else if (id == R.id.rl_misc) { //设置点歌
+ if (mRoomInfoResp!!.room_info.label_id == "1") {
+ queren()
+ } else {
+// RequestDialogFragment.show(roomId, mRoomInfoResp, 1, getSupportFragmentManager());
+ val fragment = RequestDialogFragment.show(
+ roomId, mRoomInfoResp, 1,
+ supportFragmentManager
+ )
+ if (fragment != null) {
+ addActiveDialogFragment(fragment) // 添加到管理列表
+ }
+ }
+ } else if (id == R.id.rl_gift) { //礼物
+// RoomGiftDialogFragment.show(mRoomInfoResp, null, roomId, 0, "", getSupportFragmentManager());
+ val fragment = RoomGiftDialogFragment.show(
+ mRoomInfoResp, null, roomId, 0, "",
+ supportFragmentManager
+ )
+ if (fragment != null) {
+ addActiveDialogFragment(fragment) // 添加到管理列表
+ }
+ } else if (id == R.id.iv_sound_effects) {
+// SoundEffectsDialogFragment.show(roomId, getSupportFragmentManager());
+ val fragment = SoundEffectsDialogFragment.show(
+ roomId,
+ supportFragmentManager
+ )
+ if (fragment != null) {
+ addActiveDialogFragment(fragment) // 添加到管理列表
+ }
+ } else if (id == R.id.cl_first_charge) {
+// showWheatFeedingDialog(roomId, (mRoomInfoResp.getUser_info().getIs_room_owner() == 1 || mRoomInfoResp.getUser_info().getIs_host() == 1 || mRoomInfoResp.getUser_info().getIs_management() == 1) ? 1 : 2);
+ roomFragment!!.showWheatFeedingDialog(
+ roomId,
+ if ((mRoomInfoResp!!.user_info.is_room_owner == 1 || mRoomInfoResp!!.user_info.is_host == 1 || mRoomInfoResp!!.user_info.is_management == 1)) 1 else 2
+ )
+ } else if (id == R.id.iv_wheat_feeding) { //点击上麦操作
+ if (mRoomInfoResp!!.room_info.room_up_pit_type == "1") {
+// showWheatFeedingDialog(roomId, mRoomInfoResp.getUser_info().getPit_number() == 9 ? 1 : 2);
+ if (aBoolean) {
+ MvpPre!!.applyPit(roomId, "")
+ // aBoolean = false;
+ } else {
+ MvpPre!!.downPit(roomId, "")
+ // aBoolean = true;
+ }
+ } else {
+ if (aBoolean) {
+// mBinding.ivWheatFeeding.setImageResource(com.xscm.moduleutil.R.mipmap.room_wheat_feeding_up);
+ MvpPre!!.applyPit(roomId, "")
+ aBoolean = false
+ } else {
+// mBinding.ivWheatFeeding.setImageResource(com.xscm.moduleutil.R.mipmap.room_wheat_feeding);
+ MvpPre!!.downPit(roomId, "")
+ aBoolean = true
+ }
+ }
+ }
+ }
+
+ // TODO: 2025/9/18 type==1:退出房间,进入到首页 2:退出当前页,不导航到首页,是从首页点击关闭悬浮退出的
+ fun performExitRoom(type: Int) {
+ // 清理最小化状态
+ clearMinimizeState()
+ isMinimized = false
+
+ // 执行退出房间逻辑
+ CommonAppContext.getInstance().isPlaying = false
+ CommonAppContext.getInstance().isShow = false
+ QXGiftPlayerManager.getInstance(this).destroyEffectSvga()
+ // 停止屏幕捕获和其他资源
+// AgoraManager.getInstance(this).stopScreenCapture();
+// AgoraManager.getInstance(this).leaveRoom();
+// AgoraManager.getInstance(this).stopMusicPlayer();
+ AgoraManager.getInstance(this).cleanup()
+
+ MyRoomSingleton.getInstance().onExitRoom()
+ MessageListenerSingleton.quitGroup(roomId)
+ // 清理ViewModel中的数据
+// if (sharedViewModel != null) {
+// sharedViewModel.clearAllData();
+// }
+ RoomManager.getInstance().exitRoom(roomId)
+ // 清理资源
+ cleanupResources()
+ if (type == 1) {
+ // 导航到首页
+ navigateToMainPage()
+ } else if (type == 2) {
+ // 返回上一个页面
+ finish()
+ }
+
+ finish()
+ }
+
+ private val isAppInForeground: Boolean
+ // 添加前后台状态检测
+ get() {
+ if (mRoomInfoResp == null) {
+ return true
+ }
+ if (mRoomInfoResp!!.room_info.label_id != "6") {
+ val activityManager =
+ getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
+ val appProcesses =
+ activityManager.runningAppProcesses ?: return false
+
+ val packageName = packageName
+ for (appProcess in appProcesses) {
+ if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
+ && appProcess.processName == packageName
+ ) {
+ return true
+ }
+ }
+ } else {
+ return true
+ }
+ return false
+ }
+
+ private fun navigateToMainPage() {
+ ARouter.getInstance()
+ .build(ARouteConstants.ME)
+ .navigation()
+
+ // 添加转场动画
+ overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
+ }
+
+ private fun quitUpRoom() {
+ // 清理最小化状态
+ clearMinimizeState()
+ isMinimized = false
+
+ // 执行退出房间逻辑
+ CommonAppContext.getInstance().isPlaying = false
+ CommonAppContext.getInstance().isShow = false
+ CommonAppContext.getInstance().playId = null
+ QXGiftPlayerManager.getInstance(this).destroyEffectSvga()
+ // 停止屏幕捕获和其他资源
+// AgoraManager.getInstance(this).stopScreenCapture();
+ AgoraManager.getInstance(this).leaveRoom()
+ // AgoraManager.getInstance(this).stopMusicPlayer();
+ AgoraManager.getInstance(this).cleanup()
+
+ MyRoomSingleton.getInstance().onExitRoom()
+ MessageListenerSingleton.quitGroup(roomId)
+ cleanupResources()
+ }
+
+ private fun minimizeToBackground() {
+ isMinimized = true
+ // 保存最小化状态和房间ID
+ saveMinimizeState()
+ // 设置应用状态
+ CommonAppContext.getInstance().isShow = false
+ // 通知状态管理器RoomActivity已最小化
+ if (appStateListener != null) {
+ appStateListener!!.isAppInBackground = true
+ }
+ QXGiftPlayerManager.getInstance(applicationContext).destroyEffectSvga()
+
+ ARouter.getInstance()
+ .build(ARouteConstants.ME)
+ .navigation()
+
+ // ARouter.getInstance().build(ARouteConstants.ME)
+// .addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP)
+// .navigation();
+
+ // 使用Intent启动主Activity,通过ARouter路径
+ // 这样可以避免模块间的直接依赖
+// try {
+// val intent = Intent()
+// intent.setClassName(packageName, "com.xscm.modulemain.activity.MainActivity")
+// intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
+// startActivity(intent)
+// } catch (e: Exception) {
+// // 如果直接指定类名失败,则使用默认的Launcher Activity
+// val startMain = Intent(Intent.ACTION_MAIN)
+// startMain.addCategory(Intent.CATEGORY_HOME)
+// startMain.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
+// startActivity(startMain)
+// }
+
+ // 隐藏Activity而不是销毁它
+// moveTaskToBack(true);
+ // 使用 moveTaskToBack 将应用最小化
+// moveTaskToBack(true);
+ }
+
+ private fun saveMinimizeState() {
+ val prefs = getSharedPreferences("room_minimize_state", Context.MODE_PRIVATE)
+ val editor = prefs.edit()
+ editor.putString(PREF_MINIMIZED_ROOM, roomId)
+ editor.putLong(PREF_MINIMIZED_TIME, System.currentTimeMillis())
+ editor.putBoolean("is_minimized", true)
+ editor.apply()
+ }
+
+ private fun clearMinimizeState() {
+ val prefs = getSharedPreferences("room_minimize_state", Context.MODE_PRIVATE)
+ val editor = prefs.edit()
+ editor.remove(PREF_MINIMIZED_ROOM)
+ editor.remove(PREF_MINIMIZED_TIME)
+ editor.putBoolean("is_minimized", false)
+ editor.apply()
+ }
+
+ private fun wasMinimized(): Boolean {
+ val prefs = getSharedPreferences("room_minimize_state", Context.MODE_PRIVATE)
+ return prefs.getBoolean("is_minimized", false)
+ }
+
+ private fun queren() {
+ // 创建并显示确认对话框
+ val dialog = ConfirmDialog(
+ this,
+ "提示",
+ "您将要发起点歌申请?",
+ "确认",
+ "取消",
+ { v: View? ->
+ // 点击“确认”按钮时执行删除操作
+ MvpPre!!.applySong(roomId)
+ },
+ { v: View? -> }, false, 0
+ )
+ dialog.show()
+ addActiveDialog(dialog)
+ }
+
+ /**
+ * 设置是否打开麦克风
+ *
+ * @param type
+ */
+ // @Override
+ fun switchMic(type: Int) {
+ if (type == 1) {
+ mBinding!!.ivMic.setImageResource(com.xscm.moduleutil.R.mipmap.room_microphone)
+ AgoraManager.getInstance(this)
+ .setLocalAudioEnabled(true, SpUtil.getUserId().toString() + "")
+ isSwith = false
+ AgoraManager.getInstance(this).muteLocalAudioStream(false)
+ AgoraManager.getInstance(this).ClientRole(true)
+ CommonAppContext.getInstance().isMai = true
+ } else {
+ mBinding!!.ivMic.setImageResource(com.xscm.moduleutil.R.mipmap.room_microphone_off)
+ AgoraManager.getInstance(this)
+ .setLocalAudioEnabled(false, SpUtil.getUserId().toString() + "")
+ isSwith = true
+ isMute(1)
+ CommonAppContext.getInstance().isMai = false
+ }
+ }
+
+ /** 进入小黑屋将所有的底部隐藏 */
+ fun setviewyc(voive: Boolean) {
+ mBinding!!.rlMore.visibility =
+ if (voive) View.GONE else View.GONE
+ mBinding!!.rlMisc.visibility =
+ if (voive) View.GONE else View.GONE
+ mBinding!!.rlMic.visibility =
+ if (voive) View.VISIBLE else View.GONE
+ mBinding!!.rlSett.visibility =
+ if (voive) View.VISIBLE else View.GONE
+ mBinding!!.rlVoive.visibility =
+ if (voive) View.VISIBLE else View.GONE
+ mBinding!!.rlGift.visibility =
+ if (voive) View.VISIBLE else View.GONE
+ mBinding!!.ivSoundEffects.visibility =
+ if (voive) View.VISIBLE else View.GONE
+ mBinding!!.ivWheatFeeding.visibility =
+ if (voive) View.VISIBLE else View.GONE
+ mBinding!!.clFirstCharge.visibility =
+ if (voive) View.VISIBLE else View.GONE
+ }
+
+ /** 这里是当进入电影放房的时候,如果是排麦模式,并且不是电影放的时候,就隐藏排麦视图 */
+ fun clFirstCharge(voive: Boolean) {
+ mBinding!!.clFirstCharge.visibility =
+ if (voive) View.VISIBLE else View.GONE
+ }
+
+ fun ivWheatFeeding(id: Int) {
+ mBinding!!.ivWheatFeeding.setImageResource(id)
+ }
+
+ fun setBoolean(voive: Boolean) {
+ this.aBoolean = voive
+ }
+
+ fun ivSoundEffects(voive: Boolean) {
+ mBinding!!.ivSoundEffects.visibility =
+ if (voive) View.VISIBLE else View.GONE
+ }
+
+ fun tvFirst(s: SpannableStringBuilder?) {
+ mBinding!!.tvFirst.text = s
+ }
+
+ fun setrlMic(voive: Boolean) {
+ mBinding!!.rlMic.visibility =
+ if (voive) View.VISIBLE else View.GONE
+ }
+
+ fun rlMore(voive: Boolean) {
+ mBinding!!.rlMore.visibility = if (voive) View.GONE else View.GONE
+ }
+
+ fun ivMic(inIvMic: Int) {
+ mBinding!!.ivMic.setImageResource(inIvMic)
+ }
+
+ fun setRoleType(roleType: Int, pit_number: Int) {
+ val rl_voice = mBinding!!.rlVoive // 注意:原拼写错误已修正
+ val rl_mic = mBinding!!.rlMic
+ val rl_more = mBinding!!.rlMore
+ val rl_misc = mBinding!!.rlMisc
+
+ // 默认隐藏所有按钮
+ rl_voice.visibility = View.GONE
+ rl_more.visibility = View.GONE
+ rl_misc.visibility = View.GONE
+ rl_mic.visibility = View.GONE
+
+ // 空指针保护
+ if (mRoomInfoResp == null || mRoomInfoResp!!.room_info == null || mRoomInfoResp!!.user_info == null) {
+ return
+ }
+
+ val typeId = mRoomInfoResp!!.room_info.type_id
+ val labelId = mRoomInfoResp!!.room_info.label_id
+ val userPitNumber = mRoomInfoResp!!.user_info.pit_number
+
+ // 特殊房间类型处理(优先级最高)
+ if ("6" == typeId) {
+ mBinding!!.rlMessage.visibility = View.GONE
+ return // 全部隐藏,无需继续处理
+ }
+
+
+ // 根据角色类型显示按钮
+ when (roleType) {
+ 1, 2, 3 -> {
+ rl_voice.visibility = View.VISIBLE
+ rl_mic.visibility =
+ if (pit_number != 0) View.VISIBLE else View.GONE
+ rl_more.visibility =
+ if (pit_number == 9) View.GONE else View.GONE
+ rl_misc.visibility = View.VISIBLE
+ }
+
+ 0 -> {
+ rl_voice.visibility = View.VISIBLE
+ rl_mic.visibility =
+ if (pit_number != 0) View.VISIBLE else View.GONE
+ rl_misc.visibility = View.VISIBLE
+ }
+
+ 5 -> {
+ rl_voice.visibility = View.VISIBLE
+ rl_more.visibility = View.GONE
+ rl_misc.visibility = View.VISIBLE
+ }
+
+ else -> {}
+ }
+ if (roleType != 5) {
+ rl_misc.visibility = View.VISIBLE
+ if (userPitNumber == 9) {
+ rl_more.visibility = View.GONE
+ }
+ }
+
+
+ // label_id 和 type_id 联合判断
+ if (mutableListOf("1", "3", "4", "8").contains(typeId) && "2" == labelId) {
+ rl_more.visibility = View.GONE
+ }
+
+ // mic 显示逻辑
+ if (userPitNumber > 0) {
+ rl_mic.visibility = View.VISIBLE
+ switchMic(2)
+ } else {
+ if (pit_number == 888) {
+ rl_mic.visibility = View.VISIBLE
+ switchMic(2)
+ } else if (pit_number == -1) {
+ rl_mic.visibility = View.VISIBLE // 原代码此处缺少 View. 前缀,已补全
+ switchMic(1)
+ } else {
+ rl_mic.visibility = View.GONE
+ switchMic(2)
+ }
+ }
+
+ if ("7" == typeId || "2" == typeId) {
+ rl_more.visibility = View.GONE
+ rl_misc.visibility = View.GONE
+ }
+ }
+
+ fun isMute(is_mute: Int) {
+ val text = text()
+ text.is_mute = is_mute
+ val s = GsonUtils.toJson(text)
+ val t = T()
+ t.fromUserInfo = SpUtil.getUserInfo()
+ t.text = s
+ val roomMessageEvent = RoomMessageEvent(126, mRoomInfoResp!!.room_info.room_id, t)
+ val json = GsonUtils.toJson(roomMessageEvent)
+ // 转换为 byte[]
+ val binaryData = json.toByteArray(StandardCharsets.UTF_8)
+ // 创建自定义消息
+ MessageListenerSingleton.getInstance().sendCustomRoomMessage(
+ roomId + "",
+ binaryData
+ )
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ fun roomInfoEvent(messageEvent: RoomWheatEvent) {
+ if (messageEvent.roomId == roomId) {
+ if (messageEvent.isOccupied) {
+ mBinding!!.ivWheatFeeding.setImageResource(com.xscm.moduleutil.R.mipmap.room_wheat_feeding_up)
+ aBoolean = false
+ } else {
+ mBinding!!.ivWheatFeeding.setImageResource(com.xscm.moduleutil.R.mipmap.room_wheat_feeding)
+ aBoolean = true
+ }
+ }
+ }
+
+ @SuppressLint("MissingInflatedId")
+ fun dialogDismiss(type:Int) {
+ // // 使用 AlertDialog.Builder
+
+ val builder = AlertDialog.Builder(this)
+ val inflater = layoutInflater
+ val dialogView =
+ inflater.inflate(com.xscm.moduleutil.R.layout.room_message_input_menu, null)
+ builder.setView(dialogView)
+ val etContent = dialogView.findViewById(com.xscm.moduleutil.R.id.et_content)
+ val tvSend = dialogView.findViewById