11package com.v2ray.ang.ui
22
33import android.content.Intent
4+ import android.net.Uri
45import android.os.Bundle
56import androidx.core.content.FileProvider
67import androidx.lifecycle.lifecycleScope
@@ -98,7 +99,7 @@ class BackupActivity : HelperBaseActivity() {
9899 }
99100
100101 /* *
101- * Backup configuration to cache directory
102+ * Backup configuration to cache directory.
102103 * Returns Pair<success, zipFilePath>
103104 */
104105 private fun backupConfigurationToCache (): Pair <Boolean , String > {
@@ -115,13 +116,50 @@ class BackupActivity : HelperBaseActivity() {
115116 return Pair (false , " " )
116117 }
117118
119+ // Backup custom banner image files alongside MMKV data
120+ backupBannerImages(backupDir)
121+
118122 return if (ZipUtil .zipFromFolder(backupDir, outputZipFilePath)) {
119123 Pair (true , outputZipFilePath)
120124 } else {
121125 Pair (false , " " )
122126 }
123127 }
124128
129+ /* *
130+ * Copy banner image files into the backup directory.
131+ * Each banner is saved as "banners/<key>.jpg" so restore can find them by key.
132+ */
133+ private fun backupBannerImages (backupDir : String ) {
134+ val bannerKeys = listOf (
135+ AppConfig .PREF_CUSTOM_HOME_BANNER_URI ,
136+ AppConfig .PREF_CUSTOM_SHEET_BANNER_URI ,
137+ AppConfig .PREF_PROFILE_BANNER_URI ,
138+ )
139+ val bannersDir = java.io.File (backupDir, " banners" ).also { it.mkdirs() }
140+ for (key in bannerKeys) {
141+ val uriString = MmkvManager .decodeSettingsString(key) ? : continue
142+ if (uriString.isBlank()) continue
143+ try {
144+ val uri = Uri .parse(uriString)
145+ val srcFile = if (uri.scheme == " file" ) {
146+ java.io.File (uri.path!! )
147+ } else {
148+ val tmp = java.io.File (cacheDir, " banner_backup_tmp_${key} .jpg" )
149+ contentResolver.openInputStream(uri)?.use { input ->
150+ tmp.outputStream().use { input.copyTo(it) }
151+ }
152+ tmp
153+ }
154+ if (srcFile.exists()) {
155+ srcFile.copyTo(java.io.File (bannersDir, " $key .jpg" ), overwrite = true )
156+ }
157+ } catch (e: Exception ) {
158+ LogUtil .e(AppConfig .TAG , " Failed to backup banner for key $key " , e)
159+ }
160+ }
161+ }
162+
125163 private fun restoreConfiguration (zipFile : File ): Boolean {
126164 val backupDir = this .cacheDir.absolutePath + " /${System .currentTimeMillis()} "
127165
@@ -133,10 +171,44 @@ class BackupActivity : HelperBaseActivity() {
133171 SettingsChangeManager .makeSetupGroupTab()
134172 SettingsChangeManager .makeRestartService()
135173
174+ // Restore custom banner image files and fix their paths in MMKV
175+ restoreBannerImages(backupDir)
176+
136177 SettingsManager .initApp(this )
137178 return count > 0
138179 }
139180
181+ /* *
182+ * Copy banner image files from the backup dir into cacheDir,
183+ * then update each URI key in MMKV to the new path.
184+ */
185+ private fun restoreBannerImages (backupDir : String ) {
186+ val bannerKeys = listOf (
187+ AppConfig .PREF_CUSTOM_HOME_BANNER_URI ,
188+ AppConfig .PREF_CUSTOM_SHEET_BANNER_URI ,
189+ AppConfig .PREF_PROFILE_BANNER_URI ,
190+ )
191+ val bannersDir = java.io.File (backupDir, " banners" )
192+ if (! bannersDir.exists()) return
193+
194+ for (key in bannerKeys) {
195+ val srcFile = java.io.File (bannersDir, " $key .jpg" )
196+ if (! srcFile.exists()) {
197+ // No backup for this banner — clear the stale URI so we don't point to a missing file
198+ MmkvManager .encodeSettings(key, " " )
199+ continue
200+ }
201+ try {
202+ val destFile = java.io.File (cacheDir, " ${key} _${System .currentTimeMillis()} .jpg" )
203+ srcFile.copyTo(destFile, overwrite = true )
204+ MmkvManager .encodeSettings(key, Uri .fromFile(destFile).toString())
205+ } catch (e: Exception ) {
206+ LogUtil .e(AppConfig .TAG , " Failed to restore banner for key $key " , e)
207+ MmkvManager .encodeSettings(key, " " )
208+ }
209+ }
210+ }
211+
140212 private fun showFileChooser () {
141213 launchFileChooser { uri ->
142214 if (uri == null ) {
0 commit comments