Compare commits

..

16 Commits

Author SHA1 Message Date
6d907e46cb <positions>APK 15.12.13 release Publish. 2026-03-17 15:21:11 +08:00
5ab16c2387 添加任务列表里启用状态颜色标识。设置实时距离文字的绿色与任务列表的绿色协调一致。 2026-03-17 15:17:33 +08:00
160614ce2a <positions>APK 15.12.12 release Publish. 2026-01-24 21:01:09 +08:00
1e0a9d222c 增加对签名证书修改后的证书识别能力。 2026-01-24 20:59:38 +08:00
a93cad67a4 <positions>APK 15.12.11 release Publish. 2026-01-24 13:10:53 +08:00
db264eb85a 更新类库,类库应用联网验证模块有改进。 2026-01-24 13:09:20 +08:00
8361cb0728 <positions>APK 15.12.10 release Publish. 2026-01-23 05:03:29 +08:00
92f94f462f 添加应用介绍窗口 2026-01-23 05:00:56 +08:00
22c719d87c Merge branch 'winboll' into positions 2026-01-23 03:23:15 +08:00
8c18710e36 正在制作模块“发送Action 让Termux自行Bash命令。”。。。 2026-01-19 12:04:58 +08:00
224bd243e2 设置WinBoLL在Termux的工作目录。 2026-01-19 11:39:35 +08:00
b30bdc6802 添加Termux环境测试 2026-01-19 11:35:00 +08:00
8f0973cb6c 添加Termux环境测试窗口 2026-01-19 11:24:07 +08:00
b9fab2d737 <winboll>APK 15.11.10 release Publish. 2026-01-13 16:53:24 +08:00
156af54eaa 应用窗口切换调试完成 2026-01-13 16:52:01 +08:00
fb9dd93162 正在调整工具栏与应用介绍窗口。。。 2026-01-13 12:26:11 +08:00
37 changed files with 553 additions and 981 deletions

View File

@@ -81,8 +81,8 @@ dependencies {
//api 'androidx.fragment:fragment:1.1.0' //api 'androidx.fragment:fragment:1.1.0'
// WinBoLL库 nexus.winboll.cc 地址 // WinBoLL库 nexus.winboll.cc 地址
api 'cc.winboll.studio:libaes:15.12.13' api 'cc.winboll.studio:libaes:15.15.2'
api 'cc.winboll.studio:libappbase:15.14.2' api 'cc.winboll.studio:libappbase:15.15.11'
// WinBoLL备用库 jitpack.io 地址 // WinBoLL备用库 jitpack.io 地址
//api 'com.github.ZhanGSKen:AES:aes-v15.12.9' //api 'com.github.ZhanGSKen:AES:aes-v15.12.9'

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Tue Jan 13 06:44:22 HKT 2026 #Tue Mar 17 15:21:11 HKT 2026
stageCount=10 stageCount=14
libraryProject= libraryProject=
baseVersion=15.12 baseVersion=15.12
publishVersion=15.12.9 publishVersion=15.12.13
buildCount=0 buildCount=0
baseBetaVersion=15.12.10 baseBetaVersion=15.12.14

View File

@@ -157,6 +157,8 @@
<activity android:name="cc.winboll.studio.positions.activities.SettingsActivity"/> <activity android:name="cc.winboll.studio.positions.activities.SettingsActivity"/>
<activity android:name="cc.winboll.studio.positions.activities.AboutActivity"/>
</application> </application>
</manifest> </manifest>

View File

@@ -13,6 +13,7 @@ import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.Button; import android.widget.Button;
import android.widget.CompoundButton; import android.widget.CompoundButton;
import android.widget.LinearLayout;
import android.widget.Switch; import android.widget.Switch;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@@ -24,12 +25,12 @@ import cc.winboll.studio.libaes.utils.DevelopUtils;
import cc.winboll.studio.libaes.views.ADsBannerView; import cc.winboll.studio.libaes.views.ADsBannerView;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.positions.R; import cc.winboll.studio.positions.R;
import cc.winboll.studio.positions.activities.AboutActivity;
import cc.winboll.studio.positions.activities.LocationActivity; import cc.winboll.studio.positions.activities.LocationActivity;
import cc.winboll.studio.positions.activities.SettingsActivity; import cc.winboll.studio.positions.activities.SettingsActivity;
import cc.winboll.studio.positions.activities.WinBoLLActivity; import cc.winboll.studio.positions.activities.WinBoLLActivity;
import cc.winboll.studio.positions.utils.AppConfigsUtil; import cc.winboll.studio.positions.utils.AppConfigsUtil;
import cc.winboll.studio.positions.utils.ServiceUtil; import cc.winboll.studio.positions.utils.ServiceUtil;
import android.widget.LinearLayout;
/** /**
* 主页面:仅负责 * 主页面:仅负责
@@ -220,6 +221,10 @@ public class MainActivity extends WinBoLLActivity implements IWinBoLLActivity {
Intent intent = new Intent(); Intent intent = new Intent();
intent.setClass(this, SettingsActivity.class); intent.setClass(this, SettingsActivity.class);
startActivity(intent); startActivity(intent);
} else if (menuItemId == R.id.item_about) {
Intent intent = new Intent();
intent.setClass(this, AboutActivity.class);
startActivity(intent);
} else { } else {
// 在switch语句中处理每个ID并在处理完后返回true未处理的情况返回false。 // 在switch语句中处理每个ID并在处理完后返回true未处理的情况返回false。
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);

View File

@@ -0,0 +1,81 @@
package cc.winboll.studio.positions.activities;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.models.APPInfo;
import cc.winboll.studio.libappbase.views.AboutView;
import cc.winboll.studio.positions.R;
/**
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026/01/13 11:25
* @Describe 应用介绍窗口
*/
public class AboutActivity extends WinBoLLActivity {
public static final String TAG = "AboutActivity";
private Toolbar mToolbar;
@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_about);
// 设置工具栏
initToolbar();
AboutView aboutView = findViewById(R.id.aboutview);
aboutView.setAPPInfo(genDefaultAppInfo());
}
private void initToolbar() {
LogUtils.d(TAG, "initToolbar() 开始初始化");
mToolbar = findViewById(R.id.toolbar);
if (mToolbar == null) {
LogUtils.e(TAG, "initToolbar() | Toolbar未找到");
return;
}
setSupportActionBar(mToolbar);
mToolbar.setSubtitle(getTag());
mToolbar.setTitleTextAppearance(this, R.style.Toolbar_TitleText);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LogUtils.d(TAG, "导航栏 点击返回按钮");
finish();
}
});
LogUtils.d(TAG, "initToolbar() 配置完成");
}
private APPInfo genDefaultAppInfo() {
LogUtils.d(TAG, "genDefaultAppInfo() 调用");
String branchName = "positions";
APPInfo appInfo = new APPInfo();
appInfo.setAppName(getString(R.string.app_name));
appInfo.setAppIcon(R.drawable.ic_winboll);
appInfo.setAppDescription(getString(R.string.app_description));
appInfo.setAppGitName("Positions");
appInfo.setAppGitOwner("Studio");
appInfo.setAppGitAPPBranch(branchName);
appInfo.setAppGitAPPSubProjectFolder(branchName);
appInfo.setAppHomePage("https://www.winboll.cc/apks/index.php?project=Positions");
appInfo.setAppAPKName("Positions");
appInfo.setAppAPKFolderName("Positions");
LogUtils.d(TAG, "genDefaultAppInfo: 应用信息已生成");
return appInfo;
}
}

