BackupManager: create backup zip on api < 30

This commit is contained in:
Yuriy Liskov
2026-03-09 01:21:43 +02:00
parent 5dc73fcf92
commit 173d1c2870
4 changed files with 96 additions and 76 deletions

View File

@@ -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()) {

View File

@@ -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<File> 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<String> 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);
}
}
}

View File

@@ -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<Void> 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) {

View File

@@ -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(); }
}
}