package com.datonicgroup.narrate.app.dataprovider; import android.accounts.Account; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Environment; import android.preference.PreferenceManager; import com.datonicgroup.narrate.app.BuildConfig; import com.datonicgroup.narrate.app.dataprovider.providers.Contract; import com.datonicgroup.narrate.app.dataprovider.providers.EntryHelper; import com.datonicgroup.narrate.app.dataprovider.providers.PhotosDao; import com.datonicgroup.narrate.app.dataprovider.receivers.AlarmReceiver; import com.datonicgroup.narrate.app.dataprovider.sync.AbsSyncService; import com.datonicgroup.narrate.app.dataprovider.sync.SyncHelper; import com.datonicgroup.narrate.app.dataprovider.sync.SyncInfoManager; import com.datonicgroup.narrate.app.models.Entry; import com.datonicgroup.narrate.app.models.Photo; import com.datonicgroup.narrate.app.models.SyncStatus; import com.datonicgroup.narrate.app.models.User; import com.datonicgroup.narrate.app.ui.GlobalApplication; import com.datonicgroup.narrate.app.util.DateUtil; import com.datonicgroup.narrate.app.util.FileUtil; import com.datonicgroup.narrate.app.util.LogUtil; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.nio.channels.FileChannel; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Locale; /** * Created by timothymiko on 12/12/14. */ public class LocalBackupManager { public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy_MM_dd-HH_mm"); public static void setEnabled(boolean enabled) { if (enabled) enableBackup(); else disableBackup(); } private static void enableBackup() { Intent intent = new Intent("NARRATE_BACKUP"); intent.setClass(GlobalApplication.getAppContext(), AlarmReceiver.class); PendingIntent pendingIntent = PendingIntent.getBroadcast(GlobalApplication.getAppContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); AlarmManager am = (AlarmManager) GlobalApplication.getAppContext().getSystemService(GlobalApplication.getAppContext().ALARM_SERVICE); int interval = Settings.getLocalBackupFrequency(); if ( interval > -1 ) { long millis = 0; switch (interval) { case 0: millis = DateUtil.DAY_IN_MILLISECONDS; break; case 1: millis = DateUtil.WEEK_IN_MILLISECONDS; break; case 2: millis = DateUtil.WEEK_IN_MILLISECONDS * 4; break; } // delay things by a second am.setRepeating(AlarmManager.RTC, Calendar.getInstance().getTimeInMillis() + (30 * DateUtil.SECOND_IN_MILLISECONDS), millis, pendingIntent); } } private static void disableBackup() { AlarmManager am = (AlarmManager) GlobalApplication.getAppContext().getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent("NARRATE_BACKUP"); intent.setClass(GlobalApplication.getAppContext(), AlarmReceiver.class); PendingIntent reminderNotificationIntent = PendingIntent.getBroadcast(GlobalApplication.getAppContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); am.cancel(reminderNotificationIntent); } public static void backup() { new Thread() { @Override public void run() { super.run(); LogUtil.log("NarrateBackups", "Beginning local backup of Narrate's data."); File sdcard = Environment.getExternalStorageDirectory(); // create 'Narrate Backups' folder File backup = new File(sdcard, "/Narrate/"); // create .pendingbackup folder try { // don't keep more than 5 backups File[] backups = backup.listFiles(); if ( backups != null && backups.length > 0 ) { List<File> backupList = new ArrayList<File>(); for (int i = 0; i < backups.length; i++) backupList.add(backups[i]); // sort oldest to newest Collections.sort(backupList, new Comparator<File>() { @Override public int compare(File lhs, File rhs) { return lhs.getName().compareTo(rhs.getName()); } }); // delete the oldest backups for (int i = 0; i <= backupList.size()-Settings.getLocalBackupsToKeep(); i++) backupList.get(i).delete(); } File pendingFolder = new File(backup, "pendingbackup"); // create entries folder File entries = new File(pendingFolder, "entries"); entries.mkdirs(); // create photos folder File photos = new File(pendingFolder, "photos"); photos.mkdirs(); // save all entries List<Entry> entryData = EntryHelper.getAllEntries(); for (int i = 0; i < entryData.size(); i++) { Entry e = entryData.get(i); String data = EntryHelper.toJson(e); File file = new File(entries, e.uuid); FileOutputStream os = new FileOutputStream(file); PrintWriter writer = new PrintWriter(os); writer.write(data); writer.close(); writer.flush(); os.close(); } // copy all photos File[] photoData = PhotosDao.getPhotosFolder().listFiles(); for (int i = 0; i < photoData.length; i++) { File destFile = new File(photos, photoData[i].getName()); FileChannel source = null; FileChannel destination = null; try { source = new FileInputStream(photoData[i]).getChannel(); destination = new FileOutputStream(destFile).getChannel(); destination.transferFrom(source, 0, source.size()); } finally { if (source != null) source.close(); if (destination != null) destination.close(); } } // compress and rename String name = "backup-" + DATE_FORMAT.format(Calendar.getInstance(Locale.getDefault()).getTime()); File result = new File(backup, name); FileUtil.zipFileAtPath(pendingFolder, result); // we are finished, delete the .pendingbackup folder FileUtil.deleteDirectory(pendingFolder); LogUtil.log("NarrateBackups", "Narrate local backup complete!"); } catch (Exception e) { LogUtil.e("NarrateBackups", "Narrate local backup failed."); e.printStackTrace(); } } }.start(); } public static void restore(File file) { LogUtil.log("NarrateBackups", "Beginning restore from local backup."); // stop any active sync processes SyncHelper.cancelPendingActiveSync(User.getAccount()); // disable syncing for now ContentResolver.setSyncAutomatically(User.getAccount(), Contract.AUTHORITY, false); // delete all data on remote clients List<AbsSyncService> syncServices; if ((syncServices = SyncHelper.getSyncServices()).size() > 0) { Iterator<AbsSyncService> it = syncServices.iterator(); while (it.hasNext()) { it.next().deleteEverything(); } } File sdcard = Environment.getExternalStorageDirectory(); // create 'Narrate Backups' folder File backup = new File(sdcard, "/Narrate/"); File dest = new File(backup, "pendingRestore"); FileUtil.unzip(file, dest.getAbsolutePath()); File pendingRestore = new File(dest, "pendingBackup"); // delete old photos File photosFolder = PhotosDao.getPhotosFolder(); File[] existing = photosFolder.listFiles(); if ( existing != null ) for (int i = 0; i < existing.length; i++) existing[i].delete(); // copy new photos File[] photos = new File(pendingRestore, "photos").listFiles(); if ( photos != null ) { for (int i = 0; i < photos.length; i++) { File destFile = new File(photosFolder, photos[i].getName()); FileChannel source = null; FileChannel destination = null; try { source = new FileInputStream(photos[i]).getChannel(); destination = new FileOutputStream(destFile).getChannel(); destination.transferFrom(source, 0, source.size()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (source != null) try { source.close(); } catch (IOException e) { e.printStackTrace(); } if (destination != null) try { destination.close(); } catch (IOException e) { e.printStackTrace(); } } Photo photo = new Photo(); photo.path = photos[i].getAbsolutePath(); photo.name = photos[i].getName(); photo.uuid = EntryHelper.getUUIDFromString(photo.name); SyncInfoManager.setStatus(photo, SyncStatus.UPLOAD); } } // delete old entries EntryHelper.deleteAllEntries(); // create new entries File[] entries = new File(pendingRestore, "entries").listFiles(); if ( entries != null ) { for (int i = 0; i < entries.length; i++) { try { FileInputStream is = new FileInputStream(entries[i]); int size = is.available(); byte[] buffer = new byte[size]; is.read(buffer); is.close(); String text = new String(buffer); Entry e = EntryHelper.fromJson(text); DataManager.getInstance().save(e, true); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } // delete the pending restore folder FileUtil.deleteDirectory(dest); // re-enable syncing if (Settings.getDropboxSyncEnabled() || Settings.getGoogleDriveSyncEnabled()) { Account acc = User.getAccount(); ContentResolver.setSyncAutomatically(acc, Contract.AUTHORITY, true); long interval = Long.valueOf(PreferenceManager.getDefaultSharedPreferences(GlobalApplication.getAppContext()) .getString("key_sync_interval", "0")); ContentResolver.addPeriodicSync(acc, Contract.AUTHORITY, Bundle.EMPTY, interval); Bundle b = new Bundle(); b.putBoolean("resync_files", true); SyncHelper.requestManualSync(acc, b); } LogUtil.log("NarrateBackups", "Local backup restore complete."); } public static File[] getBackups() { File sdcard = Environment.getExternalStorageDirectory(); // get 'Narrate Backups' folder File backup = new File(sdcard, "/Narrate/"); return backup.listFiles(); } }