前面十天都在搞介面和基礎功能,今天終於要實作真正的刪除功能了。這是整個工具最核心的部分,也是最容易出包的地方。
畢竟如果刪錯重要的系統 APP,手機可能會出問題。
在實作刪除功能之前,我決定先建立一個黑名單,防止使用者刪到重要的系統 APP:
const CRITICAL_PACKAGES = [
'com.android.systemui',
'com.android.phone',
'com.android.settings',
'android',
'com.android.vending', // Google Play Store
// ... 更多重要套件
];
function isCriticalPackage(packageName) {
return CRITICAL_PACKAGES.some(critical =>
packageName.startsWith(critical)
);
}
對於這些重要套件,我會在介面上標示警告,或是直接不允許刪除。
ADB 刪除 APP 的指令其實很簡單:
adb shell pm uninstall --user 0 套件名稱
把這個包裝成 JavaScript 函數:
async uninstallApp(deviceId, packageName) {
if (this.isCriticalPackage(packageName)) {
throw new Error('不能刪除重要的系統應用程式');
}
const cmd = `"${this.adbPath}" -s ${deviceId} shell pm uninstall --user 0 ${packageName}`;
return new Promise((resolve, reject) => {
exec(cmd, (error, stdout, stderr) => {
if (error) {
reject(error);
return;
}
if (stdout.includes('Success')) {
resolve(true);
} else {
reject(new Error(`刪除失敗: ${stdout}`));
}
});
});
}
刪除 APP 是不可逆的操作,一定要讓使用者確認。用 MDUI 的 dialog 來實作:
async confirmDelete(selectedApps) {
const appNames = selectedApps.map(app => app.displayName).join('、');
const result = await mdui.confirm({
headline: '確認刪除',
description: `確定要刪除以下應用程式嗎?\n${appNames}\n\n注意:此操作無法復原!`,
confirmText: '確定刪除',
cancelText: '取消'
});
return result;
}
使用者可能會一次選擇多個 APP,所以需要批次處理:
async deleteSelectedApps() {
const checkboxes = document.querySelectorAll('.app-checkbox:checked');
const selectedApps = Array.from(checkboxes).map(cb => ({
package: cb.value,
displayName: cb.dataset.displayName
}));
if (selectedApps.length === 0) {
mdui.snackbar({ message: '請先選擇要刪除的應用程式' });
return;
}
// 確認對話框
const confirmed = await this.confirmDelete(selectedApps);
if (!confirmed) return;
// 顯示進度
const progress = document.getElementById('delete-progress');
progress.style.display = 'block';
// 逐一刪除
let successCount = 0;
for (const app of selectedApps) {
try {
await this.adbManager.uninstallApp(this.selectedDevice, app.package);
successCount++;
} catch (error) {
console.error(`刪除 ${app.displayName} 失敗:`, error);
}
}
// 隱藏進度,顯示結果
progress.style.display = 'none';
mdui.snackbar({
message: `成功刪除 ${successCount} 個應用程式`
});
// 重新載入 APP 列表
this.loadAppList();
}
刪除多個 APP 時會需要一些時間,加入進度條讓使用者知道進度:
<mdui-linear-progress id="delete-progress" style="display: none;"></mdui-linear-progress>
刪除過程中顯示進度條,完成後隱藏。
刪除 APP 時可能會遇到各種錯誤:
try {
await this.uninstallApp(deviceId, packageName);
} catch (error) {
if (error.message.includes('not installed')) {
return '應用程式已經不存在';
} else if (error.message.includes('permission')) {
return '權限不足,無法刪除';
} else {
return `刪除失敗: ${error.message}`;
}
}
不同的錯誤給出不同的提示訊息。
選了幾個不重要的 APP 來測試刪除功能:
成功了!APP 真的從手機上消失了。
有些系統 APP 即使用 ADB 也無法完全刪除,只能停用。需要區分這兩種情況。
ADB uninstall 只會移除 APP,但使用者資料可能還會留在手機裡。
有些手機廠商會限制某些 APP 的刪除,即使是用 ADB 也不行。
雖然 ADB uninstall 理論上不可逆,但其實可以重新安裝同一個 APP:
async reinstallApp(deviceId, packageName) {
// 如果之前有備份 APK,可以重新安裝
// 但這個功能比較複雜,之後再實作
}
這個功能比較複雜,先記下來之後再做。
終於實作了最核心的刪除功能:
從一個只能列出 APP 的工具,變成真正能刪除垃圾軟體的實用工具!
明天要加入一些進階功能,像是 APP 分類過濾、搜尋功能等,讓工具更好用。
現在核心功能都有了,剩下的就是優化使用體驗。
下一篇:[Day 12] 進階功能:讓工具更好用