基线备份:thirdparty 变体完成 + Android 4.2 兼容改造前

包含内容:
- thirdparty flavor 独立 applicationId + Intent 入口(ThirdPartyEntryActivity)
- 58 后端接口适配(单 /api 路径、code 0/200、camelCase 字段、UNBOUND 轮询)
- MAC 硬件锚定校验(防一凭证多设备共用)
- EPG mac 覆盖 SDK init(sMacOverride)+ ChannelManger 初始化对齐
- 提示文案优化(设备未注册/权益未绑定)
This commit is contained in:
zhaogd
2026-08-25 11:16:40 +08:00
commit d11464d466
1287 changed files with 154945 additions and 0 deletions

View File

@@ -0,0 +1,194 @@
/**
* @file ActivitedDialog.java
* @description
* @author ainetv
* @date 2026-03-20
*/
package com.pukka.jiecheng.utils;
import android.app.Activity;
import android.app.Dialog;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import com.pukka.jiecheng.R;
import com.pukka.jiecheng.request.AsApiRequester;
public class ActivitedDialog extends Dialog {
private Activity activity;
private TextView tvActivatedValue;
private TextView tvTotalValue;
private TextView tvFailValue;
private TextView tvUnusedValue;
private View btnConfirm;
private OnConfirmListener listener;
private boolean isShowing = false;
public interface OnConfirmListener {
void onConfirm();
}
public void setOnConfirmListener(OnConfirmListener listener) {
this.listener = listener;
}
public ActivitedDialog(@NonNull Activity activity) {
super(activity, R.style.FullScreenDialog);
this.activity = activity;
initView();
}
public ActivitedDialog(@NonNull Activity activity, @StringRes int textResId) {
super(activity, R.style.FullScreenDialog);
this.activity = activity;
initView();
setText(activity.getString(textResId));
}
public ActivitedDialog(@NonNull Activity activity, String text) {
super(activity, R.style.FullScreenDialog);
this.activity = activity;
initView();
setText(text);
}
public ActivitedDialog(@NonNull Activity activity, AsApiRequester.CheckBenefitsResult result) {
super(activity, R.style.FullScreenDialog);
this.activity = activity;
initView();
setActivationData(result);
}
public void setText(String text) {
setActivationData(text);
}
public void setText(@StringRes int textResId) {
setText(activity.getString(textResId));
}
private void initView() {
setContentView(R.layout.dialog_activation_success);
setCancelable(false);
setCanceledOnTouchOutside(false);
Window window = getWindow();
if (window != null) {
window.setLayout(WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT);
window.setGravity(android.view.Gravity.CENTER);
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
}
tvActivatedValue = findViewById(R.id.tvActivatedValue);
tvTotalValue = findViewById(R.id.tvTotalValue);
tvFailValue = findViewById(R.id.tvFailValue);
tvUnusedValue = findViewById(R.id.tvUnusedValue);
btnConfirm = findViewById(R.id.btnConfirm);
btnConfirm.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) {
listener.onConfirm();
}
dismiss();
}
});
// Add support for DPAD center key / Enter key
btnConfirm.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN &&
(keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER)) {
if (listener != null) {
listener.onConfirm();
}
dismiss();
return true;
}
return false;
}
});
}
public void setActivationData(String data) {
tvActivatedValue.setText(resolveDisplayValue(data));
tvTotalValue.setText("--");
tvFailValue.setText("--");
tvUnusedValue.setText("--");
}
public void setActivationData(AsApiRequester.CheckBenefitsResult result) {
if (result == null) {
tvActivatedValue.setText("--");
tvTotalValue.setText("--");
tvFailValue.setText("--");
tvUnusedValue.setText("--");
return;
}
tvActivatedValue.setText(resolveDisplayValue(result.usedCardCount));
tvTotalValue.setText(resolveDisplayValue(result.totalCardCount));
tvFailValue.setText(resolveDisplayValue(result.failCardCount));
tvUnusedValue.setText(resolveDisplayValue(result.unusedCardCount));
}
private String resolveDisplayValue(String value) {
if (value == null) {
return "--";
}
String trimValue = value.trim();
return trimValue.isEmpty() ? "--" : trimValue;
}
@Override
public void show() {
if (isShowing) {
return;
}
isShowing = true;
super.show();
requestConfirmFocus();
}
@Override
public void dismiss() {
if (!isShowing) {
return;
}
isShowing = false;
super.dismiss();
}
@Override
public void onBackPressed() {
}
private void requestConfirmFocus() {
if (btnConfirm == null) {
return;
}
btnConfirm.setFocusable(true);
btnConfirm.setFocusableInTouchMode(true);
btnConfirm.requestFocus();
btnConfirm.post(new Runnable() {
@Override
public void run() {
if (!btnConfirm.hasFocus()) {
btnConfirm.requestFocus();
}
}
});
}
}

View File

@@ -0,0 +1,118 @@
package com.pukka.jiecheng.utils;
import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import androidx.annotation.NonNull;
import com.pukka.libcore.utils.LogUtils;
import java.util.Stack;
public class ActivityMonitor implements Application.ActivityLifecycleCallbacks {
public static ActivityMonitor mAppStatusManager;
private final Application application;
//是否前台
private boolean isForground;
//Activity运行个数
private int activeCount;
private static final String TAG = "ActivityManager";
//activity栈管理
private static Stack<Activity> activityStack;
//activity栈管理
private static Stack<Activity> saveStack;
// 详情页的activity栈管理
private static Stack<Activity> playBseStack;
//保存的播放详情页数量
private static final int MAX_ACTIVITY_DETAIL_NUM = 2;
private ActivityMonitor(Application application){
this.application = application;
activityStack = new Stack<>();
playBseStack = new Stack<>();
saveStack = new Stack<>();
application.registerActivityLifecycleCallbacks(this);
}
public static void init(Application application){
if(mAppStatusManager == null){
mAppStatusManager = new ActivityMonitor(application);
}
}
public static ActivityMonitor getInstance(){
return mAppStatusManager;
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
LogUtils.d("onActivityCreated -- activity : "+activity);
activityStack.add(activity);
LogUtils.d("onActivityCreated -- activityStack : "+activityStack);
saveStack.add(activity);
}
@Override
public void onActivityStarted(@NonNull Activity activity) {
LogUtils.d("onActivityStarted -- activity : "+activity);
}
@Override
public void onActivityResumed(@NonNull Activity activity) {
LogUtils.d("onActivityResumed -- activity : "+activity);
}
@Override
public void onActivityPaused(@NonNull Activity activity) {
LogUtils.d("onActivityPaused -- activity : "+activity);
}
@Override
public void onActivityStopped(Activity activity) {
LogUtils.d("onActivityStopped -- activity : "+activity);
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
LogUtils.d("onActivityDestroyed -- activity : "+activity);
activityStack.remove(activity);
LogUtils.d("onActivityDestroyed -- activityStack : "+activityStack);
saveStack.remove(activity);
}
public Activity getLastAct(){
return activityStack.lastElement();
}
public String getPreAct(){
LogUtils.d("getPreAct -- saveStack : "+saveStack);
if (!saveStack.isEmpty()){
int pos = saveStack.size()-2;
if (pos >= 0){
return saveStack.get(pos).getLocalClassName();
}
}
return "null";
}
/**
* 把传入的activity finish掉
* @param activity
*/
public void removeActivity(Activity activity){
if (activity != null && !activity.isFinishing() && !activity.isDestroyed()){
activity.finish();
}
}
}

View File

@@ -0,0 +1,74 @@
package com.pukka.jiecheng.utils;
import android.content.Context;
import android.text.TextUtils;
import com.pukka.jiecheng.BuildConfig;
import com.pukka.libcore.utils.LogUtils;
import com.pukka.libcore.utils.SharedPreferenceUtils;
import com.tencent.bugly.crashreport.CrashReport;
import java.util.concurrent.atomic.AtomicBoolean;
public final class BuglyInitManager {
private static final String USER_AGREEMENT_FILE = "userAgreementJc";
private static final String USER_AGREEMENT_KEY = "userAgreementJc";
private static final String BUGLY_DEBUG_FILE = "bugly_debug";
private static final String BUGLY_TEST_CRASH_KEY = "bugly_test_crash_once";
private static final AtomicBoolean INITIALIZED = new AtomicBoolean(false);
private BuglyInitManager() {
}
public static void initIfPrivacyAgreed(Context context) {
if (!isPrivacyAgreed()) {
return;
}
init(context);
}
public static boolean isPrivacyAgreed() {
return "1".equals(SharedPreferenceUtils.getString(USER_AGREEMENT_FILE, USER_AGREEMENT_KEY, "0"));
}
public static void init(Context context) {
if (context == null) {
return;
}
if (INITIALIZED.get()) {
return;
}
String appId = BuildConfig.BUGLY_APP_ID;
if (TextUtils.isEmpty(appId)) {
LogUtils.e("Bugly init skipped: BUGLY_APP_ID is empty");
return;
}
if (INITIALIZED.compareAndSet(false, true)) {
CrashReport.UserStrategy strategy = new CrashReport.UserStrategy(context.getApplicationContext());
strategy.setAppChannel(getChannelForReport());
CrashReport.initCrashReport(context.getApplicationContext(), appId, BuildConfig.DEBUG, strategy);
}
}
public static void triggerTestExceptionOnceIfInDebug(Context context) {
// if (context == null || !BuildConfig.DEBUG) {
// return;
// }
// if (!isPrivacyAgreed()) {
// return;
// }
// init(context);
// if (SharedPreferenceUtils.getBoolean(BUGLY_DEBUG_FILE, BUGLY_TEST_CRASH_KEY, false)) {
// return;
// }
// SharedPreferenceUtils.put(BUGLY_DEBUG_FILE, BUGLY_TEST_CRASH_KEY, true);
CrashReport.testJavaCrash();
}
private static String getChannelForReport() {
// if (!TextUtils.isEmpty(BuildConfig.CHANNEL_ID)) {
// return BuildConfig.CHANNEL_ID;
// }
return BuildConfig.FLAVOR;
}
}

View File

@@ -0,0 +1,22 @@
package com.pukka.jiecheng.utils;
import android.content.Context;
import com.pukka.jiecheng.BuildConfig;
import com.pukka.libcore.utils.LogUtils;
public class BuildConfigUtils {
public static void initBuildConfig(Context context){
com.pukka.libcore.base.BuildConfig.FLAVOR = BuildConfig.FLAVOR;
com.pukka.libcore.base.BuildConfig.BUILD_TYPE = BuildConfig.BUILD_TYPE;
com.pukka.libcore.base.BuildConfig.VERSION_NAME = BuildConfig.VERSION_NAME;
com.pukka.libcore.base.BuildConfig.VERSION_CODE = BuildConfig.VERSION_CODE;
LogUtils.d("MainActivity -- FLAVOR : " + com.pukka.libcore.base.BuildConfig.FLAVOR);
LogUtils.d("MainActivity -- VERSION_CODE : " + com.pukka.libcore.base.BuildConfig.VERSION_CODE);
LogUtils.d("MainActivity -- VERSION_NAME : " + com.pukka.libcore.base.BuildConfig.VERSION_NAME);
LogUtils.d("MainActivity -- BUILD_TYPE : " + com.pukka.libcore.base.BuildConfig.BUILD_TYPE);
}
}

View File