View File

@@ -331,7 +331,11 @@ public class PositionTaskListView extends LinearLayout {
String distanceCond = task.isGreaterThan() ? "大于" : "小于"; String distanceCond = task.isGreaterThan() ? "大于" : "小于";
simpleHolder.tvSimpleDistanceCond.setText(String.format("条件:距离 %s %d 米", distanceCond, task.getDiscussDistance())); simpleHolder.tvSimpleDistanceCond.setText(String.format("条件:距离 %s %d 米", distanceCond, task.getDiscussDistance()));
// 启用状态 // 启用状态
simpleHolder.tvSimpleIsEnable.setText(task.isEnable() ? "状态:已启用" : "状态:已禁用"); simpleHolder.tvSimpleIsEnable.setText(task.isEnable() ? "状态:已启用" : "状态:已禁用");
// 修改为从colors.xml读取颜色
simpleHolder.tvSimpleIsEnable.setTextColor(task.isEnable()
? getContext().getResources().getColor(R.color.colorEnableGreen)
: getContext().getResources().getColor(R.color.colorGrayText));
// isBingo红点任务触发时显示未触发时隐藏 // isBingo红点任务触发时显示未触发时隐藏
simpleHolder.vBingoDot.setVisibility(task.isBingo() ? View.VISIBLE : View.GONE); simpleHolder.vBingoDot.setVisibility(task.isBingo() ? View.VISIBLE : View.GONE);
} else if (holder instanceof TaskContentViewHolder) { } else if (holder instanceof TaskContentViewHolder) {
@@ -380,7 +384,7 @@ public class PositionTaskListView extends LinearLayout {
// 步骤3刷新Adapter局部刷新+范围通知,避免列表错乱) // 步骤3刷新Adapter局部刷新+范围通知,避免列表错乱)
notifyItemRemoved(position); notifyItemRemoved(position);
notifyItemRangeChanged(position, mAdapterData.size()); notifyItemRangeChanged(position, mAdapterData.size());
LogUtils.d(TAG, "Adapter已移除任务刷新列表位置索引=" + position + ""); LogUtils.d(TAG, "Adapter已移除任务刷新列表位置索引=" + position + "");
// 步骤4通知外部如Activity任务已更新 // 步骤4通知外部如Activity任务已更新
@@ -457,7 +461,7 @@ public class PositionTaskListView extends LinearLayout {
} }
}); });
} }
private String genSelectedTimeText(long timeMillis) { private String genSelectedTimeText(long timeMillis) {
// 2. 格式化时间字符串Java 7 用 SimpleDateFormat需处理 ParseException // 2. 格式化时间字符串Java 7 用 SimpleDateFormat需处理 ParseException
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault()); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault());

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<cc.winboll.studio.libaes.views.ASupportToolbar
android:layout_width="match_parent"
android:layout_height="@dimen/toolbar_height"
android:id="@+id/toolbar"
android:gravity="center_vertical"/>
<cc.winboll.studio.libappbase.views.AboutView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.0"
android:id="@+id/aboutview"/>
</LinearLayout>

