package co.smartreceipts.android.sync.manual; import android.net.Uri; import android.support.annotation.NonNull; import com.google.common.base.Preconditions; import java.io.File; import java.io.FileFilter; import java.io.IOException; import co.smartreceipts.android.date.DateUtils; import co.smartreceipts.android.persistence.DatabaseHelper; import co.smartreceipts.android.persistence.PersistenceManager; import co.smartreceipts.android.utils.log.Logger; import io.reactivex.Scheduler; import io.reactivex.Single; import io.reactivex.schedulers.Schedulers; import io.reactivex.subjects.ReplaySubject; import wb.android.storage.SDCardStateException; import wb.android.storage.StorageManager; public class ManualBackupTask { public static final String DATABASE_EXPORT_NAME = "receipts_backup.db"; private static final String EXPORT_FILENAME = DateUtils.getCurrentDateAsYYYY_MM_DDString() + "_SmartReceipts.smr"; private static final String DATABASE_JOURNAL = "receipts.db-journal"; private final PersistenceManager mPersistenceManager; private final Scheduler mObserveOnScheduler; private final Scheduler mSubscribeOnScheduler; private ReplaySubject<Uri> mBackupBehaviorSubject; ManualBackupTask(@NonNull PersistenceManager persistenceManager) { this(persistenceManager, Schedulers.io(), Schedulers.io()); } ManualBackupTask(@NonNull PersistenceManager persistenceManager, @NonNull Scheduler observeOnScheduler, @NonNull Scheduler subscribeOnScheduler) { mPersistenceManager = Preconditions.checkNotNull(persistenceManager); mObserveOnScheduler = Preconditions.checkNotNull(observeOnScheduler); mSubscribeOnScheduler = Preconditions.checkNotNull(subscribeOnScheduler); } @NonNull public synchronized ReplaySubject<Uri> backupData() { if (mBackupBehaviorSubject == null) { mBackupBehaviorSubject = ReplaySubject.create(); backupDataToSingle() .observeOn(mObserveOnScheduler) .subscribeOn(mSubscribeOnScheduler) .toObservable() .subscribe(mBackupBehaviorSubject); } return mBackupBehaviorSubject; } @NonNull private Single<Uri> backupDataToSingle() { return Single.create(emitter -> { try { final StorageManager external = mPersistenceManager.getExternalStorageManager(); final StorageManager internal = mPersistenceManager.getInternalStorageManager(); external.delete(external.getFile(EXPORT_FILENAME)); //Remove old export external.copy(external.getFile(DatabaseHelper.DATABASE_NAME), external.getFile(DATABASE_EXPORT_NAME), true); final File prefs = internal.getFile(internal.getRoot().getParentFile(), "shared_prefs"); //Preferences File if (prefs != null && prefs.exists()) { File sdPrefs = external.getFile("shared_prefs"); Logger.debug(ManualBackupTask.this, "Copying the prefs file from: {} to {}", prefs.getAbsolutePath(), sdPrefs.getAbsolutePath()); try { external.copy(prefs, sdPrefs, true); } catch (IOException e) { Logger.error(this, e); } } //Internal Files final File[] internalFiles = internal.listFilesAndDirectories(); if (internalFiles != null && internalFiles.length > 0) { Logger.debug(ManualBackupTask.this, "Copying {} files/directories to the SD Card.", internalFiles.length); final File internalOnSD = external.mkdir("Internal"); internal.copy(internalOnSD, true); } //Finish File zip = external.zipBuffered(8192, new BackupFileFilter()); zip = external.rename(zip, EXPORT_FILENAME); emitter.onSuccess(Uri.fromFile(zip)); } catch (IOException | SDCardStateException e) { Logger.error(this, e); emitter.onError(e); } }); } private static final class BackupFileFilter implements FileFilter { @Override public boolean accept(File file) { return !file.getName().equalsIgnoreCase(DatabaseHelper.DATABASE_NAME) && !file.getName().equalsIgnoreCase(DATABASE_JOURNAL) && !file.getName().endsWith(".smr"); //Ignore previous backups } } }