@@ -0,0 +1,359 @@
package com.pukka.jiecheng.utils;
import static com.pukka.libcore.base.BaseConst.serviceComboCode;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.migu.libnetwork.HttpRequestCallback;
import com.migu.libnetwork.HttpUtils;
import com.pukka.jiecheng.JcConfig;
import com.pukka.jiecheng.login.bean.GetCloudLoginBean;
import com.pukka.jiecheng.login.bean.GetQueryUsableBean;
import com.pukka.jiecheng.login.bean.QueryVipInfoBean;
import com.pukka.jiecheng.order.bean.ActivateBean;
import com.pukka.libcore.base.BaseConst;
import com.pukka.libcore.base.BuildConfig;
import com.pukka.libcore.boxShared.BoxSharedParameters;
import com.pukka.libcore.boxShared.BoxSharedParamsUtils;
import com.pukka.libcore.boxShared.MacUtils;
import com.pukka.libcore.utils.ContextUtils;
import com.pukka.libcore.utils.DateUtils;
import com.pukka.libcore.utils.JsonUtils;
import com.pukka.libcore.utils.LogUtils;
import com.pukka.libcore.utils.MD5Utils;
import com.pukka.libcore.utils.SharedPreferenceUtils;
import com.pukka.libcore.utils.SpTimeUtils;
import com.pukka.libcore.utils.StringUtils;
import java.util.HashMap;
import java.util.TreeMap;
import java.util.UUID;
public class ChannelManger {
public static final String TAG = "ChannelManger";
public static String publicKeyStr_could = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoDFiT8rDa6ftkRCxO8GctdhOYsEcmyrwyy0IYZKperNNukZnRYG0ucmmZ1C+9NWvVdmzoV1ikl5gBjdWlmnBzRZxtlaUaygm9/TrFDzOHzNLrkLtz1HSuSxNVMoOIaaSOdBcGtehK220td4U1jJFsyGwX/nsNhrlZ7Nhg3iFsrhAJ/9V7FLSJSc4W7XEKA36L5MBj/urCZNVbK7VIlfTfZy5W0CA8IwzEOPClkzjDG6H/s2zW/acxkyWBcOOBtNuNPUtQrmHKB9HJc0oxlbEVxW9UZ0rEWyFm/JN1z2ItMw+8upmFKbCGTXW6/1kzRs9Nsgc3zAqUzTMvT9qlDoeqwIDAQAB";
public static String publicKeyStr_Hotel = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs9MGm6Wz0Z9GuImV1drFJNVOJ6F9Pno/qqdj2q5RqFb/1AxrkaiCKjaeCYZmhSR3F9BqO8nC2L2JGqYn17bTAzLcti3gcoSyy7BVt+nq0pDQsoQ/2CloMY2okMatcfENsUGz1IhrAwUxUfQcdk9lS8T5bKshxXnZKKXhLBEh3xsOMBZNhWssGadj8gtmuW8CHEXyi/04Php8erpOjpba4zgAwCRzMEC54fcSraD+h81U6S/jkuZSvhGVVl0AYLHSJIyTkfOjYxbjDZh4XH/Wco/p0zeQH4a3gP+bTj8bsDZIjfeprLHnwshEdNfUx2HP8++zkJLO2VT/NM2C/ILqWwIDAQAB";
public static String publicKeyStr_Hotel_release = "";
public static String publicKeyStr_could_release = "";
public static void initChannel() {
LogUtils.d(TAG, "initChannel:" + BuildConfig.FLAVOR);
LogUtils.d(TAG, "initChannel:isJcHotel");
// if (BuildConfig.BUILD_TYPE.contains("release")) {
// JetsenxSdkUtil.ACCESSER = "JSTASJD";
// JetsenxSdkUtil.SECRET = "kMeWsBu0QsTXpnft";
// JetsenxSdkUtil.AGCODE = "1100101010200492358120169";
// BaseConst.agentVendorCode = JetsenxSdkUtil.AGCODE;
// BaseConst.serviceComboCode = "1100121010200494755850172";
// BaseConst.serviceGroupCode = "1100122010201752209710182";
// BaseConst.businessLine = "4100450011381755998420012";
// publicKeyStr_Hotel_release = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtlfaNsJwQ8wuVdITMLJPrXMeD9idJ0aTziOgkZZhigwzhyPNDAqw+nCKeJGGoTJyhbjPXKtnyI8eNpj+xhCCMXUD3BxOVMFWBhFxzIl83IQJG43mPRXwsJrI4oCYDaKv5aeZZBqF0OLPglXU+q19/28ltONvxcKp8YxXagmixzz1SDgLITM3s3ExqiaScK1dcpPtXbCpI4GXPP9l0ekkCBAo/d1oQqbxHvdE5ufAhEZ042k5tVHUgX9cUoedA9F2JcPhc+zUNCJUAQLvfVXxqNdhwxA4TY+64OX2iSje/cTqxp+4RcrX4rbUIYZitVQCRexZjKz7Yd/KXp8CKCho6wIDAQAB";
// } else {
// JetsenxSdkUtil.ACCESSER = "ASJDTEST";
// JetsenxSdkUtil.SECRET = "ykgEDrYccunGtb0x";
// JetsenxSdkUtil.AGCODE = "1100101010200492358120169";
// BaseConst.agentVendorCode = JetsenxSdkUtil.AGCODE;
// BaseConst.serviceComboCode = "1100121010200492753300170";
// BaseConst.serviceGroupCode = "1100122010200493334770171";
// BaseConst.businessLine = "4100450011967708592750001";
// publicKeyStr_Hotel = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnemyIjwf1S5PsqgPhwiPGcF2Efa4UvFiFpg34+d2ZHQ6mkTTaussXCWQQRZGUfGbvkhIJOspGxmxtaMYgNxMQv9BT5B6xcnMILqYzG40hc2N3EiLm4hDYP12f8pshikh5aNxolbgkdA+Y2vrMf2KNucArMh8gT1mUuQRyUMiefy+N/kk2nK2p6SSX0628/BtczNgj13MaMHMlr8krNTlzB/22M8WMSk10nFS4ULp1KOPia8VxqzMIFKeREPIHIUnarz+llFIj6zKGg0ocqS6KEml7jv1yCgBpmyA2kAGEgnuTreO/5h5DqXByKLBEW8bYDiMUb7H/58IRerrITXfVQIDAQAB";
//
// }
JetsenxSdkUtil.ACCESSER = "JSTASJD";
JetsenxSdkUtil.SECRET = "kMeWsBu0QsTXpnft";
JetsenxSdkUtil.AGCODE = "1100101010200492358120169";
BaseConst.agentVendorCode = JetsenxSdkUtil.AGCODE;
BaseConst.serviceComboCode = "1100121010200494755850172";
BaseConst.serviceGroupCode = "1100122010201752209710182";
BaseConst.businessLine = "4100450011381755998420012";
publicKeyStr_Hotel_release = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtlfaNsJwQ8wuVdITMLJPrXMeD9idJ0aTziOgkZZhigwzhyPNDAqw+nCKeJGGoTJyhbjPXKtnyI8eNpj+xhCCMXUD3BxOVMFWBhFxzIl83IQJG43mPRXwsJrI4oCYDaKv5aeZZBqF0OLPglXU+q19/28ltONvxcKp8YxXagmixzz1SDgLITM3s3ExqiaScK1dcpPtXbCpI4GXPP9l0ekkCBAo/d1oQqbxHvdE5ufAhEZ042k5tVHUgX9cUoedA9F2JcPhc+zUNCJUAQLvfVXxqNdhwxA4TY+64OX2iSje/cTqxp+4RcrX4rbUIYZitVQCRexZjKz7Yd/KXp8CKCho6wIDAQAB";
BoxSharedParameters.getInstance().setNewTvPass(true);
}
public static void loginAuto(String uid, OnCallBack onCallBack) {
String baseHost = JcConfig.getBaseUrl();
String host = baseHost + "ums_api/api/v1/third/login";
String timestamp = String.valueOf(System.currentTimeMillis());
String accesser = JetsenxSdkUtil.ACCESSER;
String businessLine = BaseConst.businessLine;
String serviceComboCode = BaseConst.serviceComboCode;
String nonce = UUID.randomUUID().toString().replace("-", "");
TreeMap<String, String> params = new TreeMap<>();
params.put("timestamp", timestamp);
params.put("nonce", nonce);
params.put("serviceComboCode", serviceComboCode);
String url = UrlParamBuilder.buildUrl(host, params);
HashMap<String, String> signMap = new HashMap<>();
signMap.put("accesser", accesser);
signMap.put("businessLine", businessLine);
signMap.put("uid", uid); //此处需要传递安柠的用户的唯一标识
signMap.put("mac", MacUtils.getMac(ContextUtils.getApplicationContext())); //安柠设备mac
signMap.put("nonce", nonce); //注意:此处的nonce,需要和接口的url参数相同
signMap.put("timestamp", timestamp); //注意:此处的时间戳,需要和接口的url参数相同
signMap.put("serviceComboCode", serviceComboCode);
// 使用 Gson 转为 JSON 字符串 注意:若无gson依赖,改为其它任意安全的json转化字符串方法均可
Gson gson = new Gson();
String encryptData = gson.toJson(signMap);
LogUtils.d(TAG, "待加密字符串为:" + encryptData);
//公钥字符串,联系华视开发获取
String keyStr = "";
if (BuildConfig.BUILD_TYPE.contains("release")) {
keyStr = publicKeyStr_Hotel_release;
} else {
keyStr = publicKeyStr_Hotel;
}
String encrypt = RsaSignUtil.encrypt(keyStr, encryptData);
HttpUtils.getInstance().executePostCloudLogin(url, new TreeMap(), JetsenxSdkUtil.TENANT, encrypt, new HttpRequestCallback() {
@Override
public void onRequestNetFail(int type) {
onCallBack.over(false);
}
@Override
public void onRequestSuccess(String result, int type) {
LogUtils.d(TAG, "loginCloud:" + result);
try {
//test
//result="{\"retCode\":\"E0000000\",\"retMsg\":\"登录失败\",\"retInfo\":null}";
GetCloudLoginBean bean = new Gson().fromJson(result, GetCloudLoginBean.class);
String retCode = bean.getRetCode();
if (!StringUtils.isEmpty(retCode) && retCode.equals("00000000")) {
String phone = bean.getRetInfo().getExternalId();
String token = bean.getRetInfo().getToken();
JetsenxSdkUtil.init(ContextUtils.getApplicationContext(), JetsenxSdkUtil.AGCODE, MacUtils.getMac(ContextUtils.getApplicationContext()), phone, JetsenxSdkUtil.TENANT, JetsenxSdkUtil.SECRET, JetsenxSdkUtil.ACCESSER, "1", "");
long time = System.currentTimeMillis();
SpTimeUtils.getInstance(ContextUtils.getContext()).setString("phoneMember2", phone + "@" + time, 6 * SpTimeUtils.TIME_DAY);
SharedPreferenceUtils.put("phoneToken", token);
BoxSharedParameters.getInstance().setUserId(phone);
BoxSharedParameters.getInstance().setToken(token);
LogUtils.d(TAG, "登录成功");
onCallBack.over(true);
} else {
LogUtils.d(TAG, "登录失败");
onCallBack.over(false);
}
} catch (Exception e) {
LogUtils.d(TAG, "登录Exception:" + e);
onCallBack.over(false);
throw new RuntimeException(e);
}
}
@Override
public void onRequestFail(String value, String failCode, int type) {
LogUtils.d(TAG, "onRequestFail:" + value);
onCallBack.over(false);
}
});
}
public static void queryUsable(OnDeviceNoActCallBack onDeviceNoActCallBack) {
LogUtils.d(TAG, "queryUsable:");
String baseHost = JcConfig.getBaseUrl();
String host = baseHost + "ccms_api/hswj/api/card/query-usable?serviceComboCode=" + BaseConst.serviceComboCode;
HttpUtils.getInstance().mHttpGetUserInfo(host, BoxSharedParameters.getInstance().getToken(), JcConfig.header, new HttpRequestCallback() {
@Override
public void onRequestNetFail(int type) {
onDeviceNoActCallBack.goHome();
}
@Override
public void onRequestSuccess(String result, int type) {
LogUtils.d(TAG, "queryUsable onRequestSuccess:" + result);
try {
if (!StringUtils.isEmpty(result)) {
GetQueryUsableBean bean = new Gson().fromJson(result, GetQueryUsableBean.class);
if (bean.getRetInfo() != null) {
if (!bean.getRetInfo().getCards().isEmpty()) {
String str = bean.getRetInfo().getCards().get(0).getActivateCode();
activate(str, onDeviceNoActCallBack);
return;
}
}
}
LogUtils.d("当前用户无可激活卡密,获取会员到期时间");
//如果用户没注册过,返回当前用户无可激活卡密;如果用户已经激活过卡,已经是会员,也会返回当前用户无可激活卡密
queryVipInfo(onDeviceNoActCallBack);
} catch (Exception e) {
e.printStackTrace();
onDeviceNoActCallBack.goHome();
}
}
@Override
public void onRequestFail(String value, String failCode, int type) {
onDeviceNoActCallBack.goHome();
}
});
}
/**
* 卡密激活接口
**/
private static void activate(String activateCode, OnDeviceNoActCallBack onDeviceNoActCallBack) {
LogUtils.d(TAG, "activate:");
try {
String token = BoxSharedParameters.getInstance().getToken();
LogUtils.d("token:" + token);
if (StringUtils.isEmpty(token) || StringUtils.isEmpty(activateCode)) {
return;
}
String memberCode = BaseConst.getMemberCode(token);
LogUtils.d("memberCode:" + memberCode);
long timestamp = System.currentTimeMillis() / 1000;
LogUtils.d("timestamp:" + timestamp);
String oldSign = "activateCode=" + activateCode + "&memberCode=" + memberCode + "&timestamp=" + timestamp;
LogUtils.d("oldSign:" + oldSign);
String stringSignTemp = oldSign + "&key=" + JcConfig.key;
LogUtils.d("stringSignTemp:" + stringSignTemp);
String sign = MD5Utils.getMD5(stringSignTemp).toUpperCase();
LogUtils.d("sign1:" + sign);
String url = JcConfig.ACTIVATE + "sign=" + sign;
LogUtils.d("url:" + url);
JsonObject content = new JsonObject();
content.addProperty("activateCode", activateCode);
content.addProperty("memberCode", memberCode);
content.addProperty("timestamp", timestamp);
content.addProperty("serviceComboCode", serviceComboCode);
String strContent = content.toString();
LogUtils.d("strContent:" + strContent);
HttpUtils.getInstance().mHttpPostActivate(url, strContent, JcConfig.header, new HttpRequestCallback() {
@Override
public void onRequestNetFail(int type) {
LogUtils.d(TAG, "mHttpPostActivate onRequestNetFail()");
onDeviceNoActCallBack.goHome();
}
@Override
public void onRequestSuccess(String result, int type) {
LogUtils.d(TAG, "mHttpPostActivate onRequestSuccess:" + result);
try {
if (!StringUtils.isEmpty(result)) {
ActivateBean activateBean = JsonUtils.json2Object(result, ActivateBean.class);
String retCode = activateBean.getRetCode();
if (!StringUtils.isEmpty(retCode) && !retCode.equals("00000000")) {
LogUtils.d("激活失败");
onDeviceNoActCallBack.errorMessage("403");
return;
}
}
onDeviceNoActCallBack.goHome();
} catch (Exception ex) {
ex.printStackTrace();
onDeviceNoActCallBack.goHome();
}
}
@Override
public void onRequestFail(String value, String failCode, int type) {
LogUtils.d(TAG, "mHttpPostActivate onRequestFail()");
onDeviceNoActCallBack.goHome();
}
});
} catch (Exception ex) {
ex.printStackTrace();
onDeviceNoActCallBack.goHome();
}
}
/**
* 获取会员到期时间
* 本接口有签名校验功能,sign=Md5(businessLine + requestTime + uid + key),其中key固定为
* c25svtwbTyCE
* <p>
* {"retCode":"00000000","retMsg":"操作成功","retInfo":{"vipExpireTime":null,"isVip":false}}
*/
private static void queryVipInfo(OnDeviceNoActCallBack onDeviceNoActCallBack) {
String requestTime = System.currentTimeMillis() + "";
String uid = MacUtils.getMac(ContextUtils.getApplicationContext());
String preSign = BaseConst.businessLine + requestTime + uid + "c25svtwbTyCE";
LogUtils.d("preSign=" + preSign);
String sign = MD5Utils.getMD5(preSign);
LogUtils.d("sign=" + sign);
String url = JcConfig.QUERYVIPINFO + "requestTime=" + requestTime + "&uid=" + uid + "&businessLine=" + BaseConst.businessLine + "&sign=" + sign;
HttpUtils.getInstance().mHttpPostQueryVipInfo(url, JcConfig.header, new HttpRequestCallback() {
@Override
public void onRequestNetFail(int type) {
LogUtils.d(TAG, "mHttpPostQueryVipInfo onRequestNetFail()");
onDeviceNoActCallBack.goHome();
}
@Override
public void onRequestSuccess(String result, int type) {
LogUtils.d(TAG, "mHttpPostQueryVipInfo onRequestSuccess:" + result);
try {
QueryVipInfoBean bean = new Gson().fromJson(result, QueryVipInfoBean.class);
String retCode = bean.getRetCode();
if (!StringUtils.isEmpty(retCode) && retCode.equals("00000000")) {
boolean isVip = bean.getRetInfo().isIsVip();
if (isVip) {
//{"retCode":"00000000","retMsg":"操作成功","retInfo":{"vipExpireTime":"2026-02-24 11:22:39","isVip":true}}
String vipExpireTime = bean.getRetInfo().getVipExpireTime();
LogUtils.d("Vip到期日期:" + vipExpireTime);
int temp = DateUtils.compareDate(DateUtils.getNowDate(), DateUtils.convertToDate(vipExpireTime, "yyyy-MM-dd HH:mm:ss"));
if (temp <= 0) {
LogUtils.d("VIP没到期,跳转到首页");
onDeviceNoActCallBack.goHome();
} else {
LogUtils.d("VIP到期,显示错误码402");
onDeviceNoActCallBack.errorMessage("402");
}
} else {
LogUtils.d("不是vip,显示错误码402");
onDeviceNoActCallBack.errorMessage("402");
}
} else {
//可能该mac在其他环境上注册过导致请求参数异常
LogUtils.d("请求参数异常,显示错误码403");
onDeviceNoActCallBack.errorMessage("403");
}
} catch (Exception ex) {
ex.printStackTrace();
onDeviceNoActCallBack.goHome();
}
}
@Override
public void onRequestFail(String value, String failCode, int type) {
LogUtils.d(TAG, "mHttpPostActivate onRequestFail()");
onDeviceNoActCallBack.goHome();
}
});
}
public interface OnCallBack {
void over(boolean isLogin);
}
public interface OnDeviceNoActCallBack {
void errorMessage(String message);
void goHome();
}
/**
* 是否是安柠酒店
*/
public static boolean isAnNingHotel() {
return BoxSharedParamsUtils.isJcHotel() && "ANNING".equals(com.pukka.jiecheng.BuildConfig.HOTEL_TYPE);
}
/**
* 是否需要去掉首页我的页面
* */
public static boolean isNeedMy(){
return com.pukka.jiecheng.BuildConfig.NEED_MY;
}
}

View File

@@ -0,0 +1,30 @@
package com.pukka.jiecheng.utils;
import android.view.KeyEvent;
import com.pukka.libcore.boxShared.BoxSharedParamsUtils;
import com.pukka.libcore.utils.LogUtils;
/**
* 云主机版本
* 遥控器按键适配
* */
public class CloudKeyCodeUtils {
/**
* 遥控器按键适配 返回和确认按键键值转换
* */
public static int changeToKeyCode(int keyCode){
if(BoxSharedParamsUtils.isJcCloud() || BoxSharedParamsUtils.isJcHotel()){
//遥控器返回键是97
if(keyCode == 97){
LogUtils.d("遥控器返回按键适配");
keyCode = KeyEvent.KEYCODE_BACK;
}
if(keyCode == 96){
LogUtils.d("遥控器OK按键适配");
keyCode = KeyEvent.KEYCODE_DPAD_CENTER;
}
}
return keyCode;
}
}

View File