View File

@@ -5,5 +5,8 @@
<item <item
android:id="@+id/item_settings" android:id="@+id/item_settings"
android:title="Settings"/> android:title="Settings"/>
<item
android:id="@+id/item_about"
android:title="@string/item_about"/>
</menu> </menu>

View File

@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name">悟空笔记</string> <string name="app_name">悟空笔记</string>
<string name="app_description">安卓位置应用,有关于地理位置的相关应用。</string>
<string name="item_about">关于</string>
</resources> </resources>

View File

@@ -27,7 +27,7 @@
<color name="background_light">#F5F5F5</color> <!-- 浅灰背景(备用,提升页面质感) --> <color name="background_light">#F5F5F5</color> <!-- 浅灰背景(备用,提升页面质感) -->
<!-- 扩展颜色(避免后续新增功能时再次缺失) --> <!-- 扩展颜色(避免后续新增功能时再次缺失) -->
<color name="green">#4CAF50</color> <!-- 绿色(备用:如“启用”状态标识) --> <color name="green">#2E8B57</color> <!-- 绿色(备用:如“启用”状态标识) -->
<color name="yellow">#FFC107</color> <!-- 黄色(备用:如“提醒”标识) --> <color name="yellow">#FFC107</color> <!-- 黄色(备用:如“提醒”标识) -->
<color name="color_text_primary">#333333</color> <color name="color_text_primary">#333333</color>
<color name="color_gray">#999999</color> <color name="color_gray">#999999</color>

View File

@@ -1,6 +1,8 @@
<resources> <resources>
<string name="app_name">Positions</string> <string name="app_name">Positions</string>
<string name="app_description">Android location-based apps, including those related to geolocation.</string>
<string name="appplus_name">PositionsPlus</string> <string name="appplus_name">PositionsPlus</string>
<string name="item_about">About</string>
<string name="open_appplus">Open APP Plus</string> <string name="open_appplus">Open APP Plus</string>
<string name="close_appplus">Close APP Plus</string> <string name="close_appplus">Close APP Plus</string>
<string name="appplus_open_disabled">APP Plus Open Disable</string> <string name="appplus_open_disabled">APP Plus Open Disable</string>

View File

@@ -90,12 +90,12 @@ dependencies {
//annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0' //annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
// WinBoLL库 nexus.winboll.cc 地址 // WinBoLL库 nexus.winboll.cc 地址
api 'cc.winboll.studio:libaes:15.12.13' api 'cc.winboll.studio:libaes:15.15.7'
api 'cc.winboll.studio:libappbase:15.14.2' api 'cc.winboll.studio:libappbase:15.15.4'
// WinBoLL备用库 jitpack.io 地址 // WinBoLL备用库 jitpack.io 地址
//api 'com.github.ZhanGSKen:AES:aes-v15.12.9' //api 'com.github.ZhanGSKen:AES:aes-v15.15.7'
//api 'com.github.ZhanGSKen:APPBase:appbase-v15.14.1' //api 'com.github.ZhanGSKen:APPBase:appbase-v15.15.4'
api fileTree(dir: 'libs', include: ['*.jar']) api fileTree(dir: 'libs', include: ['*.jar'])
} }

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Tue Jan 13 03:11:40 HKT 2026 #Mon Jan 19 03:57:58 GMT 2026
stageCount=10 stageCount=11
libraryProject= libraryProject=
baseVersion=15.11 baseVersion=15.11
publishVersion=15.11.9 publishVersion=15.11.10
buildCount=0 buildCount=12
baseBetaVersion=15.11.10 baseBetaVersion=15.11.11

View File

@@ -1,7 +1,8 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<manifest <manifest
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
package="cc.winboll.studio.winboll"> package="cc.winboll.studio.winboll"
android:sharedUserId="com.termux">
<!-- 拥有完全的网络访问权限 --> <!-- 拥有完全的网络访问权限 -->
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
@@ -282,6 +283,8 @@
<activity android:name="cc.winboll.studio.winboll.activities.WXPayActivity"/> <activity android:name="cc.winboll.studio.winboll.activities.WXPayActivity"/>
<activity android:name="cc.winboll.studio.winboll.unittest.TermuxEnvTestActivity"/>
</application> </application>
</manifest> </manifest>

View File

