1 Commits

Author SHA1 Message Date
RainySY
2c2c0273ff Checkpoint from VS Code for cloud agent session
Co-authored-by: Copilot <copilot@github.com>
2026-04-29 20:08:41 +08:00
22 changed files with 2955 additions and 8171 deletions

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
# VS Code/ Copilot agent & skill 自定義配置
.github/
# 備份文件
*.bak

491
README.md
View File

@@ -1,445 +1,124 @@
# Backup_script 數據備份腳本
[![Stars](https://img.shields.io/github/stars/YAWAsau/backup_script?label=stars)](https://github.com/YAWAsau)
[![Download](https://img.shields.io/github/downloads/YAWAsau/backup_script/total)](https://github.com/YAWAsau/backup_script/releases)
[![Release](https://img.shields.io/github/v/release/YAWAsau/backup_script?label=release)](https://github.com/YAWAsau/backup_script/releases/latest)
[![License](https://img.shields.io/github/license/YAWAsau/backup_script?label=License)](https://choosealicense.com/licenses/gpl-3.0)
[![Channel](https://img.shields.io/badge/Follow-Telegram-blue.svg?logo=telegram)](https://t.me/yawasau_script)
<p align="center">
<a href="https://github.com/YAWAsau/backup_script/stargazers"><img src="https://img.shields.io/github/stars/YAWAsau/backup_script?label=stars&style=flat-square" /></a>
<a href="https://github.com/YAWAsau/backup_script/releases"><img src="https://img.shields.io/github/downloads/YAWAsau/backup_script/total?style=flat-square" /></a>
<a href="https://github.com/YAWAsau/backup_script/releases/latest"><img src="https://img.shields.io/github/v/release/YAWAsau/backup_script?label=release&style=flat-square" /></a>
<a href="https://choosealicense.com/licenses/gpl-3.0"><img src="https://img.shields.io/github/license/YAWAsau/backup_script?label=License&style=flat-square" /></a>
<a href="https://t.me/yawasau_script"><img src="https://img.shields.io/badge/Follow-Telegram-blue.svg?logo=telegram&style=flat-square" /></a>
</p>
## 概述
---
創作該腳本是為了使用戶能夠更加完整地**備份/恢復**應用數據,
支援設備必須符合以下條件:`Android 8+`+`arm64`
## 📖 概述
由於本人是台灣人所以發布的版本為繁體版
(CN系統將自動翻譯自身腳本為簡體中文
一款專為 Android 設計的完整應用數據備份/恢復 Shell 腳本,支援 SSAID、運行時權限、OBB 數據包、WiFi 設定等完整備份,讓你換機換系統後能無縫還原所有應用狀態。
新版增加**完整的遠端備份系統**,支援 WebDAV / SMB 上傳到 NAS / 雲端 / 區網電腦,並可從遠端下載備份回手機直接恢復。支援**流式備份模式**,邊壓縮邊傳輸,數據完全不佔用本機空間。
## 優勢
> 作者為台灣人,預設發布繁體版本。CN 系統環境下腳本將自動翻譯為簡體中文
- 數據完整:在更換系統之後,原有的數據全部保留,無需重新登陸或者下載額外數據包
- 支援備份SSAID 可完美備份LINE
- 支援備份應用權限 可備份運行時權限與ops權限
- 易操作:簡單几步即可備份應用完整數據!
- 限制少:不限制機型,可跨安桌版本。
- 功能強:可備份恢復`split apk`
- 算法多:目前支持的壓縮算法有 `tar(默認)`
- `zstd`
- 速度快:即使使用`zstd`壓縮算法速率依舊快速(對比鈦備份 swift backup
- 腳本自帶tools完整性效驗與壓縮包效驗
## 如何使用
`請認真閱讀以下說明,以減少不必要的問題`
**系統需求:** `Android 8+` · `arm64 架構` · `Root 權限(Magisk / KernelSU)`
##### 推薦工具:[`MT管理器`](https://www.coolapk.com/apk/bin.mt.plus),若使用`Termux`,則請勿使用`tsu`。
---
#### !!!以下操作皆須ROOT!!! ####
## ✨ 功能特色
| 功能 | 說明 |
|------|------|
| 📦 完整數據備份 | 換機換系統後原有數據完整保留,無需重新登入或下載額外數據包 |
| 🔑 SSAID 備份 | 支援 SSAID 備份,可完美備份 LINE 等依賴設備識別碼的應用 |
| 🛡️ 權限備份 | 支援備份運行時權限(Runtime Permission)與 ops 權限 |
| 📂 Split APK | 支援備份與恢復 Split APK 格式 |
| 🎮 OBB 數據包 | 可選備份外部 OBB 數據(如原神、王者榮耀等大型遊戲) |
| 📡 WiFi 備份 | 支援備份與恢復 WiFi 設定 |
| 📁 自定義資料夾備份 | 可備份 DCIM、Download、Music 等任意自定義目錄 |
| 🗜️ 多種壓縮算法 | 支援 `tar`(僅打包)與 `zstd`(高壓縮率高速度) |
| ⚡ 高速壓縮 | zstd 壓縮速率快速,優於鈦備份、Swift Backup |
| 🔒 完整性校驗 | 內建 tools SHA-256 校驗與壓縮包完整性驗證 |
| 🔄 增量備份 | 多維度比對(版本/大小/權限/SSAID),無變化則跳過 |
| ✅ 最終計數核驗 | 備份結束後逐檔核對存在性,三態顯示結果 |
| 🖥️ 後台執行 | 支援後台執行模式,可完全關閉終端,log 持續刷新 |
| 💡 偽裝亮屏 | 備份/恢復期間可偽裝亮屏,避免 IO 因息屏降速 |
| 🌐 自動更新 | 聯網偵測最新版本,支援 CDN 節點(適合中國大陸用戶) |
| 🌏 多語言 | 自動識別系統語言環境,支援繁體中文/簡體中文自動切換 |
| 👥 多用戶支援 | 支援多用戶環境(user 0、999 等),可手動或自動選擇用戶 |
| ⬛ 黑名單模式 | 黑名單應用可選「完全忽略」或「僅備份安裝包」 |
| ⬜ 白名單支援 | 支援預裝應用白名單與系統應用白名單,可指定備份範圍 |
| 📱 進程偵測 | 可設定忽略正在運行中的應用,避免備份數據不一致 |
| ☁️ 遠程備份上傳 | 支援 WebDAV / SMB 兩種協議,備份完成自動上傳,智能增量 |
| 🚀 流式備份 | 邊壓縮邊傳輸,數據不落本機,節省本地空間 |
| 📥 遠程下載恢復 | 可從遠端直接下載備份回手機,點 start.sh 即可恢復 |
| 🔍 區網掃描 | 自動掃描區網內所有 SMB 主機,免去手動找 IP |
| 🧪 連線測試 | 三層測試(TCP / 認證 / 路徑),設定不需備份就能驗證 |
---
## 🗂️ 主選單功能
### 備份模式
| 選項 | 功能 |
|------|------|
| 生成應用列表 | 掃描已安裝的第三方應用並生成 `appList.txt` |
| 備份應用 | 根據列表與設定完整備份應用數據 |
| 備份已更新應用 | 僅備份自上次備份以來有版本更新的應用 |
| 備份自定義資料夾 | 備份 `backup_settings.conf` 內設定的自定義目錄 |
| 備份 WiFi | 備份當前設備的 WiFi 設定 |
| 測試遠端連線 | 驗證 WebDAV / SMB 設定,三層測試(TCP / 認證 / 路徑) |
| 單獨上傳當前備份 | 上傳現有本地備份到遠端,不重新跑備份流程 |
| 列出遠端備份 | 連線遠端、產生 `appList_network.txt` 讓你勾選要下載哪些 app |
| 從遠端下載備份 | 依清單下載備份到本地,可直接執行恢復 |
| 殺死運行中腳本 | 安全終止正在執行的備份腳本(整棵進程樹) |
### 恢復模式
| 選項 | 功能 |
|------|------|
| 重新生成應用列表 | 刷新恢復資料夾內的 `appList.txt` |
| 恢復備份 | 根據列表完整恢復應用與數據 |
| 僅恢復包含 SSAID 應用(含數據) | 只恢復有 SSAID 的應用及其完整數據 |
| 僅恢復包含 SSAID 應用(不含數據) | 只套用 SSAID,不覆蓋現有數據 |
| 恢復自定義資料夾 | 恢復備份的自定義目錄 |
| 恢復 WiFi | 恢復已備份的 WiFi 設定 |
| 壓縮檔完整性檢查 | 驗證備份壓縮包是否完整無損 |
| 轉換文件夾名稱 | 將備份資料夾名稱格式轉換(用於跨版本相容) |
| 殺死運行中腳本 | 安全終止正在執行的恢復腳本 |
---
## 📁 目錄結構
1. 首先將下載到的腳本解壓到任意目錄後,可以看到以下目錄結構 警告! 不論備份或是恢復都必須保證tools的存在與完整性 否則腳本失效或是二進制調用失敗。
`這是腳本結構與說明`
```
backup_script.zip
├── tools/
├── busybox # 核心工具集
├── zstd # zstd 壓縮工具
├── tar # tar 打包工具
│ ├── curl # 遠程傳輸工具 (WebDAV)
├── smbclient # SMB 遠程傳輸
│ ├── jq # JSON 處理
│ ├── find # 文件搜索
│ ├── keycheck # 音量鍵監聽
├── cmd # 系統指令橋接
├── classes.dex # Java 功能擴展(詳見下方說明)
├── soc.json # 處理器資料庫
├── Device_List # 設備型號資料庫
└── tools.sh # 核心腳本
── backup_settings.conf # 備份行為設定檔
└── start.sh # 主執行腳本
├── tools
├── Device_List
├── bc
├── busybox
├── classes.dex
├── cmd
├── jq
├── find
├── keycheck
├── soc.json
├── tar
├── tools.sh
├── zip
└── zstd
├── backup_settings.conf <--- 腳本默認行為設置
── start.sh <--- 執行腳本
```
> ⚠️ **重要:** 無論備份或恢復,都必須確保 `tools/` 目錄完整存在,否則腳本將無法正常運作
2. 然後執行`start.sh`腳本音量鍵選擇生成應用列表,等待腳本輸出提示結束,此時會在當前目錄生成一個`appList.txt`,這就是你當前安裝的所有第三方應用(腳本會屏蔽預裝應用可於backup_settings.conf設置需要備份包名)
備份完成後,每個 app 子目錄會額外生成 `backup.sh` / `recover.sh` / `upload.sh`,可單獨備份、恢復或上傳單一應用
3. 現在打開生成`appList.txt`,根據裏面的提示操作後保存,這樣你就設置好了需要備份的軟件
---
4. 最後找到`backup_settings.conf`打開後根據提示設置保存,再打開`start.sh`,音量鍵選擇備份應用,備份結束完成後會在當前目錄生成一個以`Backup_壓縮算法名`命名的資料夾,裡面就是你的軟件備份。把這個資料夾整個保持到其他位置,刷完機后複製回手機,直接執行`Backup_壓縮算法名/start.sh`即可恢復備份的所有數據,同樣道理,裡面也有個`appList.txt`使用方法跟第3步驟一樣不需要還原的刪除即可另外進去備份好的資料夾找到單獨應用資料夾有個 backup.sh and recover.sh可以單獨備份與恢復腳本。
## ⚙️ 設定檔說明(backup_settings.conf)
5. 腳本執行過程中請留意紅色字眼提示有無任何錯誤並且使用恢復腳本時留意恢復結束後是否提示應用存在ssaid假設提示存在ssaid請在恢復後立刻重啟已便套用ssaid,假設恢復ssaid後立刻打開應用會導致ssaid套用失敗因為Android會產生一個新的saaid如此會導致應用卡白屏或是提示需要登錄ssaid是判斷應用是否換過環境與設備的判斷之一保持一致可以減少諸如提示異地登錄或是需要重新登入驗證的方法。
| 設定項 | 說明 | 預設值 |
|--------|------|--------|
| `Lo` | 操作方式:`0` 音量鍵 / `1` 音量鍵(強制) / `2` 鍵盤輸入 | `0` |
| `background_execution` | 後台執行:`1` 可關閉終端 / `0` 需保持終端開啟 | `0` |
| `setDisplayPowerMode` | 備份期間偽裝亮屏防止 IO 降速 | `0` |
| `Shell_LANG` | 語言:`0` 繁體中文 / `1` 簡體中文(留空自動偵測) | 自動 |
| `Output_path` | 自定義備份輸出路徑(留空使用當前目錄) | 空 |
| `list_location` | 自定義 appList.txt 位置(留空使用當前目錄) | 空 |
| `update` | 自動更新:`1` 開啟 / `0` 關閉 | `1` |
| `cdn` | 更新 CDN 節點:`0` 直連 / `1` ghfast.top / `2` workers.dev | `1` |
| `mount_point` | 屏蔽外部掛載點,多個用 `\|` 分隔 | `rannki\|0000-1` |
| `user` | 指定用戶 ID(留空自動選擇) | 空 |
| `Backup_Mode` | 備份模式:`1` 應用+數據 / `0` 僅安裝包 | `1` |
| `Backup_user_data` | 備份 user 數據:`1` 是 / `0` 否 | `1` |
| `Backup_obb_data` | 備份 OBB 外部數據:`1` 是 / `0` 否 | `1` |
| `backup_media` | 備份完成後一併備份自定義資料夾 | `0` |
| `Background_apps_ignore` | 忽略正在運行中的應用:`1` 忽略 / `0` 備份 | `0` |
| `Custom_path` | 自定義備份目錄列表(絕對路徑,每行一個) | DCIM / Download 等 |
| `blacklist_mode` | 黑名單模式:`1` 完全忽略 / `0` 僅備份安裝包 | `0` |
| `blacklist` | 黑名單應用包名列表 | 空 |
| `whitelist` | 預裝應用白名單包名列表 | 小米系列預裝 |
| `system` | 系統應用白名單包名列表 | Google 系列 |
| `Compression_method` | 壓縮算法:`zstd``tar` | `zstd` |
| `rgb_a` / `rgb_b` / `rgb_c` | 終端輸出主色/輔色1/輔色2(256 色代碼) | `220` / `51` / `213` |
| `remote_type` | 遠程備份協議:`webdav` / `smb`(留空不啟用) | 空 |
| `smb_url` | SMB 伺服器地址 | 空 |
| `smb_remote_user` | SMB 認證用戶名 | 空 |
| `smb_remote_pass` | SMB 認證密碼 | 空 |
| `webdav_url` | WebDAV 伺服器地址 | 空 |
| `webdav_remote_user` | WebDAV 認證用戶名 | 空 |
| `webdav_remote_pass` | WebDAV 認證密碼 | 空 |
| `remote_keep_local` | 上傳成功後本地檔案:`1` 保留 / `0` 刪除 | `0` |
| `remote_stream` | 流式備份:`1` 開啟(邊壓邊傳,不佔本機) / `0` 關閉 | `0` |
---
##### 附加說明:如何恢復 以下是關於恢復資料夾內的文件說明?
## 🚀 使用方式
1. 找到恢復資料夾內的appList.txt打開 編輯列表 保存退出
> 推薦使用 [MT 管理器](https://www.coolapk.com/apk/bin.mt.plus) 執行腳本。若使用 Termux,請勿使用 `tsu`。
2. 找到start.sh 給予root音量鍵選擇恢復備份後等待腳本結束即可
### 備份流程
3. start.sh的重新生成應用列表功能可用於刷新appList.txt內的列表 使用時機為當你刪除列表內的任何應用備份時,抑或者是恢復備份提示列表錯誤時
**Step 1 — 生成應用列表**
4. start.sh的終止腳本功能用於突然想要終止腳本或是意外操作時使用 同理備份也有一個,因為腳本無須後台特性不能使用常規手段終結,故此另外寫了一個終止
解壓腳本後執行 `start.sh`,選擇「**生成應用列表**」。執行完畢後,當前目錄會生成 `appList.txt`
**Step 2 — 編輯應用列表**
# 關於如何更新腳本?
- 目前有三種更新方法,有下列方式
- 1.手動將下載的備份腳本zip不解壓縮直接放到腳本任意目錄(不包括tools目錄內)的任意地方執行任何腳本即可更新,腳本將提示
- 2.此備份的任何腳本在執行時均會聯網檢測腳本版本,當更新時會自己提示與下載,根據腳本提示操作的即可(conf update=1時生效),腳本聯網僅作為檢查更新用途,無任何非法操作亦或是下發格機
- 3.將下載的壓縮包不解壓縮直接放在/storage/emulated/0/Download腳本自動檢測更新並按照提示操作即可
- 4.在QQ群內下載的腳本不解壓縮腳本會自己檢測更新
打開 `appList.txt`,根據需求調整:
- 行首加 `#`:注釋掉該應用,不備份
- 行首加 `!`:僅備份安裝包,不備份數據
## 關於反饋
- 如果使用過程中出現問題,請攜帶截圖並詳細說明問題,建立 [issues](https://github.com/YAWAsau/backup_script/issues)。
- 酷安 @[落葉淒涼TEL](http://www.coolapk.com/u/2277637)
- QQ組 976613477 很少上 盡量來TG
- TG https://t.me/yawasau_script
**Step 3 — 設置備份選項**
## 答疑
- 一個shell腳本內為什麼有dex?
- dex用來實現腳本難以實現的目的目前saaid備份恢復備份恢復運行時權限與ops權限下載與訪問GitHub api來檢查腳本更新列出使用者應用名稱與包名繁體轉簡體均為dex的功能感謝[Android-DataBackup](https://github.com/XayahSuSuSu/Android-DataBackup) by [XayahSuSuSu](https://github.com/XayahSuSuSu)
打開 `backup_settings.conf`,根據上方設定說明調整各選項後儲存。
## 常見問題
**Step 4 — 執行備份**
Q1批量備份大量提示失敗怎麼辦
A1退出腳本刪除/data/backup_tools再備份一次
執行 `start.sh`,選擇「**備份應用**」。備份完成後生成 `Backup_<壓縮算法>_<用戶ID>/` 資料夾。
Q2批量恢復大量提示失敗怎麼辦
A2退出腳本按照上面同樣操作。 如果還是錯誤請建立issues我幫你排除錯誤
---
Q3微信/QQ 能不能完美備份&恢復數據?
A3不能保證有的人說不能有的人說能所以備份會有提示。 建議用你信賴的備份軟件針對微信/QQ再備份一次以防丟失重要數據
### 恢復流程
Q4為什麼部分應用備份很久 例如王者榮耀、PUBG、原神、微信、QQ。
A4因為連同軟件數據包都給你備份了例如原神數據包9GB+,當然久到裂開了,恢復也是同理,還要解壓縮數據包
**Step 1 — 編輯恢復列表**
Q5:腳本每次備份都是全新備份嗎?
A5;腳本備份時會比對上次備份時的備份SIZE大小 如果有差異就備份,反之忽略備份節省時間
進入備份資料夾,打開 `appList.txt`,刪除或注釋不需要恢復的應用行。
備份腳本耗費了我大量時間與精力 如果你覺得好用可以捐贈XD
.(https://paypal.me/YAWAsau?country.x=TW&locale.x=zh_TW))
**Step 2 — 執行恢復**
執行備份資料夾內的 `start.sh`,選擇「**恢復備份**」,等待腳本完成。
## 銘謝貢獻
- 臭批老k([kmou424](https://github.com/kmou424)):提供部分與驗證函數思路
- 屑老方([雄氏老方](http://www.coolapk.com/u/665894)):提供自動更新腳本方案
- 胖子老陳(雨季騷年)
- XayahSuSuSu([XayahSuSuSu](https://github.com/XayahSuSuSu))提供App支持,dex支持
**Step 3 — 注意 SSAID**
若恢復結束後提示應用存在 SSAID,請**立刻重啟**後再開啟應用。若先開啟應用,Android 會生成新的 SSAID,導致應用白屏或需要重新登入。
> 💡 備份資料夾內每個應用子目錄都有獨立的 `backup.sh`、`recover.sh`、`upload.sh`,可單獨操作單一應用。
---
## ☁️ 遠程備份
### 設定方式
編輯 `backup_settings.conf`,SMB 與 WebDAV 地址分開設定,切換協議免重輸:
```conf
remote_type=smb
smb_url=smb://192.168.1.100/Backup
smb_remote_user=用戶名
smb_remote_pass=密碼
webdav_url=http://192.168.1.100:8080/dav/
webdav_remote_user=用戶名
webdav_remote_pass=密碼
remote_keep_local=0
remote_stream=0
```
| 協議 | 地址格式 | 適用場景 |
|------|----------|---------|
| SMB | `smb://192.168.1.100/share/` | Windows 共享 / Samba / NAS |
| WebDAV | `http://192.168.1.100:8080/dav/` | NAS / Nextcloud / rclone serve |
### 遠端目錄結構
腳本在 `remote_url` 下自動建立 `Backup_<壓縮算法>_<用戶ID>/`,與本地結構完全鏡像:
```
smb://NAS/Backup/
Backup_zstd_0/
1DM+/
LINE/
Keep記事/
wifi/wifi.json
tools/
start.sh
restore_settings.conf
appList.txt
MT管理器.apk
```
不同用戶(0、999)自動分開到 `Backup_zstd_0/``Backup_zstd_999/`,互不衝突。
### 特性
- **智能增量** — 多維度比對(版本/大小/權限/SSAID),只上傳本次有變更的 app
- **遠端預掃** — 單次連線取全部列表 + 批量下載 json,主循環零網路開銷
- **流式備份** — `remote_stream=1` 邊壓縮邊傳輸,數據完全不佔本機空間
- **最終核驗** — 備份結束後逐檔核對存在性,缺失檔案列出清單
- **連線預檢** — 沒網路時 3 秒內判斷並停用上傳,不卡死腳本
- **失敗保護** — 流式上傳失敗不更新遠端 json,確保下輪整體重備
---
### 流式備份模式
`remote_stream=1` 啟用流式備份,數據直接 `tar | zstd | 傳輸`,本機不產生任何備份檔案:
```conf
remote_type=smb
smb_url=smb://192.168.1.100/Backup
smb_remote_user=用戶名
smb_remote_pass=密碼
remote_stream=1
```
> ⚠️ 流式模式為全量上傳(無本機校驗),建議搭配穩定的區網環境使用。
---
### 從遠端下載備份
**Step 1** — 主選單選「**列出遠端備份**」,產生 `appList_network.txt`
**Step 2** — 打開 `appList_network.txt`,用 `#` 註解掉不要下載的 app
**Step 3** — 主選單選「**從遠端下載備份**」,下載完成後直接執行內附 `start.sh` 恢復
---
### 連線測試
設定完 `backup_settings.conf` 後,主選單選「**測試遠端連線**」:
```
—————— TCP 連線測試 ——————
目標: 192.168.1.100:445
TCP 連線通過
—————— 認證與列目錄測試 ——————
SMB 認證通過, share 可存取
全部測試通過, 可以開始備份
```
---
## 🔄 腳本更新方式
1. **ZIP 放置更新**:將下載的 `.zip` 不解壓,直接放到腳本任意目錄(`tools/` 除外),執行任何腳本即自動更新
2. **聯網自動更新**:腳本執行時自動連線 GitHub API 檢查版本(需設置 `update=1`)
3. **Download 目錄**:將 `.zip` 放置於 `/storage/emulated/0/Download/`,腳本自動偵測並更新
4. **QQ 群下載**:從 QQ 群下載的腳本不解壓,直接放置後執行即可自動更新
> 🔒 腳本聯網**僅用於檢查更新**,無任何資料收集或非法操作。
---
## ❓ 常見問題
<details>
<summary><b>Q1:批量備份/恢復大量提示失敗?</b></summary>
退出腳本,刪除 `/data/backup_tools/` 目錄後重新執行。若問題持續,請建立 [Issue](https://github.com/YAWAsau/backup_script/issues) 並附上截圖與 log。
</details>
<details>
<summary><b>Q2:微信/QQ 能完美備份恢復嗎?</b></summary>
無法保證。建議同時使用其他你信賴的備份工具針對微信/QQ 額外備份,以防丟失重要數據。
</details>
<details>
<summary><b>Q3:為什麼部分應用備份很久?</b></summary>
腳本會一同備份應用的 OBB 數據包,例如原神數據包超過 9GB。可在 `backup_settings.conf` 設置 `Backup_obb_data=0` 跳過 OBB 備份。
</details>
<details>
<summary><b>Q4:腳本每次都是全量備份嗎?</b></summary>
否。腳本多維度比對上次備份(版本號/數據大小/權限/SSAID),無差異則跳過,節省時間與空間。
</details>
<details>
<summary><b>Q5:為什麼腳本內包含 .dex 檔案?</b></summary>
`classes.dex` 用於實現 Shell 腳本難以達成的功能:
- SSAID 備份與恢復
- 運行時權限(Runtime Permission)與 ops 權限批量備份恢復
- GitHub API 更新版本檢查與下載
- 應用名稱與包名查詢
- 繁體中文 ↔ 簡體中文自動翻譯
- 後台執行模式的推送通知
感謝 [XayahSuSuSu](https://github.com/XayahSuSuSu) 的 [Android-DataBackup](https://github.com/XayahSuSuSu/Android-DataBackup) 提供 App 支持。
</details>
<details>
<summary><b>Q6:息屏後備份速度變慢?</b></summary>
這是 Android 內核的 IO 節能機制。建議在 `backup_settings.conf` 設置 `setDisplayPowerMode=1` 開啟偽裝亮屏,或備份期間保持螢幕常亮。
</details>
<details>
<summary><b>Q7:如何單獨備份/恢復/上傳單一應用?</b></summary>
進入備份資料夾內對應的應用子目錄,直接執行:
- `backup.sh` — 單獨備份該 app
- `recover.sh` — 單獨恢復該 app
- `upload.sh` — 單獨上傳該 app 到遠端
</details>
<details>
<summary><b>Q8:WebDAV 上傳顯示 HTTP 423 Locked?</b></summary>
某些雲端網盤的 WebDAV 對大檔有大小限制。建議改用:
- 自家 NAS / Windows SMB
- rclone serve webdav
- 群暉 / Nextcloud
</details>
<details>
<summary><b>Q9:WebDAV 上傳顯示 HTTP 404?</b></summary>
請檢查:
- `webdav_url` 路徑是否含正確的 webdav 端點(例如 `/dav/``/remote.php/webdav/`)
- 帳號是否有寫入權限
</details>
<details>
<summary><b>Q10:SMB 提示「找不到 share」?</b></summary>
- Windows 端確認 SMB 共享已開啟,且網路設成「私人」而非「公用」
- 防火牆放行 445 port
- 主選單啟動時的 `scan_smb` 會自動列出區網 SMB 主機與 share 名,可對照確認
</details>
<details>
<summary><b>Q11:沒網路會影響備份嗎?</b></summary>
不會。腳本啟動時做 TCP 預檢(3 秒內判斷),沒網路時自動停用遠端上傳但**完整保留本地備份**,流程繼續跑完。
</details>
<details>
<summary><b>Q12:流式備份和一般備份有什麼差別?</b></summary>
| | 一般備份 | 流式備份 |
|---|---|---|
| 本機空間佔用 | 先壓縮到本機再上傳 | 不佔用(直接傳輸) |
| 增量比對 | ✅ 支援 | ✅ 支援 |
| 本機完整性校驗 | ✅ 支援 | ❌ 不支援(信任傳輸) |
| 適合場景 | 本機空間充足 | 本機空間有限 |
</details>
---
## 📬 問題反饋
遇到問題請攜帶截圖與 log 檔,透過以下方式反饋:
- 🐛 [GitHub Issues](https://github.com/YAWAsau/backup_script/issues)
- 💬 [Telegram 頻道](https://t.me/yawasau_script)
- 🐧 QQ 群:`976613477`
- 🧊 酷安:[@落葉淒涼TEL](http://www.coolapk.com/u/2277637)
---
## ☕ 支持作者
備份腳本耗費了大量時間與精力,如果你覺得好用,歡迎贊助支持!
[![Donate](https://img.shields.io/badge/Donate-PayPal-blue.svg?style=flat-square&logo=paypal)](https://paypal.me/YAWAsau?country.x=TW&locale.x=zh_TW)
---
## 🙏 銘謝貢獻
| 貢獻者 | 貢獻內容 |
|--------|----------|
| [kmou424](https://github.com/kmou424)(臭批老k) | 提供部分驗證函數思路 |
| [雄氏老方](http://www.coolapk.com/u/665894)(屑老方) | 提供自動更新腳本方案 |
| [sakuradairong](https://github.com/sakuradairong)(雨季騷年/胖子老陳) | 新增 WebDAV / SMB 功能與測試 |
| [XayahSuSuSu](https://github.com/XayahSuSuSu) | 提供 App 支持與 dex 功能支持 |
`文檔編輯:Petit-Abba, YuKongA`
---
<p align="center">
<sub>GPL-3.0 Licensed · Made with ❤️ by <a href="https://github.com/YAWAsau">YAWAsau</a></sub>
</p>
`文檔編輯Petit-Abba, YuKongA`

View File

@@ -4,13 +4,13 @@
Lo=0
#後台執行腳本
#0不能關閉當前終端有壓縮速率
#1終端有可能完全無顯示但是log會持續刷新可直接完全關閉終端
0不能關閉當前終端有壓縮速率
1終端有可能完全無顯示但是log會持續刷新可直接完全關閉終端
background_execution=0
#腳本語言設置 留空則自動識別系統語言環境並翻譯
#1簡體中文 0繁體中文
Shell_LANG=
Shell_LANG=0
#備份開始後偽裝亮屏
#1開啟 0關閉
@@ -19,12 +19,6 @@ setDisplayPowerMode=0
#自定義備份文件輸出位置 支持相對路徑(留空則默認當前路徑)
Output_path=""
#自定義備份目錄後綴(留空則不添加後綴)
#支持日期時間變量:%yyyymmdd %hhmmss %yyyymmddhhmmss %yyyy %mm %dd
#例_daily → Backup_zstd_0_daily
#例_%yyyymmdd → Backup_zstd_0_20260522
Backup_suffix=""
#自定義applist.txt位置 支持相對路徑(留空則默認當前路徑)
list_location=""
@@ -43,7 +37,7 @@ cdn=1
mount_point="rannki|0000-1"
#使用者(如0 999等用戶如存在多個用戶留空強制選擇無多個用戶則默認用戶0不詢問)
user=0
user=
#備份模式
#1包含數據+安裝包0僅包安裝包
@@ -65,19 +59,23 @@ backup_media=0
Background_apps_ignore=0
#添加自定義備份路徑 例Download DCIM等文件夾 請使用絕對路徑,請勿刪除""
Custom_path=""
Custom_path="
/storage/emulated/0/Pictures/
/storage/emulated/0/Download/
/storage/emulated/0/Music
/storage/emulated/0/DCIM/
#/data/adb
"
#黑名單模式(1完全忽略不備份 0僅備份安裝包注意此選項Backup_Mode=1時黑名單模式才能使用)
blacklist_mode=0
#備份黑名單(備份策略由「黑名單模式」控制,此處只作為黑名單應用列表)
blacklist="
com.facebook.orca
com.facebook.katana
com.esunbank
com.instagram.barcelona
com.instagram.android
com.esunbank.ESUNWALLET
com.esunbank
com.facebook.katana
#com.chailease.tw.app.android.ccfappcust"
#位於data的預裝應用白名單 例:相冊 錄音機 天氣 計算器等(默認屏蔽備份預裝應用,如需備份請添加預裝應用白名單)
@@ -118,56 +116,9 @@ com.android.chrome"
#zstd擁有良好的壓縮率與速度
Compression_method=zstd
#色彩設定 (256 色 ANSI 編號)
#常用值: 39藍 51青 82綠 196紅 208橘 213粉 220黃 165紫
#主色 (一般資訊, 預設亮黃)
rgb_a=220
#輔色1 (提示/進度, 預設亮青)
rgb_b=51
#輔色2 (強調/變數值, 預設粉紅)
rgb_c=213
#遠程備份類型 (留空不啟用)
#推薦 webdav (穩定)
#smb 支援 SMB2/SMB3 (本腳本拒絕 SMB1/CIFS, 會自動協商到伺服器支援的最高版本)
remote_type=
# 保存原始值, 供連線失敗後 (如中途開 WiFi) 重新檢測用
_remote_type_orig=
#遠程地址 (兩種協議分開設定, 切換 remote_type 免重輸)
#SMB例: smb://192.168.1.100/backup/
smb_url=
#認證用戶名
smb_remote_user=
#認證密碼
smb_remote_pass=""
#WebDAV例: http://192.168.1.100:8080/dav/
webdav_url=
#認證用戶名
webdav_remote_user=
#認證密碼
webdav_remote_pass=""
#流式上傳 (邊壓邊傳, 不佔本機空間)
#1 開啟流式: 數據直接壓縮→管道傳到遠端, 本機不留 tar (省空間, 全量上傳, 不做本機校驗/增量)
#0 關閉(預設): 先壓到本機→校驗→再上傳 (保留本機檔案, 支援增量)
#支援 smb / webdav 兩種 remote_type
remote_stream=0
#流式上傳除錯 (1=失敗時印出 smbclient/curl 的具體錯誤, 用於排查流式失敗原因)
_stream_debug=1
#遠程備份完成後是否保留本地檔案
#1保留本地檔案(上傳後不刪除) 0上傳成功後刪除本地檔案
remote_keep_local=0
#邊備份邊上傳 (每備份完一個應用立即上傳,然後刪除本機檔案再備份下一個,以節省本機空間)
#1 開啟 0 關閉
#開啟後:每個應用備份完成 → 立即上傳遠端 → 上傳成功後刪除本機檔案 → 繼續備份下一個
#關閉後:先備份所有應用 → 全部備份完再統一上傳
remote_upload_per_app=0
#log 目錄大小上限 (單位 MB), 達到上限會在啟動時自動清空 log/
#留空或設 0 = 關閉自動清理
log_max_size_mb=1
#
rgb_a=226
#輔色
rgb_b=123
rgb_c=177

View File

@@ -1,246 +0,0 @@
# Remote Backup (WebDAV & SMB) Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Add WebDAV upload and SMB mount backup support to the backup script using built-in busybox tools.
**Architecture:** Four config variables in `backup_settings.conf` control remote backup. Five new functions in `tools/tools.sh` handle SMB mount/unmount and WebDAV upload. A single hook in `backup_path()` triggers remote setup. The existing EXIT trap is extended to handle remote cleanup.
**Tech Stack:** Shell script (Android mksh compatible), busybox (wget, mount, umount)
---
## File Structure
| File | Action | Responsibility |
|------|--------|---------------|
| `backup_settings.conf` | Append | Remote backup configuration |
| `tools/tools.sh` | Modify | Remote backup functions + hook + trap extension |
---
### Task 1: Add remote config to backup_settings.conf
**Files:**
- Modify: `backup_settings.conf`
- [ ] **Step 1: Append remote backup configuration**
Append after line 123 (`rgb_c=177`), before the trailing newline at line 124:
```conf
#遠程備份類型 (留空不啟用)
#webdav 或 smb
remote_type=
#遠程地址
#WebDAV例: http://192.168.1.100:8080/dav/
#SMB例: //192.168.1.100/backup
remote_url=
#遠程認證用戶名
remote_user=
#遠程認證密碼
remote_pass=
```
Edit command: Use the Edit tool to append these lines after `rgb_c=177`.
- [ ] **Step 2: Verify config file**
Run: Read `backup_settings.conf` lines 120-140 to confirm the config lines are present.
- [ ] **Step 3: Commit**
```bash
git add backup_settings.conf
git commit -m "feat: add remote backup config (remote_type, remote_url, remote_user, remote_pass)"
```
---
### Task 2: Add remote backup functions to tools.sh
**Files:**
- Modify: `tools/tools.sh` (insert after line 465, before `Show_boottime` at line 466)
- [ ] **Step 1: Insert remote functions**
Insert after line 465 (`kill_Serve` call) and before line 466 (`Show_boottime()`):
```sh
# -------- 遠程備份功能 --------
mount_smb() {
local mnt="$TMPDIR/smb_mount"
mkdir -p "$mnt"
if busybox mount -t cifs "$remote_url" "$mnt" -o "username=$remote_user,password=$remote_pass,iocharset=utf8,vers=2.0" 2>/dev/null; then
SMB_MOUNT="$mnt"
[[ $(mount | grep "$mnt") != "" ]] || { echoRgb "SMB掛載失敗: $remote_url" "0"; return 1; }
echoRgb "SMB已掛載: $remote_url -> $mnt" "1"
return 0
fi
echoRgb "SMB掛載失敗回退本地備份" "0"
return 1
}
umount_smb() {
[[ -n $SMB_MOUNT ]] && {
busybox umount -l "$SMB_MOUNT" 2>/dev/null
rm -rf "$SMB_MOUNT"
unset SMB_MOUNT
echoRgb "SMB已卸載" "2"
}
}
upload_webdav() {
local base_url="${remote_url%/}"
local auth=$(echo -n "$remote_user:$remote_pass" | busybox base64)
local failed=0
local list_file="$TMPDIR/.wdav_list"
find "$Backup" -type f > "$list_file"
while read -r f; do
[[ -z $f ]] && continue
local rel="${f#$Backup/}"
echoRgb "上傳: $rel" "2"
if busybox wget -q --method PUT --body-file="$f" --header "Authorization: Basic $auth" "$base_url/$rel" 2>/dev/null; then
rm -f "$f"
else
failed=1
break
fi
done < "$list_file"
rm -f "$list_file"
if [[ $failed -eq 0 ]]; then
echoRgb "WebDAV上傳完成" "1"
else
echoRgb "WebDAV上傳失敗本地檔案已保留" "0"
return 1
fi
}
remote_setup() {
[[ -z $remote_type ]] && return
case $remote_type in
smb)
mount_smb || return
Backup="$SMB_MOUNT/Backup_${Compression_method}_$user"
mkdir -p "$Backup"
Backup_path="${Backup%/*}"
Output_path=""
echoRgb "遠程備份目錄: $Backup" "3"
;;
webdav)
echoRgb "WebDAV模式: 備份完成後將自動上傳" "3"
;;
esac
}
remote_cleanup() {
case $remote_type in
smb) umount_smb ;;
webdav) upload_webdav ;;
esac
}
```
- [ ] **Step 2: Verify insertion**
Run: Read `tools/tools.sh` lines 464-550 to confirm functions are inserted correctly and `Show_boottime()` follows.
- [ ] **Step 3: Commit**
```bash
git add tools/tools.sh
git commit -m "feat: add remote backup functions (mount_smb, umount_smb, upload_webdav, remote_setup, remote_cleanup)"
```
---
### Task 3: Wire hook and trap
**Files:**
- Modify: `tools/tools.sh` (line 462 and line 991-993)
- [ ] **Step 1: Modify EXIT trap in kill_Serve**
Change line 462 from:
```sh
trap "rm -rf '$LOCK_DIR'" EXIT
```
to:
```sh
trap "rm -rf '$LOCK_DIR'; remote_cleanup" EXIT
```
- [ ] **Step 2: Add remote_setup call at end of backup_path()**
Change line 991-993 from:
```sh
echoRgb "$hx備份資料夾所使用分區統計如下↓\n -$(df -h "${Backup%/*}" | sed -n 's|% /.*|%|p' | awk '{print $(NF-3),$(NF-2),$(NF-1),$(NF)}' | awk 'END{print "總共:"$1"已用:"$2"剩餘:"$3"使用率:"$4}')檔案系統:$(df -T "$Backup_path" | sed -n 's|% /.*|%|p' | awk '{print $(NF-4)}')\n -備份目錄輸出位置↓\n -$Backup"
echoRgb "$outshow" "2"
}
```
to:
```sh
echoRgb "$hx備份資料夾所使用分區統計如下↓\n -$(df -h "${Backup%/*}" | sed -n 's|% /.*|%|p' | awk '{print $(NF-3),$(NF-2),$(NF-1),$(NF)}' | awk 'END{print "總共:"$1"已用:"$2"剩餘:"$3"使用率:"$4}')檔案系統:$(df -T "$Backup_path" | sed -n 's|% /.*|%|p' | awk '{print $(NF-4)}')\n -備份目錄輸出位置↓\n -$Backup"
echoRgb "$outshow" "2"
remote_setup
}
```
- [ ] **Step 3: Verify EXIT trap**
Run: Read `tools/tools.sh` line 462 to confirm the trap includes `remote_cleanup`.
- [ ] **Step 4: Verify hook**
Run: Read `tools/tools.sh` lines 989-994 to confirm `remote_setup` is inside `backup_path()` before the closing `}`.
- [ ] **Step 5: Commit**
```bash
git add tools/tools.sh
git commit -m "feat: wire remote backup hook and EXIT trap"
```
---
### Task 4: Verify changes
**Files:**
- Check: `tools/tools.sh`, `backup_settings.conf`
- [ ] **Step 1: Verify git diff**
```bash
git diff master
```
Expected: ~80 lines added across 2 files:
- `backup_settings.conf`: +9 lines (remote config)
- `tools/tools.sh`: +~70 lines (functions, trap modification, hook), 1 line modified (trap), 1 line added (hook call)
- [ ] **Step 2: Verify no syntax errors in shell script**
```bash
bash -n tools/tools.sh
```
Ignore "not found" warnings for Android-specific tools — we're only checking syntax.
- [ ] **Step 3: Verify function names don't collide**
```bash
Select-String -Pattern "^(mount_smb|umount_smb|upload_webdav|remote_setup|remote_cleanup)\b" -Path tools/tools.sh
```
Expected: Each function name appears once (definition only, not duplicated).
- [ ] **Step 4: Commit any fixes if needed**
```bash
git add -A && git commit -m "fix: address verification issues"
```

View File

@@ -1,136 +0,0 @@
# Remote Backup (WebDAV & SMB) 设计文档
**日期:** 2026-05-09
**目标:** 在不涉及大范围改动的前提下,为脚本增加备份到远程 WebDAV 和 SMB 的功能。
---
## 需求
- 支持直接备份到远程 WebDAV 和 SMB 设备
- 使用脚本内置 busybox不依赖额外工具
- 配置通过 `backup_settings.conf`
- 最小化对现有代码的改动
---
## 架构
### 配置
`backup_settings.conf` 末尾追加 4 个配置项:
```conf
#远程备份类型 (留空不启用)
#webdav 或 smb
remote_type=
#远程地址
#WebDAV例: http://192.168.1.100:8080/dav/
#SMB例: //192.168.1.100/backup
remote_url=
#远程认证用户名
remote_user=
#远程认证密码
remote_pass=
```
### 新增函数tools/tools.sh
所有函数定义在 `tools/tools.sh` 中,靠近 `kill_Serve()` 之后的位置(~line 465
| 函数 | 行数 | 说明 |
|------|------|------|
| `mount_smb()` | ~10 | busybox `mount -t cifs` 挂载 SMB 到 `$TMPDIR/smb_mount` |
| `umount_smb()` | ~5 | 安全卸载 SMB 挂载点 |
| `upload_webdav()` | ~20 | 遍历备份目录,用 busybox `wget --method PUT` 逐个上传 |
| `remote_setup()` | ~25 | 入口函数SMB 挂载后设置 Output_pathWebDAV 注册 trap |
| `remote_cleanup()` | ~15 | SMB 卸载 / WebDAV 上传+清理本地文件 |
### Hook 点
`backup_path()` 调用之后line 991 附近)插入一行:
```sh
remote_setup
```
### 流程
```
backup_path() # 确定本地备份路径 $Backup
remote_setup() # 如启用远程,挂载 SMB 或准备 WebDAV uploader
[SMB] [WebDAV]
mount_smb() 正常本地备份
$Backup → mount ↓
正常备份 upload_webdav() (at EXIT)
↓ rm -rf 本地副本
umount_smb()
```
### SMB 实现细节
```sh
mount_smb() {
local mnt="$TMPDIR/smb_mount"
mkdir -p "$mnt"
busybox mount -t cifs "$remote_url" "$mnt" -o "username=$remote_user,password=$remote_pass,iocharset=utf8"
# 若 busybox mount 不支持 cifs尝试系统 mount
# 失败则返回非0remote_setup 回退到本地备份
}
```
挂载成功后,`remote_setup()``$Backup` 覆盖为 `$mnt/Backup_${Compression_method}_$user`,后续备份流程无需修改。
### WebDAV 实现细节
```sh
upload_webdav() {
local base_url="${remote_url%/}"
# 创建远程目录 (MKCOL)
busybox wget -q --method MKCOL --header "Authorization: ..." "$base_url/..." 2>/dev/null
# 遍历上传
find "$Backup" -type f | while read f; do
busybox wget -q --method PUT --body-file="$f" --header "Authorization: ..." "$base_url/..."
# 上传成功才删除本地文件
done
}
```
备份先写到本地正常流程EXIT 时 `remote_cleanup()` 触发上传,成功后删除本地副本。
### 错误处理
- SMB 挂载失败回退到本地备份echo 警告,继续执行)
- WebDAV 上传失败:保留本地文件不删除(保留数据,用户手动排查)
- SMB 连接断开trap EXIT 确保 `umount_smb()` 被调用
- 网络异常wget 自带超时,不会永久阻塞
### 恢复支持(后续迭代)
本次仅实现备份到远程。恢复从远程可在后续 PR 中补充,方向是对称实现:
- SMB挂载后从挂载点恢复
- WebDAVwget 下载到临时目录后恢复
---
## 改动范围
| 文件 | 改动 |
|------|------|
| `backup_settings.conf` | +9 行配置 |
| `tools/tools.sh` | +75 行5 个函数 + 1 个 hook 调用) |
---
## 测试要点
1. SMB 挂载成功 → 备份文件出现在远程共享目录
2. SMB 挂载失败 → 回退到本地备份,有警告提示
3. WebDAV 上传成功 → 远程有备份文件,本地副本已清理
4. WebDAV 上传失败 → 本地文件保留,有错误提示
5. EXIT 时 SMB 挂载点被正确卸载(无残留)

View File

@@ -2,11 +2,8 @@
if [ -f "${0%/*}/tools/tools.sh" ]; then
MODDIR="${0%/*}"
conf_path="${0%/*}/backup_settings.conf"
[ ! -f "${0%/*}/backup_settings.conf" ] && . "${0%/*}/tools/tools.sh"
else
echo "${0%/*}/tools/tools.sh遺失"
exit 1
fi
mkdir -p "${0%/*}/log" 2>/dev/null
logfile="${0%/*}/log/log_$(date +%Y-%m-%d_%H-%M).txt"
. "${0%/*}/tools/tools.sh" | tee "$logfile"
sed -i "$(printf 's/\[[0-9;]*m//g')" "$logfile"
. "${0%/*}/tools/tools.sh" | tee "${0%/*}/log_$(date +%Y-%m-%d_%H-%M).txt"

View File

@@ -158,13 +158,6 @@
"2512BPNDAI" "Xiaomi 17 Ultra 印度版"
"25128PNA1C" "Xiaomi 17 Ultra by Leica 國行版 (衛星通訊)"
"25128PNA1G" "Leica Leitzphone powered by Xiaomi 國際版"
"2605EPN8EC" "Xiaomi 17 Max"
"M531DA" "Xiaomi 17T 國行版"
"2602DPT53G" "Xiaomi 17T 國際版"
"2602DPT53I" "Xiaomi 17T 印度版"
"M025EC" "Xiaomi 17T Pro 國行版"
"2602EPTC0G" "Xiaomi 17T Pro 國際版"
"2602EPTC0R" "Xiaomi 17T Pro 日本版 (無鎖)"
"2014616" "小米 Note 雙網通版"
"2014619" "小米 Note 全網通版"
"2014618" "小米 Note 移動合約版"
@@ -434,7 +427,6 @@
"2508CRN2BI" "REDMI 15C 5G 印度版"
"2508CRN2BR" "REDMI 15C 5G 日本版"
"25082RNC1C" "REDMI 15R 5G"
"2602BRNA4I" "REDMI 15A 5G 印度版"
"2014018" "紅米 Note 聯通 3G 標準版"
"2013121" "紅米 Note 聯通 3G 增強版"
"2014017" "紅米 Note 移動 3G 標準版"
@@ -600,7 +592,7 @@
"24117RN76E" "Redmi Note 14 歐洲版"
"24117RN76L" "Redmi Note 14 拉美版"
"24117RN76O" "Redmi Note 14 國際版 (NFC)"
"24094RAD4C" "Redmi Note 14 5G 國行版 / REDMI Note 15R Pro 國行版"
"24094RAD4C" "Redmi Note 14 5G 國行版"
"24094RAD4I" "Redmi Note 14 5G 印度版 / Redmi Note 14 SE 5G 印度版"
"24094RAD4G" "Redmi Note 14 5G 國際版"
"2502FRA65G" "Redmi Note 14S 國際版"
@@ -619,7 +611,6 @@
"25098RA98E" "REDMI Note 15 5G 歐洲版"
"25098RA98I" "REDMI Note 15 5G 印度版"
"25098RA98T" "REDMI Note 15 5G 沙特版"
"26022PCACI" "REDMI Note 15 SE 5G 印度版"
"25100RA69G" "REDMI Note 15 Pro 國際版"
"25080RABDC" "REDMI Note 15 Pro 國行版"
"25080RABDG" "REDMI Note 15 Pro 5G 國際版"
@@ -638,8 +629,7 @@
"24069RA21C" "Redmi Turbo 3"
"24129RT7CC" "REDMI Turbo 4"
"25053RT47C" "REDMI Turbo 4 Pro"
"2511FRT34C" "REDMI Turbo 5 國行版"
"2606FRT34I" "REDMI Turbo 5 印度版"
"2511FRT34C" "REDMI Turbo 5"
"2602BRT18C" "REDMI Turbo 5 Max"
"M1903F10A" "Redmi K20 全網通版"
"M1903F10C" "Redmi K20 運營商全網通版"
@@ -682,7 +672,6 @@
"2510DRK44C" "REDMI K90"
"25102RKBEC" "REDMI K90 Pro Max"
"25102RK69C" "REDMI K90 Pro Max 冠軍版"
"2604FRK1EC" "REDMI K90 Max"
"2016020" "紅米 Pro 標準版"
"2016021" "紅米 Pro 高配版 / 尊享版"
"M1803E6E" "紅米 S2 全網通版"
@@ -715,14 +704,8 @@
"25028RN03Y" "REDMI A5 國際版"
"25028RN03I" "REDMI A5 印度版"
"25028RN03L" "REDMI A5 拉美版"
"26020RNB4A" "REDMI A7 國際版"
"26020RNB4I" "REDMI A7 印度版"
"26020RNB4L" "REDMI A7 拉美版"
"25128RN17Y" "REDMI A7 Pro 國際版"
"25128RN17I" "REDMI A7 Pro 印度版"
"25128RN17L" "REDMI A7 Pro 拉美版"
"26020RN1AC" "REDMI R70 5G 國行版 / REDMI R70m 5G 國行版"
"26020RN1AI" "REDMI A7 Pro 5G 印度版"
"22081283C" "Redmi Pad 國行版"
"22081283G" "Redmi Pad 國際版"
"23073RPBFC" "Redmi Pad SE 國行版"
@@ -753,13 +736,7 @@
"2509BRP2DC" "REDMI Pad 2 Pro 5G 國行版"
"2509BRP2DG" "REDMI Pad 2 Pro 5G 國際版"
"2509BRP2DI" "REDMI Pad 2 Pro 5G 印度版"
"2603ARP14C" "REDMI Pad 2 SE 國行版"
"2603ARP14G" "REDMI Pad 2 9.7 國際版"
"2604ERP4DC" "REDMI Pad 2 SE 4G 國行版"
"2604ERP4DG" "REDMI Pad 2 9.7 4G 國際版"
"2604ERP4DI" "REDMI Pad 2 9.7 4G 印度版"
"25079RPDCC" "REDMI K Pad"
"26048RP6AC" "REDMI K Pad 2"
"M1805E10A" "POCO F1"
"M2004J11G" "POCO F2 Pro 國際版"
"M2012K11AG" "POCO F3 國際版"
@@ -831,8 +808,6 @@
"25062PC34E" "POCO M7 歐洲版"
"24108PCE2I" "POCO M7 5G 印度版"
"25057PC09I" "POCO M7 Plus 5G 印度版"
"26067PC09G" "POCO M8s 5G 國際版"
"26067PC09E" "POCO M8s 5G 歐洲版"
"2409FPCC4G" "POCO M7 Pro 5G 國際版"
"25118PC98G" "POCO M8 5G 國際版"
"25118PC98I" "POCO M8 5G 印度版"
@@ -856,10 +831,6 @@
"2410FPCC5G" "POCO C75 國際版"
"2410FPCC5I" "POCO C75 印度版"
"24116PCC1I" "POCO C75 5G 印度版"
"25128PC17Y" "POCO C81 Pro 國際版"
"25128PC17L" "POCO C81 Pro 拉美版"
"25128PC17I" "POCO C81 印度版"
"26020PCB4I" "POCO C81x 印度版"
"25078PC3EG" "POCO C85 國際版"
"25078PC3EE" "POCO C85 歐洲版"
"2508CPC2BI" "POCO C85 5G 印度版"
@@ -868,12 +839,9 @@
"24074PCD2I" "POCO Pad 5G 印度版"
"25099RP08G" "POCO Pad X1 國際版"
"2509ARPBDG" "POCO Pad M1 國際版"
"2603APC14G" "POCO Pad C1 國際版"
"FYJ01QP" "小米米家翻譯機"
"21051191C" "CyberDog 仿生四足機器人"
"2405AVPB7C" "小米澎湃智能座艙 (第一代 SU7/SU7 Ultra)"
"25033VP3FC" "小米澎湃智能座艙 (YU7/新一代 SU7)"
"2503CVPC6C" "小米汽車後排移動控制屏"
"2503CVPC6C" "Xiaomi YU7 後排移動控制屏"
"MI-ONE" "Mi 1 Lite"
"2109119BC" "Xiaomi Civi China"
"2109119BC" "Xiaomi Civi 1S China"
@@ -1534,7 +1502,6 @@
"SCG34" "Galaxy Z Fold7 Japan (au)"
"SC-56F" "Galaxy Z Fold7 Japan (NTT Docomo)"
"SM-F968B" "Galaxy Z TriFold Global"
"SM-F968U1" "Galaxy Z TriFold US Unlocked"
"SM-F968N" "Galaxy Z TriFold South Korea"
"SM-F700F" "Galaxy Z Flip Global"
"SM-F700U" "Galaxy Z Flip US Carrier"
@@ -2626,13 +2593,8 @@
"CPH2769" "一加 15R 歐洲版 / 國際版"
"CPH2771" "一加 15R 北美版"
"PLZ110" "一加 15T 國行版"
"PMB110" "一加 Ace 6 至尊版 國行版"
"PLU110" "一加 Turbo 6 國行版"
"CPH2793" "一加 Nord 6 印度版"
"CPH2795" "一加 Nord 6 國際版"
"PLY110" "一加 Turbo 6V 國行版"
"CPH2805" "一加 Nord CE 6 印度版"
"CPH2807" "一加 Nord CE 6 國際版"
"AC2001" "一加 Nord 印度版"
"AC2003" "一加 Nord 歐洲版 / 國際版"
"DN2101" "一加 Nord 2 印度版"
@@ -2658,7 +2620,6 @@
"CPH2621" "一加 Nord CE 4 Lite 歐洲版 / 國際版"
"CPH2717" "一加 Nord CE 5 印度版"
"CPH2719" "一加 Nord CE 5 國際版"
"CPH2943" "一加 Nord CE 6 Lite 印度版"
"BE2025" "一加 Nord N10 Metro 版"
"BE2026" "一加 Nord N10 北美版"
"BE2029" "一加 Nord N10 歐洲版 / 國際版"
@@ -2688,8 +2649,6 @@
"OPD2504" "一加平板 Go 2 Wi-Fi"
"OPD2505" "一加平板 Go 2 5G"
"OPD2508" "一加平板 2 (2025) 國行版"
"OPD2513" "一加平板 3 Pro 國行版"
"OPD2514" "一加平板 4 國際版"
"W101IN" "一加 Band 印度版"
"OPBBE221" "一加 Nord Watch"
"W301CN" "一加手表 國行版 / Cyberpunk 2077 限定版"
@@ -2701,7 +2660,6 @@
"OPWW251" "一加手表 3 國行版~"
"OPWWE251" "一加手表 3 國際版"
"OPWE242" "一加手表 3 43mm 國際版"
"OPWWE261" "一加手表 4 國際版"
"E6683" "Xperia Z5 dual"
"E6883" "Xperia Z5 Premium"
"F8332" "Xperia XZ"
@@ -2747,7 +2705,7 @@
"RMX3310" "真我 GT2"
"RMX3300" "真我 GT2 Pro"
"RMX3551" "真我 GT2 大師探索版"
"RMX3820" "真我 GT5"
"RMX3820" "真我 GT5 150W"
"RMX3823" "真我 GT5 240W"
"RMX3888" "真我 GT5 Pro"
"RMX3800" "真我 GT6"
@@ -2760,15 +2718,15 @@
"RMX3350" "真我 GT Neo 閃速版"
"RMX3370" "真我 GT Neo2 / 真我 GT Neo2 龍珠定制版"
"RMX3357" "真我 GT Neo2T"
"RMX3560" "真我 GT Neo3"
"RMX3560" "真我 GT Neo3 80W"
"RMX3562" "真我 GT Neo3 150W / 真我 GT Neo3 火影限定版"
"RMX3706" "真我 GT Neo5"
"RMX3706" "真我 GT Neo5 150W"
"RMX3708" "真我 GT Neo5 240W"
"RMX3700" "真我 GT Neo5 SE"
"RMX3852" "真我 GT Neo6 / 真我 GT Neo6 《完美世界》動畫雲曦限定禮盒"
"RMX3850" "真我 GT Neo6 SE"
"RMX5060" "真我 Neo7 / 真我 Neo7 《畫江湖之不良人》限定禮盒"
"RMX5062" "真我 Neo7 Turbo / 真我 Neo7 Turbo AI版"
"RMX5062" "真我 Neo7 Turbo"
"RMX5080" "真我 Neo7 SE"
"RMX5071" "真我 Neo7x"
"RMX8899" "真我 Neo8"
@@ -2863,9 +2821,9 @@
"RMX3491" "realme 9i India / Global"
"RMX3492" "realme 9i"
"RMX3493" "realme 9i EU"
"RMX3612" "realme 9i 5G"
"RMX3388" "realme 9 5G India"
"RMX3474" "realme 9 5G EU"
"RMX3612" "realme 9i 5G"
"RMX3471" "realme 9 Pro 5G India"
"RMX3472" "realme 9 Pro 5G Global"
"RMX3392" "realme 9 Pro+ 5G India"
@@ -2905,7 +2863,6 @@
"RMX5171" "realme 16 5G"
"RMX5120" "realme 16 Pro 5G"
"RMX5131" "realme 16 Pro+ 5G"
"RMX5268" "realme 16T 5G"
"RMX1921" "realme XT Global"
"RMX1922" "realme XT India"
"RMX1992" "realme X2 India"
@@ -2975,11 +2932,8 @@
"RMX5566" "realme C85"
"RMX5253" "realme C85 5G"
"RMX5555" "realme C85 Pro"
"RMX5258" "realme C100 5G"
"RMX5377" "realme C100i"
"RMX3938" "realme Note 60x"
"RMX5313" "realme Note 70 / realme Note 70T"
"RMX5388" "realme Note 80"
"RMX1833" "realme U1"
"RMX3870" "realme P1 5G"
"RMX3844" "realme P1 Pro 5G"
@@ -3133,10 +3087,8 @@
"V2430A" "vivo S20 Pro"
"V2464A" "vivo S30"
"V2465A" "vivo S30 Pro mini"
"V2528A" "vivo S50 / vivo S50t"
"V2528A" "vivo S50"
"V2527A" "vivo S50 Pro mini"
"V2571A" "vivo S60"
"V2572A" "vivo S60 元氣版"
"V1901A" "vivo Y3 全網通版"
"V1901T" "vivo Y3 移動全網通版"
"V1930A" "vivo Y3 標準版 全網通版"
@@ -3180,9 +3132,8 @@
"V2123A" "vivo Y53s (t2 版) 5G"
"V2045A" "vivo Y54s 5G"
"V2164A" "vivo Y55s 5G"
"V2559A" "vivo Y60 5G / vivo Y6t 5G"
"V2559A" "vivo Y60 5G"
"V2559BA" "vivo Y60m 5G"
"V2532BA" "vivo Y6m"
"V1731CA" "vivo Y71s"
"V2102A" "vivo Y71t 5G"
"V2164PA" "vivo Y73t 5G"
@@ -3217,9 +3168,7 @@
"V2452GA" "vivo Y300 GT"
"V2506A" "vivo Y500"
"V2516A" "vivo Y500 Pro"
"V2531A" "vivo Y500i / vivo Y500s / vivo Y6"
"V2561A" "vivo Y600 Pro"
"V2553A" "vivo Y600 Turbo"
"V2531A" "vivo Y500i / vivo Y500s"
"V2115A" "vivo T1"
"V2199GA" "vivo T2"
"V2188A" "vivo T2x"
@@ -3264,7 +3213,6 @@
"V2408A" "iQOO 13"
"V2505A" "iQOO 15"
"V2546A" "iQOO 15 Ultra"
"V2564A" "iQOO 15T"
"V1914A" "iQOO Neo 全網通版"
"V1914T" "iQOO Neo 移動全網通版"
"V1936A" "iQOO Neo 855 版 全網通版"
@@ -3386,7 +3334,6 @@
"V2515" "vivo X300"
"V2514" "vivo X300 Pro"
"V2542" "vivo X300 FE"
"V2562" "vivo X300 Ultra"
"vivo 1819" "vivo V15"
"vivo 1920" "vivo V17"
"vivo 1907" "vivo V17 Neo"
@@ -3427,17 +3374,17 @@
"V2418" "vivo V40 Lite 5G Indonesia"
"V2337" "vivo V40 SE 5G"
"V2451" "vivo V50"
"V2504" "vivo V50 Pro"
"V2428" "vivo V50e"
"V2441" "vivo V50 Lite"
"V2453" "vivo V50 Lite 5G"
"V2512" "vivo V60"
"V2549" "vivo V60 Lite"
"V2530" "vivo V60 Lite"
"V2529" "vivo V60 Lite 5G"
"V2513" "vivo V60e"
"V2540" "vivo V70"
"V2548" "vivo V70 Elite"
"V2558" "vivo V70 FE"
"V2637" "vivo V70 Lite 5G"
"vivo 1920_20" "vivo S1 Pro"
"V2168" "vivo T1"
"V2157" "vivo T1 5G"
@@ -3457,11 +3404,8 @@
"V2509" "vivo T4 Lite 5G"
"V2518" "vivo T4R 5G"
"V2510" "vivo T4 Pro"
"V2504" "vivo T4 Ultra"
"V2568" "vivo T5 Pro"
"V2545" "vivo T5x 5G"
"V2603" "vivo T5 Lite 5G"
"V2557" "vivo T5 Lite 44W 5G"
"vivo 2015_21" "vivo Y1s"
"V2044" "vivo Y3s"
"vivo 1902" "vivo Y5"
@@ -3539,8 +3483,7 @@
"V2036_21" "vivo Y31"
"V2521" "vivo Y31 5G"
"V2534" "vivo Y31 Pro 5G"
"V2636" "vivo Y31d"
"V2543" "vivo Y31d Pro"
"V2611" "vivo Y31d"
"V2533" "vivo Y31e 5G"
"V2057" "vivo Y33"
"V2109-EG" "vivo Y33A"
@@ -3553,7 +3496,7 @@
"V2447" "vivo Y39 5G"
"V2035" "vivo Y51"
"V2031_21" "vivo Y51s"
"V2613" "vivo Y51 Pro 5G"
"V2544" "vivo Y51 Pro 5G"
"V2053" "vivo Y52 5G"
"V2058" "vivo Y53s"
"V2154" "vivo Y55"
@@ -3623,19 +3566,12 @@
"I2505" "iQOO Z10R 5G Global"
"I2512" "iQOO Z11 5G"
"I2507" "iQOO Z11x 5G"
"I2510" "iQOO Z11 Lite 5G"
"I2515" "iQOO Z11 Lite 44W 5G"
"J2505" "JOVI X300 FE"
"J2510" "JOVI X300 Ultra"
"V2427" "JOVI V50"
"V2440" "JOVI V50 Lite 5G"
"J2507" "JOVI V70 5G"
"V2454" "JOVI Y19s"
"J2503" "JOVI Y21"
"J2508" "JOVI Y21 5G"
"V2445" "JOVI Y29"
"V2459" "JOVI Y29s 5G"
"J2506" "JOVI Y31"
"V2444" "JOVI Y39 5G"
"PAFM00" "OPPO Find X 標準版 全網通版"
"PAFT00" "OPPO Find X 標準版 移動版"
@@ -3731,8 +3667,6 @@
"PLW110" "OPPO Reno15"
"PLV110" "OPPO Reno15 Pro"
"PMD110" "OPPO Reno15c"
"PMM110" "OPPO Reno16"
"PMK110" "OPPO Reno16 Pro"
"PCLM10" "OPPO Reno Ace"
"PDHM00" "OPPO Ace2"
"PACM00" "OPPO R15 全網通版"
@@ -3791,7 +3725,6 @@
"PEHT00" "OPPO A93 移動版"
"PFGM00" "OPPO A93s"
"PELM00" "OPPO A95"
"PFUM10" "OPPO A96"
"PHA120" "OPPO A96"
"PFTM10" "OPPO A97"
"PHS110" "OPPO A1 5G (2023)"
@@ -3815,9 +3748,8 @@
"PLN110" "OPPO A6 Pro"
"PKW120" "OPPO A6i"
"PLT120" "OPPO A6s / OPPO A6i+ / OPPO A6k"
"PMT110" "OPPO A6s Pro"
"PLT130" "OPPO A6v"
"PLT140" "OPPO A6x / OPPO A6m"
"PLT140" "OPPO A6x"
"PMC110" "OPPO A6c"
"PBCM30" "OPPO K1"
"PCGM00" "OPPO K3 全網通版"
@@ -3840,8 +3772,6 @@
"PLD110" "OPPO K12s"
"PLM110" "OPPO K13 Turbo"
"PLE110" "OPPO K13 Turbo Pro"
"PMH110" "OPPO K15 Pro"
"PMG110" "OPPO K15 Pro+"
"OPD2101" "OPPO Pad"
"OPD2102" "OPPO Pad Air"
"OPD2201" "OPPO Pad 2"
@@ -3852,9 +3782,6 @@
"OPD2417" "OPPO Pad SE"
"OPD2506" "OPPO Pad 5"
"OPD2501" "OPPO Pad Air5"
"OPD2511" "OPPO Pad 5 Pro"
"OPD2515" "OPPO Pad Mini"
"OPD2601" "OPPO Pad 6"
"OB19O1" "OPPO Band 運動版"
"OB19O3" "OPPO Band 運動版 (國際版)"
"OB19O7" "OPPO Band 活力版"
@@ -3883,7 +3810,6 @@
"OWW251" "OPPO Watch X2"
"OWW262" "OPPO Watch S"
"OWW261" "OPPO Watch X3"
"OWW263" "OPPO Watch X3 Mini"
"CPH1875" "OPPO Find X"
"CPH2023" "OPPO Find X2"
"CPH2025" "OPPO Find X2 Pro"
@@ -3902,8 +3828,6 @@
"CPH2797" "OPPO Find X9"
"OPG07" "OPPO Find X9 (KDDI)"
"CPH2791" "OPPO Find X9 Pro"
"CPH2873" "OPPO Find X9s"
"CPH2841" "OPPO Find X9 Ultra"
"CPH2439" "OPPO Find N2"
"CPH2437" "OPPO Find N2 Flip"
"CPH2499" "OPPO Find N3"
@@ -3994,8 +3918,6 @@
"CPH2781" "OPPO F31 5G"
"CPH2763" "OPPO F31 Pro 5G"
"CPH2757" "OPPO F31 Pro+ 5G"
"CPH2777" "OPPO F33 5G"
"CPH2835" "OPPO F33 Pro 5G"
"CPH1835" "OPPO R15"
"CPH1833" "OPPO R15 Pro"
"CPH1879" "OPPO R17"
@@ -4029,10 +3951,8 @@
"CPH2831" "OPPO A6 5G Global"
"CPH2785" "OPPO A6 5G India"
"CPH2799" "OPPO A6 Pro"
"CPH2827" "OPPO A6 Pro 5G India"
"CPH2815" "OPPO A6s"
"CPH2789" "OPPO A6s 5G Global"
"CPH2889" "OPPO A6s 5G India"
"CPH2789" "OPPO A6s 5G"
"CPH2819" "OPPO A6x"
"CPH2783" "OPPO A6x 5G Global"
"CPH2823" "OPPO A6x 5G India"
@@ -4040,7 +3960,6 @@
"CPH2853" "OPPO A6t 5G"
"CPH2849" "OPPO A6t Pro"
"CPH2851" "OPPO A6t Pro 5G"
"CPH2895" "OPPO A6c"
"CPH1905" "OPPO A7"
"CPH1903" "OPPO AX7"
"CPH1938" "OPPO A9"
@@ -4109,8 +4028,6 @@
"OPD2303" "OPPO Pad Neo LTE"
"OPD2406" "OPPO Pad 3"
"OPD2402" "OPPO Pad 3 Pro"
"OPD2502" "OPPO Pad 5 Wi-Fi"
"OPD2503" "OPPO Pad 5 5G"
"OPD2419" "OPPO Pad SE Wi-Fi"
"OPD2420" "OPPO Pad SE LTE"
"OBBE215" "OPPO Band 2 Japan"
@@ -4120,6 +4037,3 @@
"OW19W12" "OPPO Watch 46mm LTE"
"OWWE231" "OPPO Watch X"
"OWWE251" "OPPO Watch X2"
"OWWE242" "OPPO Watch X2 Mini"
"OWWE261" "OPPO Watch X3"
"OWWE262" "OPPO Watch S"

BIN
tools/bc Normal file

Binary file not shown.

Binary file not shown.

98
tools/core/app_utils.sh Normal file
View File

@@ -0,0 +1,98 @@
#!/system/bin/sh
# module: app_utils.sh
Process_Information() {
dumpsys activity processes | awk -v key="$1" -v user="$user" 'function getUserFromUid(uid){return int(uid/100000)} /^ *user #[0-9]+ uid=/ {if($0 ~ /ISOLATED uid=[0-9]+/){uid="";pid="";pkg="";next} if(pkg!="" && uid!="" && pid!=""){if((key=="" || pkg==key) && (user=="" || getUserFromUid(uid)==user)){print pid}} uid="";pid="";pkg=""; if($0 ~ /uid=/ && uid==""){tmp=$0; sub(/^.*uid=/,"",tmp); sub(/ .*/,"",tmp); uid=tmp}} /packageList=\{/ {tmp=$0; sub(/^.*packageList=\{/,"",tmp); sub(/\}.*/,"",tmp); pkg=tmp} /pid=/ {tmp=$0; sub(/^.*pid=/,"",tmp); sub(/ .*/,"",tmp); pid=tmp} END {if(pkg!="" && uid!="" && pid!=""){if((key=="" || pkg==key) && (user=="" || getUserFromUid(uid)==user)){print pid}}}'
}
Background_application_list() {
[[ $activity != false ]] && {
if [[ $Background_apps_ignore = true || $1 = debug ]]; then
unset Backstage
#獲取後台
Backstage="$(dumpsys activity activities | awk -v uid="$user" '/ActivityRecord\{/{split($4,a,"/"); user=$3; pkg=a[1]; if(user~/^u[0-9]+$/ && pkg!~/\//){sub(/^u/,"",user); if(uid=="" || user==uid) if(!seen[user","pkg]++) print pkg}}')"
if [[ $Backstage = "" ]]; then
Backstage="$(am stack list | awk -v uid="$user" '/taskId/&&!/unknown/{split($2,a,"/"); pkg=a[1]; user="unknown"; for(i=1;i<=NF;i++) if($i~/^userId=/){split($i,b,"="); user=b[2]; break} if(uid==""|user==uid) if(!seen[pkg]++) print pkg}')"
[[ $Backstage = "" ]] && {
echoRgb "獲取當前後台應用失敗" "0" && unset Backstage
}
fi
fi
}
}
kill_app() {
process_Information="$(Process_Information "$name2")"
if [[ $name2 != bin.mt.plus && $name2 != com.termux && $name2 != bin.mt.plus.canary ]]; then
if [[ $process_Information != "" ]]; then
am force-stop --user "$user" "$name2" &>/dev/null
echo "$process_Information" | xargs -r kill -9
pkill -9 -f "$name2$|$name2[:/_]"
#killall -9 "$name2" &>/dev/null
#am kill "$name2" &>/dev/null
echoRgb "殺死$name1進程"
fi
fi
}
disable_verify() {
#禁用apk驗證
settings put global verifier_verify_adb_installs 0 2>/dev/null
#禁用安裝包驗證
settings put global package_verifier_enable 0 2>/dev/null
#未知來源
settings put secure install_non_market_apps 1 2>/dev/null
#關閉play安全校驗
if [[ $(settings get global package_verifier_user_consent 2>/dev/null) != -1 ]]; then
settings put global package_verifier_user_consent -1 2>/dev/null
settings put global upload_apk_enable 0 2>/dev/null
echoRgb "PLAY安全驗證為開啟狀態已被腳本關閉防止apk安裝失敗" "3"
fi
# 設定檔案路徑
FILE="/data/data/com.android.vending/shared_prefs/finsky.xml"
if [[ -f $FILE ]]; then
# 提取當前的 auto_update_enabled 值
CURRENT_VALUE="$(sed -n '/<boolean name="auto_update_enabled" /s/.*value="\([^"]*\)".*/\1/p' "$FILE")"
if [[ $CURRENT_VALUE = true ]]; then
sed -i '/<boolean name="auto_update_enabled" /s/value="true"/value="false"/' "$FILE"
[[ $(sed -n '/<boolean name="auto_update_enabled" /s/.*value="\([^"]*\)".*/\1/p' "$FILE") = false ]] && echoRgb "play自動更新已關閉" "3"
echoRgb "殺死 Google Play 商店..."
am force-stop com.android.vending
else
if [[ $CURRENT_VALUE = "" ]]; then
sed -i '/<\/map>/i \ <boolean name="auto_update_enabled" value="false" />' "$FILE"
[[ $(sed -n '/<boolean name="auto_update_enabled" /s/.*value="\([^"]*\)".*/\1/p' "$FILE") = false ]] && echoRgb "auto_update_enabled已插入false,play自動更新已關閉" "3"
echoRgb "殺死 Google Play 商店..."
am force-stop com.android.vending
else
[[ $CURRENT_VALUE != false ]] && echoRgb "無法識別play auto_update_enabled當前$CURRENT_VALUE值" "0"
fi
fi
fi
}
restore_permissions () {
echoRgb "恢復權限"
appops reset --user "$user" "$name2" &>/dev/null
true_permissions="$(jq -r 'to_entries[] | select(.value.permissions != null) | .value.permissions | to_entries | map(select(.value | startswith("true")) | .key) | join(" ")' "$app_details")"
false_permissions="$(jq -r 'to_entries[] | select(.value.permissions != null) | .value.permissions | to_entries | map(select(.value | startswith("false")) | .key) | join(" ")' "$app_details")"
Set_Ops_permissions="$(jq -r '.[] | select(.permissions != null).permissions | to_entries | map(.value | split(" ")) | map(select(.[1] != "-1")) | map(.[1:]) | flatten | join(" ")' "$app_details")"
[[ $true_permissions != "" ]] && {
Set_true_Permissions "$name2" "$true_permissions"
[[ $? != 0 ]] && echo_log "設置允許權限"
}
[[ $false_permissions != "" ]] && {
Set_false_Permissions "$name2" "$false_permissions"
[[ $? != 0 ]] && echo_log "設置拒絕權限"
}
[[ $Set_Ops_permissions != "" ]] && {
Set_Ops "$name2" "$Set_Ops_permissions"
[[ $? != 0 ]] && echo_log "設置ops權限"
}
}
#分區佔用信息
partition_info() {
unset Skip
Occupation_status="$(df -B1 "${1%/*}" | sed -n 's|% /.*|%|p' | awk '{print $(NF-1)}')"
Filesize2="$(size "$Filesize")"
echo " -$2大小:$Filesize2 剩餘大小:$(size "$Occupation_status")"
[[ $Filesize != "" ]] && [[ $(echo "$Filesize > $Occupation_status" | bc) -eq 1 ]] && echoRgb "$2備份大小將超出rom可用大小" "0" && Skip=1
Occupation_status="$(df -h "${Backup%/*}" | sed -n 's|% /.*|%|p' | awk '{print $(NF-1),$(NF)}')"
}

848
tools/core/backup_core.sh Normal file
View File

@@ -0,0 +1,848 @@
#!/system/bin/sh
# module: backup_core.sh
backup_path() {
if [[ $Output_path != "" ]]; then
[[ ${Output_path: -1} = / ]] && Output_path="${Output_path%?}"
if [[ ${Output_path:0:1} != / ]]; then
Directory_type="相對路徑"
Backup="$MODDIR/$Output_path/Backup_${Compression_method}_$user"
else
Directory_type="絕對路徑"
Backup="$Output_path/Backup_${Compression_method}_$user"
fi
outshow="使用自定義目錄($Directory_type)"
else
Backup="$MODDIR/Backup_${Compression_method}_$user"
if [[ ! -f ${0%/*}/app_details.json ]]; then
outshow="使用當前路徑作為備份目錄"
else
[[ -d $Backup ]] && outshow="使用上層路徑作為備份目錄" || echoRgb "$Backup目錄不存在" "0"
fi
fi
PU=$(mount | awk '$3 ~ "/mnt/media_rw/[^/]+$" {print $3, $5}' | egrep -v "$mount_point")
OTGPATH="$(echo "$PU" | cut -d' ' -f1)"
OTGFormat="$(echo "$PU" | cut -d' ' -f2)"
if [[ -d $OTGPATH ]]; then
if [[ $(echo "$MODDIR" | egrep -o "^${OTGPATH}") != "" ]]; then
hx="true"
Backup="$MODDIR/Backup_${Compression_method}_$user"
else
case $Lo in
0|1)
echoRgb "檢測到隨身碟 是否在隨身碟備份\n -音量上是,音量下不是" "2"
get_version "選擇了隨身碟備份" "選擇了本地備份" ;;
2)
Enter_options "檢測到隨身碟輸入1使用隨身碟備份 0本地備份" "選擇了隨身碟備份" "本地備份" && isBoolean "$parameter" "branch" && branch="$nsx" ;;
esac
[[ $branch = true ]] && hx="$branch"
[[ $hx = true ]] && Backup="$OTGPATH/Backup_${Compression_method}_$user"
fi
if [[ $hx = true ]]; then
if [[ $OTGFormat = vfat ]]; then
echoRgb "隨身碟檔案系統$OTGFormat不支持超過單檔4GB\n -請格式化為exfat" "0"
exit 1
fi
outshow="於隨身碟備份" && hx=usb
fi
fi
[[ ! -d $Backup ]] && mkdir -p "$Backup"
#分區詳細
if [[ $(echo "$Backup" | egrep -o "^/storage/emulated") != "" ]]; then
Backup_path="/data"
else
Backup_path="${Backup%/*}"
fi
echoRgb "$hx備份資料夾所使用分區統計如下↓\n -$(df -h "${Backup%/*}" | sed -n 's|% /.*|%|p' | awk '{print $(NF-3),$(NF-2),$(NF-1),$(NF)}' | awk 'END{print "總共:"$1"已用:"$2"剩餘:"$3"使用率:"$4}')檔案系統:$(df -T "$Backup_path" | sed -n 's|% /.*|%|p' | awk '{print $(NF-4)}')\n -備份目錄輸出位置↓\n -$Backup"
echoRgb "$outshow" "2"
}
Calculate_size() {
#計算出備份大小跟差異性
filesizee="$(find "$1" -type f -printf "%s\n" | awk '{s+=$1} END {print s}')"
if [[ $(echo "$filesizee > $filesize" | bc) -eq 1 ]]; then
NJL="本次備份增加 $(size "$(echo "scale=2; $filesizee - $filesize" | bc)")"
elif [[ $(echo "$filesizee < $filesize" | bc) -eq 1 ]]; then
NJL="本次備份減少 $(size "$(echo "scale=2; $filesize - $filesizee" | bc)")"
else
NJL="文件大小未改變"
fi
echoRgb "備份資料夾路徑↓↓↓\n -$1"
echoRgb "備份資料夾總體大小$(size "$filesizee")"
echoRgb "$NJL"
}
Backup_apk() {
#檢測apk狀態進行備份
#創建APP備份文件夾
[[ ! -d $Backup_folder ]] && mkdir -p "$Backup_folder"
[[ ! -f $app_details ]] && echo "{\n}">"$app_details"
apk_version="$(jq -r '.[] | select(.apk_version != null).apk_version' "$app_details")"
apk_version2="$(pm list packages --show-versioncode --user "$user" "$name2" 2>/dev/null | cut -f3 -d ':' | head -n 1)"
if [[ $apk_version = $apk_version2 ]]; then
[[ $(echo "$txt2" | sed -e '/^$/d' | cut -d' ' -f2 | awk -v pkg="$name2" '$1 == pkg {print $1}') = "" ]] && txt2="$txt2\n${Backup_folder##*/} $name2"
unset xb
osj=$((osj + 1))
result=0
echoRgb "Apk版本無更新 跳過備份" "2"
else
if [[ $nobackup = false ]]; then
if [[ $apk_version != "" ]]; then
osn=$((osn + 1))
update_apk="$(echo "$name1 \"$name2\"")"
update_apk2="$(echo "$update_apk\n$update_apk2")"
echoRgb "版本:$apk_version>$apk_version2"
else
osk=$((osk + 1))
add_app="$(echo "$name1 \"$name2\"")"
add_app2="$(echo "$add_app\n$add_app2")"
echoRgb "版本:$apk_version2"
fi
unset Filesize
Filesize="$(find "$apk_path2" -type f -printf "%s\n" | awk '{s+=$1} END {print s}')"
rm -rf "$Backup_folder/apk.tar"*
partition_info "$Backup" "$name1 apk"
if [[ $Skip != 1 ]]; then
#備份apk
echoRgb "$1"
echo "$apk_path" | sed -e '/^$/d' | while read; do
echoRgb "${REPLY##*/} $(size "$REPLY")"
done
(
cd "$apk_path2"
case $Compression_method in
tar | TAR | Tar) tar --checkpoint-action="ttyout=%T\r" -cf "$Backup_folder/apk.tar" *.apk ;;
zstd | Zstd | ZSTD) tar --checkpoint-action="ttyout=%T\r" -cf - *.apk | zstd --ultra -3 -T0 -q --priority=rt >"$Backup_folder/apk.tar.zst" ;;
esac
)
echo_log "備份$apk_number個Apk"
if [[ $result = 0 ]]; then
Validation_file "$Backup_folder/apk.tar"*
if [[ $result = 0 ]]; then
[[ $(echo "$txt2" | sed -e '/^$/d' | cut -d' ' -f2 | awk -v pkg="$name2" '$1 == pkg {print $1}') = "" ]] && txt2="$txt2\n${Backup_folder##*/} $name2"
[[ $apk_version != "" ]] && {
echoRgb "覆蓋app_details"
jq --arg apk_version "$apk_version2" --arg software "$name1" '.[$software].apk_version = $apk_version' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
} || {
echoRgb "新增app_details"
extra_content="{
\"$name1\": {
\"PackageName\": \"$name2\",
\"apk_version\": \"$apk_version2\"
}
}"
jq --argjson new_content "$extra_content" '. += $new_content' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
}
else
rm -rf "$Backup_folder"
fi
if [[ $name2 = com.android.chrome ]]; then
#刪除所有舊apk ,保留一個最新apk進行備份
ReservedNum=1
FileNum="$(ls /data/app/*/com.google.android.trichromelibrary_*/base.apk 2>/dev/null | wc -l)"
while [[ $FileNum -gt $ReservedNum ]]; do
OldFile="$(ls -rt /data/app/*/com.google.android.trichromelibrary_*/base.apk 2>/dev/null | head -1)"
rm -rf "${OldFile%/*/*}" && echo "刪除文件:${OldFile%/*/*}"
FileNum=$((FileNum - 1))
done
[[ -f $(ls /data/app/*/com.google.android.trichromelibrary_*/base.apk 2>/dev/null) && $(ls /data/app/*/com.google.android.trichromelibrary_*/base.apk 2>/dev/null | wc -l) = 1 ]] && cp -r "$(ls /data/app/*/com.google.android.trichromelibrary_*/base.apk 2>/dev/null)" "$Backup_folder/nmsl.apk"
fi
else
rm -rf "$Backup_folder"
fi
fi
else
osj=$((osj + 1))
rm -rf "$Backup_folder"
fi
fi
[[ $name2 = bin.mt.plus && ! -f $Backup/$name1.apk ]] && cp -r "$apk_path" "$Backup/$name1.apk"
}
Backup_ssaid() {
Ssaid="$(jq -r '.[] | select(.Ssaid != null).Ssaid' "$app_details")"
ssaid="$(awk -v pkg="$name2" '$1 == pkg {print $2}'<<<"$ssaid_info")"
[[ $ssaid != null && $ssaid != "" ]] && echoRgb "SSAID:$ssaid"
if [[ $ssaid != null && $ssaid != $Ssaid ]]; then
echoRgb "備份ssaid"
echoRgb "$Ssaid>$ssaid"
SSAID_apk="$(echo "$name1 \"$name2\"")"
SSAID_apk2="$(echo "$SSAID_apk\n$SSAID_apk2")"
jq --arg entry "$name1" --arg new_value "$ssaid" '.[$entry].Ssaid |= $new_value' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
echo_log "備份ssaid"
fi
[[ $ssaid = null ]] && ssaid=
}
Backup_Permissions() {
get_Permissions="$(jq -r '.[] | select(.permissions != null).permissions' "$app_details")"
Get_Permissions="$(get_Permissions "$name2" | jq -nR '[inputs | select(. != "null" and length>0) | split(" ") | {(.[0]): (.[1:] | join(" "))}] | if length > 0 then add else empty end')"
if [[ $Get_Permissions != "" && ($Get_Permissions = *true* || $Get_Permissions = *false*) ]]; then
if [[ $get_Permissions = "" ]]; then
echoRgb "備份權限"
jq --arg packageName "$name1" --argjson permissions "$Get_Permissions" '.[$packageName].permissions |= $permissions' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
echo_log "備份權限"
else
if [[ $get_Permissions != "" && ($get_Permissions = *true* || $get_Permissions = *false*) ]]; then
if [[ $get_Permissions != $Get_Permissions ]]; then
echoRgb "權限變更"
jq -n --argjson old "$get_Permissions" --argjson new "$Get_Permissions" '$new | to_entries | map(select(.key as $k | $old[$k] != null and $old[$k] != .value)) | .[].key' | sed 's/^/ /'
jq --arg packageName "$name1" --argjson permissions "$Get_Permissions" '.[$packageName] |= . + {permissions: $permissions}' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
echo_log "備份權限"
fi
fi
fi
else
[[ $Get_Permissions != "" ]] && echoRgb "備份權限失敗$(get_Permissions "$name2")" "0"
fi
}
#檢測數據位置進行備份
Backup_data() {
data_path="$path/$1/$name2"
MODDIR_NAME="${data_path%/*}"
MODDIR_NAME="${MODDIR_NAME##*/}"
[[ -f $app_details ]] && Size="$(jq -r --arg entry "$1" '.[$entry] | select(.Size != null).Size' "$app_details" 2>/dev/null)"
case $1 in
user) data_path="$path2/$name2" ;;
user_de) data_path="$path3/$name2" ;;
data|obb) ;;
*)
data_path="$2"
if [[ $1 != thanox ]]; then
Compression_method1="$Compression_method"
Compression_method=tar
fi
zsize=1
zmediapath=1
;;
esac
if [[ -d $data_path ]]; then
unset Filesize ssaid Get_Permissions result Permissions
Filesize="$(find "$data_path" -type f -printf "%s\n" 2>/dev/null | awk '{s+=$1} END {print s}')"
[[ $Filesize != "" ]] && {
if [[ $Size != $Filesize ]]; then
case $1 in
user)
if [[ $(su "$(pm list packages -U --user "$user" </dev/null | awk -v pkg="$name2" -F'[ :]' '$2 == pkg {print $4}')" -c keystore_cli_v2 list | wc -l) -ge 2 ]]; then
echoRgb "$name1包含keystore 恢復可能閃退" "0"
jq --arg entry "$name1" '.[$entry].keystore |= "true"' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
else
jq --arg entry "$name1" '.[$entry].keystore |= "false"' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
fi
Backup_ssaid
Backup_Permissions ;;
esac
#停止應用
case $1 in
user|data|obb|user_de) kill_app ;;
esac
rm -rf "$Backup_folder/$1.tar"*
partition_info "$Backup" "$1"
if [[ $Skip != 1 ]]; then
echoRgb "備份$1數據"
# 判斷是否超過指定大小
if [[ $Filesize2 != *"bytes"* ]]; then
if [[ $Filesize2 = *"KB"* ]]; then
if [[ $(echo "${Filesize2% KB}" | bc) > 1 ]]; then
Start_backup="true"
else
Start_backup="false"
fi
else
Start_backup="true"
fi
else
Start_backup="false"
fi
if [[ $Start_backup = true ]]; then
case $1 in
user|user_de)
case $Compression_method in
tar | Tar | TAR) tar --checkpoint-action="ttyout=%T\r" --exclude="${data_path##*/}/.ota" --exclude="${data_path##*/}/cache" --exclude="${data_path##*/}/lib" --exclude="${data_path##*/}/code_cache" --exclude="${data_path##*/}/no_backup" --warning=no-file-changed -cpf "$Backup_folder/$1.tar" -C "${data_path%/*}" "${data_path##*/}" 2>/dev/null ;;
zstd | Zstd | ZSTD) tar --checkpoint-action="ttyout=%T\r" --exclude="${data_path##*/}/.ota" --exclude="${data_path##*/}/cache" --exclude="${data_path##*/}/lib" --exclude="${data_path##*/}/code_cache" --exclude="${data_path##*/}/no_backup" --warning=no-file-changed -cpf - -C "${data_path%/*}" "${data_path##*/}" | zstd --ultra -3 -T0 -q --priority=rt >"$Backup_folder/$1.tar.zst" 2>/dev/null ;;
esac
;;
*)
case $Compression_method in
tar | Tar | TAR) tar --checkpoint-action="ttyout=%T\r" --exclude="Backup_"* --exclude="${data_path##*/}/cache" --exclude="${data_path##*/}/QQ" --exclude="${data_path##*/}/Telegram" --exclude="${data_path##*/}"/.* --warning=no-file-changed -cpf "$Backup_folder/$1.tar" -C "${data_path%/*}" "${data_path##*/}" ;;
zstd | Zstd | ZSTD) tar --checkpoint-action="ttyout=%T\r" --exclude="Backup_"* --exclude="${data_path##*/}/cache" --exclude="${data_path##*/}/QQ" --exclude="${data_path##*/}/Telegram" --exclude="${data_path##*/}"/.* --warning=no-file-changed -cpf - -C "${data_path%/*}" "${data_path##*/}" | zstd --ultra -3 -T0 -q --priority=rt >"$Backup_folder/$1.tar.zst" ;;
esac
;;
esac
echo_log "備份$1數據"
else
echoRgb "$1數據 $Filesize2太小" "0" && result=1
fi
if [[ $result = 0 ]]; then
Validation_file "$Backup_folder/$1.tar"*
if [[ $result = 0 ]]; then
if [[ ! $Filesize -eq 0 ]]; then
size2="$(stat -c %s "$Backup_folder/$1.tar"*)"
rate="$(echo "scale=2; (1 - ($size2 / $Filesize)) * 100" | bc)"
echoRgb "壓縮率${rate}% 大小$(size "$size2")"
fi
[[ ${Backup_folder##*/} = Media ]] && [[ $(sed -e '/^$/d' "$mediatxt" | grep -w "${REPLY##*/}.tar$" | head -1) = "" ]] && echo "$FILE_NAME" >> "$mediatxt"
if [[ $zsize != "" ]]; then
extra_content="{
\"$1\": {
\"path\": \"$2\",
\"Size\": \"$Filesize\"
},
\"Backup time\": {
\"date\": \"$(date "+%Y.%m.%d %H:%M:%S")\"
}
}"
jq --argjson new_content "$extra_content" '. += $new_content' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
else
extra_content="{
\"$1\": {
\"Size\": \"$Filesize\"
},
\"Backup time\": {
\"date\": \"$(date "+%Y.%m.%d %H:%M:%S")\"
}
}"
jq --argjson new_content "$extra_content" '. += $new_content' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
fi
else
rm -rf "$Backup_folder/$1".tar.*
fi
fi
[[ $Compression_method1 != "" ]] && Compression_method="$Compression_method1"
unset Compression_method1
fi
else
[[ $Size != "" ]] && echoRgb "$1數據無發生變化 跳過備份" "2"
fi
}
else
[[ -f $data_path ]] && echoRgb "$1是一個文件 不支持備份" "0"
fi
}
# ===================== 子函數: 驗證備份參數 =====================
validate_backup_params() {
self_test
case $MODDIR in
/storage/emulated/0/Android/* | /data/media/0/Android/* | /sdcard/Android/*) echoRgb "請勿在$MODDIR內備份" "0" && exit 2 ;;
esac
case $Compression_method in
zstd | Zstd | ZSTD | tar | Tar | TAR) ;;
*) echoRgb "$Compression_method為不支持的壓縮算法" "0" && exit 2 ;;
esac
#校驗選填是否正確
case $Lo in
0)
[[ $Backup_Mode != "" ]] && isBoolean "$Backup_Mode" "Backup_Mode" && Backup_Mode="$nsx" || {
echoRgb "選擇備份模式\n -音量上備份應用+數據,音量下僅應用不包含數據" "2"
get_version "應用+數據" "僅應用" && Backup_Mode="$branch"
}
if [[ $Backup_Mode = true ]]; then
if [[ $(echo "$blacklist" | egrep -v '#|' | wc -l) -gt 0 ]]; then
if [[ $blacklist_mode != "" ]]; then
isBoolean "$blacklist_mode" "blacklist_mode" && blacklist_mode="$nsx"
else
echoRgb "選擇黑名單模式\n -音量上不備份,音量下僅備份安裝檔\n -警告! " "2"
get_version "不備份" "備份安裝檔" && blacklist_mode="$branch"
fi
fi
fi
if [[ $Backup_Mode = true ]]; then
[[ $Backup_obb_data != "" ]] && isBoolean "$Backup_obb_data" "Backup_obb_data" && Backup_obb_data="$nsx" || {
echoRgb "是否備份外部數據 即比如原神的數據包\n -音量上備份,音量下不備份" "2"
get_version "備份" "不備份" && Backup_obb_data="$branch"
}
[[ $Backup_user_data != "" ]] && isBoolean "$Backup_user_data" "Backup_user_data" && Backup_user_data="$nsx" || {
echoRgb "是否備份使用者數據\n -音量上備份,音量下不備份" "2"
get_version "備份" "不備份" && Backup_user_data="$branch"
}
else
Backup_user_data="false"
Backup_obb_data="false"
fi
[[ $backup_media != "" ]] && isBoolean "$backup_media" "backup_media" && backup_media="$nsx" || {
echoRgb "全部應用備份結束後是否備份自定義目錄\n -音量上備份,音量下不備份" "2"
get_version "備份" "不備份" && backup_media="$branch"
}
[[ $setDisplayPowerMode != "" ]] && isBoolean "$setDisplayPowerMode" "setDisplayPowerMode" && setDisplayPowerMode="$nsx" || {
echoRgb "應用備份開始後關閉螢幕\n -音量上關閉,音量下不關閉" "2"
get_version "關閉" "不關閉" && setDisplayPowerMode="$branch"
}
[[ $Background_apps_ignore != "" ]] && isBoolean "$Background_apps_ignore" "Background_apps_ignore" && Background_apps_ignore="$nsx" || {
echoRgb "存在進程忽略備份\n -音量上忽略,音量下備份" "2"
get_version "忽略" "備份" && Background_apps_ignore="$branch"
} ;;
1)
[[ $Backup_Mode = "" ]] && {
echoRgb "選擇備份模式\n -音量上備份應用+數據,音量下僅應用不包含數據" "2"
get_version "應用+數據" "僅應用" && Backup_Mode="$branch"
} || isBoolean "$Backup_Mode" "Backup_Mode" && Backup_Mode="$nsx"
if [[ $Backup_Mode = true ]]; then
if [[ $(echo "$blacklist" | egrep -v '#|' | wc -l) -gt 0 ]]; then
[[ $blacklist_mode = "" ]] && {
echoRgb "選擇黑名單模式\n -音量上不備份,音量下僅備份安裝檔" "2"
get_version "不備份" "備份安裝檔" && blacklist_mode="$branch"
} || isBoolean "$blacklist_mode" "blacklist_mode" && blacklist_mode="$nsx"
fi
[[ $Backup_obb_data = "" ]] && {
echoRgb "是否備份外部數據 即比如原神的數據包\n -音量上備份,音量下不備份" "2"
get_version "備份" "不備份" && Backup_obb_data="$branch"
} || isBoolean "$Backup_obb_data" "Backup_obb_data" && Backup_obb_data="$nsx"
[[ $Backup_user_data = "" ]] && {
echoRgb "是否備份使用者數據\n -音量上備份,音量下不備份" "2"
get_version "備份" "不備份" && Backup_user_data="$branch"
} || isBoolean "$Backup_user_data" "Backup_user_data" && Backup_user_data="$nsx"
fi
[[ $backup_media = "" ]] && {
echoRgb "全部應用備份結束後是否備份自定義目錄\n -音量上備份,音量下不備份" "2"
get_version "備份" "不備份" && backup_media="$branch"
} || isBoolean "$backup_media" "backup_media" && backup_media="$nsx"
[[ $setDisplayPowerMode = "" ]] && {
echoRgb "應用備份開始後關閉螢幕\n -音量上關閉,音量下不關閉" "2"
get_version "關閉" "不關閉" && setDisplayPowerMode="$branch"
} || isBoolean "$setDisplayPowerMode" "setDisplayPowerMode" && setDisplayPowerMode="$nsx"
[[ $Background_apps_ignore = "" ]] && {
echoRgb "存在進程忽略備份\n -音量上忽略,音量下備份" "2"
get_version "忽略" "備份" && Background_apps_ignore="$branch"
} || isBoolean "$Background_apps_ignore" "Background_apps_ignore" && Background_apps_ignore="$nsx"
;;
2)
[[ $Backup_Mode = "" ]] && {
Enter_options "輸入1備份應用+數據輸入0僅應用不包含數據" "應用+數據" "僅應用" && isBoolean "$parameter" "Backup_Mode" && Backup_Mode="$nsx"
} || {
isBoolean "$Backup_Mode" "Backup_Mode" && Backup_Mode="$nsx"
}
if [[ $Backup_Mode = true ]]; then
[[ $(echo "$blacklist" | egrep -v '#|' | wc -l) -gt 0 ]] && {
[[ $blacklist_mode = "" ]] && {
Enter_options "選擇黑名單模式輸入1不備份輸入0備份安裝檔" "不備份" "僅應用安裝檔" && isBoolean "$parameter" "blacklist_mode" && blacklist_mode="$nsx"
} || {
isBoolean "$blacklist_mode" "blacklist_mode" && blacklist_mode="$nsx"
}
}
[[ $Backup_obb_data = "" ]] && {
Enter_options "是否備份外部數據 即比如原神的數據包\n -輸入1備份輸入0不備份" "備份" "不備份" && isBoolean "$parameter" "Backup_obb_data" && Backup_obb_data="$nsx"
} || {
isBoolean "$Backup_obb_data" "Backup_obb_data" && Backup_obb_data="$nsx"
}
[[ $Backup_user_data = "" ]] && {
Enter_options "是否備份使用者數據輸入1備份輸入0不備份" "備份" "不備份" && isBoolean "$parameter" "Backup_user_data" && Backup_user_data="$nsx"
} || {
isBoolean "$Backup_user_data" "Backup_user_data" && Backup_user_data="$nsx"
}
fi
[[ $backup_media = "" ]] && {
Enter_options "全部應用備份結束後是否備份自定義目錄\n -輸入1備份0不備份" "備份" "不備份" && isBoolean "$parameter" "backup_media" && backup_media="$nsx"
} || {
isBoolean "$backup_media" "backup_media" && backup_media="$nsx"
}
[[ $setDisplayPowerMode = "" ]] && {
Enter_options "應用備份開始後關閉螢幕\n -輸入1關閉0不關閉" "關閉" "不關閉" && isBoolean "$parameter" "setDisplayPowerMode" && setDisplayPowerMode="$nsx"
} || {
isBoolean "$setDisplayPowerMode" "setDisplayPowerMode" && setDisplayPowerMode="$nsx"
}
[[ $Background_apps_ignore = "" ]] && {
Enter_options "存在進程忽略備份\n -輸入1不備份0備份" "忽略" "備份" && isBoolean "$parameter" "Background_apps_ignore" && Background_apps_ignore="$nsx"
} || {
isBoolean "$Background_apps_ignore" "Background_apps_ignore" && Background_apps_ignore="$nsx"
} ;;
*) echoRgb "$conf_path Lo=$Lo填寫錯誤正確值0 1 2" "0" && exit 2 ;;
esac
}
# ===================== 子函數: 準備應用列表 =====================
prepare_app_list() {
i=1
#數據目錄
if [[ $list_location != "" ]]; then
if [[ ${list_location:0:1} = / ]]; then
txt="$list_location"
else
txt="$MODDIR/$list_location"
fi
else
txt="$MODDIR/appList.txt"
fi
txt="${txt/'/storage/emulated/'/'/data/media/'}"
txt_path="$txt"
[[ ! -f $txt ]] && echoRgb "請執行start.sh獲取應用列表再來備份" "0" && exit 1
TXT_NAME="${txt##*/}"
case ${TXT_NAME##*.} in
txt) ;;
*) echoRgb "$txt不是腳本讀取格式" "0" && exit 2 ;;
esac
sort -u "$txt" -o "$txt" &>/dev/null
data="$MODDIR"
hx="本地"
echoRgb "腳本受到內核機制影響 息屏後IO性能嚴重影響\n -請勿關閉終端或是息屏備份 如需終止腳本\n -請執行start.sh選擇終止腳本即可停止" "3"
backup_path
echoRgb "配置詳細:\n -壓縮方式:$Compression_method\n -音量鍵確認:$Lo\n -更新:$update\n -備份模式:$Backup_Mode\n -備份外部數據:$Backup_obb_data\n -備份user數據:$Backup_user_data\n -自定義目錄備份:$backup_media\n -存在進程忽略備份:$Background_apps_ignore\n -關閉螢幕:$setDisplayPowerMode"
D="1"
Apk_info="$(pm list packages --user "$user" | cut -f2 -d ':' | egrep -v 'ice.message|com.topjohnwu.magisk' | sort -u)"
if [[ $Apk_info != "" ]]; then
[[ $Apk_info = *"Failure calling service package"* ]] && Apk_info="$(appinfo "user|system" "pkgName" 2>/dev/null | egrep -v 'ice.message|com.topjohnwu.magisk' | sort -u)"
else
Apk_info="$(appinfo "user|system" "pkgName" 2>/dev/null | egrep -v 'ice.message|com.topjohnwu.magisk' | sort -u)"
fi
[[ $Apk_info = "" ]] && echoRgb "Apk_info變量為空" "0" && exit
[[ ! -f ${0%/*}/app_details.json ]] && {
echoRgb "檢查備份列表中是否存在已經卸載應用" "3"
while read -r ; do
if [[ $(echo "$REPLY" | sed -E 's/^[ \t]*//; /^[ \t]*[#!]/d') != "" ]]; then
app=($REPLY $REPLY)
if [[ ${app[1]} != "" && ${app[2]} != "" ]]; then
if [[ $(echo "$Apk_info" | awk -v pkg="${app[1]}" '$1 == pkg {print $1}') != "" ]]; then
[[ $Tmplist = "" ]] && Tmplist='#不需要備份的應用請在開頭使用#注釋 比如:#酷安 com.coolapk.market忽略安裝包和數據\n#不需要備份數據的應用請在開頭使用!注釋 比如:!酷安 com.coolapk.market僅忽略數據'
Tmplist="$Tmplist\n$REPLY"
else
echoRgb "$REPLY不存在系統,從列表中刪除" "0"
fi
fi
else
Tmplist="$Tmplist\n$REPLY"
fi
done < "$txt"
}
[[ $Update_backup = true ]] && {
echoRgb "檢查備份列表中已經更新應用" "3"
while read apk; do
Backup_folder="$Backup/$(echo "$apk" | cut -d':' -f1)"
app_details="$Backup_folder/app_details.json"
if [[ -d $Backup_folder ]]; then
apk_version="$(jq -r '.[] | select(.apk_version != null).apk_version' "$app_details")"
apk_version2="$(pm list packages --show-versioncode --user "$user" "$(echo "$apk" | cut -d':' -f2)" </dev/null | cut -f3 -d ':' | head -n 1)"
[[ $apk_version != $apk_version2 ]] && {
[[ $Tmplist2 = "" ]] && Tmplist2="${apk/:/ }" || Tmplist2="$Tmplist2\n${apk/:/ }"
}
fi
done<<<"$(grep -Ev '^[#!]' "$txt" | awk '{print $1 ":" $2}')"
}
[[ $Tmplist != "" ]] && echo "$Tmplist" | sed -e '/^$/d' | sort>"$txt"
if [[ $Tmplist2 != "" ]]; then
txt="$(echo "$Tmplist2" | sort)"
else
[[ $Update_backup != "" ]] && echoRgb "應用目前無更新" "0" && exit 0
fi
if [[ ! -f $txt ]]; then
[[ $(echo "$txt") != "" ]] && txt="$(echo "$txt" | sed -e '/^$/d')"
else
txt="$(egrep -v '#|' "$txt" | sed -e '/^$/d')"
fi
r="$(echo "$txt" | awk 'NF != 0 { count++ } END { print count }')"
[[ -f ${0%/*}/app_details.json ]] && r=1
[[ $r = "" && ! -f ${0%/*}/app_details.json ]] && echoRgb "$MODDIR_NAME/appList.txt是空的或是包名被注釋備份個鬼\n -檢查是否注釋亦或者執行$MODDIR_NAME/start.sh" "0" && exit 1
}
# ===================== 子函數: 初始化備份存儲 =====================
init_backup_storage() {
if [[ $Backup_Mode = true ]]; then
[[ $Backup_user_data = false ]] && echoRgb "當前$MODDIR_NAME/backup_settings.conf的\n -Backup_user_data=0將不備份user數據" "0"
[[ $Backup_obb_data = false ]] && echoRgb "當前$MODDIR_NAME/backup_settings.conf的\n -Backup_obb_data=0將不備份外部數據" "0"
fi
[[ $backup_media = false ]] && echoRgb "當前$MODDIR_NAME/backup_settings.conf的\n -backup_media=0將不備份自定義資料夾" "0"
txt2="$Backup/appList.txt"
txt2="${txt2/'/storage/emulated/'/'/data/media/'}"
txt_path2="$txt2"
[[ ! -f $txt2 ]] && echo "#不需要恢復還原的應用請在開頭使用#注釋 比如:#酷安 com.coolapk.market">"$txt2"
txt2="$(cat "$txt2")"
[[ ! -d $Backup/tools ]] && cp -r "$tools_path" "$Backup"
[[ ! -f $Backup/start.sh ]] && touch_shell "2" "$Backup/start.sh"
[[ ! -f $Backup/restore_settings.conf ]] && update_Restore_settings_conf>"$Backup/restore_settings.conf"
if [[ -d $Backup/tools ]]; then
find "$Backup/tools" -maxdepth 1 -type f | while read; do
Tools_FILE_NAME="${REPLY##*/}"
if [[ -f $tools_path/$Tools_FILE_NAME ]]; then
filesha256="$(sha256sum "$tools_path/$Tools_FILE_NAME" 2>/dev/null | cut -d" " -f1)"
filesha256_1="$(sha256sum "$REPLY" 2>/dev/null | cut -d" " -f1)"
if [[ $filesha256 != $filesha256_1 ]]; then
cp -r "$tools_path/$Tools_FILE_NAME" "$REPLY"
echoRgb "更新$REPLY"
fi
fi
done
fi
filesize="$(find "$Backup" -type f -printf "%s\n" | awk '{s+=$1} END {print s}')"
Quantity=0
#開始循環$txt內的資料進行備份
#記錄開始時間
en=118 # ANSI 256色起始編號用於通知欄圖標顏色
osn=0; osj=0; osk=0
#獲取已經開啟的無障礙
var="$(settings get secure enabled_accessibility_services 2>/dev/null)"
#獲取預設鍵盤
keyboard="$(settings get secure default_input_method 2>/dev/null)"
Set_screen_pause_seconds on
[[ $txt != "" ]] && [[ $(echo "$txt" | cut -d' ' -f2 | grep -w "^${keyboard%/*}$") != ${keyboard%/*} ]] && unset keyboard
if [[ -f ${0%/*}/app_details.json ]]; then
ssaid_info="$(get_ssaid "$(jq -r '.[] | select(.PackageName != null).PackageName' "${0%/*}/app_details.json")")"
else
ssaid_info="$(get_ssaid "$(echo "$txt" | awk '{printf "%s ", $2}')")"
fi
starttime1="$(date -u "+%s")"
TIME="$starttime1"
notification "101" "開始備份"
}
# ===================== 子函數: 應用循環備份 =====================
backup_applications_loop() {
while [[ $i -le $r ]]; do
[[ $en -ge 229 ]] && en=118 # 超過229重置避免顏色越界
unset name1 name2 apk_path apk_path2
if [[ ! -f ${0%/*}/app_details.json ]]; then
name1="$(echo "$txt" | sed -n "${i}p" | cut -d' ' -f1)"
name2="$(echo "$txt" | sed -n "${i}p" | cut -d' ' -f2)"
else
ChineseName="$(jq -r 'to_entries[] | select(.key != null).key' "${0%/*}/app_details.json" | head -n 1)"
PackageName="$(jq -r '.[] | select(.PackageName != null).PackageName' "${0%/*}/app_details.json")"
name1="$ChineseName"
name2="$PackageName"
fi
[[ $name2 = "" || $name1 = "" ]] && echoRgb "警告! appList.txt應用包名獲取失敗可能修改有問題" "0" && exit 1
apk_path="$(pm path --user "$user" "$name2" 2>/dev/null | cut -f2 -d ':')"
apk_path2="$(echo "$apk_path" | head -1)"
apk_path2="${apk_path2%/*}"
if [[ -d $apk_path2 ]]; then
echoRgb "備份第$i/$r個應用 剩下$((r - i))" "3"
echoRgb "備份 $name1" "2"
notification "101" "備份第$i/$r個應用 剩下$((r - i))
備份 $name1"
unset Backup_folder ChineseName PackageName nobackup No_backupdata result apk_version apk_version2 zsize zmediapath Size data_path Ssaid ssaid Permissions
nobackup="false"
Background_application_list
[[ $Backstage != "" && $(echo "$Backstage" | egrep -w "^$name2$") != "" ]] && echoRgb "$name1存在後台 忽略備份" "0" && nobackup="true"
if [[ $Backup_Mode = true ]]; then
if [[ $name1 = !* || $name1 = * ]]; then
name1="$(echo "$name1" | sed 's/!//g ; s///g')"
echoRgb "跳過備份所有數據" "0"
No_backupdata=1
fi
if [[ $(echo "$blacklist" | grep -w "^$name2$") = $name2 ]]; then
if [[ $blacklist_mode = true ]]; then
echoRgb "黑名單應用跳過備份" "0"
nobackup="true"
else
echoRgb "黑名單應用跳過備份所有數據" "0"
fi
No_backupdata=1
fi
fi
Backup_folder="$Backup/$name1"
app_details="$Backup_folder/app_details.json"
if [[ -f $app_details ]]; then
PackageName="$(jq -r '.[] | select(.PackageName != null).PackageName' "$app_details")"
[[ $PackageName != $name2 ]] && jq --arg name2 "$name2" 'walk(if type == "object" and .PackageName then .PackageName = $name2 else . end)' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
echoRgb "上次備份時間$(jq -r --arg entry "Backup time" '.[$entry] | select(.date != null).date' "$app_details" 2>/dev/null)"
fi
[[ $hx = USB && $PT = "" ]] && echoRgb "隨身碟意外斷開 請檢查穩定性" "0" && exit 1
starttime2="$(date -u "+%s")"
[[ $name2 = com.tencent.mobileqq ]] && echoRgb "QQ可能恢復備份失敗或是丟失聊天記錄請自行用你信賴的應用備份" "0"
[[ $name2 = com.tencent.mm ]] && echoRgb "WX可能恢復備份失敗或是丟失聊天記錄請自行用你信賴的應用備份" "0"
apk_number="$(echo "$apk_path" | wc -l)"
if [[ $nobackup != true ]]; then
if [[ $apk_number = 1 ]]; then
Backup_apk "非Split Apk" "3"
else
Backup_apk "Split Apk支持備份" "3"
fi
if [[ $result = 0 && $No_backupdata = "" ]]; then
if [[ $Backup_Mode = true ]]; then
if [[ $Backup_obb_data = true ]]; then
if [[ $name2 != bin.mt.plus ]]; then
#備份data數據
[[ $name1 = Nekogram ]] && rm -rf /data/media/0/Android/data/tw.nekomimi.nekogram/files/Telegram/Telegram\ {Video,Stories,Documents,Images}/{*,.*} 2>/dev/null
Backup_data "data"
#備份obb數據
Backup_data "obb"
else
echoRgb "$name1無法備份" "0"
fi
fi
#備份user數據
[[ $name2 != bin.mt.plus ]] && {
[[ $Backup_user_data = true ]] && {
Backup_data "user"
Backup_data "user_de"
}
}
[[ $name2 = github.tornaco.android.thanos ]] && Backup_data "thanox" "$(find "/data/system" -name "thanos"* -maxdepth 1 -type d 2>/dev/null)"
fi
fi
[[ -f $Backup_folder/${name2}.sh ]] && rm -rf "$Backup_folder/${name2}.sh"
[[ ! -f $Backup_folder/recover.sh ]] && touch_shell "3" "$Backup_folder/recover.sh"
[[ ! -f $Backup_folder/backup.sh ]] && touch_shell "1" "$Backup_folder/backup.sh"
fi
endtime 2 "$name1 備份" "3"
lxj="$(echo "$Occupation_status" | awk '{print $3}' | sed 's/%//g')"
echoRgb "完成$((i * 100 / r))% $hx$(echo "$Occupation_status" | awk 'END{print "剩餘:"$1"使用率:"$2}')" "3"
rgb_d="$rgb_a"
rgb_a=188 # ANSI 256色: 淺灰色分隔線
echoRgb "_________________$(endtime 1 "已經")___________________"
rgb_a="$rgb_d"
else
echoRgb "$name1[$name2] 不在安裝列表,備份個寂寞?" "0"
fi
if [[ $i = $r ]]; then
endtime 1 "應用備份" "3"
#設置無障礙開關
if [[ $var != "" ]]; then
if [[ $var != null ]]; then
settings put secure enabled_accessibility_services "$var" &>/dev/null
echo_log "設置無障礙"
settings put secure accessibility_enabled 1 &>/dev/null
echo_log "打開無障礙開關"
fi
fi
#設置鍵盤
if [[ $keyboard != "" ]]; then
ime enable "$keyboard" &>/dev/null
ime set "$keyboard" &>/dev/null
settings put secure default_input_method "$keyboard" &>/dev/null
echo_log "設置鍵盤$(appinfo2 "${keyboard%/*}" 2>/dev/null)"
fi
update_apk2="${update_apk2:="暫無更新"}"
add_app2="${add_app2:="暫無更新"}"
echoRgb "\n -已更新的apk=\"$osn\"\n -已新增的備份=\"$osk\"\n -apk版本號無變化=\"$osj\"\n -下列為版本號已變更的應用\n$update_apk2\n -新增的備份....\n$add_app2\n -包含SSAID的應用\n$SSAID_apk2" "3"
notification "101" "app備份完成 $(endtime 1 "應用備份" "3")"
[[ $txt2 != "" ]] && {
echo "$txt2" | sort | sed '/^$/d'>"$txt_path2"
}
if [[ $backup_media = true && ! -f ${0%/*}/app_details.json ]]; then
A=1
B="$(echo "$Custom_path" | egrep -v '#|' | awk 'NF != 0 { count++ } END { print count }')"
if [[ $B != "" ]]; then
echoRgb "備份結束,備份多媒體" "1"
notification "102" "Media備份開始"
starttime1="$(date -u "+%s")"
Backup_folder="$Backup/Media"
[[ ! -f $Backup/start.sh ]] && touch_shell "2" "$Backup/start.sh"
[[ ! -d $Backup_folder ]] && mkdir -p "$Backup_folder"
app_details="$Backup_folder/app_details.json"
[[ ! -f $app_details ]] && echo "{\n}">"$app_details"
mediatxt="$Backup/mediaList.txt"
[[ ! -f $mediatxt ]] && echo "#不需要恢復的資料夾請在開頭使用#注釋 比如:#Download" > "$mediatxt"
echo "$Custom_path" | sed -e '/^#/d; /^$/d; s/\/$//' > "$TMPDIR/custom_paths"
while read; do
echoRgb "備份第$A/$B個資料夾 剩下$((B - A))" "3"
notification "102" "備份第$A/$B個資料夾 剩下$((B - A))"
starttime2="$(date -u "+%s")"
if [[ ${REPLY##*/} = adb ]]; then
if [[ $ksu != ksu ]]; then
echoRgb "Magisk adb"
Backup_data "${REPLY##*/}" "$REPLY"
else
echoRgb "KernelSU adb不支持備份" "0"
Set_back_0
fi
else
Backup_data "${REPLY##*/}" "$REPLY"
fi
endtime 2 "${REPLY##*/}備份" "1"
echoRgb "完成$((A * 100 / B))% $hx$(echo "$Occupation_status" | awk 'END{print "剩餘:"$1"使用率:"$2}')" "2"
rgb_d="$rgb_a"
rgb_a=188 # ANSI 256色: 淺灰色分隔線
echoRgb "_________________$(endtime 1 "已經")___________________"
rgb_a="$rgb_d" && A=$((A + 1))
done < "$TMPDIR/custom_paths"
echoRgb "目錄↓↓↓\n -$Backup_folder"
notification "102" "Media備份完成 $(endtime 1 "自定義備份")"
endtime 1 "自定義備份"
else
echoRgb "自定義路徑為空 無法備份" "0"
fi
fi
fi
i=$((i + 1)); en=$((en + 1)); nskg=$((nskg + 1))
done
}
# ===================== 子函數: 清理與統計 =====================
finalize_backup() {
backup_wifi "$Backup/wifi"
Set_screen_pause_seconds off
[[ $user != 0 ]] && am stop-user "$user"
Calculate_size "$Backup"
echoRgb "批量備份完成"
echoRgb "備份結束時間$(date +"%Y-%m-%d %H:%M:%S")"
starttime1="$TIME"
endtime 1 "批量備份開始到結束"
notification "105" "備份完成 $(endtime 1 "批量備份開始到結束")"
[[ -f $txt_path ]] && chown "$(stat -c '%u:%g' '/data/media/0/Download')" "$txt_path"
[[ -f $txt_path2 ]] && chown "$(stat -c '%u:%g' '/data/media/0/Download')" "$txt_path2"
exit 0
}
# ===================== 重構後的 backup() 入口 =====================
backup() {
validate_backup_params
prepare_app_list
init_backup_storage
backup_applications_loop
finalize_backup
}
backup_update_apk() {
Update_backup='true'
backup
}
backup_media() {
self_test
backup_path
echoRgb "假設反悔了要終止腳本請儘速離開此腳本點擊start.sh選擇終止腳本,否則腳本將繼續執行直到結束" "0"
A=1
B="$(echo "$Custom_path" | egrep -v '#|' | awk 'NF != 0 { count++ } END { print count }')"
if [[ $B != "" ]]; then
starttime1="$(date -u "+%s")"
Backup_folder="$Backup/Media"
[[ ! -d $Backup_folder ]] && mkdir -p "$Backup_folder"
[[ ! -f $Backup/start.sh ]] && touch_shell "2" "$Backup/start.sh"
[[ ! -d $Backup/tools ]] && cp -r "$tools_path" "$Backup"
[[ ! -f $Backup/restore_settings.conf ]] && update_Restore_settings_conf>"$Backup/restore_settings.conf"
app_details="$Backup_folder/app_details.json"
[[ ! -f $app_details ]] && echo "{\n}">"$app_details"
filesize="$(find "$Backup_folder" -type f -printf "%s\n" 2>/dev/null | awk '{s+=$1} END {print s}')"
mediatxt="$Backup/mediaList.txt"
[[ ! -f $mediatxt ]] && echo "#不需要恢復的資料夾請在開頭使用#注釋 比如:#Download" > "$mediatxt"
Set_screen_pause_seconds on
notification "109" "Media備份開始"
echo "$Custom_path" | sed -e '/^#/d; /^$/d; s/\/$//' > "$TMPDIR/custom_paths"
while read; do
echoRgb "備份第$A/$B個資料夾 剩下$((B - A))" "3"
starttime2="$(date -u "+%s")"
if [[ ${REPLY##*/} = adb ]]; then
if [[ $ksu != ksu ]]; then
echoRgb "Magisk adb"
Backup_data "${REPLY##*/}" "$REPLY"
fi
else
Backup_data "${REPLY##*/}" "$REPLY"
fi
endtime 2 "${REPLY##*/}備份" "1"
echoRgb "完成$((A * 100 / B))% $hx$(echo "$Occupation_status" | awk 'END{print "剩餘:"$1"使用率:"$2}')" "2" && echoRgb "____________________________________" && A=$((A + 1))
done < "$TMPDIR/custom_paths"
Calculate_size "$Backup_folder"
Set_screen_pause_seconds off
endtime 1 "自定義備份"
notification "109" "Media備份完成 $(endtime 1 "自定義備份")"
else
echoRgb "自定義路徑為空 無法備份" "0"
fi
}
Set_screen_pause_seconds () {
if [[ $1 = on ]]; then
#獲取系統設置的無操作息屏秒數
if [[ $Get_dark_screen_seconds = "" ]]; then
Get_dark_screen_seconds="$(settings get system screen_off_timeout)"
#設置30分鐘後息屏
settings put system screen_off_timeout 1800000
echo_log "設置無操作息屏時間30分鐘"
fi
[[ $setDisplayPowerMode = true ]] && {
setDisplay 0
echo_log "設置螢幕狀態false"
}
elif [[ $1 = off ]]; then
if [[ $Get_dark_screen_seconds != "" ]]; then
settings put system screen_off_timeout "$Get_dark_screen_seconds"
echo_log "設置無操作息屏時間為$Get_dark_screen_seconds"
input keyevent 224
fi
[[ $setDisplayPowerMode = true ]] && {
setDisplay 2
echo_log "設置螢幕狀態true"
}
fi
}

172
tools/core/base_utils.sh Normal file
View File

@@ -0,0 +1,172 @@
#!/system/bin/sh
# module: base_utils.sh
echoRgb() {
#轉換echo顏色提高可讀性
if [[ $2 = 0 ]]; then
echo -e "\e[38;5;197m -!$1\e[0m"
elif [[ $2 = 1 ]]; then
echo -e "\e[38;5;121m -$1\e[0m"
elif [[ $2 = 2 ]]; then
echo -e "\e[38;5;${rgb_c}m -$1\e[0m"
elif [[ $2 = 3 ]]; then
echo -e "\e[38;5;${rgb_b}m -$1\e[0m"
else
echo -e "\e[38;5;${rgb_a}m -$1\e[0m"
fi
}
Set_back_0() {
return 0
}
Set_back_1() {
return 1
}
endtime() {
#計算總體切換時長耗費
case $1 in
1) starttime="${starttime1:-$(date -u "+%s")}" ;;
2) starttime="${starttime2:-$(date -u "+%s")}" ;;
*) starttime="$(date -u "+%s")" ;;
esac
endtime="$(date -u "+%s")"
duration="$(echo $((endtime - starttime)) | awk '{t=split("60 秒 60 分 24 時 999 天",a);for(n=1;n<t;n+=2){if($1==0)break;s=$1%a[n]a[n+1]s;$1=int($1/a[n])}print s}')"
[[ $duration != "" ]] && echo " -$2用時:$duration" || echo " -$2用時:0秒"
}
get_version() {
while :; do
keycheck
case $? in
42)
[[ $Select_user = true ]] && branch="$1" || branch=true
echoRgb "$1" "1"
;;
41)
[[ $Select_user = true ]] && branch="$2" || branch=false
echoRgb "$2" "0"
;;
*)
echoRgb "keycheck錯誤" "0"
continue
;;
esac
sleep 0.3
break
done
}
isBoolean() {
nsx="$1"
if [[ $1 = 1 ]]; then
nsx=true
elif [[ $1 = 0 ]]; then
nsx=false
else
echoRgb "$conf_path $2=$1填寫錯誤正確值1or0" "0"
Set_back_1
return 1
fi
}
echo_log() {
if [[ $? = 0 ]]; then
[[ $2 = "" ]] && echoRgb "$1成功" "1"
result=0
Set_back_0
else
echoRgb "$1失敗,過世了" "0"
notification "$RANDOM" "$name1: $1失敗,過世"
result=1
Set_back_1
fi
}
kill_Serve() {
LOCK_FILE="/tmp/backup_script_$$.lock"
if [[ -f $LOCK_FILE ]]; then
OLD_PID="$(cat "$LOCK_FILE")"
if kill -0 "$OLD_PID" 2>/dev/null; then
echo "發現先前的備份程序 (PID=$OLD_PID),將其終止"
kill -KILL "$OLD_PID"
echo "結束自身,避免重複執行"
exit 1
else
echo "發現 lock 檔但程序已不存在,視為殘留 lock"
fi
fi
echo $$ > "$LOCK_FILE"
trap "rm -f '$LOCK_FILE'" EXIT
}
Show_boottime() {
awk -F '.' '{run_days=$1 / 86400;run_hour=($1 % 86400)/3600;run_minute=($1 % 3600)/60;run_second=$1 % 60;printf("%d天%d時%d分%d秒",run_days,run_hour,run_minute,run_second)}' /proc/uptime 2>/dev/null
}
Enter_options() {
echoRgb "$1" "2"
unset option parameter
while true ;do
if [[ $option != "" ]]; then
case $option in
0|1)
parameter="$option"
[[ $option = 1 ]] && echoRgb "$2" "2" || echoRgb "$3" "2"
break ;;
*)
echoRgb "$option參數錯誤 只能是0或1" "0"
read option ;;
esac
else
read option
fi
done
}
add_entry() {
app_name="$1"
package_name="$2"
# 檢查是否已經存在同樣的應用名稱
if [[ $(echo "$3" | cut -d' ' -f1 | grep -w "^$app_name$") = $app_name ]]; then
if [[ $(echo "$3" | cut -d' ' -f2 | grep -w "^$package_name$") != $package_name ]]; then
# 如果應用名稱存在但包名不同,則需要添加數字後綴
count=1
new_app_name="${app_name}_${count}"
while echo "$3" | grep -q "$new_app_name"; do
count=$((count + 1))
new_app_name="${app_name}_${count}"
done
app_name="$new_app_name"
fi
fi
REPLY="$app_name $package_name"
}
size() {
local b_size get_size
varr="$(echo "$1" | bc 2>/dev/null)"
if [[ $varr != $1 ]]; then
b_size="$(ls -l "$1" 2>/dev/null | awk '{print $5}')"
else
b_size="$1"
fi
if [[ $b_size -eq 0 ]]; then
get_size="0 bytes"
elif [[ $(echo "$b_size < 1024" | bc) -eq 1 ]]; then
get_size="${b_size} bytes"
elif [[ $(echo "$b_size < 1048576" | bc) -eq 1 ]]; then
get_size="$(echo "scale=2; $b_size / 1024" | bc) KB"
elif [[ $(echo "$b_size < 1073741824" | bc) -eq 1 ]]; then
get_size="$(echo "scale=2; $b_size / 1048576" | bc) MB"
else
get_size="$(echo "scale=2; $b_size / 1073741824" | bc) GB"
fi
echo "$get_size"
}
self_test() {
if [[ $(dumpsys deviceidle get charging) = false && $(dumpsys battery | awk '/level/{print $2}' | egrep -o '[0-9]+') -le 15 ]]; then
echoRgb "電量$(dumpsys battery | awk '/level/{print $2}' | egrep -o '[0-9]+')%太低且未充電\n -為防止備份檔案或是恢復因低電量強制關機導致檔案損毀\n -請連接充電器後備份" "0" && exit 2
fi
}
Validation_file() {
MODDIR_NAME="${1%/*}"
MODDIR_NAME="${MODDIR_NAME##*/}"
FILE_NAME="${1##*/}"
echoRgb "校驗$FILE_NAME"
case ${FILE_NAME##*.} in
zst) zstd -t "$1" 2>/dev/null ;;
tar) tar -tf "$1" &>/dev/null ;;
esac
echo_log "${FILE_NAME##*.}校驗"
}

171
tools/core/config_mgmt.sh Normal file
View File

@@ -0,0 +1,171 @@
#!/system/bin/sh
# module: config_mgmt.sh
update_backup_settings_conf() {
echo "#0關閉音量鍵選擇 (如選項未設置,則強制使用音量鍵選擇)
#1開啟音量鍵選擇 (如選項已設置,則跳過該選項提示)
#2使用鍵盤輸入適用於無音量鍵可用設備選擇 (如選項未設置,則強制使用鍵盤輸入)
Lo="${Lo:-0}"
#後台執行腳本
0不能關閉當前終端有壓縮速率
1終端有可能完全無顯示但是log會持續刷新可直接完全關閉終端
background_execution="${background_execution:-0}"
#腳本語言設置 留空則自動識別系統語言環境並翻譯
#1簡體中文 0繁體中文
Shell_LANG="$Shell_LANG"
#備份開始後偽裝亮屏
#1開啟 0關閉
setDisplayPowerMode="${setDisplayPowerMode:-0}"
#自定義備份文件輸出位置 支持相對路徑(留空則默認當前路徑)
Output_path=\""$Output_path"\"
#自定義applist.txt位置 支持相對路徑(留空則默認當前路徑)
list_location=\""$list_location"\"
#自動更新腳本(留空強制選擇)
#1開啟 0關閉
update="${update:-1}"
#自動更新的cdn節點針對國內用戶使用無牆或是使用VPN請設置0
#0 直鏈下載
#1 https://ghfast.top
#2 https://shrill-pond-3e81.hunsh.workers.dev
cdn=${cdn:-1}
#自定義屏蔽外部掛載點 例OTG 虛擬SD等 多個掛載點請使用 | 區隔
#屏蔽後不會提示音量鍵選擇不影響Output_path指定外置存儲位置
mount_point=\""${mount_point:-rannki|0000-1}"\"
#使用者(如0 999等用戶如存在多個用戶留空強制選擇無多個用戶則默認用戶0不詢問)
user="$user"
#備份模式
#1包含數據+安裝包0僅包安裝包
#此選項設置1時Backup_obb_dataBackup_user_datablacklist_mode將可設置 0時Backup_user_dataBackup_obb_datablacklist_mode選項不生效
#此外設置0時將同時忽略appList.txt的!與任何黑名單設置(包括黑名單列表)
Backup_Mode="${Backup_Mode:-1}"
#是否備份使用者數據 (1備份 0不備份 留空強制選擇)
Backup_user_data="${Backup_user_data:-1}"
#是否備份外部數據 例:原神的數據包(1備份 0不備份 留空強制選擇)
Backup_obb_data="${Backup_obb_data:-1}"
#是否在應用數據備份完成後備份自定義目錄
#1開啟 0關閉
backup_media="${backup_media:-0}"
#存在進程忽略備份(1忽略0備份)
Background_apps_ignore="${Background_apps_ignore:-0}"
#添加自定義備份路徑 例Download DCIM等文件夾 請使用絕對路徑,請勿刪除\"\"
Custom_path=\""${Custom_path:-
/storage/emulated/0/Pictures/
/storage/emulated/0/Download/
/storage/emulated/0/Music
/storage/emulated/0/DCIM/
/data/adb
}"\"
#黑名單模式(1完全忽略不備份 0僅備份安裝包注意此選項Backup_Mode=1時黑名單模式才能使用)
blacklist_mode="${blacklist_mode:-0}"
#備份黑名單(備份策略由「黑名單模式」控制,此處只作為黑名單應用列表)
blacklist=\""${blacklist:-
#com.esunbank
#com.chailease.tw.app.android.ccfappcust}"\"
#位於data的預裝應用白名單 例:相冊 錄音機 天氣 計算器等(默認屏蔽備份預裝應用,如需備份請添加預裝應用白名單)
whitelist=\""${whitelist:-
com.xiaomi.xmsf
com.xiaomi.xiaoailite
com.xiaomi.hm.health
com.duokan.phone.remotecontroller
com.miui.weather2
com.milink.service
com.android.soundrecorder
com.miui.virtualsim
com.xiaomi.vipaccount
com.miui.fm
com.xiaomi.shop
com.xiaomi.smarthome
com.miui.notes
com.xiaomi.router
com.xiaomi.mico
dev.miuiicons.pedroz}"\"
#可被備份的系統應用白名單(默認屏蔽備份系統應用,如需備份請添加系統應用白名單)
system=\""${system:-
com.google.android.calendar
com.google.android.gm
com.google.android.googlequicksearchbox
com.google.android.tts
com.google.android.apps.maps
com.google.android.apps.messaging
com.google.android.inputmethod.latin
com.instagram.android
com.facebook.orca
sh.siava.AOSPMods
com.facebook.katana
com.android.chrome}"\"
#壓縮算法(可用zstd tartar為僅打包 有什麼好用的壓縮算法請聯系我
#zstd擁有良好的壓縮率與速度
Compression_method=${Compression_method:-zstd}
#主色
rgb_a="${rgb_a:-226}"
#輔色
rgb_b="${rgb_b:-123}"
rgb_c="${rgb_c:-177}"" | sed 's/true/1/g ; s/false/0/g'
}
update_Restore_settings_conf() {
echo "#0關閉音量鍵選擇 (如選項未設置,則強制使用音量鍵選擇)
#1開啟音量鍵選擇 (如選項已設置,則跳過該選項提示)
#2使用鍵盤輸入適用於無音量鍵可用設備選擇 (如選項未設置,則強制使用鍵盤輸入)
Lo="${Lo:-0}"
#後台執行腳本
0不能關閉當前終端有壓縮速率
1終端有可能完全無顯示但是log會持續刷新可直接完全關閉終端
background_execution="${background_execution:-0}"
#恢復開始後偽裝亮屏
#1開啟 0關閉
setDisplayPowerMode="${setDisplayPowerMode:-0}"
#腳本語言設置 為空自動針對當前系統語言環境自動翻譯
#1簡體中文 0繁體中文
Shell_LANG="$Shell_LANG"
#自動更新腳本(留空強制選擇)
update="${update:-1}"
#自動更新的cdn節點針對國內用戶使用無牆或是使用VPN請設置0
#0 直鏈下載
#1 https://ghfast.top
#2 https://shrill-pond-3e81.hunsh.workers.dev
cdn=${cdn:-1}
#恢復模式(1恢復未安裝應用 0全恢復)
recovery_mode="${recovery_mode:-0}"
#恢復資料夾
media_recovery="${media_recovery:-0}"
#存在進程忽略恢復(1忽略0恢復)
Background_apps_ignore="${Background_apps_ignore:-0}"
#使用者(如0 999等用戶留空如存在多個用戶強制音量鍵選擇無多用戶則默認0不詢問)
user="$user"
#主色
rgb_a="${rgb_a:-226}"
#輔色
rgb_b="${rgb_b:-123}"
rgb_c="${rgb_c:-177}"" | sed 's/true/1/g ; s/false/0/g'
}

587
tools/core/restore_core.sh Normal file
View File

@@ -0,0 +1,587 @@
#!/system/bin/sh
# module: restore_core.sh
Release_data() {
tar_path="$1"
X="$path2/$name2"
MODDIR_NAME="${tar_path%/*}"
MODDIR_NAME="${MODDIR_NAME##*/}"
FILE_NAME="${tar_path##*/}"
FILE_NAME2="${FILE_NAME%%.*}"
case ${FILE_NAME##*.} in
zst | tar)
unset FILE_PATH Size Selinux_state
[[ -f $app_details ]] && Size="$(jq -r --arg entry "$FILE_NAME2" '.[$entry] | select(.Size != null).Size' "$app_details" 2>/dev/null)"
case $FILE_NAME2 in
user)
if [[ -d $X ]]; then
[[ $(jq -r '.[] | select(.Ssaid != null).keystore' "$app_details") = true ]] && echoRgb "$name1存在keystore 恢復可能閃退" "0"
FILE_PATH="$path2"
Selinux_state="$(LS "$X" | awk 'NF>1{print $1}' | sed -e "s/system_data_file/app_data_file/g" 2>/dev/null)"
else
echoRgb "$X不存在 無法恢復$FILE_NAME2數據" "0"
fi ;;
user_de)
X="$path3/$name2"
if [[ -d $X ]]; then
FILE_PATH="$path3"
Selinux_state="$(LS "$X" | awk 'NF>1{print $1}' | sed -e "s/system_data_file/app_data_file/g" 2>/dev/null)"
else
echoRgb "$X不存在 無法恢復$FILE_NAME2數據" "0"
fi ;;
data) FILE_PATH="$path/data" Selinux_state="$(LS "$FILE_PATH" | awk 'NF>1{print $1}' | sed -e "s/system_data_file/app_data_file/g" 2>/dev/null)" ;;
obb) FILE_PATH="$path/obb" Selinux_state="$(LS "$FILE_PATH" | awk 'NF>1{print $1}' | sed -e "s/system_data_file/app_data_file/g" 2>/dev/null)";;
thanox) FILE_PATH="/data/system" && find "/data/system" -name "thanos"* -maxdepth 1 -type d -exec rm -rf {} \; 2>/dev/null ;;
*)
if [[ $A != "" ]]; then
if [[ ${MODDIR_NAME##*/} = Media ]]; then
FILE_PATH="$(jq -r --arg entry "${FILE_NAME2}" 'select(.[$entry].path != null).[$entry].path' "$app_details")"
if [[ $FILE_PATH = "" ]]; then
echoRgb "路徑獲取失敗" "0"
else
echoRgb "解壓路徑↓\n -$FILE_PATH" "2"
FILE_PATH="${FILE_PATH%/*}"
[[ ! -d $FILE_PATH ]] && mkdir -p "$FILE_PATH"
fi
fi
else
echoRgb "$tar_path名稱似乎有誤" "0"
fi ;;
esac
echoRgb "恢復$FILE_NAME2數據 釋放$(size "$Size")" "3"
if [[ $FILE_PATH != "" ]]; then
[[ ${MODDIR_NAME##*/} != Media ]] && rm -rf "$FILE_PATH/$name2"
case ${FILE_NAME##*.} in
zst) tar --checkpoint-action="ttyout=%T\r" -I zstd -xmpf "$tar_path" -C "$FILE_PATH" ;;
tar) [[ ${MODDIR_NAME##*/} = Media ]] && tar --checkpoint-action="ttyout=%T\r" -axf "$tar_path" -C "$FILE_PATH" || tar --checkpoint-action="ttyout=%T\r" -amxf "$tar_path" -C "$FILE_PATH" ;;
esac
else
Set_back_1
fi
echo_log "解壓縮$FILE_NAME"
if [[ $result = 0 ]]; then
case $FILE_NAME2 in
user|data|obb|user_de)
G="$(pm list packages -U --user "$user" </dev/null | awk -v pkg="$name2" -F'[ :]' '$2 == pkg {print $4}')"
if [[ $G = "" ]]; then
G="$(dumpsys package "$name2" 2>/dev/null | awk -F'uid=' '{print $2}' | egrep -o '[0-9]+' | head -n 1)"
[[ $(echo "$G" | egrep -o '[0-9]+') = "" ]] && G="$(get_uid "$name2" 2>/dev/null)"
fi
G="$(echo "$G" | egrep -o '[0-9]+')"
if [[ $G != "" ]]; then
if [[ -d $X ]]; then
case ${#G} in
5)
if [[ $user = 0 ]]; then
uid="$G:$G"
else
uid="$user$G:$user$G"
fi ;;
6|7|8|9|10)
uid="$G:$G" ;;
esac
case $FILE_NAME2 in
user|user_de)
case $FILE_NAME2 in
user) [[ $X = $path2/$name2 ]] && Validation_settings="true" || Validation_settings="false" ;;
user_de) [[ $X = $path3/$name2 ]] && Validation_settings="true" || Validation_settings="false" ;;
esac
if [[ $Validation_settings = true ]]; then
chown -hR "$uid" "$X/"
echo_log "設置用戶組$uid"
chcon -hR "$Selinux_state" "$X/" 2>/dev/null
echo_log "selinux上下文設置" "E"
else
echoRgb "路徑:$X出現錯誤"
fi ;;
data|obb)
chown -hR "$uid" "$FILE_PATH/$name2/"
echo_log "設置用戶組$uid" "E"
chcon -hR "$Selinux_state" "$FILE_PATH/$name2/" 2>/dev/null
echo_log "selinux上下文設置" "E" ;;
esac
else
echoRgb "$FILE_NAME2路徑$X不存在" "0"
fi
else
echoRgb "uid獲取失敗" "0"
fi
;;
thanox)
restorecon -RF "$(find "/data/system" -name "thanos"* -maxdepth 1 -type d 2>/dev/null)/" 2>/dev/null
echo_log "selinux上下文設置" && echoRgb "警告 thanox配置恢復後務必重啟\n -否則不生效" "0"
;;
esac
fi
;;
*)
echoRgb "$FILE_NAME 壓縮包不支持解壓縮" "0"
Set_back_1
;;
esac
rm -rf "$TMPDIR"/*
}
installapk() {
apkfile="$(find "$Backup_folder" -maxdepth 1 -name "apk.*" -type f 2>/dev/null)"
if [[ $apkfile != "" ]]; then
rm -rf "$TMPDIR"/*
case ${apkfile##*.} in
zst) tar --checkpoint-action="ttyout=%T\r" -I zstd -xmpf "$apkfile" -C "$TMPDIR" ;;
tar) tar --checkpoint-action="ttyout=%T\r" -xmpf "$apkfile" -C "$TMPDIR" ;;
*)
echoRgb "${apkfile##*/} 壓縮包不支持解壓縮" "0"
Set_back_1
;;
esac
echo_log "${apkfile##*/}解壓縮" && [[ -f $Backup_folder/nmsl.apk ]] && cp -r "$Backup_folder/nmsl.apk" "$TMPDIR"
else
echoRgb "你的Apk壓縮包離家出走了可能備份後移動過程遺失了\n -解決辦法手動安裝Apk後再執行恢復腳本" "0"
fi
if [[ $result = 0 ]]; then
case $(find "$TMPDIR" -maxdepth 1 -name "*.apk" -type f 2>/dev/null | wc -l) in
1)
echoRgb "恢復普通apk" "2"
INSTALL "$TMPDIR"/*.apk
echo_log "Apk安裝"
;;
0)
echoRgb "$TMPDIR中沒有apk" "0"
;;
*)
echoRgb "恢復split apk" "2"
b="$(create | egrep -o '[0-9]+')"
if [[ -f $TMPDIR/nmsl.apk ]]; then
INSTALL "$TMPDIR/nmsl.apk"
echo_log "nmsl.apk安裝"
fi
find "$TMPDIR" -maxdepth 1 -name "*.apk" -type f | grep -v 'nmsl.apk' | while read apk; do
pm install-write "$b" "${apk##*/}" "$apk" </dev/null >/dev/null
echo_log "${apk##*/}安裝"
done
pm install-commit "$b" >/dev/null
echo_log "split Apk安裝"
;;
esac
fi
}
# ===================== 子函數: 前置驗證 =====================
Restore_PreCheck() {
self_test
disable_verify
[[ ! -d $path2 ]] && echoRgb "設備不存在user目錄" "0" && exit 1
}
# ===================== 子函數: 批量恢復配置 =====================
Restore_LoadBatchConfig() {
if [[ ! -f ${0%/*}/app_details.json ]]; then
echoRgb "假設反悔了要終止腳本請儘速離開此腳本點擊$MODDIR_NAME/start.sh選擇終止腳本\n -否則腳本將繼續執行直到結束" "0"
echoRgb "如果大量提示找不到資料夾請執行$MODDIR_NAME/start.sh選擇轉換資料夾名稱"
txt="$MODDIR/appList.txt"
[[ ! -f $txt ]] && echoRgb "請執行start.sh獲取應用列表再來恢復" "0" && exit 2
sort -u "$txt" -o "$txt" 2>/dev/null
i=1
r="$(egrep -v '#|' "$txt" 2>/dev/null | awk 'NF != 0 { count++ } END { print count }')"
[[ $r = "" ]] && echoRgb "appList.txt包名為空或是被注釋了\n -請執行start.sh獲取應用列表再來恢復" "0" && exit 1
Backup_folder2="$MODDIR/Media"
#校驗選填是否正確
case $Lo in
0)
[[ $recovery_mode != "" ]] && isBoolean "$recovery_mode" "recovery_mode" && recovery_mode="$nsx" || {
echoRgb "選擇應用恢復模式\n -音量上僅恢復未安裝,下全恢復"
get_version "恢復未安裝" "全恢復" && recovery_mode="$branch"
}
[[ $setDisplayPowerMode != "" ]] && isBoolean "$setDisplayPowerMode" "setDisplayPowerMode" && setDisplayPowerMode="$nsx" || {
echoRgb "應用恢復時關閉螢幕\n -音量上關閉,下不關閉"
get_version "關閉" "不關閉" && setDisplayPowerMode="$branch"
}
Get_user="$(echo "$MODDIR" | rev | cut -d '/' -f1 | cut -d '_' -f1 | rev | egrep -o '[0-9]+')"
if [[ $Get_user != $user ]]; then
echoRgb "檢測當前用戶$user與恢復資料夾用戶:$Get_user不同,音量上繼續恢復,下不恢復並離開腳本"
get_version "恢復安裝" "不恢復安裝" && recovery_mode2="$branch"
fi
if [[ -d $Backup_folder2 ]]; then
[[ $media_recovery != "" ]] && isBoolean "$media_recovery" "media_recovery" && media_recovery="$nsx" || {
echoRgb "是否恢復多媒體數據\n -音量上恢復,音量下不恢復" "2"
get_version "恢復媒體數據" "跳過恢復媒體數據" && media_recovery="$branch"
}
fi
[[ $Background_apps_ignore != "" ]] && isBoolean "$Background_apps_ignore" "Background_apps_ignore" && Background_apps_ignore="$nsx" || {
echoRgb "存在進程忽略恢復\n -音量上忽略,音量下恢復" "2"
get_version "忽略" "恢復" && Background_apps_ignore="$branch"
} ;;
1)
echoRgb "選擇應用恢復模式\n -音量上僅恢復未安裝,下全恢復"
get_version "恢復未安裝" "全恢復" && recovery_mode="$branch"
echoRgb "應用恢復時關閉螢幕\n -音量上關閉,下不關閉"
get_version "關閉" "不關閉" && setDisplayPowerMode="$branch"
Get_user="$(echo "$MODDIR" | rev | cut -d '/' -f1 | cut -d '_' -f1 | rev | egrep -o '[0-9]+')"
if [[ $Get_user != $user ]]; then
echoRgb "檢測當前用戶$user與恢復資料夾用戶:$Get_user不同,音量上繼續恢復,下不恢復並離開腳本"
get_version "恢復安裝" "不恢復安裝" && recovery_mode2="$branch"
fi
echoRgb "是否恢復多媒體數據\n -音量上恢復,音量下不恢復" "2"
get_version "恢復媒體數據" "跳過恢復媒體數據" && media_recovery="$branch"
echoRgb "存在進程忽略恢復\n -音量上忽略,音量下恢復" "2"
get_version "忽略" "恢復" && Background_apps_ignore="$branch" ;;
2)
[[ $recovery_mode = "" ]] && {
Enter_options "選擇應用恢復模式\n -輸入1僅恢復未安裝0全恢復" "僅恢復未安裝" "全恢復" && isBoolean "$parameter" "recovery_mode" && recovery_mode="$nsx"
} || {
isBoolean "$recovery_mode" "recovery_mode" && recovery_mode="$nsx"
}
[[ $setDisplayPowerMode = "" ]] && {
Enter_options "應用恢復時關閉螢幕\n -輸入1關閉0不關閉" "關閉" "不關閉" && isBoolean "$parameter" "setDisplayPowerMode" && setDisplayPowerMode="$nsx"
} || {
isBoolean "$recovery_mode" "recovery_mode" && recovery_mode="$nsx"
}
Get_user="$(echo "$MODDIR" | rev | cut -d '/' -f1 | cut -d '_' -f1 | rev | egrep -o '[0-9]+')"
[[ $Get_user != $user ]] && {
[[ $recovery_mode2 = "" ]] && {
Enter_options "檢測當前用戶$user與恢復資料夾用戶:$Get_user不同輸入1繼續恢復0不恢復並離開腳本" "恢復安裝" "離開腳本" && isBoolean "$parameter" "recovery_mode2" && recovery_mode2="$nsx"
} || {
isBoolean "$recovery_mode2" "recovery_mode2" && recovery_mode2="$nsx"
}
}
[[ $media_recovery = "" ]] && {
Enter_options "是否恢復多媒體\n -輸入1僅恢復0不恢復" "恢復" "不恢復" && isBoolean "$parameter" "media_recovery" && media_recovery="$nsx"
} || {
isBoolean "$media_recovery" "media_recovery" && media_recovery="$nsx"
}
[[ $Background_apps_ignore = "" ]] && {
Enter_options "存在進程忽略恢復\n -輸入1不恢復0恢復" "忽略" "恢復" && isBoolean "$parameter" "Background_apps_ignore" && Background_apps_ignore="$nsx"
} || {
isBoolean "$Background_apps_ignore" "Background_apps_ignore" && Background_apps_ignore="$nsx"
} ;;
*) echoRgb "$conf_path Lo=$Lo填寫錯誤正確值0 1 2" "0" && exit 2 ;;
esac
[[ $recovery_mode2 = false ]] && exit 2
if [[ $recovery_mode = true && $ssaid_mode != true ]]; then
echoRgb "獲取未安裝應用中"
Apk_info="$(pm list packages --user "$user" | cut -f2 -d ':' | egrep -v 'ice.message|com.topjohnwu.magisk' | sort -u)"
if [[ $Apk_info != "" ]]; then
[[ $Apk_info = *"Failure calling service package"* ]] && Apk_info="$(appinfo "user|system" "pkgName" 2>/dev/null | egrep -v 'ice.message|com.topjohnwu.magisk' | sort -u)"
else
Apk_info="$(appinfo "user|system" "pkgName" 2>/dev/null | egrep -v 'ice.message|com.topjohnwu.magisk' | sort -u)"
fi
[[ $Apk_info = "" ]] && echoRgb "Apk_info變量為空" "0" && exit
while read -r ; do
if [[ $(echo "$REPLY" | sed 's/^[ \t]*//') != \#* ]]; then
app=($REPLY $REPLY)
if [[ ${app[1]} != "" && ${app[2]} != "" ]]; then
[[ $(echo "$Apk_info" | awk -v pkg="${app[1]}" '$1 == pkg {print $1}') = "" ]] && Tmplist="$Tmplist\n$REPLY"
fi
fi
done < "$txt"
if [[ $(echo "$Tmplist" | awk 'NF != 0 { count++ } END { print count }') != "" ]]; then
echoRgb "獲取完成 預計安裝$(echo "$Tmplist" | awk 'NF != 0 { count++ } END { print count }')個應用"
txt="$Tmplist"
case $Lo in
0|1)
echoRgb "未安裝應用列表\n$txt\n確認無誤使用音量上繼續恢復音量下退出腳本" "1"
get_version "恢復安裝" "退出腳本" ;;
2)
Enter_options "未安裝應用列表\n$txt\n-輸入1退出腳本0恢復" "退出腳本" "恢復安裝" isBoolean "$parameter" "branch" && branch="$nsx" ;;
esac
[[ $branch = false ]] && exit
else
echoRgb "獲取完成 但備份內應用都已安裝....正在退出腳本" "0" && exit 0
fi
fi
if [[ $ssaid_mode = true ]]; then
while read; do
if [[ $(jq -r '.[] | select(.Ssaid != null).Ssaid' "$REPLY") != "" ]]; then
ChineseName="$(jq -r 'to_entries[] | select(.key != null).key' "$REPLY" | head -n 1)"
PackageName="$(jq -r '.[] | select(.PackageName != null).PackageName' "$REPLY")"
if [[ $ssaid_name = "" ]]; then
ssaid_name="$ChineseName $PackageName"
else
ssaid_name="$ssaid_name\n$ChineseName $PackageName"
fi
fi
done<<<"$(find "$MODDIR" -maxdepth 2 -name "app_details.json" -type f 2>/dev/null | sort)"
[[ $ssaid_name != "" ]] && txt="$ssaid_name"
fi
if [[ ! -f $txt ]]; then
[[ $(echo "$txt") != "" ]] && txt="$(echo "$txt" | sed -e '/^$/d')"
else
txt="$(egrep -v '#|' "$txt" | sed -e '/^$/d')"
fi
r="$(echo "$txt" | awk 'NF != 0 { count++ } END { print count }')"
DX="批量恢復"
else
i=1
r=1
Backup_folder="$MODDIR"
app_details="$Backup_folder/app_details.json"
if [[ ! -f $app_details ]]; then
echoRgb "$app_details遺失,無法獲取包名" "0" && exit 1
else
ChineseName="$(jq -r 'to_entries[] | select(.key != null).key' "$app_details" | head -n 1)"
PackageName="$(jq -r '.[] | select(.PackageName != null).PackageName' "$app_details")"
apk_version="$(jq -r '.[] | select(.apk_version != null).apk_version' "$app_details")"
fi
name1="$ChineseName"
name1="${name1:="${Backup_folder##*/}"}"
[[ $name1 = "" ]] && echoRgb "應用名獲取失敗" "0" && exit 2
name2="$PackageName"
[[ $name2 = "" ]] && echoRgb "包名獲取失敗" "0" && exit 2
DX="單獨恢復"
[[ $Background_apps_ignore != "" ]] && isBoolean "$Background_apps_ignore" "Background_apps_ignore" && Background_apps_ignore="$nsx" || {
echoRgb "存在進程忽略恢復\n -音量上忽略,音量下恢復" "2"
get_version "忽略" "恢復" && Background_apps_ignore="$branch"
}
fi
}
# ===================== 子函數: 恢復循環初始化 =====================
Restore_InitializeLoop() {
starttime1="$(date -u "+%s")"
TIME="$starttime1"
Set_screen_pause_seconds on
en=118 # ANSI 256色起始編號用於通知欄圖標顏色
notification "105" "開始恢復app"
}
# ===================== 子函數: 單應用恢復 =====================
Restore_ProcessSingleApp() {
while [[ $i -le $r ]]; do
[[ $en -ge 229 ]] && en=118 # 超過229重置避免顏色越界
if [[ ! -f ${0%/*}/app_details.json ]]; then
echoRgb "恢復第$i/$r個應用 剩下$((r - i))" "3"
notification "105" "恢復第$i/$r個應用 剩下$((r - i))
恢復 $name1"
name1="$(echo "$txt" | sed -n "${i}p" | cut -d' ' -f1)"
name2="$(echo "$txt" | sed -n "${i}p" | cut -d' ' -f2)"
unset No_backupdata apk_version Permissions
if [[ $name1 = *! || $name1 = * ]]; then
name1="$(echo "$name1" | sed 's/!//g ; s///g')"
echoRgb "跳過恢復$name1 所有數據" "0"
No_backupdata=1
fi
Backup_folder="$MODDIR/$name1"
if [[ -f "$Backup_folder/app_details.json" ]]; then
app_details="$Backup_folder/app_details.json"
apk_version="$(jq -r '.[] | select(.apk_version != null).apk_version' "$app_details")"
else
echoRgb "$Backup_folder/app_details.json不存在" "0"
fi
[[ $name2 = "" ]] && echoRgb "應用包名獲取失敗" "0" && exit 1
fi
if [[ -d $Backup_folder ]]; then
echoRgb "恢復$name1" "2"
Background_application_list
restore="true"
[[ $Backstage != "" && $(echo "$Backstage" | egrep -w "^$name2$") != "" ]] && echoRgb "$name1存在後台 忽略恢復" "0" && restore="false"
[[ $restore = true ]] && {
starttime2="$(date -u "+%s")"
if [[ $(pm list packages --user "$user" | awk -v pkg="$name2" -F':' '$2 == pkg {print $2}') = "" ]]; then
installapk
else
[[ $apk_version -gt $(pm list packages --show-versioncode --user "$user" "$name2" 2>/dev/null | cut -f3 -d ':' | head -n 1) ]] && installapk && [[ $? = 0 ]] && echoRgb "版本提升$(pm list packages --show-versioncode --user "$user" "$name2" 2>/dev/null | cut -f3 -d ':' | head -n 1)>$apk_version" "1"
fi
if [[ $(pm list packages --user "$user" | awk -v pkg="$name2" -F':' '$2 == pkg {print $2}') != "" ]]; then
if [[ $No_backupdata = "" ]]; then
[[ $name2 != *mt* ]] && {
kill_app
find "$Backup_folder" -maxdepth 1 ! -name "apk.*" -name "*.tar*" -type f 2>/dev/null | sort | while read; do
Release_data "$REPLY"
done
unset G
restore_permissions
Ssaid="$(jq -r '.[] | select(.Ssaid != null).Ssaid' "$app_details")"
if [[ $Ssaid != "" ]]; then
SSAID_Package="$(echo "$name1 $name2 $Ssaid")"
SSAID_Package2="$(echo "$SSAID_Package\n$SSAID_Package2")"
unset Ssaid
fi
}
fi
else
[[ $No_backupdata = "" ]]&& echoRgb "$name1沒有安裝無法恢復數據" "0"
fi
endtime 2 "$name1恢復" "2" && echoRgb "完成$((i * 100 / r))%" "3"
rgb_d="$rgb_a"
rgb_a=188 # ANSI 256色: 淺灰色分隔線
echoRgb "_________________$(endtime 1 "已經")___________________"
rgb_a="$rgb_d"
}
else
echoRgb "$Backup_folder資料夾遺失,無法恢復" "0"
fi
Restore_ProcessSSAIDAndMedia
i=$((i + 1)); en=$((en + 1)); nskg=$((nskg + 1))
done
}
# ===================== 子函數: SSAID 與媒體恢復 =====================
Restore_ProcessSSAIDAndMedia() {
if [[ $i = $r ]]; then
endtime 1 "應用恢復" "2"
[[ $SSAID_Package2 != "" ]] && {
echoRgb "開始恢復saaid" "0"
set_ssaid "$(echo "$SSAID_Package2" | awk '{printf "%s %s ", $2, $3}')"
ssaid_info="$(get_ssaid "$(echo "$SSAID_Package2" | awk '{printf "%s ", $2}')")"
echo "$SSAID_Package2" | while read; do
Ssaid="$(echo "$REPLY" | cut -d' ' -f3)"
name1="$(echo "$REPLY" | cut -d' ' -f1)"
name2="$(echo "$REPLY" | cut -d' ' -f2)"
if [[ $(awk -v pkg="$name2" '$1 == pkg {print $2}'<<<"$ssaid_info") = $Ssaid ]]; then
echoRgb "$name1 SSAID恢復成功" "1"
else
echoRgb "$name1 SSAID恢復失敗" "0"
fi
unset Ssaid
done
echoRgb "SSAID恢復後必須重新開機套用,否則應用閃退,如果沒有應用恢復ssaid則無須重啟" "0"
notification "107" "SSAID恢復後必須重新開機套用,否則應用閃退,如果沒有應用恢復ssaid則無須重啟"
}
notification "105" "app恢復完成 $(endtime 1 "應用恢復" "2")"
[[ ! -f ${0%/*}/app_details.json ]] && {
if [[ $media_recovery = true ]]; then
starttime1="$(date -u "+%s")"
app_details="$Backup_folder2/app_details.json"
txt="$MODDIR/mediaList.txt"
sort -u "$txt" -o "$txt" 2>/dev/null
A=1
B="$(egrep -v '#|' "$txt" 2>/dev/null | awk 'NF != 0 { count++ } END { print count }')"
[[ $B = "" ]] && echoRgb "mediaList.txt壓縮包名為空或是被注釋了\n -請執行start.sh獲取列表再來恢復" "0" && B=0
notification "106" "Media恢復開始"
while [[ $A -le $B ]]; do
name1="$(egrep -v '#|' "$txt" 2>/dev/null | sed -e '/^$/d' | sed -n "${A}p" | awk '{print $1}')"
starttime2="$(date -u "+%s")"
echoRgb "恢復第$A/$B個壓縮包 剩下$((B - A))" "3"
Release_data "$Backup_folder2/$name1"
endtime 2 "$FILE_NAME2恢復" "2" && echoRgb "完成$((A * 100 / B))%" "3" && echoRgb "____________________________________" && A=$((A + 1))
done
endtime 1 "自定義恢復" "2"
notification "106" "Media恢復完成 $(endtime 1 "Media恢復" "2")"
fi
recover_wifi "$MODDIR/wifi"
}
fi
}
# ===================== 子函數: 清理 =====================
Restore_Cleanup() {
Set_screen_pause_seconds off
[[ $user != 0 ]] && am stop-user "$user"
starttime1="$TIME"
echoRgb "$DX完成" && endtime 1 "$DX開始到結束"
notification "109" "恢復完成 $(endtime 1 "$DX開始到結束")"
rm -rf "$TMPDIR"/*
}
# ===================== 重構後的 Restore() 入口 =====================
Restore() {
Restore_PreCheck
Restore_LoadBatchConfig
Restore_InitializeLoop
Restore_ProcessSingleApp
Restore_ProcessSSAIDAndMedia
Restore_Cleanup
}
Restore3() {
self_test
case $Lo in
0|1)
echoRgb "點錯了?這是恢復自定義資料夾腳本 如果你是要恢復應用那你就點錯了\n -音量上繼續恢復自定義資料夾,音量下離開腳本" "2"
echoRgb "假設反悔了要終止腳本請儘速離開此腳本點擊start.sh選擇終止腳本,否則腳本將繼續執行直到結束" "0"
get_version "恢復自定義資料夾" "離開腳本" && [[ $branch = false ]] && exit 0 ;;
2)
Enter_options "點錯了?這是恢復自定義資料夾腳本 如果你是要恢復應用那你就點錯了\n -輸入1繼續恢復自定義資料夾輸入0離開腳本" "恢復" "退出腳本" && isBoolean "$parameter" "branch" && branch="$nsx" && [[ $branch = false ]] && exit 0 ;;
esac
mediaDir="$MODDIR/Media"
[[ -f "$mediaDir/app_details.json" ]] && app_details="$mediaDir/app_details.json"
Backup_folder2="$mediaDir"
[[ ! -d $mediaDir ]] && echoRgb "媒體資料夾不存在" "0" && exit 2
txt="$MODDIR/mediaList.txt"
[[ ! -f $txt ]] && echoRgb "請執行start.sh獲取媒體列表再來恢復" "0" && exit 2
sort -u "$txt" -o "$txt" 2>/dev/null
#記錄開始時間
starttime1="$(date -u "+%s")"
echo_log() {
if [[ $? = 0 ]]; then
echoRgb "$1成功" "1" && result=0
else
echoRgb "$1恢復失敗,過世了" "0" && result=1
fi
}
starttime1="$(date -u "+%s")"
A=1
B="$(egrep -v '#|' "$txt" 2>/dev/null | awk 'NF != 0 { count++ } END { print count }')"
Set_screen_pause_seconds on
[[ $B = "" ]] && echoRgb "mediaList.txt壓縮包名為空或是被注釋了\n -請執行start.sh獲取列表再來恢復" "0" && exit 1
notification "108" "Media恢復開始"
while [[ $A -le $B ]]; do
name1="$(egrep -v '#|' "$txt" 2>/dev/null | sed -e '/^$/d' | sed -n "${A}p" | awk '{print $1}')"
starttime2="$(date -u "+%s")"
echoRgb "恢復第$A/$B個壓縮包 剩下$((B - A))" "3"
Release_data "$mediaDir/$name1"
endtime 2 "$FILE_NAME2恢復" "2" && echoRgb "完成$((A * 100 / B))%" "3" && echoRgb "____________________________________" && A=$((A + 1))
done
Set_screen_pause_seconds off
endtime 1 "恢復結束"
notification "108" "Media恢復完成 $(endtime 1 "Media恢復")"
}
Restore4() {
if [[ $ssaid_mode_1 = true ]]; then
while read; do
if [[ $(jq -r '.[] | select(.Ssaid != null).Ssaid' "$REPLY") != "" ]]; then
ChineseName="$(jq -r 'to_entries[] | select(.key != null).key' "$REPLY" | head -n 1)"
PackageName="$(jq -r '.[] | select(.PackageName != null).PackageName' "$REPLY")"
if [[ $ssaid_name = "" ]]; then
ssaid_name="$ChineseName $PackageName"
else
ssaid_name="$ssaid_name\n$ChineseName $PackageName"
fi
fi
done<<<"$(find "$MODDIR" -maxdepth 2 -name "app_details.json" -type f 2>/dev/null | sort)"
[[ $ssaid_name != "" ]] && txt="$ssaid_name"
i=1
[[ $(echo "$txt") != "" ]] && txt="$(echo "$txt" | sed -e '/^$/d')"
r="$(echo "$txt" | awk 'NF != 0 { count++ } END { print count }')"
while [[ $i -le $r ]]; do
name1="$(echo "$txt" | sed -n "${i}p" | cut -d' ' -f1)"
name2="$(echo "$txt" | sed -n "${i}p" | cut -d' ' -f2)"
Backup_folder="$MODDIR/$name1"
if [[ -f "$Backup_folder/app_details.json" ]]; then
app_details="$Backup_folder/app_details.json"
apk_version="$(jq -r '.[] | select(.apk_version != null).apk_version' "$app_details")"
else
echoRgb "$Backup_folder/app_details.json不存在" "0"
fi
[[ $name2 = "" ]] && echoRgb "應用包名獲取失敗" "0" && exit 1
if [[ $(pm list packages --user "$user" | awk -v pkg="$name2" -F':' '$2 == pkg {print $2}') != "" ]]; then
[[ $name2 != *mt* ]] && {
kill_app
Ssaid="$(jq -r '.[] | select(.Ssaid != null).Ssaid' "$app_details")"
if [[ $Ssaid != "" ]]; then
SSAID_Package="$(echo "$name1 $name2 $Ssaid")"
SSAID_Package2="$(echo "$SSAID_Package\n$SSAID_Package2")"
unset Ssaid
fi
}
fi
if [[ $i = $r ]]; then
[[ $SSAID_Package2 != "" ]] && {
echoRgb "開始恢復saaid" "0"
set_ssaid "$(echo "$SSAID_Package2" | awk '{printf "%s %s ", $2, $3}')"
ssaid_info="$(get_ssaid "$(echo "$SSAID_Package2" | awk '{printf "%s ", $2}')")"
echo "$SSAID_Package2" | while read; do
Ssaid="$(echo "$REPLY" | cut -d' ' -f3)"
name1="$(echo "$REPLY" | cut -d' ' -f1)"
name2="$(echo "$REPLY" | cut -d' ' -f2)"
if [[ $(awk -v pkg="$name2" '$1 == pkg {print $2}'<<<"$ssaid_info") = $Ssaid ]]; then
echoRgb "$name1 SSAID恢復成功" "1"
else
echoRgb "$name1 SSAID恢復失敗" "0"
fi
unset Ssaid
done
echoRgb "SSAID恢復後必須重新開機套用,否則應用閃退,如果沒有應用恢復ssaid則無須重啟" "0"
notification "107" "SSAID恢復後必須重新開機套用,否則應用閃退,如果沒有應用恢復ssaid則無須重啟"
}
fi
i=$((i + 1))
done
fi

573
tools/core/updater.sh Normal file
View File

@@ -0,0 +1,573 @@
#!/system/bin/sh
# module: updater.sh
backup_wifi() {
[[ ! -d $1 ]] && mkdir -p "$1"
if [[ -d $1 ]]; then
echoRgb "備份wifi密碼"
rm -rf "$1"/*
app_process /system/bin com.xayah.dex.NetworkUtil saveNetworks>"$1/wifi.json"
echo_log "wifi備份"
fi
}
recover_wifi() {
if [[ -d $1 ]]; then
if [[ -f $1/wifi.json ]]; then
echoRgb "恢復wifi密碼"
app_process /system/bin com.xayah.dex.NetworkUtil restoreNetworks "$1/wifi.json"
echo_log "wifi恢復"
else
echoRgb "wifi.json遺失"
fi
else
echoRgb "$1不存在 wifi無法恢復" "0"
fi
}
Rename_script () {
HT="${HT:=0}"
find "$path_hierarchy" -maxdepth 3 -name "*.sh" -type f -not -name "tools.sh" | sort | while read ; do
MODDIR_NAME="${REPLY%/*}"
FILE_NAME="${REPLY##*/}"
if [[ -f ${REPLY%/*}/app_details.json || -f ${REPLY%/*}/app_details ]]; then
if [[ $FILE_NAME = backup.sh ]]; then
touch_shell "1" "$REPLY"
elif [[ $FILE_NAME = recover.sh ]]; then
touch_shell "3" "$REPLY"
fi
else
if [[ -d ${REPLY%/*}/tools ]]; then
if [[ $FILE_NAME = start.sh ]]; then
[[ -f ${REPLY%/*}/backup_settings.conf ]] && touch_shell "0" "$REPLY"
[[ -f ${REPLY%/*}/restore_settings.conf ]] && touch_shell "2" "$REPLY"
fi
fi
HT=$((HT + 1))
fi
done
unset HT
}
touch_shell () {
unset conf_path MODDIR_Path
case $1 in
0)
MODDIR_Path='${0%/*}'
MODDIR_Path1="$MODDIR_Path"
conf_path='${0%/*}/backup_settings.conf' ;;
1)
MODDIR_Path='${0%/*/*/*}'
MODDIR_Path1="$MODDIR_Path"
conf_path='${0%/*/*/*}/backup_settings.conf' ;;
2)
MODDIR_Path='${0%/*}'
MODDIR_Path1="$MODDIR_Path"
conf_path='${0%/*}/restore_settings.conf' ;;
3)
MODDIR_Path='${0%/*/*}'
MODDIR_Path1='${0%/*}'
conf_path='${0%/*/*}/restore_settings.conf' ;;
esac
echo "#!/system/bin/sh
if [ -f \"$MODDIR_Path/tools/tools.sh\" ]; then
MODDIR=\"$MODDIR_Path1\"
conf_path=\"$conf_path\"
[ ! -f \"$conf_path\" ] && . \"$MODDIR_Path/tools/tools.sh\"
else
echo \"$MODDIR_Path/tools/tools.sh遺失\"
fi
. \"$MODDIR_Path/tools/tools.sh\" | tee \"\${0%/*}/log_\$(date +%Y-%m-%d_%H-%M).txt\""> "$2"
}
update_script() {
[[ $zipFile = "" ]] && zipFile="$(find "$MODDIR" -maxdepth 1 -name "*.zip" -type f 2>/dev/null)"
if [[ $zipFile != "" ]]; then
case $(echo "$zipFile" | wc -l) in
1)
if [[ $(unzip -l "$zipFile" | awk '{print $4}' | egrep -o "^backup_settings.conf$") != "" ]]; then
unzip -o "$zipFile" -j "tools/tools.sh" -d "$MODDIR" &>/dev/null
if [[ -f $MODDIR/tools.sh ]]; then
if [[ $(expr "$(echo "$backup_version" | tr -d "a-zA-Z")" \> "$(awk '/backup_version/{print $1}' "$MODDIR/tools.sh" | cut -f2 -d '=' | head -1 | sed 's/\"//g' | tr -d "a-zA-Z")") -eq 0 ]]; then
shell_language="$(awk -F= '/^shell_language=/ {gsub(/"/, "", $2); print $2}' "$MODDIR/tools.sh")"
case $MODDIR in
*Backup_*)
if [[ -f $MODDIR/app_details.json ]]; then
echoRgb "請在${MODDIR%/*}更新腳本" "0"
rm -rf "$MODDIR/tools.sh"
exit 2
fi ;;
esac
echoRgb "$zipFile更新"
if [[ -d $path_hierarchy/tools ]]; then
mv "$path_hierarchy/tools" "$TMPDIR"
[[ -d $TMPDIR/tools ]] && {
unzip -o "$zipFile" tools/* -d "$path_hierarchy" | sed 's/inflating/釋放/g ; s/creating/創建/g ; s/Archive/解壓縮/g'
echo_log "解壓縮${zipFile##*/}"
if [[ $result = 0 ]]; then
if [[ $shell_language != $Script_target_language ]]; then
echoRgb "腳本語言為$shell_language....轉換為$Script_target_language中,請稍後等待轉換...."
ts <"$path_hierarchy/tools/Device_List">temp && cp temp "$path_hierarchy/tools/Device_List" && rm temp
echo_log "$path_hierarchy/tools/Device_List翻譯"
ts <"$path_hierarchy/tools/tools.sh">temp && cp temp "$path_hierarchy/tools/tools.sh" && rm temp && sed "s/shell_language=\"$shell_language\"/shell_language=\"$Script_target_language\"/g" "$path_hierarchy/tools/tools.sh" > temp && cp temp "$path_hierarchy/tools/tools.sh" && rm temp
echo_log "$path_hierarchy/tools/tools.sh翻譯"
HT=1
fi
update_backup_settings_conf>"$path_hierarchy/backup_settings.conf"
ts <"$path_hierarchy/backup_settings.conf">temp && cp temp "$path_hierarchy/backup_settings.conf" && rm temp
echo_log "$path_hierarchy/backup_settings.conf翻譯"
echo "$find_tools_path" | while read; do
if [[ $REPLY != $path_hierarchy/tools ]]; then
rm -rf "$REPLY"
cp -r "$path_hierarchy/tools" "${REPLY%/*}"
update_Restore_settings_conf>"${REPLY%/*}/restore_settings.conf"
ts <"${REPLY%/*}/restore_settings.conf">temp && cp temp "${REPLY%/*}/restore_settings.conf" && rm temp
echo_log "${REPLY%/*}/restore_settings.conf翻譯"
fi
done
Rename_script
if [[ $Output_path != "" ]]; then
[[ ${Output_path: -1} = / ]] && Output_path="${Output_path%?}"
if [[ ${Output_path:0:1} != / ]]; then
update_path="$MODDIR/$Output_path/Backup_${Compression_method}_$user"
else
update_path="$Output_path/Backup_${Compression_method}_$user"
fi
rm -rf "$update_path/tools"
cp -r "$path_hierarchy/tools" "$update_path"
echoRgb "$update_path/tools已經更新完成"
fi
else
mv "$TMPDIR/tools" "$MODDIR"
fi
rm -rf "$TMPDIR"/* "$zipFile" "$MODDIR/tools.sh"
echoRgb "更新完成 請重新執行腳本" "2"
exit 0
} || echoRgb "tools移動到TMPDIR失敗" "0"
fi
else
echoRgb "${zipFile##*/}版本低於當前版本,自動刪除" "0"
rm -rf "$zipFile" "$path_hierarchy/tools.sh"
fi
else
rm -rf "$zipFile"
unset zipFile
fi
fi ;;
*)
echoRgb "錯誤 請刪除當前目錄多餘zip\n -保留一個最新的數據備份.zip\n -下列為當前目錄zip\n$zipFile" "0"
exit 1 ;;
esac
fi
unset NAME
}
Check_archive() {
starttime1="$(date -u "+%s")"
error_log="$TMPDIR/error_log"
rm -rf "$error_log"
FIND_PATH="$(find "$1" -maxdepth 3 -name "*.tar*" -type f 2>/dev/null | sort)"
i=1
r="$(find "$MODDIR" -maxdepth 2 -name "app_details.json" -type f 2>/dev/null | wc -l)"
find "$MODDIR" -maxdepth 2 -name "app_details.json" -type f 2>/dev/null | sort | while read; do
REPLY="${REPLY%/*}"
echoRgb "校驗第$i/$r個資料夾 剩下$((r - i))" "3"
echoRgb "校驗:${REPLY##*/}"
find "$REPLY" -maxdepth 1 -name "*.tar*" -type f 2>/dev/null | sort | while read; do
Validation_file "$REPLY"
[[ $result != 0 ]] && echo "$REPLY">>"$error_log"
done
echoRgb "$((i * 100 / r))%"
i=$((i + 1)); nskg=$((nskg + 1))
done
endtime 1
[[ -f $error_log ]] && echoRgb "以下為失敗的檔案\n $(cat "$error_log")" || echoRgb "恭喜~~全數校驗通過"
rm -rf "$error_log"
}
get_name(){
txt="$MODDIR/appList.txt"
txt="${txt/'/storage/emulated/'/'/data/media/'}"
txt2="$MODDIR/mediaList.txt"
if [[ $1 = Apkname ]]; then
rm -rf "$txt" "$txt2"
echoRgb "列出全部資料夾內應用名與自定義目錄壓縮包名稱" "3"
fi
rgb_a=118
user="$(echo "${0%}" | sed 's/.*\/Backup_zstd_\([0-9]*\).*/\1/')"
Apk_info="$(pm list packages --user "$user" | cut -f2 -d ':' | egrep -v 'ice.message|com.topjohnwu.magisk' | sort -u)"
if [[ $Apk_info != "" ]]; then
[[ $Apk_info = *"Failure calling service package"* ]] && Apk_info="$(appinfo "user|system" "pkgName" 2>/dev/null | egrep -v 'ice.message|com.topjohnwu.magisk' | sort -u)"
else
Apk_info="$(appinfo "user|system" "pkgName" 2>/dev/null | egrep -v 'ice.message|com.topjohnwu.magisk' | sort -u)"
fi
[[ $Apk_info = "" ]] && echoRgb "Apk_info變量為空" "0" && exit
starttime1="$(date -u "+%s")"
i=1
while read; do
Folder="${REPLY%/*}"
[[ $rgb_a -ge 229 ]] && rgb_a=118
unset PackageName NAME DUMPAPK ChineseName apk_version Ssaid dataSize userSize obbSize
if [[ -f $Folder/app_details.json ]]; then
ChineseName="$(jq -r 'to_entries[] | select(.key != null).key' "$Folder/app_details.json" | head -n 1)"
PackageName="$(jq -r '.[] | select(.PackageName != null).PackageName' "$Folder/app_details.json")"
if [[ -f $Folder/Permissions ]]; then
unset Permissions
. "$Folder/Permissions"
jq --arg packageName "$ChineseName" --argjson permissions "$(echo "$Permissions" | jq -nR '[inputs | select(length>0) | split(" ") | {(.[0]): .[-1]}] | add')" '.[$packageName] |= . + {permissions: $permissions}' "$Folder/app_details.json" > "$TMPDIR/temp.json" && cp "$TMPDIR/temp.json" "$Folder/app_details.json" && rm "$Folder/Permissions" "$TMPDIR/temp.json" && echoRgb "更新$Folder/app_details.json"
fi
else
if [[ -f $Folder/app_details ]]; then
. "$Folder/app_details" &>/dev/null
extra_content="{
\"$ChineseName\": {
\"PackageName\": \"$PackageName\",
\"apk_version\": \"$apk_version\",
\"Ssaid\": \"$Ssaid\"
},
\"data\": {
\"Size\": \"$dataSize\"
},
\"obb\": {
\"Size\": \"$obbSize\"
},
\"user\": {
\"Size\": \"$userSize\"
}
}"
echo "{\n}">"$Folder/app_details.json"
jq --argjson new_content "$extra_content" '. += $new_content' "$Folder/app_details.json" > "$TMPDIR/temp.json" && cp "$TMPDIR/temp.json" "$Folder/app_details.json" && rm "$TMPDIR/temp.json" "$Folder/app_details"
fi
fi
if [[ $PackageName = "" || $ChineseName = "" ]]; then
echoRgb "${Folder##*/}包名獲取失敗,解壓縮獲取包名中..." "0"
rm -rf "$TMPDIR"/*
case ${REPLY##*.} in
zst) tar -I zstd -xmpf "$REPLY" -C "$TMPDIR" --wildcards --no-anchored 'base.apk' ;;
tar) tar -xmpf "$REPLY" -C "$TMPDIR" --wildcards --no-anchored 'base.apk' ;;
*)
echoRgb "${REPLY##*/} 壓縮包不支持解壓縮" "0"
Set_back_1
;;
esac
echo_log "${REPLY##*/}解壓縮"
if [[ $result = 0 ]]; then
if [[ -f $TMPDIR/base.apk ]]; then
DUMPAPK="$(appinfo3 "$TMPDIR/base.apk")"
if [[ $DUMPAPK != "" ]]; then
app=($DUMPAPK $DUMPAPK)
PackageName="${app[1]}"
ChineseName="${app[2]}"
rm -rf "$TMPDIR"/*
else
echoRgb "appinfo輸出失敗" "0"
fi
fi
fi
fi
if [[ $PackageName != "" && $ChineseName != "" ]]; then
if [[ $(echo "$Apk_info" | awk -v pkg="$PackageName" '$1 == pkg {print $1}') = "" ]]; then
echoRgb "$ChineseName已經不存在$user使用者中"
if [[ $delete_app = "" ]]; then
delete_app="$ChineseName $PackageName"
else
delete_app="$delete_app\n$ChineseName $PackageName"
fi
fi
case $1 in
Apkname)
[[ -f $Folder/${PackageName}.sh ]] && rm -rf "$Folder/${PackageName}.sh"
[[ ! -f $Folder/recover.sh ]] && touch_shell "3" "$Folder/recover.sh"
[[ ! -f $Folder/backup.sh ]] && touch_shell "1" "$Folder/backup.sh"
echoRgb "$i:$ChineseName $PackageName"
if [[ $TMPTXT = "" ]]; then
TMPTXT="#不需要恢復還原的應用請在開頭使用#注釋 比如:#酷安 com.coolapk.market\n$ChineseName $PackageName"
else
TMPTXT="$TMPTXT\n$ChineseName $PackageName"
fi
i=$((i + 1)) ;;
convert)
if [[ ${Folder##*/} = $PackageName ]]; then
DIR_NAME="${Folder%/*}/$ChineseName"
echoRgb "${Folder##*/} > $ChineseName"
else
DIR_NAME="${Folder%/*}/$PackageName"
echoRgb "${Folder##*/} > $PackageName"
fi
if [[ -d $DIR_NAME ]]; then
i=1
NEW_DIR_NAME="${DIR_NAME}_${i}"
while [[ -d $NEW_DIR_NAME ]]; do
i=$((i + 1))
NEW_DIR_NAME="${DIR_NAME}_${i}"
done
DIR_NAME="$NEW_DIR_NAME"
fi
mv "$Folder" "$DIR_NAME" ;;
esac
fi
rgb_a=$((rgb_a + 1))
done<<<"$(find "$MODDIR" -maxdepth 2 -name "apk.*" -type f 2>/dev/null | sort)"
[[ $TMPTXT != "" ]] && echo "$TMPTXT">"$txt"
if [[ -d $MODDIR/Media ]]; then
echoRgb "存在媒體資料夾" "2"
[[ ! -f $txt2 ]] && echo "#不需要恢復的資料夾請在開頭使用#注釋 比如:#Download" > "$txt2"
find "$MODDIR/Media" -maxdepth 1 -name "*.tar*" -type f 2>/dev/null | while read; do
echoRgb "${REPLY##*/}" && echo "${REPLY##*/}" >> "$txt2"
done
echoRgb "$txt2重新生成" "1"
fi
if [[ $delete_app != "" ]]; then
if [[ $(echo "$delete_app" | awk 'NF != 0 { count++ } END { print count }') != "" ]]; then
echoRgb "列出需要刪除的應用中....\n -$delete_app"
case $Lo in
0|1)
echoRgb "確認列表無誤後音量上刪除,音量下退出腳本編輯列表" "2"
get_version "刪除" "退出腳本" && Delete_App="$branch" ;;
2)
Enter_options "確認列表無誤後輸入1刪除輸入0退出腳本編輯列表" "刪除" "退出腳本" && isBoolean "$parameter" "Delete_App" && Delete_App="$nsx" ;;
esac
if [[ $Delete_App = true ]]; then
echoRgb "警告 即將刪除未安裝應用資料夾,請再三確認後在執行" "0"
i=1
r="$(echo "$delete_app" | awk 'NF != 0 { count++ } END { print count }')"
while [[ $i -le $r ]]; do
name1="$(echo "$delete_app" | sed -e '/^$/d' | sed -n "${i}p" | cut -d' ' -f1)"
name2="$(echo "$delete_app" | sed -e '/^$/d' | sed -n "${i}p" | cut -d' ' -f2)"
Backup_folder="$MODDIR/$name1"
[[ -d $Backup_folder ]] && rm -rf "$Backup_folder"
echo "$(sed -e "s/$name1 $name2//g ; /^$/d" "$txt" 2>/dev/null)" >"$txt"
i=$((i + 1))
done
else
exit 0
fi
fi
fi
chown "$(stat -c '%u:%g' '/data/media/0/Download')" "$txt"
endtime 1
exit 0
}
Getlist() {
case $MODDIR in
/storage/emulated/0/Android/* | /data/media/0/Android/* | /sdcard/Android/*) echoRgb "請勿在$MODDIR內生成列表" "0" && exit 2 ;;
esac
#校驗選填是否正確
case $Lo in
0)
[[ $blacklist_mode != "" ]] && isBoolean "$blacklist_mode" "blacklist_mode" && blacklist_mode="$nsx" || {
echoRgb "選擇黑名單模式\n -音量上不輸出,音量下輸出應用列表" "2"
get_version "不輸出" "輸出應用列表" && blacklist_mode="$branch"
} ;;
1)
if [[ $(echo "$blacklist" | egrep -v '#|' | wc -l) -gt 0 ]]; then
[[ $blacklist_mode = "" ]] && {
echoRgb "選擇黑名單模式\n -音量上不輸出,音量下輸出應用列表" "2"
get_version "不輸出" "輸出應用列表" && blacklist_mode="$branch"
} || isBoolean "$blacklist_mode" "blacklist_mode" && blacklist_mode="$nsx"
fi ;;
2)
[[ $blacklist_mode = "" ]] && {
Enter_options "選擇黑名單模式輸入1不輸出輸入0輸出應用列表" "不輸出" "輸出應用列表" && isBoolean "$parameter" "blacklist_mode" && blacklist_mode="$nsx"
} || {
isBoolean "$blacklist_mode" "blacklist_mode" && blacklist_mode="$nsx"
} ;;
*) echoRgb "$conf_path Lo=$Lo填寫錯誤正確值0 1 2" "0" && exit 2 ;;
esac
txt="$TMPDIR/appList"
[[ -f "$MODDIR/appList.txt" ]] && cat "$MODDIR/appList.txt" >"$txt"
[[ ! -f $txt ]] && echo '#不需要備份的應用請在開頭使用#注釋 比如:#酷安 com.coolapk.market忽略安裝包和數據\n#不需要備份數據的應用請在開頭使用!注釋 比如:!酷安 com.coolapk.market僅忽略數據' >"$txt"
echoRgb "請勿關閉腳本,等待提示結束"
rgb_a=118
starttime1="$(date -u "+%s")"
echoRgb "提示! 腳本默認會屏蔽預裝應用 如需備份請添加預裝應用白名單" "0"
Apk_info="$(appinfo "system|user|xposed" "label|pkgName|flag" | egrep -v 'ice.message|com.topjohnwu.magisk' | tr '/:' '_')"
xposed_name="$(echo "$Apk_info" | awk '$3 == "xposed" {print $2}')"
TARGET_PACKAGES="$(echo "$system" | paste -sd'|' - | sed 's/^|//')"
Pre_installed_apps="$(echo "$Apk_info" | awk '$3 == "system" {print $1, $2}' | egrep -w "$TARGET_PACKAGES")"
Apk_info="$(echo "$(echo "$Apk_info" | awk '$3 != "system" {print $1, $2}')\n$Pre_installed_apps" | sort -u)"
[[ $Apk_info = "" ]] && {
echoRgb "appinfo輸出失敗,請截圖畫面回報作者" "0"
exit 2 ; } || Apk_info2="$(echo "$Apk_info" | cut -d' ' -f2)"
Apk_Quantity="$(echo "$Apk_info" | wc -l)"
LR="1"
echoRgb "列出第三方應用......." "2"
i="0"
rc="0"
rd="0"
Q="0"
rb="0"
Output_list() {
if [[ $(cat "$txt" | cut -f2 -d ' ' | egrep -w "^${app_1[1]}$") != ${app_1[1]} ]]; then
[[ $REPLY2 = "" ]] && add_entry "${app_1[2]}" "${app_1[1]}" "$(grep -w "${app_1[2]}" "$txt")" || add_entry "${app_1[2]}" "${app_1[1]}" "$REPLY2"
case ${app_1[1]} in
*oneplus*|*miui*|*xiaomi*|*oppo*|*flyme*|*meizu*|com.android.soundrecorder|com.mfashiongallery.emag|com.mi.health|*coloros*|com.android.soundrecorder|com.duokan.phone.remotecontroller|com.android.calendar|com.android.deskclock|com.android.calendar|com.android.deskclock|com.google.android.safetycore|com.google.android.contactkeys|com.google.android.apps.messaging|com.google.android.calendar)
if [[ $(echo "$xposed_name" | egrep -w "${app_1[1]}$") = ${app_1[1]} ]]; then
echoRgb "$((i+1)):$app_name為Xposed模塊 進行添加" "0"
if [[ $REPLY2 = "" ]]; then
REPLY2="$REPLY" && [[ $tmp = "" ]] && tmp="1"
else
REPLY2="$REPLY2\n$REPLY" && [[ $tmp = "" ]] && tmp="1"
fi
i=$((i + 1)); rd=$((rd + 1))
else
if [[ $(echo "$whitelist" | egrep -w "^${app_1[1]}$") = ${app_1[1]} ]]; then
if [[ $REPLY2 = "" ]]; then
REPLY2="$REPLY" && [[ $tmp = "" ]] && tmp="1"
else
REPLY2="$REPLY2\n$REPLY" && [[ $tmp = "" ]] && tmp="1"
fi
echoRgb "$((i+1)):$app_name ${app_1[1]}($rgb_a)"
i=$((i + 1))
else
echoRgb "$app_name 預裝應用 忽略輸出" "0"
if [[ $REPLY2 = "" ]]; then
REPLY2="#$REPLY" && [[ $tmp = "" ]] && tmp="1"
else
REPLY2="$REPLY2\n#$REPLY" && [[ $tmp = "" ]] && tmp="1"
fi
rc=$((rc + 1))
fi
fi
;;
*)
if [[ $REPLY2 = "" ]]; then
REPLY2="$REPLY" && [[ $tmp = "" ]] && tmp="1"
else
REPLY2="$REPLY2\n$REPLY" && [[ $tmp = "" ]] && tmp="1"
fi
if [[ $(echo "$xposed_name" | egrep -w "${app_1[1]}$") = ${app_1[1]} ]]; then
echoRgb "$((i+1)):Xposed: $app_name ${app_1[1]}($rgb_a)"
rd=$((rd + 1))
else
echoRgb "$((i+1)):$app_name ${app_1[1]}($rgb_a)"
fi
i=$((i + 1))
;;
esac
else
Q=$((Q + 1))
fi
}
[[ $(echo "$blacklist" | egrep -v '#|') != "" ]] && NZK=1
echo "$Apk_info" | sed 's/[\/:()\[\]\-!]//g' | while read; do
[[ $rgb_a -ge 229 ]] && rgb_a=118
app_1=($REPLY $REPLY)
if [[ $NZK = 1 ]]; then
if [[ $(echo "$blacklist" | egrep -w "^${app_1[1]}$") != ${app_1[1]} ]]; then
Output_list
else
if [[ $blacklist_mode = false ]]; then
Output_list
rb=$((rb + 1))
else
echoRgb "${app_1[2]}黑名單應用 不輸出" "0"
rb=$((rb + 1))
fi
fi
else
Output_list
fi
if [[ $LR = $Apk_Quantity ]]; then
echo "$REPLY2">>"$txt"
if [[ $(cat "$txt" | wc -l | awk '{print $1-2}') -lt $i ]]; then
rm -rf "$txt"
echoRgb "\n -輸出異常 請聯繫作者解決" "0"
exit 1
fi
echoRgb "已經將預裝應用輸出至appList.txt並注釋# 需要備份則去掉#" "0"
[[ $tmp != "" ]] && echoRgb "\n -第三方apk數量=\"$Apk_Quantity\"\n -已過濾=\"$rc\"\n -xposed=\"$rd\"\n -黑名單應用=\"$rb\"\n -存在列表中=\"$Q\"\n -輸出=\"$i\""
fi
rgb_a=$((rgb_a + 1)); LR=$((LR + 1))
done
if [[ -f $txt ]]; then
while read -r ; do
if [[ $(echo "$REPLY" | sed -E 's/^[ \t]*//; /^[ \t]*[#!]/d') != "" ]]; then
app=($REPLY $REPLY)
if [[ ${app[1]} != "" && ${app[2]} != "" ]]; then
if [[ $(echo "$Apk_info2" | awk -v pkg="${app[1]}" '$1 == pkg {print $1}') != "" ]]; then
[[ $Tmplist = "" ]] && Tmplist='#不需要備份的應用請在開頭使用#注釋 比如:#酷安 com.coolapk.market忽略安裝包和數據\n#不需要備份數據的應用請在開頭使用!注釋 比如:!酷安 com.coolapk.market僅忽略數據'
Tmplist="$Tmplist\n$REPLY"
else
echoRgb "$REPLY不存在系統,從列表中刪除" "0"
fi
fi
else
Tmplist="$Tmplist\n$REPLY"
fi
done < "$txt"
[[ $Tmplist != "" ]] && echo "$Tmplist" | sed -e '/^$/d' | sort>"$txt"
fi
wait
endtime 1
cat "$txt">"$MODDIR/appList.txt" && rm "$txt"
chown "$(stat -c '%u:%g' '/data/media/0/Download')" "$MODDIR/appList.txt"
echoRgb "輸出包名結束 請查看$MODDIR/appList.txt"
}
}
Device_List() {
URL="https://raw.githubusercontent.com/KHwang9883/MobileModels/refs/heads/master/brands"
rm -rf "$tools_path/Device_List"
for i in $(echo "xiaomi\nxiaomi_en\nsamsung\nsamsung_global\nasus\nBlack_Shark\nBlack_Shark_en\ngoogle\nLenovo\nMEIZU\nMEIZU_en\nMotorola\nNokia\nnothing\nnubia\nOnePlus\nOnePlus_en\nSony\nrealme\nrealme_en\nvivo\nvivo_en\noppo\noppo_en"); do
echoRgb "獲取品牌$i"
case $i in
xiaomi) Brand_URL="$URL/xiaomi.md" ;;
xiaomi_en) Brand_URL="$URL/xiaomi_en.md" ;;
samsung) Brand_URL="$URL/samsung_cn.md" ;;
samsung_global) Brand_URL="$URL/samsung_global_en.md" ;;
asus) Brand_URL="$URL/asus.md" ;;
Black_Shark) Brand_URL="$URL/blackshark.md" ;;
Black_Shark_en) Brand_URL="$URL/blackshark_en.md" ;;
google) Brand_URL="$URL/google.md" ;;
Lenovo) Brand_URL="$URL/lenovo.md" ;;
MEIZU) Brand_URL="$URL/meizu.md" ;;
MEIZU_en) Brand_URL="$URL/meizu_en.md" ;;
Motorola) Brand_URL="$URL/motorola.md" ;;
Nokia) Brand_URL="$URL/nokia.md" ;;
nothing) Brand_URL="$URL/nothing.md" ;;
nubia) Brand_URL="$URL/nubia.md" ;;
OnePlus) Brand_URL="$URL/oneplus.md" ;;
OnePlus_en) Brand_URL="$URL/oneplus_en.md" ;;
Sony) Brand_URL="$URL/sony_cn.md" ;;
realme) Brand_URL="$URL/realme_cn.md" ;;
realme_en) Brand_URL="$URL/realme_global_en.md" ;;
vivo) Brand_URL="$URL/vivo_cn.md" ;;
vivo_en) Brand_URL="$URL/vivo_global_en.md" ;;
oppo) Brand_URL="$URL/oppo_cn.md" ;;
oppo_en) Brand_URL="$URL/oppo_global_en.md" ;;
esac
if [[ ! -e $tools_path/Device_List ]]; then
down "$Brand_URL" | grep -oE '`[^`]+`:[^`]*' | sed -E 's/: /:/g' | sed -E 's/`([^`]+)`:(.*)/"\1" "\2"/'>"$tools_path/Device_List"
else
down "$Brand_URL" | grep -oE '`[^`]+`:[^`]*' | sed -E 's/: /:/g' | sed -E 's/`([^`]+)`:(.*)/"\1" "\2"/' | while read ; do
unset model
model="$(echo "$REPLY" | awk -F'"' '{print $2}')"
if [[ $(egrep -w "$model" "$tools_path/Device_List" | awk -F'"' '{print $2}') != $model ]]; then
echo "$REPLY">>"$tools_path/Device_List"
else
echo "$(egrep -w "$model" "$tools_path/Device_List" | awk -F'"' '{print $2}') = $model"
fi
done
fi
done
if [[ -e $tools_path/Device_List ]]; then
if [[ $(ls -l "$tools_path/Device_List" | awk '{print $5}') -gt 1 ]]; then
[[ $shell_language = zh-TW ]] && ts <"$tools_path/Device_List">temp && cp temp "$tools_path/Device_List" && rm temp
echoRgb "已下載機型列表在$tools_path/Device_List"
else
echoRgb "下載機型失敗"
fi
else
echoRgb "下載機型失敗"
fi
}
wifi() {
backup_path
[[ ! -d $Backup/tools ]] && cp -r "$tools_path" "$Backup"
[[ ! -f $Backup/start.sh ]] && touch_shell "2" "$Backup/start.sh"
[[ ! -f $Backup/restore_settings.conf ]] && update_Restore_settings_conf>"$Backup/restore_settings.conf"
backup_wifi "$Backup/wifi"
}
dumpname() {
get_name "Apkname"
}
convert() {
get_name "convert"
}
check_file() {
Check_archive "$MODDIR"
}