@@ -0,0 +1,16 @@
package com.pukka.jiecheng.utils;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import androidx.annotation.ColorInt;
public class ColorSpanUtils {
public static SpannableString textColorChanges(String fullText, String colorText, @ColorInt int color) {
SpannableString spannableString = new SpannableString(fullText);
ForegroundColorSpan colorSpan = new ForegroundColorSpan(color);
spannableString.setSpan(colorSpan, fullText.indexOf(colorText), fullText.indexOf(colorText) + colorText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableString;
}
}

View File

@@ -0,0 +1,61 @@
package com.pukka.jiecheng.utils;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.text.TextUtils;
import android.view.View;
import androidx.core.graphics.ColorUtils;
/**
* [颜色工具类]<BR>
*
* @author wcg
* @version [V0.1.0.1, 2023/11/27 0027]
* @since V0.1.0.1
*/
public class ColorUtil {
private static final String START_STR = "#";
private static final int ALPHA_MAX = 100;
/**
* 设置shape color
* strokeWidth 边框宽度
* strokeColor 边框颜色
*/
public static void setBackgroundShapeColor(View view, String strColor, int strokeWidth) {
if (view == null || TextUtils.isEmpty(strColor)) {
return;
}
try {
GradientDrawable drawable = (GradientDrawable) view.getBackground();
drawable.setColor(ColorUtils.setAlphaComponent(Color.parseColor(strColor), getAlphaValue(10)));
if (strokeWidth > 0 && strColor.startsWith(START_STR)) {
drawable.setStroke(strokeWidth,
ColorUtils.setAlphaComponent(Color.parseColor(strColor), getAlphaValue(50)));
}
view.setBackground(drawable);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取透明度值 十进制
*
* @param alphaPercent 不透明度百分比
* @return 0~255
*/
public static int getAlphaValue(int alphaPercent) {
if (alphaPercent >= 0 && alphaPercent <= ALPHA_MAX) {
float temp = 255 * alphaPercent * 1.0f / 100f;
// 四舍五入
return Math.round(temp);
} else {
return 0;
}
}
}

View File

@@ -0,0 +1,146 @@
/**
* @file CommonLoadingDialog.java
* @description 通用Loading弹框,支持文案配置,不可取消
* @author ainetv
* @date 2026-03-20
*/
package com.pukka.jiecheng.utils;
import android.app.Activity;
import android.app.Dialog;
import android.content.DialogInterface;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.SystemClock;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.pukka.jiecheng.R;
public class CommonLoadingDialog extends Dialog {
public enum Mode {
FULLSCREEN,
ACTIVATION_SMALL
}
private static final long SHOW_DISMISS_INTERVAL_MS = 120L;
private Activity activity;
private ImageView idIvLoading;
private TextView idTvText;
private ConstraintLayout clContent;
private final Mode mode;
private long lastShowTimestamp;
private long lastDismissTimestamp;
public CommonLoadingDialog(@NonNull Activity activity) {
this(activity, Mode.FULLSCREEN);
}
public CommonLoadingDialog(@NonNull Activity activity, Mode mode) {
super(activity, mode == Mode.ACTIVATION_SMALL ? R.style.ActivationLoadingDialog : R.style.FullScreenDialog);
this.activity = activity;
this.mode = mode;
initView();
}
public CommonLoadingDialog(@NonNull Activity activity, @StringRes int textResId) {
this(activity, Mode.FULLSCREEN);
setText(textResId);
}
public CommonLoadingDialog(@NonNull Activity activity, String text) {
this(activity, Mode.FULLSCREEN);
setText(text);
}
private void initView() {
int layoutRes = mode == Mode.ACTIVATION_SMALL
? R.layout.layout_activation_loading_small
: R.layout.layout_common_loading;
View view = View.inflate(activity, layoutRes, null);
idIvLoading = view.findViewById(R.id.id_iv_loading);
idTvText = view.findViewById(R.id.id_tv_text);
clContent = view.findViewById(R.id.cl_content);
Animation animation = AnimationUtils.loadAnimation(activity, R.anim.rotate_anim);
idIvLoading.startAnimation(animation);
setContentView(view);
Window window = getWindow();
if (window != null) {
if (mode == Mode.ACTIVATION_SMALL) {
window.setLayout(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT);
} else {
window.setLayout(WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT);
}
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
window.getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
window.setDimAmount(0f);
}
setCancelable(false);
setCanceledOnTouchOutside(false);
getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
setOnKeyListener(new OnKeyListener() {
@Override
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
return keyCode == KeyEvent.KEYCODE_BACK;
}
});
}
public void setText(@StringRes int textResId) {
if (idTvText != null) {
idTvText.setText(textResId);
}
}
public void setText(String text) {
if (idTvText != null) {
idTvText.setText(text);
}
}
@Override
public void show() {
if (activity == null || activity.isFinishing() || activity.isDestroyed()) {
return;
}
if (super.isShowing()) {
return;
}
long now = SystemClock.elapsedRealtime();
if (now - lastDismissTimestamp < SHOW_DISMISS_INTERVAL_MS) {
return;
}
super.show();
lastShowTimestamp = now;
}
@Override
public void dismiss() {
if (!super.isShowing()) {
return;
}
long now = SystemClock.elapsedRealtime();
if (now - lastShowTimestamp < SHOW_DISMISS_INTERVAL_MS) {
return;
}
super.dismiss();
lastDismissTimestamp = now;
}
}

View File

@@ -0,0 +1,206 @@
package com.pukka.jiecheng.utils;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;
import androidx.annotation.NonNull;
import com.pukka.jiecheng.MainActivity;
import com.pukka.libcore.utils.AppManager;
import com.pukka.libcore.utils.DateUtils;
import com.pukka.libcore.utils.LogUtils;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
* Author: wxf
* Date: 2023/4/25 15:44
* Description:全局异常捕获
*/
public class CrashHandler implements Thread.UncaughtExceptionHandler {
private static final String TAG = CrashHandler.class.getSimpleName();
/**
* 程序Context对象
*/
private Context context;
/**
* 系统默认的UncaughtException处理类
*/
private Thread.UncaughtExceptionHandler defalutHandler;
/**
* 用来存储设备信息和异常信息
*/
private final Map<String, String> infos = new HashMap<String, String>();
private CrashHandler() {
}
private static final class InstanceHolder {
@SuppressLint("StaticFieldLeak")
static final CrashHandler INSTANCE = new CrashHandler(); // 单例模式
}
/**
* 获取CrashHandler实例
*
* @return CrashHandler
*/
public static CrashHandler getInstance() {
return InstanceHolder.INSTANCE;
}
/**
* 异常处理初始化
*
* @param context
*/
public void init(Context context) {
this.context = context;
// 获取系统默认的UncaughtException处理器
defalutHandler = Thread.getDefaultUncaughtExceptionHandler();
// 设置该CrashHandler为程序的默认处理器
Thread.setDefaultUncaughtExceptionHandler(this);
}
/**
* 当UncaughtException发生时会转入该函数来处理
*/
@Override
public void uncaughtException(@NonNull Thread thread, @NonNull Throwable ex) {
Log.i(TAG, "uncaughtException=====" + "crash UncaughtExceptionHandler ");
// 自定义错误处理
boolean res = handleException(ex);
if (!res && defalutHandler != null) {
// 如果用户没有处理则让系统默认的异常处理器来处理
defalutHandler.uncaughtException(thread, ex);
} else {
Log.i(TAG, "uncaughtException=====" + "kill app");
Activity activity = AppManager.getAppManager().currentActivity();
if (activity instanceof MainActivity) {
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(1);
} else {
Intent intent = new Intent(context, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(1);
}
}
}
/**
* 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
*
* @param ex
* @return true:如果处理了该异常信息;否则返回false.
*/
private boolean handleException(final Throwable ex) {
LogUtils.e("handleException=====" + "自定义错误处理,收集错误信息 发送错误报告等操作均在此完成");
//MGJSEPG-12834
if (ex == null || ex.getMessage() == null) {
return false;
}
LogUtils.d("uncaughtException:" + ex.getMessage());
Log.e(TAG, "uncaughtException:\n", ex);
// 收集设备参数信息 \日志信息
collectDeviceInfo(context);
// 保存日志文件
saveCrashInfo2File(context, ex);
return true;
}
/**
* 保存错误信息到文件中
*
* @param ex
*/
private void saveCrashInfo2File(Context context, Throwable ex) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : infos.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
sb.append(key).append("=").append(value).append("\n");
}
Writer writer = new StringWriter();
PrintWriter printWriter = new PrintWriter(writer);
ex.printStackTrace(printWriter);
Throwable cause = ex.getCause();
while (cause != null) {
cause.printStackTrace(printWriter);
cause = cause.getCause();
}
printWriter.close();
String result = writer.toString();
sb.append(result);
try {
long timestamp = System.currentTimeMillis();
String time = DateUtils.getStringNowDate();
String fileName = "crash-" + time + "-" + timestamp + ".log";
File dir = new File(context.getFilesDir() + "/crash");
if (!dir.exists()) {
dir.mkdirs();
}
OutputStreamWriter write =
new OutputStreamWriter(new FileOutputStream(dir.getAbsolutePath() + fileName), "UTF-8");
BufferedWriter fileWrite = new BufferedWriter(write);
fileWrite.write(sb.toString());
fileWrite.close();
} catch (Exception e) {
Log.e(TAG, "an error occured while writing file...", e);
}
}
/**
* 收集设备参数信息
*
* @param ctx
*/
public void collectDeviceInfo(Context ctx) {
try {
PackageManager pm = ctx.getPackageManager();
PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
if (pi != null) {
String versionName = pi.versionName == null ? "null" : pi.versionName;
String versionCode = pi.versionCode + "";
infos.put("versionName", versionName);
infos.put("versionCode", versionCode);
}
} catch (PackageManager.NameNotFoundException e) {
LogUtils.e("an error occured when collect package info");
}
Field[] fields = Build.class.getDeclaredFields();
for (Field field : fields) {
try {
field.setAccessible(true);
infos.put(field.getName(), field.get(null).toString());
LogUtils.e(field.getName() + " : " + field.get(null));
} catch (Exception e) {
LogUtils.e("an error occured when collect crash info");
}
}
}
}

View File

@@ -0,0 +1,96 @@
package com.pukka.jiecheng.utils;
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import java.util.Iterator;
import java.util.Set;
import org.json.JSONException;
public class DataValueUtils {
private static final String TAG = "DataValueUtils";
public DataValueUtils() {
}
public static String getDataValues(String dataAll, String... valueKeys) {
String value = dataAll;
try {
for(int i = 0; i < valueKeys.length; ++i) {
JSONObject jsonObject = JSON.parseObject(value);
value = jsonObject.getString(valueKeys[i]);
if (i == valueKeys.length - 1) {
return value;
}
}
} catch (Exception var5) {
}
return "";
}
public static String setDataValue(String dataAll, String keys, String values) {
try {
JSONObject json = null;
json = JSONObject.parseObject(dataAll);
Set<String> keySet = json.keySet();
Iterator var5 = keySet.iterator();
while(true) {
while(var5.hasNext()) {
String key = (String)var5.next();
Object obj = json.get(key);
if (obj instanceof JSONArray) {
JSONArray arr = (JSONArray)obj;
for(int i = 0; i < arr.size(); ++i) {
String child = setDataValue(arr.get(i).toString(), keys, values);
arr.set(i, JSONObject.parse(child));
}
json.put(key, arr);
} else if (obj instanceof JSONObject) {
JSONObject sub = (JSONObject)obj;
String substr = setDataValue(sub.toJSONString(), keys, values);
json.put(key, JSONObject.parse(substr));
} else if (key.equals(keys)) {
json.put(key, values);
}
}
return json.toJSONString();
}
} catch (Exception var11) {
return "";
}
}
public static org.json.JSONObject toJSONObject(String dataAll) {
org.json.JSONObject jsonObject = new org.json.JSONObject();
try {
JSONObject json = null;
json = JSONObject.parseObject(dataAll);
Set<String> keySet = json.keySet();
Iterator var4 = keySet.iterator();
while(var4.hasNext()) {
String key = (String)var4.next();
Object obj = json.get(key);
jsonObject.put(key, obj);
}
return jsonObject;
} catch (JSONException var7) {
var7.printStackTrace();
return jsonObject;
}
}
}

View File

@@ -0,0 +1,47 @@
package com.pukka.jiecheng.utils;
/**
* @Auther: sunmm
* @datetime: 2025/4/17 9:46
* @desc:
*/
import android.graphics.Rect;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
private final int spanCount;
private final int spacing;
private final boolean includeEdge;
public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) {
this.spanCount = spanCount;
this.spacing = spacing;
this.includeEdge = includeEdge;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view);
int column = position % spanCount;
int currentRow = position / spanCount;
if (includeEdge) {
outRect.left = spacing - column * spacing / spanCount;
outRect.right = (column + 1) * spacing / spanCount;
if (position < spanCount) outRect.top = spacing;
outRect.bottom = spacing;
} else {
outRect.left = column * spacing / spanCount;
outRect.right = spacing - (column + 1) * spacing / spanCount;
// 行间距处理
if (currentRow > 0) {
// 第二行 - 使用边缘行间距
outRect.top = spacing;
}
//if (position >= spanCount) outRect.top = spacing;
}
}
}

View File

@@ -0,0 +1,63 @@
package com.pukka.jiecheng.utils;
import com.pukka.jiecheng.login.viewmodel.IsLoginViewModel;
import com.pukka.jiecheng.order.viewmodel.OrderViewModel;
import com.pukka.libcore.boxShared.BoxSharedParameters;
import com.pukka.libcore.threading.DispatchQueue;
import com.pukka.libcore.utils.LogUtils;
/**
* 微信小程序是否登录轮询工具类
* */
public class IsLoginTimerUtil {
private static IsLoginTimerUtil isLoginTimerUtil;
private static final Object LOCK = new Object();
private Runnable timerRunnable;
//是否在登录页面开启过轮询
private boolean isStartTimer = false;
public static IsLoginTimerUtil getInstance() {
synchronized (LOCK) {
if (null == isLoginTimerUtil) {
isLoginTimerUtil = new IsLoginTimerUtil();
}
return isLoginTimerUtil;
}
}
/**
* 定时器监听
*/
public void startTimer(IsLoginViewModel isLoginViewModel) {
isStartTimer = true;
stopTimer();
timerRunnable = () -> isLogin(isLoginViewModel);
//3s轮询
DispatchQueue.global.timer(500, 3000, timerRunnable);
}
public void stopTimer() {
if (null != timerRunnable) {
LogUtils.i("stopTimer()");
DispatchQueue.global.remove(timerRunnable);
timerRunnable = null;
}
}
/**
* 轮询接口
*/
public void isLogin(IsLoginViewModel isLoginViewModel) {
if(null != isLoginViewModel){
if(BoxSharedParameters.getInstance().isLogin()){
LogUtils.d("用户已经登录,暂停轮询是否登录接口");
stopTimer();
return;
}
isLoginViewModel.isLogin();
}
}
/**
* 是否在登录页面开启过轮询
*/
public boolean isStartTimer() {
return isStartTimer;
}
}

View File

@@ -0,0 +1,163 @@
package com.pukka.jiecheng.utils;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import android.view.View;
import android.widget.Toast;
import com.google.gson.Gson;
import com.migu.bean.GetLayoutBean;
import com.pukka.jiecheng.filter.FilterActivity;
import com.pukka.jiecheng.index.IndexAmberManager;
import com.pukka.jiecheng.index.IndexConstant;
import com.pukka.jiecheng.video.VideoDetailActivity;
import com.pukka.libcore.base.BaseUrl;
import com.pukka.libcore.base.BuildConfig;
import com.pukka.libcore.utils.AppUtils;
import com.pukka.libcore.utils.JsonUtils;
import com.pukka.libcore.utils.LogUtils;
import com.pukka.libcore.utils.StringUtils;
import org.json.JSONObject;
import java.util.HashMap;
/**
* Author: wxf
* Date: 2023/3/15 16:19
* Description:首页点击处理
*/
public class ItemOnClickUtils {
// 首页推荐位点击处理
@SuppressLint("WrongConstant")
public static void indexItemOnClick(View view, GetLayoutBean.DataBean floor, int position, Context mContext) {
GetLayoutBean.DataBean.ExtraDataBean mItemData = floor.getExtraData().get(position);
if (mItemData == null) {
return;
}
IndexAmberManager.recommendClick(mItemData,(position+1)+"");
String targetType = mItemData.getType();
LogUtils.d("getType-----" + targetType);
switch (targetType) {
// 点播详情页
case "1":
String showType = mItemData.getShowtype();
if(StringUtils.isEmpty(showType) || showType.equals("1")){
LogUtils.d("视频推荐位==");
String reserved = mItemData.getReserved1();
if(!StringUtils.isEmpty(reserved)) {
try {
JSONObject jsonObject = new JSONObject(reserved);
String code = jsonObject.getString("code");
LogUtils.d("vod reserved getContentcode=" + code);
if (!TextUtils.isEmpty(code)) {
MgRoute.go2DetailActivity(mContext, code);
} else {
Toast.makeText(mContext, "未找到内容id", Toast.LENGTH_LONG).show();
}
}catch (Exception exception){
exception.printStackTrace();
}
} else {
LogUtils.d("vod getContentcode=" + mItemData.getContentcode());
if (!TextUtils.isEmpty(mItemData.getContentcode())) {
MgRoute.go2DetailActivity(mContext, mItemData.getContentcode());
} else {
Toast.makeText(mContext, "未找到内容id", Toast.LENGTH_LONG).show();
}
}
} else {
LogUtils.d("普通推荐位 vod getContentcode=" + mItemData.getContentcode());
if (!TextUtils.isEmpty(mItemData.getContentcode())) {
MgRoute.go2DetailActivity(mContext, mItemData.getContentcode());
} else {
Toast.makeText(mContext, "未找到内容id", Toast.LENGTH_LONG).show();
}
}
break;
// 点播播放页
case "2":
if(!StringUtils.isEmpty(mItemData.getReserved1())) {
try {
JSONObject jsonObject = new JSONObject(mItemData.getReserved1());
String code1 = jsonObject.getString("code");
LogUtils.d("vod reserved getContentcode=" + code1);
if (!TextUtils.isEmpty(code1)) {
MgRoute.go2DetailActivity(mContext, code1);
} else {
Toast.makeText(mContext, "未找到内容id", Toast.LENGTH_LONG).show();
}
}catch (Exception exception){
exception.printStackTrace();
}
}else {
LogUtils.d("vod getContentcode=" + mItemData.getContentcode());
if (!TextUtils.isEmpty(mItemData.getContentcode())) {
MgRoute.go2FullPlayerActivity(mContext, mItemData.getContentcode());
} else {
Toast.makeText(mContext, "未找到内容id", Toast.LENGTH_LONG).show();
}
}
break;
// 列表页
case "3":
String listParam = mItemData.getPlayurl();
if (!TextUtils.isEmpty(listParam)) {
String[] split = listParam.split("&");
String listId="";
String parentId="";
if (split.length>=2){
// 子栏目id
listId = split[0];
// 父栏目id
parentId = split[1];
}else if (split.length == 1){
parentId = listParam;
}
MgRoute.go2ListActivity(mContext,parentId,listId);
} else {
Toast.makeText(mContext, "未找到栏目id", Toast.LENGTH_LONG).show();
}
break;
// 外链
case "5":
String url =mItemData.getPlayurl();
if(!StringUtils.isEmpty(url)) {
MgRoute.go2SimpleWebActivity(mContext, url);
}else{
Toast.makeText(mContext, "没有配置外链地址", Toast.LENGTH_LONG).show();
}
break;
// 跳转APK
case "6":
String playurl = mItemData.getPlayurl();
if(!StringUtils.isEmpty(playurl)){
LogUtils.d("playUrl="+playurl);
String[] strings = playurl.split("&&");
if(strings.length>1){
String packageName = strings[0];
String className = strings[1];
LogUtils.d("packageName="+packageName+" className="+className);
AppUtils.startApkByPackageAndClass(mContext,packageName,className);
}else{
AppUtils.startApkByPackageAndClass(mContext,playurl,"");
}
}else{
Toast.makeText(mContext, "配置包名和类名为空", Toast.LENGTH_LONG).show();
}
break;
// 无动作
case "7":
break;
case "8":
FilterActivity.start(mContext);
break;
default:
LogUtils.d("do nothing");
break;
}
}
}

View File