@@ -1,53 +0,0 @@
package cc.winboll.studio.winboll;
/**
* @Author ZhanGSKen<zhangsken@188.com>
* @Date 2025/05/22 13:08
*/
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
public class CustomToolbar extends Toolbar {
private View viewMain;
public CustomToolbar(Context context) {
this(context, null);
}
public CustomToolbar(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomToolbar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context, attrs);
}
private void initView(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomToolbar);
// 获取属性值
String toolbarTitle = typedArray.getString(R.styleable.CustomToolbar_toolbarTitle);
int toolbarTitleColor = typedArray.getColor(R.styleable.CustomToolbar_toolbarTitleColor, android.graphics.Color.WHITE);
int toolbarBackgroundColor = typedArray.getColor(R.styleable.CustomToolbar_toolbarBackgroundColor, android.graphics.Color.BLUE);
// 加载布局
viewMain = LayoutInflater.from(context).inflate(R.layout.view_toolbar, this, true);
// 应用属性值
TextView toolbarTitleTextView = viewMain.findViewById(R.id.toolbar_title);
toolbarTitleTextView.setText(toolbarTitle);
toolbarTitleTextView.setTextColor(toolbarTitleColor);
viewMain.setBackgroundColor(toolbarBackgroundColor);
// 释放 TypedArray
typedArray.recycle();
}
}

View File

@@ -8,23 +8,22 @@ import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.AdapterView; import android.widget.AdapterView;
import cc.winboll.studio.libaes.activitys.AboutActivity;
import cc.winboll.studio.libaes.activitys.DrawerFragmentActivity; import cc.winboll.studio.libaes.activitys.DrawerFragmentActivity;
import cc.winboll.studio.libaes.models.APPInfo;
import cc.winboll.studio.libaes.models.DrawerMenuBean; import cc.winboll.studio.libaes.models.DrawerMenuBean;
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager; import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.winboll.R; import cc.winboll.studio.winboll.R;
import cc.winboll.studio.winboll.activities.AboutActivity;
import cc.winboll.studio.winboll.activities.SettingsActivity; import cc.winboll.studio.winboll.activities.SettingsActivity;
import cc.winboll.studio.winboll.activities.WXPayActivity;
import cc.winboll.studio.winboll.fragments.BrowserFragment; import cc.winboll.studio.winboll.fragments.BrowserFragment;
import cc.winboll.studio.winboll.unittest.TermuxEnvTestActivity;
import java.util.ArrayList; import java.util.ArrayList;
public class MainActivity extends DrawerFragmentActivity { public class MainActivity extends DrawerFragmentActivity {
public static final String TAG = "MainActivity"; public static final String TAG = "MainActivity";
BrowserFragment mBrowserFragment; BrowserFragment mBrowserFragment;
// ------------------- 新增Handler 消息定义接收URL历史更新消息 ------------------- // ------------------- 新增Handler 消息定义接收URL历史更新消息 -------------------
@@ -46,6 +45,12 @@ public class MainActivity extends DrawerFragmentActivity {
showFragment(mBrowserFragment); showFragment(mBrowserFragment);
} }
@Override
public String getTag() {
return TAG;
}
public static void sendMessage(Message msg) { public static void sendMessage(Message msg) {
_mMainHandler.sendMessage(msg); _mMainHandler.sendMessage(msg);
} }
@@ -123,6 +128,7 @@ public class MainActivity extends DrawerFragmentActivity {
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.toolbar_main, menu); getMenuInflater().inflate(R.menu.toolbar_main, menu);
getMenuInflater().inflate(R.menu.toolbar_termux, menu);
return super.onCreateOptionsMenu(menu); return super.onCreateOptionsMenu(menu);
} }
@@ -149,12 +155,13 @@ public class MainActivity extends DrawerFragmentActivity {
} }
} else if (nItemId == R.id.item_settings) { } else if (nItemId == R.id.item_settings) {
WinBoLLActivityManager.getInstance().startWinBoLLActivity(getApplicationContext(), SettingsActivity.class); WinBoLLActivityManager.getInstance().startWinBoLLActivity(getApplicationContext(), SettingsActivity.class);
} else if (nItemId == R.id.item_wxpayactivity) { } else if (nItemId == R.id.item_about) {
WinBoLLActivityManager.getInstance().startWinBoLLActivity(getApplicationContext(), WXPayActivity.class);
} else if (nItemId == cc.winboll.studio.libaes.R.id.item_about) {
Intent intent = new Intent(getApplicationContext(), AboutActivity.class); Intent intent = new Intent(getApplicationContext(), AboutActivity.class);
APPInfo appInfo = genDefaultAPPInfo();
intent.putExtra(AboutActivity.EXTRA_APPINFO, appInfo); WinBoLLActivityManager.getInstance().startWinBoLLActivity(getApplicationContext(), intent, AboutActivity.class);
} else if (nItemId == R.id.item_termux_env_test) {
Intent intent = new Intent(getApplicationContext(), TermuxEnvTestActivity.class);
WinBoLLActivityManager.getInstance().startWinBoLLActivity(getApplicationContext(), intent, AboutActivity.class); WinBoLLActivityManager.getInstance().startWinBoLLActivity(getApplicationContext(), intent, AboutActivity.class);
} else { } else {
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
@@ -163,23 +170,6 @@ public class MainActivity extends DrawerFragmentActivity {
return true; return true;
} }
APPInfo genDefaultAPPInfo() {
String szBranchName = "winboll";
APPInfo appInfo = new APPInfo();
appInfo.setAppName("WinBoLL");
appInfo.setAppIcon(cc.winboll.studio.libaes.R.drawable.ic_winboll);
appInfo.setAppDescription("WinBoLL 网站浏览器。");
appInfo.setAppGitName("WinBoLL");
appInfo.setAppGitOwner("Studio");
appInfo.setAppGitAPPBranch(szBranchName);
appInfo.setAppGitAPPSubProjectFolder(szBranchName);
appInfo.setAppHomePage("https://www.winboll.cc/apks/index.php?project=WinBoLL");
appInfo.setAppAPKName("WinBoLL");
appInfo.setAppAPKFolderName("WinBoLL");
return appInfo;
}
// ------------------- 新增对外提供Handler供其他组件发送消息如BrowserFragment ------------------- // ------------------- 新增对外提供Handler供其他组件发送消息如BrowserFragment -------------------
public Handler getMainHandler() { public Handler getMainHandler() {
return _mMainHandler; return _mMainHandler;

View File

@@ -0,0 +1,80 @@
package cc.winboll.studio.winboll.activities;
import android.os.Bundle;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.models.APPInfo;
import cc.winboll.studio.libappbase.views.AboutView;
import cc.winboll.studio.winboll.MainActivity;
import cc.winboll.studio.winboll.R;
/**
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026/01/13 11:54
* @Describe 应用介绍窗口
*/
public class AboutActivity extends BaseWinBoLLActivity {
public static final String TAG = "AboutActivity";
private Toolbar mToolbar;
@Override
public String getTag() {
return TAG;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_about);
// 设置工具栏
initToolbar();
AboutView aboutView = getActivity().findViewById(R.id.aboutview);
aboutView.setAPPInfo(genDefaultAppInfo());
}
private void initToolbar() {
LogUtils.d(TAG, "initToolbar() 开始初始化");
mToolbar = (Toolbar) findViewById(R.id.toolbar);
if (mToolbar == null) {
LogUtils.e(TAG, "initToolbar() | Toolbar未找到");
return;
}
setSupportActionBar(mToolbar);
mToolbar.setSubtitle(getTag());
((AppCompatActivity)getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LogUtils.d(TAG, "导航栏 点击返回按钮");
getActivity().finish();
WinBoLLActivityManager.getInstance().startWinBoLLActivity(getActivity(), MainActivity.class);
}
});
LogUtils.d(TAG, "initToolbar() 配置完成");
}
private APPInfo genDefaultAppInfo() {
LogUtils.d(TAG, "genDefaultAppInfo() 调用");
String branchName = "winboll";
APPInfo appInfo = new APPInfo();
appInfo.setAppName(getActivity().getString(R.string.app_name));
appInfo.setAppIcon(R.drawable.ic_winboll);
appInfo.setAppDescription(getActivity().getString(R.string.app_description));
appInfo.setAppGitName("WinBoLL");
appInfo.setAppGitOwner("Studio");
appInfo.setAppGitAPPBranch(branchName);
appInfo.setAppGitAPPSubProjectFolder(branchName);
appInfo.setAppHomePage("https://www.winboll.cc/apks/index.php?project=WinBoLL");
appInfo.setAppAPKName("WinBoLL");
appInfo.setAppAPKFolderName("WinBoLL");
LogUtils.d(TAG, "genDefaultAppInfo: 应用信息已生成");
return appInfo;
}
}

