diff --git a/winboll/build.gradle b/winboll/build.gradle index 548cc8b..774d29e 100644 --- a/winboll/build.gradle +++ b/winboll/build.gradle @@ -43,6 +43,12 @@ android { packagingOptions { doNotStrip "*/*/libmimo_1011.so" } + + sourceSets { + main { + jniLibs.srcDirs = ['libs'] // 若SO库放在libs目录下 + } + } } dependencies { @@ -63,6 +69,10 @@ dependencies { api 'io.github.medyo:android-about-page:2.0.0' // 网络连接类库 api 'com.squareup.okhttp3:okhttp:4.4.1' + // OkHttp网络请求 + implementation 'com.squareup.okhttp3:okhttp:3.14.9' + // FastJSON解析 + implementation 'com.alibaba:fastjson:1.2.76' // AndroidX 类库 api 'androidx.appcompat:appcompat:1.1.0' diff --git a/winboll/build.properties b/winboll/build.properties index 0bd08a6..dc0bb79 100644 --- a/winboll/build.properties +++ b/winboll/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Wed Dec 31 20:28:06 HKT 2025 +#Sun Jan 04 09:03:20 GMT 2026 stageCount=9 libraryProject= baseVersion=15.11 publishVersion=15.11.8 -buildCount=0 +buildCount=5 baseBetaVersion=15.11.9 diff --git a/winboll/libs/libWeWorkSpecSDK.so b/winboll/libs/libWeWorkSpecSDK.so new file mode 100644 index 0000000..d9f592b Binary files /dev/null and b/winboll/libs/libWeWorkSpecSDK.so differ diff --git a/winboll/src/main/AndroidManifest.xml b/winboll/src/main/AndroidManifest.xml index cfcf3fb..8236f00 100644 --- a/winboll/src/main/AndroidManifest.xml +++ b/winboll/src/main/AndroidManifest.xml @@ -278,6 +278,10 @@ + + + + - + \ No newline at end of file diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/MainActivity.java b/winboll/src/main/java/cc/winboll/studio/winboll/MainActivity.java index 24f4d2a..935b86f 100644 --- a/winboll/src/main/java/cc/winboll/studio/winboll/MainActivity.java +++ b/winboll/src/main/java/cc/winboll/studio/winboll/MainActivity.java @@ -1,6 +1,6 @@ package cc.winboll.studio.winboll; -import android.app.Activity; +import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Message; @@ -8,20 +8,17 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; +import cc.winboll.studio.libaes.activitys.AboutActivity; import cc.winboll.studio.libaes.activitys.DrawerFragmentActivity; -import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity; import cc.winboll.studio.libaes.models.APPInfo; import cc.winboll.studio.libaes.models.DrawerMenuBean; -import cc.winboll.studio.libaes.unittests.TestAButtonFragment; -import cc.winboll.studio.libaes.unittests.TestViewPageFragment; import cc.winboll.studio.libaes.utils.WinBoLLActivityManager; import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.winboll.R; import cc.winboll.studio.winboll.activities.SettingsActivity; +import cc.winboll.studio.winboll.activities.WXPayActivity; import cc.winboll.studio.winboll.fragments.BrowserFragment; import java.util.ArrayList; -import android.content.Intent; -import cc.winboll.studio.libaes.activitys.AboutActivity; public class MainActivity extends DrawerFragmentActivity { @@ -152,6 +149,8 @@ public class MainActivity extends DrawerFragmentActivity { } } else if (nItemId == R.id.item_settings) { WinBoLLActivityManager.getInstance().startWinBoLLActivity(getApplicationContext(), SettingsActivity.class); + } else if (nItemId == R.id.item_wxpayactivity) { + WinBoLLActivityManager.getInstance().startWinBoLLActivity(getApplicationContext(), WXPayActivity.class); } else if (nItemId == cc.winboll.studio.libaes.R.id.item_about) { Intent intent = new Intent(getApplicationContext(), AboutActivity.class); APPInfo appInfo = genDefaultAPPInfo(); diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/WxPayConfig.java b/winboll/src/main/java/cc/winboll/studio/winboll/WxPayConfig.java new file mode 100644 index 0000000..69c7493 --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/WxPayConfig.java @@ -0,0 +1,28 @@ +package cc.winboll.studio.winboll; + +/** + * 微信支付配置类 + * @Author ZhanGSKen + * @Date 2026/01/07 + */ +public class WxPayConfig { + // ========== 核心修改点:替换为你的服务端地址 ========== + // 服务端IP/域名 + 端口(Docker部署的服务端,需确保安卓端可访问) + public static final String BASE_URL = "https://wxpay.winboll.cc"; + + // 统一下单接口路径(对应服务端的测试接口) + public static final String CREATE_ORDER_URL = BASE_URL + "/pay/createOrder"; + + // 订单查询接口路径 + public static final String QUERY_ORDER_URL = BASE_URL + "/pay/queryOrder"; + + // ========== 固定支付配置 ========== + public static final String ORDER_BODY = "定额测试支付"; // 商品描述 + public static final int TOTAL_FEE = 1; // 固定金额:1分(沙箱环境推荐) + public static final String TRADE_TYPE = "NATIVE"; // 支付类型:二维码 + + // ========== 轮询配置 ========== + public static final long POLL_INTERVAL = 10000; // 轮询间隔:10秒 + public static final long POLL_TIMEOUT = 45000; // 轮询超时:45秒 +} + diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/activities/WXPayActivity.java b/winboll/src/main/java/cc/winboll/studio/winboll/activities/WXPayActivity.java new file mode 100644 index 0000000..e881993 --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/activities/WXPayActivity.java @@ -0,0 +1,211 @@ +package cc.winboll.studio.winboll.activities; + +import android.app.Activity; +import android.graphics.Bitmap; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.view.View; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; +import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity; +import cc.winboll.studio.winboll.R; +import cc.winboll.studio.winboll.WxPayConfig; +import cc.winboll.studio.winboll.utils.SpecUtil; +import cc.winboll.studio.winboll.utils.WxPayApi; +import cc.winboll.studio.winboll.utils.ZXingUtils; +import java.util.Timer; +import java.util.TimerTask; + + + +/** + * 主界面:生成二维码 + 轮询查询支付结果 + * @Author ZhanGSKen + * @Date 2026/01/07 + */ +public class WXPayActivity extends WinBoLLActivity implements IWinBoLLActivity { + + private static final String TAG = "WXPayActivity"; + + // Handler消息标识 + private static final int MSG_POLL_TIMEOUT = 1001; + private static final int MSG_POLL_SUCCESS = 1002; + private static final int MSG_POLL_FAILED = 1003; + + private ImageView mIvQrCode; + private TextView mTvOrderNo; + private TextView mTvPayStatus; + private Button mBtnCreateOrder; + + private String mOutTradeNo; // 商户订单号 + private Timer mPollTimer; // 轮询定时器 + private long mPollStartTime; // 轮询开始时间 + + + + @Override + public Activity getActivity() { + return this; + } + + @Override + public String getTag() { + return TAG; + } + + private Handler mPollHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + super.handleMessage(msg); + switch (msg.what) { + case MSG_POLL_TIMEOUT: + stopPoll(); + mTvPayStatus.setText("支付状态:轮询超时"); + mTvPayStatus.setTextColor(getResources().getColor(android.R.color.darker_gray)); + Toast.makeText(WXPayActivity.this, "轮询超时,请手动查询", Toast.LENGTH_SHORT).show(); + break; + case MSG_POLL_SUCCESS: + boolean isPaySuccess = (boolean) msg.obj; + String tradeState = (String) msg.getData().getString("tradeState"); + stopPoll(); + if (isPaySuccess) { + mTvPayStatus.setText("支付状态:支付成功 ✅"); + mTvPayStatus.setTextColor(getResources().getColor(android.R.color.holo_green_dark)); + Toast.makeText(WXPayActivity.this, "支付成功!", Toast.LENGTH_SHORT).show(); + } else { + mTvPayStatus.setText("支付状态:" + tradeState); + mTvPayStatus.setTextColor(getResources().getColor(android.R.color.holo_red_dark)); + } + break; + case MSG_POLL_FAILED: + String errorMsg = (String) msg.obj; + mTvPayStatus.setText("查询失败:" + errorMsg); + break; + } + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_wxpay); + + initView(); + initListener(); + } + + private void initView() { + mIvQrCode = findViewById(R.id.iv_qrcode); + mTvOrderNo = findViewById(R.id.tv_order_no); + mTvPayStatus = findViewById(R.id.tv_pay_status); + mBtnCreateOrder = findViewById(R.id.btn_create_order); + } + + private void initListener() { + mBtnCreateOrder.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + createOrder(); + } + }); + } + + /** + * 统一下单,生成二维码 + */ + private void createOrder() { + mBtnCreateOrder.setEnabled(false); + mTvPayStatus.setText("支付状态:生成订单中..."); + mIvQrCode.setImageBitmap(null); + + WxPayApi.createOrder(new WxPayApi.OnCreateOrderCallback() { + @Override + public void onSuccess(String outTradeNo, String codeUrl) { + mOutTradeNo = outTradeNo; + mTvOrderNo.setText("商户订单号:" + outTradeNo); + mTvPayStatus.setText("支付状态:未支付,请扫码"); + + // 生成二维码 + Bitmap qrCodeBitmap = ZXingUtils.createQRCodeBitmap(codeUrl, 250, 250); + if (qrCodeBitmap != null) { + mIvQrCode.setImageBitmap(qrCodeBitmap); + // 开始轮询查询支付结果 + startPoll(); + } else { + mTvPayStatus.setText("支付状态:生成二维码失败"); + mBtnCreateOrder.setEnabled(true); + } + } + + @Override + public void onFailure(String errorMsg) { + SpecUtil.WWSpecLogError(TAG, "统一下单失败:" + errorMsg); + mTvPayStatus.setText("生成订单失败:" + errorMsg); + mBtnCreateOrder.setEnabled(true); + Toast.makeText(WXPayActivity.this, errorMsg, Toast.LENGTH_SHORT).show(); + } + }); + } + + /** + * 开始轮询查询支付结果 + */ + private void startPoll() { + stopPoll(); // 先停止之前的轮询 + mPollStartTime = System.currentTimeMillis(); + mPollTimer = new Timer(); + mPollTimer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + // 检查是否超时 + long elapsedTime = System.currentTimeMillis() - mPollStartTime; + if (elapsedTime >= WxPayConfig.POLL_TIMEOUT) { + mPollHandler.sendEmptyMessage(MSG_POLL_TIMEOUT); + return; + } + + // 查询订单状态 + WxPayApi.queryOrder(mOutTradeNo, new WxPayApi.OnQueryOrderCallback() { + @Override + public void onSuccess(boolean isPaySuccess, String tradeState) { + Message msg = Message.obtain(); + msg.what = MSG_POLL_SUCCESS; + msg.obj = isPaySuccess; + Bundle bundle = new Bundle(); + bundle.putString("tradeState", tradeState); + msg.setData(bundle); + mPollHandler.sendMessage(msg); + } + + @Override + public void onFailure(String errorMsg) { + Message msg = Message.obtain(); + msg.what = MSG_POLL_FAILED; + msg.obj = errorMsg; + mPollHandler.sendMessage(msg); + } + }); + } + }, 0, WxPayConfig.POLL_INTERVAL); + } + + /** + * 停止轮询 + */ + private void stopPoll() { + if (mPollTimer != null) { + mPollTimer.cancel(); + mPollTimer = null; + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + stopPoll(); + } +} + diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/unittest/TestWeWorkSpecSDK.java b/winboll/src/main/java/cc/winboll/studio/winboll/unittest/TestWeWorkSpecSDK.java new file mode 100644 index 0000000..5fdac27 --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/unittest/TestWeWorkSpecSDK.java @@ -0,0 +1,311 @@ +package cc.winboll.studio.winboll.unittest; + +import android.app.Activity; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.view.View; +import android.widget.Button; +import android.widget.Toast; +import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity; +import cc.winboll.studio.libappbase.LogUtils; +import cc.winboll.studio.winboll.R; +import cc.winboll.studio.winboll.activities.WinBoLLActivity; + +/** + * @Author 豆包&ZhanGSKen + * @Date 2026/01/03 10:52 + * @Describe 企业微信SDK接口测试(基础调试版) + * 包含:SDK初始化、基础接口调用、日志输出、主线程回调处理 + */ +public class TestWeWorkSpecSDK extends WinBoLLActivity implements IWinBoLLActivity, View.OnClickListener { + + public static final String TAG = "TestWeWorkSpecSDK"; + + // ------------------- 企业微信SDK配置常量(需替换为实际项目参数) ------------------- + // 企业微信 CorpID(从企业微信管理后台获取) + private static final String CORP_ID = "wwb37c73f34c722852"; + // 应用 AgentID(从企业微信应用管理后台获取) + private static final String AGENT_ID = "your_agent_id_here"; + // 应用 Secret(从企业微信应用管理后台获取,注意保密) + private static final String APP_SECRET = "your_app_secret_here"; + + // ------------------- Handler消息标识(主线程处理SDK回调) ------------------- + private static final int MSG_SDK_INIT_SUCCESS = 1001; + private static final int MSG_SDK_INIT_FAILED = 1002; + private static final int MSG_GET_CORP_INFO_SUCCESS = 1003; + private static final int MSG_GET_CORP_INFO_FAILED = 1004; + + // ------------------- 控件声明 ------------------- + private Button mBtnInitSDK; + private Button mBtnGetCorpInfo; + private Button mBtnCheckAuth; + + // ------------------- 主线程Handler(处理SDK异步回调) ------------------- + private Handler mWeWorkHandler; + + + @Override + public Activity getActivity() { + return this; + } + + @Override + public String getTag() { + return TAG; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_test_weworkspecsdk); + + // 初始化控件 + initViews(); + // 绑定点击事件 + initEvents(); + // 初始化Handler(主线程处理回调,更新UI) + initHandler(); + // 初始化SDK(可选:启动时自动初始化,或点击按钮初始化) + // initWeWorkSDK(); + } + + /** + * 初始化控件(Java 7 显式绑定) + */ + private void initViews() { + mBtnInitSDK = (Button) findViewById(R.id.btn_init_sdk); + mBtnGetCorpInfo = (Button) findViewById(R.id.btn_get_corp_info); + mBtnCheckAuth = (Button) findViewById(R.id.btn_check_auth); + } + + /** + * 绑定点击事件(Java 7 匿名内部类) + */ + private void initEvents() { + mBtnInitSDK.setOnClickListener(this); + mBtnGetCorpInfo.setOnClickListener(this); + mBtnCheckAuth.setOnClickListener(this); + } + + /** + * 初始化主线程Handler(处理SDK异步回调,安全更新UI) + */ + private void initHandler() { + mWeWorkHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + super.handleMessage(msg); + switch (msg.what) { + case MSG_SDK_INIT_SUCCESS: + showToast("企业微信SDK初始化成功"); + LogUtils.d(TAG, "SDK初始化成功"); + break; + case MSG_SDK_INIT_FAILED: + String initError = (String) msg.obj; + showToast("SDK初始化失败:" + initError); + LogUtils.e(TAG, "SDK初始化失败:" + initError); + break; + case MSG_GET_CORP_INFO_SUCCESS: + String corpInfo = (String) msg.obj; + showToast("获取企业信息成功"); + LogUtils.d(TAG, "企业信息:" + corpInfo); + break; + case MSG_GET_CORP_INFO_FAILED: + String corpError = (String) msg.obj; + showToast("获取企业信息失败:" + corpError); + LogUtils.e(TAG, "获取企业信息失败:" + corpError); + break; + default: + break; + } + } + }; + } + + // ------------------- 企业微信SDK核心接口调用 ------------------- + + /** + * 初始化企业微信SDK(异步操作,通过Handler回调结果) + */ + private void initWeWorkSDK() { + showToast("开始初始化企业微信SDK..."); + // 模拟SDK异步初始化(实际项目中替换为企业微信SDK的真实初始化接口) + new Thread(new Runnable() { + @Override + public void run() { + try { + // 真实SDK初始化逻辑示例: + // WeWorkSDK.init(TestWeWorkSpecSDK.this, CORP_ID, AGENT_ID, new WeWorkSDKCallback() { + // @Override + // public void onSuccess() { + // mWeWorkHandler.sendEmptyMessage(MSG_SDK_INIT_SUCCESS); + // } + // + // @Override + // public void onFailure(String errorMsg) { + // Message msg = Message.obtain(); + // msg.what = MSG_SDK_INIT_FAILED; + // msg.obj = errorMsg; + // mWeWorkHandler.sendMessage(msg); + // } + // }); + + // 调试模拟:休眠1秒,模拟异步初始化 + Thread.sleep(1000); + // 模拟初始化成功(如需测试失败,替换为发送MSG_SDK_INIT_FAILED) + mWeWorkHandler.sendEmptyMessage(MSG_SDK_INIT_SUCCESS); + // 模拟初始化失败 + // Message msg = Message.obtain(); + // msg.what = MSG_SDK_INIT_FAILED; + // msg.obj = "CorpID或AgentID错误"; + // mWeWorkHandler.sendMessage(msg); + } catch (InterruptedException e) { + e.printStackTrace(); + Message msg = Message.obtain(); + msg.what = MSG_SDK_INIT_FAILED; + msg.obj = "线程中断:" + e.getMessage(); + mWeWorkHandler.sendMessage(msg); + } + } + }).start(); + } + + /** + * 获取企业基本信息(异步操作,需先初始化SDK) + */ + private void getCorpInfo() { + if (!isSDKInitialized()) { + showToast("请先初始化SDK"); + return; + } + showToast("开始获取企业信息..."); + // 模拟SDK异步获取企业信息(实际项目中替换为真实接口) + new Thread(new Runnable() { + @Override + public void run() { + try { + // 真实SDK接口示例: + // WeWorkSDK.getCorpInfo(APP_SECRET, new CorpInfoCallback() { + // @Override + // public void onSuccess(CorpInfo info) { + // Message msg = Message.obtain(); + // msg.what = MSG_GET_CORP_INFO_SUCCESS; + // msg.obj = "企业名称:" + info.getCorpName() + ",企业ID:" + info.getCorpId(); + // mWeWorkHandler.sendMessage(msg); + // } + // + // @Override + // public void onFailure(String errorMsg) { + // Message msg = Message.obtain(); + // msg.what = MSG_GET_CORP_INFO_FAILED; + // msg.obj = errorMsg; + // mWeWorkHandler.sendMessage(msg); + // } + // }); + + // 调试模拟:休眠1秒,模拟异步获取 + Thread.sleep(1000); + // 模拟获取成功 + Message successMsg = Message.obtain(); + successMsg.what = MSG_GET_CORP_INFO_SUCCESS; + successMsg.obj = "企业名称:WinBoLL Studio,企业ID:" + CORP_ID; + mWeWorkHandler.sendMessage(successMsg); + // 模拟获取失败 + // Message failMsg = Message.obtain(); + // failMsg.what = MSG_GET_CORP_INFO_FAILED; + // failMsg.obj = "AppSecret错误或权限不足"; + // mWeWorkHandler.sendMessage(failMsg); + } catch (InterruptedException e) { + e.printStackTrace(); + Message msg = Message.obtain(); + msg.what = MSG_GET_CORP_INFO_FAILED; + msg.obj = "线程中断:" + e.getMessage(); + mWeWorkHandler.sendMessage(msg); + } + } + }).start(); + } + + /** + * 检查当前用户是否已授权(同步操作,示例) + */ + private void checkAuthStatus() { + if (!isSDKInitialized()) { + showToast("请先初始化SDK"); + return; + } + // 真实SDK接口示例: + // boolean isAuthorized = WeWorkSDK.isAuthorized(); + // 调试模拟:默认返回true + boolean isAuthorized = true; + + if (isAuthorized) { + showToast("用户已授权"); + LogUtils.d(TAG, "当前用户已授权企业微信应用"); + } else { + showToast("用户未授权,请先授权"); + LogUtils.d(TAG, "当前用户未授权企业微信应用"); + // 真实项目中可调用授权接口: + // WeWorkSDK.requestAuth(TestWeWorkSpecSDK.this, new AuthCallback() { + // @Override + // public void onSuccess(String code) { + // showToast("授权成功,code:" + code); + // } + // + // @Override + // public void onFailure(String errorMsg) { + // showToast("授权失败:" + errorMsg); + // } + // }); + } + } + + // ------------------- 工具方法 ------------------- + + /** + * 检查SDK是否已初始化(模拟方法,实际项目中替换为SDK的真实状态检查) + */ + private boolean isSDKInitialized() { + // 真实SDK可通过静态方法检查状态: + // return WeWorkSDK.isInitialized(); + // 调试模拟:假设Handler不为空即表示已初始化 + return mWeWorkHandler != null; + } + + /** + * 显示Toast提示(Java 7 简化封装) + */ + private void showToast(String msg) { + Toast.makeText(this, msg, Toast.LENGTH_SHORT).show(); + } + + // ------------------- 点击事件处理 ------------------- + + @Override + public void onClick(View v) { + int id = v.getId(); + if (id == R.id.btn_init_sdk) { + initWeWorkSDK(); + } else if (id == R.id.btn_get_corp_info) { + getCorpInfo(); + } else if (id == R.id.btn_check_auth) { + checkAuthStatus(); + } + } + + // ------------------- 生命周期管理 ------------------- + + @Override + protected void onDestroy() { + super.onDestroy(); + // 释放Handler资源,避免内存泄漏 + if (mWeWorkHandler != null) { + mWeWorkHandler.removeCallbacksAndMessages(null); + mWeWorkHandler = null; + } + // 真实SDK需调用销毁方法: + // WeWorkSDK.destroy(); + } +} + diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/utils/OkHttpUtil.java b/winboll/src/main/java/cc/winboll/studio/winboll/utils/OkHttpUtil.java new file mode 100644 index 0000000..4e23dcb --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/utils/OkHttpUtil.java @@ -0,0 +1,83 @@ +package cc.winboll.studio.winboll.utils; + +import android.os.Handler; +import android.os.Looper; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +/** + * OkHttp网络请求工具类 + * @Author ZhanGSKen + * @Date 2026/01/07 + */ +public class OkHttpUtil { + + private static OkHttpClient sOkHttpClient; + private static Handler sMainHandler = new Handler(Looper.getMainLooper()); + + static { + sOkHttpClient = new OkHttpClient.Builder() + .connectTimeout(10, TimeUnit.SECONDS) + .readTimeout(10, TimeUnit.SECONDS) + .writeTimeout(10, TimeUnit.SECONDS) + .build(); + } + + /** + * GET请求 + * @param url 请求地址 + * @param callback 回调 + */ + public static void get(String url, final OnResultCallback callback) { + Request request = new Request.Builder() + .url(url) + .get() + .build(); + + sOkHttpClient.newCall(request).enqueue(new Callback() { + @Override + public void onFailure(Call call, final IOException e) { + sMainHandler.post(new Runnable() { + @Override + public void run() { + if (callback != null) { + callback.onFailure(e.getMessage()); + } + } + }); + } + + @Override + public void onResponse(Call call, final Response response) throws IOException { + final String result = response.body().string(); + sMainHandler.post(new Runnable() { + @Override + public void run() { + if (callback != null) { + if (response.isSuccessful()) { + callback.onSuccess(result); + } else { + callback.onFailure("请求失败:" + response.code()); + } + } + } + }); + } + }); + } + + /** + * 回调接口 + */ + public interface OnResultCallback { + void onSuccess(String result); + void onFailure(String errorMsg); + } +} + diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/utils/SpecUtil.java b/winboll/src/main/java/cc/winboll/studio/winboll/utils/SpecUtil.java new file mode 100644 index 0000000..279f286 --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/utils/SpecUtil.java @@ -0,0 +1,26 @@ +package cc.winboll.studio.winboll.utils; + +import cc.winboll.studio.libappbase.LogUtils; + +/** + * 日志工具类(适配项目规范) + * @Author ZhanGSKen + * @Date 2026/01/07 + */ +public class SpecUtil { + + private static final boolean isDebug = true; + + public static void WWSpecLogInfo(String tag, String msg) { + if (isDebug) { + LogUtils.i(tag, msg); + } + } + + public static void WWSpecLogError(String tag, String msg) { + if (isDebug) { + LogUtils.e(tag, msg); + } + } +} + diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/utils/WxPayApi.java b/winboll/src/main/java/cc/winboll/studio/winboll/utils/WxPayApi.java new file mode 100644 index 0000000..dd0b508 --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/utils/WxPayApi.java @@ -0,0 +1,100 @@ +package cc.winboll.studio.winboll.utils; + +import cc.winboll.studio.winboll.WxPayConfig; +import com.alibaba.fastjson.JSONObject; + +/** + * 微信支付服务端接口封装 + * @Author ZhanGSKen + * @Date 2026/01/07 + */ +public class WxPayApi { + + /** + * 统一下单(生成二维码) + * @param callback 回调 + */ + public static void createOrder(final OnCreateOrderCallback callback) { + // 拼接请求参数(服务端测试接口需支持GET传参,若为POST需修改为表单/JSON) + String url = WxPayConfig.CREATE_ORDER_URL + + "?body=" + WxPayConfig.ORDER_BODY + + "&totalFee=" + WxPayConfig.TOTAL_FEE + + "&tradeType=" + WxPayConfig.TRADE_TYPE; + + OkHttpUtil.get(url, new OkHttpUtil.OnResultCallback() { + @Override + public void onSuccess(String result) { + try { + JSONObject jsonObject = JSONObject.parseObject(result); + String outTradeNo = jsonObject.getString("out_trade_no"); + String codeUrl = jsonObject.getString("code_url"); + if (callback != null) { + callback.onSuccess(outTradeNo, codeUrl); + } + } catch (Exception e) { + if (callback != null) { + callback.onFailure("解析统一下单结果失败:" + e.getMessage()); + } + } + } + + @Override + public void onFailure(String errorMsg) { + if (callback != null) { + callback.onFailure("统一下单请求失败:" + errorMsg); + } + } + }); + } + + /** + * 订单查询 + * @param outTradeNo 商户订单号 + * @param callback 回调 + */ + public static void queryOrder(String outTradeNo, final OnQueryOrderCallback callback) { + String url = WxPayConfig.QUERY_ORDER_URL + "?outTradeNo=" + outTradeNo; + + OkHttpUtil.get(url, new OkHttpUtil.OnResultCallback() { + @Override + public void onSuccess(String result) { + try { + JSONObject jsonObject = JSONObject.parseObject(result); + String tradeState = jsonObject.getString("trade_state"); + boolean isSuccess = "SUCCESS".equals(tradeState); + if (callback != null) { + callback.onSuccess(isSuccess, tradeState); + } + } catch (Exception e) { + if (callback != null) { + callback.onFailure("解析订单查询结果失败:" + e.getMessage()); + } + } + } + + @Override + public void onFailure(String errorMsg) { + if (callback != null) { + callback.onFailure("订单查询请求失败:" + errorMsg); + } + } + }); + } + + /** + * 统一下单回调接口 + */ + public interface OnCreateOrderCallback { + void onSuccess(String outTradeNo, String codeUrl); + void onFailure(String errorMsg); + } + + /** + * 订单查询回调接口 + */ + public interface OnQueryOrderCallback { + void onSuccess(boolean isPaySuccess, String tradeState); + void onFailure(String errorMsg); + } +} + diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/utils/ZXingUtils.java b/winboll/src/main/java/cc/winboll/studio/winboll/utils/ZXingUtils.java new file mode 100644 index 0000000..389298d --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/utils/ZXingUtils.java @@ -0,0 +1,75 @@ +package cc.winboll.studio.winboll.utils; + +import android.graphics.Bitmap; +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.qrcode.QRCodeWriter; +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; +import com.journeyapps.barcodescanner.BarcodeEncoder; +import java.util.HashMap; +import java.util.Map; + +/** + * ZXing二维码生成工具类 + * 依赖:com.google.zxing:core:3.4.1 + com.journeyapps:zxing-android-embedded:3.6.0 + * @Author ZhanGSKen + * @Date 2026/01/07 + */ +public class ZXingUtils { + + /** + * 生成二维码Bitmap(核心方法,使用journeyapps工具类) + * @param content 内容(如微信支付的code_url) + * @param width 二维码宽度(px) + * @param height 二维码高度(px) + * @return 二维码Bitmap,失败返回null + */ + public static Bitmap createQRCodeBitmap(String content, int width, int height) { + // 1. 入参合法性校验 + if (content == null || content.trim().isEmpty()) { + return null; + } + if (width <= 0 || height <= 0) { + return null; + } + + // 2. 配置二维码参数 + Map hints = new HashMap<>(); + hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); // 字符编码 + hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); // 高容错级别(H级可容忍30%遮挡) + hints.put(EncodeHintType.MARGIN, 1); // 边距(值越小,二维码越紧凑,建议1-4) + + try { + // 3. 生成BitMatrix(二维码矩阵) + QRCodeWriter qrCodeWriter = new QRCodeWriter(); + BitMatrix bitMatrix = qrCodeWriter.encode( + content, + BarcodeFormat.QR_CODE, + width, + height, + hints + ); + + // 4. 转换BitMatrix为Bitmap(关键:使用journeyapps的BarcodeEncoder) + BarcodeEncoder barcodeEncoder = new BarcodeEncoder(); + return barcodeEncoder.createBitmap(bitMatrix); + + } catch (WriterException e) { + e.printStackTrace(); + return null; + } + } + + /** + * 重载方法:生成正方形二维码(宽度=高度) + * @param content 内容 + * @param size 二维码边长(px) + * @return 二维码Bitmap + */ + public static Bitmap createQRCodeBitmap(String content, int size) { + return createQRCodeBitmap(content, size, size); + } +} + diff --git a/winboll/src/main/java/com/tencent/wework/SpecCallbackSDK.java b/winboll/src/main/java/com/tencent/wework/SpecCallbackSDK.java new file mode 100644 index 0000000..18faee5 --- /dev/null +++ b/winboll/src/main/java/com/tencent/wework/SpecCallbackSDK.java @@ -0,0 +1,210 @@ +package com.tencent.wework; + +import java.util.Map; +import java.util.HashMap;; + +/** + * @warning: 1. 不要修改成员变量名,native方法内有反射调用 + * 2. 调用本地方法需保持包结构,本工具需放在包com.tencent.wework内 + * 3. 不允许继承,类名和函数名均不可修改,会影响本地方法的引用,详见:javah生成本地方法头文件 + */ +public final class SpecCallbackSDK { + + /** + * @description 调用本地方法后实例化的对象指针 + */ + private long specCallbackSDKptr = 0; + + public long GetPtr() { return specCallbackSDKptr; } + + /** + * @description: 回包的headers + */ + private Map responseHeaders; + + public Map GetResponseHeaders() { return responseHeaders; } + + /** + * @description: 回包的加密后的body + */ + private String responseBody; + + public String GetResponseBody() { return responseBody; } + + /** + * @description: 每个请求构造一个SpecCallbackSDK示例, + * SpecCallbackSDK仅持有headers和body的引用, + * 因此需保证headers和body的生存期比SpecCallbackSDK长 + * @param method: 请求方法GET/POST + * @param headers: 请求header + * @param body: 请求body + * @example: + * SpecCallbackSDK sdk = new SpecCallbackSDK(method, headers, body); + * if (sdk.IsOk()) { + * String corpid = sdk.GetCorpId(); + * String agentid = sdk.GetAgentId(); + * String call_type = sdk.GetCallType(); + * String data = sdk.GetData(); + * //do something... + * } + * String response = ...; + * sdk.BuildResponseHeaderBody(response); + * Map responseHeaders = sdk.GetResponseHeaders(); + * String body = sdk.GetResponseBody(); + * //do response + * + * @return errorcode 示例如下: + * -920001: 未设置请求方法 + * -920002: 未设置请求header + * -920003: 未设置请求body + * */ + public SpecCallbackSDK(String method, Map headers, String body) { + try { + specCallbackSDKptr = NewCallbackSDK(method, headers, body); + } catch (Exception e) { + SpecUtil.WWSpecLogError("SpecCallbackSDK exception caught", e.getMessage()); + } + } + + private native long NewCallbackSDK(String method, Map headers, String body); + + /** + * @usage 在Java对象的内存回收前析构C++对象 + */ + @Override + protected void finalize() throws Throwable { + DeleteCPPInstance(specCallbackSDKptr); + super.finalize(); + } + + private native void DeleteCPPInstance(long specCallbackSDKptr); + + /** + * @description: 判断构造函数中传入的请求是否解析成功 + * @return: 成功与否 + * */ + public boolean IsOk() { + return IsOk(specCallbackSDKptr); + } + + private native boolean IsOk(long specCallbackSDKptr); + + /** + * @description: 获取请求的企业 + * @require: 仅当IsOk() == true可调用 + * @return: corpid + * */ + public String GetCorpId() { + return GetCorpId(specCallbackSDKptr); + } + + private native String GetCorpId(long specCallbackSDKptr); + + /** + * @description: 获取请求的应用 + * @require: 仅当IsOk() == true可调用 + * @return: agentid + * */ + public long GetAgentId() { + return GetAgentId(specCallbackSDKptr); + } + + private native long GetAgentId(long specCallbackSDKptr); + + /** + * @description: 获取请求的类型 + * @require: 仅当IsOk() == true可调用 + * @return: 1 - 来自[应用调用专区]的请求 + * 2 - 来自企业微信的回调事件 + * */ + public long GetCallType() { + return GetCallType(specCallbackSDKptr); + } + + private native long GetCallType(long specCallbackSDKptr); + + /** + * @description: 获取请求数据 + * @require: 仅当IsOk() == true可调用 + * @return: 请求数据,根据call_type可能是: + * - 企业微信回调事件 + * - [应用调用专区]接口中的request_data + * */ + public String GetData() { + return GetData(specCallbackSDKptr); + } + + private native String GetData(long specCallbackSDKptr); + + /** + * @description: 是否异步请求 + * @require: 仅当IsOk() == true可调用 + * @return: 是否异步请求 + * */ + public boolean GetIsAsync() { + return GetIsAsync(specCallbackSDKptr); + } + + private native boolean GetIsAsync(long specCallbackSDKptr); + + /** + * @description: 获取请求的job_info, + * @require: 仅当IsOk() == true可调用 + * @return: job_info,无需理解内容, + * 在同一个请求上下文中使用SpecSDK的时候传入 + * */ + public String GetJobInfo() { + return GetJobInfo(specCallbackSDKptr); + } + + private native String GetJobInfo(long specCallbackSDKptr); + + /** + * @description: 获取请求的ability_id,[应用调用专区]接口时指定 + * @require: 仅当IsOk() == true可调用 + * @return: ability_id + * */ + public String GetAbilityId() { + return GetAbilityId(specCallbackSDKptr); + } + + private native String GetAbilityId(long specCallbackSDKptr); + + /** + * @description: 获取请求的notify_id,用于[应用同步调用专区程序]接口 + * @require: 仅当IsOk() == true可调用 + * @return: notify_id + * */ + public String GetNotifyId() { + return GetNotifyId(specCallbackSDKptr); + } + + private native String GetNotifyId(long specCallbackSDKptr); + + /** + * @description: 对返回包计算签名&加密 + * @param response: 待加密的回包明文.如果IsOk()==false,传入空串即可 + * @note 本接口的执行问题可查看日志 + * */ + public void BuildResponseHeaderBody(String response) { + try { + responseHeaders = new HashMap(); + responseBody = ""; + BuildResponseHeaderBody(specCallbackSDKptr, response); + } catch (Exception e) { + SpecUtil.WWSpecLogError("SpecCallbackSDK exception caught", e.getMessage()); + } + } + + private native void BuildResponseHeaderBody(long specCallbackSDKptr, String response); + + // 静态代码块内还无法调用native日志函数,这里的日志在管理系统无法查询 + static { + try { + Class.forName("com.tencent.wework.SpecUtil"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + System.exit(1); + } + } +} diff --git a/winboll/src/main/java/com/tencent/wework/SpecSDK.java b/winboll/src/main/java/com/tencent/wework/SpecSDK.java new file mode 100644 index 0000000..8cf555a --- /dev/null +++ b/winboll/src/main/java/com/tencent/wework/SpecSDK.java @@ -0,0 +1,163 @@ +package com.tencent.wework; + +/** + * @warning: 1. 不要修改成员变量名,native方法内有反射调用 + * 2. 调用本地方法需保持包结构,本工具需放在包com.tencent.wework内 + * 3. 不允许继承,类名和函数名均不可修改,会影响本地方法的引用,详见:javah生成本地方法头文件 + */ +public final class SpecSDK { + + /** + * @description 调用本地方法后实例化的对象指针 + */ + private long specSDKptr = 0; + + /** + * @usage invoke的请求 + * @example "{\"limit\":1} + */ + private String request; + + public void SetRequest(String request) { + this.request = request; + } + + /** + * @usage 访问上一次invoke的结果 + */ + private String response; + + public String GetResponse() { + return response; + } + + /** + * @param corpid: 企业corpid,必选参数 + * @param agentid: 应用id,必选参数 + * @param ability_id: 能力ID,可选参数 + * @param job_info: job_info,可选参数 + * */ + public SpecSDK(String corpId, long agentId) { + specSDKptr = NewSDK1(corpId, agentId); + } + + private native long NewSDK1(String corpId, long agentId); + + public SpecSDK(String corpId, long agentId, String abilityId) { + specSDKptr = NewSDK2(corpId, agentId, abilityId); + } + + private native long NewSDK2(String corpId, long agentId, String abilityId); + + public SpecSDK(String corpId, long agentId, String abilityId, String jobInfo) { + specSDKptr = NewSDK3(corpId, agentId, abilityId, jobInfo); + } + + private native long NewSDK3(String corpId, long agentId, String abilityId, String jobInfo); + + /** + * @description 使用callback的请求来初始化 + * @param callback_sdk: 要求IsOk()==true + * @return C++内部指针,创建失败时指针仍为0,并输出错误日志 + * */ + public SpecSDK(SpecCallbackSDK callbackSDK) { + specSDKptr = NewSDK4(callbackSDK.GetPtr()); + } + + private native long NewSDK4(long callbackSDK); + + /** + * @usage 在Java对象的内存回收前析构C++对象 + */ + @Override + protected void finalize() throws Throwable { + DeleteCPPInstance(specSDKptr); + super.finalize(); + } + + private native void DeleteCPPInstance(long specSDKptr); + + /** + * @description 用于在专区内调用企业微信接口 + * @param api_name 接口名 + * @param request json格式的请求数据 + * @param response json格式的返回数据 + * @return errorcode 参考如下: + * 0: 成功 + * -910001: SDK没有初始化 + * -910002: 没有设置请求体 + * -910003: 没有设置请求的API + * -910004: 在SDK成员内找不到成员"response",注意lib内有反射机制,不要修改成员变量名 + * -910005: 使用未初始化的callback初始化SDK + * -910006: invoke调用失败,应检查日志查看具体原因 + * -910007: 响应体为空 + * @note 当返回0时,表示没有网络或请求协议层面或调用方法的失败, + * 调用方需继续检查response中的errcode字段确保业务层面的成功 + * + * @usage 当前版本sdk支持的接口列表,每个接口的具体协议请查看企业微信文档: + * https://developer.work.weixin.qq.com/document/path/91201 + * + * +--------------------------------+--------------------------------+ + * |接口名 |描述 | + * |--------------------------------|--------------------------------| + * |program_async_job_call_back |上报异步任务结果 | + * |sync_msg |获取会话记录 | + * |get_group_chat |获取内部群信息 | + * |get_agree_status_single |获取单聊会话同意情况 | + * |get_agree_status_room |获取群聊会话同意情况 | + * |set_hide_sensitiveinfo_config |设置成员会话组件敏感信息隐藏配置| + * |get_hide_sensitiveinfo_config |获取成员会话组件敏感信息隐藏配置| + * |search_chat |会话名称搜索 | + * |search_msg |会话消息搜索 | + * |create_rule |新增关键词规则 | + * |get_rule_list |获取关键词列表 | + * |get_rule_detail |获取关键词规则详情 | + * |update_rule |修改关键词规则 | + * |delete_rule |删除关键词规则 | + * |get_hit_msg_list |获取命中关键词规则的会话记录 | + * |create_sentiment_task |创建情感分析任务 | + * |get_sentiment_result |获取情感分析结果 | + * |create_summary_task |创建摘要提取任务 | + * |get_summary_result |获取摘要提取结果 | + * |create_customer_tag_task |创建标签匹配任务 | + * |get_customer_tag_result |获取标签任务结果 | + * |create_recommend_dialog_task |创建话术推荐任务 | + * |get_recommend_dialog_result |获取话术推荐结果 | + * |create_private_task |创建自定义模型任务 | + * |get_private_task_result |获取自定义模型结果 | + * |(废弃)document_list |获取知识集列表 | + * |create_spam_task |会话反垃圾创建分析任务 | + * |get_spam_result |会话反垃圾获取任务结果 | + * |create_chatdata_export_job |创建会话内容导出任务 | + * |get_chatdata_export_job_status |获取会话内容导出任务结果 | + * |spec_notify_app |专区通知应用 | + * |create_program_task |创建自定义程序任务 | + * |get_program_task_result |获取自定义程序结果 | + * |knowledge_base_list |获取企业授权给应用的知识集列表 | + * |knowledge_base_create |创建知识集 | + * |knowledge_base_detail |获取知识集详情 | + * |knowledge_base_add_doc |添加知识集內容 | + * |knowledge_base_remove_doc |删除知识集內容 | + * |knowledge_base_modify_name |修改知识集名称 | + * |knowledge_base_delete |删除知识集 | + * |search_contact_or_customer |员工或者客户名称搜索 | + * |create_ww_model_task |创建企微通用模型任务 | + * |get_ww_model_result |获取企微通用模型结果 | + * |get_msg_list_by_page_id |page_id获取消息列表 | + * +-----------------------------------------------------------------+ + * */ + public int Invoke(String apiName) { + return Invoke(specSDKptr, apiName, request); + } + + private native int Invoke(long sdk, String apiName, String request); + + // 静态代码块内还无法调用native日志函数,这里的日志在管理系统无法查询 + static { + try { + Class.forName("com.tencent.wework.SpecUtil"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } +} diff --git a/winboll/src/main/java/com/tencent/wework/SpecUtil.java b/winboll/src/main/java/com/tencent/wework/SpecUtil.java new file mode 100644 index 0000000..85ea23b --- /dev/null +++ b/winboll/src/main/java/com/tencent/wework/SpecUtil.java @@ -0,0 +1,171 @@ +package com.tencent.wework; + +//import java.lang.management.ManagementFactory; +//import java.lang.management.RuntimeMXBean; + +/** + * @warning: 1. 不要修改成员变量名,native方法内有反射调用 + * 2. 调用本地方法需保持包结构,本工具需放在包com.tencent.wework内 + * 3. 不允许继承,类名和函数名均不可修改,会影响本地方法的引用,详见:javah生成本地方法头文件 + * 4. 使用其他工具打印的日志将无法被查询,如需使用SLF4j风格的日志或性能更好的日志框架, + * 请自行封装SpecUtil.SpecLog或SpecUtil.SpecLogNative方法 + * + * @usage: 1. 获取SDK的版本号 + * 2. 打印三个级别的日志 + * 3. 开启调试模式 + */ +public final class SpecUtil { + + /** + * @description SDK版本号 + * @usage 可用于校对不同SDK版本,或后续针对不同的SDK版本添加业务逻辑 + */ + private static final String SDK_VERSION = "1.4.0"; + + public static String GetSDKVersion() { + return SDK_VERSION; + } + + /** + * @description 正确的包名,SDK必须存放在"com.tencent.wework"下,否则会影响本地方法的调用 + */ + private static final String EXPECTED_PACKAGE_NAME = "com.tencent.wework"; + + public static String GetExpectedPackageName() { + return EXPECTED_PACKAGE_NAME; + } + + private static final String LINE_SEPERATOR = System.getProperty("line.separator"); + + public static void WWSpecLogInfo(String... args) { + SpecLog('I', args); + } + + public static void WWSpecLogError(String... args) { + SpecLog('E', args); + } + + public static void WWSpecLogDebug(String... args) { + SpecLog('D', args); + } + + public static void WWSpecLogInfoWithReqId(String reqId, String... args) { + SpecLogWithReqId(reqId, 'I', args); + } + + public static void WWSpecLogErrorWithReqId(String reqId, String... args) { + SpecLogWithReqId(reqId, 'E', args); + } + + public static void WWSpecLogDebugWithReqId(String reqId, String... args) { + SpecLogWithReqId(reqId, 'D', args); + } + + /** + * @usage 打印标准日志 + * @note 只有使用SpecLog和SpecLogNative函数打印的日志能被调试平台查询,其他框架的日志仅能本地查看 + * @param logLevel 日志级别,使用char传递,目前支持I——INFO、E——ERROR、D——DEBUG + * @param args 自定义参数 + */ + public static void SpecLog(char logLevel, String... args) { + StackTraceElement element = Thread.currentThread().getStackTrace()[3]; + SpecLogNative( + logLevel, + element.getFileName(), + element.getLineNumber(), + String.join(",", args).replace(LINE_SEPERATOR, " ") + ); + } + + /** + * @usage 打印标准日志 + * @note 只有使用SpecLog和SpecLogNative函数打印的日志能被调试平台查询,其他框架的日志仅能本地查看 + * @param reqid 请求id + * @param logLevel 日志级别,使用char传递,目前支持I——INFO、E——ERROR、D——DEBUG + * @param args 自定义参数 + */ + public static void SpecLogWithReqId(String reqId, char logLevel, String... args) { + StackTraceElement element = Thread.currentThread().getStackTrace()[3]; + SpecLogNativeWithReqId( + reqId, + logLevel, + element.getFileName(), + element.getLineNumber(), + String.join(",", args).replace(LINE_SEPERATOR, " ") + ); + } + + /** + * @usage 打印标准日志 + * @note 只有使用SpecLog和SpecLogNative函数打印的日志能被调试平台查询,其他框架的日志仅能本地查看 + * 如需SLF4J风格的接口或对日志性能有进一步需求,开发者可以自行封装该函数 + * @param logLevel 日志级别,使用char传递,目前支持I——INFO、E——ERROR、D——DEBUG + * @param fileName 文件名(类名) + * @param lineNumber 行号 + * @param argsString 自定义参数 + */ + public static native void SpecLogNative(char logLevel, String fileName, int lineNumber, String argsString); + + /** + * @usage 打印标准日志 + * @note 只有使用SpecLog和SpecLogNative函数打印的日志能被调试平台查询,其他框架的日志仅能本地查看 + * 如需SLF4J风格的接口或对日志性能有进一步需求,开发者可以自行封装该函数 + * @param reqid 请求id + * @param logLevel 日志级别,使用char传递,目前支持I——INFO、E——ERROR、D——DEBUG + * @param fileName 文件名(类名) + * @param lineNumber 行号 + * @param argsString 自定义参数 + */ + public static native void SpecLogNativeWithReqId(String reqId, char logLevel, String fileName, int lineNumber, String argsString); + + + + /** + * @usage 开启调试模式,进程级别开关 + * @param debugToken 调试凭证,在管理端获取 + * @param accessToken 应用access token + * @return 是否开启成功 + */ + public static boolean SpecOpenDebugMode(String debugToken, String accessToken) { + return SpecOpenDebugModeNative(debugToken, accessToken); + } + + private static native boolean SpecOpenDebugModeNative(String debugToken, String accessToken); + + /** + * @usage 生成notify id。用户可调用本接口生成notify id,也可完全自定义生成 + * @return 新的notify id,支持纳秒级隔离,内部异常时会输出日志并返回空串 + * @note 1. 用户可先生成notify id,将其与回调数据关联存储后,再使用该notify id通知应用, + * 从而保证回调数据被请求时已存储完毕 + */ + public static String GenerateNotifyId() { + return GenerateNotifyIdNative(); + } + + private static native String GenerateNotifyIdNative(); + + static { + // 检查包名 + String packageName = SpecUtil.class.getPackage().getName(); + if (!EXPECTED_PACKAGE_NAME.equals(packageName)) { + // 静态代码块内还无法调用native日志函数,这里的日志在管理系统无法查询 + System.out.println("SpecUtil class must be in package com.tencent.wework"); + System.exit(1); + } + + // 加载so库 + try { + System.loadLibrary("WeWorkSpecSDK"); + } catch (UnsatisfiedLinkError e) { + System.out.println("libWeWorkSpecSDK.so not found in java.library.path"); + e.printStackTrace(); + System.exit(1); + } catch (Exception e) { + System.out.println("unexpected exception: " + e.getMessage()); + e.printStackTrace(); + System.exit(1); + } + + SpecUtil.WWSpecLogInfo("SDK init done", "packageName=" + packageName, "SDK_VERSION=" + SDK_VERSION); + } +} diff --git a/winboll/src/main/res/layout/activity_test_weworkspecsdk.xml b/winboll/src/main/res/layout/activity_test_weworkspecsdk.xml new file mode 100644 index 0000000..b1448e8 --- /dev/null +++ b/winboll/src/main/res/layout/activity_test_weworkspecsdk.xml @@ -0,0 +1,30 @@ + + + +