@@ -0,0 +1,656 @@
package com.pukka.jiecheng.utils;
import android.content.Context;
import android.os.Handler;
import android.text.TextUtils;
import com.jetsenxsdk.api.JetsenxSdk;
import com.jetsenxsdk.bean.VideoInfo;
import com.jetsenxsdk.callback.JetsenxInitCallback;
import com.jetsenxsdk.callback.JetsenxPricesCallback;
import com.jetsenxsdk.callback.JetsenxUrlCallback;
import com.pukka.jiecheng.newtv.NewTvUtils;
import com.pukka.jiecheng.video.bean.PlayCodeBean;
import com.pukka.jiecheng.video.utils.PlayRecordContentManager;
import com.pukka.jiecheng.video.utils.PlayRecordManager;
import com.pukka.jiecheng.video.utils.PlayRecordShortManager;
import com.pukka.libcore.base.BaseConst;
import com.pukka.libcore.boxShared.BoxSharedParameters;
import com.pukka.libcore.boxShared.MacUtils;
import com.pukka.libcore.threading.DispatchQueue;
import com.pukka.libcore.utils.LogUtils;
import com.pukka.libcore.utils.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* [获取播放串]<BR>
*
* @author wxf
* @version [V0.1.0.1, 2025/5/23 0027]
* @since V0.1.0.1
*/
public class JetsenxSdkUtil {
public static String AGCODE = "1100101010208919998410060";
public static String TENANT = "huashitv";
public static String ACCESSER = "JSTPKTEST";
public static String SECRET = "utudfD9gVU94r52D";
private static final String TAG = "JetsenxSdkUtil";
private static Handler orderTimeOutHandler = new Handler();
private static Thread thread;
private static String productCodeSp;
private static String sUid = "";
/**
* 第三方变体(thirdparty)EPG 传入的 MAC 覆盖。
*
* 背景:EPG 在后端用该 MAC 注册设备并下发卡密(thirdCardNo),SDK init 必须用同一 MAC
* 才能与后端权益绑定匹配;若回退 MacUtils.getMac 本机自采集 MAC,会导致权益不生效。
* 为空时回退本机自采集(原版行为不变)。
* 进程级静态:ThirdPartyEntryActivity 解析 Intent extras 时设置。
*/
private static volatile String sMacOverride = "";
/**
* 设置 SDK init 的 MAC 覆盖值(EPG 传入的 mac 优先)。
* 传空字符串/null 清除覆盖,恢复本机自采集。
*/
public static void setMacOverride(String mac) {
String normalized = mac == null ? "" : mac.trim();
if (normalized.equals(sMacOverride)) {
return;
}
sMacOverride = normalized;
LogUtils.d(TAG, "setMacOverride: " + (TextUtils.isEmpty(normalized) ? "<cleared>" : normalized));
}
/**
* 读取 SDK init 的 MAC 覆盖值(空串表示未设置)。
*/
public static String getMacOverride() {
return sMacOverride;
}
/**
* 标记当前是否有 re-init 正在进行,保证 globalInit 串行执行
* volatile 确保多线程可见性,synchronized 块保证检查-置位原子性
*/
private static volatile boolean sReInitializing = false;
/**
* 等待中的 re-init 请求队列(FIFO),前一个 init 完成后依次触发下一个
* 避免多个 JetsenxSdk.init 并发执行损坏 SDK 内部鉴权状态
*/
private static final List<InitTask> sPendingReInitQueue = new ArrayList<>();
public interface InitMemberCodeCallback {
void onSuccess(String memberCode);
void onFail(String message);
}
/**
* 封装一次 re-init 请求的上下文与回调,用于排队等待
*/
private static class InitTask {
final Context context;
final InitMemberCodeCallback callback;
InitTask(Context context, InitMemberCodeCallback callback) {
this.context = context;
this.callback = callback;
}
}
/**
* 初始化
*
* @param context 上下文
* @param agcode 捷成华视网聚分配的渠道号
* @param mac mac 可以传空
* @param uid uid 用户的唯一标识
* @param tenant 租户
* @param secret 秘钥
* @param accesser 请求者账号
* @param utype 用户类型 1-手机号,2-三方ID,3-mac,4-sn,5-其他
* @param extendJson 拓展数据
*/
public static void init(Context context, String agcode, String mac, String uid, String tenant, String secret, String accesser, String utype,
String extendJson) {
init(context, agcode, mac, uid, tenant, secret, accesser, utype, extendJson, null);
}
/**
* 全局初始化方法,复用已缓存的 uid
*
* @param context 上下文
* @param callback 初始化结果回调
*/
public static void globalInit(Context context, InitMemberCodeCallback callback) {
if (TextUtils.isEmpty(sUid)) {
LogUtils.d(TAG, "globalInit skipped: no verified uid cached in current process");
if (callback != null) {
callback.onFail("缺少可复用的SDK初始化标识");
}
return;
}
synchronized (sPendingReInitQueue) {
if (sReInitializing) {
// 已有 init 在飞行中:本次请求入队等待,避免并发 init 损坏 SDK 内部状态
sPendingReInitQueue.add(new InitTask(context, callback));
LogUtils.d(TAG, "globalInit queued: another re-init in flight, pending=" + sPendingReInitQueue.size());
return;
}
sReInitializing = true;
}
// 首个请求:真正调用 SDK init,用内部包装回调统一放行下一个排队请求
// try-catch 保护 SDK 同步抛异常的场景:放行队列避免永久锁死,回调 onFail 避免异常上抛调用方线程
try {
init(context, sUid, new InitMemberCodeCallback() {
@Override
public void onSuccess(String memberCode) {
dispatchNextReInit();
if (callback != null) {
try {
callback.onSuccess(memberCode);
} catch (Exception e) {
LogUtils.e(TAG, "globalInit onSuccess callback exception: " + e.getMessage());
}
}
}
@Override
public void onFail(String message) {
dispatchNextReInit();
if (callback != null) {
try {
callback.onFail(message);
} catch (Exception e) {
LogUtils.e(TAG, "globalInit onFail callback exception: " + e.getMessage());
}
}
}
});
} catch (Exception e) {
LogUtils.e(TAG, "globalInit init invoke exception: " + e.getMessage());
// SDK init 同步异常:sReInitializing 已置 true,必须放行队列,否则后续请求永久排队
dispatchNextReInit();
if (callback != null) {
try {
callback.onFail("SDK初始化调用异常: " + e.getMessage());
} catch (Exception ce) {
LogUtils.e(TAG, "globalInit onFail callback exception: " + ce.getMessage());
}
}
}
}
/**
* 当前 re-init 完成后,放行队列中下一个等待请求
* 下一个请求重新走 globalInit 入口,sReInitializing 已置 false 会真正执行 init,串行逻辑闭环
*/
private static void dispatchNextReInit() {
InitTask next;
synchronized (sPendingReInitQueue) {
sReInitializing = false;
next = sPendingReInitQueue.isEmpty() ? null : sPendingReInitQueue.remove(0);
}
if (next != null) {
globalInit(next.context, next.callback);
}
}
public static void cacheReusableUid(String uid) {
if (TextUtils.isEmpty(uid)) {
LogUtils.d(TAG, "cacheReusableUid ignored: empty uid");
return;
}
sUid = uid;
LogUtils.d(TAG, "cacheReusableUid success");
}
/**
* 读取已缓存的 SDK uid。
*
* <p>该字段由 {@link #cacheReusableUid(String)} 写入,主要写入场景:
* <ul>
* <li>MainActivity.onActivated 收到 /api/jst/check 10200 时写入 thirdCardNo</li>
* <li>VideoDetailActivity.onCheckActivated 收到 BenefitOrderController 阶段三 10200 时写入 thirdCardNo</li>
* <li>VideoDetailActivity.onCheckActivated asAScan 10900 路径因 thirdCardNo 为空,不写入</li>
* </ul>
*
* <p>外部使用场景:UI 入口短路(详情页进入时若 sUid 已存在,跳过扫码下单直接展示"已购买VIP")。
* 注意:该字段为进程内静态变量,应用冷启动后为空,需等 MainActivity 启动时 /api/jst/check
* 轮询填充后才能复用。</p>
*
* @return 已缓存的 uid;为空字符串表示未缓存
*/
public static String getSuid() {
return sUid;
}
public static void clearReusableUid() {
sUid = "";
LogUtils.d(TAG, "clearReusableUid: sUid cleared");
}
/**
* 简化的初始化方法
*
* @param context 上下文
* @param uid 用户的唯一标识(如第三方卡号 thirdCardNo)
* @param callback 初始化结果回调
*/
public static void init(Context context, String uid, InitMemberCodeCallback callback) {
// EPG 传入的 MAC 覆盖优先(thirdparty 变体):后端权益以 EPG 注册的 MAC 绑定,
// 本机自采集 MAC 与之不匹配会导致 SDK 鉴权失败、权益不生效
String mac = TextUtils.isEmpty(sMacOverride) ? MacUtils.getMac(context) : sMacOverride;
String utype = "2";
if (!TextUtils.isEmpty(uid) && uid.matches("^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$")) {
mac = uid;
utype = "3";
}
init(context, AGCODE, mac, uid, TENANT, SECRET, ACCESSER, utype, "", callback);
}
public static void init(Context context, String agcode, String mac, String uid, String tenant, String secret, String accesser, String utype,
String extendJson, InitMemberCodeCallback callback) {
LogUtils.d("渠道信息> agcode:" + agcode + " mac:" + mac + " uid:" + uid + " tenant:" + tenant + " secret:" + secret + " accesser:" + accesser + " utype:" + utype + " extendJson:" + extendJson + " comboCode:"+BaseConst.serviceComboCode);
if (StringUtils.isEmpty(mac)) {
//防止初始化报600错误,非法参数的错误,保底
mac = "CC:8C:DA:C6:E9:9B";
}
//如果用户没有登录,获取不到手机号,则使用mac的方式初始化
if(StringUtils.isEmpty(uid)){
LogUtils.d("电话号码获取不到,使用Mac方式登录");
utype = "3";
uid = mac;
}
// JetsenxSdk.init(context, agcode, mac, uid, tenant, secret, accesser, utype, extendJson, new JetsenxInitCallback() {
JetsenxSdk.init(context, agcode, mac, uid, tenant, secret, accesser, utype, extendJson,BaseConst.serviceComboCode, new JetsenxInitCallback() {
@Override
public void onInitSuccess(String s, boolean b) {
LogUtils.d("onInitSuccess()");
if (callback != null) {
callback.onSuccess(s);
}
}
@Override
public void onFail(String s, String s1) {
LogUtils.d("onFail s=" + s);
LogUtils.d("onFail s1=" + s1);
if (callback != null) {
callback.onFail(StringUtils.isEmpty(s1) ? "SDK初始化失败" : s1);
}
}
});
}
/**
* 2.获取付费内容播放地址接口
*
* @param productCode 产品编码
* @param programCode 节目编码
*/
public static void getVideoUrls(String productCode, String programCode) {
LogUtils.d("productCode:" + productCode + " programCode:" + programCode);
JetsenxSdk.getVideoUrls(productCode, programCode, new JetsenxUrlCallback() {
@Override
public void onVideoUrls(VideoInfo[] videoInfos) {
}
@Override
public void onFail(String s, String s1) {
}
});
}
/**
* 未来播控一直不返回的处理的提示
* */
// public static void weiLaiCheckTimeOut(){
// LogUtils.d("未来鉴权超时3秒,放通处理,继续播放");
// //checkValue = true;
// }
// /**
// * 未来鉴权是否通过 true 通过; false 不通过
// * */
// public static boolean checkValue = false;
// /**
// * 未来播控一直不返回的处理
// */
// private static Runnable mFeaturePlayRunnable;
/**
* 未来播控鉴权,超时处理
* */
public static void newtvCheckQuery(String programCode,String programName, GetVideoUrlCallback getVideoUrlCallback){
// checkValue = false;
// mFeaturePlayRunnable =()->getFreeVideoUrls(programCode,programName, getVideoUrlCallback);
// DispatchQueue.main.remove(mFeaturePlayRunnable);
// DispatchQueue.main.after(3000,mFeaturePlayRunnable);
DispatchQueue.global.async(() -> {
//String timeStartCheck = DateUtils.getStringNowDate();
boolean checkValue = NewTvUtils.newtvCheckQuery(programCode, "program");
//String timeEndCheck = DateUtils.getStringNowDate();
//int timeSecond = DateUtils.getSecondCompareStringDate(timeStartCheck,timeEndCheck);
//LogUtils.d("未来鉴权耗时:"+timeSecond);
// if(timeSecond>3){
// LogUtils.d("未来播控鉴权超时3秒,流程丢弃,已经在mFeaturePlayRunnable处理了");
// }else{
DispatchQueue.main.async(() -> {
LogUtils.d("未来鉴权结果 checkValue="+checkValue);
//DispatchQueue.main.remove(mFeaturePlayRunnable);
if (checkValue) { //未来鉴权通过
LogUtils.d("未来鉴权通过,获取播放地址");
getFreeVideoUrls(programCode,programName, getVideoUrlCallback);
} else {
if(null != getVideoUrlCallback){
//未来播控鉴权失败,1001,销毁播放器
getVideoUrlCallback.onResponse("1001");
}
ToastUtils.showLongToast("该节目已下线");
}
});
// }
});
}
/**
* 4、获取免费内容播放地址接口
*
* @param programCode 节目编码
*/
public static void getFreeVideoUrls(String programCode,String programName, GetVideoUrlCallback getVideoUrlCallback) {
LogUtils.d("programCode:" + programCode);
if (StringUtils.isEmpty(programCode)) {
return;
}
JetsenxSdk.getFreeVideoUrls(programCode, new JetsenxUrlCallback() {
@Override
public void onVideoUrls(VideoInfo[] videoInfos) {
if (null != videoInfos && videoInfos.length>0) {
String videoUrl;
NewTvUtils.sensorsPlayBean.init();
NewTvUtils.sensorsPlayBean.setCpID(programCode);
NewTvUtils.sensorsPlayBean.setCpName(programName);
String videoUrl_1080="", videoUrl_720 = "";
for (VideoInfo info:videoInfos
) {
if (info.getFormat().contains("720")
&&TextUtils.isEmpty(videoUrl_720)){
videoUrl_720 = info.getVideoUrl();
PlayCodeBean playCodeBean = new PlayCodeBean();
playCodeBean.setMovieCode(info.movieCode);
playCodeBean.setProgramCode(info.progCode);
playCodeBean.setSpecCode(info.specCode);
PlayRecordShortManager.setPlayCodeBean(playCodeBean);
PlayRecordContentManager.setPlayCodeBean(playCodeBean);
}else if (info.getFormat().contains("1080")
&&TextUtils.isEmpty(videoUrl_1080)){
videoUrl_1080 = info.getVideoUrl();
PlayCodeBean playCodeBean = new PlayCodeBean();
playCodeBean.setMovieCode(info.movieCode);
playCodeBean.setProgramCode(info.progCode);
playCodeBean.setSpecCode(info.specCode);
PlayRecordShortManager.setPlayCodeBean(playCodeBean);
PlayRecordContentManager.setPlayCodeBean(playCodeBean);
}
}
if (!TextUtils.isEmpty(videoUrl_1080)) {
//高清
videoUrl = videoUrl_1080;
NewTvUtils.resolution = "0";
LogUtils.d("videoUrl_1080:" + videoUrl_1080);
} else {
//标清
videoUrl = videoUrl_720;
NewTvUtils.resolution = "1";
LogUtils.d("videoUrl_720:" + videoUrl_720);
}
LogUtils.d("videoUrl:" + videoUrl);
if (null != getVideoUrlCallback) {
getVideoUrlCallback.onResponse(videoUrl);
}
}else{
if (null != getVideoUrlCallback) {
//为短视频切换频道,销毁播放器
LogUtils.d("没有获取到播放地址");
getVideoUrlCallback.onResponse("");
}
}
}
@Override
public void onFail(String s, String s1) {
LogUtils.d("onFail s=" + s);
LogUtils.d("onFail s1=" + s1);
getVideoUrlCallback.onResponse("");
}
});
}
public static void getAllVideoUrl(boolean isProgram, String parentContentType, String programCode, OnGetAllVideoUrlCallback onGetAllVideoUrlCallback) {
LogUtils.d(TAG, "getAllVideoUrl parentContentType:" + parentContentType + " programCode:" + programCode);
// isTimeOut = -1;
// if (thread != null) {
// thread.interrupt();
// }
// orderTimeOutHandler.removeCallbacksAndMessages(null);
if (BoxSharedParameters.getInstance().isNewTvPass()) {
LogUtils.d(TAG, "getAllVideoUrl 未来鉴权放通");
getAllVideoUrlNext(parentContentType, programCode, onGetAllVideoUrlCallback);
return;
}
DispatchQueue.global.async(() -> {
try {
boolean isOrderOk = NewTvUtils.newtvCheckQuery(programCode, isProgram ? "program" : "programset");
if (isOrderOk) {
LogUtils.d(TAG, "getAllVideoUrl 未来鉴权成功");
getAllVideoUrlNext(parentContentType, programCode, onGetAllVideoUrlCallback);
} else {
LogUtils.d(TAG, "getAllVideoUrl 未来鉴权失败");
onGetAllVideoUrlCallback.onErr("1111", "该节目已下线");
}
} catch (Exception e) {
System.out.println("Task interrupted");
Thread.currentThread().interrupt();
}
});
// thread = new Thread(new Runnable() {
// @Override
// public void run() {
// try {
// boolean isOrderOk = NewTvUtils.newtvCheckQuery(programCode, isProgram ? "program" : "programset");
// if (isTimeOut == 2) {
// return;
// }
// isTimeOut = 1;
// if (isOrderOk) {
// orderTimeOutHandler.removeCallbacksAndMessages(null);
// LogUtils.d(TAG, "getAllVideoUrl 未来鉴权成功");
// getAllVideoUrlNext(parentContentType, programCode, onGetAllVideoUrlCallback);
// } else {
// LogUtils.d(TAG, "getAllVideoUrl 未来鉴权失败");
// onGetAllVideoUrlCallback.onErr("1111", "该节目已下线");
// }
// } catch (Exception e) {
// System.out.println("Task interrupted");
// Thread.currentThread().interrupt();
// }
// }
// });
// thread.start();
// orderTimeOutHandler.postDelayed(new Runnable() {
// @Override
// public void run() {
// if (isTimeOut == -1) {
// isTimeOut = 2;
// LogUtils.d(TAG, "getAllVideoUrl 未来鉴权超时");
// getAllVideoUrlNext(parentContentType, programCode, onGetAllVideoUrlCallback);
// }
// }
// }, 5000);
}
// private static int isTimeOut = -1;
private static void getAllVideoUrlNext(String parentContentType, String programCode, OnGetAllVideoUrlCallback onGetAllVideoUrlCallback) {
if (!TextUtils.isEmpty(productCodeSp)) {
getAllVideoUrl2(parentContentType, productCodeSp, programCode, onGetAllVideoUrlCallback);
} else {
JetsenxSdk.getAllAssetPrices(ACCESSER, SECRET, new JetsenxPricesCallback() {
@Override
public void onAllAssetPrices(List<Map<String, Object>> list) {
if (null != list) {
LogUtils.d(TAG, "onAllAssetPrices list.size:" + list.size());
String productCode = list.get(0).get("packageCode").toString();
LogUtils.d(TAG, "packageCode:" + productCode);
productCodeSp = productCode;
getAllVideoUrl2(parentContentType, productCode, programCode, onGetAllVideoUrlCallback);
} else {
onGetAllVideoUrlCallback.onErr("-2", "productCode is NULL");
}
}
@Override
public void onFail(String s, String s1) {
LogUtils.d(TAG, "onFail s=" + s);
LogUtils.d(TAG, "onFail s1=" + s1);
onGetAllVideoUrlCallback.onErr(s, s1);
}
});
}
}
private static void getAllVideoUrl2(String parentContentType, String productCode, String programCode, OnGetAllVideoUrlCallback onGetAllVideoUrlCallback) {
LogUtils.d(TAG, "videoUrl parentContentType:" + parentContentType);
if (TextUtils.isEmpty(parentContentType)) {
onGetAllVideoUrlCallback.onErr("-1", "parentContentType is NULL");
} else if (!parentContentType.equals("8")) {
getVideoUrls2(productCode, programCode, onGetAllVideoUrlCallback);
} else {
getFreeVideoUrls2(programCode, onGetAllVideoUrlCallback);
}
}
private static void getVideoUrls2(String productCode, String programCode, OnGetAllVideoUrlCallback onGetAllVideoUrlCallback) {
LogUtils.d(TAG, "videoUrl productCode:" + productCode + " programCode:" + programCode);
JetsenxSdk.getVideoUrls(productCode, programCode, new JetsenxUrlCallback() {
@Override
public void onVideoUrls(VideoInfo[] videoInfos) {
LogUtils.d(TAG, "videoUrl_all:" + Arrays.toString(videoInfos));
if (null != videoInfos && videoInfos.length>0) {
String videoUrl_1080="", videoUrl_720 = "";
for (VideoInfo info:videoInfos
) {
if (info.getFormat().contains("720")
&&TextUtils.isEmpty(videoUrl_720)){
videoUrl_720 = info.getVideoUrl();
PlayCodeBean playCodeBean = new PlayCodeBean();
playCodeBean.setMovieCode(info.movieCode);
playCodeBean.setProgramCode(info.progCode);
playCodeBean.setSpecCode(info.specCode);
PlayRecordManager.playCodeMaps.put(720,playCodeBean);
}else if (info.getFormat().contains("1080")
&&TextUtils.isEmpty(videoUrl_1080)){
videoUrl_1080 = info.getVideoUrl();
PlayCodeBean playCodeBean = new PlayCodeBean();
playCodeBean.setMovieCode(info.movieCode);
playCodeBean.setProgramCode(info.progCode);
playCodeBean.setSpecCode(info.specCode);
PlayRecordManager.playCodeMaps.put(1080,playCodeBean);
}
}
LogUtils.d(TAG, "videoUrl_1080:" + videoUrl_1080);
LogUtils.d(TAG, "videoUrl_720:" + videoUrl_720);
if (null != onGetAllVideoUrlCallback) {
onGetAllVideoUrlCallback.onResponse(videoUrl_1080, videoUrl_720, false);
}
}else {
onGetAllVideoUrlCallback.onErr("411", "未返回播放地址");
}
}
@Override
public void onFail(String s, String s1) {
LogUtils.d(TAG, "videoUrl onFail:" + s + " msg:" + s1);
onGetAllVideoUrlCallback.onErr(s, s1);
}
});
}
private static void getFreeVideoUrls2(String programCode, OnGetAllVideoUrlCallback onGetAllVideoUrlCallback) {
LogUtils.d(TAG, "videoUrl programCode:" + programCode);
if (StringUtils.isEmpty(programCode)) {
return;
}
JetsenxSdk.getFreeVideoUrls(programCode, new JetsenxUrlCallback() {
@Override
public void onVideoUrls(VideoInfo[] videoInfos) {
if (null != videoInfos && videoInfos.length>0) {
String videoUrl_1080="", videoUrl_720 = "";
for (VideoInfo info:videoInfos
) {
if (info.getFormat().contains("720")
&&TextUtils.isEmpty(videoUrl_720)){
videoUrl_720 = info.getVideoUrl();
PlayCodeBean playCodeBean = new PlayCodeBean();
playCodeBean.setMovieCode(info.movieCode);
playCodeBean.setProgramCode(info.progCode);
playCodeBean.setSpecCode(info.specCode);
PlayRecordManager.playCodeMaps.put(720,playCodeBean);
}else if (info.getFormat().contains("1080")
&&TextUtils.isEmpty(videoUrl_1080)){
videoUrl_1080 = info.getVideoUrl();
PlayCodeBean playCodeBean = new PlayCodeBean();
playCodeBean.setMovieCode(info.movieCode);
playCodeBean.setProgramCode(info.progCode);
playCodeBean.setSpecCode(info.specCode);
PlayRecordManager.playCodeMaps.put(1080,playCodeBean);
}
}
LogUtils.d(TAG, "videoUrl_1080:" + videoUrl_1080);
LogUtils.d(TAG, "videoUrl_720:" + videoUrl_720);
if (null != onGetAllVideoUrlCallback) {
onGetAllVideoUrlCallback.onResponse(videoUrl_1080, videoUrl_720, true);
}
}else {
onGetAllVideoUrlCallback.onErr("410", "未返回播放地址");
}
}
@Override
public void onFail(String s, String s1) {
LogUtils.d(TAG, "videoUrl onFail:" + s + " msg:" + s1);
onGetAllVideoUrlCallback.onErr(s, s1);
}
});
}
public interface GetVideoUrlCallback {
void onResponse(String videoUrl);
}
public interface OnGetAllVideoUrlCallback {
void onResponse(String videoUrl_1080, String videoUrl_720, boolean isFree);
void onErr(String code, String msg);
}
}

