mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 09:08:52 -06:00
Merge branch 'feature/sync' into dev
This commit is contained in:
@@ -12,7 +12,7 @@ android {
|
|||||||
minSdkVersion 15
|
minSdkVersion 15
|
||||||
targetSdkVersion 25
|
targetSdkVersion 25
|
||||||
|
|
||||||
buildConfigField "Integer", "databaseVersion", "18"
|
buildConfigField "Integer", "databaseVersion", "19"
|
||||||
buildConfigField "String", "databaseFilename", "\"uhabits.db\""
|
buildConfigField "String", "databaseFilename", "\"uhabits.db\""
|
||||||
|
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
@@ -80,6 +80,10 @@ dependencies {
|
|||||||
|
|
||||||
provided 'javax.annotation:jsr250-api:1.0'
|
provided 'javax.annotation:jsr250-api:1.0'
|
||||||
|
|
||||||
|
compile ('io.socket:socket.io-client:0.7.0') {
|
||||||
|
exclude group: 'org.json', module: 'json'
|
||||||
|
}
|
||||||
|
|
||||||
testApt 'com.google.dagger:dagger-compiler:2.2'
|
testApt 'com.google.dagger:dagger-compiler:2.2'
|
||||||
|
|
||||||
testCompile 'junit:junit:4.12'
|
testCompile 'junit:junit:4.12'
|
||||||
|
|||||||
@@ -36,6 +36,8 @@
|
|||||||
|
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name="HabitsApplication"
|
android:name="HabitsApplication"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
|||||||
41
app/src/main/assets/cacert.pem
Normal file
41
app/src/main/assets/cacert.pem
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIHPTCCBSWgAwIBAgIBADANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290
|
||||||
|
IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB
|
||||||
|
IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA
|
||||||
|
Y2FjZXJ0Lm9yZzAeFw0wMzAzMzAxMjI5NDlaFw0zMzAzMjkxMjI5NDlaMHkxEDAO
|
||||||
|
BgNVBAoTB1Jvb3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEi
|
||||||
|
MCAGA1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJ
|
||||||
|
ARYSc3VwcG9ydEBjYWNlcnQub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
|
||||||
|
CgKCAgEAziLA4kZ97DYoB1CW8qAzQIxL8TtmPzHlawI229Z89vGIj053NgVBlfkJ
|
||||||
|
8BLPRoZzYLdufujAWGSuzbCtRRcMY/pnCujW0r8+55jE8Ez64AO7NV1sId6eINm6
|
||||||
|
zWYyN3L69wj1x81YyY7nDl7qPv4coRQKFWyGhFtkZip6qUtTefWIonvuLwphK42y
|
||||||
|
fk1WpRPs6tqSnqxEQR5YYGUFZvjARL3LlPdCfgv3ZWiYUQXw8wWRBB0bF4LsyFe7
|
||||||
|
w2t6iPGwcswlWyCR7BYCEo8y6RcYSNDHBS4CMEK4JZwFaz+qOqfrU0j36NK2B5jc
|
||||||
|
G8Y0f3/JHIJ6BVgrCFvzOKKrF11myZjXnhCLotLddJr3cQxyYN/Nb5gznZY0dj4k
|
||||||
|
epKwDpUeb+agRThHqtdB7Uq3EvbXG4OKDy7YCbZZ16oE/9KTfWgu3YtLq1i6L43q
|
||||||
|
laegw1SJpfvbi1EinbLDvhG+LJGGi5Z4rSDTii8aP8bQUWWHIbEZAWV/RRyH9XzQ
|
||||||
|
QUxPKZgh/TMfdQwEUfoZd9vUFBzugcMd9Zi3aQaRIt0AUMyBMawSB3s42mhb5ivU
|
||||||
|
fslfrejrckzzAeVLIL+aplfKkQABi6F1ITe1Yw1nPkZPcCBnzsXWWdsC4PDSy826
|
||||||
|
YreQQejdIOQpvGQpQsgi3Hia/0PsmBsJUUtaWsJx8cTLc6nloQsCAwEAAaOCAc4w
|
||||||
|
ggHKMB0GA1UdDgQWBBQWtTIb1Mfz4OaO873SsDrusjkY0TCBowYDVR0jBIGbMIGY
|
||||||
|
gBQWtTIb1Mfz4OaO873SsDrusjkY0aF9pHsweTEQMA4GA1UEChMHUm9vdCBDQTEe
|
||||||
|
MBwGA1UECxMVaHR0cDovL3d3dy5jYWNlcnQub3JnMSIwIAYDVQQDExlDQSBDZXJ0
|
||||||
|
IFNpZ25pbmcgQXV0aG9yaXR5MSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0QGNhY2Vy
|
||||||
|
dC5vcmeCAQAwDwYDVR0TAQH/BAUwAwEB/zAyBgNVHR8EKzApMCegJaAjhiFodHRw
|
||||||
|
czovL3d3dy5jYWNlcnQub3JnL3Jldm9rZS5jcmwwMAYJYIZIAYb4QgEEBCMWIWh0
|
||||||
|
dHBzOi8vd3d3LmNhY2VydC5vcmcvcmV2b2tlLmNybDA0BglghkgBhvhCAQgEJxYl
|
||||||
|
aHR0cDovL3d3dy5jYWNlcnQub3JnL2luZGV4LnBocD9pZD0xMDBWBglghkgBhvhC
|
||||||
|
AQ0ESRZHVG8gZ2V0IHlvdXIgb3duIGNlcnRpZmljYXRlIGZvciBGUkVFIGhlYWQg
|
||||||
|
b3ZlciB0byBodHRwOi8vd3d3LmNhY2VydC5vcmcwDQYJKoZIhvcNAQEEBQADggIB
|
||||||
|
ACjH7pyCArpcgBLKNQodgW+JapnM8mgPf6fhjViVPr3yBsOQWqy1YPaZQwGjiHCc
|
||||||
|
nWKdpIevZ1gNMDY75q1I08t0AoZxPuIrA2jxNGJARjtT6ij0rPtmlVOKTV39O9lg
|
||||||
|
18p5aTuxZZKmxoGCXJzN600BiqXfEVWqFcofN8CCmHBh22p8lqOOLlQ+TyGpkO/c
|
||||||
|
gr/c6EWtTZBzCDyUZbAEmXZ/4rzCahWqlwQ3JNgelE5tDlG+1sSPypZt90Pf6DBl
|
||||||
|
Jzt7u0NDY8RD97LsaMzhGY4i+5jhe1o+ATc7iwiwovOVThrLm82asduycPAtStvY
|
||||||
|
sONvRUgzEv/+PDIqVPfE94rwiCPCR/5kenHA0R6mY7AHfqQv0wGP3J8rtsYIqQ+T
|
||||||
|
SCX8Ev2fQtzzxD72V7DX3WnRBnc0CkvSyqD/HMaMyRa+xMwyN2hzXwj7UfdJUzYF
|
||||||
|
CpUCTPJ5GhD22Dp1nPMd8aINcGeGG7MW9S/lpOt5hvk9C8JzC6WZrG/8Z7jlLwum
|
||||||
|
GCSNe9FINSkYQKyTYOGWhlC0elnYjyELn8+CkcY7v2vcB5G5l1YjqrZslMZIBjzk
|
||||||
|
zk6q5PYvCdxTby78dOs6Y5nCpqyJvKeyRKANihDjbPIky/qbn3BHLt4Ui9SyIAmW
|
||||||
|
omTxJBzcoTWcFbLUvFUufQb1nA5V9FrWk9p2rSVzTMVD
|
||||||
|
-----END CERTIFICATE-----
|
||||||
@@ -19,11 +19,16 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.commands;
|
package org.isoron.uhabits.commands;
|
||||||
|
|
||||||
|
import android.support.annotation.*;
|
||||||
|
|
||||||
import org.isoron.uhabits.*;
|
import org.isoron.uhabits.*;
|
||||||
import org.isoron.uhabits.models.*;
|
import org.isoron.uhabits.models.*;
|
||||||
|
import org.json.*;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.isoron.uhabits.commands.CommandParser.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Command to archive a list of habits.
|
* Command to archive a list of habits.
|
||||||
*/
|
*/
|
||||||
@@ -33,12 +38,36 @@ public class ArchiveHabitsCommand extends Command
|
|||||||
|
|
||||||
private final HabitList habitList;
|
private final HabitList habitList;
|
||||||
|
|
||||||
public ArchiveHabitsCommand(HabitList habitList, List<Habit> selectedHabits)
|
public ArchiveHabitsCommand(@NonNull HabitList habitList,
|
||||||
|
@NonNull List<Habit> selectedHabits)
|
||||||
{
|
{
|
||||||
|
super();
|
||||||
this.habitList = habitList;
|
this.habitList = habitList;
|
||||||
this.selectedHabits = selectedHabits;
|
this.selectedHabits = selectedHabits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ArchiveHabitsCommand(@NonNull String id,
|
||||||
|
@NonNull HabitList habitList,
|
||||||
|
@NonNull List<Habit> selectedHabits)
|
||||||
|
{
|
||||||
|
super(id);
|
||||||
|
this.habitList = habitList;
|
||||||
|
this.selectedHabits = selectedHabits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Command fromJSON(@NonNull JSONObject json,
|
||||||
|
@NonNull HabitList habitList)
|
||||||
|
throws JSONException
|
||||||
|
{
|
||||||
|
String id = json.getString("id");
|
||||||
|
JSONObject data = (JSONObject) json.get("data");
|
||||||
|
JSONArray habitIds = data.getJSONArray("ids");
|
||||||
|
|
||||||
|
LinkedList<Habit> selectedHabits =
|
||||||
|
habitListFromJSON(habitList, habitIds);
|
||||||
|
return new ArchiveHabitsCommand(id, habitList, selectedHabits);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute()
|
public void execute()
|
||||||
{
|
{
|
||||||
@@ -58,6 +87,24 @@ public class ArchiveHabitsCommand extends Command
|
|||||||
return R.string.toast_habit_unarchived;
|
return R.string.toast_habit_unarchived;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public JSONObject toJSON()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JSONObject root = super.toJSON();
|
||||||
|
JSONObject data = root.getJSONObject("data");
|
||||||
|
root.put("event", "ArchiveHabits");
|
||||||
|
data.put("ids", habitListToJSON(selectedHabits));
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
catch (JSONException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void undo()
|
public void undo()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -19,11 +19,16 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.commands;
|
package org.isoron.uhabits.commands;
|
||||||
|
|
||||||
|
import android.support.annotation.*;
|
||||||
|
|
||||||
import org.isoron.uhabits.*;
|
import org.isoron.uhabits.*;
|
||||||
import org.isoron.uhabits.models.*;
|
import org.isoron.uhabits.models.*;
|
||||||
|
import org.json.*;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.isoron.uhabits.commands.CommandParser.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Command to change the color of a list of habits.
|
* Command to change the color of a list of habits.
|
||||||
*/
|
*/
|
||||||
@@ -37,16 +42,35 @@ public class ChangeHabitColorCommand extends Command
|
|||||||
|
|
||||||
Integer newColor;
|
Integer newColor;
|
||||||
|
|
||||||
public ChangeHabitColorCommand(HabitList habitList,
|
public ChangeHabitColorCommand(@NonNull HabitList habitList,
|
||||||
List<Habit> selected,
|
@NonNull List<Habit> selected,
|
||||||
Integer newColor)
|
@NonNull Integer newColor)
|
||||||
{
|
{
|
||||||
this.habitList = habitList;
|
super();
|
||||||
this.selected = selected;
|
init(habitList, selected, newColor);
|
||||||
this.newColor = newColor;
|
}
|
||||||
this.originalColors = new ArrayList<>(selected.size());
|
|
||||||
|
|
||||||
for (Habit h : selected) originalColors.add(h.getColor());
|
public ChangeHabitColorCommand(@NonNull String id,
|
||||||
|
@NonNull HabitList habitList,
|
||||||
|
@NonNull List<Habit> selected,
|
||||||
|
@NonNull Integer newColor)
|
||||||
|
{
|
||||||
|
super(id);
|
||||||
|
init(habitList, selected, newColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Command fromJSON(@NonNull JSONObject json,
|
||||||
|
@NonNull HabitList habitList)
|
||||||
|
throws JSONException
|
||||||
|
{
|
||||||
|
String id = json.getString("id");
|
||||||
|
JSONObject data = (JSONObject) json.get("data");
|
||||||
|
JSONArray habitIds = data.getJSONArray("ids");
|
||||||
|
int newColor = data.getInt("color");
|
||||||
|
|
||||||
|
LinkedList<Habit> selected = habitListFromJSON(habitList, habitIds);
|
||||||
|
return new ChangeHabitColorCommand(id, habitList, selected, newColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -68,6 +92,25 @@ public class ChangeHabitColorCommand extends Command
|
|||||||
return R.string.toast_habit_changed;
|
return R.string.toast_habit_changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
|
public JSONObject toJSON()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JSONObject root = super.toJSON();
|
||||||
|
JSONObject data = root.getJSONObject("data");
|
||||||
|
root.put("event", "ChangeHabitColor");
|
||||||
|
data.put("ids", habitListToJSON(selected));
|
||||||
|
data.put("color", newColor);
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
catch (JSONException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void undo()
|
public void undo()
|
||||||
{
|
{
|
||||||
@@ -75,4 +118,16 @@ public class ChangeHabitColorCommand extends Command
|
|||||||
for (Habit h : selected) h.setColor(originalColors.get(k++));
|
for (Habit h : selected) h.setColor(originalColors.get(k++));
|
||||||
habitList.update(selected);
|
habitList.update(selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void init(@NonNull HabitList habitList,
|
||||||
|
@NonNull List<Habit> selected,
|
||||||
|
@NonNull Integer newColor)
|
||||||
|
{
|
||||||
|
this.habitList = habitList;
|
||||||
|
this.selected = selected;
|
||||||
|
this.newColor = newColor;
|
||||||
|
this.originalColors = new ArrayList<>(selected.size());
|
||||||
|
|
||||||
|
for (Habit h : selected) originalColors.add(h.getColor());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,9 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.commands;
|
package org.isoron.uhabits.commands;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.utils.*;
|
||||||
|
import org.json.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Command represents a desired set of changes that should be performed on the
|
* A Command represents a desired set of changes that should be performed on the
|
||||||
* models.
|
* models.
|
||||||
@@ -30,6 +33,18 @@ package org.isoron.uhabits.commands;
|
|||||||
*/
|
*/
|
||||||
public abstract class Command
|
public abstract class Command
|
||||||
{
|
{
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
public Command()
|
||||||
|
{
|
||||||
|
id = DatabaseUtils.getRandomId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Command(String id)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
public abstract void execute();
|
public abstract void execute();
|
||||||
|
|
||||||
public Integer getExecuteStringId()
|
public Integer getExecuteStringId()
|
||||||
@@ -43,4 +58,25 @@ public abstract class Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
public abstract void undo();
|
public abstract void undo();
|
||||||
|
|
||||||
|
public JSONObject toJSON()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JSONObject root = new JSONObject();
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
root.put("id", getId());
|
||||||
|
root.put("data", data);
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
catch (JSONException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId()
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
105
app/src/main/java/org/isoron/uhabits/commands/CommandParser.java
Normal file
105
app/src/main/java/org/isoron/uhabits/commands/CommandParser.java
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker 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.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker 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 org.isoron.uhabits.commands;
|
||||||
|
|
||||||
|
import android.support.annotation.*;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.models.*;
|
||||||
|
import org.json.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class CommandParser
|
||||||
|
{
|
||||||
|
|
||||||
|
private HabitList habitList;
|
||||||
|
|
||||||
|
private ModelFactory modelFactory;
|
||||||
|
|
||||||
|
public CommandParser(@NonNull HabitList habitList,
|
||||||
|
@NonNull ModelFactory modelFactory)
|
||||||
|
{
|
||||||
|
this.habitList = habitList;
|
||||||
|
this.modelFactory = modelFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static LinkedList<Habit> habitListFromJSON(
|
||||||
|
@NonNull HabitList habitList, @NonNull JSONArray habitIds)
|
||||||
|
throws JSONException
|
||||||
|
{
|
||||||
|
LinkedList<Habit> habits = new LinkedList<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < habitIds.length(); i++)
|
||||||
|
{
|
||||||
|
Long hId = habitIds.getLong(i);
|
||||||
|
Habit h = habitList.getById(hId);
|
||||||
|
if (h == null) continue;
|
||||||
|
|
||||||
|
habits.add(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
return habits;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
protected static JSONArray habitListToJSON(List<Habit> habits)
|
||||||
|
{
|
||||||
|
JSONArray habitIds = new JSONArray();
|
||||||
|
for (Habit h : habits) habitIds.put(h.getId());
|
||||||
|
return habitIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public Command fromJSON(@NonNull JSONObject json) throws JSONException
|
||||||
|
{
|
||||||
|
switch (json.getString("event"))
|
||||||
|
{
|
||||||
|
case "ToggleRepetition":
|
||||||
|
return ToggleRepetitionCommand.fromJSON(json, habitList);
|
||||||
|
|
||||||
|
case "ArchiveHabits":
|
||||||
|
return ArchiveHabitsCommand.fromJSON(json, habitList);
|
||||||
|
|
||||||
|
case "UnarchiveHabits":
|
||||||
|
return UnarchiveHabitsCommand.fromJSON(json, habitList);
|
||||||
|
|
||||||
|
case "ChangeHabitColor":
|
||||||
|
return ChangeHabitColorCommand.fromJSON(json, habitList);
|
||||||
|
|
||||||
|
case "CreateHabit":
|
||||||
|
return CreateHabitCommand.fromJSON(json, habitList,
|
||||||
|
modelFactory);
|
||||||
|
|
||||||
|
case "DeleteHabits":
|
||||||
|
return DeleteHabitsCommand.fromJSON(json, habitList);
|
||||||
|
|
||||||
|
case "EditHabit":
|
||||||
|
return EditHabitCommand.fromJSON(json, habitList, modelFactory);
|
||||||
|
|
||||||
|
// TODO: Implement this
|
||||||
|
// case "ReorderHabit":
|
||||||
|
// return ReorderHabitCommand.fromJSON(json);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ import com.google.auto.factory.*;
|
|||||||
|
|
||||||
import org.isoron.uhabits.*;
|
import org.isoron.uhabits.*;
|
||||||
import org.isoron.uhabits.models.*;
|
import org.isoron.uhabits.models.*;
|
||||||
|
import org.json.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Command to create a habit.
|
* Command to create a habit.
|
||||||
@@ -36,19 +37,50 @@ public class CreateHabitCommand extends Command
|
|||||||
|
|
||||||
HabitList habitList;
|
HabitList habitList;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
private Habit model;
|
private Habit model;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
private Long savedId;
|
private Long savedId;
|
||||||
|
|
||||||
public CreateHabitCommand(@Provided @NonNull ModelFactory modelFactory,
|
public CreateHabitCommand(@Provided @NonNull ModelFactory modelFactory,
|
||||||
@NonNull HabitList habitList,
|
@NonNull HabitList habitList,
|
||||||
@NonNull Habit model)
|
@NonNull Habit model)
|
||||||
{
|
{
|
||||||
|
super();
|
||||||
this.modelFactory = modelFactory;
|
this.modelFactory = modelFactory;
|
||||||
this.habitList = habitList;
|
this.habitList = habitList;
|
||||||
this.model = model;
|
this.model = model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CreateHabitCommand(@Provided @NonNull ModelFactory modelFactory,
|
||||||
|
@NonNull String commandId,
|
||||||
|
@NonNull HabitList habitList,
|
||||||
|
@NonNull Habit model,
|
||||||
|
@Nullable Long savedId)
|
||||||
|
{
|
||||||
|
super(commandId);
|
||||||
|
this.modelFactory = modelFactory;
|
||||||
|
this.habitList = habitList;
|
||||||
|
this.model = model;
|
||||||
|
this.savedId = savedId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Command fromJSON(@NonNull JSONObject root,
|
||||||
|
@NonNull HabitList habitList,
|
||||||
|
@NonNull ModelFactory modelFactory)
|
||||||
|
throws JSONException
|
||||||
|
{
|
||||||
|
String commandId = root.getString("id");
|
||||||
|
JSONObject data = (JSONObject) root.get("data");
|
||||||
|
Habit model = Habit.fromJSON(data.getJSONObject("habit"), modelFactory);
|
||||||
|
Long savedId = data.getLong("id");
|
||||||
|
|
||||||
|
return new CreateHabitCommand(modelFactory, commandId, habitList, model,
|
||||||
|
savedId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute()
|
public void execute()
|
||||||
{
|
{
|
||||||
@@ -72,6 +104,24 @@ public class CreateHabitCommand extends Command
|
|||||||
return R.string.toast_habit_deleted;
|
return R.string.toast_habit_deleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSONObject toJSON()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JSONObject root = super.toJSON();
|
||||||
|
JSONObject data = root.getJSONObject("data");
|
||||||
|
root.put("event", "CreateHabit");
|
||||||
|
data.put("habit", model.toJSON());
|
||||||
|
data.put("id", savedId);
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
catch (JSONException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void undo()
|
public void undo()
|
||||||
{
|
{
|
||||||
@@ -80,5 +130,4 @@ public class CreateHabitCommand extends Command
|
|||||||
|
|
||||||
habitList.remove(habit);
|
habitList.remove(habit);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -19,11 +19,17 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.commands;
|
package org.isoron.uhabits.commands;
|
||||||
|
|
||||||
|
import android.support.annotation.*;
|
||||||
|
|
||||||
import org.isoron.uhabits.*;
|
import org.isoron.uhabits.*;
|
||||||
import org.isoron.uhabits.models.*;
|
import org.isoron.uhabits.models.*;
|
||||||
|
import org.json.*;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.isoron.uhabits.commands.CommandParser.habitListFromJSON;
|
||||||
|
import static org.isoron.uhabits.commands.CommandParser.habitListToJSON;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Command to delete a list of habits.
|
* Command to delete a list of habits.
|
||||||
*/
|
*/
|
||||||
@@ -33,12 +39,36 @@ public class DeleteHabitsCommand extends Command
|
|||||||
|
|
||||||
private List<Habit> habits;
|
private List<Habit> habits;
|
||||||
|
|
||||||
public DeleteHabitsCommand(HabitList habitList, List<Habit> habits)
|
public DeleteHabitsCommand(@NonNull HabitList habitList,
|
||||||
|
@NonNull List<Habit> habits)
|
||||||
{
|
{
|
||||||
|
super();
|
||||||
this.habits = habits;
|
this.habits = habits;
|
||||||
this.habitList = habitList;
|
this.habitList = habitList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DeleteHabitsCommand(@NonNull String id,
|
||||||
|
@NonNull HabitList habitList,
|
||||||
|
@NonNull List<Habit> habits)
|
||||||
|
{
|
||||||
|
super(id);
|
||||||
|
this.habits = habits;
|
||||||
|
this.habitList = habitList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Command fromJSON(@NonNull JSONObject json,
|
||||||
|
@NonNull HabitList habitList)
|
||||||
|
throws JSONException
|
||||||
|
{
|
||||||
|
String id = json.getString("id");
|
||||||
|
JSONObject data = (JSONObject) json.get("data");
|
||||||
|
JSONArray habitIds = data.getJSONArray("ids");
|
||||||
|
|
||||||
|
LinkedList<Habit> habits = habitListFromJSON(habitList, habitIds);
|
||||||
|
return new DeleteHabitsCommand(id, habitList, habits);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute()
|
public void execute()
|
||||||
{
|
{
|
||||||
@@ -63,6 +93,24 @@ public class DeleteHabitsCommand extends Command
|
|||||||
return R.string.toast_habit_restored;
|
return R.string.toast_habit_restored;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
|
public JSONObject toJSON()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JSONObject root = super.toJSON();
|
||||||
|
JSONObject data = root.getJSONObject("data");
|
||||||
|
root.put("event", "DeleteHabits");
|
||||||
|
data.put("ids", habitListToJSON(habits));
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
catch (JSONException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void undo()
|
public void undo()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import com.google.auto.factory.*;
|
|||||||
|
|
||||||
import org.isoron.uhabits.*;
|
import org.isoron.uhabits.*;
|
||||||
import org.isoron.uhabits.models.*;
|
import org.isoron.uhabits.models.*;
|
||||||
|
import org.json.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Command to modify a habit.
|
* Command to modify a habit.
|
||||||
@@ -42,27 +43,42 @@ public class EditHabitCommand extends Command
|
|||||||
|
|
||||||
private boolean hasFrequencyChanged;
|
private boolean hasFrequencyChanged;
|
||||||
|
|
||||||
private final boolean hasTargetChanged;
|
private boolean hasTargetChanged;
|
||||||
|
|
||||||
public EditHabitCommand(@Provided @NonNull ModelFactory modelFactory,
|
public EditHabitCommand(@Provided @NonNull ModelFactory modelFactory,
|
||||||
@NonNull HabitList habitList,
|
@NonNull HabitList habitList,
|
||||||
@NonNull Habit original,
|
@NonNull Habit original,
|
||||||
@NonNull Habit modified)
|
@NonNull Habit modified)
|
||||||
{
|
{
|
||||||
this.habitList = habitList;
|
super();
|
||||||
this.savedId = original.getId();
|
init(modelFactory, habitList, original, modified);
|
||||||
this.modified = modelFactory.buildHabit();
|
}
|
||||||
this.original = modelFactory.buildHabit();
|
|
||||||
|
|
||||||
this.modified.copyFrom(modified);
|
public EditHabitCommand(@Provided @NonNull ModelFactory modelFactory,
|
||||||
this.original.copyFrom(original);
|
@NonNull String id,
|
||||||
|
@NonNull HabitList habitList,
|
||||||
|
@NonNull Habit original,
|
||||||
|
@NonNull Habit modified)
|
||||||
|
{
|
||||||
|
super(id);
|
||||||
|
init(modelFactory, habitList, original, modified);
|
||||||
|
}
|
||||||
|
|
||||||
Frequency originalFreq = this.original.getFrequency();
|
@NonNull
|
||||||
Frequency modifiedFreq = this.modified.getFrequency();
|
public static Command fromJSON(@NonNull JSONObject root,
|
||||||
hasFrequencyChanged = (!originalFreq.equals(modifiedFreq));
|
@NonNull HabitList habitList,
|
||||||
hasTargetChanged =
|
@NonNull ModelFactory modelFactory)
|
||||||
(original.getTargetType() != modified.getTargetType() ||
|
throws JSONException
|
||||||
original.getTargetValue() != modified.getTargetValue());
|
{
|
||||||
|
String commandId = root.getString("id");
|
||||||
|
JSONObject data = (JSONObject) root.get("data");
|
||||||
|
Habit original = habitList.getById(data.getLong("id"));
|
||||||
|
if (original == null) throw new HabitNotFoundException();
|
||||||
|
|
||||||
|
Habit modified =
|
||||||
|
Habit.fromJSON(data.getJSONObject("params"), modelFactory);
|
||||||
|
return new EditHabitCommand(modelFactory, commandId, habitList,
|
||||||
|
original, modified);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -83,6 +99,24 @@ public class EditHabitCommand extends Command
|
|||||||
return R.string.toast_habit_changed_back;
|
return R.string.toast_habit_changed_back;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSONObject toJSON()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JSONObject root = super.toJSON();
|
||||||
|
JSONObject data = root.getJSONObject("data");
|
||||||
|
root.put("event", "EditHabit");
|
||||||
|
data.put("id", savedId);
|
||||||
|
data.put("params", modified.toJSON());
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
catch (JSONException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void undo()
|
public void undo()
|
||||||
{
|
{
|
||||||
@@ -100,6 +134,27 @@ public class EditHabitCommand extends Command
|
|||||||
invalidateIfNeeded(habit);
|
invalidateIfNeeded(habit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void init(@NonNull ModelFactory modelFactory,
|
||||||
|
@NonNull HabitList habitList,
|
||||||
|
@NonNull Habit original,
|
||||||
|
@NonNull Habit modified)
|
||||||
|
{
|
||||||
|
this.habitList = habitList;
|
||||||
|
this.savedId = original.getId();
|
||||||
|
this.modified = modelFactory.buildHabit();
|
||||||
|
this.original = modelFactory.buildHabit();
|
||||||
|
|
||||||
|
this.modified.copyFrom(modified);
|
||||||
|
this.original.copyFrom(original);
|
||||||
|
|
||||||
|
Frequency originalFreq = this.original.getFrequency();
|
||||||
|
Frequency modifiedFreq = this.modified.getFrequency();
|
||||||
|
hasFrequencyChanged = (!originalFreq.equals(modifiedFreq));
|
||||||
|
hasTargetChanged =
|
||||||
|
(original.getTargetType() != modified.getTargetType() ||
|
||||||
|
original.getTargetValue() != modified.getTargetValue());
|
||||||
|
}
|
||||||
|
|
||||||
private void invalidateIfNeeded(Habit habit)
|
private void invalidateIfNeeded(Habit habit)
|
||||||
{
|
{
|
||||||
if (hasFrequencyChanged || hasTargetChanged)
|
if (hasFrequencyChanged || hasTargetChanged)
|
||||||
|
|||||||
@@ -19,26 +19,80 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.commands;
|
package org.isoron.uhabits.commands;
|
||||||
|
|
||||||
|
import android.support.annotation.*;
|
||||||
|
|
||||||
import org.isoron.uhabits.models.*;
|
import org.isoron.uhabits.models.*;
|
||||||
|
import org.json.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Command to toggle a repetition.
|
* Command to toggle a repetition.
|
||||||
*/
|
*/
|
||||||
public class ToggleRepetitionCommand extends Command
|
public class ToggleRepetitionCommand extends Command
|
||||||
{
|
{
|
||||||
private Long offset;
|
private Long timestamp;
|
||||||
|
|
||||||
private Habit habit;
|
private Habit habit;
|
||||||
|
|
||||||
public ToggleRepetitionCommand(Habit habit, long offset)
|
public ToggleRepetitionCommand(@NonNull Habit habit, long timestamp)
|
||||||
{
|
{
|
||||||
this.offset = offset;
|
super();
|
||||||
|
this.timestamp = timestamp;
|
||||||
this.habit = habit;
|
this.habit = habit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ToggleRepetitionCommand(@NonNull String id,
|
||||||
|
@NonNull Habit habit,
|
||||||
|
long timestamp)
|
||||||
|
{
|
||||||
|
super(id);
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.habit = habit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Command fromJSON(@NonNull JSONObject json,
|
||||||
|
@NonNull HabitList habitList)
|
||||||
|
throws JSONException
|
||||||
|
{
|
||||||
|
String id = json.getString("id");
|
||||||
|
JSONObject data = (JSONObject) json.get("data");
|
||||||
|
Long habitId = data.getLong("habit");
|
||||||
|
Long timestamp = data.getLong("timestamp");
|
||||||
|
|
||||||
|
Habit habit = habitList.getById(habitId);
|
||||||
|
if (habit == null) throw new HabitNotFoundException();
|
||||||
|
|
||||||
|
return new ToggleRepetitionCommand(id, habit, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute()
|
public void execute()
|
||||||
{
|
{
|
||||||
habit.getRepetitions().toggleTimestamp(offset);
|
habit.getRepetitions().toggleTimestamp(timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Habit getHabit()
|
||||||
|
{
|
||||||
|
return habit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
|
public JSONObject toJSON()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JSONObject root = super.toJSON();
|
||||||
|
JSONObject data = root.getJSONObject("data");
|
||||||
|
root.put("event", "ToggleRepetition");
|
||||||
|
data.put("habit", habit.getId());
|
||||||
|
data.put("timestamp", timestamp);
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
catch (JSONException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -46,9 +100,4 @@ public class ToggleRepetitionCommand extends Command
|
|||||||
{
|
{
|
||||||
execute();
|
execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Habit getHabit()
|
|
||||||
{
|
|
||||||
return habit;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -19,11 +19,16 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.commands;
|
package org.isoron.uhabits.commands;
|
||||||
|
|
||||||
|
import android.support.annotation.*;
|
||||||
|
|
||||||
import org.isoron.uhabits.*;
|
import org.isoron.uhabits.*;
|
||||||
import org.isoron.uhabits.models.*;
|
import org.isoron.uhabits.models.*;
|
||||||
|
import org.json.*;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.isoron.uhabits.commands.CommandParser.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Command to unarchive a list of habits.
|
* Command to unarchive a list of habits.
|
||||||
*/
|
*/
|
||||||
@@ -33,23 +38,40 @@ public class UnarchiveHabitsCommand extends Command
|
|||||||
|
|
||||||
private List<Habit> habits;
|
private List<Habit> habits;
|
||||||
|
|
||||||
public UnarchiveHabitsCommand(HabitList habitList, List<Habit> selected)
|
public UnarchiveHabitsCommand(@NonNull HabitList habitList,
|
||||||
|
@NonNull List<Habit> selected)
|
||||||
{
|
{
|
||||||
|
super();
|
||||||
this.habits = selected;
|
this.habits = selected;
|
||||||
this.habitList = habitList;
|
this.habitList = habitList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UnarchiveHabitsCommand(@NonNull String id,
|
||||||
|
@NonNull HabitList habitList,
|
||||||
|
@NonNull List<Habit> selected)
|
||||||
|
{
|
||||||
|
super(id);
|
||||||
|
this.habits = selected;
|
||||||
|
this.habitList = habitList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Command fromJSON(@NonNull JSONObject json,
|
||||||
|
@NonNull HabitList habitList)
|
||||||
|
throws JSONException
|
||||||
|
{
|
||||||
|
String id = json.getString("id");
|
||||||
|
JSONObject data = (JSONObject) json.get("data");
|
||||||
|
JSONArray habitIds = data.getJSONArray("ids");
|
||||||
|
|
||||||
|
LinkedList<Habit> selected = habitListFromJSON(habitList, habitIds);
|
||||||
|
return new UnarchiveHabitsCommand(id, habitList, selected);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute()
|
public void execute()
|
||||||
{
|
{
|
||||||
for(Habit h : habits) h.setArchived(false);
|
for (Habit h : habits) h.setArchived(false);
|
||||||
habitList.update(habits);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void undo()
|
|
||||||
{
|
|
||||||
for(Habit h : habits) h.setArchived(true);
|
|
||||||
habitList.update(habits);
|
habitList.update(habits);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,4 +86,30 @@ public class UnarchiveHabitsCommand extends Command
|
|||||||
{
|
{
|
||||||
return R.string.toast_habit_archived;
|
return R.string.toast_habit_archived;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
|
public JSONObject toJSON()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JSONObject root = super.toJSON();
|
||||||
|
JSONObject data = root.getJSONObject("data");
|
||||||
|
root.put("event", "UnarchiveHabits");
|
||||||
|
data.put("ids", habitListToJSON(habits));
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
catch (JSONException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void undo()
|
||||||
|
{
|
||||||
|
for (Habit h : habits) h.setArchived(true);
|
||||||
|
habitList.update(habits);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -23,6 +23,7 @@ import android.net.*;
|
|||||||
import android.support.annotation.*;
|
import android.support.annotation.*;
|
||||||
|
|
||||||
import org.apache.commons.lang3.builder.*;
|
import org.apache.commons.lang3.builder.*;
|
||||||
|
import org.json.*;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@@ -371,4 +372,64 @@ public class Habit
|
|||||||
.append("unit", unit)
|
.append("unit", unit)
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public JSONObject toJSON()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JSONObject json = new JSONObject();
|
||||||
|
json.put("name", name);
|
||||||
|
json.put("description", description);
|
||||||
|
json.put("freqNum", frequency.getNumerator());
|
||||||
|
json.put("freqDen", frequency.getDenominator());
|
||||||
|
json.put("color", color);
|
||||||
|
json.put("type", type);
|
||||||
|
json.put("targetType", targetType);
|
||||||
|
json.put("targetValue", targetValue);
|
||||||
|
json.put("unit", unit);
|
||||||
|
json.put("archived", archived);
|
||||||
|
|
||||||
|
if(reminder != null)
|
||||||
|
{
|
||||||
|
json.put("reminderHour", reminder.getHour());
|
||||||
|
json.put("reminderMin", reminder.getMinute());
|
||||||
|
json.put("reminderDays", reminder.getDays().toInteger());
|
||||||
|
}
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
catch(JSONException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Habit fromJSON(@NonNull JSONObject json,
|
||||||
|
@NonNull ModelFactory modelFactory)
|
||||||
|
throws JSONException
|
||||||
|
{
|
||||||
|
Habit habit = modelFactory.buildHabit();
|
||||||
|
habit.name = json.getString("name");
|
||||||
|
habit.description = json.getString("description");
|
||||||
|
int freqNum = json.getInt("freqNum");
|
||||||
|
int freqDen = json.getInt("freqDen");
|
||||||
|
habit.frequency = new Frequency(freqNum, freqDen);
|
||||||
|
habit.color = json.getInt("color");
|
||||||
|
habit.archived = json.getBoolean("archived");
|
||||||
|
habit.targetValue = json.getInt("targetValue");
|
||||||
|
habit.targetType = json.getInt("targetType");
|
||||||
|
habit.unit = json.getString("unit");
|
||||||
|
habit.type = json.getInt("type");
|
||||||
|
|
||||||
|
if(json.has("reminderHour"))
|
||||||
|
{
|
||||||
|
int hour = json.getInt("reminderHour");
|
||||||
|
int min = json.getInt("reminderMin");
|
||||||
|
int days = json.getInt("reminderDays");
|
||||||
|
habit.reminder = new Reminder(hour, min, new WeekdayList(days));
|
||||||
|
}
|
||||||
|
return habit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,11 +77,6 @@ public class Preferences
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNumericalHabitsFeatureEnabled()
|
|
||||||
{
|
|
||||||
return prefs.getBoolean("pref_feature_numerical_habits", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDefaultOrder(HabitList.Order order)
|
public void setDefaultOrder(HabitList.Order order)
|
||||||
{
|
{
|
||||||
prefs.edit().putString("pref_default_order", order.name()).apply();
|
prefs.edit().putString("pref_default_order", order.name()).apply();
|
||||||
@@ -120,6 +115,16 @@ public class Preferences
|
|||||||
return prefs.getLong("last_hint_timestamp", -1);
|
return prefs.getLong("last_hint_timestamp", -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getLastSync()
|
||||||
|
{
|
||||||
|
return prefs.getLong("lastSync", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastSync(long timestamp)
|
||||||
|
{
|
||||||
|
prefs.edit().putLong("last_sync", timestamp).apply();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean getShowArchived()
|
public boolean getShowArchived()
|
||||||
{
|
{
|
||||||
return prefs.getBoolean("pref_show_archived", false);
|
return prefs.getBoolean("pref_show_archived", false);
|
||||||
@@ -145,6 +150,11 @@ public class Preferences
|
|||||||
return Long.parseLong(prefs.getString("pref_snooze_interval", "15"));
|
return Long.parseLong(prefs.getString("pref_snooze_interval", "15"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSyncKey()
|
||||||
|
{
|
||||||
|
return prefs.getString("pref_sync_key", "");
|
||||||
|
}
|
||||||
|
|
||||||
public int getTheme()
|
public int getTheme()
|
||||||
{
|
{
|
||||||
return prefs.getInt("pref_theme", ThemeSwitcher.THEME_LIGHT);
|
return prefs.getInt("pref_theme", ThemeSwitcher.THEME_LIGHT);
|
||||||
@@ -186,6 +196,11 @@ public class Preferences
|
|||||||
prefs.edit().putBoolean("pref_first_run", isFirstRun).apply();
|
prefs.edit().putBoolean("pref_first_run", isFirstRun).apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isNumericalHabitsFeatureEnabled()
|
||||||
|
{
|
||||||
|
return prefs.getBoolean("pref_feature_numerical_habits", false);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isPureBlackEnabled()
|
public boolean isPureBlackEnabled()
|
||||||
{
|
{
|
||||||
return prefs.getBoolean("pref_pure_black", false);
|
return prefs.getBoolean("pref_pure_black", false);
|
||||||
|
|||||||
65
app/src/main/java/org/isoron/uhabits/sync/Event.java
Normal file
65
app/src/main/java/org/isoron/uhabits/sync/Event.java
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker 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.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker 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 org.isoron.uhabits.sync;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.activeandroid.Model;
|
||||||
|
import com.activeandroid.annotation.Column;
|
||||||
|
import com.activeandroid.annotation.Table;
|
||||||
|
import com.activeandroid.query.Select;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Table(name = "Events")
|
||||||
|
public class Event extends Model
|
||||||
|
{
|
||||||
|
@NonNull
|
||||||
|
@Column(name = "timestamp")
|
||||||
|
public Long timestamp;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Column(name = "message")
|
||||||
|
public String message;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Column(name = "server_id")
|
||||||
|
public String serverId;
|
||||||
|
|
||||||
|
public Event()
|
||||||
|
{
|
||||||
|
timestamp = 0L;
|
||||||
|
message = "";
|
||||||
|
serverId = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Event(@NonNull String serverId, long timestamp, @NonNull String message)
|
||||||
|
{
|
||||||
|
this.serverId = serverId;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static List<Event> getAll()
|
||||||
|
{
|
||||||
|
return new Select().from(Event.class).orderBy("timestamp").execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
334
app/src/main/java/org/isoron/uhabits/sync/SyncManager.java
Normal file
334
app/src/main/java/org/isoron/uhabits/sync/SyncManager.java
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker 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.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker 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 org.isoron.uhabits.sync;
|
||||||
|
|
||||||
|
import android.content.*;
|
||||||
|
import android.support.annotation.*;
|
||||||
|
import android.util.*;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.*;
|
||||||
|
import org.isoron.uhabits.commands.*;
|
||||||
|
import org.isoron.uhabits.preferences.*;
|
||||||
|
import org.isoron.uhabits.utils.*;
|
||||||
|
import org.json.*;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.*;
|
||||||
|
import java.security.*;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
|
import java.security.cert.*;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import javax.net.ssl.*;
|
||||||
|
|
||||||
|
import io.socket.client.*;
|
||||||
|
import io.socket.client.Socket;
|
||||||
|
import io.socket.emitter.*;
|
||||||
|
|
||||||
|
import static io.socket.client.Socket.*;
|
||||||
|
|
||||||
|
public class SyncManager
|
||||||
|
{
|
||||||
|
public static final String EVENT_AUTH = "auth";
|
||||||
|
|
||||||
|
public static final String EVENT_AUTH_OK = "authOK";
|
||||||
|
|
||||||
|
public static final String EVENT_EXECUTE_EVENT = "execute";
|
||||||
|
|
||||||
|
public static final String EVENT_FETCH = "fetch";
|
||||||
|
|
||||||
|
public static final String EVENT_FETCH_OK = "fetchOK";
|
||||||
|
|
||||||
|
public static final String EVENT_POST_EVENT = "postEvent";
|
||||||
|
|
||||||
|
public static final String SYNC_SERVER_URL =
|
||||||
|
"https://sync.loophabits.org:4000";
|
||||||
|
|
||||||
|
private static String CLIENT_ID;
|
||||||
|
|
||||||
|
private static String GROUP_KEY;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private Socket socket;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private LinkedList<Event> pendingConfirmation;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private List<Event> pendingEmit;
|
||||||
|
|
||||||
|
private boolean readyToEmit = false;
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
|
||||||
|
private final Preferences prefs;
|
||||||
|
|
||||||
|
private CommandRunner commandRunner;
|
||||||
|
|
||||||
|
private CommandParser commandParser;
|
||||||
|
|
||||||
|
public SyncManager(@NonNull Context context,
|
||||||
|
@NonNull Preferences prefs,
|
||||||
|
@NonNull CommandRunner commandRunner,
|
||||||
|
@NonNull CommandParser commandParser)
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
this.prefs = prefs;
|
||||||
|
this.commandRunner = commandRunner;
|
||||||
|
this.commandParser = commandParser;
|
||||||
|
|
||||||
|
pendingConfirmation = new LinkedList<>();
|
||||||
|
pendingEmit = Event.getAll();
|
||||||
|
|
||||||
|
GROUP_KEY = prefs.getSyncKey();
|
||||||
|
CLIENT_ID = DatabaseUtils.getRandomId();
|
||||||
|
|
||||||
|
Log.d("SyncManager", CLIENT_ID);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IO.setDefaultSSLContext(getCACertSSLContext());
|
||||||
|
socket = IO.socket(SYNC_SERVER_URL);
|
||||||
|
logSocketEvent(socket, EVENT_CONNECT, "Connected");
|
||||||
|
logSocketEvent(socket, EVENT_CONNECT_TIMEOUT, "Connect timeout");
|
||||||
|
logSocketEvent(socket, EVENT_CONNECTING, "Connecting...");
|
||||||
|
logSocketEvent(socket, EVENT_CONNECT_ERROR, "Connect error");
|
||||||
|
logSocketEvent(socket, EVENT_DISCONNECT, "Disconnected");
|
||||||
|
logSocketEvent(socket, EVENT_RECONNECT, "Reconnected");
|
||||||
|
logSocketEvent(socket, EVENT_RECONNECT_ATTEMPT, "Reconnecting...");
|
||||||
|
logSocketEvent(socket, EVENT_RECONNECT_ERROR, "Reconnect error");
|
||||||
|
logSocketEvent(socket, EVENT_RECONNECT_FAILED, "Reconnect failed");
|
||||||
|
logSocketEvent(socket, EVENT_DISCONNECT, "Disconnected");
|
||||||
|
logSocketEvent(socket, EVENT_PING, "Ping");
|
||||||
|
logSocketEvent(socket, EVENT_PONG, "Pong");
|
||||||
|
|
||||||
|
socket.on(EVENT_CONNECT, new OnConnectListener());
|
||||||
|
socket.on(EVENT_DISCONNECT, new OnDisconnectListener());
|
||||||
|
socket.on(EVENT_EXECUTE_EVENT, new OnExecuteCommandListener());
|
||||||
|
socket.on(EVENT_AUTH_OK, new OnAuthOKListener());
|
||||||
|
socket.on(EVENT_FETCH_OK, new OnFetchOKListener());
|
||||||
|
|
||||||
|
socket.connect();
|
||||||
|
}
|
||||||
|
catch (URISyntaxException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close()
|
||||||
|
{
|
||||||
|
socket.off();
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void postCommand(Command command)
|
||||||
|
{
|
||||||
|
JSONObject msg = command.toJSON();
|
||||||
|
if (msg == null) return;
|
||||||
|
|
||||||
|
Long now = new Date().getTime();
|
||||||
|
Event e = new Event(command.getId(), now, msg.toString());
|
||||||
|
e.save();
|
||||||
|
|
||||||
|
Log.i("SyncManager", "Adding to outbox: " + msg.toString());
|
||||||
|
|
||||||
|
pendingEmit.add(e);
|
||||||
|
if (readyToEmit) emitPending();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void emitPending()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (Event e : pendingEmit)
|
||||||
|
{
|
||||||
|
Log.i("SyncManager", "Emitting: " + e.message);
|
||||||
|
socket.emit(EVENT_POST_EVENT, new JSONObject(e.message));
|
||||||
|
pendingConfirmation.add(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingEmit.clear();
|
||||||
|
}
|
||||||
|
catch (JSONException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SSLContext getCACertSSLContext()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||||
|
InputStream caInput = context.getAssets().open("cacert.pem");
|
||||||
|
Certificate ca = cf.generateCertificate(caInput);
|
||||||
|
|
||||||
|
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||||
|
ks.load(null, null);
|
||||||
|
ks.setCertificateEntry("ca", ca);
|
||||||
|
|
||||||
|
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
|
||||||
|
TrustManagerFactory.getDefaultAlgorithm());
|
||||||
|
tmf.init(ks);
|
||||||
|
|
||||||
|
SSLContext ctx = SSLContext.getInstance("TLS");
|
||||||
|
ctx.init(null, tmf.getTrustManagers(), null);
|
||||||
|
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logSocketEvent(Socket socket, String event, final String msg)
|
||||||
|
{
|
||||||
|
socket.on(event, args -> Log.i("SyncManager", msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateLastSync(Long timestamp)
|
||||||
|
{
|
||||||
|
prefs.setLastSync(timestamp + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class OnAuthOKListener implements Emitter.Listener
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void call(Object... args)
|
||||||
|
{
|
||||||
|
Log.i("SyncManager", "Auth OK");
|
||||||
|
Log.i("SyncManager", "Requesting commands since last sync");
|
||||||
|
|
||||||
|
Long lastSync = prefs.getLastSync();
|
||||||
|
socket.emit(EVENT_FETCH, buildFetchMessage(lastSync));
|
||||||
|
}
|
||||||
|
|
||||||
|
private JSONObject buildFetchMessage(Long lastSync)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JSONObject json = new JSONObject();
|
||||||
|
json.put("since", lastSync);
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
catch (JSONException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class OnConnectListener implements Emitter.Listener
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void call(Object... args)
|
||||||
|
{
|
||||||
|
Log.i("SyncManager", "Sending auth message");
|
||||||
|
socket.emit(EVENT_AUTH, buildAuthMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
private JSONObject buildAuthMessage()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JSONObject json = new JSONObject();
|
||||||
|
json.put("groupKey", GROUP_KEY);
|
||||||
|
json.put("clientId", CLIENT_ID);
|
||||||
|
json.put("version", BuildConfig.VERSION_NAME);
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
catch (JSONException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class OnDisconnectListener implements Emitter.Listener
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void call(Object... args)
|
||||||
|
{
|
||||||
|
readyToEmit = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class OnExecuteCommandListener implements Emitter.Listener
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void call(Object... args)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Log.d("SyncManager",
|
||||||
|
String.format("Received command: %s", args[0].toString()));
|
||||||
|
JSONObject root = new JSONObject(args[0].toString());
|
||||||
|
updateLastSync(root.getLong("timestamp"));
|
||||||
|
executeCommand(root);
|
||||||
|
}
|
||||||
|
catch (JSONException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void executeCommand(JSONObject root) throws JSONException
|
||||||
|
{
|
||||||
|
Command received = commandParser.fromJSON(root);
|
||||||
|
for (Event e : pendingConfirmation)
|
||||||
|
{
|
||||||
|
if (e.serverId.equals(received.getId()))
|
||||||
|
{
|
||||||
|
Log.i("SyncManager", "Pending command confirmed");
|
||||||
|
pendingConfirmation.remove(e);
|
||||||
|
e.delete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d("SyncManager", "Executing received command");
|
||||||
|
commandRunner.execute(received, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class OnFetchOKListener implements Emitter.Listener
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void call(Object... args)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Log.i("SyncManager", "Fetch OK");
|
||||||
|
|
||||||
|
JSONObject json = (JSONObject) args[0];
|
||||||
|
updateLastSync(json.getLong("timestamp"));
|
||||||
|
|
||||||
|
emitPending();
|
||||||
|
readyToEmit = true;
|
||||||
|
}
|
||||||
|
catch (JSONException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,9 +27,12 @@ import com.activeandroid.*;
|
|||||||
import org.isoron.uhabits.*;
|
import org.isoron.uhabits.*;
|
||||||
import org.isoron.uhabits.models.sqlite.*;
|
import org.isoron.uhabits.models.sqlite.*;
|
||||||
import org.isoron.uhabits.models.sqlite.records.*;
|
import org.isoron.uhabits.models.sqlite.records.*;
|
||||||
|
import org.isoron.uhabits.sync.*;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.math.*;
|
||||||
import java.text.*;
|
import java.text.*;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
public abstract class DatabaseUtils
|
public abstract class DatabaseUtils
|
||||||
{
|
{
|
||||||
@@ -67,6 +70,11 @@ public abstract class DatabaseUtils
|
|||||||
return databaseFilename;
|
return databaseFilename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getRandomId()
|
||||||
|
{
|
||||||
|
return new BigInteger(260, new Random()).toString(32).substring(0, 32);
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static void initializeActiveAndroid(Context context)
|
public static void initializeActiveAndroid(Context context)
|
||||||
{
|
{
|
||||||
@@ -74,7 +82,8 @@ public abstract class DatabaseUtils
|
|||||||
.setDatabaseName(getDatabaseFilename())
|
.setDatabaseName(getDatabaseFilename())
|
||||||
.setDatabaseVersion(BuildConfig.databaseVersion)
|
.setDatabaseVersion(BuildConfig.databaseVersion)
|
||||||
.addModelClasses(CheckmarkRecord.class, HabitRecord.class,
|
.addModelClasses(CheckmarkRecord.class, HabitRecord.class,
|
||||||
RepetitionRecord.class, ScoreRecord.class, StreakRecord.class)
|
RepetitionRecord.class, ScoreRecord.class, StreakRecord.class,
|
||||||
|
Event.class)
|
||||||
.create();
|
.create();
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -147,6 +147,11 @@
|
|||||||
android:key="pref_feature_numerical_habits"
|
android:key="pref_feature_numerical_habits"
|
||||||
android:title="Enable numerical habits"/>
|
android:title="Enable numerical habits"/>
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:key="pref_sync_key"
|
||||||
|
android:title="Sync: group key"
|
||||||
|
android:summary="%s"/>
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
@@ -32,6 +32,7 @@ import org.isoron.uhabits.commands.*;
|
|||||||
import org.isoron.uhabits.intents.*;
|
import org.isoron.uhabits.intents.*;
|
||||||
import org.isoron.uhabits.io.*;
|
import org.isoron.uhabits.io.*;
|
||||||
import org.isoron.uhabits.models.*;
|
import org.isoron.uhabits.models.*;
|
||||||
|
import org.isoron.uhabits.preferences.*;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
import org.junit.runner.*;
|
import org.junit.runner.*;
|
||||||
import org.junit.runners.*;
|
import org.junit.runners.*;
|
||||||
@@ -77,6 +78,8 @@ public class ListHabitsScreenTest extends BaseUnitTest
|
|||||||
|
|
||||||
private ListHabitsScreen baseScreen;
|
private ListHabitsScreen baseScreen;
|
||||||
|
|
||||||
|
private Preferences prefs;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@Override
|
@Override
|
||||||
public void setUp()
|
public void setUp()
|
||||||
@@ -93,10 +96,12 @@ public class ListHabitsScreenTest extends BaseUnitTest
|
|||||||
filePickerDialogFactory = mock(FilePickerDialogFactory.class);
|
filePickerDialogFactory = mock(FilePickerDialogFactory.class);
|
||||||
colorPickerDialogFactory = mock(ColorPickerDialogFactory.class);
|
colorPickerDialogFactory = mock(ColorPickerDialogFactory.class);
|
||||||
dialogFactory = mock(EditHabitDialogFactory.class);
|
dialogFactory = mock(EditHabitDialogFactory.class);
|
||||||
|
prefs = mock(Preferences.class);
|
||||||
|
|
||||||
screen = spy(new ListHabitsScreen(activity, commandRunner, dirFinder,
|
screen = spy(new ListHabitsScreen(activity, commandRunner, dirFinder,
|
||||||
rootView, intentFactory, themeSwitcher, confirmDeleteDialogFactory,
|
rootView, intentFactory, themeSwitcher, confirmDeleteDialogFactory,
|
||||||
filePickerDialogFactory, colorPickerDialogFactory, dialogFactory));
|
filePickerDialogFactory, colorPickerDialogFactory, dialogFactory,
|
||||||
|
prefs));
|
||||||
|
|
||||||
doNothing().when(screen).showMessage(anyInt());
|
doNothing().when(screen).showMessage(anyInt());
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user