iT邦幫忙

2025 iThome 鐵人賽

DAY 12
0
Software Development

試著開發一些有用的工具程式系列 第 12

[Day 11] 實作刪除功能:小心不要刪錯

  • 分享至 

  • xImage
  •  

終於要做最重要的功能了

前面十天都在搞介面和基礎功能,今天終於要實作真正的刪除功能了。這是整個工具最核心的部分,也是最容易出包的地方。

畢竟如果刪錯重要的系統 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 來測試刪除功能:

  1. 選擇一個測試 APP
  2. 點擊刪除按鈕
  3. 出現確認對話框
  4. 確認後開始刪除
  5. 顯示成功訊息

成功了!APP 真的從手機上消失了。

遇到的問題

某些 APP 刪不掉

有些系統 APP 即使用 ADB 也無法完全刪除,只能停用。需要區分這兩種情況。

刪除後殘留資料

ADB uninstall 只會移除 APP,但使用者資料可能還會留在手機裡。

廠商限制

有些手機廠商會限制某些 APP 的刪除,即使是用 ADB 也不行。

加入復原機制

雖然 ADB uninstall 理論上不可逆,但其實可以重新安裝同一個 APP:

async reinstallApp(deviceId, packageName) {
  // 如果之前有備份 APK,可以重新安裝
  // 但這個功能比較複雜,之後再實作
}

這個功能比較複雜,先記下來之後再做。

今天成果

終於實作了最核心的刪除功能:

  • 安全檢查防止刪除重要 APP
  • 確認對話框避免誤刪
  • 批次刪除提升效率
  • 進度顯示改善體驗
  • 錯誤處理增加穩定性

從一個只能列出 APP 的工具,變成真正能刪除垃圾軟體的實用工具!

下一步

明天要加入一些進階功能,像是 APP 分類過濾、搜尋功能等,讓工具更好用。

現在核心功能都有了,剩下的就是優化使用體驗。


下一篇:[Day 12] 進階功能:讓工具更好用


上一篇
[Day 10] 美化介面:改用 MDUI 省時間
下一篇
[Day 12] 進階功能:讓工具更好用
系列文
試著開發一些有用的工具程式13
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言