View File

@@ -0,0 +1,293 @@
package com.pukka.jiecheng.utils;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import com.alibaba.fastjson.JSONObject;
import com.pukka.jiecheng.R;
import com.pukka.jiecheng.aboutmy.AboutMyActivity;
import com.pukka.jiecheng.device.DeviceActivity;
import com.pukka.jiecheng.history.HistoryActivity;
import com.pukka.jiecheng.index.IndexAmberManager;
import com.pukka.jiecheng.list.ListActivity;
import com.pukka.jiecheng.login.LoginActivity;
import com.pukka.jiecheng.order.OrderActivity;
import com.pukka.jiecheng.search.SearchActivity;
import com.pukka.jiecheng.video.VideoDetailActivity;
import com.pukka.jiecheng.web.SimpleWebActivity;
import com.pukka.libcore.boxShared.BoxSharedParameters;
import com.pukka.libcore.utils.LogUtils;
import com.pukka.libcore.utils.StringUtils;
/**
* Author: wxf
* Date: 2023/2/3 13:14
* Description:跳转路由
*/
public class MgRoute {
/**
* 跳转到原生搜索页面
*/
public static void go2Search(Context context) {
LogUtils.d("search 0");
Intent intent = new Intent(context, SearchActivity.class);
context.startActivity(intent);
}
/**
* 跳转到原生搜索页面
*/
public static void go2Search(Context context, int type) {
LogUtils.d("search 0:"+type);
Intent intent = new Intent(context, SearchActivity.class);
intent.putExtra("byPos", type);
context.startActivity(intent);
}
/**
* 跳转到更多历史页面
*/
public static void go2HisMore(Context context) {
HistoryActivity.start(context);
}
/**
* 跳转到更多收藏页面
*/
public static void go2ColMore(Context context,int position) {
HistoryActivity.start(context,position);
}
/**
* 跳转到订购页面
*/
public static void go2Order(Context context) {
// if (!BoxSharedParameters.getInstance().isLogin()){
// LoginActivity.start(context);
// return;
// }
Intent intent = new Intent(context, OrderActivity.class);
context.startActivity(intent);
}
/**
* 跳转到我的订购
*/
public static void go2Order(Context context,String go_type) {
// if (!BoxSharedParameters.getInstance().isLogin()){
// LoginActivity.start(context);
// return;
// }
Intent intent = new Intent(context, OrderActivity.class);
intent.putExtra(OrderActivity.go_type_param,go_type);
context.startActivity(intent);
}
/**
* 跳转到设备页面
*/
public static void go2Device(Context context) {
Intent intent = new Intent(context, DeviceActivity.class);
context.startActivity(intent);
}
/**
* 跳转到帮助页面
*/
public static void go2AboutMy(Context context) {
Intent intent = new Intent(context, AboutMyActivity.class);
context.startActivity(intent);
}
/**
* 跳转到设置面
*/
public static void go2Setting(Context context) {
// Intent intent = new Intent(context, MainWebActivity.class);
// Bundle bundle = new Bundle();
// StringBuilder sbUrl = new StringBuilder();
// sbUrl.append(BaseUrl.getSingleton().getBaseEpgHttpUrl());
// sbUrl.append(MgRoute.urlSetting);
// bundle.putString("url",sbUrl.toString());
// intent.putExtras(bundle);
// context.startActivity(intent);
}
/**
* 跳转到收藏面
*/
public static void go2Collect(Context context) {
// Intent intent = new Intent(context, MainWebActivity.class);
// Bundle bundle = new Bundle();
// StringBuilder sbUrl = new StringBuilder();
// sbUrl.append(BaseUrl.getSingleton().getBaseEpgHttpUrl());
// sbUrl.append(MgRoute.urlCollect);
// bundle.putString("url",sbUrl.toString());
// intent.putExtras(bundle);
// context.startActivity(intent);
}
/**
* 跳转到更多历史记录
*/
public static void go2History(Context context) {
// LogUtils.d("go to history:go2History");
// isGo2TopClick=false;
// ItemOnClickUtils.go2Epg(context,"", IndexConstant.getHistoryUrl(),"noparam","");
}
/**
* 首页TOP,跳转分发
*
* @param context context
* @param view view
* @param pageType 导航或二级页
*/
public static void topRoute(Context context, View view, String pageType) {
if (view.getId() == R.id.id_iv_history) {
IndexAmberManager.topButtonClick("历史记录");
HistoryActivity.start(context);
} else if (view.getId() == R.id.id_iv_search || view.getId() == R.id.id_bottom_cl_search) {
int type = 2;
if (view.getId() == R.id.id_iv_search) {
type = 1;
}
if (type == 1) {
IndexAmberManager.topButtonClick("搜索");
} else {
IndexAmberManager.bottomButtonClick("搜索");
}
go2Search(context, type);
} else if (view.getId() == R.id.id_iv_order) {
IndexAmberManager.topButtonClick("订购中心");
go2Order(context);
} else if(view.getId() == R.id.id_iv_help){
go2AboutMy(context);
}
}
/**
* 我的页面 收藏
*/
public static void myRouteColl(Context context, int contentId) {
// isGo2TopClick=false;
// Intent intent = new Intent(context, MainWebActivity.class);
// Bundle bundle = new Bundle();
// StringBuilder sbUrl = new StringBuilder();
// sbUrl.append(BaseUrl.getSingleton().getBaseEpgHttpUrl());
// sbUrl.append(BaseUrl.getSingleton().getHttpMidUrl()+"/pages/vod_detail/index.html?programCode=");
// sbUrl.append(contentId);
// sbUrl.append("&fromAppIndex=true");
// bundle.putString("url",sbUrl.toString());
// intent.putExtras(bundle);
// context.startActivity(intent);
}
/**
* 跳转到用户协议
*/
public static void go2UserAgree(Context context, String url) {
LogUtils.d("url=" + url);
Intent intent = new Intent(context, SimpleWebActivity.class);
Bundle bundle = new Bundle();
bundle.putString("url",url);
intent.putExtras(bundle);
context.startActivity(intent);
}
/**
* 跳转到图片
*/
public static void go2ImageActivity(Context context, String imageUrl) {
// isGo2TopClick=true;
// Intent intent = new Intent(context, ImageActivity.class);
// intent.putExtra("IMAGEURL",imageUrl);
// context.startActivity(intent);
}
/**
* 跳转外链
*/
public static void go2SimpleWebActivity(Context context, String url) {
Intent starter = new Intent(context, SimpleWebActivity.class);
starter.putExtra("url", url);
context.startActivity(starter);
}
/**
* 跳转列表页
*/
public static void go2ListActivity(Context context, String parentId, String listId) {
Intent intent = new Intent(context, ListActivity.class);
intent.putExtra(ListActivity.PARENTID, parentId);
intent.putExtra(ListActivity.LISTID, listId);
context.startActivity(intent);
}
private static long lastClickTime = 0;
private static final long CLICK_INTERVAL = 1000; // 1秒内防止重复点击
/**
* 跳转详情页
*/
public static void go2DetailActivity(Context context, String contentCode) {
long currentTime = System.currentTimeMillis();
if (currentTime - lastClickTime < CLICK_INTERVAL) {
LogUtils.d("1秒内防止重复点击");
return;
}
lastClickTime = currentTime;
VideoDetailActivity.start(context, contentCode, "");
}
/**
* 跳转详情页
*/
public static void go2DetailActivity(Context context, String contentCode,String contentType) {
VideoDetailActivity.start(context, contentCode, contentType);
}
/**
* 跳转到全屏播放页面
*/
public static void go2FullPlayerActivity(Context context, String contentCode) {
VideoDetailActivity.startFull(context, contentCode, "");
}
// 解析appParam 格式例如:gameId:6771&&gameType:1
public static String getGameParam(String appParam){
try {
JSONObject jsonObject = new JSONObject();
for (String keyValue : appParam.split("&&")) {
String[] keyValues = keyValue.split(":");
if (keyValues.length == 2) {
String key = keyValues[0];
int value = Integer.valueOf(keyValues[1]);
jsonObject.put(key,value);
}
}
String str = jsonObject.toJSONString();
LogUtils.d("getGameParam="+str);
return str;
}catch (Exception ex){
ex.printStackTrace();
}
return "";
}
/**
* 启动云游戏或启动本地游戏
* 跳转到游戏
* */
public static void startActivityByClass(Context context,String packageName,String className,String appParams){
try {
Intent intent = new Intent();
intent.putExtra("param", appParams);
ComponentName cn = new ComponentName(packageName, className);
intent.setComponent(cn);
context.startActivity(intent);
}catch (Exception ex){
LogUtils.e("class start error="+ex.getMessage());
ex.printStackTrace();
}
}
}

View File

@@ -0,0 +1,68 @@
package com.pukka.jiecheng.utils;
import android.text.TextUtils;
import com.google.gson.Gson;
import com.migu.bean.ContentListBean;
import com.pukka.jiecheng.video.bean.SendAddPlayRecordBean;
import com.pukka.libcore.utils.LogUtils;
public class NewRecordCollManager {
public static String getDataJson(ContentListBean.ResultEntity mContentData, String contentcode) {
SendAddPlayRecordBean bean = new SendAddPlayRecordBean();
bean.setParentcode(mContentData.getCode());
bean.setContentcode(contentcode);
bean.setActordisplay(mContentData.getActordisplay());
bean.setCategoryname(mContentData.getCategoryname());
bean.setActordisplayen(mContentData.getActordisplay());
bean.setCategoryid(mContentData.getCatalogid());
bean.setCategorynameen("");
bean.setContentkindname(mContentData.getContentkindname());
bean.setContentcountryid("");
bean.setContentkindid("");
bean.setContentname(mContentData.getName());
bean.setCpname(mContentData.getCpname());
bean.setContenttype(mContentData.getContenttype() + "");
bean.setContentkindnameen("");
bean.setContentyearid("");
bean.setEpisodeindex(getRecordIndex(mContentData,contentcode));
bean.setExtendimg("");
bean.setSpid("");
bean.setSpName("");
bean.setIcon("");
bean.setOriginalcountry(mContentData.getOriginalcountry());
bean.setWriterdisplay(mContentData.getWriterdisplay());
String img = "";
if (!TextUtils.isEmpty(mContentData.getMidimg())) {
img = mContentData.getMidimg();
} else if (!TextUtils.isEmpty(mContentData.getMidimg2())) {
img = mContentData.getMidimg2();
} else if (!TextUtils.isEmpty(mContentData.getPosterimg())) {
img = mContentData.getPosterimg();
}
bean.setMidimg2(img);
bean.setMidimg(img);
bean.setPosterimg3(img);
bean.setOriginalcountryen("");
bean.setWriterdisplayen("");
String str = new Gson().toJson(bean);
LogUtils.d("addPlayRecord:" + str);
return str;
}
private static String getRecordIndex(ContentListBean.ResultEntity mContentData, String code) {
int recordIndex = 0;
if (mContentData.getContenttype() == 2 || mContentData.getContenttype() == 3) {
if (mContentData.getSubcontents() != null) {
for (int i = 0; i < mContentData.getSubcontents().size(); i++) {
if (mContentData.getSubcontents().get(i).getContentcode().equals(code)) {
recordIndex = i;
break;
}
}
}
}
return (recordIndex+1)+"";
}
}

