v2.2.1: Auto-install build tools (aapt2/ecj/d8/apksigner), dev tools banner with one-tap install

This commit is contained in:
admin
2026-05-19 19:47:33 +04:00
Unverified
parent 538505e38b
commit 9880baac5b
6 changed files with 217 additions and 25 deletions

View File

@@ -631,6 +631,12 @@ data: [DONE]
## Changelog
### v2.2.1 (2026-05-19)
- **Auto-Install Build Tools** — `aapt2`, `ecj`, `d8`, `apksigner` auto-installed via `pkg` with full paths and retry logic
- **Dev Tools Banner** — yellow warning banner on Coding/Agentic mode if tools missing, one-tap Install button
- **Bootstrap + Tools in One Tap** — Install button auto-installs Termux bootstrap then build tools sequentially
- Checks tool availability before every build attempt, auto-installs if missing
### v2.2.0 (2026-05-19)
- **Build Pipeline Rewrite** — proven 7-step pipeline tested with Android SDK 36: resources → link → compile (R.java + sources) → DEX → package → sign
- **Auto-Install Build Tools** — `aapt2`, `ecj`, `d8`, `apksigner` auto-installed via `pkg` on first build

View File

@@ -7,8 +7,8 @@ android {
applicationId "ai.z.chat"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 13
versionName "2.2.0"
versionCode 14
versionName "2.2.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
ignoreAssetsPattern = '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~'

View File

@@ -1,6 +1,6 @@
{
"name": "zai-chat",
"version": "2.2.0",
"version": "2.2.1",
"description": "Z.AI Chat - Full stack AI chat powered by GLM Coding Plan",
"main": "index.js",
"scripts": {

View File

@@ -1107,6 +1107,51 @@ a:hover { text-decoration: underline; }
-webkit-overflow-scrolling: touch;
}
/* Dev Tools Banner */
.dev-tools-banner {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 14px;
margin: 8px 12px;
background: rgba(255, 165, 2, 0.1);
border: 1px solid var(--warning);
border-radius: var(--radius-sm);
font-size: 13px;
color: var(--text-primary);
animation: fadeIn 0.3s ease;
}
.dtb-icon {
font-size: 16px;
flex-shrink: 0;
}
.dtb-msg {
flex: 1;
line-height: 1.4;
}
.dtb-install-btn {
background: var(--accent);
color: white;
border: none;
padding: 6px 14px;
border-radius: 6px;
font-size: 12px;
font-weight: 700;
cursor: pointer;
flex-shrink: 0;
white-space: nowrap;
}
.dtb-install-btn:disabled { opacity: 0.6; cursor: not-allowed; }
.dtb-dismiss-btn {
background: none;
border: none;
color: var(--text-muted);
font-size: 18px;
cursor: pointer;
padding: 0 4px;
flex-shrink: 0;
}
/* Responsive */
@media (max-width: 480px) {
.message { max-width: 92%; }

View File

@@ -327,13 +327,23 @@
</div>
<div class="settings-section">
<h3>About</h3>
<p class="about-text">Z.AI Chat v2.2.0</p>
<p class="about-text">Z.AI Chat v2.2.1</p>
<p class="about-text">Built with Z.AI SDK &amp; GLM-5.1</p>
<p class="about-text">Compatible with Android 15/16</p>
</div>
<div class="settings-section">
<h3>Changelog</h3>
<ul class="changelog-list">
<li>
<span class="changelog-version">v2.2.1</span>
<span class="changelog-date">2026-05-19</span>
<ul>
<li><strong>Auto-Install Build Tools</strong> — aapt2/ecj/d8/apksigner auto-installed via pkg with full paths + retry logic</li>
<li><strong>Dev Tools Banner</strong> — warns on Coding/Agentic mode if tools missing, one-tap install button</li>
<li><strong>Bootstrap + Tools in One Tap</strong> — Install button auto-installs bootstrap then build tools</li>
<li>Checks tool availability before every build, auto-installs if missing</li>
</ul>
</li>
<li>
<span class="changelog-version">v2.2.0</span>
<span class="changelog-date">2026-05-19</span>

View File

@@ -549,6 +549,10 @@
return;
}
if ((state.currentMode === 'coding' || state.currentMode === 'agentic') && !termState.devToolsInstalled) {
checkDevEnvironment();
}
if (!state.activeConversationId) {
newConversation();
}
@@ -1901,33 +1905,159 @@
}
async function ensureBuildTools() {
var check = await shellExec('command -v aapt2 && command -v ecj && (command -v d8 || command -v dx)', termState.homeDir, false);
if (check.exitCode === 0) return true;
termPrint('[*] Installing build tools...', 'info');
showStatusToast('Installing build tools (first time)...', 'info');
var installResult = await shellExec(
'apt update -y 2>&1 && apt install -y aapt2 ecj dx apksigner 2>&1 || pkg install -y aapt2 ecj dx apksigner 2>&1',
termState.homeDir, false
);
if (installResult.output) {
termPrint(installResult.output.replace(/\n$/, ''), '');
}
var recheck = await shellExec('command -v aapt2 && command -v ecj', termState.homeDir, false);
if (recheck.exitCode === 0) {
termPrint('[OK] Build tools installed', 'success');
showStatusToast('Build tools installed!', 'success');
var check = await shellExec('command -v aapt2 >/dev/null 2>&1 && command -v ecj >/dev/null 2>&1 && (command -v d8 >/dev/null 2>&1 || command -v dx >/dev/null 2>&1)', termState.homeDir, false);
if (check.exitCode === 0) {
termState.devToolsInstalled = true;
return true;
}
termPrint('[!] Failed to install build tools automatically', 'err');
termPrint('Please run manually: pkg install aapt2 ecj dx apksigner', 'warning');
if (Shell) {
var env = await Shell.getEnv();
termState.homeDir = env.HOME;
termState.toolsDir = env.TOOLS;
termState.projectsDir = env.PROJECTS;
termState.cwd = env.CWD || env.HOME;
}
var prefix = termState.homeDir ? termState.homeDir.replace('/home', '') + '/usr' : '';
var pkgBin = prefix + '/bin/pkg';
var aptBin = prefix + '/bin/apt';
var pkgExists = await shellExec('test -x "' + pkgBin + '"', termState.homeDir, false);
var aptExists = await shellExec('test -x "' + aptBin + '"', termState.homeDir, false);
if (pkgExists.exitCode !== 0 && aptExists.exitCode !== 0) {
termPrint('[!] No package manager found. Install Termux bootstrap first.', 'err');
termState.devToolsInstalled = false;
return false;
}
termPrint('[*] Installing build tools (aapt2, ecj, dx, apksigner)...', 'info');
termPrint('[*] This may take a few minutes on first run...', 'info');
showStatusToast('Installing build tools...', 'info');
var installCmd;
if (pkgExists.exitCode === 0) {
installCmd = pkgBin + ' update -y 2>&1 && ' + pkgBin + ' install -y aapt2 ecj dx apksigner 2>&1';
} else {
installCmd = aptBin + ' update -y 2>&1 && ' + aptBin + ' install -y aapt2 ecj dx apksigner 2>&1';
}
var installResult = await shellExec(installCmd, termState.homeDir, false);
if (installResult.output) {
var out = installResult.output;
if (out.length > 2000) out = out.substring(0, 1000) + '\n... truncated ...\n' + out.substring(out.length - 800);
termPrint(out.replace(/\n$/, ''), '');
}
var recheck = await shellExec('command -v aapt2 >/dev/null 2>&1 && command -v ecj >/dev/null 2>&1', termState.homeDir, false);
if (recheck.exitCode === 0) {
termPrint('[OK] Build tools installed successfully!', 'success');
showStatusToast('Build tools installed!', 'success');
termState.devToolsInstalled = true;
return true;
}
if (pkgExists.exitCode === 0 && installResult.exitCode !== 0) {
termPrint('[*] Retrying with apt directly...', 'info');
var retryResult = await shellExec(aptBin + ' update 2>&1 && ' + aptBin + ' install -y aapt2 ecj dx apksigner 2>&1', termState.homeDir, false);
if (retryResult.output) termPrint(retryResult.output.substring(0, 1500).replace(/\n$/, ''), '');
var recheck2 = await shellExec('command -v aapt2 >/dev/null 2>&1 && command -v ecj >/dev/null 2>&1', termState.homeDir, false);
if (recheck2.exitCode === 0) {
termPrint('[OK] Build tools installed!', 'success');
showStatusToast('Build tools installed!', 'success');
termState.devToolsInstalled = true;
return true;
}
}
termPrint('[!] Auto-install failed. Open Terminal and run:', 'err');
termPrint(' pkg update && pkg install aapt2 ecj dx apksigner', 'warning');
termState.devToolsInstalled = false;
return false;
}
async function checkDevEnvironment() {
if (state.currentMode !== 'coding' && state.currentMode !== 'agentic') return;
if (!Bootstrap) return;
try {
var bsStatus = await Bootstrap.getStatus();
if (!bsStatus.installed) {
showDevToolsBanner('Termux not installed. Tap Dev Setup to install Linux environment + build tools.');
return;
}
} catch(e) {}
if (termState.devToolsInstalled) return;
var check = await shellExec('command -v aapt2 >/dev/null 2>&1 && command -v ecj >/dev/null 2>&1', termState.homeDir, false);
if (check.exitCode === 0) {
termState.devToolsInstalled = true;
return;
}
showDevToolsBanner('Build tools (aapt2, ecj, d8) not installed. Tap to auto-install.');
}
function showDevToolsBanner(msg) {
var existing = $('#dev-tools-banner');
if (existing) existing.remove();
var container = $('#messages');
if (!container) return;
var banner = document.createElement('div');
banner.id = 'dev-tools-banner';
banner.className = 'dev-tools-banner';
banner.innerHTML = '<span class="dtb-icon">&#9888;</span> ' +
'<span class="dtb-msg">' + msg + '</span>' +
'<button class="dtb-install-btn">Install</button>' +
'<button class="dtb-dismiss-btn">&times;</button>';
container.insertBefore(banner, container.firstChild);
banner.querySelector('.dtb-install-btn').addEventListener('click', async function() {
var btn = this;
btn.textContent = 'Installing...';
btn.disabled = true;
var bsStatus;
try { bsStatus = await Bootstrap.getStatus(); } catch(e) { bsStatus = { installed: false }; }
if (!bsStatus.installed) {
try {
await Bootstrap.install();
if (Shell) {
var env = await Shell.getEnv();
termState.homeDir = env.HOME;
termState.toolsDir = env.TOOLS;
termState.projectsDir = env.PROJECTS;
termState.cwd = env.CWD || env.HOME;
}
updateCwdDisplay();
} catch(e) {
btn.textContent = 'Bootstrap failed';
return;
}
}
var ok = await ensureBuildTools();
if (ok) {
banner.remove();
showStatusToast('All tools installed!', 'success');
} else {
btn.textContent = 'Retry';
btn.disabled = false;
}
});
banner.querySelector('.dtb-dismiss-btn').addEventListener('click', function() {
banner.remove();
});
}
async function autoExecuteActions(actions, conv) {
var hasFiles = actions.some(function(a) { return a.type === 'create_file'; });
var hasBuild = actions.some(function(a) { return a.type === 'build_apk'; });
@@ -2585,6 +2715,7 @@
updateModeSelector();
updateHeader();
updateTerminalVisibility();
checkDevEnvironment();
saveState();
});
});