/*
* Copyright (c) 2015 Jonas Kalderstam.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nononsenseapps.notepad.data.local.json;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.os.Environment;
import android.util.Log;
import com.nononsenseapps.notepad.data.model.sql.Notification;
import com.nononsenseapps.notepad.data.model.sql.RemoteTask;
import com.nononsenseapps.notepad.data.model.sql.RemoteTaskList;
import com.nononsenseapps.notepad.data.model.sql.Task;
import com.nononsenseapps.notepad.data.model.sql.TaskList;
import com.nononsenseapps.notepad.data.receiver.NotificationHelper;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class JSONBackup {
public static final String DEFAULT_BACKUP_DIR = Environment
.getExternalStorageDirectory().toString() + "/NoNonsenseNotes";
public static final String DEFAULT_BACKUP_FILENAME = "/backup.json";
public static final String DEFAULT_BACKUP_FILEPATH = DEFAULT_BACKUP_DIR
+ DEFAULT_BACKUP_FILENAME;
private static final String KEY_REMINDERS = "reminders";
private static final String KEY_TASKS = "tasks";
private static final String KEY_REMOTES = "remotes";
private static final String KEY_LISTS = "lists";
private final Context context;
public JSONBackup(final Context context) {
this.context = context;
}
private List<TaskList> getTaskLists() {
final ArrayList<TaskList> taskLists = new ArrayList<TaskList>();
final Cursor c = context.getContentResolver().query(TaskList.URI,
TaskList.Columns.FIELDS, null, null, TaskList.Columns.TITLE);
while (c != null && c.moveToNext()) {
taskLists.add(new TaskList(c));
}
if (c != null)
c.close();
return taskLists;
}
private List<RemoteTaskList> getRemotesOf(final TaskList list) {
final ArrayList<RemoteTaskList> remotes = new ArrayList<RemoteTaskList>();
final Cursor c = context.getContentResolver().query(RemoteTaskList.URI,
RemoteTaskList.Columns.FIELDS,
RemoteTaskList.Columns.DBID + " IS ?",
new String[] { Long.toString(list._id) },
RemoteTaskList.Columns.SERVICE);
while (c != null && c.moveToNext()) {
remotes.add(new RemoteTaskList(c));
}
if (c != null)
c.close();
return remotes;
}
private List<Task> getTasksIn(final TaskList list) {
final ArrayList<Task> tasks = new ArrayList<Task>();
// Reverse order because adding stuff is always done at the top
final Cursor c = context.getContentResolver().query(Task.URI,
Task.Columns.FIELDS, Task.Columns.DBLIST + " IS ?",
new String[] { Long.toString(list._id) },
Task.Columns.LEFT + " DESC");
while (c != null && c.moveToNext()) {
tasks.add(new Task(c));
}
if (c != null)
c.close();
return tasks;
}
private List<RemoteTask> getRemotesOf(final Task task) {
final ArrayList<RemoteTask> remotes = new ArrayList<RemoteTask>();
final Cursor c = context.getContentResolver().query(RemoteTask.URI,
RemoteTask.Columns.FIELDS, RemoteTask.Columns.DBID + " IS ?",
new String[] { Long.toString(task._id) },
RemoteTask.Columns.SERVICE);
while (c != null && c.moveToNext()) {
remotes.add(new RemoteTask(c));
}
if (c != null)
c.close();
return remotes;
}
private List<Notification> getRemindersFor(final Task task) {
final ArrayList<Notification> reminders = new ArrayList<Notification>();
final Cursor c = context.getContentResolver().query(Notification.URI,
Notification.Columns.FIELDS,
Notification.Columns.TASKID + " IS ?",
new String[] { Long.toString(task._id) },
Notification.Columns.TIME);
while (c != null && c.moveToNext()) {
reminders.add(new Notification(c));
}
if (c != null)
c.close();
return reminders;
}
private JSONObject getJSONBackup() throws JSONException {
final JSONArray listarray = new JSONArray();
for (final TaskList list : getTaskLists()) {
final JSONObject jsonlist = new JSONObject();
jsonlist.put(TaskList.Columns._ID, list._id);
addAllContentToJSON(list.getContent(), jsonlist);
jsonlist.put(KEY_REMOTES, getJSONRemotesFor(list));
jsonlist.put(KEY_TASKS, getJSONTasksFor(list));
// Add tasklist to array
listarray.put(jsonlist);
}
final JSONObject backup = new JSONObject();
backup.put(KEY_LISTS, listarray);
return backup;
}
private void addAllContentToJSON(final ContentValues content,
final JSONObject json) throws JSONException {
for (String key : content.keySet()) {
json.put(key, content.get(key));
}
}
private JSONArray getJSONRemotesFor(final TaskList list)
throws JSONException {
final JSONArray remotelistarray = new JSONArray();
for (final RemoteTaskList remote : getRemotesOf(list)) {
final JSONObject jsonremote = new JSONObject();
jsonremote.put(RemoteTaskList.Columns._ID, remote._id);
addAllContentToJSON(remote.getContent(), jsonremote);
remotelistarray.put(jsonremote);
}
return remotelistarray;
}
private JSONArray getJSONTasksFor(final TaskList list) throws JSONException {
final JSONArray taskarray = new JSONArray();
for (final Task task : getTasksIn(list)) {
final JSONObject jsontask = new JSONObject();
jsontask.put(Task.Columns._ID, task._id);
addAllContentToJSON(task.getContent(), jsontask);
jsontask.put(Task.Columns.LEFT, task.left);
jsontask.put(Task.Columns.RIGHT, task.right);
jsontask.put(KEY_REMOTES, getJSONRemotesFor(task));
jsontask.put(KEY_REMINDERS, getJSONRemindersFor(task));
taskarray.put(jsontask);
}
return taskarray;
}
private JSONArray getJSONRemotesFor(final Task task) throws JSONException {
final JSONArray remotetaskarray = new JSONArray();
for (final RemoteTask remote : getRemotesOf(task)) {
final JSONObject jsonremote = new JSONObject();
jsonremote.put(RemoteTask.Columns._ID, remote._id);
addAllContentToJSON(remote.getContent(), jsonremote);
remotetaskarray.put(jsonremote);
}
return remotetaskarray;
}
private JSONArray getJSONRemindersFor(final Task task) throws JSONException {
final JSONArray reminderarray = new JSONArray();
for (final Notification reminder : getRemindersFor(task)) {
final JSONObject jsonreminder = new JSONObject();
jsonreminder.put(Notification.Columns._ID, reminder._id);
addAllContentToJSON(reminder.getContent(), jsonreminder);
reminderarray.put(jsonreminder);
}
return reminderarray;
}
/**
* Backs up the entire database to a JSON file. The location and name of the
* file are hardcoded.
*
* @throws JSONException
* @throws IOException
*/
public void writeBackup() throws JSONException, IOException {
// Create JSON object
final JSONObject backup = getJSONBackup();
// Serialise the JSON object to a file
final File backupFile = new File(DEFAULT_BACKUP_FILEPATH);
if (backupFile.exists()) {
backupFile.delete();
}
backupFile.getParentFile().mkdirs();
backupFile.createNewFile();
final FileWriter writer = new FileWriter(backupFile);
writer.write(backup.toString(2));
writer.flush();
writer.close();
}
/**
* Clears the database and restores the backup. Throws exceptions on
* failure.
*
* @throws JSONException
* @throws IOException
* @throws FileNotFoundException
*/
public void restoreBackup() throws FileNotFoundException, JSONException,
IOException {
final JSONObject backup = readBackup();
// Only if backup exists will we clear the database
clearDatabase();
final JSONArray listsarray = backup.getJSONArray(KEY_LISTS);
for (int i = 0; i < listsarray.length(); i++) {
final JSONObject jsonlist = listsarray.getJSONObject(i);
final TaskList tasklist = new TaskList(jsonlist);
if (tasklist.updated != null)
tasklist.save(context, tasklist.updated);
else
tasklist.save(context);
if (!jsonlist.isNull(KEY_REMOTES)) {
restoreRemotes(tasklist, jsonlist.getJSONArray(KEY_REMOTES));
}else {
Log.d("JONAS", "Remotes was null");
}
if (!jsonlist.isNull(KEY_TASKS)) {
restoreTasks(tasklist, jsonlist.getJSONArray(KEY_TASKS));
}
}
// Schedule notifications
NotificationHelper.schedule(context);
// TODO Add geofences
}
private void clearDatabase() {
// TODO Remove geofences
context.getContentResolver().delete(RemoteTask.URI, null, null);
context.getContentResolver().delete(RemoteTaskList.URI, null, null);
context.getContentResolver().delete(TaskList.URI, null, null);
context.getContentResolver().delete(Task.URI, null, null);
context.getContentResolver().delete(Notification.URI, null, null);
}
private JSONObject readBackup() throws JSONException, IOException,
FileNotFoundException {
// Try to read the backup file
final File backupFile = new File(DEFAULT_BACKUP_FILEPATH);
final StringBuilder sb = new StringBuilder();
String line;
BufferedReader reader = new BufferedReader(new FileReader(backupFile));
while ((line = reader.readLine()) != null) {
sb.append(line);
}
return new JSONObject(sb.toString());
}
private void restoreRemotes(final TaskList tasklist,
final JSONArray jsonArray) throws JSONException {
Log.d("JONAS", "Remote length: " + jsonArray.length());
for (int i = 0; i < jsonArray.length(); i++) {
final JSONObject json = jsonArray.getJSONObject(i);
final RemoteTaskList remote = new RemoteTaskList(json);
remote.dbid = tasklist._id;
remote.save(context);
Log.d("JONAS", "RemoteL restored: " + remote._id);
}
}
private void restoreTasks(final TaskList list, final JSONArray tasksarray)
throws JSONException {
for (int i = 0; i < tasksarray.length(); i++) {
final JSONObject jsontask = tasksarray.getJSONObject(i);
final Task task = new Task(jsontask);
task.dblist = list._id;
if (task.updated != null)
task.save(context, task.updated);
else
task.save(context);
if (!jsontask.isNull(KEY_REMOTES)) {
restoreRemotes(task, jsontask.getJSONArray(KEY_REMOTES));
}
if (!jsontask.isNull(KEY_REMINDERS)) {
restoreReminders(task, jsontask.getJSONArray(KEY_REMINDERS));
}
}
}
private void restoreRemotes(final Task task, final JSONArray jsonArray)
throws JSONException {
for (int i = 0; i < jsonArray.length(); i++) {
final JSONObject json = jsonArray.getJSONObject(i);
final RemoteTask remote = new RemoteTask(json);
remote.dbid = task._id;
remote.listdbid = task.dblist;
remote.save(context);
Log.d("JONAS", "RemoteT restored: " + remote._id);
}
}
private void restoreReminders(final Task task, final JSONArray jsonArray)
throws JSONException {
for (int i = 0; i < jsonArray.length(); i++) {
final JSONObject json = jsonArray.getJSONObject(i);
final Notification not = new Notification(json);
not.taskID = task._id;
not.save(context);
}
}
}