View File

@@ -0,0 +1,141 @@
package com.pukka.jiecheng.utils;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
/**
* 分页管理器
*
* @param <T> 数据集合列表
* @param <V> 适配器
*/
public class NextPageManger<T, V extends RecyclerView.Adapter> {
private final List<T> list = new ArrayList<>();
private int pageIndex = 1;
private boolean isNextPage;
private int pageTotal;
private OnUpdateCallBack onUpdateCallBack;
private V adapter;
/**
* 刷新数据调用接口的监听
*/
public void setOnUpdateCallBack(OnUpdateCallBack onUpdateCallBack) {
this.onUpdateCallBack = onUpdateCallBack;
}
/**
* 设置适配器
*/
public void setAdapter(V adapter) {
this.adapter = adapter;
}
public void setPageIndex(int pageIndex) {
this.pageIndex = pageIndex;
}
/**
* 执行分页操作
*/
public void nextPage() {
if (onUpdateCallBack == null) {
return;
}
if (isNextPage) {
isNextPage = false;
pageIndex++;
onUpdateCallBack.onUpdate();
} else {
onUpdateCallBack.onNoMoreDate();
}
}
/**
* 获取分页数据失败zhix
*/
public void notifyDataFail() {
if (pageIndex > 1) {
pageIndex--;
}
if (pageTotal <= pageIndex) {
isNextPage = false;
} else {
isNextPage = true;
}
}
/**
* 获取分页数据成功执行
*
* @param pageTotal 总页数
* @param dateList 分页数据集合
*/
public void notifyData(int pageTotal, List<T> dateList) {
notifyData(null,pageTotal,dateList);
}
public void notifyData(RecyclerView recyclerView,int pageTotal, List<T> dateList) {
this.pageTotal = pageTotal;
if (pageIndex == 1) {
if (dateList.isEmpty()){
onUpdateCallBack.onNoDate();
}
int oldSize = list.size();
list.clear();
if (adapter != null) {
if (oldSize > 0) {
adapter.notifyItemRangeRemoved(0, oldSize);
}
}
if (!dateList.isEmpty()) {
list.addAll(dateList);
if (adapter != null) {
adapter.notifyItemRangeInserted(0, dateList.size());
}
}
} else {
int positionInserted = list.size();
this.list.addAll(dateList);
if (adapter != null) {
adapter.notifyItemRangeInserted(positionInserted, dateList.size());
}
}
if (pageTotal <= pageIndex) {
isNextPage = false;
} else {
isNextPage = true;
}
}
/**
* 返回数据集合,适配器构造时放入
*/
public List<T> getList() {
return list;
}
/**
* 返回当前页数,接口获取分页数据时放入
*/
public int getPageIndex() {
return pageIndex;
}
/**
* 返回是否可以分页操作
*/
public boolean isNextPage() {
return isNextPage;
}
public interface OnUpdateCallBack {
void onUpdate();
void onNoMoreDate();
void onNoDate();
}
}

View File

@@ -0,0 +1,24 @@
package com.pukka.jiecheng.utils;
public class PageNumberUtil {
/**
* 计算总页数
* */
public static int calculateTotalPages(int totalItems, int pageSize) {
if (totalItems <= 0) {
return 0; // 没有数据时返回0页
}
if (pageSize <= 0) {
throw new IllegalArgumentException("每页大小必须大于0");
}
return (totalItems - 1) / pageSize + 1;
}
/**
* 计算当前页数
* @param position 当前position
* @param itemsPerPage 每页项数
* */
public static int calculatePage(int position, int itemsPerPage) {
return (position / itemsPerPage) + 1;
}
}

View File

@@ -0,0 +1,118 @@
package com.pukka.jiecheng.utils;
import java.util.List;
/**
* 分页工具类
* */
public class PaginationUtil<T> {
private List<T> allData; // 所有数据
private int currentPage; // 当前页码
private int pageSize; // 每页大小
private int totalPages; // 总页数
private int totalItems; // 总数据量
public PaginationUtil(List<T> allData, int pageSize) {
this.allData = allData;
this.pageSize = pageSize;
this.totalItems = allData.size();
this.totalPages = (int) Math.ceil((double) totalItems / pageSize);
this.currentPage = 1;
}
/**
* 获取当前页数据
*/
public List<T> getCurrentPageData() {
int start = (currentPage - 1) * pageSize;
int end = Math.min(start + pageSize, totalItems);
// if (start >= totalItems) {
// return List.of();
// }
return allData.subList(start, end);
}
/**
* 跳转到指定页
*/
public List<T> goToPage(int pageNumber) {
if (pageNumber < 1) {
pageNumber = 1;
} else if (pageNumber > totalPages) {
pageNumber = totalPages;
}
this.currentPage = pageNumber;
return getCurrentPageData();
}
/**
* 下一页
*/
public List<T> nextPage() {
if (hasNextPage()) {
currentPage++;
}
return getCurrentPageData();
}
/**
* 上一页
*/
public List<T> previousPage() {
if (hasPreviousPage()) {
currentPage--;
}
return getCurrentPageData();
}
/**
* 是否有下一页
*/
public boolean hasNextPage() {
return currentPage < totalPages;
}
/**
* 是否有上一页
*/
public boolean hasPreviousPage() {
return currentPage > 1;
}
/**
* 获取分页信息
*/
public PageInfo getPageInfo() {
return new PageInfo(currentPage, pageSize, totalPages, totalItems);
}
// Getters
public int getCurrentPage() { return currentPage; }
public int getPageSize() { return pageSize; }
public int getTotalPages() { return totalPages; }
public int getTotalItems() { return totalItems; }
/**
* 分页信息类
*/
public static class PageInfo {
private int currentPage;
private int pageSize;
private int totalPages;
private int totalItems;
public PageInfo(int currentPage, int pageSize, int totalPages, int totalItems) {
this.currentPage = currentPage;
this.pageSize = pageSize;
this.totalPages = totalPages;
this.totalItems = totalItems;
}
// Getters
public int getCurrentPage() { return currentPage; }
public int getPageSize() { return pageSize; }
public int getTotalPages() { return totalPages; }
public int getTotalItems() { return totalItems; }
}
}

View File

@@ -0,0 +1,49 @@
package com.pukka.jiecheng.utils;
import android.content.Context;
import com.pukka.jiecheng.order.viewmodel.OrderViewModel;
import com.pukka.libcore.threading.DispatchQueue;
import com.pukka.libcore.utils.LogUtils;
/**
* 轮询工具类
* */
public class PayResultTimerUtil {
private static PayResultTimerUtil payResultTimerUtil;
private static final Object LOCK = new Object();
private Runnable timerRunnable;
public static PayResultTimerUtil getInstance() {
synchronized (LOCK) {
if (null == payResultTimerUtil) {
payResultTimerUtil = new PayResultTimerUtil();
}
return payResultTimerUtil;
}
}
/**
* 定时器监听
*/
public void startTimer(OrderViewModel orderViewModel,String preOrderCode) {
stopTimer();
timerRunnable = () -> getPayResultCycle(orderViewModel,preOrderCode);
//3s轮询
DispatchQueue.global.timer(500, 3000, timerRunnable);
}
public void stopTimer() {
if (null != timerRunnable) {
LogUtils.i("stopTimer()");
DispatchQueue.global.remove(timerRunnable);
timerRunnable = null;
}
}
/**
* 轮询接口
*/
public void getPayResultCycle(OrderViewModel orderViewModel,String preOrderCode) {
if(null != orderViewModel){
orderViewModel.getPayResultCycle(preOrderCode);
}
}
}

View File

@@ -0,0 +1,107 @@
package com.pukka.jiecheng.utils;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.text.TextUtils;
import androidx.annotation.ColorInt;
import androidx.annotation.Nullable;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.CharacterSetECI;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import java.util.Hashtable;
/**
* @ClassName: QRCodeUtil
* @Description: 二维码工具类
* @Author Wangnan
* @Date 2017/2/8
*/
public class QRCodeUtil {
/**
* 创建二维码位图
*
* @param content 字符串内容(支持中文)
* @param width 位图宽度(单位:px)
* @param height 位图高度(单位:px)
* @return
*/
@Nullable
public static Bitmap createQRCodeBitmap(String content, int width, int height){
return createQRCodeBitmap(content, width, height, "UTF-8", "", "", Color.BLACK, Color.WHITE);
}
/**
* 创建二维码位图 (支持自定义配置和自定义样式)
*
* @param content 字符串内容
* @param width 位图宽度,要求>=0(单位:px)
* @param height 位图高度,要求>=0(单位:px)
* @param character_set 字符集/字符转码格式 (支持格式:{@link CharacterSetECI })。传null时,zxing源码默认使用 "ISO-8859-1"
* @param error_correction 容错级别 (支持级别:{@link ErrorCorrectionLevel })。传null时,zxing源码默认使用 "L"
* @param margin 空白边距 (可修改,要求:整型且>=0), 传null时,zxing源码默认使用"4"。
* @param color_black 黑色色块的自定义颜色值
* @param color_white 白色色块的自定义颜色值
* @return
*/
@Nullable
public static Bitmap createQRCodeBitmap(String content, int width, int height,
@Nullable String character_set, @Nullable String error_correction, @Nullable String margin,
@ColorInt int color_black, @ColorInt int color_white){
/** 1.参数合法性判断 */
if(TextUtils.isEmpty(content)){ // 字符串内容判空
return null;
}
if(width < 0 || height < 0){ // 宽和高都需要>=0
return null;
}
try {
/** 2.设置二维码相关配置,生成BitMatrix(位矩阵)对象 */
Hashtable<EncodeHintType, String> hints = new Hashtable<>();
if(!TextUtils.isEmpty(character_set)) {
hints.put(EncodeHintType.CHARACTER_SET, character_set); // 字符转码格式设置
}
if(!TextUtils.isEmpty(error_correction)){
hints.put(EncodeHintType.ERROR_CORRECTION, error_correction); // 容错级别设置
}
if(!TextUtils.isEmpty(margin)){
hints.put(EncodeHintType.MARGIN, margin); // 空白边距设置
}
BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
/** 3.创建像素数组,并根据BitMatrix(位矩阵)对象为数组元素赋颜色值 */
int[] pixels = new int[width * height];
for(int y = 0; y < height; y++){
for(int x = 0; x < width; x++){
if(bitMatrix.get(x, y)){
pixels[y * width + x] = color_black; // 黑色色块像素设置
} else {
pixels[y * width + x] = color_white; // 白色色块像素设置
}
}
}
/** 4.创建Bitmap对象,根据像素数组设置Bitmap每个像素点的颜色值,之后返回Bitmap对象 */
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
return bitmap;
} catch ( WriterException e) {
e.printStackTrace();
}
return null;
}
}

View File