Binary file not shown.

BIN
tools/jq

Binary file not shown.

Binary file not shown.

View File

@@ -66,7 +66,6 @@
"MSM8930AB": {"VENDOR":"Qualcomm®","NAME":"Snapdragon™ 400","MEMORY":"","CHANNELS":""},
"SXR2230P": {"VENDOR":"Qualcomm®","NAME":"Snapdragon™ XR2 Gen 2","MEMORY":"","CHANNELS":""},
"QCM6490": {"VENDOR":"Qualcomm®","NAME":"Snapdragon™ QCM6490","MEMORY":"","CHANNELS":""},
"sm-sip1": {"VENDOR":"Qualcomm®","NAME":"Snapdragon™ Sip 1","MEMORY":"","CHANNELS":""},
"MSM8953": {"VENDOR":"Qualcomm®","NAME":"Snapdragon™ 625","MEMORY":"LPDDR3 933 MHz","CHANNELS":"single channel"},
"SM4450": {"VENDOR":"Qualcomm®","NAME":"Snapdragon™ 4 Gen 2","MEMORY":"","CHANNELS":""},
"SM4450A": {"VENDOR":"Qualcomm®","NAME":"Snapdragon™ 4 Gen 2 Accelerated Edition","MEMORY":"","CHANNELS":""},
@@ -347,8 +346,6 @@
"MT6985": {"VENDOR":"MediaTek","NAME":"Dimensity 9200+","MEMORY":"LPDDR5X","CHANNELS":""},
"MT6993W/CZA": {"VENDOR":"MediaTek","NAME":"Dimensity 9500","MEMORY":"","CHANNELS":""},
"MT6993": {"VENDOR":"MediaTek","NAME":"Dimensity 9500","MEMORY":"","CHANNELS":""},
"MT6899Z_T/TZA": {"VENDOR":"MediaTek","NAME":"Dimensity 8550 SUPER","MEMORY":"","CHANNELS":""},
"MT6881V/ZA": {"VENDOR":"MediaTek","NAME":"Dimensity 7500","MEMORY":"","CHANNELS":""},
"MT6991Z/ECZB": {"VENDOR":"MediaTek","NAME":"Dimensity 9500s","MEMORY":"","CHANNELS":""},
"MT6991Z/SCZB": {"VENDOR":"MediaTek","NAME":"Dimensity 9500s","MEMORY":"","CHANNELS":""},
"MT6991Z/TCZB": {"VENDOR":"MediaTek","NAME":"Dimensity 9400+","MEMORY":"","CHANNELS":""},
@@ -450,11 +447,8 @@
"MT6878V_H/ZA": {"VENDOR":"MediaTek","NAME":"Dimensity 7360-Apex","MEMORY":"","CHANNELS":""},
"MT6878V/FZA": {"VENDOR":"MediaTek","NAME":"Dimensity 7300X","MEMORY":"","CHANNELS":""},
"MT6878V_A/ZA": {"VENDOR":"MediaTek","NAME":"Dimensity 7300-ENERGY","MEMORY":"","CHANNELS":""},
"MT6878V_I/TZA": {"VENDOR":"MediaTek","NAME":"Dimensity 7400-SUPER","MEMORY":"","CHANNELS":""},
"MT6878V_I/ZA": {"VENDOR":"MediaTek","NAME":"Dimensity 7360-SUPER","MEMORY":"","CHANNELS":""},
"MT6878": {"VENDOR":"MediaTek","NAME":"Dimensity 7300 Ultra","MEMORY":"","CHANNELS":""},
"MT6858V/ZA": {"VENDOR":"MediaTek","NAME":"Dimensity 7100","MEMORY":"","CHANNELS":""},
"MT6858V_A/ZA": {"VENDOR":"MediaTek","NAME":"Dimensity 7100 Ultimate","MEMORY":"","CHANNELS":""},
"MT6858V/TZA": {"VENDOR":"MediaTek","NAME":"Dimensity 7300e","MEMORY":"","CHANNELS":""},
"MT6855V/ATZA": {"VENDOR":"MediaTek","NAME":"Dimensity 7025","MEMORY":"","CHANNELS":""},
"MT6855V_A/ATZA": {"VENDOR":"MediaTek","NAME":"Dimensity 7025 Ultra","MEMORY":"","CHANNELS":""},
@@ -549,7 +543,7 @@
"MT6789": {"VENDOR":"MediaTek","NAME":"Helio G99","MEMORY":"","CHANNELS":""},
"mt6789": {"VENDOR":"MediaTek","NAME":"Helio G99","MEMORY":"","CHANNELS":""},
"mt5867": {"VENDOR":"MediaTek","NAME":"MT5867","MEMORY":"","CHANNELS":""},
"MT6769I": {"VENDOR":"MediaTek","NAME":"Helio G92 Max","MEMORY":"","CHANNELS":""},
"MT6769I": {"VENDOR":"MediaTek","NAME":"Helio G92","MEMORY":"","CHANNELS":""},
"MT6769L": {"VENDOR":"MediaTek","NAME":"Helio G81 Extreme","MEMORY":"","CHANNELS":""},
"MT6785": {"VENDOR":"MediaTek","NAME":"Helio G90 series","MEMORY":"LPDDR4x 2133MHz","CHANNELS":"Dual channel"},
"MT6769H": {"VENDOR":"MediaTek","NAME":"Helio G88","MEMORY":"LPDDR4X 1800MHz","CHANNELS":"Dual channel"},
@@ -631,7 +625,7 @@
"MT8768CT": {"VENDOR":"MediaTek","NAME":"MT8768CT","MEMORY":"","CHANNELS":""},
"MT6762": {"VENDOR":"MediaTek","NAME":"Helio P22","MEMORY":"","CHANNELS":""},
"mt6762": {"VENDOR":"MediaTek","NAME":"Helio P22","MEMORY":"","CHANNELS":""},
"MT8766B": {"VENDOR":"MediaTek","NAME":"Helio A22","MEMORY":"","CHANNELS":""},
"MT8766B": {"VENDOR":"MediaTek","NAME":"MT8766B","MEMORY":"","CHANNELS":""},
"MT8768WT": {"VENDOR":"MediaTek","NAME":"Helio P22T","MEMORY":"","CHANNELS":""},
"MT8768T": {"VENDOR":"MediaTek","NAME":"Helio P22T","MEMORY":"","CHANNELS":""},
"MT8735P": {"VENDOR":"MediaTek","NAME":"MT8735P","MEMORY":"LPDDR3","CHANNELS":""},
@@ -875,11 +869,9 @@
"T770": {"VENDOR":"Unisoc","NAME":"T770","MEMORY":"","CHANNELS":""},
"T7200": {"VENDOR":"Unisoc","NAME":"T7200","MEMORY":"","CHANNELS":""},
"ums512": {"VENDOR":"Unisoc","NAME":"T610","MEMORY":"","CHANNELS":""},
"ums312": {"VENDOR":"Unisoc","NAME":"T310","MEMORY":"","CHANNELS":""},
"UMS312": {"VENDOR":"Unisoc","NAME":"T310","MEMORY":"","CHANNELS":""},
"UMS9230T": {"VENDOR":"Unisoc","NAME":"T7225","MEMORY":"","CHANNELS":""},
"UMS9230E": {"VENDOR":"Unisoc","NAME":"T7200","MEMORY":"","CHANNELS":""},
"ums312": {"VENDOR":"Unisoc","NAME":"","MEMORY":"","CHANNELS":""},
"ums9230": {"VENDOR":"Unisoc","NAME":"T7200","MEMORY":"","CHANNELS":""},
"UMS9230E": {"VENDOR":"Unisoc","NAME":"T7200","MEMORY":"","CHANNELS":""},
"UMS9230": {"VENDOR":"Unisoc","NAME":"T7200","MEMORY":"","CHANNELS":""},
"SC9832E": {"VENDOR":"Unisoc","NAME":"SC9832E","MEMORY":"","CHANNELS":""},
"sc9832a": {"VENDOR":"Unisoc","NAME":"SC9832A","MEMORY":"","CHANNELS":""},

BIN
tools/tar

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.