View File

@@ -0,0 +1,59 @@
package cc.winboll.studio.winboll.activities;
import android.app.Activity;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
import cc.winboll.studio.libaes.models.AESThemeBean;
import cc.winboll.studio.libaes.utils.AESThemeUtil;
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
/**
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026/01/13 16:33
* BaseWinBollActivity 【继承AppCompatActivity保留核心能力不额外暴露方法】
* 继承链路BaseWinBoLLActivity → AppCompatActivity → FragmentActivityAppCompat能力天然继承可用
*/
public abstract class BaseWinBoLLActivity extends AppCompatActivity implements IWinBoLLActivity {
public static final String TAG = "BaseWinBoLLActivity";
protected volatile AESThemeBean.ThemeType mThemeType;
@Override
protected void onCreate(Bundle savedInstanceState) {
mThemeType = getThemeType();
setThemeStyle();
super.onCreate(savedInstanceState);
WinBoLLActivityManager.getInstance().add(this);
//ToastUtils.show(getTag() + ": onCreate");
}
AESThemeBean.ThemeType getThemeType() {
/*SharedPreferences sharedPreferences = getSharedPreferences(
SHAREDPREFERENCES_NAME, MODE_PRIVATE);
return AESThemeBean.ThemeType.values()[((sharedPreferences.getInt(DRAWER_THEME_TYPE, AESThemeBean.ThemeType.DEFAULT.ordinal())))];
*/
return AESThemeBean.getThemeStyleType(AESThemeUtil.getThemeTypeID(getApplicationContext()));
}
void setThemeStyle() {
//setTheme(AESThemeBean.getThemeStyle(getThemeType()));
setTheme(AESThemeUtil.getThemeTypeID(getApplicationContext()));
}
@Override
protected void onDestroy() {
WinBoLLActivityManager.getInstance().registeRemove(this);
super.onDestroy();
}
// 子类必须实现getTag(),确保唯一标识
@Override
public abstract String getTag();
@Override
public Activity getActivity() {
return this;
}
}