@@ -0,0 +1,105 @@
package com.pukka.jiecheng.utils;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.Writer;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.google.zxing.qrcode.encoder.ByteMatrix;
import com.google.zxing.qrcode.encoder.Encoder;
import com.google.zxing.qrcode.encoder.QRCode;
import java.util.Map;
/**
* This object renders a QR Code as a BitMatrix 2D array of greyscale values.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
public final class QRCodeWriter implements Writer {
private static final int QUIET_ZONE_SIZE = 0;
@Override
public BitMatrix encode(String contents, BarcodeFormat format, int width, int height)
throws WriterException {
return encode(contents, format, width, height, null);
}
@Override
public BitMatrix encode(String contents,
BarcodeFormat format,
int width,
int height,
Map<EncodeHintType,?> hints) throws WriterException {
if (contents.isEmpty()) {
throw new IllegalArgumentException("Found empty contents");
}
if (format != BarcodeFormat.QR_CODE) {
throw new IllegalArgumentException("Can only encode QR_CODE, but got " + format);
}
if (width < 0 || height < 0) {
throw new IllegalArgumentException("Requested dimensions are too small: " + width + 'x' +
height);
}
ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L;
int quietZone = QUIET_ZONE_SIZE;
if (hints != null) {
ErrorCorrectionLevel requestedECLevel = (ErrorCorrectionLevel) hints.get(EncodeHintType.ERROR_CORRECTION);
if (requestedECLevel != null) {
errorCorrectionLevel = requestedECLevel;
}
Integer quietZoneInt = (Integer) hints.get(EncodeHintType.MARGIN);
if (quietZoneInt != null) {
quietZone = quietZoneInt;
}
}
QRCode code = Encoder.encode(contents, errorCorrectionLevel, hints);
return renderResult(code, width, height, quietZone);
}
// Note that the input matrix uses 0 == white, 1 == black, while the output matrix uses
// 0 == black, 255 == white (i.e. an 8 bit greyscale bitmap).
private static BitMatrix renderResult(QRCode code, int width, int height, int quietZone) {
ByteMatrix input = code.getMatrix();
if (input == null) {
throw new IllegalStateException();
}
int inputWidth = input.getWidth();
int inputHeight = input.getHeight();
int qrWidth = inputWidth + (quietZone * 2);
int qrHeight = inputHeight + (quietZone * 2);
int outputWidth = Math.max(width, qrWidth);
int outputHeight = Math.max(height, qrHeight);
int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);
// Padding includes both the quiet zone and the extra white pixels to accommodate the requested
// dimensions. For example, if input is 25x25 the QR will be 33x33 including the quiet zone.
// If the requested size is 200x160, the multiple will be 4, for a QR of 132x132. These will
// handle all the padding from 100x100 (the actual QR) up to 200x160.
int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
int topPadding = (outputHeight - (inputHeight * multiple)) / 2;
BitMatrix output = new BitMatrix(outputWidth, outputHeight);
for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {
// Write the contents of this row of the barcode
for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
if (input.get(inputX, inputY) == 1) {
output.setRegion(outputX, outputY, multiple, multiple);
}
}
}
return output;
}
}

View File

@@ -0,0 +1,274 @@
package com.pukka.jiecheng.utils;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.fragment.app.FragmentActivity;
import com.pukka.libcore.utils.AppContext;
import com.pukka.libcore.utils.ContextUtils;
public final class ResUtil {
private static final String TAG = "ResUtil";
private static final float ROUNDING = 0.5F;
private ResUtil() {
}
public static Resources getResources() {
return ContextUtils.getContext().getResources();
}
@NonNull
public static Resources getResources(Context context) {
if (null != context) {
Resources resources = context.getResources();
if (null != resources) {
return resources;
}
}
return AppContext.getContext().getResources();
}
public static String getString(int resId) {
try {
return AppContext.getContext().getString(resId);
} catch (NotFoundException var2) {
return null;
}
}
public static String getString(int resId, Object... formatArgs) {
try {
return AppContext.getContext().getString(resId, formatArgs);
} catch (NotFoundException var3) {
return null;
}
}
public static String getQuantityString(int resId, int quantity, Object... formatArgs) {
try {
return AppContext.getContext().getResources().getQuantityString(resId, quantity, formatArgs);
} catch (NotFoundException var4) {
return null;
}
}
private static ArrayMap<Integer, Integer> dimensionPixelSizeMap = new ArrayMap<>();
public static int getDimensionPixelSize(int resId) {
try {
Integer value = dimensionPixelSizeMap.get(resId);
if (value == null) {
value = AppContext.getContext().getResources().getDimensionPixelSize(resId);
dimensionPixelSizeMap.put(resId, value);
}
return value;
} catch (NotFoundException var2) {
return 0;
}
}
public static int getIdentifier(String name, String defType, String defPackTAGe) {
try {
return AppContext.getContext().getResources().getIdentifier(name, defType, defPackTAGe);
} catch (NotFoundException var4) {
return 0;
}
}
public static float getDimension(int resId) {
try {
return AppContext.getContext().getResources().getDimension(resId);
} catch (NotFoundException var2) {
return 0.0F;
}
}
public static float getFloat(int resId) {
try {
TypedValue outValue = new TypedValue();
AppContext.getContext().getResources().getValue(resId, outValue, true);
return outValue.getFloat();
} catch (NotFoundException var2) {
return 0.0F;
}
}
public static int getColor(int resId) {
try {
return AppContext.getContext().getResources().getColor(resId);
} catch (NotFoundException var2) {
return 0;
}
}
public static int getInt(int resId) {
try {
return AppContext.getContext().getResources().getInteger(resId);
} catch (NotFoundException var2) {
return 0;
}
}
public static Drawable getDrawable(int resId) {
try {
return AppContext.getContext().getResources().getDrawable(resId);
} catch (NotFoundException var2) {
return null;
}
}
public static String[] getStringArray(int resId) {
try {
return AppContext.getContext().getResources().getStringArray(resId);
} catch (NotFoundException var2) {
return new String[0];
}
}
public static int[] getIntegerArray(int resId) {
try {
return AppContext.getContext().getResources().getIntArray(resId);
} catch (NotFoundException var2) {
return new int[0];
}
}
public static ColorStateList getColorStateList(int resId) {
try {
return AppContext.getContext().getResources().getColorStateList(resId);
} catch (NotFoundException var2) {
return null;
}
}
public static boolean getBoolean(int resId) {
try {
return AppContext.getContext().getResources().getBoolean(resId);
} catch (NotFoundException var2) {
return false;
}
}
public static int dp2Px(float dp) {
float scale = AppContext.getContext().getResources().getDisplayMetrics().density;
return (int)(dp * scale + 0.5F);
}
public static int sp2Px(float sp) {
float scale = AppContext.getContext().getResources().getDisplayMetrics().scaledDensity;
return (int)(sp * scale + 0.5F);
}
public static int px2Dp(float px) {
float scale = AppContext.getContext().getResources().getDisplayMetrics().density;
return (int)(px / scale + 0.5F);
}
public static boolean isDirectionLTR() {
return getResources().getConfiguration().getLayoutDirection() == 0;
}
public static int getIdentifier(Context context, String name, String defType, String defPackTAGe) {
try {
return getResources(context).getIdentifier(name, defType, defPackTAGe);
} catch (NotFoundException var5) {
return 0;
}
}
public static int getColor(Context context, int resId) {
try {
return getResources(context).getColor(resId);
} catch (NotFoundException var3) {
return 0;
}
}
public static Drawable getDrawable(Context context, int resId) {
try {
return getResources(context).getDrawable(resId);
} catch (NotFoundException var3) {
return null;
}
}
public static String[] getStringArray(Context context, int resId) {
try {
return getResources(context).getStringArray(resId);
} catch (NotFoundException var3) {
return new String[0];
}
}
public static ColorStateList getColorStateList(Context context, int resId) {
try {
return getResources(context).getColorStateList(resId);
} catch (NotFoundException var3) {
return null;
}
}
public static boolean getBoolean(Context context, int resId) {
try {
return getResources(context).getBoolean(resId);
} catch (NotFoundException var3) {
return false;
}
}
public static DisplayMetrics getDisplayMetrics() {
return getResources().getDisplayMetrics();
}
public static Configuration getConfiguration() {
return getResources().getConfiguration();
}
public static int getDimensionPixelOffset(int resId) {
try {
return getResources().getDimensionPixelOffset(resId);
} catch (NotFoundException var2) {
return 0;
}
}
/**
* activity 判定以及转化
*/
public static FragmentActivity scanForActivity(Context cont) {
if (cont == null) {
return null;
} else if (cont instanceof FragmentActivity){
return (FragmentActivity)cont;
} else if (cont instanceof ContextWrapper) {
return scanForActivity(((ContextWrapper)cont).getBaseContext());
}
return null;
}
public static int getColor(String color) {
try {
return Color.parseColor(color);
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
}

View File

@@ -0,0 +1,144 @@
package com.pukka.jiecheng.utils;
import android.util.Base64;
import com.google.gson.Gson;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.UUID;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
/**
* 纯 JDK 实现的 RSA 工具类
* 不依赖任何第三方库
*/
public class RsaSignUtil {
// 加密算法
private static final String CIPHER_ALGORITHM = "RSA/ECB/PKCS1Padding";
// 密钥算法
private static final String KEY_ALGORITHM = "RSA";
/**
* 免登录接口加密demo
*
* @param args
*/
public static void main(String[] args) {
HashMap<String, String> signMap = new HashMap<>();
signMap.put("accesser", "ANNINGTEST");
signMap.put("businessLine", "4100450011967708592750001");
signMap.put("uid", "xiaoqiangtest1111111"); //此处需要传递安柠的用户的唯一标识
signMap.put("mac", "11:22:cc:dd:cc"); //安柠设备mac
signMap.put("nonce", UUID.randomUUID().toString().replace("-", "")); //注意:此处的nonce,需要和接口的url参数相同
signMap.put("timestamp", System.currentTimeMillis() + ""); //注意:此处的时间戳,需要和接口的url参数相同
signMap.put("serviceComboCode", "1100121010201665723750127");
// 使用 Gson 转为 JSON 字符串 注意:若无gson依赖,改为其它任意安全的json转化字符串方法均可
Gson gson = new Gson();
String encryptData = gson.toJson(signMap);
System.out.println("待加密字符串为:" + encryptData);
//公钥字符串,联系华视开发获取
String publicKeyStr = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoDFiT8rDa6ftkRCxO8GctdhOYsEcmyrwyy0IYZKperNNukZnRYG0ucmmZ1C+9NWvVdmzoV1ikl5gBjdWlmnBzRZxtlaUaygm9/TrFDzOHzNLrkLtz1HSuSxNVMoOIaaSOdBcGtehK220td4U1jJFsyGwX/nsNhrlZ7Nhg3iFsrhAJ/9V7FLSJSc4W7XEKA36L5MBj/urCZNVbK7VIlfTfZy5W0CA8IwzEOPClkzjDG6H/s2zW/acxkyWBcOOBtNuNPUtQrmHKB9HJc0oxlbEVxW9UZ0rEWyFm/JN1z2ItMw+8upmFKbCGTXW6/1kzRs9Nsgc3zAqUzTMvT9qlDoeqwIDAQAB";
String encrypt = encrypt(publicKeyStr, encryptData);
System.out.println("加密后的字符串为:" + encrypt);
}
/**
* 使用公钥加密字符串
*
* @param key 公钥
* @param plainText 明文字符串
* @return 加密后的Base64编码字符串
*/
public static String encrypt(PublicKey key, String plainText) {
try {
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key);
// 将字符串转换为字节数组
byte[] plainTextBytes = plainText.getBytes("UTF-8");
// 分段加密(RSA有加密数据长度限制)
int inputLen = plainTextBytes.length;
int maxEncryptBlock = 245; // 2048位密钥的最大加密块大小
byte[] encryptedData = new byte[0];
int offset = 0;
while (inputLen - offset > 0) {
if (inputLen - offset > maxEncryptBlock) {
byte[] encryptedBlock = cipher.doFinal(plainTextBytes, offset, maxEncryptBlock);
encryptedData = concatenateByteArrays(encryptedData, encryptedBlock);
offset += maxEncryptBlock;
} else {
byte[] encryptedBlock = cipher.doFinal(plainTextBytes, offset, inputLen - offset);
encryptedData = concatenateByteArrays(encryptedData, encryptedBlock);
offset = inputLen;
}
}
// 返回Base64编码的加密数据
return android.util.Base64.encodeToString(encryptedData, android.util.Base64.NO_WRAP);
} catch (NoSuchAlgorithmException | NoSuchPaddingException
| InvalidKeyException | IllegalBlockSizeException
| BadPaddingException | java.io.UnsupportedEncodingException e) {
throw new RuntimeException("加密失败", e);
}
}
/**
* 使用Base64编码的公钥字符串加密
*
* @param publicKeyStr Base64编码的公钥字符串
* @param plainText 明文字符串
* @return 加密后的Base64编码字符串
*/
public static String encrypt(String publicKeyStr, String plainText) {
try {
PublicKey publicKey = getPublicKeyFromString(publicKeyStr);
return encrypt(publicKey, plainText);
} catch (Exception e) {
throw new RuntimeException("加密失败", e);
}
}
/**
* 从Base64编码的字符串获取公钥
*
* @param publicKeyStr Base64编码的公钥字符串
* @return PublicKey对象
*/
public static PublicKey getPublicKeyFromString(String publicKeyStr) {
try {
byte[] keyBytes = android.util.Base64.decode(publicKeyStr, Base64.NO_WRAP);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
return keyFactory.generatePublic(keySpec);
} catch (Exception e) {
throw new RuntimeException("获取公钥失败", e);
}
}
/**
* 合并字节数组
*
* @param a 第一个字节数组
* @param b 第二个字节数组
* @return 合并后的字节数组
*/
private static byte[] concatenateByteArrays(byte[] a, byte[] b) {
byte[] result = new byte[a.length + b.length];
System.arraycopy(a, 0, result, 0, a.length);
System.arraycopy(b, 0, result, a.length, b.length);
return result;
}
}

View File

@@ -0,0 +1,339 @@
package com.pukka.jiecheng.utils;
import android.os.Handler;
import android.os.Looper;
import android.view.Gravity;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.StringRes;
import com.pukka.libcore.utils.ContextUtils;
/**
* Toast相关工具类
*/
public class ToastUtils {
private ToastUtils() {
throw new UnsupportedOperationException("u can't instantiate me...");
}
private static Toast sToast;
private static Handler sHandler = new Handler(Looper.getMainLooper());
private static boolean isJumpWhenMore;
/**
* 吐司初始化
*
* @param isJumpWhenMore 当连续弹出吐司时,是要弹出新吐司还是只修改文本内容
* <p>{@code true}: 弹出新吐司<br>{@code false}: 只修改文本内容</p>
* <p>如果为{@code false}的话可用来做显示任意时长的吐司</p>
*/
public static void init(boolean isJumpWhenMore) {
ToastUtils.isJumpWhenMore = isJumpWhenMore;
}
/**
* 安全地显示短时吐司
*
* @param text 文本
*/
public static void showShortToastSafe(final CharSequence text) {
sHandler.post(new Runnable() {
@Override
public void run() {
showToast(text, Toast.LENGTH_SHORT,Gravity.CENTER);
}
});
}
/**
* 安全地显示短时吐司
*
* @param resId 资源Id
*/
public static void showShortToastSafe(final @StringRes int resId) {
sHandler.post(new Runnable() {
@Override
public void run() {
showToast(resId, Toast.LENGTH_SHORT);
}
});
}
/**
* 安全地显示短时吐司
*
* @param resId 资源Id
* @param args 参数
*/
public static void showShortToastSafe(final @StringRes int resId, final Object... args) {
sHandler.post(new Runnable() {
@Override
public void run() {
showToast(resId, Toast.LENGTH_SHORT, args);
}
});
}
/**
* 安全地显示短时吐司
*
* @param format 格式
* @param args 参数
*/
public static void showShortToastSafe(final String format, final Object... args) {
sHandler.post(new Runnable() {
@Override
public void run() {
showToast(format, Toast.LENGTH_SHORT, args);
}
});
}
/**
* 安全地显示长时吐司
*
* @param text 文本
*/
public static void showLongToastSafe(final CharSequence text) {
sHandler.post(new Runnable() {
@Override
public void run() {
showToast(text, Toast.LENGTH_LONG,Gravity.CENTER);
}
});
}
/**
* 安全地显示长时吐司
*
* @param resId 资源Id
*/
public static void showLongToastSafe(final @StringRes int resId) {
sHandler.post(new Runnable() {
@Override
public void run() {
showToast(resId, Toast.LENGTH_LONG);
}
});
}
/**
* 安全地显示长时吐司
*
* @param resId 资源Id
* @param args 参数
*/
public static void showLongToastSafe(final @StringRes int resId, final Object... args) {
sHandler.post(new Runnable() {
@Override
public void run() {
showToast(resId, Toast.LENGTH_LONG, args);
}
});
}
/**
* 安全地显示长时吐司
*
* @param format 格式
* @param args 参数
*/
public static void showLongToastSafe(final String format, final Object... args) {
sHandler.post(new Runnable() {
@Override
public void run() {
showToast(format, Toast.LENGTH_LONG, args);
}
});
}
/**
* 显示短时吐司
*
* @param text 文本
*/
public static void showShortToast(CharSequence text) {
showToast(text, Toast.LENGTH_SHORT,Gravity.CENTER);
}
/**
*
*
* @param text 文本
*/
public static void showLongBottomToast(CharSequence text) {
sHandler.post(new Runnable() {
@Override
public void run() {
showBottomToast(text, Toast.LENGTH_SHORT);
}
});
}
/**
* 显示短时吐司
*
* @param resId 资源Id
*/
public static void showShortToast(@StringRes int resId) {
showToast(resId, Toast.LENGTH_SHORT);
}
/**
* 显示短时吐司
*
* @param resId 资源Id
* @param args 参数
*/
public static void showShortToast(@StringRes int resId, Object... args) {
showToast(resId, Toast.LENGTH_SHORT, args);
}
/**
* 显示短时吐司
*
* @param format 格式
* @param args 参数
*/
public static void showShortToast(String format, Object... args) {
showToast(format, Toast.LENGTH_SHORT, args);
}
/**
* 显示长时吐司
*
* @param text 文本
*/
public static void showLongToast(CharSequence text) {
showToast(text, Toast.LENGTH_LONG,Gravity.CENTER);
}
/**
* 显示长时吐司
*
* @param text 文本
*/
public static void showLongToast(CharSequence text,int gravity) {
showToast(text, Toast.LENGTH_LONG,gravity);
}
/**
* 显示长时吐司
*
* @param resId 资源Id
*/
public static void showLongToast(@StringRes int resId) {
showToast(resId, Toast.LENGTH_LONG);
}
/**
* 显示长时吐司
*
* @param resId 资源Id
* @param args 参数
*/
public static void showLongToast(@StringRes int resId, Object... args) {
showToast(resId, Toast.LENGTH_LONG, args);
}
/**
* 显示长时吐司
*
* @param format 格式
* @param args 参数
*/
public static void showLongToast(String format, Object... args) {
showToast(format, Toast.LENGTH_LONG, args);
}
/**
* 显示吐司
*
* @param resId 资源Id
* @param duration 显示时长
*/
private static void showToast(@StringRes int resId, int duration) {
showToast(ContextUtils.getContext().getResources().getText(resId).toString(), duration);
}
/**
* 显示吐司
*
* @param resId 资源Id
* @param duration 显示时长
* @param args 参数
*/
private static void showToast(@StringRes int resId, int duration, Object... args) {
showToast(String.format(ContextUtils.getContext().getResources().getString(resId), args), duration);
}
/**
* 显示吐司
*
* @param format 格式
* @param duration 显示时长
* @param args 参数
*/
private static void showToast(String format, int duration, Object... args) {
showToast(String.format(format, args), duration,Gravity.CENTER);
}
/**
* 显示吐司
*
* @param text 文本
* @param duration 显示时长
*/
private static void showToast(CharSequence text, int duration,int gravity) {
if (isJumpWhenMore) {
cancelToast();
}
if (sToast == null) {
sToast = Toast.makeText(ContextUtils.getContext(), text, duration);
//Android 11 空指针
// TextView tv = (TextView) sToast.getView().findViewById(android.R.id.message);
// tv.setTextSize(18);
sToast.setGravity(gravity, 0, gravity == Gravity.BOTTOM ? 90 : 0);
} else {
sToast.setText(text);
sToast.setDuration(duration);
}
sToast.show();
}
/**
* 显示吐司
*
* @param text 文本
* @param duration 显示时长
*/
private static void showBottomToast(CharSequence text, int duration) {
if (isJumpWhenMore) {
cancelToast();
}
if (sToast == null) {
sToast = Toast.makeText(ContextUtils.getContext(), text, duration);
// TextView tv = (TextView) sToast.getView().findViewById(android.R.id.message);
// tv.setTextSize(18);
// sToast.setGravity(Gravity., 0, 0);
} else {
sToast.setText(text);
sToast.setDuration(duration);
}
sToast.show();
}
/**
* 取消吐司显示
*/
public static void cancelToast() {
if (sToast != null) {
sToast.cancel();
sToast = null;
}
}
}

View File

@@ -0,0 +1,81 @@
package com.pukka.jiecheng.utils;
import android.content.Context;
import android.view.View;
import android.widget.TextView;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.view.ViewCompat;
import com.pukka.jiecheng.R;
/**
* Author: wxf
* Date: 2023/1/12 10:48
* Description:Top样式切换
*/
public class TopStyleUtils {
// 搜索、历史、订购、帮助 样式切换
public static void topStyleChange(Context context, View view, boolean focus) {
// ConstraintLayout constraintLayout = (ConstraintLayout) view;
// View icon = constraintLayout.getChildAt(0);
// icon.setSelected(focus);
// TextView textView = (TextView) constraintLayout.getChildAt(1);
if (focus) {
view.animate().scaleX(1.1f).scaleY(1.1f).setDuration(100).start();
// textView.setTextColor(context.getResources().getColor(R.color.index_top_text_focus));
// textView.getPaint().setFakeBoldText(true);
} else {
view.animate().scaleX(1.0f).scaleY(1.0f).setDuration(100).start();
// textView.setTextColor(context.getResources().getColor(R.color.index_top_text_nor));
// textView.getPaint().setFakeBoldText(false);
}
}
public static void topImageChange(View view, boolean focus) {
if (focus) {
ViewCompat.animate(view).scaleX(1.05f).scaleY(1.05f).setDuration(100).start();
} else {
ViewCompat.animate(view).scaleX(1.0f).scaleY(1.0f).setDuration(100).start();
}
}
public static void topImageChangeFilter(View view, boolean focus) {
if (focus) {
ViewCompat.animate(view).scaleX(1.02f).scaleY(1.02f).setDuration(100).start();
} else {
ViewCompat.animate(view).scaleX(1.0f).scaleY(1.0f).setDuration(100).start();
}
}
/**
* 一行的大模板 放大 防止焦点框外溢
* */
public static void topImageChange3(View view, boolean focus) {
if (focus) {
ViewCompat.animate(view).scaleX(1.02f).scaleY(1.05f).setDuration(100).start();
} else {
ViewCompat.animate(view).scaleX(1.0f).scaleY(1.0f).setDuration(100).start();
}
}
/**
* 对view放大1.05f
*
* @param view view
* @param focus focus
*/
public static void topImageChange2(View view, boolean focus) {
if (focus) {
ViewCompat.animate(view).scaleX(1.03f).scaleY(1.05f).setDuration(100).start();
} else {
ViewCompat.animate(view).scaleX(1.0f).scaleY(1.0f).setDuration(100).start();
}
}
public static void topImageChange4(View view, boolean focus) {
if (focus) {
ViewCompat.animate(view).scaleX(1.03f).scaleY(1.03f).setDuration(100).start();
} else {
ViewCompat.animate(view).scaleX(1.0f).scaleY(1.0f).setDuration(100).start();
}
}
}

