|
|
|
|
@ -3,265 +3,256 @@ package com.todoroo.astrid.backup;
|
|
|
|
|
import java.io.File;
|
|
|
|
|
import java.io.FileOutputStream;
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.util.Date;
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.HashSet;
|
|
|
|
|
import java.util.LinkedList;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
|
|
import org.xmlpull.v1.XmlSerializer;
|
|
|
|
|
|
|
|
|
|
import android.content.Context;
|
|
|
|
|
import android.database.Cursor;
|
|
|
|
|
import android.os.Environment;
|
|
|
|
|
import android.util.Log;
|
|
|
|
|
import android.util.Xml;
|
|
|
|
|
import android.widget.Toast;
|
|
|
|
|
|
|
|
|
|
import com.timsu.astrid.R;
|
|
|
|
|
import com.timsu.astrid.data.TaskController;
|
|
|
|
|
import com.timsu.astrid.data.TaskIdentifier;
|
|
|
|
|
import com.timsu.astrid.data.TaskModelForXml;
|
|
|
|
|
import com.timsu.astrid.data.alerts.AlertController;
|
|
|
|
|
import com.timsu.astrid.data.sync.SyncDataController;
|
|
|
|
|
import com.timsu.astrid.data.sync.SyncMapping;
|
|
|
|
|
import com.timsu.astrid.data.tag.TagController;
|
|
|
|
|
import com.timsu.astrid.data.tag.TagIdentifier;
|
|
|
|
|
import com.timsu.astrid.data.tag.TagModelForView;
|
|
|
|
|
import com.todoroo.andlib.data.AbstractModel;
|
|
|
|
|
import com.todoroo.andlib.data.Property;
|
|
|
|
|
import com.todoroo.andlib.data.Property.PropertyVisitor;
|
|
|
|
|
import com.todoroo.andlib.data.TodorooCursor;
|
|
|
|
|
import com.todoroo.andlib.service.ExceptionService;
|
|
|
|
|
import com.todoroo.andlib.sql.Order;
|
|
|
|
|
import com.todoroo.andlib.sql.Query;
|
|
|
|
|
import com.todoroo.astrid.core.PluginServices;
|
|
|
|
|
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
|
|
|
|
|
import com.todoroo.astrid.model.Metadata;
|
|
|
|
|
import com.todoroo.astrid.model.Task;
|
|
|
|
|
import com.todoroo.astrid.service.MetadataService;
|
|
|
|
|
import com.todoroo.astrid.service.TaskService;
|
|
|
|
|
import com.todoroo.astrid.utility.Preferences;
|
|
|
|
|
|
|
|
|
|
public class TasksXmlExporter {
|
|
|
|
|
|
|
|
|
|
private TaskController taskController;
|
|
|
|
|
private TagController tagController;
|
|
|
|
|
private AlertController alertController;
|
|
|
|
|
private SyncDataController syncDataController;
|
|
|
|
|
private Context ctx;
|
|
|
|
|
private String output;
|
|
|
|
|
// --- public interface
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Import tasks from the given file
|
|
|
|
|
*
|
|
|
|
|
* @param input
|
|
|
|
|
* @param runAfterImport
|
|
|
|
|
*/
|
|
|
|
|
public static void exportTasks(Context context, boolean isService) {
|
|
|
|
|
new TasksXmlExporter(context, isService);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- implementation
|
|
|
|
|
|
|
|
|
|
private static final int FORMAT = 2;
|
|
|
|
|
|
|
|
|
|
private final Context context;
|
|
|
|
|
private final boolean isService;
|
|
|
|
|
private int exportCount;
|
|
|
|
|
private XmlSerializer xml;
|
|
|
|
|
private HashMap<TagIdentifier, TagModelForView> tagMap;
|
|
|
|
|
|
|
|
|
|
public static final String ASTRID_TAG = "astrid";
|
|
|
|
|
public static final String ASTRID_ATTR_VERSION = "version";
|
|
|
|
|
public static final String TASK_TAG = "task";
|
|
|
|
|
public static final String TAG_TAG = "tag";
|
|
|
|
|
public static final String TAG_ATTR_NAME = "name";
|
|
|
|
|
public static final String ALERT_TAG = "alert";
|
|
|
|
|
public static final String ALERT_ATTR_DATE = "date";
|
|
|
|
|
public static final String SYNC_TAG = "sync";
|
|
|
|
|
public static final String SYNC_ATTR_SERVICE = "service";
|
|
|
|
|
public static final String SYNC_ATTR_REMOTE_ID = "remote_id";
|
|
|
|
|
public static final String XML_ENCODING = "utf-8";
|
|
|
|
|
public static final String ASTRID_DIR = "/astrid";
|
|
|
|
|
private static final String EXPORT_FILE_NAME = "user.%s.xml";
|
|
|
|
|
private static final String BACKUP_FILE_NAME = "auto.%s.xml";
|
|
|
|
|
public static final int FILENAME_DATE_BEGIN_INDEX = 5;
|
|
|
|
|
public static final int FILENAME_DATE_END_INDEX = 11;
|
|
|
|
|
|
|
|
|
|
public TasksXmlExporter(boolean isService) {
|
|
|
|
|
private final TaskService taskService = PluginServices.getTaskService();
|
|
|
|
|
private final MetadataService metadataService = PluginServices.getMetadataService();
|
|
|
|
|
private final ExceptionService exceptionService = PluginServices.getExceptionService();
|
|
|
|
|
|
|
|
|
|
private TasksXmlExporter(Context context, boolean isService) {
|
|
|
|
|
this.context = context;
|
|
|
|
|
this.isService = isService;
|
|
|
|
|
this.exportCount = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void initTagMap() {
|
|
|
|
|
tagMap = tagController.getAllTagsAsMap();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void serializeTags(TaskIdentifier task)
|
|
|
|
|
throws IOException {
|
|
|
|
|
LinkedList<TagIdentifier> tags = tagController.getTaskTags(task);
|
|
|
|
|
for (TagIdentifier tag : tags) {
|
|
|
|
|
if(!tagMap.containsKey(tag) || tagMap.get(tag) == null)
|
|
|
|
|
continue;
|
|
|
|
|
xml.startTag(null, TAG_TAG);
|
|
|
|
|
xml.attribute(null, TAG_ATTR_NAME, tagMap.get(tag).toString());
|
|
|
|
|
xml.endTag(null, TAG_TAG);
|
|
|
|
|
try {
|
|
|
|
|
String output = setupFile(BackupConstants.getExportDirectory());
|
|
|
|
|
doTasksExport(output);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
if(!isService)
|
|
|
|
|
displayErrorToast(e);
|
|
|
|
|
exceptionService.reportError("backup-exception", e); //$NON-NLS-1$
|
|
|
|
|
// TODO record last backup error
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void serializeSyncMappings(TaskIdentifier task)
|
|
|
|
|
throws IOException {
|
|
|
|
|
HashSet<SyncMapping> syncMappings = syncDataController.getSyncMappings(task);
|
|
|
|
|
for (SyncMapping sync : syncMappings) {
|
|
|
|
|
xml.startTag(null, SYNC_TAG);
|
|
|
|
|
xml.attribute(null, SYNC_ATTR_SERVICE,
|
|
|
|
|
Integer.toString(sync.getSyncServiceId()));
|
|
|
|
|
xml.attribute(null, SYNC_ATTR_REMOTE_ID, sync.getRemoteId());
|
|
|
|
|
xml.endTag(null, SYNC_TAG);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void serializeAlerts(TaskIdentifier task)
|
|
|
|
|
throws IOException {
|
|
|
|
|
List<Date> alerts = alertController.getTaskAlerts(task);
|
|
|
|
|
for (Date alert : alerts) {
|
|
|
|
|
xml.startTag(null, ALERT_TAG);
|
|
|
|
|
xml.attribute(null, ALERT_ATTR_DATE, BackupDateUtilities.getIso8601String(alert));
|
|
|
|
|
xml.endTag(null, ALERT_TAG);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void serializeTasks()
|
|
|
|
|
throws IOException {
|
|
|
|
|
Cursor c = taskController.getBackupTaskListCursor();
|
|
|
|
|
if (! c.moveToFirst()) {
|
|
|
|
|
return; // No tasks.
|
|
|
|
|
}
|
|
|
|
|
do {
|
|
|
|
|
TaskModelForXml task = new TaskModelForXml(c);
|
|
|
|
|
TaskIdentifier taskId = task.getTaskIdentifier();
|
|
|
|
|
xml.startTag(null, TASK_TAG);
|
|
|
|
|
HashMap<String, String> taskAttributes = task.getTaskAttributes();
|
|
|
|
|
for (String key : taskAttributes.keySet()) {
|
|
|
|
|
String value = taskAttributes.get(key);
|
|
|
|
|
xml.attribute(null, key, value);
|
|
|
|
|
}
|
|
|
|
|
serializeTags(taskId);
|
|
|
|
|
serializeAlerts(taskId);
|
|
|
|
|
serializeSyncMappings(taskId);
|
|
|
|
|
xml.endTag(null, TASK_TAG);
|
|
|
|
|
this.exportCount++;
|
|
|
|
|
} while (c.moveToNext());
|
|
|
|
|
c.close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void doTasksExport() throws IOException {
|
|
|
|
|
File xmlFile = new File(this.output);
|
|
|
|
|
@SuppressWarnings("nls")
|
|
|
|
|
private void doTasksExport(String output) throws IOException {
|
|
|
|
|
File xmlFile = new File(output);
|
|
|
|
|
xmlFile.createNewFile();
|
|
|
|
|
FileOutputStream fos = new FileOutputStream(xmlFile);
|
|
|
|
|
xml = Xml.newSerializer();
|
|
|
|
|
xml.setOutput(fos, XML_ENCODING);
|
|
|
|
|
xml.setOutput(fos, BackupConstants.XML_ENCODING);
|
|
|
|
|
|
|
|
|
|
xml.startDocument(null, null);
|
|
|
|
|
xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
|
|
|
|
|
|
|
|
|
|
xml.startTag(null, ASTRID_TAG);
|
|
|
|
|
xml.attribute(null, ASTRID_ATTR_VERSION,
|
|
|
|
|
Integer.toString(Preferences.getCurrentVersion(ctx)));
|
|
|
|
|
xml.startTag(null, BackupConstants.ASTRID_TAG);
|
|
|
|
|
xml.attribute(null, BackupConstants.ASTRID_ATTR_VERSION,
|
|
|
|
|
Integer.toString(Preferences.getCurrentVersion()));
|
|
|
|
|
xml.attribute(null, BackupConstants.ASTRID_ATTR_FORMAT,
|
|
|
|
|
Integer.toString(FORMAT));
|
|
|
|
|
|
|
|
|
|
openControllers();
|
|
|
|
|
initTagMap();
|
|
|
|
|
serializeTasks();
|
|
|
|
|
closeControllers();
|
|
|
|
|
|
|
|
|
|
xml.endTag(null, ASTRID_TAG);
|
|
|
|
|
xml.endTag(null, BackupConstants.ASTRID_TAG);
|
|
|
|
|
xml.endDocument();
|
|
|
|
|
xml.flush();
|
|
|
|
|
fos.close();
|
|
|
|
|
|
|
|
|
|
if (!isService) {
|
|
|
|
|
displayToast();
|
|
|
|
|
displayToast(output);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void displayToast() {
|
|
|
|
|
CharSequence text = String.format(ctx.getString(R.string.export_toast),
|
|
|
|
|
ctx.getResources().getQuantityString(R.plurals.Ntasks, exportCount,
|
|
|
|
|
exportCount), output);
|
|
|
|
|
Toast.makeText(ctx, text, Toast.LENGTH_LONG).show();
|
|
|
|
|
private void serializeTasks() throws IOException {
|
|
|
|
|
TodorooCursor<Task> cursor = taskService.query(Query.select(
|
|
|
|
|
Task.PROPERTIES).orderBy(Order.asc(Task.ID)));
|
|
|
|
|
try {
|
|
|
|
|
Task task = new Task();
|
|
|
|
|
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
|
|
|
|
|
task.readFromCursor(cursor);
|
|
|
|
|
|
|
|
|
|
xml.startTag(null, BackupConstants.TASK_TAG);
|
|
|
|
|
serializeModel(task, Task.PROPERTIES);
|
|
|
|
|
serializeMetadata(task);
|
|
|
|
|
xml.endTag(null, BackupConstants.TASK_TAG);
|
|
|
|
|
this.exportCount++;
|
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
cursor.close();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void displayErrorToast(String error) {
|
|
|
|
|
Toast.makeText(ctx, error, Toast.LENGTH_LONG).show();
|
|
|
|
|
private void serializeMetadata(Task task) throws IOException {
|
|
|
|
|
TodorooCursor<Metadata> cursor = metadataService.query(Query.select(
|
|
|
|
|
Metadata.PROPERTIES).where(MetadataCriteria.byTask(task.getId())));
|
|
|
|
|
try {
|
|
|
|
|
Metadata metadata = new Metadata();
|
|
|
|
|
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
|
|
|
|
|
metadata.readFromCursor(cursor);
|
|
|
|
|
|
|
|
|
|
xml.startTag(null, BackupConstants.TAG_TAG);
|
|
|
|
|
serializeModel(metadata, Metadata.PROPERTIES);
|
|
|
|
|
xml.endTag(null, BackupConstants.TAG_TAG);
|
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
cursor.close();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void closeControllers() {
|
|
|
|
|
tagController.close();
|
|
|
|
|
taskController.close();
|
|
|
|
|
alertController.close();
|
|
|
|
|
syncDataController.close();
|
|
|
|
|
/**
|
|
|
|
|
* Turn a model into xml attributes
|
|
|
|
|
* @param model
|
|
|
|
|
*/
|
|
|
|
|
private void serializeModel(AbstractModel model, Property<?>[] properties) {
|
|
|
|
|
for(Property<?> property : properties) {
|
|
|
|
|
try {
|
|
|
|
|
Log.e("read", "reading " + property.name + " from " + model.getDatabaseValues());
|
|
|
|
|
property.accept(xmlWritingVisitor, model);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
Log.e("caught", "caught while reading " + property.name + " from " + model.getDatabaseValues(), e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void openControllers() {
|
|
|
|
|
taskController.open();
|
|
|
|
|
tagController.open();
|
|
|
|
|
alertController.open();
|
|
|
|
|
syncDataController.open();
|
|
|
|
|
}
|
|
|
|
|
private final XmlWritingPropertyVisitor xmlWritingVisitor = new XmlWritingPropertyVisitor();
|
|
|
|
|
|
|
|
|
|
private class XmlWritingPropertyVisitor implements PropertyVisitor<Void, AbstractModel> {
|
|
|
|
|
@Override
|
|
|
|
|
public Void visitInteger(Property<Integer> property, AbstractModel data) {
|
|
|
|
|
try {
|
|
|
|
|
xml.attribute(null, property.name, data.getValue(property).toString());
|
|
|
|
|
} catch (IllegalArgumentException e) {
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
} catch (IllegalStateException e) {
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public Void visitLong(Property<Long> property, AbstractModel data) {
|
|
|
|
|
try {
|
|
|
|
|
xml.attribute(null, property.name, data.getValue(property).toString());
|
|
|
|
|
} catch (IllegalArgumentException e) {
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
} catch (IllegalStateException e) {
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void exportTasks(File directory) {
|
|
|
|
|
if (isService && !Preferences.isBackupEnabled(ctx)) {
|
|
|
|
|
// Automatic backups are disabled.
|
|
|
|
|
return;
|
|
|
|
|
@Override
|
|
|
|
|
public Void visitDouble(Property<Double> property, AbstractModel data) {
|
|
|
|
|
try {
|
|
|
|
|
xml.attribute(null, property.name, data.getValue(property).toString());
|
|
|
|
|
} catch (IllegalArgumentException e) {
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
} catch (IllegalStateException e) {
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
if (setupFile(directory)) {
|
|
|
|
|
Thread thread = new Thread(doBackgroundExport);
|
|
|
|
|
thread.start();
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public Void visitString(Property<String> property, AbstractModel data) {
|
|
|
|
|
try {
|
|
|
|
|
String value = data.getValue(property);
|
|
|
|
|
if(value == null)
|
|
|
|
|
return null;
|
|
|
|
|
xml.attribute(null, property.name, value);
|
|
|
|
|
} catch (IllegalArgumentException e) {
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
} catch (IllegalStateException e) {
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void displayToast(String output) {
|
|
|
|
|
CharSequence text = String.format(context.getString(R.string.export_toast),
|
|
|
|
|
context.getResources().getQuantityString(R.plurals.Ntasks, exportCount,
|
|
|
|
|
exportCount), output);
|
|
|
|
|
Toast.makeText(context, text, Toast.LENGTH_LONG).show();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void displayErrorToast(Exception error) {
|
|
|
|
|
Toast.makeText(context, error.toString(), Toast.LENGTH_LONG).show();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean setupFile(File directory) {
|
|
|
|
|
/**
|
|
|
|
|
* Creates directories if necessary and returns fully qualified file
|
|
|
|
|
* @param directory
|
|
|
|
|
* @return output file name
|
|
|
|
|
* @throws IOException
|
|
|
|
|
*/
|
|
|
|
|
private String setupFile(File directory) throws IOException {
|
|
|
|
|
File astridDir = directory;
|
|
|
|
|
if (astridDir != null) {
|
|
|
|
|
// Check for /sdcard/astrid directory. If it doesn't exist, make it.
|
|
|
|
|
if (astridDir.exists() || astridDir.mkdir()) {
|
|
|
|
|
String fileName;
|
|
|
|
|
if (isService) {
|
|
|
|
|
fileName = BACKUP_FILE_NAME;
|
|
|
|
|
fileName = BackupConstants.BACKUP_FILE_NAME;
|
|
|
|
|
} else {
|
|
|
|
|
fileName = EXPORT_FILE_NAME;
|
|
|
|
|
fileName = BackupConstants.EXPORT_FILE_NAME;
|
|
|
|
|
}
|
|
|
|
|
fileName = String.format(fileName, BackupDateUtilities.getDateForExport());
|
|
|
|
|
setOutput(astridDir.getAbsolutePath() + "/" + fileName);
|
|
|
|
|
return true;
|
|
|
|
|
return astridDir.getAbsolutePath() + File.separator + fileName;
|
|
|
|
|
} else {
|
|
|
|
|
// Unable to make the /sdcard/astrid directory.
|
|
|
|
|
String error = ctx.getString(R.string.error_sdcard, astridDir.getAbsolutePath());
|
|
|
|
|
Log.e("TasksXmlExporter", error);
|
|
|
|
|
if (!isService) {
|
|
|
|
|
displayErrorToast(error);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
throw new IOException(context.getString(R.string.DLG_error_sdcard,
|
|
|
|
|
astridDir.getAbsolutePath()));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Unable to access the sdcard because it's not in the mounted state.
|
|
|
|
|
String error = ctx.getString(R.string.error_sdcard_general);
|
|
|
|
|
Log.e("TasksXmlExporter", error);
|
|
|
|
|
if (!isService) {
|
|
|
|
|
displayErrorToast(error);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
throw new IOException(context.getString(R.string.DLG_error_sdcard_general));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void setOutput(String file) {
|
|
|
|
|
this.output = file;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private final Runnable doBackgroundExport = new Runnable() {
|
|
|
|
|
public void run() {
|
|
|
|
|
/*Looper.prepare();
|
|
|
|
|
try {
|
|
|
|
|
doTasksExport();
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
Log.e("TasksXmlExporter", "IOException in doTasksExport " + e.getMessage());
|
|
|
|
|
}
|
|
|
|
|
Looper.loop();*/
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
public void setTaskController(TaskController taskController) {
|
|
|
|
|
this.taskController = taskController;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setTagController(TagController tagController) {
|
|
|
|
|
this.tagController = tagController;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setAlertController(AlertController alertController) {
|
|
|
|
|
this.alertController = alertController;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setSyncDataController(SyncDataController syncDataController) {
|
|
|
|
|
this.syncDataController = syncDataController;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setContext(Context ctx) {
|
|
|
|
|
this.ctx = ctx;
|
|
|
|
|
setTaskController(new TaskController(ctx));
|
|
|
|
|
setTagController(new TagController(ctx));
|
|
|
|
|
setAlertController(new AlertController(ctx));
|
|
|
|
|
setSyncDataController(new SyncDataController(ctx));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|