View File

@@ -5,20 +5,25 @@ package cc.winboll.studio.winboll.activities;
* @Date 2025/03/25 11:46:40 * @Date 2025/03/25 11:46:40
* @Describe 测试窗口2 * @Describe 测试窗口2
*/ */
import android.app.Activity;
import android.os.Bundle; import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.winboll.R; import cc.winboll.studio.winboll.R;
public class New2Activity extends WinBoLLActivity { public class New2Activity extends BaseWinBoLLActivity {
public static final String TAG = "New2Activity"; public static final String TAG = "New2Activity";
Toolbar mToolbar; Toolbar mToolbar;
@Override
public String getTag() {
return TAG;
}
//LogView mLogView; //LogView mLogView;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@@ -28,15 +33,10 @@ public class New2Activity extends WinBoLLActivity {
// mLogView = findViewById(R.id.logview); // mLogView = findViewById(R.id.logview);
// mLogView.start(); // mLogView.start();
mToolbar = findViewById(R.id.toolbar); mToolbar = findViewById(R.id.toolbar);
setActionBar(mToolbar); setSupportActionBar(mToolbar);
} }
@Override
protected void onResume() {
super.onResume();
//mLogView.start();
}
public void onCloseThisActivity(View view) { public void onCloseThisActivity(View view) {
//WinBoLLActivityManager.getInstance().finish(this); //WinBoLLActivityManager.getInstance().finish(this);
@@ -49,17 +49,4 @@ public class New2Activity extends WinBoLLActivity {
public void onNewActivity(View view) { public void onNewActivity(View view) {
//WinBoLLActivityManager.getInstance().startWinBoLLActivity(this, NewActivity.class); //WinBoLLActivityManager.getInstance().startWinBoLLActivity(this, NewActivity.class);
} }
@Override
public boolean onCreateOptionsMenu(Menu menu) {
//getMenuInflater().inflate(R.menu.toolbar_main, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// 在switch语句中处理每个ID并在处理完后返回true未处理的情况返回false。
return super.onOptionsItemSelected(item);
}
} }

View File

@@ -5,29 +5,19 @@ package cc.winboll.studio.winboll.activities;
* @Date 2025/03/25 05:04:22 * @Date 2025/03/25 05:04:22
* @Describe * @Describe
*/ */
import android.app.Activity;
import android.os.Bundle; import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager; import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
import cc.winboll.studio.winboll.R; import cc.winboll.studio.winboll.R;
import cc.winboll.studio.winboll.App;
public class NewActivity extends WinBoLLActivity implements IWinBoLLActivity { public class NewActivity extends BaseWinBoLLActivity {
public static final String TAG = "NewActivity"; public static final String TAG = "NewActivity";
Toolbar mToolbar; Toolbar mToolbar;
//LogView mLogView; //LogView mLogView;
@Override
public Activity getActivity() {
return this;
}
@Override @Override
public String getTag() { public String getTag() {
return TAG; return TAG;
@@ -40,15 +30,10 @@ public class NewActivity extends WinBoLLActivity implements IWinBoLLActivity {
// mLogView = findViewById(R.id.logview); // mLogView = findViewById(R.id.logview);
// mLogView.start(); // mLogView.start();
mToolbar = findViewById(R.id.toolbar); mToolbar = findViewById(R.id.toolbar);
setActionBar(mToolbar); setSupportActionBar(mToolbar);
} }
@Override
protected void onResume() {
super.onResume();
//mLogView.start();
}
public void onCloseThisActivity(View view) { public void onCloseThisActivity(View view) {
WinBoLLActivityManager.getInstance().finish(this); WinBoLLActivityManager.getInstance().finish(this);
@@ -61,17 +46,4 @@ public class NewActivity extends WinBoLLActivity implements IWinBoLLActivity {
public void onNew2Activity(View view) { public void onNew2Activity(View view) {
// WinBoLLActivityManager.getInstance().startWinBoLLActivity(App.getInstance(), New2Activity.class); // WinBoLLActivityManager.getInstance().startWinBoLLActivity(App.getInstance(), New2Activity.class);
} }
@Override
public boolean onCreateOptionsMenu(Menu menu) {
//getMenuInflater().inflate(R.menu.toolbar_main, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// 在switch语句中处理每个ID并在处理完后返回true未处理的情况返回false。
return super.onOptionsItemSelected(item);
}
} }

View File

@@ -1,10 +1,8 @@
package cc.winboll.studio.winboll.activities; package cc.winboll.studio.winboll.activities;
import android.app.Activity;
import android.os.Bundle; import android.os.Bundle;
import android.view.View; import android.view.View;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
import cc.winboll.studio.libaes.utils.AESThemeUtil; import cc.winboll.studio.libaes.utils.AESThemeUtil;
import cc.winboll.studio.winboll.R; import cc.winboll.studio.winboll.R;
@@ -13,15 +11,10 @@ import cc.winboll.studio.winboll.R;
* @Date 2025/12/05 18:48 * @Date 2025/12/05 18:48
* @Describe Settings Activity * @Describe Settings Activity
*/ */
public class SettingsActivity extends WinBoLLActivity implements IWinBoLLActivity { public class SettingsActivity extends BaseWinBoLLActivity {
public static final String TAG = "SettingsActivity"; public static final String TAG = "SettingsActivity";
@Override
public Activity getActivity() {
return this;
}
@Override @Override
public String getTag() { public String getTag() {
return TAG; return TAG;

View File

@@ -1,211 +0,0 @@
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();
}
}

View File

@@ -1,47 +0,0 @@
package cc.winboll.studio.winboll.activities;
/**
* @Author ZhanGSKen<zhangsken@qq.com>
* @Date 2025/05/10 09:48
* @Describe WinBoLL 窗口基础类
*/
import android.app.Activity;
import android.os.Bundle;
import android.view.MenuItem;
import androidx.appcompat.app.AppCompatActivity;
import cc.winboll.studio.libappbase.LogUtils;
public class WinBoLLActivity extends AppCompatActivity {
public static final String TAG = "WinBoLLActivity";
@Override
protected void onResume() {
super.onResume();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
/*if (item.getItemId() == R.id.item_log) {
WinBoLLActivityManager.getInstance().startLogActivity(this);
return true;
} else if (item.getItemId() == R.id.item_home) {
startActivity(new Intent(this, MainActivity.class));
return true;
}*/
// 在switch语句中处理每个ID并在处理完后返回true未处理的情况返回false。
return super.onOptionsItemSelected(item);
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
//WinBoLLActivityManager.getInstance().add(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
//WinBoLLActivityManager.getInstance().registeRemove(this);
}
}

View File

@@ -0,0 +1,147 @@
package cc.winboll.studio.winboll.unittest;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.ToastUtils;
import cc.winboll.studio.winboll.MainActivity;
import cc.winboll.studio.winboll.R;
import cc.winboll.studio.winboll.activities.BaseWinBoLLActivity;
import java.io.File;
/**
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026/01/19 11:11
* @LastEditTime 2026/01/19 15:30
* @Describe Termux 环境测试新增读取Termux主目录文件列表功能
*/
public class TermuxEnvTestActivity extends BaseWinBoLLActivity {
public static final String TAG = "TermuxEnvTestActivity";
// Termux主目录固定路径
private static final String TERMUX_HOME_PATH = "/data/data/com.termux/files/home/WinBoLLStudio";
private Toolbar mToolbar;
@Override
public String getTag() {
return TAG;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_termux_env_test);
initToolbar();
}
private void initToolbar() {
LogUtils.d(TAG, "initToolbar() 开始初始化");
mToolbar = (Toolbar) findViewById(R.id.toolbar);
if (mToolbar == null) {
LogUtils.e(TAG, "initToolbar() | Toolbar未找到");
return;
}
setSupportActionBar(mToolbar);
mToolbar.setSubtitle(getTag());
((AppCompatActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LogUtils.d(TAG, "导航栏 点击返回按钮");
getActivity().finish();
WinBoLLActivityManager.getInstance().startWinBoLLActivity(getActivity(), MainActivity.class);
}
});
LogUtils.d(TAG, "initToolbar() 配置完成");
}
/**
* 核心功能读取Termux主目录文件列表返回字符串格式
* @return 目录路径+文件列表(换行分隔),异常时返回错误信息
*/
public String readTermuxHomeFileList() {
LogUtils.d(TAG, "开始读取Termux目录" + TERMUX_HOME_PATH);
File termuxHomeDir = new File(TERMUX_HOME_PATH);
StringBuilder result = new StringBuilder();
// 1. 检查目录是否存在
if (!termuxHomeDir.exists()) {
String errorMsg = "错误Termux目录不存在 → " + TERMUX_HOME_PATH;
LogUtils.e(TAG, errorMsg);
return errorMsg;
}
// 2. 检查是否为目录
if (!termuxHomeDir.isDirectory()) {
String errorMsg = "错误:指定路径不是目录 → " + TERMUX_HOME_PATH;
LogUtils.e(TAG, errorMsg);
return errorMsg;
}
// 3. 检查读写权限
if (!termuxHomeDir.canRead()) {
String errorMsg = "错误:无目录读取权限 → " + TERMUX_HOME_PATH;
LogUtils.e(TAG, errorMsg);
return errorMsg;
}
// 4. 获取目录下所有文件/子目录
File[] files = termuxHomeDir.listFiles();
if (files == null || files.length == 0) {
String emptyMsg = "Termux目录为空 → " + TERMUX_HOME_PATH;
LogUtils.d(TAG, emptyMsg);
return emptyMsg;
}
// 5. 拼接文件列表字符串(包含类型、名称、路径)
result.append("Termux主目录").append(TERMUX_HOME_PATH).append("\n");
result.append("文件/子目录总数:").append(files.length).append("\n");
result.append("-------------------------\n");
for (File file : files) {
String fileType = file.isDirectory() ? "[目录]" : "[文件]";
String fileName = file.getName();
String filePath = file.getAbsolutePath();
result.append(fileType).append(" ").append(fileName).append("").append(filePath).append("\n");
}
// 6. 去除末尾多余换行
String fileListStr = result.toString().trim();
LogUtils.d(TAG, "Termux目录读取完成结果长度" + fileListStr.length());
return fileListStr;
}
public void onTestTermuxEnv(View view) {
TextView tvMessage = findViewById(R.id.tv_message);
// 测试读取Termux目录文件列表并打印日志
String fileListStr = readTermuxHomeFileList();
tvMessage.append(fileListStr);
}
public void onTestTermuxCMD(View view) {
// pkg update && pkg upgrade -y
// pkg install termux-api -y
Intent intent = new Intent();
intent.setPackage("com.termux");
intent.setAction("com.termux.RUN_COMMAND");
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.putExtra("com.termux.RUN_COMMAND_PATH", "/data/data/com.termux/files/home");
intent.putExtra("com.termux.RUN_COMMAND_ARGUMENTS", new String[]{"ls"});
intent.putExtra("com.termux.RUN_COMMAND_WAIT_FOR_RESULT", false);
intent.putExtra("com.termux.RUN_COMMAND_BACKGROUND", false);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
} else {
Toast.makeText(this, "Termux或Termux:API未正确安装", Toast.LENGTH_SHORT).show();
}
}
}

View File

@@ -7,10 +7,10 @@ import android.os.Message;
import android.view.View; import android.view.View;
import android.widget.Button; import android.widget.Button;
import android.widget.Toast; import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity; import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.winboll.R; import cc.winboll.studio.winboll.R;
import cc.winboll.studio.winboll.activities.WinBoLLActivity;
/** /**
* @Author 豆包&ZhanGSKen<zhangsken@qq.com> * @Author 豆包&ZhanGSKen<zhangsken@qq.com>
@@ -18,7 +18,7 @@ import cc.winboll.studio.winboll.activities.WinBoLLActivity;
* @Describe 企业微信SDK接口测试基础调试版 * @Describe 企业微信SDK接口测试基础调试版
* 包含SDK初始化、基础接口调用、日志输出、主线程回调处理 * 包含SDK初始化、基础接口调用、日志输出、主线程回调处理
*/ */
public class TestWeWorkSpecSDK extends WinBoLLActivity implements IWinBoLLActivity, View.OnClickListener { public class TestWeWorkSpecSDK extends AppCompatActivity implements IWinBoLLActivity, View.OnClickListener {
public static final String TAG = "TestWeWorkSpecSDK"; public static final String TAG = "TestWeWorkSpecSDK";

View File

@@ -1,210 +0,0 @@
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);
}
}
}

View File

@@ -1,163 +0,0 @@
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();
}
}
}

