From 173d1c28705707e0292e8115eef39a9a37e93631 Mon Sep 17 00:00:00 2001 From: Yuriy Liskov Date: Mon, 9 Mar 2026 01:21:43 +0200 Subject: [PATCH] BackupManager: create backup zip on api < 30 --- .../common/misc/BackupAndRestoreHelper.java | 72 ++++--------------- .../common/misc/BackupAndRestoreManager.java | 27 ++++--- .../common/misc/GDriveBackupManager.java | 13 ++-- .../common/misc/ZipHelper2.java | 60 ++++++++++++++++ 4 files changed, 96 insertions(+), 76 deletions(-) create mode 100644 common/src/main/java/com/liskovsoft/smartyoutubetv2/common/misc/ZipHelper2.java diff --git a/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/misc/BackupAndRestoreHelper.java b/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/misc/BackupAndRestoreHelper.java index fcd58b908..f81180a01 100644 --- a/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/misc/BackupAndRestoreHelper.java +++ b/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/misc/BackupAndRestoreHelper.java @@ -17,18 +17,20 @@ import com.liskovsoft.smartyoutubetv2.common.app.presenters.settings.BackupSetti import com.liskovsoft.smartyoutubetv2.common.misc.MotherActivity.OnResult; import java.io.File; -import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; -import java.util.zip.ZipOutputStream; public class BackupAndRestoreHelper implements OnResult { private static final int REQ_PICK_FILES = 1001; private final Context mContext; private Runnable mOnSuccess; + private final String[] mBackupPatterns = new String[] { + "yt_service_prefs.xml", + "com.liskovsoft.appupdatechecker2.preferences.xml", + "com.liskovsoft.sharedutils.prefs.GlobalPreferences.xml", + "_preferences.xml" // before _ should be the app package name + }; public BackupAndRestoreHelper(Context context) { mContext = context; @@ -44,7 +46,7 @@ public class BackupAndRestoreHelper implements OnResult { if (!dataDir.exists()) return; File zipFile = new File(mediaDir, "backup_" + mContext.getPackageName() + ".zip"); - zipDirectory(dataDir, zipFile); + ZipHelper2.zipDirectory(dataDir, zipFile); Uri uri = FileProvider.getUriForFile( mContext, @@ -99,7 +101,7 @@ public class BackupAndRestoreHelper implements OnResult { deleteRecursive(dataDir); dataDir.mkdirs(); - unzip(zipFile, mediaDir); + ZipHelper2.unzip(zipFile, mediaDir); zipFile.delete(); @@ -129,11 +131,11 @@ public class BackupAndRestoreHelper implements OnResult { copyUriToFile(zipUri, tempZip); // Unpack ZIP with data folder - unzip(tempZip, mediaDir); + ZipHelper2.unzip(tempZip, mediaDir); if (FileHelpers.isEmpty(dataDir)) { // Seems we've packed the contents of the data dir not data itself - unzip(tempZip, dataDir); + ZipHelper2.unzip(tempZip, dataDir); } // Delete the temporary ZIP @@ -150,6 +152,10 @@ public class BackupAndRestoreHelper implements OnResult { } } + public String[] getBackupPatterns() { + return mBackupPatterns; + } + private void copyUriToDir(Uri uri, File targetDir) { try { String fileName = getFileName(uri); @@ -222,56 +228,6 @@ public class BackupAndRestoreHelper implements OnResult { return result; } - private void zipDirectory(File sourceDir, File zipFile) { - try { - ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile)); - zipFileRecursive(zos, sourceDir, "data/"); - zos.close(); - } catch (Exception e) { e.printStackTrace(); } - } - - private void zipFileRecursive(ZipOutputStream zos, File file, String base) throws Exception { - if (file.isDirectory()) { - File[] children = file.listFiles(); - if (children != null) { - for (File child : children) { - zipFileRecursive(zos, child, base + child.getName() + "/"); - } - } - } else { - FileInputStream fis = new FileInputStream(file); - zos.putNextEntry(new ZipEntry(base.substring(0, base.length() -1))); // strip "/" at the end to mark as file - byte[] buf = new byte[8192]; - int len; - while ((len = fis.read(buf)) > 0) zos.write(buf, 0, len); - fis.close(); - zos.closeEntry(); - } - } - - private void unzip(File zipFile, File targetRoot) { - try { - ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile)); - ZipEntry entry; - byte[] buffer = new byte[8192]; - - while ((entry = zis.getNextEntry()) != null) { - File out = new File(targetRoot, entry.getName()); - if (entry.isDirectory()) { - out.mkdirs(); - } else { - out.getParentFile().mkdirs(); - FileOutputStream fos = new FileOutputStream(out); - int len; - while ((len = zis.read(buffer)) > 0) fos.write(buffer, 0, len); - fos.close(); - } - zis.closeEntry(); - } - zis.close(); - } catch (Exception e) { e.printStackTrace(); } - } - private void deleteRecursive(File f) { if (!f.exists()) return; if (f.isDirectory()) { diff --git a/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/misc/BackupAndRestoreManager.java b/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/misc/BackupAndRestoreManager.java index 2715f7d9b..0e1673a7b 100644 --- a/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/misc/BackupAndRestoreManager.java +++ b/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/misc/BackupAndRestoreManager.java @@ -12,6 +12,7 @@ import com.liskovsoft.sharedutils.helpers.Helpers; import com.liskovsoft.sharedutils.helpers.MessageHelpers; import com.liskovsoft.sharedutils.helpers.PermissionHelpers; import com.liskovsoft.sharedutils.mylogger.Log; +import com.liskovsoft.sharedutils.rx.RxHelper; import com.liskovsoft.smartyoutubetv2.common.R; import com.liskovsoft.smartyoutubetv2.common.prefs.HiddenPrefs; @@ -20,6 +21,8 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import io.reactivex.disposables.Disposable; + public class BackupAndRestoreManager implements MotherActivity.OnPermissions { private static final String TAG = BackupAndRestoreManager.class.getSimpleName(); private static final String BACKUP_DIR_NAME = "Backup"; @@ -29,13 +32,8 @@ public class BackupAndRestoreManager implements MotherActivity.OnPermissions { private final List mBackupDirs; private final BackupAndRestoreHelper mHelper; private final boolean mForceApi30; - private final String[] mBackupPatterns = new String[] { - "yt_service_prefs.xml", - "com.liskovsoft.appupdatechecker2.preferences.xml", - "com.liskovsoft.sharedutils.prefs.GlobalPreferences.xml", - "_preferences.xml" // before _ should be the app package name - }; private Runnable mPendingHandler; + private Disposable mZipAction; public interface OnBackupNames { void onBackupNames(List backupNames); @@ -90,7 +88,6 @@ public class BackupAndRestoreManager implements MotherActivity.OnPermissions { return new File(appDir, BACKUP_DIR_NAME); } - @SuppressWarnings("deprecation") private File getExternalStorageDirectory() { File result; @@ -158,7 +155,7 @@ public class BackupAndRestoreManager implements MotherActivity.OnPermissions { if (mDataDir.isDirectory() && !FileHelpers.isEmpty(mDataDir)) { File destination = new File(currentBackup, mDataDir.getName()); - FileHelpers.copy(mDataDir, destination, fileName -> Helpers.endsWithAny(fileName.toString(), mBackupPatterns)); + FileHelpers.copy(mDataDir, destination, fileName -> Helpers.endsWithAny(fileName.toString(), mHelper.getBackupPatterns())); // Don't store unique id FileHelpers.delete(new File(destination, HiddenPrefs.SHARED_PREFERENCES_NAME + ".xml")); @@ -166,6 +163,9 @@ public class BackupAndRestoreManager implements MotherActivity.OnPermissions { if (hasAccessOnlyToAppFolders()) { mHelper.exportAppMediaFolder(); + } else { + RxHelper.disposeActions(mZipAction); + mZipAction = RxHelper.runAsync(this::saveDataToZip); } } @@ -186,7 +186,7 @@ public class BackupAndRestoreManager implements MotherActivity.OnPermissions { FileHelpers.delete(mDataDir); } - FileHelpers.copy(sourceBackupDir, mDataDir, fileName -> Helpers.endsWithAny(fileName.toString(), mBackupPatterns)); + FileHelpers.copy(sourceBackupDir, mDataDir, fileName -> Helpers.endsWithAny(fileName.toString(), mHelper.getBackupPatterns())); fixFileNames(mDataDir); MessageHelpers.showMessage(mContext, R.string.msg_done); @@ -342,4 +342,13 @@ public class BackupAndRestoreManager implements MotherActivity.OnPermissions { private boolean hasAccessOnlyToAppFolders() { return AppInfoHelpers.getTargetSdkVersion(mContext) > 29 || mForceApi30; } + + private void saveDataToZip() { + File mediaDir = getExternalStorageDirectory(); + File dataDir = new File(mediaDir, "data"); + if (dataDir.exists()) { + File zipFile = new File(mediaDir, "SmartTubeBackup.zip"); + ZipHelper2.zipDirectory(dataDir, zipFile); + } + } } diff --git a/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/misc/GDriveBackupManager.java b/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/misc/GDriveBackupManager.java index 8fdb0af65..ee7bbe1cd 100644 --- a/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/misc/GDriveBackupManager.java +++ b/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/misc/GDriveBackupManager.java @@ -39,13 +39,13 @@ public class GDriveBackupManager { private static final String SHARED_PREFS_SUBDIR = "shared_prefs"; private static final String BACKUP_NAME = "backup.zip"; private final GoogleSignInService mSignInService; + private final BackupAndRestoreHelper mHelper; private final String mDataDir; private final String mBackupDir; private final String mRootBackupDir; private final GeneralData mGeneralData; private Disposable mBackupAction; private Disposable mRestoreAction; - private final String[] mBackupNames; private boolean mIsBlocking; private GDriveBackupManager(Context context) { @@ -55,12 +55,7 @@ public class GDriveBackupManager { mBackupDir = String.format("SmartTubeBackup/%s", context.getPackageName()); mRootBackupDir = "SmartTubeBackup"; mSignInService = GoogleSignInService.instance(); - mBackupNames = new String[] { - "yt_service_prefs.xml", - "com.liskovsoft.appupdatechecker2.preferences.xml", - "com.liskovsoft.sharedutils.prefs.GlobalPreferences.xml", - "_preferences.xml" // before _ should be the app package name - }; + mHelper = new BackupAndRestoreHelper(context); } public static GDriveBackupManager instance(Context context) { @@ -156,7 +151,7 @@ public class GDriveBackupManager { private void startBackup(String backupDir, String dataDir) { File source = new File(dataDir); File zipFile = new File(mContext.getCacheDir(), BACKUP_NAME); - ZipHelper.zipFolder(source, zipFile, mBackupNames); + ZipHelper.zipFolder(source, zipFile, mHelper.getBackupPatterns()); Observable uploadFile = DriveService.uploadFile(zipFile, Uri.parse(String.format("%s/%s", backupDir, BACKUP_NAME))); @@ -249,7 +244,7 @@ public class GDriveBackupManager { } private boolean checkFileName(String name) { - return Helpers.endsWithAny(name, mBackupNames); + return Helpers.endsWithAny(name, mHelper.getBackupPatterns()); } private String fixAltPackageName(String name) { diff --git a/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/misc/ZipHelper2.java b/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/misc/ZipHelper2.java new file mode 100644 index 000000000..23910e8b0 --- /dev/null +++ b/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/misc/ZipHelper2.java @@ -0,0 +1,60 @@ +package com.liskovsoft.smartyoutubetv2.common.misc; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +public class ZipHelper2 { + public static void zipDirectory(File sourceDir, File zipFile) { + try { + ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile)); + zipFileRecursive(zos, sourceDir, sourceDir.getName() + "/"); + zos.close(); + } catch (Exception e) { e.printStackTrace(); } + } + + private static void zipFileRecursive(ZipOutputStream zos, File file, String base) throws Exception { + if (file.isDirectory()) { + File[] children = file.listFiles(); + if (children != null) { + for (File child : children) { + zipFileRecursive(zos, child, base + child.getName() + "/"); + } + } + } else { + FileInputStream fis = new FileInputStream(file); + zos.putNextEntry(new ZipEntry(base.substring(0, base.length() -1))); // strip "/" at the end to mark as file + byte[] buf = new byte[8192]; + int len; + while ((len = fis.read(buf)) > 0) zos.write(buf, 0, len); + fis.close(); + zos.closeEntry(); + } + } + + public static void unzip(File zipFile, File targetRoot) { + try { + ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile)); + ZipEntry entry; + byte[] buffer = new byte[8192]; + + while ((entry = zis.getNextEntry()) != null) { + File out = new File(targetRoot, entry.getName()); + if (entry.isDirectory()) { + out.mkdirs(); + } else { + out.getParentFile().mkdirs(); + FileOutputStream fos = new FileOutputStream(out); + int len; + while ((len = zis.read(buffer)) > 0) fos.write(buffer, 0, len); + fos.close(); + } + zis.closeEntry(); + } + zis.close(); + } catch (Exception e) { e.printStackTrace(); } + } +}