Compare commits
18 Commits
powerbell-
...
powerbell-
| Author | SHA1 | Date | |
|---|---|---|---|
| d03c105ccd | |||
| 5d58b3b20d | |||
| 32864c72b1 | |||
| a490ab3ff5 | |||
| a5c5cc91e1 | |||
| 5a9d068fc0 | |||
| cce4a643e7 | |||
| 8405097573 | |||
| 3d69d4da09 | |||
| c98685440d | |||
| 7183338c97 | |||
| 61cfa7f3ff | |||
| cb9ad1dc57 | |||
| e18ed1b0fd | |||
| 98c334f442 | |||
| 9d42a3e1e9 | |||
| 411af44303 | |||
| 9afb072351 |
87
.github/workflows/android.yml
vendored
87
.github/workflows/android.yml
vendored
@@ -1,87 +0,0 @@
|
||||
name: Android CI
|
||||
|
||||
# 触发器
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- *-beta
|
||||
pull_request:
|
||||
tags:
|
||||
- *-beta
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# 设置 JDK 环境
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: set up JDK 11
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'temurin'
|
||||
cache: gradle
|
||||
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
|
||||
# 获取应用打包秘钥库
|
||||
- name: Checkout Android Keystore
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: zhangsken/keystore # 存储应用打包用的 keystore 的仓库(格式:用户名/仓库名)
|
||||
token: ${{ secrets.APP_SECRET_TOKEN_1 }} # 连接仓库的 token , 需要单独配置
|
||||
path: keystore # 仓库的根目录名
|
||||
|
||||
# 打包 Stage Release 版本应用
|
||||
- name: Build with Gradle
|
||||
run: bash ./gradlew assembleBetaRelease
|
||||
# 创建release
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.APP_SECRET_TOKEN_1 }}
|
||||
# GitHub 会自动创建 GITHUB_TOKEN 密码以在工作流程中使用。
|
||||
# 您可以使用 GITHUB_TOKEN 在工作流程运行中进行身份验证。
|
||||
# 当您启用 GitHub Actions 时,GitHub 在您的仓库中安装 GitHub 应用程序。
|
||||
# GITHUB_TOKEN 密码是一种 GitHub 应用程序 安装访问令牌。
|
||||
# 您可以使用安装访问令牌代表仓库中安装的 GitHub 应用程序 进行身份验证。
|
||||
# 令牌的权限仅限于包含您的工作流程的仓库。 更多信息请参阅“GITHUB_TOKEN 的权限”。
|
||||
# 在每个作业开始之前, GitHub 将为作业提取安装访问令牌。 令牌在作业完成后过期。
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Release ${{ github.ref }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
|
||||
# 获取 APK 版本号
|
||||
- name: Get Version Name
|
||||
uses: actions/github-script@v3
|
||||
id: get-version
|
||||
with:
|
||||
script: |
|
||||
const str=process.env.GITHUB_REF;
|
||||
return str.substring(str.indexOf("v"));
|
||||
result-encoding: string
|
||||
# 上传至 Release 的资源
|
||||
- name: Upload Release Asset
|
||||
id: upload-release-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.APP_SECRET_TOKEN_1 }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }} # 上传网址,无需改动
|
||||
#asset_path: app/build/outputs/apk/release/app-release.apk # 上传路径(Release)
|
||||
asset_path: app/build/outputs/apk/beta/release/app-beta-release.apk # 上传路径(WinBoll Stage Release)
|
||||
asset_name: WinBoll-${{steps.get-version.outputs.result}}0.apk # 资源名
|
||||
asset_content_type: application/vnd.android.package-archiv # 资源类型
|
||||
|
||||
# 存档打包的文件
|
||||
- name: Archive production artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: build
|
||||
path: app/build/outputs # 将打包之后的文件全部上传(里面会有混淆的 map 文件)
|
||||
@@ -64,6 +64,11 @@ android {
|
||||
dimension "WinBoLLApp"
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
// 应用包输出配置
|
||||
//
|
||||
|
||||
@@ -100,12 +100,15 @@ allprojects {
|
||||
}
|
||||
|
||||
subprojects {
|
||||
// 1. 对纯 Java 模块的 JavaCompile 任务配置(升级为 Java 11)
|
||||
tasks.withType(JavaCompile) {
|
||||
options.compilerArgs << "-parameters"
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
// 可选:确保编码一致
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
|
||||
@@ -19,12 +19,12 @@ def genVersionName(def versionName){
|
||||
|
||||
android {
|
||||
|
||||
// 1. compileSdkVersion:必须 ≥ targetSdkVersion,建议直接等于 targetSdkVersion(30)
|
||||
compileSdkVersion 30
|
||||
|
||||
// 2. buildToolsVersion:需匹配 compileSdkVersion,建议使用 30.x.x 最新稳定版(无需高于 compileSdkVersion)
|
||||
buildToolsVersion "30.0.3" // 这是 30 对应的最新稳定版,避免使用 beta 版
|
||||
// 关键:改为你已安装的 SDK 32(≥ targetSdkVersion 30,兼容已安装环境)
|
||||
compileSdkVersion 32
|
||||
|
||||
// 直接使用已安装的构建工具 33.0.3(无需修改)
|
||||
buildToolsVersion "33.0.3"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "cc.winboll.studio.powerbell"
|
||||
minSdkVersion 23
|
||||
@@ -83,12 +83,12 @@ dependencies {
|
||||
//api 'androidx.fragment:fragment:1.1.0'
|
||||
|
||||
// WinBoLL库 nexus.winboll.cc 地址
|
||||
//api 'cc.winboll.studio:libaes:15.12.7'
|
||||
//api 'cc.winboll.studio:libappbase:15.12.2'
|
||||
api 'cc.winboll.studio:libaes:15.12.12'
|
||||
api 'cc.winboll.studio:libappbase:15.14.2'
|
||||
|
||||
// WinBoLL备用库 jitpack.io 地址
|
||||
api 'com.github.ZhanGSKen:AES:aes-v15.12.8'
|
||||
api 'com.github.ZhanGSKen:APPBase:appbase-v15.14.1'
|
||||
//api 'com.github.ZhanGSKen:AES:aes-v15.12.9'
|
||||
//api 'com.github.ZhanGSKen:APPBase:appbase-v15.14.1'
|
||||
|
||||
//api fileTree(dir: 'libs', include: ['*.aar'])
|
||||
api fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Wed Dec 31 20:53:46 HKT 2025
|
||||
stageCount=46
|
||||
#Tue Jan 06 19:10:13 HKT 2026
|
||||
stageCount=49
|
||||
libraryProject=
|
||||
baseVersion=15.14
|
||||
publishVersion=15.14.45
|
||||
publishVersion=15.14.48
|
||||
buildCount=0
|
||||
baseBetaVersion=15.14.46
|
||||
baseBetaVersion=15.14.49
|
||||
|
||||
@@ -15,19 +15,21 @@ import cc.winboll.studio.powerbell.utils.NotificationManagerUtils;
|
||||
import cc.winboll.studio.powerbell.views.MemoryCachedBackgroundView;
|
||||
|
||||
/**
|
||||
* 应用全局入口类(适配Android API 30,基于Java 7编写)
|
||||
* 应用全局入口类
|
||||
* 适配:Java7 语法规范 | Android API30 系统版本
|
||||
* 核心策略:极致强制缓存 - 无论内存紧张程度,永不自动清理任何缓存(Bitmap/视图控件/路径记录)
|
||||
*
|
||||
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
|
||||
* @Version 1.0.0
|
||||
* @Date 2025-12-29
|
||||
* @Date 2025-12-29 15:30:00
|
||||
* @LastModified 2026-01-02 19:01:00
|
||||
*/
|
||||
public class App extends GlobalApplication {
|
||||
|
||||
// ==================== 常量定义 ====================
|
||||
|
||||
// ====================================== 常量区 - 置顶排序 (按功能归类) ======================================
|
||||
// 基础日志TAG
|
||||
private static final String TAG = "App";
|
||||
// 缓存保护专用TAG
|
||||
private static final String CACHE_PROTECT_TAG = "FORCE_CACHE_PROTECT";
|
||||
// 电池无效值常量(修复拼写错误:INVALID_BATTERY_VALUE)
|
||||
private static final int INVALID_BATTERY_VALUE = -1;
|
||||
|
||||
// 组件跳转常量
|
||||
@@ -40,268 +42,278 @@ public class App extends GlobalApplication {
|
||||
public static final String ACTION_SWITCHTO_CN1 = "cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN1";
|
||||
public static final String ACTION_SWITCHTO_CN2 = "cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN2";
|
||||
|
||||
// ==================== 静态属性 ====================
|
||||
|
||||
// ====================================== 静态属性区 - 全局单例/状态 (按核心程度排序) ======================================
|
||||
// 应用单例
|
||||
private static App sApp;
|
||||
|
||||
// 配置与缓存
|
||||
// 配置与缓存工具 (全局单例)
|
||||
public static AppConfigUtils sAppConfigUtils;
|
||||
private static AppCacheUtils sAppCacheUtils;
|
||||
|
||||
// 资源与视图缓存(强制驻留)
|
||||
// 资源与视图缓存 (强制驻留,极致缓存核心)
|
||||
public static BackgroundSourceUtils sBackgroundSourceUtils;
|
||||
public static BitmapCacheUtils sBitmapCacheUtils;
|
||||
private static MemoryCachedBackgroundView sMemoryCachedBackgroundView;
|
||||
|
||||
// 状态
|
||||
// 系统状态 (电池电量)
|
||||
public static volatile int sQuantityOfElectricity = INVALID_BATTERY_VALUE;
|
||||
|
||||
// 系统工具
|
||||
// 系统工具 (通知管理器)
|
||||
private static NotificationManagerUtils sNotificationManagerUtils;
|
||||
|
||||
// ==================== 成员属性 ====================
|
||||
|
||||
// ====================================== 成员属性区 - 非静态成员 (广播接收器) ======================================
|
||||
private GlobalApplicationReceiver mGlobalReceiver;
|
||||
|
||||
// ==================== 公共静态方法 (工具/单例) ====================
|
||||
|
||||
// ====================================== 公共静态方法 - 单例/工具获取 (对外入口) ======================================
|
||||
/**
|
||||
* 获取应用单例
|
||||
* 获取应用全局单例实例
|
||||
* @return 应用单例App实例
|
||||
*/
|
||||
public static App getInstance() {
|
||||
LogUtils.d(TAG, "getInstance() called | Result: " + sApp);
|
||||
LogUtils.d(TAG, "【getInstance】应用单例获取方法调用 | 当前实例:" + sApp);
|
||||
return sApp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置工具实例
|
||||
* 获取配置工具类单例实例
|
||||
* @param context 上下文对象
|
||||
* @return 配置工具类AppConfigUtils实例
|
||||
*/
|
||||
public static AppConfigUtils getAppConfigUtils(Context context) {
|
||||
String contextName = context != null ? context.getClass().getSimpleName() : "null";
|
||||
LogUtils.d(TAG, "getAppConfigUtils() called with: context = [" + contextName + "]");
|
||||
String contextClass = context != null ? context.getClass().getSimpleName() : "null";
|
||||
LogUtils.d(TAG, "【getAppConfigUtils】配置工具获取方法调用 | 入参Context类型:" + contextClass);
|
||||
|
||||
if (sAppConfigUtils == null) {
|
||||
sAppConfigUtils = AppConfigUtils.getInstance(context);
|
||||
LogUtils.d(TAG, "getAppConfigUtils: Initialized new instance");
|
||||
LogUtils.d(TAG, "【getAppConfigUtils】配置工具实例为空,已初始化新实例");
|
||||
}
|
||||
return sAppConfigUtils;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存工具实例
|
||||
* 获取缓存工具类单例实例
|
||||
* @param context 上下文对象
|
||||
* @return 缓存工具类AppCacheUtils实例
|
||||
*/
|
||||
public static AppCacheUtils getAppCacheUtils(Context context) {
|
||||
String contextName = context != null ? context.getClass().getSimpleName() : "null";
|
||||
LogUtils.d(TAG, "getAppCacheUtils() called with: context = [" + contextName + "]");
|
||||
String contextClass = context != null ? context.getClass().getSimpleName() : "null";
|
||||
LogUtils.d(TAG, "【getAppCacheUtils】缓存工具获取方法调用 | 入参Context类型:" + contextClass);
|
||||
|
||||
if (sAppCacheUtils == null) {
|
||||
sAppCacheUtils = AppCacheUtils.getInstance(context);
|
||||
LogUtils.d(TAG, "getAppCacheUtils: Initialized new instance");
|
||||
LogUtils.d(TAG, "【getAppCacheUtils】缓存工具实例为空,已初始化新实例");
|
||||
}
|
||||
return sAppCacheUtils;
|
||||
}
|
||||
|
||||
// ==================== 公共成员方法 (业务) ====================
|
||||
|
||||
// ====================================== 公共成员方法 - 业务逻辑 (实例方法) ======================================
|
||||
/**
|
||||
* 清除电池历史数据
|
||||
*/
|
||||
public void clearBatteryHistory() {
|
||||
LogUtils.d(TAG, "clearBatteryHistory() called");
|
||||
LogUtils.d(TAG, "【clearBatteryHistory】清除电池历史数据方法调用");
|
||||
if (sAppCacheUtils != null) {
|
||||
sAppCacheUtils.clearBatteryHistory();
|
||||
LogUtils.d(TAG, "clearBatteryHistory: Success");
|
||||
LogUtils.d(TAG, "【clearBatteryHistory】电池历史数据清除成功");
|
||||
} else {
|
||||
LogUtils.w(TAG, "clearBatteryHistory: Failed, sAppCacheUtils is null");
|
||||
LogUtils.w(TAG, "【clearBatteryHistory】电池历史数据清除失败 | 缓存工具实例sAppCacheUtils为空");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动清理所有缓存(仅主动调用生效)
|
||||
*/
|
||||
public static void manualClearAllCache() {
|
||||
LogUtils.w(CACHE_PROTECT_TAG, "manualClearAllCache() called - Manual trigger only");
|
||||
|
||||
if (sBitmapCacheUtils != null) {
|
||||
sBitmapCacheUtils.clearAllCache();
|
||||
LogUtils.d(CACHE_PROTECT_TAG, "manualClearAllCache: Bitmap cache cleared");
|
||||
}
|
||||
|
||||
// 仅置空引用,不销毁实例(符合极致缓存策略)
|
||||
if (sMemoryCachedBackgroundView != null) {
|
||||
LogUtils.d(CACHE_PROTECT_TAG, "manualClearAllCache: View cache reference cleared");
|
||||
sMemoryCachedBackgroundView = null;
|
||||
}
|
||||
|
||||
LogUtils.w(CACHE_PROTECT_TAG, "manualClearAllCache: Manual cleanup finished");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取视图缓存实例
|
||||
* @return 视图缓存MemoryCachedBackgroundView实例
|
||||
*/
|
||||
public MemoryCachedBackgroundView getMemoryCachedBackgroundView() {
|
||||
LogUtils.d(TAG, "getMemoryCachedBackgroundView() called | Current: " + sMemoryCachedBackgroundView);
|
||||
LogUtils.d(TAG, "【getMemoryCachedBackgroundView】视图缓存获取方法调用 | 当前实例:" + sMemoryCachedBackgroundView);
|
||||
return sMemoryCachedBackgroundView;
|
||||
}
|
||||
|
||||
// ====================================== 公共静态方法 - 业务逻辑 (全局工具方法) ======================================
|
||||
/**
|
||||
* 发送通知消息
|
||||
* 手动清理所有缓存(仅主动调用生效,符合极致缓存策略)
|
||||
*/
|
||||
public static void manualClearAllCache() {
|
||||
LogUtils.w(CACHE_PROTECT_TAG, "【manualClearAllCache】手动清理缓存方法调用 | 仅主动触发生效");
|
||||
|
||||
// 清理Bitmap缓存
|
||||
if (sBitmapCacheUtils != null) {
|
||||
sBitmapCacheUtils.clearAllCache();
|
||||
LogUtils.d(CACHE_PROTECT_TAG, "【manualClearAllCache】Bitmap缓存已清理");
|
||||
}
|
||||
|
||||
// 仅置空视图缓存引用,不销毁实例(极致缓存策略)
|
||||
if (sMemoryCachedBackgroundView != null) {
|
||||
LogUtils.d(CACHE_PROTECT_TAG, "【manualClearAllCache】视图缓存引用已置空 | 实例保留");
|
||||
sMemoryCachedBackgroundView = null;
|
||||
}
|
||||
|
||||
LogUtils.w(CACHE_PROTECT_TAG, "【manualClearAllCache】手动清理缓存操作完成");
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送通知消息(仅调试模式下生效)
|
||||
* @param title 通知标题
|
||||
* @param content 通知内容
|
||||
*/
|
||||
public static void notifyMessage(String title, String content) {
|
||||
LogUtils.d(TAG, "notifyMessage() called with: title = [" + title + "], content = [" + content + "]");
|
||||
LogUtils.d(TAG, "【notifyMessage】发送通知消息方法调用 | 标题:" + title + " | 内容:" + content);
|
||||
|
||||
boolean canSend = isDebugging() && sApp != null && sNotificationManagerUtils != null;
|
||||
if (canSend) {
|
||||
NotificationMessage message = new NotificationMessage(title, content, "");
|
||||
sNotificationManagerUtils.showMessageNotification(sApp, message);
|
||||
LogUtils.d(TAG, "notifyMessage: Sent successfully");
|
||||
LogUtils.d(TAG, "【notifyMessage】通知消息发送成功");
|
||||
} else {
|
||||
LogUtils.d(TAG, "notifyMessage: Send failed, conditions not met");
|
||||
LogUtils.d(TAG, "【notifyMessage】通知消息发送失败 | 条件不满足:调试模式=" + isDebugging() + " | 应用实例=" + (sApp != null) + " | 通知工具=" + (sNotificationManagerUtils != null));
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 生命周期方法 ====================
|
||||
|
||||
// ====================================== 生命周期方法 - 应用全局生命周期 (重写父类方法) ======================================
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
LogUtils.d(TAG, "onCreate() called | Initializing application...");
|
||||
LogUtils.d(TAG, "【onCreate】应用启动生命周期方法调用 | 开始初始化应用...");
|
||||
|
||||
// 初始化应用单例与调试模式
|
||||
sApp = this;
|
||||
setIsDebugging(BuildConfig.DEBUG);
|
||||
LogUtils.d(TAG, "onCreate: Debug mode = " + BuildConfig.DEBUG);
|
||||
LogUtils.d(TAG, "【onCreate】应用单例已初始化 | 调试模式:" + BuildConfig.DEBUG);
|
||||
|
||||
// 初始化核心组件
|
||||
initBaseTools();
|
||||
initUtils();
|
||||
initReceiver();
|
||||
|
||||
LogUtils.d(TAG, "onCreate: Application initialization completed. Force-cache strategy active.");
|
||||
LogUtils.d(TAG, "【onCreate】应用初始化完成 | 极致强制缓存策略已激活");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTerminate() {
|
||||
super.onTerminate();
|
||||
LogUtils.d(TAG, "onTerminate() called | Releasing non-cache resources");
|
||||
LogUtils.d(TAG, "【onTerminate】应用终止生命周期方法调用 | 开始释放非缓存资源...");
|
||||
|
||||
// 仅释放非缓存资源
|
||||
// 释放非缓存资源
|
||||
ToastUtils.release();
|
||||
releaseNotificationManager();
|
||||
releaseReceiver();
|
||||
|
||||
// 核心策略:不清理缓存
|
||||
LogUtils.w(CACHE_PROTECT_TAG, "onTerminate: Force-cache active, caches remain in memory");
|
||||
LogUtils.d(TAG, "onTerminate: Non-cache resources released");
|
||||
// 核心策略:不清理任何缓存
|
||||
LogUtils.w(CACHE_PROTECT_TAG, "【onTerminate】极致缓存策略生效 | 所有缓存将保留在内存中");
|
||||
LogUtils.d(TAG, "【onTerminate】非缓存资源释放完成");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrimMemory(int level) {
|
||||
super.onTrimMemory(level);
|
||||
LogUtils.w(CACHE_PROTECT_TAG, "onTrimMemory() called with level: " + level + " | Ignoring, caches protected");
|
||||
LogUtils.w(CACHE_PROTECT_TAG, "【onTrimMemory】系统内存修剪回调 | 内存等级:" + level + " | 忽略修剪,缓存强制保护");
|
||||
logDetailedCacheStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLowMemory() {
|
||||
super.onLowMemory();
|
||||
LogUtils.w(CACHE_PROTECT_TAG, "onLowMemory() called | Force-cache active, no cleanup performed");
|
||||
LogUtils.w(CACHE_PROTECT_TAG, "【onLowMemory】系统低内存回调 | 极致缓存策略生效 | 不执行任何缓存清理操作");
|
||||
logDetailedCacheStatus();
|
||||
}
|
||||
|
||||
// ==================== 私有初始化方法 ====================
|
||||
|
||||
// ====================================== 私有初始化方法 - 组件初始化 (按依赖顺序排序) ======================================
|
||||
/**
|
||||
* 初始化基础工具类
|
||||
* 初始化基础工具类(Activity管理、Toast、通知管理器)
|
||||
*/
|
||||
private void initBaseTools() {
|
||||
LogUtils.d(TAG, "initBaseTools: Starting...");
|
||||
LogUtils.d(TAG, "【initBaseTools】基础工具类初始化开始...");
|
||||
WinBoLLActivityManager.init(this);
|
||||
ToastUtils.init(this);
|
||||
sNotificationManagerUtils = new NotificationManagerUtils(this);
|
||||
LogUtils.d(TAG, "initBaseTools: Completed");
|
||||
LogUtils.d(TAG, "【initBaseTools】基础工具类初始化完成");
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化核心工具与缓存(极致强制驻留)
|
||||
* 初始化核心工具与缓存(极致强制驻留,缓存核心)
|
||||
*/
|
||||
private void initUtils() {
|
||||
LogUtils.d(TAG, "initUtils: Starting with force-cache strategy");
|
||||
LogUtils.d(TAG, "【initUtils】核心工具与缓存初始化开始 | 极致缓存策略激活");
|
||||
|
||||
// 1. 配置与基础缓存
|
||||
// 1. 配置与基础缓存工具初始化
|
||||
sAppConfigUtils = getAppConfigUtils(this);
|
||||
sAppCacheUtils = getAppCacheUtils(this);
|
||||
|
||||
// 2. 资源与Bitmap缓存
|
||||
// 2. 资源与Bitmap缓存工具初始化(永久驻留)
|
||||
sBackgroundSourceUtils = BackgroundSourceUtils.getInstance(this);
|
||||
sBackgroundSourceUtils.loadSettings();
|
||||
sBitmapCacheUtils = BitmapCacheUtils.getInstance();
|
||||
LogUtils.d(TAG, "initUtils: Resource & Bitmap tools initialized (Permanent)");
|
||||
LogUtils.d(TAG, "【initUtils】资源与Bitmap缓存工具初始化完成 | 永久驻留内存");
|
||||
|
||||
// 3. 视图缓存
|
||||
// 3. 视图缓存初始化(永久驻留,无实例则创建)
|
||||
sMemoryCachedBackgroundView = MemoryCachedBackgroundView.getLastInstance(this);
|
||||
if (sMemoryCachedBackgroundView == null) {
|
||||
sMemoryCachedBackgroundView = MemoryCachedBackgroundView.getInstance(this, sBackgroundSourceUtils.getCurrentBackgroundBean(), true);
|
||||
LogUtils.d(TAG, "initUtils: View cache - New instance created");
|
||||
LogUtils.d(TAG, "【initUtils】视图缓存无现有实例,已创建新实例");
|
||||
}
|
||||
LogUtils.d(TAG, "initUtils: View cache initialized (Permanent)");
|
||||
LogUtils.d(TAG, "【initUtils】视图缓存初始化完成 | 永久驻留内存");
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册全局广播接收器
|
||||
*/
|
||||
private void initReceiver() {
|
||||
LogUtils.d(TAG, "initReceiver: Starting...");
|
||||
LogUtils.d(TAG, "【initReceiver】全局广播接收器注册开始...");
|
||||
mGlobalReceiver = new GlobalApplicationReceiver(this);
|
||||
mGlobalReceiver.registerAction();
|
||||
LogUtils.d(TAG, "initReceiver: Completed");
|
||||
LogUtils.d(TAG, "【initReceiver】全局广播接收器注册完成");
|
||||
}
|
||||
|
||||
// ==================== 私有释放方法 ====================
|
||||
|
||||
// ====================================== 私有释放方法 - 资源释放 (按创建逆序排序) ======================================
|
||||
/**
|
||||
* 释放广播接收器
|
||||
* 释放全局广播接收器
|
||||
*/
|
||||
private void releaseReceiver() {
|
||||
LogUtils.d(TAG, "releaseReceiver: Starting...");
|
||||
LogUtils.d(TAG, "【releaseReceiver】全局广播接收器释放开始...");
|
||||
if (mGlobalReceiver != null) {
|
||||
mGlobalReceiver.unregisterAction();
|
||||
mGlobalReceiver = null;
|
||||
LogUtils.d(TAG, "releaseReceiver: Completed");
|
||||
LogUtils.d(TAG, "【releaseReceiver】全局广播接收器释放完成");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放通知管理器
|
||||
* 释放通知管理器资源
|
||||
*/
|
||||
private void releaseNotificationManager() {
|
||||
LogUtils.d(TAG, "releaseNotificationManager: Starting...");
|
||||
LogUtils.d(TAG, "【releaseNotificationManager】通知管理器资源释放开始...");
|
||||
if (sNotificationManagerUtils != null) {
|
||||
sNotificationManagerUtils.release();
|
||||
sNotificationManagerUtils = null;
|
||||
LogUtils.d(TAG, "releaseNotificationManager: Completed");
|
||||
LogUtils.d(TAG, "【releaseNotificationManager】通知管理器资源释放完成");
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 私有辅助方法 ====================
|
||||
|
||||
// ====================================== 私有辅助方法 - 日志/工具 (辅助功能) ======================================
|
||||
/**
|
||||
* 记录当前缓存详细状态(用于调试监控)
|
||||
* 记录当前缓存详细状态(用于调试监控,极致缓存策略监控)
|
||||
*/
|
||||
private void logDetailedCacheStatus() {
|
||||
LogUtils.d(TAG, "logDetailedCacheStatus: Reporting cache state");
|
||||
LogUtils.d(TAG, "【logDetailedCacheStatus】缓存状态监控日志开始...");
|
||||
|
||||
// Bitmap缓存状态
|
||||
if (sBitmapCacheUtils != null) {
|
||||
LogUtils.d(CACHE_PROTECT_TAG, "Cache Status: BitmapCache [Valid]");
|
||||
LogUtils.d(CACHE_PROTECT_TAG, "【缓存状态】BitmapCache - 有效");
|
||||
try {
|
||||
LogUtils.d(CACHE_PROTECT_TAG, "Cache Detail: Bitmap Count = " + sBitmapCacheUtils.getCacheCount());
|
||||
LogUtils.d(CACHE_PROTECT_TAG, "【缓存详情】Bitmap缓存数量:" + sBitmapCacheUtils.getCacheCount());
|
||||
} catch (Exception e) {
|
||||
LogUtils.d(CACHE_PROTECT_TAG, "Cache Detail: Failed to get bitmap count - " + e.getMessage());
|
||||
LogUtils.e(CACHE_PROTECT_TAG, "【缓存详情】获取Bitmap缓存数量失败", e);
|
||||
}
|
||||
} else {
|
||||
LogUtils.d(CACHE_PROTECT_TAG, "【缓存状态】BitmapCache - 未初始化");
|
||||
}
|
||||
|
||||
// 视图缓存状态
|
||||
if (sMemoryCachedBackgroundView != null) {
|
||||
LogUtils.d(CACHE_PROTECT_TAG, "Cache Status: ViewCache [Valid]");
|
||||
LogUtils.d(CACHE_PROTECT_TAG, "Cache Detail: View Instance Count = " + MemoryCachedBackgroundView.getInstanceCount());
|
||||
LogUtils.d(CACHE_PROTECT_TAG, "【缓存状态】ViewCache - 有效");
|
||||
LogUtils.d(CACHE_PROTECT_TAG, "【缓存详情】视图实例数量:" + MemoryCachedBackgroundView.getInstanceCount());
|
||||
} else {
|
||||
LogUtils.d(CACHE_PROTECT_TAG, "【缓存状态】ViewCache - 引用已置空(实例可能保留)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,118 +13,130 @@
|
||||
android:gravity="center_vertical"
|
||||
style="@style/DefaultAToolbar"/>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="right">
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1.0">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_frame"
|
||||
android:padding="10dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="TTS语音服务设置:"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="<仅限在切换充电状态时,发送的TTS语音提醒。用于提醒用户当前的服务设置状态。>"
|
||||
android:textSize="12sp"/>
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="right">
|
||||
|
||||
<CheckBox
|
||||
android:layout_width="wrap_content"
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="用电提醒启用时的TTS贴心服务"
|
||||
android:onClick="onEnableUsePowerTts"
|
||||
android:id="@+id/activitysettingsCheckBox1"/>
|
||||
android:background="@drawable/bg_frame"
|
||||
android:padding="10dp">
|
||||
|
||||
<CheckBox
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="充电提醒启用时的TTS贴心服务"
|
||||
android:onClick="onEnableChargeTts"
|
||||
android:id="@+id/activitysettingsCheckBox2"/>
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="TTS语音服务设置:"
|
||||
android:paddingLeft="10dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="<仅限在切换充电状态时,发送的TTS语音提醒。用于提醒用户当前的服务设置状态。>"
|
||||
android:textSize="12sp"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<CheckBox
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="用电提醒启用时的TTS贴心服务"
|
||||
android:onClick="onEnableUsePowerTts"
|
||||
android:id="@+id/activitysettingsCheckBox1"/>
|
||||
|
||||
<CheckBox
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="充电提醒启用时的TTS贴心服务"
|
||||
android:onClick="onEnableChargeTts"
|
||||
android:id="@+id/activitysettingsCheckBox2"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="right">
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="检查TTS语音悬浮窗权限"
|
||||
android:padding="10dp"
|
||||
android:onClick="onCheckTTSDrawOverlaysPermission"
|
||||
android:layout_marginRight="10dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="300dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/bg_frame"
|
||||
android:padding="10dp">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="电量图表绘制风格设置:"
|
||||
android:paddingLeft="10dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<cc.winboll.studio.powerbell.views.BatteryStyleView
|
||||
android:id="@+id/battery_style_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1200dp"
|
||||
app:batteryPreviewColor="@color/colorPrimary"
|
||||
app:previewBatteryValue="100"
|
||||
app:defaultSelectedStyle="zebra_style"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="right">
|
||||
android:background="@drawable/bg_frame">
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="检查TTS语音悬浮窗权限"
|
||||
android:padding="10dp"
|
||||
android:onClick="onCheckTTSDrawOverlaysPermission"/>
|
||||
<cc.winboll.studio.libaes.views.ADsControlView
|
||||
android:id="@+id/ads_control_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_frame"
|
||||
android:padding="10dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="米盟广告SDK设置:"/>
|
||||
|
||||
<cc.winboll.studio.libaes.views.ADsControlView
|
||||
android:id="@+id/ads_control_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/bg_frame"
|
||||
android:padding="10dp"
|
||||
android:layout_weight="1.0">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="电量图表绘制风格设置:"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<cc.winboll.studio.powerbell.views.BatteryStyleView
|
||||
android:id="@+id/battery_style_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1200dp"
|
||||
app:batteryPreviewColor="@color/colorPrimary"
|
||||
app:previewBatteryValue="100"
|
||||
app:defaultSelectedStyle="zebra_style"/>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@@ -19,11 +19,11 @@ def genVersionName(def versionName){
|
||||
|
||||
android {
|
||||
|
||||
// 1. compileSdkVersion:必须 ≥ targetSdkVersion,建议直接等于 targetSdkVersion(30)
|
||||
compileSdkVersion 30
|
||||
// 关键:改为你已安装的 SDK 32(≥ targetSdkVersion 30,兼容已安装环境)
|
||||
compileSdkVersion 32
|
||||
|
||||
// 2. buildToolsVersion:需匹配 compileSdkVersion,建议使用 30.x.x 最新稳定版(无需高于 compileSdkVersion)
|
||||
buildToolsVersion "30.0.3" // 这是 30 对应的最新稳定版,避免使用 beta 版
|
||||
// 直接使用已安装的构建工具 33.0.3(无需修改)
|
||||
buildToolsVersion "33.0.3"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "cc.winboll.studio.winboll"
|
||||
@@ -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'
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Wed Dec 31 20:28:06 HKT 2025
|
||||
#Tue Jan 06 06:07:46 GMT 2026
|
||||
stageCount=9
|
||||
libraryProject=
|
||||
baseVersion=15.11
|
||||
publishVersion=15.11.8
|
||||
buildCount=0
|
||||
buildCount=10
|
||||
baseBetaVersion=15.11.9
|
||||
|
||||
BIN
winboll/libs/libWeWorkSpecSDK.so
Normal file
BIN
winboll/libs/libWeWorkSpecSDK.so
Normal file
Binary file not shown.
@@ -278,6 +278,10 @@
|
||||
|
||||
<activity android:name="cc.winboll.studio.winboll.activities.SettingsActivity"/>
|
||||
|
||||
<activity android:name="cc.winboll.studio.winboll.unittest.TestWeWorkSpecSDK"/>
|
||||
|
||||
<activity android:name="cc.winboll.studio.winboll.activities.WXPayActivity"/>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
</manifest>
|
||||
@@ -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();
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package cc.winboll.studio.winboll;
|
||||
|
||||
/**
|
||||
* 微信支付配置类
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @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秒
|
||||
}
|
||||
|
||||
@@ -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<zhangsken@qq.com>
|
||||
* @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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<zhangsken@qq.com>
|
||||
* @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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<zhangsken@qq.com>
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package cc.winboll.studio.winboll.utils;
|
||||
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
|
||||
/**
|
||||
* 日志工具类(适配项目规范)
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
package cc.winboll.studio.winboll.utils;
|
||||
|
||||
import cc.winboll.studio.winboll.WxPayConfig;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
||||
/**
|
||||
* 微信支付服务端接口封装
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<zhangsken@qq.com>
|
||||
* @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<EncodeHintType, Object> 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);
|
||||
}
|
||||
}
|
||||
|
||||
210
winboll/src/main/java/com/tencent/wework/SpecCallbackSDK.java
Normal file
210
winboll/src/main/java/com/tencent/wework/SpecCallbackSDK.java
Normal file
@@ -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<String, String> responseHeaders;
|
||||
|
||||
public Map<String, String> 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<String, String> responseHeaders = sdk.GetResponseHeaders();
|
||||
* String body = sdk.GetResponseBody();
|
||||
* //do response
|
||||
*
|
||||
* @return errorcode 示例如下:
|
||||
* -920001: 未设置请求方法
|
||||
* -920002: 未设置请求header
|
||||
* -920003: 未设置请求body
|
||||
* */
|
||||
public SpecCallbackSDK(String method, Map<String, String> 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<String, String> 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<String, String>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
163
winboll/src/main/java/com/tencent/wework/SpecSDK.java
Normal file
163
winboll/src/main/java/com/tencent/wework/SpecSDK.java
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
171
winboll/src/main/java/com/tencent/wework/SpecUtil.java
Normal file
171
winboll/src/main/java/com/tencent/wework/SpecUtil.java
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
30
winboll/src/main/res/layout/activity_test_weworkspecsdk.xml
Normal file
30
winboll/src/main/res/layout/activity_test_weworkspecsdk.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="20dp"
|
||||
android:gravity="center_horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_init_sdk"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="初始化企业微信SDK"
|
||||
android:layout_marginBottom="10dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_get_corp_info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="获取企业基本信息"
|
||||
android:layout_marginBottom="10dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_check_auth"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="检查用户授权状态"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
43
winboll/src/main/res/layout/activity_wxpay.xml
Normal file
43
winboll/src/main/res/layout/activity_wxpay.xml
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center_horizontal"
|
||||
android:padding="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="定额支付测试(0.01元)"
|
||||
android:textSize="20sp"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_qrcode"
|
||||
android:layout_width="250dp"
|
||||
android:layout_height="250dp"
|
||||
android:scaleType="fitXY"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_order_no"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="商户订单号:"
|
||||
android:textSize="16sp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_pay_status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="支付状态:未支付"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@android:color/black"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_create_order"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="生成支付二维码"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -7,4 +7,7 @@
|
||||
<item
|
||||
android:id="@+id/item_settings"
|
||||
android:title="Settings"/>
|
||||
<item
|
||||
android:id="@+id/item_wxpayactivity"
|
||||
android:title="WXPayActivity"/>
|
||||
</menu>
|
||||
|
||||
Reference in New Issue
Block a user