View File

@@ -1,171 +0,0 @@
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);
}
}

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<cc.winboll.studio.libaes.views.ASupportToolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/toolbar"/>
<cc.winboll.studio.libappbase.views.AboutView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.0"
android:id="@+id/aboutview"/>
</LinearLayout>

View File

@@ -6,14 +6,6 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<cc.winboll.studio.winboll.CustomToolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:toolbarTitle="@string/app_name"
app:toolbarTitleColor="@color/colorAccent"
app:toolbarBackgroundColor="@color/colorPrimary"
android:id="@+id/toolbar"/>
<LinearLayout <LinearLayout
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@@ -6,7 +6,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<android.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/toolbar"/> android:id="@+id/toolbar"/>

View File

@@ -6,7 +6,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<android.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/toolbar"/> android:id="@+id/toolbar"/>

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<cc.winboll.studio.libaes.views.ASupportToolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/toolbar"/>
<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="TestTermuxCMD"
android:onClick="onTestTermuxCMD"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TestTermuxEnv"
android:onClick="onTestTermuxEnv"/>
</LinearLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.0">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Text"
android:id="@+id/tv_message"/>
</ScrollView>
</LinearLayout>

View File

@@ -8,6 +8,6 @@
android:id="@+id/item_settings" android:id="@+id/item_settings"
android:title="Settings"/> android:title="Settings"/>
<item <item
android:id="@+id/item_wxpayactivity" android:id="@+id/item_about"
android:title="WXPayActivity"/> android:title="About"/>
</menu> </menu>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/item_termux_env_test"
android:title="TermuxEnvTestActivity"/>
</menu>

View File

@@ -1,5 +1,6 @@
<resources> <resources>
<string name="app_name">WinBoLL</string> <string name="app_name">WinBoLL</string>
<string name="app_description">WinBoLL 网站浏览器。</string>
<string name="app_name_cn1">筋斗云</string> <string name="app_name_cn1">筋斗云</string>
<string name="app_name_cn2">金抖云</string> <string name="app_name_cn2">金抖云</string>
<string name="switchto_en1">WinBoLL</string> <string name="switchto_en1">WinBoLL</string>

View File

@@ -11,5 +11,11 @@
<certificates src="system" /> <certificates src="system" />
</trust-anchors> </trust-anchors>
</base-config> </base-config>
<!-- 允许特定域名的明文请求仅本地127.0.0.1 -->
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">127.0.0.1</domain>
<domain includeSubdomains="true">localhost</domain>
</domain-config>
</network-security-config> </network-security-config>