diff --git a/.gitignore b/.gitignore index 3be31ff..aa18ca5 100644 --- a/.gitignore +++ b/.gitignore @@ -94,8 +94,8 @@ lint-results.html ## 忽略 AndroidIDE 临时文件夹 .androidide -## WinBoLL 基础应用(避免上传敏感配置) +## MyWinBoLL 基础应用(避免上传敏感配置) /winboll.properties /local.properties -/settings.gradle -/gradle.properties \ No newline at end of file +#/settings.gradle +#/gradle.properties \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 261eeb9..0000000 --- a/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/LICENSE-Private b/LICENSE-Private new file mode 100644 index 0000000..dc5f366 --- /dev/null +++ b/LICENSE-Private @@ -0,0 +1,97 @@ +# WinBoLL 源码 LICENSE-Private-Demo 规范说明书 + +# LICENSE-Private-Demo + +# WinBoLL 源码公共转私有继承开发规范守则 + +## 核心声明 + +本文档**唯一核心设计目的**:通过文件标识、分支隔离、操作规范、责任界定四重约束,**从根源规避私有开发分支代码被人为合并、推送、提交至公共开源主流分支的风险**,明确人为操作失误、违规合并的全部责任归属,同时保证私有分支可正常同步、拉取公共主流分支的上游更新。 + +## 一、文件宗旨与风险防控说明 + +本文件为 WinBoLL 项目公共开源分支转为私有独立分支开发的**强制标准化操作手册与责任界定文件**,核心风控目标: + +1. 严格隔离公共开源分支与私有开发分支,通过授权文件标记实现分支属性一眼可辨,杜绝人为操作混淆 + +2. **重点防控人为操作导致的私有分支代码违规合并、回合、推送至公共 ****`winboll`**** 主流分支**,从流程上封堵合并风险 + +3. 明确所有开发提交者的操作责任,违规合并公共分支的行为由操作人承担全部代码泄露、合规风险 + +4. 规范私有分支初始化全流程,保证私有分支仅可单向同步公共分支更新,禁止任何反向代码流入公共分支 + +## 二、公私分支授权标识文件定义(风控核心依据) + +### 1. 公共开源分支唯一标识 + +**文件名:LICENSE** + +- 仅允许存在于公共主流分支 `winboll` 及官方公共衍生分支 + +- 标识当前分支为**开源公开可贡献分支**,遵循原开源授权协议 + +- **严禁私有分支内保留、恢复此文件**,出现即判定分支属性异常 + +### 2. 私有开发分支唯一标识 + +**文件名:LICENSE-Private** + +- 仅允许存在于私有开发分支,**绝对禁止出现在公共 ****`winboll`**** 分支** + +- 标识当前分支为**私有闭源分支**,代码仅限内部使用,禁止公开、禁止对外贡献 + +- 为本分支私有属性的法定判定依据,也是禁止合并至公共分支的核心标记 + +## 三、分支管理与合并风控规则(强制遵守) + +1. **公共主流分支**:固定为 `winboll`,为项目唯一开源主线,仅保留 `LICENSE` 文件,**禁止接收任何私有分支的合并、提交、推送请求**。 + +2. **私有开发分支**:统一从 `winboll` 分支检出,命名固定格式为 `private-demo-*`,与公共分支物理隔离。 + +3. **核心合并风控铁则** + + - 私有分支 → 公共分支:**永久禁止任何形式的合并、推送、PR 提交、代码回合,人为操作也绝不允许** + + - 公共分支 → 私有分支:允许正常拉取、同步上游更新,不影响私有开发迭代 + +4. 所有仓库提交者、合并操作者,均视为已阅读并完全认可本规则,**人为执行私有分支向公共分支的合并操作,由操作人承担全部代码泄露、合规违约、项目安全风险**。 + +## 四、公共转私有标准化操作步骤(锁死合并风险) + +请严格按顺序执行,每一步均为风控必要环节,不可跳过、不可修改顺序。 + +1. 基于公共主流分支 `winboll`,新建私有开发分支,严格使用 `private-demo-*` 命名,从名称上明确分支私有属性,避免人为混淆。 + +2. 本地仓库切换至新建私有分支,确认当前分支名称、检出来源无误。 + +3. **永久删除项目根目录公共授权文件 ****`LICENSE`**,彻底移除公共分支标识,断绝误合并的标识漏洞。 + +4. 将本规范文件 `LICENSE-Private-Demo` 复制并重命名为 `LICENSE-Private`,作为私有分支生效授权文件。 + +5. 将以上所有变更执行一次性 Git 提交,**提交信息必须固定使用以下内容,不可修改**: + + > 初始化私有开发分支,已切换私有授权文件,本分支禁止任何人为合并、推送至 winboll 公共分支 + > + > + +6. 提交完成后,本分支正式转为私有开发状态,后续所有代码提交、分支合并、版本迭代,均严禁指向公共 `winboll` 分支。 + +## 五、人为操作责任界定(核心补充条款) + +1. 本分支所有开发者、代码提交者、分支合并操作者,均视为**完全知晓本分支的私有属性与合并禁令**,自愿遵守本规范全部约束。 + +2. **无论故意或过失,凡是人为执行私有分支向公共 ****`winboll`**** 分支的合并、推送、PR 提交、代码回合操作,全部责任由执行操作的本人独立承担**,项目方不承担任何因人为违规操作导致的代码泄露、开源合规、版本污染风险。 + +3. 仓库管理员需严格校验合并请求的分支标识与授权文件,发现带有 `LICENSE-Private` 标记的分支申请合并至公共分支,一律直接拒绝,并记录操作人信息。 + +4. 分支属性校验以根目录授权文件为唯一标准:只要分支内存在 `LICENSE-Private` 文件,就绝对禁止向公共分支发起任何合并操作。 + +## 六、分支状态校验与异常处理 + +- 合规公共分支:仅存在 `LICENSE`,无 `LICENSE-Private` + +- 合规私有分支:仅存在 `LICENSE-Private`,无 `LICENSE` + +- 异常状态:两个文件同时存在 / 均不存在 → 立即停止开发与提交,按本规范重置分支状态,严禁执行任何合并操作 + +> (注:文档部分内容可能由 AI 生成) diff --git a/autonfc/README.md b/autonfc/README.md deleted file mode 100644 index 7421e1d..0000000 --- a/autonfc/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# AutoNFC - -#### 介绍 -NFC 卡应用,主要管理 NFC 卡接触手机的动作响应,NFC 接触状态用于作为其他应用激活活动动作的启动令牌。 - -#### 软件架构 -适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。 -也适配安卓应用 [AndroidIDE] 的 Gradle 编译结构。 - - -#### Gradle 编译说明 -调试版编译命令 :gradle assembleBetaDebug -阶段版编译命令 :bash .winboll/bashPublishAPKAddTag.sh autonfc - -#### 使用说明 - -#### 参与贡献 - -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 : ZhanGSKen(ZhanGSKen) -4. 新建 Pull Request - - -#### 特技 - -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) - -#### 参考文档 diff --git a/autonfc/build.properties b/autonfc/build.properties deleted file mode 100644 index 4599e56..0000000 --- a/autonfc/build.properties +++ /dev/null @@ -1,8 +0,0 @@ -#Created by .winboll/winboll_app_build.gradle -#Mon Mar 16 18:30:19 GMT 2026 -stageCount=0 -libraryProject= -baseVersion=15.11 -publishVersion=15.0.0 -buildCount=54 -baseBetaVersion=15.0.1 diff --git a/autonfc/proguard-rules.pro b/autonfc/proguard-rules.pro deleted file mode 100644 index 64b4a05..0000000 --- a/autonfc/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/autonfc/src/main/AndroidManifest.xml b/autonfc/src/main/AndroidManifest.xml deleted file mode 100644 index 97234a5..0000000 --- a/autonfc/src/main/AndroidManifest.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/autonfc/src/main/java/cc/winboll/studio/autonfc/App.java b/autonfc/src/main/java/cc/winboll/studio/autonfc/App.java deleted file mode 100644 index d64ac77..0000000 --- a/autonfc/src/main/java/cc/winboll/studio/autonfc/App.java +++ /dev/null @@ -1,344 +0,0 @@ -package cc.winboll.studio.autonfc; - -import android.app.Activity; -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageInfo; -import android.content.res.Resources; -import android.graphics.Typeface; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.text.TextUtils; -import android.util.Log; -import android.view.Gravity; -import android.view.Menu; -import android.view.MenuItem; -import android.view.ViewGroup; -import android.widget.HorizontalScrollView; -import android.widget.ScrollView; -import android.widget.TextView; -import android.widget.Toast; -import cc.winboll.studio.libappbase.GlobalApplication; -import cc.winboll.studio.libappbase.ToastUtils; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.Closeable; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.Thread.UncaughtExceptionHandler; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Arrays; -import java.util.Date; -import java.util.LinkedHashMap; -import java.util.concurrent.atomic.AtomicBoolean; - -public class App extends GlobalApplication { - - private static Handler MAIN_HANDLER = new Handler(Looper.getMainLooper()); - - @Override - public void onCreate() { - super.onCreate(); - - // 初始化 Toast 框架 -// ToastUtils.init(this); -// // 设置 Toast 布局样式 -// //ToastUtils.setView(R.layout.view_toast); -// ToastUtils.setStyle(new WhiteToastStyle()); -// ToastUtils.setGravity(Gravity.BOTTOM, 0, 200); -// - //CrashHandler.getInstance().registerGlobal(this); - //CrashHandler.getInstance().registerPart(this); - } - - public static void write(InputStream input, OutputStream output) throws IOException { - byte[] buf = new byte[1024 * 8]; - int len; - while ((len = input.read(buf)) != -1) { - output.write(buf, 0, len); - } - } - - public static void write(File file, byte[] data) throws IOException { - File parent = file.getParentFile(); - if (parent != null && !parent.exists()) parent.mkdirs(); - - ByteArrayInputStream input = new ByteArrayInputStream(data); - FileOutputStream output = new FileOutputStream(file); - try { - write(input, output); - } finally { - closeIO(input, output); - } - } - - public static String toString(InputStream input) throws IOException { - ByteArrayOutputStream output = new ByteArrayOutputStream(); - write(input, output); - try { - return output.toString("UTF-8"); - } finally { - closeIO(input, output); - } - } - - public static void closeIO(Closeable... closeables) { - for (Closeable closeable : closeables) { - try { - if (closeable != null) closeable.close(); - } catch (IOException ignored) {} - } - } - - public static class CrashHandler { - - public static final UncaughtExceptionHandler DEFAULT_UNCAUGHT_EXCEPTION_HANDLER = Thread.getDefaultUncaughtExceptionHandler(); - - private static CrashHandler sInstance; - - private PartCrashHandler mPartCrashHandler; - - public static CrashHandler getInstance() { - if (sInstance == null) { - sInstance = new CrashHandler(); - } - return sInstance; - } - - public void registerGlobal(Context context) { - registerGlobal(context, null); - } - - public void registerGlobal(Context context, String crashDir) { - Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandlerImpl(context.getApplicationContext(), crashDir)); - } - - public void unregister() { - Thread.setDefaultUncaughtExceptionHandler(DEFAULT_UNCAUGHT_EXCEPTION_HANDLER); - } - - public void registerPart(Context context) { - unregisterPart(context); - mPartCrashHandler = new PartCrashHandler(context.getApplicationContext()); - MAIN_HANDLER.postAtFrontOfQueue(mPartCrashHandler); - } - - public void unregisterPart(Context context) { - if (mPartCrashHandler != null) { - mPartCrashHandler.isRunning.set(false); - mPartCrashHandler = null; - } - } - - private static class PartCrashHandler implements Runnable { - - private final Context mContext; - - public AtomicBoolean isRunning = new AtomicBoolean(true); - - public PartCrashHandler(Context context) { - this.mContext = context; - } - - @Override - public void run() { - while (isRunning.get()) { - try { - Looper.loop(); - } catch (final Throwable e) { - e.printStackTrace(); - if (isRunning.get()) { - MAIN_HANDLER.post(new Runnable(){ - - @Override - public void run() { - Toast.makeText(mContext, e.toString(), Toast.LENGTH_LONG).show(); - } - }); - } else { - if (e instanceof RuntimeException) { - throw (RuntimeException)e; - } else { - throw new RuntimeException(e); - } - } - } - } - } - } - - private static class UncaughtExceptionHandlerImpl implements UncaughtExceptionHandler { - - private static DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy_MM_dd-HH_mm_ss"); - - private final Context mContext; - - private final File mCrashDir; - - public UncaughtExceptionHandlerImpl(Context context, String crashDir) { - this.mContext = context; - this.mCrashDir = TextUtils.isEmpty(crashDir) ? new File(mContext.getExternalCacheDir(), "crash") : new File(crashDir); - } - - @Override - public void uncaughtException(Thread thread, Throwable throwable) { - try { - - String log = buildLog(throwable); - writeLog(log); - - try { - Intent intent = new Intent(mContext, CrashActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(Intent.EXTRA_TEXT, log); - mContext.startActivity(intent); - } catch (Throwable e) { - e.printStackTrace(); - writeLog(e.toString()); - } - - throwable.printStackTrace(); - android.os.Process.killProcess(android.os.Process.myPid()); - System.exit(0); - - } catch (Throwable e) { - if (DEFAULT_UNCAUGHT_EXCEPTION_HANDLER != null) DEFAULT_UNCAUGHT_EXCEPTION_HANDLER.uncaughtException(thread, throwable); - } - } - - private String buildLog(Throwable throwable) { - String time = DATE_FORMAT.format(new Date()); - - String versionName = "unknown"; - long versionCode = 0; - try { - PackageInfo packageInfo = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0); - versionName = packageInfo.versionName; - versionCode = Build.VERSION.SDK_INT >= 28 ? packageInfo.getLongVersionCode() : packageInfo.versionCode; - } catch (Throwable ignored) {} - - LinkedHashMap head = new LinkedHashMap(); - head.put("Time Of Crash", time); - head.put("Device", String.format("%s, %s", Build.MANUFACTURER, Build.MODEL)); - head.put("Android Version", String.format("%s (%d)", Build.VERSION.RELEASE, Build.VERSION.SDK_INT)); - head.put("App Version", String.format("%s (%d)", versionName, versionCode)); - head.put("Kernel", getKernel()); - head.put("Support Abis", Build.VERSION.SDK_INT >= 21 && Build.SUPPORTED_ABIS != null ? Arrays.toString(Build.SUPPORTED_ABIS): "unknown"); - head.put("Fingerprint", Build.FINGERPRINT); - - StringBuilder builder = new StringBuilder(); - - for (String key : head.keySet()) { - if (builder.length() != 0) builder.append("\n"); - builder.append(key); - builder.append(" : "); - builder.append(head.get(key)); - } - - builder.append("\n\n"); - builder.append(Log.getStackTraceString(throwable)); - - return builder.toString(); - } - - private void writeLog(String log) { - String time = DATE_FORMAT.format(new Date()); - File file = new File(mCrashDir, "crash_" + time + ".txt"); - try { - write(file, log.getBytes("UTF-8")); - } catch (Throwable e) { - e.printStackTrace(); - } - } - - private static String getKernel() { - try { - return App.toString(new FileInputStream("/proc/version")).trim(); - } catch (Throwable e) { - return e.getMessage(); - } - } - } - } - - public static final class CrashActivity extends Activity { - - private String mLog; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setTheme(android.R.style.Theme_DeviceDefault); - setTitle("App Crash"); - - mLog = getIntent().getStringExtra(Intent.EXTRA_TEXT); - - ScrollView contentView = new ScrollView(this); - contentView.setFillViewport(true); - - HorizontalScrollView horizontalScrollView = new HorizontalScrollView(this); - - TextView textView = new TextView(this); - int padding = dp2px(16); - textView.setPadding(padding, padding, padding, padding); - textView.setText(mLog); - textView.setTextIsSelectable(true); - textView.setTypeface(Typeface.DEFAULT); - textView.setLinksClickable(true); - - horizontalScrollView.addView(textView); - contentView.addView(horizontalScrollView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); - - setContentView(contentView); - } - - private void restart() { - Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName()); - if (intent != null) { - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); - } - finish(); - android.os.Process.killProcess(android.os.Process.myPid()); - System.exit(0); - } - - private static int dp2px(float dpValue) { - final float scale = Resources.getSystem().getDisplayMetrics().density; - return (int) (dpValue * scale + 0.5f); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - menu.add(0, android.R.id.copy, 0, android.R.string.copy) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); - return super.onCreateOptionsMenu(menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.copy: - ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); - cm.setPrimaryClip(ClipData.newPlainText(getPackageName(), mLog)); - return true; - } - return super.onOptionsItemSelected(item); - } - - @Override - public void onBackPressed() { - restart(); - } - } -} diff --git a/autonfc/src/main/java/cc/winboll/studio/autonfc/MainActivity.java b/autonfc/src/main/java/cc/winboll/studio/autonfc/MainActivity.java deleted file mode 100644 index 03743b7..0000000 --- a/autonfc/src/main/java/cc/winboll/studio/autonfc/MainActivity.java +++ /dev/null @@ -1,180 +0,0 @@ -package cc.winboll.studio.autonfc; - -import android.app.PendingIntent; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.nfc.NfcAdapter; -import android.os.Bundle; -import android.os.IBinder; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; -import cc.winboll.studio.autonfc.nfc.ActionDialog; -import cc.winboll.studio.autonfc.nfc.AutoNFCService; -import cc.winboll.studio.autonfc.nfc.NFCInterfaceActivity; -import cc.winboll.studio.libappbase.LogActivity; -import cc.winboll.studio.libappbase.LogUtils; - -public class MainActivity extends AppCompatActivity { - - public static final String TAG = "MainActivity"; - - private NfcAdapter mNfcAdapter; - private PendingIntent mPendingIntent; - private AutoNFCService mService; - private boolean mBound = false; - - // 服务连接 - private ServiceConnection mConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - AutoNFCService.LocalBinder binder = (AutoNFCService.LocalBinder) service; - mService = binder.getService(); - mBound = true; - LogUtils.d(TAG, "onServiceConnected: 服务已绑定"); - - // 关键:把 Activity 传给 Service,用于回调 - mService.attachActivity(MainActivity.this); - } - - @Override - public void onServiceDisconnected(ComponentName name) { - mBound = false; - mService = null; - LogUtils.d(TAG, "onServiceDisconnected: 服务已断开"); - } - }; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - Toolbar toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - - // 初始化 NFC - mNfcAdapter = NfcAdapter.getDefaultAdapter(this); - Intent nfcIntent = new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); - mPendingIntent = PendingIntent.getActivity(this, 0, nfcIntent, 0); - - LogUtils.d(TAG, "onCreate() -> NFC 监听已绑定到 MainActivity"); - } - - @Override - protected void onStart() { - super.onStart(); - // 绑定服务 - Intent intent = new Intent(this, AutoNFCService.class); - bindService(intent, mConnection, Context.BIND_AUTO_CREATE); - LogUtils.d(TAG, "onStart: 绑定服务"); - } - - @Override - protected void onStop() { - super.onStop(); - // 解绑服务 - if (mBound) { - unbindService(mConnection); - mBound = false; - LogUtils.d(TAG, "onStop: 解绑服务"); - } - } - - @Override - protected void onResume() { - super.onResume(); - LogUtils.d(TAG, "onResume() -> 开启 NFC 前台分发"); - if (mNfcAdapter != null) { - mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null); - } - } - - @Override - protected void onPause() { - super.onPause(); - LogUtils.d(TAG, "onPause() -> 关闭 NFC 前台分发"); - if (mNfcAdapter != null) { - mNfcAdapter.disableForegroundDispatch(this); - } - } - - // NFC 卡片靠近唯一入口 - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - LogUtils.d(TAG, "onNewIntent() -> 检测到 NFC 卡片"); - - // 把 NFC 事件交给 Service 处理 - if (mBound && mService != null) { - mService.handleNfcIntent(intent); - } else { - LogUtils.e(TAG, "服务未绑定,无法处理 NFC"); - } - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.main_menu, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - int id = item.getItemId(); - - if (id == R.id.menu_log) { - LogActivity.startLogActivity(this); - return true; - } - - return super.onOptionsItemSelected(item); - } - - public void onNFCInterfaceActivity(View view) { - startActivity(new Intent(this, NFCInterfaceActivity.class)); - } - - // ========================= 【新增】关键方法:由 Service 回调来弹出对话框 ========================= - /** - * Service 解析完 NFC 数据后,回调此方法在 Activity 中弹出对话框 - */ - public void showNfcActionDialog(final String nfcData) { - LogUtils.d(TAG, "showNfcActionDialog() -> Activity 存活,安全弹出对话框"); - - // Activity 正在运行,直接弹框,绝对不会报 BadTokenException - final ActionDialog dialog = new ActionDialog(this); - dialog.setNfcData(nfcData); - dialog.setButtonClickListener(new ActionDialog.OnButtonClickListener() { - @Override - public void onBuildClick() { - LogUtils.d(TAG, "点击 Build"); - if (mService != null) { - mService.executeTermuxCommand(AutoNFCService.ACTION_BUILD, nfcData); - } - dialog.dismiss(); - } - - @Override - public void onViewClick() { - LogUtils.d(TAG, "点击 View"); - if (mService != null) { - mService.executeTermuxCommand(AutoNFCService.ACTION_BUILD_VIEW, nfcData); - } - dialog.dismiss(); - } - - @Override - public void onCancelClick() { - dialog.dismiss(); - } - }); - - dialog.show(); - } -} - diff --git a/autonfc/src/main/java/cc/winboll/studio/autonfc/models/NfcTermuxCmd.java b/autonfc/src/main/java/cc/winboll/studio/autonfc/models/NfcTermuxCmd.java deleted file mode 100644 index faab4cb..0000000 --- a/autonfc/src/main/java/cc/winboll/studio/autonfc/models/NfcTermuxCmd.java +++ /dev/null @@ -1,66 +0,0 @@ -package cc.winboll.studio.autonfc.models; - -/** - * @Author 豆包&ZhanGSKen - * @Date 2026/03/16 09:38 - */ -public class NfcTermuxCmd { - - private String script; // 要执行的预制脚本名(如 auth.sh) - private String[] args; // 脚本参数 - private String workDir; // 工作目录 - private boolean background; // 是否后台执行 - private String resultDir; // 结果输出目录(可为 null) - - public NfcTermuxCmd() { - } - - public NfcTermuxCmd(String script, String[] args, String workDir, boolean background, String resultDir) { - this.script = script; - this.args = args; - this.workDir = workDir; - this.background = background; - this.resultDir = resultDir; - } - - public String getScript() { - return script; - } - - public void setScript(String script) { - this.script = script; - } - - public String[] getArgs() { - return args; - } - - public void setArgs(String[] args) { - this.args = args; - } - - public String getWorkDir() { - return workDir; - } - - public void setWorkDir(String workDir) { - this.workDir = workDir; - } - - public boolean isBackground() { - return background; - } - - public void setBackground(boolean background) { - this.background = background; - } - - public String getResultDir() { - return resultDir; - } - - public void setResultDir(String resultDir) { - this.resultDir = resultDir; - } -} - diff --git a/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/ActionDialog.java b/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/ActionDialog.java deleted file mode 100644 index c3f65d7..0000000 --- a/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/ActionDialog.java +++ /dev/null @@ -1,123 +0,0 @@ -package cc.winboll.studio.autonfc.nfc; - -import android.app.Dialog; -import android.content.Context; -import android.view.View; -import android.widget.Button; -import android.widget.LinearLayout; - -import cc.winboll.studio.autonfc.R; -import cc.winboll.studio.libappbase.LogUtils; - -/** - * 自定义对话框类,用于与用户交互,展示 NFC 相关操作选项 - * 兼容 Java 7 语法 - * - * @author 豆包&ZhanGSKen - * @create 2025-08-15 - * @lastModify 2026-03-17 - */ -public class ActionDialog extends Dialog { - - private static final String TAG = "ActionDialog"; - private String mNfcData; - private OnButtonClickListener mClickListener; - - /** - * 构造函数 - */ - public ActionDialog(Context context) { - super(context); - initDialog(); - } - - /** - * 设置 NFC 数据 - */ - public void setNfcData(String nfcData) { - this.mNfcData = nfcData; - LogUtils.d(TAG, "setNfcData() -> " + nfcData); - } - - /** - * 设置点击监听 - */ - public void setButtonClickListener(OnButtonClickListener listener) { - this.mClickListener = listener; - } - - /** - * 初始化布局 - */ - private void initDialog() { - setTitle("请选择操作"); - - LinearLayout layout = new LinearLayout(getContext()); - layout.setOrientation(LinearLayout.VERTICAL); - layout.setPadding(20, 20, 20, 20); - - addButtons(layout); - - setContentView(layout); - } - - /** - * 添加按钮 - */ - private void addButtons(LinearLayout layout) { - // Build 按钮 - Button btnBuild = createButton("Build", new View.OnClickListener() { - @Override - public void onClick(View v) { - LogUtils.d(TAG, "点击 Build"); - if (mClickListener != null) { - mClickListener.onBuildClick(); - } - } - }); - layout.addView(btnBuild); - - // View 按钮 - Button btnView = createButton("View", new View.OnClickListener() { - @Override - public void onClick(View v) { - LogUtils.d(TAG, "点击 View"); - if (mClickListener != null) { - mClickListener.onViewClick(); - } - } - }); - layout.addView(btnView); - - // 取消按钮 - Button btnCancel = createButton("Cancel", new View.OnClickListener() { - @Override - public void onClick(View v) { - LogUtils.d(TAG, "点击 Cancel"); - dismiss(); - } - }); - layout.addView(btnCancel); - } - - /** - * 创建按钮 - */ - private Button createButton(String text, View.OnClickListener listener) { - Button button = new Button(getContext()); - button.setText(text); - button.setPadding(10, 10, 10, 10); - button.setOnClickListener(listener); - return button; - } - - /** - * 回调接口 - */ - public interface OnButtonClickListener { - void onBuildClick(); - void onViewClick(); - void onCancelClick(); - } -} - diff --git a/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/AutoNFCService.java b/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/AutoNFCService.java deleted file mode 100644 index 0addf65..0000000 --- a/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/AutoNFCService.java +++ /dev/null @@ -1,202 +0,0 @@ -package cc.winboll.studio.autonfc.nfc; - -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.nfc.NdefMessage; -import android.nfc.NdefRecord; -import android.nfc.NfcAdapter; -import android.nfc.Tag; -import android.nfc.tech.Ndef; -import android.os.Binder; -import android.os.IBinder; - -import cc.winboll.studio.autonfc.MainActivity; -import cc.winboll.studio.libappbase.LogUtils; -import cc.winboll.studio.libappbase.ToastUtils; - -import java.nio.charset.Charset; -import java.util.Arrays; - -public class AutoNFCService extends Service { - - public static final String TAG = "AutoNFCService"; - - // ================= 已修改:更新为 Beta 包名 ================= - public static final String ACTION_BUILD = "cc.winboll.studio.winboll.termux.NfcTermuxBridgeActivity.ACTION_BUILD"; - public static final String ACTION_BUILD_VIEW = "cc.winboll.studio.winboll.termux.NfcTermuxBridgeActivity.ACTION_BUILD_VIEW"; - - private final IBinder mBinder = new LocalBinder(); - private String mNfcData; - private MainActivity mActivity; // 持有 Activity 引用,用于回调 - - // ========================= 生命周期 ========================= - @Override - public void onCreate() { - super.onCreate(); - LogUtils.d(TAG, "onCreate() -> 服务创建"); - // 移除:startForeground(NOTIFICATION_ID, buildNotification()); - } - - @Override - public void onDestroy() { - super.onDestroy(); - LogUtils.d(TAG, "onDestroy() -> 服务已停止"); - mActivity = null; // 释放引用 - } - - // ========================= 服务绑定 ========================= - @Override - public IBinder onBind(Intent intent) { - LogUtils.d(TAG, "onBind() -> 服务被绑定"); - return mBinder; - } - - @Override - public boolean onUnbind(Intent intent) { - LogUtils.d(TAG, "onUnbind() -> 服务解绑"); - // 移除:stopForeground(true); - stopSelf(); - return super.onUnbind(intent); - } - - // ========================= 对外暴露方法 ========================= - /** - * 绑定 Activity,用于回调显示对话框 - */ - public void attachActivity(MainActivity activity) { - this.mActivity = activity; - } - - /** - * 处理 NFC 意图 - */ - public void handleNfcIntent(Intent intent) { - LogUtils.d(TAG, "handleNfcIntent() -> 开始处理"); - - if (intent == null) { - LogUtils.e(TAG, "handleNfcIntent() -> 参数 intent 为空"); - return; - } - - String action = intent.getAction(); - LogUtils.d(TAG, "handleNfcIntent() -> Action = " + action); - - if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action) - || NfcAdapter.ACTION_TECH_DISCOVERED.equals(action) - || NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) { - - LogUtils.d(TAG, "handleNfcIntent() -> 匹配 NFC 动作"); - - Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); - if (tag == null) { - LogUtils.e(TAG, "handleNfcIntent() -> Tag 为空"); - return; - } - - LogUtils.d(TAG, "handleNfcIntent() -> Tag ID = " + bytesToHexString(tag.getId())); - LogUtils.d(TAG, "handleNfcIntent() -> Tech List = " + Arrays.toString(tag.getTechList())); - - parseNdefData(tag); - } - } - - // ========================= 内部业务 ========================= - private void parseNdefData(Tag tag) { - LogUtils.d(TAG, "parseNdefData() -> 开始解析"); - - if (tag == null) return; - - Ndef ndef = Ndef.get(tag); - if (ndef == null) { - LogUtils.e(TAG, "parseNdefData() -> 不支持 NDEF 格式"); - return; - } - - try { - ndef.connect(); - NdefMessage msg = ndef.getNdefMessage(); - - if (msg == null || msg.getRecords() == null || msg.getRecords().length == 0) { - LogUtils.w(TAG, "parseNdefData() -> 卡片无数据"); - return; - } - - NdefRecord record = msg.getRecords()[0]; - byte[] payload = record.getPayload(); - - int langLen = payload[0] & 0x3F; - int start = 1 + langLen; - - if (start < payload.length) { - mNfcData = new String(payload, start, payload.length - start, Charset.forName("UTF-8")); - LogUtils.d(TAG, "parseNdefData() -> 读卡成功: " + mNfcData); - - // 关键:回调给 Activity 弹框,此时 Activity 一定是存活状态 - if (mActivity != null) { - mActivity.showNfcActionDialog(mNfcData); - } - } - - } catch (Exception e) { - LogUtils.e(TAG, "parseNdefData() -> 读取失败", e); - } finally { - try { - ndef.close(); - } catch (Exception e) { - // 忽略关闭异常 - } - } - } - - /** - * 执行 Termux 命令 - */ - public void executeTermuxCommand(String action, String nfcData) { - LogUtils.d(TAG, "executeTermuxCommand() -> 开始执行"); - - if (nfcData == null || nfcData.isEmpty()) { - ToastUtils.show("数据错误"); - return; - } - - try { - LogUtils.d(TAG, "executeTermuxCommand() -> 发送指令: " + nfcData); - - Intent bridgeIntent = new Intent(action); - - // ================= 已修改:使用 Beta 包名 ================= - bridgeIntent.setClassName( - "cc.winboll.studio.winboll.beta", - "cc.winboll.studio.winboll.termux.NfcTermuxBridgeActivity" - ); - - bridgeIntent.putExtra(Intent.EXTRA_TEXT, nfcData); - bridgeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - - startActivity(bridgeIntent); - ToastUtils.show("指令已发送"); - } catch (Exception e) { - LogUtils.e(TAG, "executeTermuxCommand() -> 发送失败", e); - ToastUtils.show("发送失败"); - } - } - - // ========================= 工具方法 ========================= - private String bytesToHexString(byte[] bytes) { - if (bytes == null || bytes.length == 0) return ""; - StringBuilder sb = new StringBuilder(); - for (byte b : bytes) { - sb.append(String.format("%02X", b)); - } - return sb.toString(); - } - - // ========================= Binder ========================= - public class LocalBinder extends Binder { - public AutoNFCService getService() { - return AutoNFCService.this; - } - } -} - diff --git a/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/NFCInterfaceActivity.java b/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/NFCInterfaceActivity.java deleted file mode 100644 index 28b6f18..0000000 --- a/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/NFCInterfaceActivity.java +++ /dev/null @@ -1,230 +0,0 @@ -package cc.winboll.studio.autonfc.nfc; - -import android.app.Activity; -import android.app.PendingIntent; -import android.content.Intent; -import android.nfc.NfcAdapter; -import android.nfc.Tag; -import android.os.Bundle; -import android.view.View; -import android.widget.EditText; -import android.widget.TextView; -import android.widget.Toast; -import cc.winboll.studio.autonfc.R; -import cc.winboll.studio.autonfc.models.NfcTermuxCmd; -import cc.winboll.studio.libappbase.LogUtils; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; - -public class NFCInterfaceActivity extends Activity { - - public static final String TAG = "NFCInterfaceActivity"; - - private EditText et_script; - private EditText et_args; - private EditText et_workDir; - private EditText et_background; - private EditText et_resultDir; - - private TextView tvResult; - private TextView tvStatus; - - private NfcAdapter mNfcAdapter; - private PendingIntent mNfcPendingIntent; - private Tag mCurrentTag; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_nfc_interface); - initView(); - initNfc(); - } - - private void initView() { - et_script = findViewById(R.id.et_script); - et_args = findViewById(R.id.et_args); - et_workDir = findViewById(R.id.et_workDir); - et_background = findViewById(R.id.et_background); - et_resultDir = findViewById(R.id.et_resultDir); - - tvResult = findViewById(R.id.tv_result); - tvStatus = findViewById(R.id.tv_status); - } - - private void initNfc() { - mNfcAdapter = NfcAdapter.getDefaultAdapter(this); - - if (mNfcAdapter == null) { - tvStatus.setText("设备不支持NFC"); - return; - } - if (!mNfcAdapter.isEnabled()) { - tvStatus.setText("请开启NFC"); - return; - } - - Intent nfcIntent = new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); - mNfcPendingIntent = PendingIntent.getActivity(this, 0, nfcIntent, PendingIntent.FLAG_UPDATE_CURRENT); - - tvStatus.setText("NFC已启动,等待卡片靠近"); - } - - @Override - protected void onResume() { - super.onResume(); - if (mNfcAdapter != null && mNfcAdapter.isEnabled()) { - mNfcAdapter.enableForegroundDispatch(this, mNfcPendingIntent, null, null); - } - } - - @Override - protected void onPause() { - super.onPause(); - if (mNfcAdapter != null) { - mNfcAdapter.disableForegroundDispatch(this); - } - mCurrentTag = null; - } - - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - mCurrentTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); - if (mCurrentTag == null) return; - - tvStatus.setText("卡片已连接,解析中..."); - readNfc(); - } - - // ------------------------------------------------------------------------- - // 读取 NFC(完全委托给工具类) - // ------------------------------------------------------------------------- - private void readNfc() { - try { - NfcTermuxCmd cmd = NfcUtils.readTag(mCurrentTag); - if (cmd == null) { - tvStatus.setText("读取成功:标签为空"); - tvResult.setText(""); - // 清空窗体 - clearUiFields(); - return; - } - - // 核心改动:读取成功后,同时更新详情显示 和 窗体输入框 - updateUiWithCmd(cmd); - - } catch (Exception e) { - LogUtils.e(TAG, "readNfc 失败", e); - tvStatus.setText("读取失败:" + e.getMessage()); - // 出错时清空窗体 - clearUiFields(); - } - } - - // ------------------------------------------------------------------------- - // 新增:根据读取到的 Cmd 填充 UI(详情 + 窗体) - // ------------------------------------------------------------------------- - private void updateUiWithCmd(NfcTermuxCmd cmd) { - if (cmd == null) return; - - String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA).format(new Date()); - String show = "【读取时间】 " + time + "\n\n" - + "【解析结果】\n" - + "script: " + cmd.getScript() + "\n" - + "args: " + (cmd.getArgs() != null ? String.join(", ", cmd.getArgs()) : "[]") + "\n" - + "workDir: " + cmd.getWorkDir() + "\n" - + "background: " + cmd.isBackground() + "\n" - + "resultDir: " + cmd.getResultDir(); - - tvResult.setText(show); - tvStatus.setText("读取成功!"); - - // 👇 关键逻辑:自动填入窗体(每次读取后都会覆盖输入框) - et_script.setText(cmd.getScript() != null ? cmd.getScript() : ""); - et_args.setText(cmd.getArgs() != null ? String.join(",", cmd.getArgs()) : ""); - et_workDir.setText(cmd.getWorkDir() != null ? cmd.getWorkDir() : ""); - et_background.setText(String.valueOf(cmd.isBackground())); - et_resultDir.setText(cmd.getResultDir() != null ? cmd.getResultDir() : ""); - } - - // ------------------------------------------------------------------------- - // 辅助:清空所有输入框 - // ------------------------------------------------------------------------- - private void clearUiFields() { - et_script.setText(""); - et_args.setText(""); - et_workDir.setText(""); - et_background.setText(""); - et_resultDir.setText(""); - } - - // ------------------------------------------------------------------------- - // 写入按钮(委托给工具类) - // ------------------------------------------------------------------------- - public void onWriteClick(View view) { - if (mCurrentTag == null) { - showToast("请先靠近卡片"); - return; - } - - try { - NfcTermuxCmd cmd = buildCmdFromUI(); - NfcUtils.writeTag(mCurrentTag, cmd); - - tvStatus.setText("写入成功!"); - showToast("写入成功"); - readNfc(); // 写入后重读,此时会自动填入窗体 - - } catch (Exception e) { - LogUtils.e(TAG, "写入失败", e); - tvStatus.setText("写入失败:" + e.getMessage()); - showToast("写入失败"); - } - } - - // ------------------------------------------------------------------------- - // 填充调试数据 - // ------------------------------------------------------------------------- - public void onFillTestDataClick(View view) { - String testJson = "{\"script\":\"BuildWinBoLLProject.sh\",\"args\":[\"DebugTemp\"],\"workDir\":null,\"background\":true,\"resultDir\":null}"; - try { - NfcTermuxCmd cmd = NfcUtils.jsonToCmd(testJson); - et_script.setText(cmd.getScript()); - et_args.setText(cmd.getArgs() != null ? String.join(",", cmd.getArgs()) : ""); - et_workDir.setText(cmd.getWorkDir() != null ? cmd.getWorkDir() : ""); - et_background.setText(String.valueOf(cmd.isBackground())); - et_resultDir.setText(cmd.getResultDir() != null ? cmd.getResultDir() : ""); - - showToast("调试数据已填入"); - } catch (Exception e) { - showToast("解析失败"); - } - } - - // ------------------------------------------------------------------------- - // 从 UI 构建 NfcTermuxCmd - // ------------------------------------------------------------------------- - private NfcTermuxCmd buildCmdFromUI() { - String script = et_script.getText().toString().trim(); - String argsStr = et_args.getText().toString().trim(); - String workDir = et_workDir.getText().toString().trim(); - String bgStr = et_background.getText().toString().trim(); - String resultDir = et_resultDir.getText().toString().trim(); - - NfcTermuxCmd cmd = new NfcTermuxCmd(); - cmd.setScript(script); - cmd.setArgs(argsStr.isEmpty() ? new String[0] : argsStr.split(",")); - cmd.setWorkDir(workDir.isEmpty() ? null : workDir); - cmd.setBackground("true".equalsIgnoreCase(bgStr)); - cmd.setResultDir(resultDir.isEmpty() ? null : resultDir); - - return cmd; - } - - private void showToast(String msg) { - Toast.makeText(this, msg, Toast.LENGTH_SHORT).show(); - } -} - diff --git a/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/NfcStateMonitor.java b/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/NfcStateMonitor.java deleted file mode 100644 index a58fead..0000000 --- a/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/NfcStateMonitor.java +++ /dev/null @@ -1,78 +0,0 @@ -package cc.winboll.studio.autonfc.nfc; - -import java.util.HashMap; -import java.util.Map; - -public class NfcStateMonitor { - private static Map sListenerMap = new HashMap<>(); - private static boolean sIsRunning = false; - - public static void startMonitor() { - if (sIsRunning) return; - sListenerMap = new HashMap<>(); - sIsRunning = true; - } - - public static void stopMonitor() { - if (!sIsRunning) return; - sIsRunning = false; - if (sListenerMap != null) { - sListenerMap.clear(); - sListenerMap = null; - } - } - - // 你原来的方法名:registerListener - public static void registerListener(String key, OnNfcStateListener listener) { - if (!sIsRunning || listener == null) return; - sListenerMap.put(key, listener); - } - - public static void unregisterListener(String key) { - if (!sIsRunning || key == null) return; - sListenerMap.remove(key); - } - - public static void notifyNfcConnected() { - if (!sIsRunning) return; - for (OnNfcStateListener l : sListenerMap.values()) { - l.onNfcConnected(); - } - } - - public static void notifyNfcDisconnected() { - if (!sIsRunning) return; - for (OnNfcStateListener l : sListenerMap.values()) { - l.onNfcDisconnected(); - } - } - - public static void notifyReadSuccess(String data) { - if (!sIsRunning) return; - for (OnNfcStateListener l : sListenerMap.values()) { - l.onNfcReadSuccess(data); - } - } - - public static void notifyReadFail(String error) { - if (!sIsRunning) return; - for (OnNfcStateListener l : sListenerMap.values()) { - l.onNfcReadFail(error); - } - } - - public static void notifyWriteSuccess() { - if (!sIsRunning) return; - for (OnNfcStateListener l : sListenerMap.values()) { - l.onNfcWriteSuccess(); - } - } - - public static void notifyWriteFail(String error) { - if (!sIsRunning) return; - for (OnNfcStateListener l : sListenerMap.values()) { - l.onNfcWriteFail(error); - } - } -} - diff --git a/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/NfcUtils.java b/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/NfcUtils.java deleted file mode 100644 index 65ac597..0000000 --- a/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/NfcUtils.java +++ /dev/null @@ -1,136 +0,0 @@ -package cc.winboll.studio.autonfc.nfc; - -/** - * @Author 豆包&ZhanGSKen - * @Date 2026/03/16 14:26 - */ -import android.nfc.NdefMessage; -import android.nfc.NdefRecord; -import android.nfc.Tag; -import android.nfc.tech.Ndef; -import com.google.gson.Gson; -import com.google.gson.JsonSyntaxException; -import cc.winboll.studio.autonfc.models.NfcTermuxCmd; -import cc.winboll.studio.libappbase.LogUtils; -import java.nio.charset.Charset; -import java.util.Locale; - -public class NfcUtils { - - public static final String TAG = "NfcUtils"; - private static Gson sGson = new Gson(); - - // ------------------------------------------------------------------------- - // 读取 NFC 标签并解析为 NfcTermuxCmd - // ------------------------------------------------------------------------- - public static NfcTermuxCmd readTag(Tag tag) throws Exception { - if (tag == null) { - LogUtils.e(TAG, "readTag: tag is null"); - return null; - } - - Ndef ndef = Ndef.get(tag); - if (ndef == null) { - LogUtils.e(TAG, "readTag: 不支持 NDEF"); - return null; - } - - try { - ndef.connect(); - NdefMessage msg = ndef.getNdefMessage(); - if (msg == null) return null; - - NdefRecord[] records = msg.getRecords(); - if (records == null || records.length == 0) return null; - - byte[] payload = records[0].getPayload(); - int status = payload[0] & 0xFF; - int langLen = status & 0x3F; - int start = 1 + langLen; - - if (start >= payload.length) return null; - - String json = new String(payload, start, payload.length - start, Charset.forName("UTF-8")); - LogUtils.d(TAG, "readTag: 提取 JSON -> " + json); - - return sGson.fromJson(json, NfcTermuxCmd.class); - } finally { - if (ndef != null && ndef.isConnected()) { - ndef.close(); - } - } - } - - // ------------------------------------------------------------------------- - // 写入 NfcTermuxCmd 到 NFC 标签 - // ------------------------------------------------------------------------- - public static void writeTag(Tag tag, NfcTermuxCmd cmd) throws Exception { - if (tag == null) throw new Exception("tag is null"); - - String json = sGson.toJson(cmd); - writeJson(tag, json); - } - - // ------------------------------------------------------------------------- - // 写入原始 JSON 字符串到 NFC - // ------------------------------------------------------------------------- - public static void writeJson(Tag tag, String json) throws Exception { - if (tag == null) throw new Exception("tag is null"); - - Ndef ndef = Ndef.get(tag); - if (ndef == null) throw new Exception("标签不支持 NDEF"); - - try { - ndef.connect(); - int maxSize = ndef.getMaxSize(); - int realSize = json.getBytes(Charset.forName("UTF-8")).length; - - if (realSize > maxSize) { - throw new Exception("数据过大 (" + realSize + ") > 容量 (" + maxSize + ")"); - } - - NdefRecord record = createTextRecord(json, true); - NdefMessage msg = new NdefMessage(new NdefRecord[]{record}); - - ndef.writeNdefMessage(msg); - LogUtils.d(TAG, "writeJson: 写入成功"); - } finally { - if (ndef != null && ndef.isConnected()) { - ndef.close(); - } - } - } - - // ------------------------------------------------------------------------- - // 创建 NFC 文本记录 - // ------------------------------------------------------------------------- - public static NdefRecord createTextRecord(String text, boolean isUtf8) { - byte[] langBytes = "en".getBytes(Charset.forName("US-ASCII")); - byte[] textBytes = text.getBytes(Charset.forName(isUtf8 ? "UTF-8" : "UTF-16")); - - int status = isUtf8 ? 0 : 0x80; - status |= langBytes.length & 0x3F; - - byte[] data = new byte[1 + langBytes.length + textBytes.length]; - data[0] = (byte) status; - System.arraycopy(langBytes, 0, data, 1, langBytes.length); - System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length); - - return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], data); - } - - // ------------------------------------------------------------------------- - // 辅助:JSON -> NfcTermuxCmd - // ------------------------------------------------------------------------- - public static NfcTermuxCmd jsonToCmd(String json) throws JsonSyntaxException { - return sGson.fromJson(json, NfcTermuxCmd.class); - } - - // ------------------------------------------------------------------------- - // 辅助:NfcTermuxCmd -> JSON - // ------------------------------------------------------------------------- - public static String cmdToJson(NfcTermuxCmd cmd) { - return sGson.toJson(cmd); - } -} - diff --git a/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/OnNfcStateListener.java b/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/OnNfcStateListener.java deleted file mode 100644 index 4b149d5..0000000 --- a/autonfc/src/main/java/cc/winboll/studio/autonfc/nfc/OnNfcStateListener.java +++ /dev/null @@ -1,11 +0,0 @@ -package cc.winboll.studio.autonfc.nfc; - -public interface OnNfcStateListener { - void onNfcConnected(); // 无参数! - void onNfcDisconnected(); - void onNfcReadSuccess(String data); - void onNfcReadFail(String error); - void onNfcWriteSuccess(); - void onNfcWriteFail(String error); -} - diff --git a/autonfc/src/main/res/layout/activity_main.xml b/autonfc/src/main/res/layout/activity_main.xml deleted file mode 100644 index a6032d0..0000000 --- a/autonfc/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - -