Compare commits
12 Commits
positions-
...
winboll-v1
| Author | SHA1 | Date | |
|---|---|---|---|
| 28ecc605e1 | |||
| 523a8e49e0 | |||
| 59a9e0ee45 | |||
| cbf1341435 | |||
| dadf573675 | |||
| 7420a5cd48 | |||
| dc6a589db4 | |||
| e3f47043ef | |||
| a825951aad | |||
| 79cb841349 | |||
| d3c40efffa | |||
| 6c8867e15c |
44
build.gradle
44
build.gradle
@@ -1,6 +1,15 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
buildscript {
|
||||
repositories {
|
||||
maven { url 'https://maven.aliyun.com/repository/public/' }
|
||||
maven { url 'https://maven.aliyun.com/repository/google/' }
|
||||
maven { url 'https://maven.aliyun.com/repository/gradle-plugin/' }
|
||||
maven { url 'https://dl.bintray.com/ppartisan/maven/' }
|
||||
maven { url "https://clojars.org/repo/" }
|
||||
maven { url "https://jitpack.io" }
|
||||
mavenCentral()
|
||||
google()
|
||||
|
||||
mavenLocal {
|
||||
// 设置本地Maven仓库路径
|
||||
url 'file:///sdcard/.m2/repository/'
|
||||
@@ -11,19 +20,6 @@ buildscript {
|
||||
maven { url "https://nexus.winboll.cc/repository/maven-public/" }
|
||||
// "WinBoLL Snapshot"
|
||||
maven { url "https://nexus.winboll.cc/repository/maven-snapshots/" }
|
||||
|
||||
maven { url 'https://maven.aliyun.com/repository/public/' }
|
||||
maven { url 'https://maven.aliyun.com/repository/google/' }
|
||||
maven { url 'https://maven.aliyun.com/repository/gradle-plugin/' }
|
||||
maven { url 'https://dl.bintray.com/ppartisan/maven/' }
|
||||
maven { url "https://clojars.org/repo/" }
|
||||
maven { url "https://jitpack.io" }
|
||||
mavenCentral()
|
||||
google()
|
||||
//println "mavenLocal : ==========="
|
||||
//println mavenLocal().url
|
||||
//println "mavenLocal : ==========="
|
||||
//mavenLocal()
|
||||
}
|
||||
dependencies {
|
||||
// 适配MIUI12
|
||||
@@ -35,6 +31,15 @@ buildscript {
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
maven { url 'https://maven.aliyun.com/repository/public/' }
|
||||
maven { url 'https://maven.aliyun.com/repository/google/' }
|
||||
maven { url 'https://maven.aliyun.com/repository/gradle-plugin/' }
|
||||
maven { url 'https://dl.bintray.com/ppartisan/maven/' }
|
||||
maven { url "https://clojars.org/repo/" }
|
||||
maven { url "https://jitpack.io" }
|
||||
mavenCentral()
|
||||
google()
|
||||
|
||||
mavenLocal {
|
||||
// 设置本地Maven仓库路径
|
||||
url 'file:///sdcard/.m2/repository/'
|
||||
@@ -45,19 +50,6 @@ allprojects {
|
||||
maven { url "https://nexus.winboll.cc/repository/maven-public/" }
|
||||
// "WinBoLL Snapshot"
|
||||
maven { url "https://nexus.winboll.cc/repository/maven-snapshots/" }
|
||||
|
||||
maven { url 'https://maven.aliyun.com/repository/public/' }
|
||||
maven { url 'https://maven.aliyun.com/repository/google/' }
|
||||
maven { url 'https://maven.aliyun.com/repository/gradle-plugin/' }
|
||||
maven { url 'https://dl.bintray.com/ppartisan/maven/' }
|
||||
maven { url "https://clojars.org/repo/" }
|
||||
maven { url "https://jitpack.io" }
|
||||
mavenCentral()
|
||||
google()
|
||||
//println "mavenLocal : ==========="
|
||||
//println mavenLocal().url
|
||||
//println "mavenLocal : ==========="
|
||||
//mavenLocal()
|
||||
}
|
||||
ext {
|
||||
// 定义全局变量,常用于版本管理
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Mon Mar 16 19:52:21 GMT 2026
|
||||
stageCount=26
|
||||
#Thu Apr 30 12:07:31 HKT 2026
|
||||
stageCount=27
|
||||
libraryProject=
|
||||
baseVersion=15.11
|
||||
publishVersion=15.11.25
|
||||
buildCount=29
|
||||
baseBetaVersion=15.11.26
|
||||
publishVersion=15.11.26
|
||||
buildCount=0
|
||||
baseBetaVersion=15.11.27
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="cc.winboll.studio.winboll"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:sharedUserId="com.termux">
|
||||
package="cc.winboll.studio.winboll"
|
||||
android:sharedUserId="com.termux">
|
||||
|
||||
<!-- 拥有完全的网络访问权限 -->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
@@ -13,11 +13,15 @@
|
||||
|
||||
<!-- 对正在运行的应用重新排序 -->
|
||||
<uses-permission android:name="android.permission.REORDER_TASKS"/>
|
||||
<!-- Android 11+ 查询已安装应用权限 -->
|
||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||
tools:ignore="QueryAllPackagesPermission" />
|
||||
<!-- 可选:兼容低版本系统 -->
|
||||
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
|
||||
|
||||
<!-- 计算应用存储空间 -->
|
||||
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE"/>
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||
tools:ignore="QueryAllPackagesPermission"/>
|
||||
|
||||
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
@@ -291,18 +295,24 @@
|
||||
|
||||
<activity android:name="cc.winboll.studio.winboll.unittest.TermuxEnvTestActivity"/>
|
||||
|
||||
<activity
|
||||
android:name=".termux.NfcTermuxBridgeActivity"
|
||||
android:exported="true"> <!-- 必须设置为 true,允许外部应用调用 -->
|
||||
<activity
|
||||
android:name=".termux.NfcTermuxBridgeActivity"
|
||||
android:exported="true">
|
||||
|
||||
<!-- 接收 ACTION_BUILD 意图 -->
|
||||
<intent-filter>
|
||||
<action android:name="cc.winboll.studio.winboll.termux.NfcTermuxBridgeActivity.ACTION_BUILD" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
|
||||
</activity>
|
||||
<action android:name="cc.winboll.studio.winboll.termux.NfcTermuxBridgeActivity.ACTION_BUILD"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
|
||||
<activity android:name="cc.winboll.studio.winboll.applications.MyTermuxActivity"
|
||||
android:label="@string/my_termux_activity"
|
||||
android:exported="true"/>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
</manifest>
|
||||
@@ -15,6 +15,7 @@ import cc.winboll.studio.libappbase.LogUtils;
|
||||
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.applications.MyTermuxActivity;
|
||||
import cc.winboll.studio.winboll.fragments.BrowserFragment;
|
||||
import cc.winboll.studio.winboll.unittest.TermuxEnvTestActivity;
|
||||
import java.util.ArrayList;
|
||||
@@ -155,6 +156,10 @@ public class MainActivity extends DrawerFragmentActivity {
|
||||
Intent intent = new Intent(getApplicationContext(), AboutActivity.class);
|
||||
|
||||
WinBoLLActivityManager.getInstance().startWinBoLLActivity(getApplicationContext(), intent, AboutActivity.class);
|
||||
} else if (nItemId == R.id.item_mytermux) {
|
||||
Intent intent = new Intent(getApplicationContext(), MyTermuxActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(intent);
|
||||
} else if (nItemId == R.id.item_termux_env_test) {
|
||||
Intent intent = new Intent(getApplicationContext(), TermuxEnvTestActivity.class);
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package cc.winboll.studio.winboll.applications;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.winboll.R;
|
||||
import cc.winboll.studio.winboll.termux.TermuxCommandExecutor;
|
||||
|
||||
public class MyTermuxActivity extends AppCompatActivity {
|
||||
|
||||
public static final String TAG = "MyTermuxActivity";
|
||||
|
||||
private Toolbar mToolbar;
|
||||
private Button mTermuxButton;
|
||||
private Button mTermuxWorkSpacesButton;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_my_termux);
|
||||
|
||||
// 初始化工具栏
|
||||
initToolbar();
|
||||
// 初始化按钮
|
||||
initView();
|
||||
}
|
||||
|
||||
private void initToolbar() {
|
||||
mToolbar = findViewById(R.id.toolbar);
|
||||
if (mToolbar != null) {
|
||||
setSupportActionBar(mToolbar);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
LogUtils.d(TAG, "点击返回按钮");
|
||||
finish();
|
||||
}
|
||||
});
|
||||
LogUtils.d(TAG, "工具栏初始化完成");
|
||||
}
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
mTermuxButton = findViewById(R.id.btn_termux);
|
||||
if (mTermuxButton != null) {
|
||||
mTermuxButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
LogUtils.d(TAG, "点击 Termux 按钮");
|
||||
TermuxCommandExecutor.openTermuxBash(MyTermuxActivity.this, "cd ~");
|
||||
//TermuxCommandExecutor.openTermuxBash(MyTermuxActivity.this, "cd ~/TermuxWorkSpaces", "./TermuxWorkSpaces");
|
||||
}
|
||||
});
|
||||
LogUtils.d(TAG, "Termux 按钮初始化完成");
|
||||
}
|
||||
|
||||
mTermuxWorkSpacesButton = findViewById(R.id.btn_termuxworkspaces);
|
||||
if (mTermuxWorkSpacesButton != null) {
|
||||
mTermuxWorkSpacesButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
LogUtils.d(TAG, "点击 TermuxWorkSpaces 按钮");
|
||||
TermuxCommandExecutor.openTermuxBash(MyTermuxActivity.this, "cd ~/TermuxWorkSpaces", "./TermuxWorkSpaces");
|
||||
}
|
||||
});
|
||||
LogUtils.d(TAG, "TermuxWorkSpaces 按钮初始化完成");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isTermuxAvailable() {
|
||||
return TermuxCommandExecutor.isTermuxInstalled(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package cc.winboll.studio.winboll.models;
|
||||
|
||||
import android.util.JsonReader;
|
||||
import android.util.JsonWriter;
|
||||
import cc.winboll.studio.libappbase.BaseBean;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2026/04/30 10:47
|
||||
*/
|
||||
public class TermuxButtonModel extends BaseBean {
|
||||
|
||||
public static final String TAG = "TermuxButtonModel";
|
||||
|
||||
String buttonName;
|
||||
String exeCommand;
|
||||
String workDir;
|
||||
|
||||
// 已修改:isCommit 改为规范过去式命名 isCommitted
|
||||
boolean isCommitted;
|
||||
String commitTitle;
|
||||
String commitInfo;
|
||||
|
||||
public TermuxButtonModel() {
|
||||
this.buttonName = "";
|
||||
this.exeCommand = "";
|
||||
this.workDir = "";
|
||||
// 默认初始化
|
||||
this.isCommitted = false;
|
||||
this.commitTitle = "";
|
||||
this.commitInfo = "";
|
||||
}
|
||||
|
||||
public void setButtonName(String buttonName) {
|
||||
this.buttonName = buttonName;
|
||||
}
|
||||
|
||||
public String getButtonName() {
|
||||
return buttonName;
|
||||
}
|
||||
|
||||
public void setExeCommand(String exeCommand) {
|
||||
this.exeCommand = exeCommand;
|
||||
}
|
||||
|
||||
public String getExeCommand() {
|
||||
return exeCommand;
|
||||
}
|
||||
|
||||
public void setWorkDir(String workDir) {
|
||||
this.workDir = workDir;
|
||||
}
|
||||
|
||||
public String getWorkDir() {
|
||||
return workDir;
|
||||
}
|
||||
|
||||
// ========== 已修改 对应 isCommitted 完整 Get & Set ==========
|
||||
public boolean isCommitted() {
|
||||
return isCommitted;
|
||||
}
|
||||
|
||||
public void setCommitted(boolean committed) {
|
||||
isCommitted = committed;
|
||||
}
|
||||
|
||||
public String getCommitTitle() {
|
||||
return commitTitle;
|
||||
}
|
||||
|
||||
public void setCommitTitle(String commitTitle) {
|
||||
this.commitTitle = commitTitle;
|
||||
}
|
||||
|
||||
public String getCommitInfo() {
|
||||
return commitInfo;
|
||||
}
|
||||
|
||||
public void setCommitInfo(String commitInfo) {
|
||||
this.commitInfo = commitInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return TermuxButtonModel.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
|
||||
super.writeThisToJsonWriter(jsonWriter);
|
||||
jsonWriter.name("buttonName").value(getButtonName());
|
||||
jsonWriter.name("exeCommand").value(getExeCommand());
|
||||
jsonWriter.name("workDir").value(getWorkDir());
|
||||
|
||||
// JSON写入同步修改
|
||||
jsonWriter.name("isCommitted").value(isCommitted());
|
||||
jsonWriter.name("commitTitle").value(getCommitTitle());
|
||||
jsonWriter.name("commitInfo").value(getCommitInfo());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
|
||||
if (super.initObjectsFromJsonReader(jsonReader, name)) {
|
||||
return true;
|
||||
} else {
|
||||
if (name.equals("buttonName")) {
|
||||
setButtonName(jsonReader.nextString());
|
||||
} else if (name.equals("exeCommand")) {
|
||||
setExeCommand(jsonReader.nextString());
|
||||
} else if (name.equals("workDir")) {
|
||||
setWorkDir(jsonReader.nextString());
|
||||
}
|
||||
// JSON解析字段同步修改
|
||||
else if (name.equals("isCommitted")) {
|
||||
setCommitted(jsonReader.nextBoolean());
|
||||
} else if (name.equals("commitTitle")) {
|
||||
setCommitTitle(jsonReader.nextString());
|
||||
} else if (name.equals("commitInfo")) {
|
||||
setCommitInfo(jsonReader.nextString());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
|
||||
jsonReader.beginObject();
|
||||
while (jsonReader.hasNext()) {
|
||||
String name = jsonReader.nextName();
|
||||
if (!initObjectsFromJsonReader(jsonReader, name)) {
|
||||
jsonReader.skipValue();
|
||||
}
|
||||
}
|
||||
jsonReader.endObject();
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ public class TermuxCommandExecutor {
|
||||
// Termux RunCommandService 完整类名(包名+类名)
|
||||
private static final String TERMUX_RUN_CMD_SERVICE_CLASS = "com.termux.app.RunCommandService";
|
||||
private static final String TERMUX_RUN_CMD_ACTION = TermuxConstants.TERMUX_APP.RUN_COMMAND_SERVICE.ACTION_RUN_COMMAND;
|
||||
private static final String TERMUX_HOME_PATH = "/data/data/com.termux/files/home";
|
||||
|
||||
/**
|
||||
* 执行 Termux 命令(核心方法)
|
||||
@@ -114,7 +115,7 @@ public class TermuxCommandExecutor {
|
||||
context,
|
||||
"/data/data/com.termux/files/usr/bin/bash", // Termux 默认 bash 路径(正确)
|
||||
args,
|
||||
"/data/data/com.termux/files/home", // 默认工作目录
|
||||
TERMUX_HOME_PATH, // 默认工作目录
|
||||
false, // 终端会话执行(可见)
|
||||
null // 不输出到文件
|
||||
);
|
||||
@@ -173,5 +174,49 @@ public class TermuxCommandExecutor {
|
||||
LogUtils.d(TAG, "外部应用调用权限提示:" + tip);
|
||||
return tip;
|
||||
}
|
||||
|
||||
public static boolean openTermuxBash(Context context, String command) {
|
||||
return openTermuxBash(context, command, "~");
|
||||
}
|
||||
|
||||
public static boolean openTermuxBash(Context context, String command, String workDir) {
|
||||
LogUtils.d(TAG, "openTermuxBash() 按钮点击,执行Gradle命令(实时输出)");
|
||||
|
||||
// 1. 校验Termux是否安装
|
||||
if (!TermuxCommandExecutor.isTermuxInstalled(context)) {
|
||||
LogUtils.e(TAG, "openTermuxBash() 错误:未安装Termux应用");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 定义核心路径(确保路径与Termux中一致)
|
||||
String projectPath = TERMUX_HOME_PATH;
|
||||
if (workDir.startsWith("~") || workDir.startsWith(".")) {
|
||||
projectPath = TERMUX_HOME_PATH + "/" + workDir.substring(1);
|
||||
}
|
||||
|
||||
// 3. 构造命令(核心:用stdbuf禁用缓冲,实现实时输出)
|
||||
String targetCmd = "";
|
||||
// 步骤1:进入项目目录(不存在则创建)
|
||||
targetCmd += "cd " + projectPath + " && ";
|
||||
// 步骤2:加载环境变量
|
||||
targetCmd += "source ~/.bashrc && ";
|
||||
// 步骤3:显式配置PATH
|
||||
targetCmd += "export PATH=/data/data/com.termux/files/usr/bin:$PATH && ";
|
||||
// 步骤4:用stdbuf禁用stdout/stderr缓冲(关键!),执行Gradle命令
|
||||
// -o0:stdout无缓冲;-e0:stderr无缓冲;-i0:stdin无缓冲
|
||||
//targetCmd += "stdbuf -o0 -e0 -i0 " + gradleFullPath + " task --all | grep assemble && ";
|
||||
//targetCmd += "stdbuf -o0 -e0 -i0 " + gradleFullPath + " -Pandroid.aapt2FromMavenOverride=/data/data/com.termux/files/home/android-sdk/build-tools/34.0.4/aapt2 assembleBetaDebug && ";
|
||||
targetCmd += "stdbuf -o0 -e0 -i0 bash && ";
|
||||
// 步骤5:执行成功提示
|
||||
targetCmd += "echo '\n✅ 命令执行完成!' && echo '\n📌 当前目录:" + projectPath + "' && read -p '按回车键关闭终端...'";
|
||||
|
||||
|
||||
// 4. 执行命令(终端会话模式,唤起Termux窗口)
|
||||
boolean cmdSuccess = TermuxCommandExecutor.executeTerminalCommand(context, targetCmd);
|
||||
if (!cmdSuccess) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,264 @@
|
||||
package cc.winboll.studio.winboll.views;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.winboll.models.TermuxButtonModel;
|
||||
|
||||
/**
|
||||
* 自定义Termux功能按钮控件
|
||||
* 绑定TermuxButtonModel实体数据,拦截点击事件做确认弹窗逻辑判断
|
||||
* isCommitted为true直接执行点击事件,为false弹出确认对话框二次确认
|
||||
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
|
||||
* @CreateTime 2026/04/30 10:57:00
|
||||
* @EditTime 2026/04/30 13:52:15
|
||||
*/
|
||||
public class TermuxButton extends Button {
|
||||
|
||||
public static final String TAG = "TermuxButton";
|
||||
|
||||
/** 绑定按钮对应数据实体 */
|
||||
private TermuxButtonModel buttonModel;
|
||||
/** 保存外部设置的原始点击监听 */
|
||||
private OnClickListener originClickListener;
|
||||
|
||||
//==================== 构造方法 ====================
|
||||
/**
|
||||
* 代码动态创建控件构造
|
||||
* @param context 上下文
|
||||
*/
|
||||
public TermuxButton(Context context) {
|
||||
super(context);
|
||||
LogUtils.d(TAG, "TermuxButton 无参构造执行,上下文:" + context);
|
||||
initView(null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* XML布局引用控件基础构造
|
||||
* @param context 上下文
|
||||
* @param attrs XML属性集
|
||||
*/
|
||||
public TermuxButton(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
LogUtils.d(TAG, "TermuxButton XML构造执行");
|
||||
initView(attrs, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* XML布局带自定义属性构造
|
||||
* @param context 上下文
|
||||
* @param attrs XML属性集
|
||||
* @param defStyleAttr 默认样式属性
|
||||
*/
|
||||
public TermuxButton(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
LogUtils.d(TAG, "TermuxButton 带样式属性构造执行");
|
||||
initView(attrs, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 高版本Android完整全参构造
|
||||
* @param context 上下文
|
||||
* @param attrs XML属性集
|
||||
* @param defStyleAttr 默认样式属性
|
||||
* @param defStyleRes 默认样式资源
|
||||
*/
|
||||
public TermuxButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
LogUtils.d(TAG, "TermuxButton 全参构造执行");
|
||||
initView(attrs, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接传入Model初始化控件构造
|
||||
* @param context 上下文
|
||||
* @param model 按钮数据实体
|
||||
*/
|
||||
public TermuxButton(Context context, TermuxButtonModel model) {
|
||||
super(context);
|
||||
LogUtils.d(TAG, "TermuxButton Model入参构造执行");
|
||||
initView(null, model);
|
||||
}
|
||||
|
||||
//==================== 核心初始化 ====================
|
||||
/**
|
||||
* 控件统一初始化方法
|
||||
* @param attrs XML属性集合
|
||||
* @param model 绑定数据实体
|
||||
*/
|
||||
private void initView(AttributeSet attrs, TermuxButtonModel model) {
|
||||
this.buttonModel = model;
|
||||
|
||||
// 按钮基础默认配置
|
||||
setClickable(true);
|
||||
setFocusable(true);
|
||||
|
||||
// 解析XML布局自定义属性
|
||||
if (attrs != null) {
|
||||
parseXmlCustomAttr(attrs);
|
||||
}
|
||||
|
||||
// 同步Model内按钮名称到控件展示文本
|
||||
refreshButtonText();
|
||||
// 绑定自定义拦截点击事件
|
||||
setCustomClickEvent();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析XML布局属性,读取原生android:text与自定义属性赋值到Model
|
||||
* @param attrs XML属性集
|
||||
*/
|
||||
private void parseXmlCustomAttr(AttributeSet attrs) {
|
||||
if (buttonModel == null) {
|
||||
buttonModel = new TermuxButtonModel();
|
||||
LogUtils.d(TAG, "自动初始化空的TermuxButtonModel实体");
|
||||
}
|
||||
|
||||
// 读取原生android:text作为按钮名称
|
||||
String androidText = attrs.getAttributeValue("http://schemas.android.com/apk/res/android", "text");
|
||||
// 读取自定义扩展属性
|
||||
String exeCommand = attrs.getAttributeValue("http://schemas.android.com/apk/res-auto", "exeCommand");
|
||||
String workDir = attrs.getAttributeValue("http://schemas.android.com/apk/res-auto", "workDir");
|
||||
String isCommittedStr = attrs.getAttributeValue("http://schemas.android.com/apk/res-auto", "isCommitted");
|
||||
String commitTitle = attrs.getAttributeValue("http://schemas.android.com/apk/res-auto", "commitTitle");
|
||||
String commitInfo = attrs.getAttributeValue("http://schemas.android.com/apk/res-auto", "commitInfo");
|
||||
|
||||
// 属性赋值绑定
|
||||
if (androidText != null) {
|
||||
buttonModel.setButtonName(androidText);
|
||||
}
|
||||
if (exeCommand != null) {
|
||||
buttonModel.setExeCommand(exeCommand);
|
||||
}
|
||||
if (workDir != null) {
|
||||
buttonModel.setWorkDir(workDir);
|
||||
}
|
||||
if (isCommittedStr != null) {
|
||||
buttonModel.setCommitted(Boolean.parseBoolean(isCommittedStr));
|
||||
}
|
||||
if (commitTitle != null) {
|
||||
buttonModel.setCommitTitle(commitTitle);
|
||||
}
|
||||
if (commitInfo != null) {
|
||||
buttonModel.setCommitInfo(commitInfo);
|
||||
}
|
||||
|
||||
LogUtils.d(TAG, "XML属性解析完成,按钮名称:" + androidText);
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步Model中buttonName,更新按钮展示文字
|
||||
*/
|
||||
private void refreshButtonText() {
|
||||
if (buttonModel != null) {
|
||||
setText(buttonModel.getButtonName());
|
||||
}
|
||||
}
|
||||
|
||||
//==================== 点击事件相关 ====================
|
||||
/**
|
||||
* 重写点击监听设置,保存外部原始点击事件
|
||||
* @param l 外部传入点击监听
|
||||
*/
|
||||
@Override
|
||||
public void setOnClickListener(OnClickListener l) {
|
||||
this.originClickListener = l;
|
||||
LogUtils.d(TAG, "保存外部原始按钮点击监听");
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义拦截按钮点击逻辑
|
||||
* isCommitted=true 直接执行原始点击事件
|
||||
* isCommitted=false 弹出确认二次弹窗
|
||||
*/
|
||||
private void setCustomClickEvent() {
|
||||
super.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (buttonModel == null) {
|
||||
LogUtils.d(TAG, "无绑定Model,直接执行原始点击事件");
|
||||
if (originClickListener != null) {
|
||||
originClickListener.onClick(view);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
boolean commitState = buttonModel.isCommitted();
|
||||
LogUtils.d(TAG, "按钮点击触发,isCommitted状态:" + commitState);
|
||||
if (commitState) {
|
||||
// 无需确认,直接执行原有点击任务
|
||||
if (originClickListener != null) {
|
||||
originClickListener.onClick(view);
|
||||
}
|
||||
} else {
|
||||
// 需要二次确认,弹出提示对话框
|
||||
showCommitDialog();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 弹出操作确认对话框
|
||||
* 标题:commitTitle 内容:commitInfo
|
||||
* 取消:关闭弹窗无操作 确定:执行原始点击事件
|
||||
*/
|
||||
private void showCommitDialog() {
|
||||
Context context = getContext();
|
||||
String dialogTitle = buttonModel.getCommitTitle();
|
||||
String dialogMsg = buttonModel.getCommitInfo();
|
||||
|
||||
// 空值默认兜底处理
|
||||
if (dialogTitle == null || "".equals(dialogTitle)) {
|
||||
dialogTitle = "温馨提示";
|
||||
}
|
||||
if (dialogMsg == null || "".equals(dialogMsg)) {
|
||||
dialogMsg = "确定要执行该操作吗?";
|
||||
}
|
||||
|
||||
LogUtils.d(TAG, "弹出确认对话框,标题:" + dialogTitle);
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(dialogTitle)
|
||||
.setMessage(dialogMsg)
|
||||
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
LogUtils.d(TAG, "对话框点击取消,终止操作");
|
||||
}
|
||||
})
|
||||
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
LogUtils.d(TAG, "对话框点击确定,继续执行操作");
|
||||
if (originClickListener != null) {
|
||||
originClickListener.onClick(TermuxButton.this);
|
||||
}
|
||||
}
|
||||
})
|
||||
.setCancelable(false)
|
||||
.show();
|
||||
}
|
||||
|
||||
//==================== Getter & Setter ====================
|
||||
public TermuxButtonModel getButtonModel() {
|
||||
return buttonModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置绑定按钮数据实体,自动刷新按钮展示文字
|
||||
* @param buttonModel 数据实体类
|
||||
*/
|
||||
public void setButtonModel(TermuxButtonModel buttonModel) {
|
||||
this.buttonModel = buttonModel;
|
||||
LogUtils.d(TAG, "外部设置ButtonModel,自动刷新按钮文本");
|
||||
refreshButtonText();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
90
winboll/src/main/res/layout/activity_my_termux.xml
Normal file
90
winboll/src/main/res/layout/activity_my_termux.xml
Normal file
@@ -0,0 +1,90 @@
|
||||
<?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="wrap_content"
|
||||
android:gravity="top">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:title="@string/app_name"
|
||||
app:subtitle="@string/my_termux_activity"
|
||||
app:titleTextColor="@android:color/white"
|
||||
app:subtitleTextColor="@android:color/white"/>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1.0">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.0">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<cc.winboll.studio.winboll.views.TermuxButton
|
||||
android:id="@+id/btn_termux"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Termux"
|
||||
android:textSize="18sp"
|
||||
android:padding="16dp"
|
||||
android:backgroundTint="@android:color/holo_blue_dark"
|
||||
app:exeCommand="cd ~"
|
||||
app:workDir="~"
|
||||
app:isCommitted="true"
|
||||
app:commitTitle="打开 Termux"
|
||||
app:commitInfo="打开 Termux 应用"/>
|
||||
|
||||
<cc.winboll.studio.winboll.views.TermuxButton
|
||||
android:id="@+id/btn_termuxworkspaces"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="TermuxWorkSpaces"
|
||||
android:textSize="18sp"
|
||||
android:padding="16dp"
|
||||
android:backgroundTint="@android:color/holo_blue_dark"
|
||||
app:exeCommand="cd ~/TermuxWorkSpaces"
|
||||
app:workDir="~"
|
||||
app:isCommitted="false"
|
||||
app:commitTitle="打开 TermuxWorkSpaces"
|
||||
app:commitInfo="打开 Termux 应用,进入 TermuxWorkSpaces 目录。"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="+"
|
||||
android:id="@+id/btn_addtermuxbutton"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
android:id="@+id/item_home"
|
||||
android:title="HOME"/>
|
||||
<item
|
||||
android:id="@+id/item_mytermux"
|
||||
android:title="MyTermuxActivity"/>
|
||||
<item
|
||||
android:id="@+id/item_settings"
|
||||
android:title="Settings"/>
|
||||
<item
|
||||
|
||||
@@ -5,5 +5,13 @@
|
||||
<attr name="toolbarTitleColor" format="color" />
|
||||
<attr name="toolbarBackgroundColor" format="color" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="TermuxButton">
|
||||
<attr name="exeCommand" format="string" />
|
||||
<attr name="workDir" format="string" />
|
||||
<attr name="isCommitted" format="boolean" />
|
||||
<attr name="commitTitle" format="string" />
|
||||
<attr name="commitInfo" format="string" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
|
||||
|
||||
@@ -11,4 +11,5 @@
|
||||
<string name="cn2_switch_disabled">金抖云 X</string>
|
||||
<string name="tileservice_name">WinBoLL</string>
|
||||
<string name="toolbar_icon_description">WinBoLL APP</string>
|
||||
<string name="my_termux_activity">MyTermuxActivity</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user