View File

@@ -0,0 +1,87 @@
package com.pukka.jiecheng.utils;
import android.content.Context;
import android.view.ViewGroup;
import com.pukka.jiecheng.R;
import java.math.BigDecimal;
public class UiUtils {
/**
* 赛事卡片推荐位类型
*/
public static int POSITION_TYPE_SPORT_CARD = 1;
/**
* 把数字转化成Dimen文件里对应的值
*
* @param mContext
* @param number 原始数值
* @return
*/
public static int numberConvertDimenValue(Context mContext, int number) {
int dimenId = mContext.getResources().getIdentifier("qb_px_" + number, "dimen", mContext.getPackageName());
int value = mContext.getResources().getDimensionPixelSize(dimenId);
return value;
}
/**
* 重新设置宽高
*
* @param mContext
* @param width
* @param height
* @param params
*/
public static void resetLayoutParams(Context mContext, int width, int height, ViewGroup.LayoutParams params) {
resetLayoutParams(mContext,width,height,0,params);
}
/**
* 重新设置宽高
*
* @param mContext
* @param width
* @param height
* @param recommendedPositionType 推荐位类型 1赛事卡片 0 其他类型
* @param params
*/
public static void resetLayoutParams(Context mContext, int width, int height, int recommendedPositionType,ViewGroup.LayoutParams params) {
int realWith = UiUtils.numberConvertDimenValue(mContext, width);
int realHeight = UiUtils.numberConvertDimenValue(mContext, height);
double rate = UiUtils.divNum(realWith, realHeight);
//1赛事卡片推荐位固定高度22 其他类型23
int showHeight = mContext.getResources().getDimensionPixelSize(recommendedPositionType == POSITION_TYPE_SPORT_CARD ? R.dimen.qb_px_22 : R.dimen.qb_px_23);
params.width = (int) (rate * showHeight);
params.height = showHeight;
}
/**
* 提供精确的除法运算
*
* @param v1 被除数
* @param v2 除数
* @return 两个参数的商
*/
public static double divNum(int v1, int v2) {
try {
if (0 == v1 || 0 == v2) {
return 0;
}
BigDecimal b1 = BigDecimal.valueOf(v1);
BigDecimal b2 = BigDecimal.valueOf(v2);
return b1.divide(b2, 2, BigDecimal.ROUND_HALF_UP).doubleValue();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
return 0;
}
}

View File

@@ -0,0 +1,185 @@
package com.pukka.jiecheng.utils;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.pukka.jiecheng.R;
import com.pukka.jiecheng.index.IndexActivity;
import com.pukka.libcore.utils.LogUtils;
/**
* @Author luoyuhang
* @create 2022/1/5 0005 9:27
*/
public class UpdateDialog extends AlertDialog {
UpdateNoOnClickListener mUpdateNoOnClickListener;
ForceUpdateOnClickListener forceUpdateOnClickListener;
private Context mContext;
private TextView mUpdateNow;
private RelativeLayout two_rl;
private RelativeLayout err_rl;
private TextView err_btn;
private TextView text2;
private TextView text1;
private TextView mTemporaryNo;
private ProgressBar mProgressBar;
// private TextView mPercent;
public UpdateDialog(Context context) {
super(context);
this.mContext = context;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
getWindow().setBackgroundDrawableResource(android.R.color.transparent);
super.onCreate(savedInstanceState);
this.setContentView(R.layout.update_dialog);
setCancelable(false);
mUpdateNow = findViewById(R.id.tv_update_now);
err_rl = findViewById(R.id.err_rl);
err_btn = findViewById(R.id.err_btn);
two_rl = findViewById(R.id.two_rl);
text1 = findViewById(R.id.text1);
text2 = findViewById(R.id.text2);
mTemporaryNo = findViewById(R.id.tv_temporary_no);
mProgressBar = findViewById(R.id.id_progressBar);
// mPercent = findViewById(R.id.tv_percent);
mProgressBar.setVisibility(View.GONE);
text2.setOnClickListener(v -> {
if (null != forceUpdateOnClickListener) {
text2.setVisibility(View.GONE);
text1.setVisibility(View.GONE);
mProgressBar.setVisibility(View.VISIBLE);
// mPercent.setVisibility(View.VISIBLE);
forceUpdateOnClickListener.update(v);
}
});
err_btn.setOnClickListener(v->{
if (mContext instanceof IndexActivity){
((IndexActivity) mContext).killApp();
}
});
mUpdateNow.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
two_rl.setVisibility(View.GONE);
mProgressBar.setVisibility(View.VISIBLE);
// mPercent.setVisibility(View.VISIBLE);
if (mUpdateNoOnClickListener != null) {
mUpdateNoOnClickListener.update(arg0);
}
}
});
mTemporaryNo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
if (mUpdateNoOnClickListener != null) {
mUpdateNoOnClickListener.no(arg0);
}
dismiss();
}
});
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
CloudKeyCodeUtils.changeToKeyCode(keyCode);
if (keyCode == KeyEvent.KEYCODE_BACK) {
if ((text2.getVisibility() == View.VISIBLE || err_rl.getVisibility() == View.VISIBLE) && mContext !=null) {
if (mContext instanceof IndexActivity){
((IndexActivity) mContext).killApp();
return true;
}
}
}
return super.onKeyDown(keyCode, event);
}
public void setUpdateNoOnClickListener(UpdateNoOnClickListener onClickListener) {
this.mUpdateNoOnClickListener = onClickListener;
}
public void setForceUpdateOnClickListener(ForceUpdateOnClickListener forceUpdateOnClickListener) {
this.forceUpdateOnClickListener = forceUpdateOnClickListener;
}
public void showForceUpdate() {
text2.setVisibility(View.VISIBLE);
text1.setVisibility(View.VISIBLE);
err_rl.setVisibility(View.GONE);
text2.setFocusable(true);
text2.requestFocus();
}
public void showError() {
err_rl.setVisibility(View.VISIBLE);
hideUpdate();
text2.setVisibility(View.GONE);
text1.setVisibility(View.GONE);
mProgressBar.setVisibility(View.GONE);
// mPercent.setVisibility(View.GONE);
err_btn.requestFocus();
}
public void showUpdate() {
two_rl.setVisibility(View.VISIBLE);
err_rl.setVisibility(View.GONE);
mUpdateNow.setFocusable(true);
mTemporaryNo.setFocusable(true);
mUpdateNow.requestFocus();
}
public void hideUpdate() {
two_rl.setVisibility(View.GONE);
}
public void showProgressBar() {
mProgressBar.setVisibility(View.VISIBLE);
// mPercent.setVisibility(View.VISIBLE);
}
public ProgressBar getmProgressBar() {
return mProgressBar;
}
// public TextView getmPercent() {
// return mPercent;
// }
public void showDialog() {
show();
}
public interface ForceUpdateOnClickListener {
void update(View view);
}
public interface UpdateNoOnClickListener {
void update(View view);
void no(View view);
}
}

View File

@@ -0,0 +1,53 @@
package com.pukka.jiecheng.utils;
import android.app.AlertDialog;
import android.content.Context;
import android.os.Bundle;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.pukka.jiecheng.R;
import com.pukka.libcore.fresco.PkSimpleDraweeView;
/**
* @Author luoyuhang
* @create 2022/1/5 0005 9:27
*/
public class UpdateOtherDialog extends AlertDialog {
String mUrl;
private ProgressBar mProgressBar;
private PkSimpleDraweeView iv_icon;
private TextView tv_title;
public UpdateOtherDialog(Context context, String url) {
super(context);
this.mUrl = url;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
if (null != getWindow()) {
getWindow().setBackgroundDrawableResource(android.R.color.transparent);
}
super.onCreate(savedInstanceState);
this.setContentView(R.layout.update_other_dialog);
setCancelable(false);
mProgressBar = findViewById(R.id.progressBar);
tv_title = findViewById(R.id.tv_title);
iv_icon = findViewById(R.id.iv_icon);
}
public ProgressBar getmProgressBar() {
return mProgressBar;
}
public void setIcon(String iconStr) {
iv_icon.setImageURI(iconStr);
}
public void setTitle(String titleStr) {
tv_title.setText(titleStr);
}
}

View File

@@ -0,0 +1,71 @@
package com.pukka.jiecheng.utils;
import java.util.Map;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
public class UrlParamBuilder {
/**
* 使用Map拼接URL参数(基础版本)
* @param baseUrl 基础URL
* @param params 参数Map
* @return 拼接后的完整URL
*/
public static String buildUrl(String baseUrl, Map<String, String> params) {
if (params == null || params.isEmpty()) {
return baseUrl;
}
StringBuilder urlBuilder = new StringBuilder(baseUrl);
// 判断基础URL是否已包含问号
boolean hasQuery = baseUrl.contains("?");
for (Map.Entry<String, String> entry : params.entrySet()) {
if (entry.getKey() != null && entry.getValue() != null) {
if (!hasQuery) {
urlBuilder.append("?");
hasQuery = true;
} else {
urlBuilder.append("&");
}
urlBuilder.append(entry.getKey())
.append("=")
.append(entry.getValue());
}
}
return urlBuilder.toString();
}
/**
* 带URL编码的版本
*/
public static String buildUrlEncoded(String baseUrl, Map<String, String> params) {
if (params == null || params.isEmpty()) {
return baseUrl;
}
StringBuilder urlBuilder = new StringBuilder(baseUrl);
boolean hasQuery = baseUrl.contains("?");
for (Map.Entry<String, String> entry : params.entrySet()) {
if (entry.getKey() != null && entry.getValue() != null) {
if (!hasQuery) {
urlBuilder.append("?");
hasQuery = true;
} else {
urlBuilder.append("&");
}
urlBuilder.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8))
.append("=")
.append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8));
}
}
return urlBuilder.toString();
}
}

View File

@@ -0,0 +1,140 @@
package com.pukka.jiecheng.utils;
import android.view.View;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.pukka.jiecheng.R;
import com.pukka.jiecheng.widget.VideoContainer;
import com.pukka.libcore.utils.LogUtils;
/**
* @author wangkai
* @version [V1.0.0, 2023/7/7]
* @brief 计算VideoView是否完全可见辅助类
* @since V1.0.0
*/
public class VideoContainerHelper {
private VideoContainer mVideoContainer;
private VideoContainerHelper() {
}
private static class SingletonHolder {
private static final VideoContainerHelper INSTANCE = new VideoContainerHelper();
}
public static VideoContainerHelper getInstance() {
return SingletonHolder.INSTANCE;
}
private LinearLayoutManager mLinearLayoutManager;
/**
* 设置LinearLayoutManager,用于计算VideoView是否完全可见
*
* @param linearLayoutManager LinearLayoutManager
*/
public void setLinearLayoutManager(LinearLayoutManager linearLayoutManager) {
mLinearLayoutManager = linearLayoutManager;
LogUtils.d("setLinearLayoutManager:" + mLinearLayoutManager);
}
/**
* 计算VideoView是否完全可见
*/
public void calculateVideoVisible() {
if (mLinearLayoutManager == null) {
return;
}
LogUtils.d("calculateVideoVisible:" + mLinearLayoutManager);
mVideoContainer = null;
int firstPosition = mLinearLayoutManager.findFirstVisibleItemPosition();
int lastPosition = mLinearLayoutManager.findLastVisibleItemPosition();
int firstCplPosition = mLinearLayoutManager.findFirstCompletelyVisibleItemPosition();
int lastCplPosition = mLinearLayoutManager.findLastCompletelyVisibleItemPosition();
for (int pos = firstPosition; pos <= lastPosition; pos++) {
boolean isCompletelyVisible = pos >= firstCplPosition && pos <= lastCplPosition;
LogUtils.d("wcg, calculateVideoVisible isCompletelyVisible: " + isCompletelyVisible);
View itemView = mLinearLayoutManager.findViewByPosition(pos);
LogUtils.d("pos: " + pos);
LogUtils.d("itemView: " + itemView);
if (itemView != null) {
VideoContainer videoContainer = itemView.findViewById(R.id.id_video_container);
if (videoContainer != null) {
videoContainer.onVisibilityChange(isCompletelyVisible);
if (isCompletelyVisible) {
mVideoContainer = videoContainer;
}
}
}
}
}
public void calculateVideoVisible(int pos){
if (mLinearLayoutManager == null) {
return;
}
LogUtils.d("calculateVideoVisible:" + mLinearLayoutManager);
mVideoContainer = null;
View itemView = mLinearLayoutManager.findViewByPosition(pos);
LogUtils.d("pos: " + pos);
LogUtils.d("itemView: " + itemView);
if (itemView != null) {
VideoContainer videoContainer = itemView.findViewById(R.id.id_video_container);
if (videoContainer != null) {
boolean isCompletelyVisible = true;
//videoContainer.onVisibilityChange(true);
if (isCompletelyVisible) {
mVideoContainer = videoContainer;
}
}
}
}
/**
* 是否为全屏
*
* @return 是否为全屏
*/
public boolean isFullScreen() {
return null != mVideoContainer && mVideoContainer.isFullScreen();
}
/**
* 退出全屏
*/
public void exitFullScreen() {
if (mVideoContainer != null) {
mVideoContainer.exitFullScreen();
}
}
/**
* 释放播放器
*/
public void release() {
if (mVideoContainer != null) {
mVideoContainer.release();
}
}
public void startPlay(){
if (mVideoContainer != null) {
mVideoContainer.startPlay();
}
}
public void releaseLayout(){
mLinearLayoutManager = null;
}
public void setVideoGetFocus(boolean videoGetFocus){
if (mVideoContainer != null) {
mVideoContainer.setVideoGetFocus(videoGetFocus);
}
}
}

View File

@@ -0,0 +1,89 @@
/**
* @file XCrashInitManager.java
* @description XCrash初始化管理,按权限优先使用jcCrash目录,失败时降级到应用私有目录
* @author ainetv
* @date 2026-03-26
*/
package com.pukka.jiecheng.utils;
import android.content.Context;
import android.os.Environment;
import android.text.TextUtils;
import com.pukka.libcore.utils.LogUtils;
import java.io.File;
import java.util.concurrent.atomic.AtomicBoolean;
import xcrash.XCrash;
public final class XCrashInitManager {
private static final String TAG = "XCrashInitManager";
private static final AtomicBoolean INITIALIZED = new AtomicBoolean(false);
private XCrashInitManager() {
}
public static void initWithStoragePermission(Context context) {
if (INITIALIZED.get()) {
LogUtils.d(TAG, "xcrash already initialized, skip");
return;
}
String preferredDir = buildPreferredDir(context);
if (TextUtils.isEmpty(preferredDir)) {
initWithFallback(context, "preferred dir unavailable");
return;
}
try {
ensureDir(preferredDir);
XCrash.init(context, new XCrash.InitParameters().setLogDir(preferredDir));
INITIALIZED.set(true);
LogUtils.i(TAG, "xcrash initialized with jcCrash dir: " + preferredDir);
} catch (Throwable throwable) {
LogUtils.e(TAG, "xcrash init jcCrash failed, fallback. reason: " + throwable.getMessage());
initWithFallback(context, "init jcCrash failed");
}
}
public static void initWithFallback(Context context, String reason) {
if (INITIALIZED.get()) {
LogUtils.d(TAG, "xcrash already initialized, skip fallback");
return;
}
String fallbackDir = buildFallbackDir(context);
try {
ensureDir(fallbackDir);
XCrash.init(context, new XCrash.InitParameters().setLogDir(fallbackDir));
INITIALIZED.set(true);
LogUtils.w(TAG, "xcrash fallback init success, reason: " + reason + ", dir: " + fallbackDir);
} catch (Throwable throwable) {
LogUtils.e(TAG, "xcrash fallback init failed: " + throwable.getMessage());
}
}
private static String buildPreferredDir(Context context) {
if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
return null;
}
File externalRoot = Environment.getExternalStorageDirectory();
if (externalRoot == null) {
return null;
}
return new File(externalRoot, "jcCrash").getAbsolutePath();
}
private static String buildFallbackDir(Context context) {
return new File(context.getFilesDir(), "jcCrash").getAbsolutePath();
}
private static void ensureDir(String path) {
File dir = new File(path);
if (dir.exists()) {
return;
}
boolean mkdirsSuccess = dir.mkdirs();
if (!mkdirsSuccess) {
throw new IllegalStateException("create dir failed: " + path);
}
}
}