mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 09:08:52 -06:00
Major refactoring of ListHabitsActivity
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 551 B |
Binary file not shown.
|
After Width: | Height: | Size: 505 B |
Binary file not shown.
|
After Width: | Height: | Size: 559 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Component;
|
||||
|
||||
@Singleton
|
||||
@Component(modules = {AndroidModule.class})
|
||||
public interface AndroidTestComponent extends BaseComponent
|
||||
{
|
||||
void inject(BaseAndroidTest baseAndroidTest);
|
||||
}
|
||||
|
||||
@@ -27,11 +27,14 @@ import android.support.test.InstrumentationRegistry;
|
||||
import org.isoron.uhabits.utils.DateUtils;
|
||||
import org.isoron.uhabits.utils.InterfaceUtils;
|
||||
import org.isoron.uhabits.tasks.BaseTask;
|
||||
import org.isoron.uhabits.utils.Preferences;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
public class BaseTest
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class BaseAndroidTest
|
||||
{
|
||||
protected Context testContext;
|
||||
protected Context targetContext;
|
||||
@@ -39,8 +42,12 @@ public class BaseTest
|
||||
|
||||
public static final long FIXED_LOCAL_TIME = 1422172800000L; // 8:00am, January 25th, 2015 (UTC)
|
||||
|
||||
@Inject
|
||||
protected Preferences prefs;
|
||||
protected AndroidTestComponent androidTestComponent;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
if(!isLooperPrepared)
|
||||
{
|
||||
@@ -53,6 +60,10 @@ public class BaseTest
|
||||
|
||||
InterfaceUtils.setFixedTheme(R.style.AppBaseTheme);
|
||||
DateUtils.setFixedLocalTime(FIXED_LOCAL_TIME);
|
||||
|
||||
androidTestComponent = DaggerAndroidTestComponent.builder().build();
|
||||
HabitsApplication.setComponent(androidTestComponent);
|
||||
androidTestComponent.inject(this);
|
||||
}
|
||||
|
||||
protected void waitForAsyncTasks() throws InterruptedException, TimeoutException
|
||||
@@ -61,7 +61,7 @@ public class HabitViewActions
|
||||
@Override
|
||||
public void perform(UiController uiController, View view)
|
||||
{
|
||||
if (view.getId() != R.id.llButtons)
|
||||
if (view.getId() != R.id.checkmarkPanel)
|
||||
throw new InvalidParameterException("View must have id llButtons");
|
||||
|
||||
LinearLayout llButtons = (LinearLayout) view;
|
||||
|
||||
@@ -191,7 +191,7 @@ public class MainTest
|
||||
String name = addHabit(true);
|
||||
|
||||
onData(allOf(is(instanceOf(Habit.class)), withName(name)))
|
||||
.onChildView(withId(R.id.llButtons))
|
||||
.onChildView(withId(R.id.checkmarkPanel))
|
||||
.perform(toggleAllCheckmarks());
|
||||
|
||||
Thread.sleep(1200);
|
||||
|
||||
@@ -23,8 +23,9 @@ import android.os.Build;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.BaseAndroidTest;
|
||||
import org.isoron.uhabits.HabitsApplication;
|
||||
import org.isoron.uhabits.ui.BaseSystem;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@@ -35,7 +36,7 @@ import static org.hamcrest.Matchers.containsString;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class HabitsApplicationTest extends BaseTest
|
||||
public class HabitsApplicationTest extends BaseAndroidTest
|
||||
{
|
||||
@Test
|
||||
public void test_getLogcat() throws IOException
|
||||
@@ -49,7 +50,8 @@ public class HabitsApplicationTest extends BaseTest
|
||||
HabitsApplication app = HabitsApplication.getInstance();
|
||||
assert(app != null);
|
||||
|
||||
String log = app.getLogcat();
|
||||
BaseSystem system = new BaseSystem(targetContext);
|
||||
String log = system.getLogcat();
|
||||
assertThat(log, containsString(msg));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ package org.isoron.uhabits.unit.commands;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.BaseAndroidTest;
|
||||
import org.isoron.uhabits.commands.ArchiveHabitsCommand;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.unit.HabitFixtures;
|
||||
@@ -37,16 +37,16 @@ import static junit.framework.Assert.assertTrue;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class ArchiveHabitsCommandTest extends BaseTest
|
||||
public class ArchiveHabitsCommandTest extends BaseAndroidTest
|
||||
{
|
||||
|
||||
private ArchiveHabitsCommand command;
|
||||
private Habit habit;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
|
||||
habit = HabitFixtures.createShortHabit();
|
||||
command = new ArchiveHabitsCommand(Collections.singletonList(habit));
|
||||
|
||||
@@ -22,7 +22,7 @@ package org.isoron.uhabits.unit.commands;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.BaseAndroidTest;
|
||||
import org.isoron.uhabits.commands.ChangeHabitColorCommand;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.unit.HabitFixtures;
|
||||
@@ -37,15 +37,15 @@ import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class ChangeHabitColorCommandTest extends BaseTest
|
||||
public class ChangeHabitColorCommandTest extends BaseAndroidTest
|
||||
{
|
||||
private ChangeHabitColorCommand command;
|
||||
private LinkedList<Habit> habits;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
|
||||
habits = new LinkedList<>();
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ package org.isoron.uhabits.unit.commands;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.BaseAndroidTest;
|
||||
import org.isoron.uhabits.commands.CreateHabitCommand;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.unit.HabitFixtures;
|
||||
@@ -38,16 +38,16 @@ import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class CreateHabitCommandTest extends BaseTest
|
||||
public class CreateHabitCommandTest extends BaseAndroidTest
|
||||
{
|
||||
|
||||
private CreateHabitCommand command;
|
||||
private Habit model;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
|
||||
model = new Habit();
|
||||
model.name = "New habit";
|
||||
|
||||
@@ -22,7 +22,7 @@ package org.isoron.uhabits.unit.commands;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.BaseAndroidTest;
|
||||
import org.isoron.uhabits.commands.DeleteHabitsCommand;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.unit.HabitFixtures;
|
||||
@@ -39,7 +39,7 @@ import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class DeleteHabitsCommandTest extends BaseTest
|
||||
public class DeleteHabitsCommandTest extends BaseAndroidTest
|
||||
{
|
||||
private DeleteHabitsCommand command;
|
||||
private LinkedList<Habit> habits;
|
||||
@@ -48,9 +48,9 @@ public class DeleteHabitsCommandTest extends BaseTest
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
|
||||
HabitFixtures.purgeHabits();
|
||||
habits = new LinkedList<>();
|
||||
|
||||
@@ -22,7 +22,7 @@ package org.isoron.uhabits.unit.commands;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.BaseAndroidTest;
|
||||
import org.isoron.uhabits.commands.EditHabitCommand;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.unit.HabitFixtures;
|
||||
@@ -37,7 +37,7 @@ import static org.hamcrest.Matchers.greaterThan;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class EditHabitCommandTest extends BaseTest
|
||||
public class EditHabitCommandTest extends BaseAndroidTest
|
||||
{
|
||||
|
||||
private EditHabitCommand command;
|
||||
@@ -46,9 +46,9 @@ public class EditHabitCommandTest extends BaseTest
|
||||
private Long id;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
|
||||
habit = HabitFixtures.createShortHabit();
|
||||
habit.name = "original";
|
||||
|
||||
@@ -22,7 +22,7 @@ package org.isoron.uhabits.unit.commands;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.BaseAndroidTest;
|
||||
import org.isoron.uhabits.commands.ToggleRepetitionCommand;
|
||||
import org.isoron.uhabits.utils.DateUtils;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
@@ -36,7 +36,7 @@ import static junit.framework.Assert.assertTrue;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class ToggleRepetitionCommandTest extends BaseTest
|
||||
public class ToggleRepetitionCommandTest extends BaseAndroidTest
|
||||
{
|
||||
|
||||
private ToggleRepetitionCommand command;
|
||||
@@ -44,9 +44,9 @@ public class ToggleRepetitionCommandTest extends BaseTest
|
||||
private long today;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
|
||||
habit = HabitFixtures.createShortHabit();
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ package org.isoron.uhabits.unit.commands;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.BaseAndroidTest;
|
||||
import org.isoron.uhabits.commands.UnarchiveHabitsCommand;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.unit.HabitFixtures;
|
||||
@@ -37,16 +37,16 @@ import static junit.framework.Assert.assertTrue;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class UnarchiveHabitsCommandTest extends BaseTest
|
||||
public class UnarchiveHabitsCommandTest extends BaseAndroidTest
|
||||
{
|
||||
|
||||
private UnarchiveHabitsCommand command;
|
||||
private Habit habit;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
|
||||
habit = HabitFixtures.createShortHabit();
|
||||
Habit.archive(Collections.singletonList(habit));
|
||||
|
||||
@@ -24,7 +24,7 @@ import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.BaseAndroidTest;
|
||||
import org.isoron.uhabits.utils.FileUtils;
|
||||
import org.isoron.uhabits.io.HabitsCSVExporter;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
@@ -45,14 +45,14 @@ import static junit.framework.Assert.assertTrue;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class HabitsCSVExporterTest extends BaseTest
|
||||
public class HabitsCSVExporterTest extends BaseAndroidTest
|
||||
{
|
||||
private File baseDir;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
|
||||
HabitFixtures.purgeHabits();
|
||||
HabitFixtures.createShortHabit();
|
||||
|
||||
@@ -24,7 +24,7 @@ import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.BaseAndroidTest;
|
||||
import org.isoron.uhabits.utils.FileUtils;
|
||||
import org.isoron.uhabits.utils.DateUtils;
|
||||
import org.isoron.uhabits.io.GenericImporter;
|
||||
@@ -49,15 +49,15 @@ import static org.junit.Assert.fail;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class ImportTest extends BaseTest
|
||||
public class ImportTest extends BaseAndroidTest
|
||||
{
|
||||
private File baseDir;
|
||||
private Context context;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
DateUtils.setFixedLocalTime(null);
|
||||
|
||||
HabitFixtures.purgeHabits();
|
||||
|
||||
@@ -22,7 +22,7 @@ package org.isoron.uhabits.unit.models;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.BaseAndroidTest;
|
||||
import org.isoron.uhabits.utils.DateUtils;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.unit.HabitFixtures;
|
||||
@@ -42,15 +42,15 @@ import static org.isoron.uhabits.models.Checkmark.UNCHECKED;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class CheckmarkListTest extends BaseTest
|
||||
public class CheckmarkListTest extends BaseAndroidTest
|
||||
{
|
||||
Habit nonDailyHabit;
|
||||
private Habit emptyHabit;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
|
||||
HabitFixtures.purgeHabits();
|
||||
nonDailyHabit = HabitFixtures.createShortHabit();
|
||||
|
||||
@@ -23,7 +23,7 @@ import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.hamcrest.MatcherAssert;
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.BaseAndroidTest;
|
||||
import org.isoron.uhabits.utils.DateUtils;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.unit.HabitFixtures;
|
||||
@@ -45,12 +45,12 @@ import static org.junit.Assert.fail;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class HabitTest extends BaseTest
|
||||
public class HabitTest extends BaseAndroidTest
|
||||
{
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
HabitFixtures.purgeHabits();
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ package org.isoron.uhabits.unit.models;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.BaseAndroidTest;
|
||||
import org.isoron.uhabits.utils.DateUtils;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.models.Repetition;
|
||||
@@ -44,15 +44,15 @@ import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class RepetitionListTest extends BaseTest
|
||||
public class RepetitionListTest extends BaseAndroidTest
|
||||
{
|
||||
private Habit habit;
|
||||
private Habit emptyHabit;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
|
||||
HabitFixtures.purgeHabits();
|
||||
habit = HabitFixtures.createShortHabit();
|
||||
|
||||
@@ -22,7 +22,7 @@ package org.isoron.uhabits.unit.models;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.BaseAndroidTest;
|
||||
import org.isoron.uhabits.utils.DateUtils;
|
||||
import org.isoron.uhabits.utils.DatabaseUtils;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
@@ -41,14 +41,14 @@ import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class ScoreListTest extends BaseTest
|
||||
public class ScoreListTest extends BaseAndroidTest
|
||||
{
|
||||
private Habit habit;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
|
||||
HabitFixtures.purgeHabits();
|
||||
habit = HabitFixtures.createEmptyHabit();
|
||||
|
||||
@@ -22,7 +22,7 @@ package org.isoron.uhabits.unit.models;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.BaseAndroidTest;
|
||||
import org.isoron.uhabits.models.Checkmark;
|
||||
import org.isoron.uhabits.models.Score;
|
||||
import org.junit.Before;
|
||||
@@ -34,12 +34,12 @@ import static org.junit.Assert.assertThat;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class ScoreTest extends BaseTest
|
||||
public class ScoreTest extends BaseAndroidTest
|
||||
{
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -22,7 +22,7 @@ package org.isoron.uhabits.unit.tasks;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.BaseAndroidTest;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.tasks.ExportCSVTask;
|
||||
import org.isoron.uhabits.unit.HabitFixtures;
|
||||
@@ -41,12 +41,12 @@ import static org.hamcrest.core.IsNot.not;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class ExportCSVTaskTest extends BaseTest
|
||||
public class ExportCSVTaskTest extends BaseAndroidTest
|
||||
{
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -22,7 +22,7 @@ package org.isoron.uhabits.unit.tasks;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.BaseAndroidTest;
|
||||
import org.isoron.uhabits.tasks.ExportDBTask;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -38,12 +38,12 @@ import static org.hamcrest.core.IsNot.not;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class ExportDBTaskTest extends BaseTest
|
||||
public class ExportDBTaskTest extends BaseAndroidTest
|
||||
{
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -23,7 +23,7 @@ import android.support.annotation.NonNull;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.BaseAndroidTest;
|
||||
import org.isoron.uhabits.tasks.ImportDataTask;
|
||||
import org.isoron.uhabits.utils.FileUtils;
|
||||
import org.junit.Before;
|
||||
@@ -40,14 +40,14 @@ import static org.junit.Assert.fail;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class ImportDataTaskTest extends BaseTest
|
||||
public class ImportDataTaskTest extends BaseAndroidTest
|
||||
{
|
||||
private File baseDir;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
|
||||
baseDir = FileUtils.getFilesDir("Backups");
|
||||
if(baseDir == null) fail("baseDir should not be null");
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* 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.unit.ui.habits.list.view;
|
||||
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.models.Checkmark;
|
||||
import org.isoron.uhabits.ui.habits.list.views.CheckmarkButtonView;
|
||||
import org.isoron.uhabits.unit.views.ViewTest;
|
||||
import org.isoron.uhabits.utils.ColorUtils;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class CheckmarkButtonViewTest extends ViewTest
|
||||
{
|
||||
public static final String PATH = "ui/habits/list/CheckmarkButtonView/";
|
||||
|
||||
private CountDownLatch latch;
|
||||
private CheckmarkButtonView view;
|
||||
|
||||
@Before
|
||||
public void setUp()
|
||||
{
|
||||
super.setUp();
|
||||
setSimilarityCutoff(0.03f);
|
||||
|
||||
latch = new CountDownLatch(1);
|
||||
view = new CheckmarkButtonView(targetContext);
|
||||
view.setValue(Checkmark.UNCHECKED);
|
||||
view.setColor(ColorUtils.CSV_PALETTE[7]);
|
||||
|
||||
measureView(dpToPixels(40), dpToPixels(40), view);
|
||||
}
|
||||
|
||||
protected void assertRendersCheckedExplicitly() throws IOException
|
||||
{
|
||||
assertRenders(view, PATH + "render_explicit_check.png");
|
||||
}
|
||||
|
||||
protected void assertRendersUnchecked() throws IOException
|
||||
{
|
||||
assertRenders(view, PATH + "render_unchecked.png");
|
||||
}
|
||||
|
||||
protected void assertRendersCheckedImplicitly() throws IOException
|
||||
{
|
||||
assertRenders(view, PATH + "render_implicit_check.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_unchecked() throws Exception
|
||||
{
|
||||
view.setValue(Checkmark.UNCHECKED);
|
||||
assertRendersUnchecked();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_explicitCheck() throws Exception
|
||||
{
|
||||
view.setValue(Checkmark.CHECKED_EXPLICITLY);
|
||||
assertRendersCheckedExplicitly();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_implicitCheck() throws Exception
|
||||
{
|
||||
view.setValue(Checkmark.CHECKED_IMPLICITLY);
|
||||
assertRendersCheckedImplicitly();
|
||||
}
|
||||
|
||||
// @Test
|
||||
// public void testLongClick() throws Exception
|
||||
// {
|
||||
// setOnToggleListener();
|
||||
// view.performLongClick();
|
||||
// waitForLatch();
|
||||
// assertRendersCheckedExplicitly();
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testClick_withShortToggle_fromUnchecked() throws Exception
|
||||
// {
|
||||
// Preferences.getInstance().setShortToggleEnabled(true);
|
||||
// view.setValue(Checkmark.UNCHECKED);
|
||||
// setOnToggleListenerAndPerformClick();
|
||||
// assertRendersCheckedExplicitly();
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testClick_withShortToggle_fromChecked() throws Exception
|
||||
// {
|
||||
// Preferences.getInstance().setShortToggleEnabled(true);
|
||||
// view.setValue(Checkmark.CHECKED_EXPLICITLY);
|
||||
// setOnToggleListenerAndPerformClick();
|
||||
// assertRendersUnchecked();
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testClick_withShortToggle_withoutListener() throws Exception
|
||||
// {
|
||||
// Preferences.getInstance().setShortToggleEnabled(true);
|
||||
// view.setValue(Checkmark.CHECKED_EXPLICITLY);
|
||||
// view.setController(null);
|
||||
// view.performClick();
|
||||
// assertRendersUnchecked();
|
||||
// }
|
||||
//
|
||||
// protected void setOnToggleListenerAndPerformClick() throws InterruptedException
|
||||
// {
|
||||
// setOnToggleListener();
|
||||
// view.performClick();
|
||||
// waitForLatch();
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testClick_withoutShortToggle() throws Exception
|
||||
// {
|
||||
// Preferences.getInstance().setShortToggleEnabled(false);
|
||||
// setOnInvalidToggleListener();
|
||||
// view.performClick();
|
||||
// waitForLatch();
|
||||
// assertRendersUnchecked();
|
||||
// }
|
||||
|
||||
// protected void setOnInvalidToggleListener()
|
||||
// {
|
||||
// view.setController(new CheckmarkButtonView.Controller()
|
||||
// {
|
||||
// @Override
|
||||
// public void onToggleCheckmark(CheckmarkButtonView view, long timestamp)
|
||||
// {
|
||||
// fail();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onInvalidToggle(CheckmarkButtonView v)
|
||||
// {
|
||||
// assertThat(v, equalTo(view));
|
||||
// latch.countDown();
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
// protected void setOnToggleListener()
|
||||
// {
|
||||
// view.setController(new CheckmarkButtonView.Controller()
|
||||
// {
|
||||
// @Override
|
||||
// public void onToggleCheckmark(CheckmarkButtonView v, long t)
|
||||
// {
|
||||
// assertThat(v, equalTo(view));
|
||||
// assertThat(t, equalTo(DateUtils.getStartOfToday()));
|
||||
// latch.countDown();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onInvalidToggle(CheckmarkButtonView view)
|
||||
// {
|
||||
// fail();
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.unit.ui.habits.list.view;
|
||||
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.models.Checkmark;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.ui.habits.list.views.CheckmarkPanelView;
|
||||
import org.isoron.uhabits.unit.views.ViewTest;
|
||||
import org.isoron.uhabits.utils.ColorUtils;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class CheckmarkPanelViewTest extends ViewTest
|
||||
{
|
||||
public static final String PATH = "ui/habits/list/CheckmarkPanelView/";
|
||||
|
||||
private CountDownLatch latch;
|
||||
private CheckmarkPanelView view;
|
||||
private int checkmarks[];
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp()
|
||||
{
|
||||
super.setUp();
|
||||
setSimilarityCutoff(0.03f);
|
||||
prefs.setShouldReverseCheckmarks(false);
|
||||
|
||||
Habit habit = new Habit();
|
||||
|
||||
latch = new CountDownLatch(1);
|
||||
checkmarks = new int[]{Checkmark.CHECKED_EXPLICITLY, Checkmark.UNCHECKED,
|
||||
Checkmark.CHECKED_IMPLICITLY, Checkmark.CHECKED_EXPLICITLY};
|
||||
|
||||
view = new CheckmarkPanelView(targetContext);
|
||||
view.setHabit(habit);
|
||||
view.setCheckmarkValues(checkmarks);
|
||||
view.setColor(ColorUtils.CSV_PALETTE[7]);
|
||||
|
||||
measureView(dpToPixels(200), dpToPixels(200), view);
|
||||
}
|
||||
|
||||
// protected void waitForLatch() throws InterruptedException
|
||||
// {
|
||||
// assertTrue("Latch timeout", latch.await(1, TimeUnit.SECONDS));
|
||||
// }
|
||||
|
||||
@Test
|
||||
public void testRender() throws Exception
|
||||
{
|
||||
assertRenders(view, PATH + "render.png");
|
||||
}
|
||||
|
||||
// @Test
|
||||
// public void testToggleCheckmark_withLeftToRight() throws Exception
|
||||
// {
|
||||
// setToggleListener();
|
||||
// view.getButton(1).performToggle();
|
||||
// waitForLatch();
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testToggleCheckmark_withReverseCheckmarks() throws Exception
|
||||
// {
|
||||
// prefs.setShouldReverseCheckmarks(true);
|
||||
// view.setCheckmarkValues(checkmarks); // refresh after preference change
|
||||
//
|
||||
// setToggleListener();
|
||||
// view.getButton(2).performToggle();
|
||||
// waitForLatch();
|
||||
// }
|
||||
}
|
||||
@@ -42,9 +42,9 @@ public class CheckmarkWidgetViewTest extends ViewTest
|
||||
private Habit habit;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
InterfaceUtils.setFixedTheme(R.style.TransparentWidgetTheme);
|
||||
|
||||
habit = HabitFixtures.createShortHabit();
|
||||
|
||||
@@ -36,9 +36,9 @@ public class HabitFrequencyViewTest extends ViewTest
|
||||
private HabitFrequencyView view;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
|
||||
HabitFixtures.purgeHabits();
|
||||
Habit habit = HabitFixtures.createLongHabit();
|
||||
|
||||
@@ -43,9 +43,9 @@ public class HabitHistoryViewTest extends ViewTest
|
||||
private HabitHistoryView view;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
|
||||
HabitFixtures.purgeHabits();
|
||||
habit = HabitFixtures.createLongHabit();
|
||||
|
||||
@@ -38,9 +38,9 @@ public class HabitScoreViewTest extends ViewTest
|
||||
private HabitScoreView view;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
|
||||
HabitFixtures.purgeHabits();
|
||||
habit = HabitFixtures.createLongHabit();
|
||||
|
||||
@@ -36,9 +36,9 @@ public class HabitStreakViewTest extends ViewTest
|
||||
private HabitStreakView view;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
|
||||
HabitFixtures.purgeHabits();
|
||||
Habit habit = HabitFixtures.createLongHabit();
|
||||
|
||||
@@ -38,9 +38,9 @@ public class NumberViewTest extends ViewTest
|
||||
private NumberView view;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
|
||||
view = new NumberView(targetContext);
|
||||
view.setLabel("Hello world");
|
||||
|
||||
@@ -38,9 +38,9 @@ public class RingViewTest extends ViewTest
|
||||
private RingView view;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
|
||||
view = new RingView(targetContext);
|
||||
view.setPercentage(0.6f);
|
||||
|
||||
@@ -26,7 +26,7 @@ import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.BaseAndroidTest;
|
||||
import org.isoron.uhabits.utils.FileUtils;
|
||||
import org.isoron.uhabits.utils.InterfaceUtils;
|
||||
import org.isoron.uhabits.tasks.BaseTask;
|
||||
@@ -39,10 +39,23 @@ import java.io.InputStream;
|
||||
|
||||
import static junit.framework.Assert.fail;
|
||||
|
||||
public class ViewTest extends BaseTest
|
||||
public class ViewTest extends BaseAndroidTest
|
||||
{
|
||||
protected static final double SIMILARITY_CUTOFF = 0.09;
|
||||
protected static final double DEFAULT_SIMILARITY_CUTOFF = 0.09;
|
||||
public static final int HISTOGRAM_BIN_SIZE = 8;
|
||||
private double similarityCutoff;
|
||||
|
||||
@Override
|
||||
public void setUp()
|
||||
{
|
||||
super.setUp();
|
||||
similarityCutoff = DEFAULT_SIMILARITY_CUTOFF;
|
||||
}
|
||||
|
||||
protected void setSimilarityCutoff(double similarityCutoff)
|
||||
{
|
||||
this.similarityCutoff = similarityCutoff;
|
||||
}
|
||||
|
||||
protected void measureView(int width, int height, View view)
|
||||
{
|
||||
@@ -70,7 +83,8 @@ public class ViewTest extends BaseTest
|
||||
double distance;
|
||||
boolean similarEnough = true;
|
||||
|
||||
if ((distance = compareHistograms(getHistogram(actual), getHistogram(scaledExpected))) > SIMILARITY_CUTOFF)
|
||||
if ((distance = compareHistograms(getHistogram(actual), getHistogram(scaledExpected))) >
|
||||
similarityCutoff)
|
||||
{
|
||||
similarEnough = false;
|
||||
errorMessage.append(String.format(
|
||||
|
||||
31
app/src/main/java/org/isoron/uhabits/AndroidComponent.java
Normal file
31
app/src/main/java/org/isoron/uhabits/AndroidComponent.java
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Component;
|
||||
|
||||
@Singleton
|
||||
@Component(modules = {AndroidModule.class})
|
||||
public interface AndroidComponent extends BaseComponent
|
||||
{
|
||||
}
|
||||
|
||||
54
app/src/main/java/org/isoron/uhabits/AndroidModule.java
Normal file
54
app/src/main/java/org/isoron/uhabits/AndroidModule.java
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.isoron.uhabits.commands.CommandRunner;
|
||||
import org.isoron.uhabits.ui.habits.list.model.HabitCardListCache;
|
||||
import org.isoron.uhabits.utils.Preferences;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class AndroidModule
|
||||
{
|
||||
@Provides
|
||||
@Singleton
|
||||
Preferences providePreferences()
|
||||
{
|
||||
return new Preferences();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
CommandRunner provideCommandRunner()
|
||||
{
|
||||
return new CommandRunner();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
HabitCardListCache provideHabitCardListCache()
|
||||
{
|
||||
return new HabitCardListCache();
|
||||
}
|
||||
}
|
||||
53
app/src/main/java/org/isoron/uhabits/BaseComponent.java
Normal file
53
app/src/main/java/org/isoron/uhabits/BaseComponent.java
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.isoron.uhabits.tasks.ToggleRepetitionTask;
|
||||
import org.isoron.uhabits.ui.habits.edit.BaseDialogFragment;
|
||||
import org.isoron.uhabits.ui.habits.list.ListHabitsSelectionMenu;
|
||||
import org.isoron.uhabits.ui.habits.list.model.HabitCardListAdapter;
|
||||
import org.isoron.uhabits.ui.habits.list.model.HabitCardListCache;
|
||||
import org.isoron.uhabits.ui.habits.list.ListHabitsController;
|
||||
import org.isoron.uhabits.ui.habits.list.controllers.CheckmarkButtonController;
|
||||
import org.isoron.uhabits.ui.habits.list.model.HintList;
|
||||
import org.isoron.uhabits.ui.habits.list.views.CheckmarkPanelView;
|
||||
|
||||
public interface BaseComponent
|
||||
{
|
||||
void inject(CheckmarkButtonController checkmarkButtonController);
|
||||
|
||||
void inject(ListHabitsController listHabitsController);
|
||||
|
||||
void inject(CheckmarkPanelView checkmarkPanelView);
|
||||
|
||||
void inject(ToggleRepetitionTask toggleRepetitionTask);
|
||||
|
||||
void inject(BaseDialogFragment baseDialogFragment);
|
||||
|
||||
void inject(HabitCardListCache habitCardListCache);
|
||||
|
||||
void inject(HabitBroadcastReceiver habitBroadcastReceiver);
|
||||
|
||||
void inject(ListHabitsSelectionMenu listHabitsSelectionMenu);
|
||||
|
||||
void inject(HintList hintList);
|
||||
|
||||
void inject(HabitCardListAdapter habitCardListAdapter);
|
||||
}
|
||||
@@ -38,16 +38,18 @@ import android.support.v4.content.LocalBroadcastManager;
|
||||
|
||||
import org.isoron.uhabits.commands.CommandRunner;
|
||||
import org.isoron.uhabits.commands.ToggleRepetitionCommand;
|
||||
import org.isoron.uhabits.utils.DateUtils;
|
||||
import org.isoron.uhabits.utils.ReminderUtils;
|
||||
import org.isoron.uhabits.models.Checkmark;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.tasks.BaseTask;
|
||||
import org.isoron.uhabits.ui.habits.show.ShowHabitActivity;
|
||||
import org.isoron.uhabits.utils.DateUtils;
|
||||
import org.isoron.uhabits.utils.ReminderUtils;
|
||||
import org.isoron.uhabits.widgets.WidgetManager;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class HabitBroadcastReceiver extends BroadcastReceiver
|
||||
{
|
||||
public static final String ACTION_CHECK = "org.isoron.uhabits.ACTION_CHECK";
|
||||
@@ -55,6 +57,15 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
|
||||
public static final String ACTION_SHOW_REMINDER = "org.isoron.uhabits.ACTION_SHOW_REMINDER";
|
||||
public static final String ACTION_SNOOZE = "org.isoron.uhabits.ACTION_SNOOZE";
|
||||
|
||||
@Inject
|
||||
CommandRunner commandRunner;
|
||||
|
||||
public HabitBroadcastReceiver()
|
||||
{
|
||||
super();
|
||||
HabitsApplication.getComponent().inject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(final Context context, Intent intent)
|
||||
{
|
||||
@@ -85,14 +96,7 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
|
||||
|
||||
private void createReminderAlarmsDelayed(final Context context)
|
||||
{
|
||||
new Handler().postDelayed(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
ReminderUtils.createReminderAlarms(context);
|
||||
}
|
||||
}, 5000);
|
||||
new Handler().postDelayed(() -> ReminderUtils.createReminderAlarms(context), 5000);
|
||||
}
|
||||
|
||||
private void snoozeHabit(Context context, Intent intent)
|
||||
@@ -119,7 +123,7 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
|
||||
if(habit != null)
|
||||
{
|
||||
ToggleRepetitionCommand command = new ToggleRepetitionCommand(habit, timestamp);
|
||||
CommandRunner.getInstance().execute(command, habitId);
|
||||
commandRunner.execute(command, habitId);
|
||||
}
|
||||
|
||||
dismissNotification(context, habitId);
|
||||
|
||||
@@ -21,28 +21,15 @@ package org.isoron.uhabits;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.activeandroid.ActiveAndroid;
|
||||
|
||||
import org.isoron.uhabits.tasks.BaseTask;
|
||||
import org.isoron.uhabits.utils.DatabaseUtils;
|
||||
import org.isoron.uhabits.utils.DateUtils;
|
||||
import org.isoron.uhabits.utils.FileUtils;
|
||||
import org.isoron.uhabits.utils.ReminderUtils;
|
||||
import org.isoron.uhabits.widgets.WidgetManager;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.LinkedList;
|
||||
|
||||
public class HabitsApplication extends Application implements MainController.System
|
||||
public class HabitsApplication extends Application
|
||||
{
|
||||
public static final String ACTION_REFRESH = "org.isoron.uhabits.ACTION_REFRESH";
|
||||
public static final int RESULT_IMPORT_DATA = 1;
|
||||
@@ -55,6 +42,7 @@ public class HabitsApplication extends Application implements MainController.Sys
|
||||
|
||||
@Nullable
|
||||
private static Context context;
|
||||
private static BaseComponent component;
|
||||
|
||||
public static boolean isTestMode()
|
||||
{
|
||||
@@ -82,12 +70,23 @@ public class HabitsApplication extends Application implements MainController.Sys
|
||||
return application;
|
||||
}
|
||||
|
||||
public static BaseComponent getComponent()
|
||||
{
|
||||
return component;
|
||||
}
|
||||
|
||||
public static void setComponent(BaseComponent component)
|
||||
{
|
||||
HabitsApplication.component = component;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate()
|
||||
{
|
||||
super.onCreate();
|
||||
HabitsApplication.context = this;
|
||||
HabitsApplication.application = this;
|
||||
component = DaggerAndroidComponent.builder().build();
|
||||
|
||||
if (isTestMode())
|
||||
{
|
||||
@@ -105,108 +104,4 @@ public class HabitsApplication extends Application implements MainController.Sys
|
||||
ActiveAndroid.dispose();
|
||||
super.onTerminate();
|
||||
}
|
||||
|
||||
public String getLogcat() throws IOException
|
||||
{
|
||||
int maxNLines = 250;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
String[] command = new String[] { "logcat", "-d"};
|
||||
Process process = Runtime.getRuntime().exec(command);
|
||||
|
||||
InputStreamReader in = new InputStreamReader(process.getInputStream());
|
||||
BufferedReader bufferedReader = new BufferedReader(in);
|
||||
|
||||
LinkedList<String> log = new LinkedList<>();
|
||||
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null)
|
||||
{
|
||||
log.addLast(line);
|
||||
if(log.size() > maxNLines) log.removeFirst();
|
||||
}
|
||||
|
||||
for(String l : log)
|
||||
{
|
||||
builder.append(l);
|
||||
builder.append('\n');
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public String getDeviceInfo()
|
||||
{
|
||||
if(context == null) return "";
|
||||
|
||||
StringBuilder b = new StringBuilder();
|
||||
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
|
||||
b.append(String.format("App Version Name: %s\n", BuildConfig.VERSION_NAME));
|
||||
b.append(String.format("App Version Code: %s\n", BuildConfig.VERSION_CODE));
|
||||
b.append(String.format("OS Version: %s (%s)\n", java.lang.System.getProperty("os.version"),
|
||||
android.os.Build.VERSION.INCREMENTAL));
|
||||
b.append(String.format("OS API Level: %s\n", android.os.Build.VERSION.SDK));
|
||||
b.append(String.format("Device: %s\n", android.os.Build.DEVICE));
|
||||
b.append(String.format("Model (Product): %s (%s)\n", android.os.Build.MODEL,
|
||||
android.os.Build.PRODUCT));
|
||||
b.append(String.format("Manufacturer: %s\n", android.os.Build.MANUFACTURER));
|
||||
b.append(String.format("Other tags: %s\n", android.os.Build.TAGS));
|
||||
b.append(String.format("Screen Width: %s\n", wm.getDefaultDisplay().getWidth()));
|
||||
b.append(String.format("Screen Height: %s\n", wm.getDefaultDisplay().getHeight()));
|
||||
b.append(String.format("SD Card state: %s\n\n", Environment.getExternalStorageState()));
|
||||
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public File dumpBugReportToFile() throws IOException
|
||||
{
|
||||
String date = DateUtils.getBackupDateFormat().format(DateUtils.getLocalTime());
|
||||
|
||||
if(context == null) throw new RuntimeException("application context should not be null");
|
||||
File dir = FileUtils.getFilesDir("Logs");
|
||||
if (dir == null) throw new IOException("log dir should not be null");
|
||||
|
||||
File logFile = new File(String.format("%s/Log %s.txt", dir.getPath(), date));
|
||||
FileWriter output = new FileWriter(logFile);
|
||||
output.write(getBugReport());
|
||||
output.close();
|
||||
|
||||
return logFile;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getBugReport() throws IOException
|
||||
{
|
||||
String logcat = getLogcat();
|
||||
String deviceInfo = getDeviceInfo();
|
||||
return deviceInfo + "\n" + logcat;
|
||||
}
|
||||
|
||||
public void scheduleReminders()
|
||||
{
|
||||
new BaseTask()
|
||||
{
|
||||
|
||||
@Override
|
||||
protected void doInBackground()
|
||||
{
|
||||
ReminderUtils.createReminderAlarms(getContext());
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
public void updateWidgets()
|
||||
{
|
||||
new BaseTask()
|
||||
{
|
||||
|
||||
@Override
|
||||
protected void doInBackground()
|
||||
{
|
||||
WidgetManager.updateWidgets(getContext());
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,9 @@ public class HabitsBackupAgent extends BackupAgentHelper
|
||||
@Override
|
||||
public void onCreate()
|
||||
{
|
||||
addHelper("preferences", new SharedPreferencesBackupHelper(this, "preferences"));
|
||||
addHelper("database", new FileBackupHelper(this, "../databases/uhabits.db"));
|
||||
addHelper("preferences",
|
||||
new SharedPreferencesBackupHelper(this, "preferences"));
|
||||
addHelper("database",
|
||||
new FileBackupHelper(this, "../databases/uhabits.db"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,222 +19,13 @@
|
||||
|
||||
package org.isoron.uhabits;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import org.isoron.uhabits.ui.habits.list.ListHabitsActivity;
|
||||
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.tasks.ProgressBar;
|
||||
import org.isoron.uhabits.ui.AndroidProgressBar;
|
||||
import org.isoron.uhabits.ui.BaseActivity;
|
||||
import org.isoron.uhabits.ui.about.AboutActivity;
|
||||
import org.isoron.uhabits.ui.habits.list.ListHabitsFragment;
|
||||
import org.isoron.uhabits.ui.habits.show.ShowHabitActivity;
|
||||
import org.isoron.uhabits.ui.intro.IntroActivity;
|
||||
import org.isoron.uhabits.ui.settings.FilePickerDialog;
|
||||
import org.isoron.uhabits.ui.settings.SettingsActivity;
|
||||
import org.isoron.uhabits.utils.FileUtils;
|
||||
import org.isoron.uhabits.utils.InterfaceUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class MainActivity extends BaseActivity
|
||||
implements ListHabitsFragment.Listener, MainController.Screen
|
||||
public class MainActivity extends ListHabitsActivity
|
||||
{
|
||||
private MainController controller;
|
||||
private ListHabitsFragment listHabitsFragment;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.list_habits_activity);
|
||||
setupSupportActionBar(false);
|
||||
|
||||
FragmentManager fragmentManager = getSupportFragmentManager();
|
||||
listHabitsFragment = (ListHabitsFragment) fragmentManager.findFragmentById(R.id.fragment1);
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
|
||||
onPreLollipopCreate();
|
||||
|
||||
controller = new MainController();
|
||||
controller.setScreen(this);
|
||||
controller.setSystem((HabitsApplication) getApplication());
|
||||
controller.onStartup();
|
||||
}
|
||||
|
||||
private void onPreLollipopCreate()
|
||||
{
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if(actionBar == null) return;
|
||||
if(InterfaceUtils.isNightMode()) return;
|
||||
|
||||
int color = getResources().getColor(R.color.grey_900);
|
||||
actionBar.setBackgroundDrawable(new ColorDrawable(color));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu)
|
||||
{
|
||||
menu.clear();
|
||||
getMenuInflater().inflate(R.menu.main_activity, menu);
|
||||
MenuItem nightModeItem = menu.findItem(R.id.action_night_mode);
|
||||
nightModeItem.setChecked(InterfaceUtils.isNightMode());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item)
|
||||
{
|
||||
switch (item.getItemId())
|
||||
{
|
||||
case R.id.action_night_mode:
|
||||
toggleNightMode();
|
||||
return true;
|
||||
|
||||
case R.id.action_settings:
|
||||
showSettingsScreen();
|
||||
return true;
|
||||
|
||||
case R.id.action_about:
|
||||
showAboutScreen();
|
||||
return true;
|
||||
|
||||
case R.id.action_faq:
|
||||
showFAQScreen();
|
||||
return true;
|
||||
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
switch (resultCode)
|
||||
{
|
||||
case HabitsApplication.RESULT_IMPORT_DATA:
|
||||
showImportScreen();
|
||||
break;
|
||||
|
||||
case HabitsApplication.RESULT_EXPORT_CSV:
|
||||
controller.exportCSV();
|
||||
break;
|
||||
|
||||
case HabitsApplication.RESULT_EXPORT_DB:
|
||||
controller.exportDB();
|
||||
break;
|
||||
|
||||
case HabitsApplication.RESULT_BUG_REPORT:
|
||||
controller.sendBugReport();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void showFAQScreen()
|
||||
{
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(getString(R.string.helpURL)));
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void showAboutScreen()
|
||||
{
|
||||
Intent intent = new Intent(this, AboutActivity.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void showSettingsScreen()
|
||||
{
|
||||
Intent intent = new Intent(this, SettingsActivity.class);
|
||||
startActivityForResult(intent, 0);
|
||||
}
|
||||
|
||||
private void toggleNightMode()
|
||||
{
|
||||
if(InterfaceUtils.isNightMode())
|
||||
InterfaceUtils.setCurrentTheme(InterfaceUtils.THEME_LIGHT);
|
||||
else
|
||||
InterfaceUtils.setCurrentTheme(InterfaceUtils.THEME_DARK);
|
||||
|
||||
refreshTheme();
|
||||
}
|
||||
|
||||
private void refreshTheme()
|
||||
{
|
||||
new Handler().postDelayed(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
Intent intent = new Intent(MainActivity.this, MainActivity.class);
|
||||
|
||||
MainActivity.this.finish();
|
||||
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
|
||||
startActivity(intent);
|
||||
|
||||
}
|
||||
}, 500); // Let the menu disappear first
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHabitClick(Habit habit)
|
||||
{
|
||||
showHabitScreen(habit);
|
||||
}
|
||||
|
||||
private void showHabitScreen(Habit habit)
|
||||
{
|
||||
Intent intent = new Intent(this, ShowHabitActivity.class);
|
||||
intent.setData(Uri.parse("content://org.isoron.uhabits/habit/" + habit.getId()));
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
public void showIntroScreen()
|
||||
{
|
||||
Intent intent = new Intent(this, IntroActivity.class);
|
||||
this.startActivity(intent);
|
||||
}
|
||||
|
||||
public void showImportScreen()
|
||||
{
|
||||
File dir = FileUtils.getFilesDir(null);
|
||||
if(dir == null)
|
||||
{
|
||||
showMessage(R.string.could_not_import);
|
||||
return;
|
||||
}
|
||||
|
||||
FilePickerDialog picker = new FilePickerDialog(this, dir);
|
||||
picker.setListener(new FilePickerDialog.OnFileSelectedListener()
|
||||
{
|
||||
@Override
|
||||
public void onFileSelected(File file)
|
||||
{
|
||||
controller.importData(file);
|
||||
}
|
||||
});
|
||||
picker.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh(Long refreshKey)
|
||||
{
|
||||
listHabitsFragment.refresh(refreshKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgressBar getProgressBar()
|
||||
{
|
||||
return new AndroidProgressBar(listHabitsFragment.getProgressBar());
|
||||
}
|
||||
/*
|
||||
* Since changing the main activity on the manifest file causes things to
|
||||
* break we always point the launcher icon to this activity instead, and
|
||||
* redirect to the desired one using inheritance.
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -1,180 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.tasks.ExportCSVTask;
|
||||
import org.isoron.uhabits.tasks.ExportDBTask;
|
||||
import org.isoron.uhabits.tasks.ImportDataTask;
|
||||
import org.isoron.uhabits.tasks.ProgressBar;
|
||||
import org.isoron.uhabits.utils.DateUtils;
|
||||
import org.isoron.uhabits.utils.Preferences;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class MainController implements ImportDataTask.Listener, ExportCSVTask.Listener,
|
||||
ExportDBTask.Listener
|
||||
{
|
||||
public interface Screen
|
||||
{
|
||||
void showIntroScreen();
|
||||
|
||||
void showMessage(Integer stringId);
|
||||
|
||||
void refresh(Long refreshKey);
|
||||
|
||||
void sendFile(String filename);
|
||||
|
||||
void sendEmail(String to, String subject, String content);
|
||||
|
||||
ProgressBar getProgressBar();
|
||||
}
|
||||
|
||||
public interface System
|
||||
{
|
||||
void scheduleReminders();
|
||||
|
||||
void updateWidgets();
|
||||
|
||||
File dumpBugReportToFile() throws IOException;
|
||||
|
||||
String getBugReport() throws IOException;
|
||||
}
|
||||
|
||||
System sys;
|
||||
Screen screen;
|
||||
Preferences prefs;
|
||||
|
||||
public MainController()
|
||||
{
|
||||
prefs = Preferences.getInstance();
|
||||
}
|
||||
|
||||
public void setScreen(Screen screen)
|
||||
{
|
||||
this.screen = screen;
|
||||
}
|
||||
|
||||
public void setSystem(System sys)
|
||||
{
|
||||
this.sys = sys;
|
||||
}
|
||||
|
||||
public void onStartup()
|
||||
{
|
||||
prefs.initialize();
|
||||
prefs.incrementLaunchCount();
|
||||
prefs.updateLastAppVersion();
|
||||
if(prefs.isFirstRun()) onFirstRun();
|
||||
|
||||
sys.updateWidgets();
|
||||
sys.scheduleReminders();
|
||||
}
|
||||
|
||||
private void onFirstRun()
|
||||
{
|
||||
prefs.setFirstRun(false);
|
||||
prefs.setLastHintTimestamp(DateUtils.getStartOfToday());
|
||||
screen.showIntroScreen();
|
||||
}
|
||||
|
||||
public void importData(File file)
|
||||
{
|
||||
ImportDataTask task = new ImportDataTask(file, screen.getProgressBar());
|
||||
task.setListener(this);
|
||||
task.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImportDataFinished(int result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case ImportDataTask.SUCCESS:
|
||||
screen.refresh(null);
|
||||
screen.showMessage(R.string.habits_imported);
|
||||
break;
|
||||
|
||||
case ImportDataTask.NOT_RECOGNIZED:
|
||||
screen.showMessage(R.string.file_not_recognized);
|
||||
break;
|
||||
|
||||
default:
|
||||
screen.showMessage(R.string.could_not_import);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void exportCSV()
|
||||
{
|
||||
ExportCSVTask task = new ExportCSVTask(Habit.getAll(true), screen.getProgressBar());
|
||||
task.setListener(this);
|
||||
task.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExportCSVFinished(String filename)
|
||||
{
|
||||
if(filename != null) screen.sendFile(filename);
|
||||
else screen.showMessage(R.string.could_not_export);
|
||||
}
|
||||
|
||||
public void exportDB()
|
||||
{
|
||||
ExportDBTask task = new ExportDBTask(screen.getProgressBar());
|
||||
task.setListener(this);
|
||||
task.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExportDBFinished(String filename)
|
||||
{
|
||||
if(filename != null) screen.sendFile(filename);
|
||||
else screen.showMessage(R.string.could_not_export);
|
||||
}
|
||||
|
||||
public void sendBugReport()
|
||||
{
|
||||
try
|
||||
{
|
||||
sys.dumpBugReportToFile();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
String log = "---------- BUG REPORT BEGINS ----------\n";
|
||||
log += sys.getBugReport();
|
||||
log += "---------- BUG REPORT ENDS ------------\n";
|
||||
String to = "dev@loophabits.org";
|
||||
String subject = "Bug Report - Loop Habit Tracker";
|
||||
screen.sendEmail(log, to, subject);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
screen.showMessage(R.string.bug_report_failed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,29 +19,30 @@
|
||||
|
||||
package org.isoron.uhabits.commands;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.isoron.uhabits.tasks.BaseTask;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
public class CommandRunner
|
||||
{
|
||||
public interface Listener
|
||||
{
|
||||
void onCommandExecuted(Command command, Long refreshKey);
|
||||
}
|
||||
|
||||
private static CommandRunner singleton;
|
||||
private LinkedList<Listener> listeners;
|
||||
|
||||
private CommandRunner()
|
||||
public CommandRunner()
|
||||
{
|
||||
listeners = new LinkedList<>();
|
||||
}
|
||||
|
||||
public static CommandRunner getInstance()
|
||||
private static CommandRunner getInstance()
|
||||
{
|
||||
if(singleton == null) singleton = new CommandRunner();
|
||||
return singleton;
|
||||
return null;
|
||||
}
|
||||
|
||||
public void addListener(Listener l)
|
||||
{
|
||||
listeners.add(l);
|
||||
}
|
||||
|
||||
public void execute(final Command command, final Long refreshKey)
|
||||
@@ -57,7 +58,7 @@ public class CommandRunner
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid)
|
||||
{
|
||||
for(Listener l : listeners)
|
||||
for (Listener l : listeners)
|
||||
l.onCommandExecuted(command, refreshKey);
|
||||
|
||||
super.onPostExecute(null);
|
||||
@@ -65,13 +66,14 @@ public class CommandRunner
|
||||
}.execute();
|
||||
}
|
||||
|
||||
public void addListener(Listener l)
|
||||
{
|
||||
listeners.add(l);
|
||||
}
|
||||
|
||||
public void removeListener(Listener l)
|
||||
{
|
||||
listeners.remove(l);
|
||||
}
|
||||
|
||||
public interface Listener
|
||||
{
|
||||
void onCommandExecuted(@NonNull Command command,
|
||||
@Nullable Long refreshKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ public class ModelObservable
|
||||
listeners.remove(l);
|
||||
}
|
||||
|
||||
void notifyListeners()
|
||||
public void notifyListeners()
|
||||
{
|
||||
for(Listener l : listeners) l.onModelChange();
|
||||
}
|
||||
|
||||
@@ -19,12 +19,18 @@
|
||||
|
||||
package org.isoron.uhabits.tasks;
|
||||
|
||||
import org.isoron.uhabits.HabitsApplication;
|
||||
import org.isoron.uhabits.commands.CommandRunner;
|
||||
import org.isoron.uhabits.commands.ToggleRepetitionCommand;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class ToggleRepetitionTask extends BaseTask
|
||||
{;
|
||||
{
|
||||
@Inject
|
||||
CommandRunner commandRunner;
|
||||
|
||||
public interface Listener {
|
||||
void onToggleRepetitionFinished();
|
||||
}
|
||||
@@ -37,13 +43,14 @@ public class ToggleRepetitionTask extends BaseTask
|
||||
{
|
||||
this.timestamp = timestamp;
|
||||
this.habit = habit;
|
||||
HabitsApplication.getComponent().inject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInBackground()
|
||||
{
|
||||
ToggleRepetitionCommand command = new ToggleRepetitionCommand(habit, timestamp);
|
||||
CommandRunner.getInstance().execute(command, habit.getId());
|
||||
commandRunner.execute(command, habit.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -19,188 +19,114 @@
|
||||
|
||||
package org.isoron.uhabits.ui;
|
||||
|
||||
import android.app.backup.BackupManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import org.isoron.uhabits.HabitBroadcastReceiver;
|
||||
import org.isoron.uhabits.HabitsApplication;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.commands.Command;
|
||||
import org.isoron.uhabits.commands.CommandRunner;
|
||||
import org.isoron.uhabits.models.Checkmark;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.tasks.BaseTask;
|
||||
import org.isoron.uhabits.utils.ColorUtils;
|
||||
import org.isoron.uhabits.utils.InterfaceUtils;
|
||||
import org.isoron.uhabits.widgets.WidgetManager;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
abstract public class BaseActivity extends AppCompatActivity implements Thread.UncaughtExceptionHandler,
|
||||
CommandRunner.Listener
|
||||
/**
|
||||
* Base class for all activities in the application.
|
||||
*/
|
||||
abstract public class BaseActivity extends AppCompatActivity
|
||||
implements Thread.UncaughtExceptionHandler
|
||||
{
|
||||
private Toast toast;
|
||||
@Nullable
|
||||
private BaseMenu baseMenu;
|
||||
|
||||
Thread.UncaughtExceptionHandler androidExceptionHandler;
|
||||
@Nullable
|
||||
private Thread.UncaughtExceptionHandler androidExceptionHandler;
|
||||
|
||||
@Nullable
|
||||
private BaseScreen screen;
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(@Nullable Menu menu)
|
||||
{
|
||||
if (menu == null) return false;
|
||||
if (baseMenu == null) return false;
|
||||
return baseMenu.onCreate(getMenuInflater(), menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@Nullable MenuItem item)
|
||||
{
|
||||
if (item == null) return false;
|
||||
if (baseMenu == null) return false;
|
||||
return baseMenu.onItemSelected(item);
|
||||
}
|
||||
|
||||
public void setBaseMenu(@Nullable BaseMenu baseMenu)
|
||||
{
|
||||
this.baseMenu = baseMenu;
|
||||
}
|
||||
|
||||
public void setScreen(@Nullable BaseScreen screen)
|
||||
{
|
||||
this.screen = screen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncaughtException(@Nullable Thread thread,
|
||||
@Nullable Throwable ex)
|
||||
{
|
||||
if (ex == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
ex.printStackTrace();
|
||||
new BaseSystem(this).dumpBugReportToFile();
|
||||
} catch (Exception e)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
if (androidExceptionHandler != null)
|
||||
androidExceptionHandler.uncaughtException(thread, ex);
|
||||
else System.exit(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int request, int result, Intent data)
|
||||
{
|
||||
if (screen == null) return;
|
||||
screen.onResult(request, result, data);
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void onCommandExecuted(Command command, Long refreshKey)
|
||||
// {
|
||||
// window.showMessage(command.getExecuteStringId());
|
||||
// new BaseTask()
|
||||
// {
|
||||
// @Override
|
||||
// protected void doInBackground()
|
||||
// {
|
||||
// dismissNotifications(BaseActivity.this);
|
||||
// BackupManager.dataChanged("org.isoron.uhabits");
|
||||
// WidgetManager.updateWidgets(BaseActivity.this);
|
||||
// }
|
||||
// }.execute();
|
||||
// }
|
||||
|
||||
// private void dismissNotifications(Context context)
|
||||
// {
|
||||
// for (Habit h : Habit.getHabitsWithReminder())
|
||||
// {
|
||||
// if (h.checkmarks.getTodayValue() != Checkmark.UNCHECKED)
|
||||
// HabitBroadcastReceiver.dismissNotification(context, h);
|
||||
// }
|
||||
// }
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
InterfaceUtils.applyCurrentTheme(this);
|
||||
|
||||
androidExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
|
||||
Thread.setDefaultUncaughtExceptionHandler(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume()
|
||||
{
|
||||
super.onResume();
|
||||
CommandRunner.getInstance().addListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause()
|
||||
{
|
||||
CommandRunner.getInstance().removeListener(this);
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
public void showMessage(Integer stringId)
|
||||
{
|
||||
if (stringId == null) return;
|
||||
if (toast == null) toast = Toast.makeText(this, stringId, Toast.LENGTH_SHORT);
|
||||
else toast.setText(stringId);
|
||||
toast.show();
|
||||
}
|
||||
|
||||
protected void setupSupportActionBar(boolean homeButtonEnabled)
|
||||
{
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
if(toolbar == null) return;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
toolbar.setElevation(InterfaceUtils.dpToPixels(this, 2));
|
||||
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if(actionBar == null) return;
|
||||
|
||||
if(homeButtonEnabled)
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncaughtException(Thread thread, Throwable ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
ex.printStackTrace();
|
||||
((HabitsApplication) getApplication()).dumpBugReportToFile();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
if(androidExceptionHandler != null)
|
||||
androidExceptionHandler.uncaughtException(thread, ex);
|
||||
else
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
protected void setupActionBarColor(int color)
|
||||
{
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if(actionBar == null) return;
|
||||
|
||||
if (!InterfaceUtils.getStyledBoolean(this, R.attr.useHabitColorAsPrimary)) return;
|
||||
|
||||
ColorDrawable drawable = new ColorDrawable(color);
|
||||
actionBar.setBackgroundDrawable(drawable);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
{
|
||||
int darkerColor = ColorUtils.mixColors(color, Color.BLACK, 0.75f);
|
||||
getWindow().setStatusBarColor(darkerColor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostResume()
|
||||
{
|
||||
super.onPostResume();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
hideFakeToolbarShadow();
|
||||
}
|
||||
|
||||
protected void hideFakeToolbarShadow()
|
||||
{
|
||||
View view = findViewById(R.id.toolbarShadow);
|
||||
if(view != null) view.setVisibility(View.GONE);
|
||||
|
||||
view = findViewById(R.id.headerShadow);
|
||||
if(view != null) view.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void dismissNotifications(Context context)
|
||||
{
|
||||
for(Habit h : Habit.getHabitsWithReminder())
|
||||
{
|
||||
if(h.checkmarks.getTodayValue() != Checkmark.UNCHECKED)
|
||||
HabitBroadcastReceiver.dismissNotification(context, h);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCommandExecuted(Command command, Long refreshKey)
|
||||
{
|
||||
showMessage(command.getExecuteStringId());
|
||||
new BaseTask()
|
||||
{
|
||||
@Override
|
||||
protected void doInBackground()
|
||||
{
|
||||
dismissNotifications(BaseActivity.this);
|
||||
BackupManager.dataChanged("org.isoron.uhabits");
|
||||
WidgetManager.updateWidgets(BaseActivity.this);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
public void sendFile(@NonNull String archiveFilename)
|
||||
{
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_SEND);
|
||||
intent.setType("application/zip");
|
||||
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(archiveFilename)));
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
public void sendEmail(String to, String subject, String content)
|
||||
{
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_SEND);
|
||||
intent.setType("message/rfc822");
|
||||
intent.putExtra(Intent.EXTRA_EMAIL, new String[] {to});
|
||||
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
|
||||
intent.putExtra(Intent.EXTRA_TEXT, content);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
60
app/src/main/java/org/isoron/uhabits/ui/BaseMenu.java
Normal file
60
app/src/main/java/org/isoron/uhabits/ui/BaseMenu.java
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.ui;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
|
||||
public abstract class BaseMenu
|
||||
{
|
||||
private final BaseActivity activity;
|
||||
|
||||
public BaseMenu(BaseActivity activity)
|
||||
{
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
public final boolean onCreate(@NonNull MenuInflater inflater,
|
||||
@NonNull Menu menu)
|
||||
{
|
||||
menu.clear();
|
||||
inflater.inflate(getMenuResourceId(), menu);
|
||||
onCreate(menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void onCreate(@NonNull Menu menu)
|
||||
{
|
||||
}
|
||||
|
||||
public boolean onItemSelected(@NonNull MenuItem item)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected abstract int getMenuResourceId();
|
||||
|
||||
public void invalidate()
|
||||
{
|
||||
activity.invalidateOptionsMenu();
|
||||
}
|
||||
}
|
||||
110
app/src/main/java/org/isoron/uhabits/ui/BaseRootView.java
Normal file
110
app/src/main/java/org/isoron/uhabits/ui/BaseRootView.java
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.ui;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.utils.InterfaceUtils;
|
||||
|
||||
public abstract class BaseRootView extends FrameLayout
|
||||
{
|
||||
private final Context context;
|
||||
|
||||
public BaseRootView(Context context)
|
||||
{
|
||||
super(context);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public BaseRootView(Context context, AttributeSet attrs)
|
||||
{
|
||||
super(context, attrs);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public BaseRootView(Context context, AttributeSet attrs, int defStyleAttr)
|
||||
{
|
||||
super(context, attrs, defStyleAttr);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public BaseRootView(Context context,
|
||||
AttributeSet attrs,
|
||||
int defStyleAttr,
|
||||
int defStyleRes)
|
||||
{
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public boolean getDisplayHomeAsUp()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ProgressBar getProgressBar()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public abstract Toolbar getToolbar();
|
||||
|
||||
public int getToolbarColor()
|
||||
{
|
||||
// if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
|
||||
// {
|
||||
// if (InterfaceUtils.isNightMode()) return;
|
||||
// int color = activity.getResources().getColor(R.color.grey_900);
|
||||
// }
|
||||
// if (!InterfaceUtils.getStyledBoolean(activity, R.attr.useHabitColorAsPrimary)) return;
|
||||
return Color.BLACK;
|
||||
}
|
||||
|
||||
private void hideFakeToolbarShadow()
|
||||
{
|
||||
View view = findViewById(R.id.toolbarShadow);
|
||||
if (view != null) view.setVisibility(View.GONE);
|
||||
|
||||
view = findViewById(R.id.headerShadow);
|
||||
if (view != null) view.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
protected void initToolbar()
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
getToolbar().setElevation(InterfaceUtils.dpToPixels(context, 2));
|
||||
|
||||
hideFakeToolbarShadow();
|
||||
}
|
||||
}
|
||||
217
app/src/main/java/org/isoron/uhabits/ui/BaseScreen.java
Normal file
217
app/src/main/java/org/isoron/uhabits/ui/BaseScreen.java
Normal file
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* 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.ui;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.AppCompatDialogFragment;
|
||||
import android.support.v7.view.ActionMode;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.isoron.uhabits.tasks.ProgressBar;
|
||||
import org.isoron.uhabits.utils.ColorUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public abstract class BaseScreen
|
||||
{
|
||||
protected BaseActivity activity;
|
||||
|
||||
private Toast toast;
|
||||
|
||||
@Nullable
|
||||
private BaseRootView rootView;
|
||||
|
||||
@Nullable
|
||||
private BaseSelectionMenu selectionMenu;
|
||||
|
||||
public BaseScreen(BaseActivity activity)
|
||||
{
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
public void finishSelection()
|
||||
{
|
||||
if (selectionMenu == null) return;
|
||||
selectionMenu.finish();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ProgressBar getProgressBar()
|
||||
{
|
||||
if (rootView == null) return null;
|
||||
return new ProgressBarWrapper(rootView.getProgressBar());
|
||||
}
|
||||
|
||||
public void invalidate()
|
||||
{
|
||||
if (rootView == null) return;
|
||||
rootView.invalidate();
|
||||
}
|
||||
|
||||
public void onResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
}
|
||||
|
||||
public void setMenu(@Nullable BaseMenu menu)
|
||||
{
|
||||
activity.setBaseMenu(menu);
|
||||
}
|
||||
|
||||
public void setRootView(@Nullable BaseRootView rootView)
|
||||
{
|
||||
this.rootView = rootView;
|
||||
activity.setContentView(rootView);
|
||||
if (rootView == null) return;
|
||||
|
||||
initToolbar();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the menu to be shown when a selection is active on the screen.
|
||||
*
|
||||
* @param menu the menu to be shown during a selection
|
||||
*/
|
||||
public void setSelectionMenu(@Nullable BaseSelectionMenu menu)
|
||||
{
|
||||
this.selectionMenu = menu;
|
||||
}
|
||||
|
||||
public void showMessage(@Nullable Integer stringId)
|
||||
{
|
||||
if (stringId == null) return;
|
||||
if (toast == null)
|
||||
toast = Toast.makeText(activity, stringId, Toast.LENGTH_SHORT);
|
||||
else toast.setText(stringId);
|
||||
toast.show();
|
||||
}
|
||||
|
||||
public void showSendEmailScreen(String to, String subject, String content)
|
||||
{
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_SEND);
|
||||
intent.setType("message/rfc822");
|
||||
intent.putExtra(Intent.EXTRA_EMAIL, new String[]{to});
|
||||
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
|
||||
intent.putExtra(Intent.EXTRA_TEXT, content);
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
|
||||
public void showSendFileScreen(@NonNull String archiveFilename)
|
||||
{
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_SEND);
|
||||
intent.setType("application/zip");
|
||||
intent.putExtra(Intent.EXTRA_STREAM,
|
||||
Uri.fromFile(new File(archiveFilename)));
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs the screen to start a selection. If a selection menu was
|
||||
* provided, this menu will be shown instead of the regular one.
|
||||
*/
|
||||
public void startSelection()
|
||||
{
|
||||
activity.startSupportActionMode(new ActionModeWrapper());
|
||||
}
|
||||
|
||||
private void initToolbar()
|
||||
{
|
||||
if (rootView == null) return;
|
||||
|
||||
Toolbar toolbar = rootView.getToolbar();
|
||||
activity.setSupportActionBar(toolbar);
|
||||
ActionBar actionBar = activity.getSupportActionBar();
|
||||
if (actionBar == null) return;
|
||||
|
||||
actionBar.setDisplayHomeAsUpEnabled(rootView.getDisplayHomeAsUp());
|
||||
|
||||
int color = rootView.getToolbarColor();
|
||||
setActionBarColor(actionBar, color);
|
||||
setStatusBarColor(color);
|
||||
}
|
||||
|
||||
private void setActionBarColor(@NonNull ActionBar actionBar, int color)
|
||||
{
|
||||
ColorDrawable drawable = new ColorDrawable(color);
|
||||
actionBar.setBackgroundDrawable(drawable);
|
||||
}
|
||||
|
||||
private void setStatusBarColor(int baseColor)
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
{
|
||||
int darkerColor =
|
||||
ColorUtils.mixColors(baseColor, Color.BLACK, 0.75f);
|
||||
activity.getWindow().setStatusBarColor(darkerColor);
|
||||
}
|
||||
}
|
||||
|
||||
protected void showDialog(AppCompatDialogFragment dialog, String tag)
|
||||
{
|
||||
dialog.show(activity.getSupportFragmentManager(), tag);
|
||||
}
|
||||
|
||||
private class ActionModeWrapper implements ActionMode.Callback
|
||||
{
|
||||
@Override
|
||||
public boolean onActionItemClicked(@Nullable ActionMode mode,
|
||||
@Nullable MenuItem item)
|
||||
{
|
||||
if (item == null || selectionMenu == null) return false;
|
||||
return selectionMenu.onItemClicked(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateActionMode(@Nullable ActionMode mode,
|
||||
@Nullable Menu menu)
|
||||
{
|
||||
if (selectionMenu == null) return false;
|
||||
if (mode == null || menu == null) return false;
|
||||
selectionMenu.onCreate(activity.getMenuInflater(), mode, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyActionMode(@Nullable ActionMode mode)
|
||||
{
|
||||
if (selectionMenu == null) return;
|
||||
selectionMenu.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareActionMode(@Nullable ActionMode mode,
|
||||
@Nullable Menu menu)
|
||||
{
|
||||
if (selectionMenu == null || menu == null) return false;
|
||||
return selectionMenu.onPrepare(menu);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.ui;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.view.ActionMode;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
|
||||
public abstract class BaseSelectionMenu
|
||||
{
|
||||
@Nullable
|
||||
private ActionMode actionMode;
|
||||
|
||||
/**
|
||||
* Finishes the selection operation.
|
||||
*/
|
||||
public void finish()
|
||||
{
|
||||
if (actionMode != null) actionMode.finish();
|
||||
}
|
||||
|
||||
public void invalidate()
|
||||
{
|
||||
if (actionMode != null) actionMode.invalidate();
|
||||
}
|
||||
|
||||
public final void onCreate(@NonNull MenuInflater menuInflater,
|
||||
@NonNull ActionMode mode,
|
||||
@NonNull Menu menu)
|
||||
{
|
||||
this.actionMode = mode;
|
||||
menuInflater.inflate(getResourceId(), menu);
|
||||
onCreate(menu);
|
||||
}
|
||||
|
||||
public void onDestroy()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public boolean onItemClicked(@NonNull MenuItem item)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean onPrepare(@NonNull Menu menu)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setTitle(String title)
|
||||
{
|
||||
if (actionMode != null) actionMode.setTitle(title);
|
||||
}
|
||||
|
||||
protected abstract int getResourceId();
|
||||
|
||||
/**
|
||||
* Called when the menu is first created, right after the menu has been
|
||||
* inflated.
|
||||
*
|
||||
* @param menu the menu containing the buttons
|
||||
*/
|
||||
protected void onCreate(@NonNull Menu menu)
|
||||
{
|
||||
}
|
||||
}
|
||||
166
app/src/main/java/org/isoron/uhabits/ui/BaseSystem.java
Normal file
166
app/src/main/java/org/isoron/uhabits/ui/BaseSystem.java
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* 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.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import org.isoron.uhabits.BuildConfig;
|
||||
import org.isoron.uhabits.tasks.BaseTask;
|
||||
import org.isoron.uhabits.utils.DateUtils;
|
||||
import org.isoron.uhabits.utils.FileUtils;
|
||||
import org.isoron.uhabits.utils.ReminderUtils;
|
||||
import org.isoron.uhabits.widgets.WidgetManager;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.LinkedList;
|
||||
|
||||
public class BaseSystem
|
||||
{
|
||||
private Context context;
|
||||
|
||||
public BaseSystem(Context context)
|
||||
{
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public String getLogcat() throws IOException
|
||||
{
|
||||
int maxNLines = 250;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
String[] command = new String[]{"logcat", "-d"};
|
||||
Process process = Runtime.getRuntime().exec(command);
|
||||
|
||||
InputStreamReader in = new InputStreamReader(process.getInputStream());
|
||||
BufferedReader bufferedReader = new BufferedReader(in);
|
||||
|
||||
LinkedList<String> log = new LinkedList<>();
|
||||
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null)
|
||||
{
|
||||
log.addLast(line);
|
||||
if (log.size() > maxNLines) log.removeFirst();
|
||||
}
|
||||
|
||||
for (String l : log)
|
||||
{
|
||||
builder.append(l);
|
||||
builder.append('\n');
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public String getDeviceInfo()
|
||||
{
|
||||
if (context == null) return "";
|
||||
|
||||
StringBuilder b = new StringBuilder();
|
||||
WindowManager wm =
|
||||
(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
|
||||
b.append(
|
||||
String.format("App Version Name: %s\n", BuildConfig.VERSION_NAME));
|
||||
b.append(
|
||||
String.format("App Version Code: %s\n", BuildConfig.VERSION_CODE));
|
||||
b.append(String.format("OS Version: %s (%s)\n",
|
||||
java.lang.System.getProperty("os.version"),
|
||||
android.os.Build.VERSION.INCREMENTAL));
|
||||
b.append(
|
||||
String.format("OS API Level: %s\n", android.os.Build.VERSION.SDK));
|
||||
b.append(String.format("Device: %s\n", android.os.Build.DEVICE));
|
||||
b.append(
|
||||
String.format("Model (Product): %s (%s)\n", android.os.Build.MODEL,
|
||||
android.os.Build.PRODUCT));
|
||||
b.append(
|
||||
String.format("Manufacturer: %s\n", android.os.Build.MANUFACTURER));
|
||||
b.append(String.format("Other tags: %s\n", android.os.Build.TAGS));
|
||||
b.append(String.format("Screen Width: %s\n",
|
||||
wm.getDefaultDisplay().getWidth()));
|
||||
b.append(String.format("Screen Height: %s\n",
|
||||
wm.getDefaultDisplay().getHeight()));
|
||||
b.append(String.format("SD Card state: %s\n\n",
|
||||
Environment.getExternalStorageState()));
|
||||
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public File dumpBugReportToFile() throws IOException
|
||||
{
|
||||
String date =
|
||||
DateUtils.getBackupDateFormat().format(DateUtils.getLocalTime());
|
||||
|
||||
if (context == null) throw new RuntimeException(
|
||||
"application context should not be null");
|
||||
File dir = FileUtils.getFilesDir("Logs");
|
||||
if (dir == null) throw new IOException("log dir should not be null");
|
||||
|
||||
File logFile =
|
||||
new File(String.format("%s/Log %s.txt", dir.getPath(), date));
|
||||
FileWriter output = new FileWriter(logFile);
|
||||
output.write(getBugReport());
|
||||
output.close();
|
||||
|
||||
return logFile;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getBugReport() throws IOException
|
||||
{
|
||||
String logcat = getLogcat();
|
||||
String deviceInfo = getDeviceInfo();
|
||||
return deviceInfo + "\n" + logcat;
|
||||
}
|
||||
|
||||
public void scheduleReminders()
|
||||
{
|
||||
new BaseTask()
|
||||
{
|
||||
|
||||
@Override
|
||||
protected void doInBackground()
|
||||
{
|
||||
ReminderUtils.createReminderAlarms(context);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
public void updateWidgets()
|
||||
{
|
||||
new BaseTask()
|
||||
{
|
||||
|
||||
@Override
|
||||
protected void doInBackground()
|
||||
{
|
||||
WidgetManager.updateWidgets(context);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* 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.ui;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.utils.DateUtils;
|
||||
|
||||
public class HintManager
|
||||
{
|
||||
private Context context;
|
||||
private SharedPreferences prefs;
|
||||
private View hintView;
|
||||
|
||||
public HintManager(Context context, View hintView)
|
||||
{
|
||||
this.context = context;
|
||||
this.hintView = hintView;
|
||||
prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
}
|
||||
|
||||
public void dismissHint()
|
||||
{
|
||||
hintView.animate().alpha(0f).setDuration(500).setListener(new AnimatorListenerAdapter()
|
||||
{
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation)
|
||||
{
|
||||
hintView.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void showHintIfAppropriate()
|
||||
{
|
||||
Integer lastHintNumber = prefs.getInt("last_hint_number", -1);
|
||||
Long lastHintTimestamp = prefs.getLong("last_hint_timestamp", -1);
|
||||
|
||||
if (DateUtils.getStartOfToday() > lastHintTimestamp) showHint(lastHintNumber + 1);
|
||||
}
|
||||
|
||||
private void showHint(int hintNumber)
|
||||
{
|
||||
String[] hints = context.getResources().getStringArray(R.array.hints);
|
||||
if (hintNumber >= hints.length) return;
|
||||
|
||||
prefs.edit().putInt("last_hint_number", hintNumber).apply();
|
||||
prefs.edit().putLong("last_hint_timestamp", DateUtils.getStartOfToday()).apply();
|
||||
|
||||
TextView tvContent = (TextView) hintView.findViewById(R.id.hintContent);
|
||||
tvContent.setText(hints[hintNumber]);
|
||||
|
||||
hintView.setAlpha(0.0f);
|
||||
hintView.setVisibility(View.VISIBLE);
|
||||
hintView.animate().alpha(1f).setDuration(500);
|
||||
}
|
||||
}
|
||||
@@ -23,11 +23,11 @@ import android.view.View;
|
||||
|
||||
import org.isoron.uhabits.tasks.ProgressBar;
|
||||
|
||||
public class AndroidProgressBar implements ProgressBar
|
||||
public class ProgressBarWrapper implements ProgressBar
|
||||
{
|
||||
private final android.widget.ProgressBar progressBar;
|
||||
|
||||
public AndroidProgressBar(android.widget.ProgressBar progressBar)
|
||||
public ProgressBarWrapper(android.widget.ProgressBar progressBar)
|
||||
{
|
||||
this.progressBar = progressBar;
|
||||
}
|
||||
@@ -30,32 +30,12 @@ import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.ui.BaseActivity;
|
||||
import org.isoron.uhabits.utils.InterfaceUtils;
|
||||
|
||||
/**
|
||||
* Activity that allows the user to see information about the app itself.
|
||||
* Display current version, link to Google Play and list of contributors.
|
||||
*/
|
||||
public class AboutActivity extends BaseActivity implements View.OnClickListener
|
||||
{
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.about);
|
||||
setupSupportActionBar(true);
|
||||
|
||||
int color = InterfaceUtils.getStyledColor(this, R.attr.aboutScreenColor);
|
||||
setupActionBarColor(color);
|
||||
|
||||
TextView tvVersion = (TextView) findViewById(R.id.tvVersion);
|
||||
TextView tvRate = (TextView) findViewById(R.id.tvRate);
|
||||
TextView tvFeedback = (TextView) findViewById(R.id.tvFeedback);
|
||||
TextView tvSource = (TextView) findViewById(R.id.tvSource);
|
||||
|
||||
tvVersion.setText(String.format(getResources().getString(R.string.version_n),
|
||||
BuildConfig.VERSION_NAME));
|
||||
tvRate.setOnClickListener(this);
|
||||
tvFeedback.setOnClickListener(this);
|
||||
tvSource.setOnClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
@@ -89,4 +69,29 @@ public class AboutActivity extends BaseActivity implements View.OnClickListener
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.about);
|
||||
// setupSupportActionBar(true);
|
||||
|
||||
int color =
|
||||
InterfaceUtils.getStyledColor(this, R.attr.aboutScreenColor);
|
||||
// setupActionBarColor(color);
|
||||
|
||||
TextView tvVersion = (TextView) findViewById(R.id.tvVersion);
|
||||
TextView tvRate = (TextView) findViewById(R.id.tvRate);
|
||||
TextView tvFeedback = (TextView) findViewById(R.id.tvFeedback);
|
||||
TextView tvSource = (TextView) findViewById(R.id.tvSource);
|
||||
|
||||
tvVersion.setText(
|
||||
String.format(getResources().getString(R.string.version_n),
|
||||
BuildConfig.VERSION_NAME));
|
||||
tvRate.setOnClickListener(this);
|
||||
tvFeedback.setOnClickListener(this);
|
||||
tvSource.setOnClickListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Contains classes for AboutActivity
|
||||
*/
|
||||
package org.isoron.uhabits.ui.about;
|
||||
@@ -32,7 +32,9 @@ import com.android.colorpicker.ColorPickerSwatch;
|
||||
import com.android.datetimepicker.time.RadialPickerLayout;
|
||||
import com.android.datetimepicker.time.TimePickerDialog;
|
||||
|
||||
import org.isoron.uhabits.HabitsApplication;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.commands.CommandRunner;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.utils.ColorUtils;
|
||||
import org.isoron.uhabits.utils.DateUtils;
|
||||
@@ -40,6 +42,8 @@ import org.isoron.uhabits.utils.Preferences;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
import butterknife.OnItemSelected;
|
||||
@@ -47,15 +51,24 @@ import butterknife.OnItemSelected;
|
||||
public abstract class BaseDialogFragment extends AppCompatDialogFragment
|
||||
{
|
||||
protected Habit originalHabit;
|
||||
|
||||
protected Habit modifiedHabit;
|
||||
protected Preferences prefs = Preferences.getInstance();
|
||||
|
||||
protected BaseDialogHelper helper;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
@Inject
|
||||
Preferences prefs;
|
||||
|
||||
@Inject
|
||||
CommandRunner commandRunner;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
ViewGroup container,
|
||||
Bundle savedInstanceState)
|
||||
{
|
||||
View view = inflater.inflate(R.layout.edit_habit, container, false);
|
||||
HabitsApplication.getComponent().inject(this);
|
||||
ButterKnife.bind(this, view);
|
||||
|
||||
helper = new BaseDialogHelper(this, view);
|
||||
@@ -66,11 +79,16 @@ public abstract class BaseDialogFragment extends AppCompatDialogFragment
|
||||
return view;
|
||||
}
|
||||
|
||||
protected abstract void initializeHabits();
|
||||
|
||||
protected abstract void saveHabit();
|
||||
|
||||
protected abstract int getTitle();
|
||||
@OnItemSelected(R.id.sFrequency)
|
||||
public void onFrequencySelected(int position)
|
||||
{
|
||||
if (position < 0 || position > 4) throw new IllegalArgumentException();
|
||||
int freqNums[] = {1, 1, 2, 5, 3};
|
||||
int freqDens[] = {1, 7, 7, 7, 7};
|
||||
modifiedHabit.freqNum = freqNums[position];
|
||||
modifiedHabit.freqDen = freqDens[position];
|
||||
helper.populateFrequencyFields(modifiedHabit);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@@ -78,7 +96,7 @@ public abstract class BaseDialogFragment extends AppCompatDialogFragment
|
||||
{
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putInt("color", modifiedHabit.color);
|
||||
if(modifiedHabit.hasReminder())
|
||||
if (modifiedHabit.hasReminder())
|
||||
{
|
||||
outState.putInt("reminderMin", modifiedHabit.reminderMin);
|
||||
outState.putInt("reminderHour", modifiedHabit.reminderHour);
|
||||
@@ -86,15 +104,9 @@ public abstract class BaseDialogFragment extends AppCompatDialogFragment
|
||||
}
|
||||
}
|
||||
|
||||
protected void restoreSavedInstance(@Nullable Bundle bundle)
|
||||
{
|
||||
if(bundle == null) return;
|
||||
modifiedHabit.color = bundle.getInt("color", modifiedHabit.color);
|
||||
modifiedHabit.reminderMin = bundle.getInt("reminderMin", -1);
|
||||
modifiedHabit.reminderHour = bundle.getInt("reminderHour", -1);
|
||||
modifiedHabit.reminderDays = bundle.getInt("reminderDays", -1);
|
||||
if(modifiedHabit.reminderMin < 0) modifiedHabit.clearReminder();
|
||||
}
|
||||
protected abstract int getTitle();
|
||||
|
||||
protected abstract void initializeHabits();
|
||||
|
||||
@OnClick(R.id.buttonDiscard)
|
||||
void onButtonDiscardClick()
|
||||
@@ -102,15 +114,6 @@ public abstract class BaseDialogFragment extends AppCompatDialogFragment
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@OnClick(R.id.buttonSave)
|
||||
void onSaveButtonClick()
|
||||
{
|
||||
helper.parseFormIntoHabit(modifiedHabit);
|
||||
if (!helper.validate(modifiedHabit)) return;
|
||||
saveHabit();
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@OnClick(R.id.tvReminderTime)
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
void onDateSpinnerClick()
|
||||
@@ -127,36 +130,49 @@ public abstract class BaseDialogFragment extends AppCompatDialogFragment
|
||||
showTimePicker(defaultHour, defaultMin);
|
||||
}
|
||||
|
||||
@OnClick(R.id.buttonSave)
|
||||
void onSaveButtonClick()
|
||||
{
|
||||
helper.parseFormIntoHabit(modifiedHabit);
|
||||
if (!helper.validate(modifiedHabit)) return;
|
||||
saveHabit();
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@OnClick(R.id.tvReminderDays)
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
void onWeekdayClick()
|
||||
{
|
||||
if(!modifiedHabit.hasReminder()) return;
|
||||
if (!modifiedHabit.hasReminder()) return;
|
||||
WeekdayPickerDialog dialog = new WeekdayPickerDialog();
|
||||
dialog.setListener(new OnWeekdaysPickedListener());
|
||||
dialog.setSelectedDays(DateUtils.unpackWeekdayList(modifiedHabit.reminderDays));
|
||||
dialog.setSelectedDays(
|
||||
DateUtils.unpackWeekdayList(modifiedHabit.reminderDays));
|
||||
dialog.show(getFragmentManager(), "weekdayPicker");
|
||||
}
|
||||
|
||||
@OnItemSelected(R.id.sFrequency)
|
||||
public void onFrequencySelected(int position)
|
||||
protected void restoreSavedInstance(@Nullable Bundle bundle)
|
||||
{
|
||||
if(position < 0 || position > 4) throw new IllegalArgumentException();
|
||||
int freqNums[] = { 1, 1, 2, 5, 3 };
|
||||
int freqDens[] = { 1, 7, 7, 7, 7 };
|
||||
modifiedHabit.freqNum = freqNums[position];
|
||||
modifiedHabit.freqDen = freqDens[position];
|
||||
helper.populateFrequencyFields(modifiedHabit);
|
||||
if (bundle == null) return;
|
||||
modifiedHabit.color = bundle.getInt("color", modifiedHabit.color);
|
||||
modifiedHabit.reminderMin = bundle.getInt("reminderMin", -1);
|
||||
modifiedHabit.reminderHour = bundle.getInt("reminderHour", -1);
|
||||
modifiedHabit.reminderDays = bundle.getInt("reminderDays", -1);
|
||||
if (modifiedHabit.reminderMin < 0) modifiedHabit.clearReminder();
|
||||
}
|
||||
|
||||
protected abstract void saveHabit();
|
||||
|
||||
@OnClick(R.id.buttonPickColor)
|
||||
void showColorPicker()
|
||||
{
|
||||
int androidColor = ColorUtils.getColor(getContext(), modifiedHabit.color);
|
||||
int androidColor =
|
||||
ColorUtils.getColor(getContext(), modifiedHabit.color);
|
||||
|
||||
ColorPickerDialog picker = ColorPickerDialog.newInstance(
|
||||
R.string.color_picker_default_title, ColorUtils.getPalette(getContext()),
|
||||
androidColor, 4, ColorPickerDialog.SIZE_SMALL);
|
||||
ColorPickerDialog picker =
|
||||
ColorPickerDialog.newInstance(R.string.color_picker_default_title,
|
||||
ColorUtils.getPalette(getContext()), androidColor, 4,
|
||||
ColorPickerDialog.SIZE_SMALL);
|
||||
|
||||
picker.setOnColorSelectedListener(new OnColorSelectedListener());
|
||||
picker.show(getFragmentManager(), "picker");
|
||||
@@ -165,31 +181,56 @@ public abstract class BaseDialogFragment extends AppCompatDialogFragment
|
||||
private void showTimePicker(int defaultHour, int defaultMin)
|
||||
{
|
||||
boolean is24HourMode = DateFormat.is24HourFormat(getContext());
|
||||
TimePickerDialog timePicker = TimePickerDialog.newInstance(new OnTimeSetListener(),
|
||||
defaultHour, defaultMin, is24HourMode);
|
||||
TimePickerDialog timePicker =
|
||||
TimePickerDialog.newInstance(new OnTimeSetListener(), defaultHour,
|
||||
defaultMin, is24HourMode);
|
||||
timePicker.show(getFragmentManager(), "timePicker");
|
||||
}
|
||||
|
||||
private class OnColorSelectedListener implements ColorPickerSwatch.OnColorSelectedListener
|
||||
private class OnColorSelectedListener
|
||||
implements ColorPickerSwatch.OnColorSelectedListener
|
||||
{
|
||||
@Override
|
||||
public void onColorSelected(int androidColor)
|
||||
{
|
||||
int paletteColor = ColorUtils.colorToPaletteIndex(getActivity(), androidColor);
|
||||
int paletteColor =
|
||||
ColorUtils.colorToPaletteIndex(getActivity(), androidColor);
|
||||
prefs.setDefaultHabitColor(paletteColor);
|
||||
modifiedHabit.color = paletteColor;
|
||||
helper.populateColor(paletteColor);
|
||||
}
|
||||
}
|
||||
|
||||
private class OnWeekdaysPickedListener implements WeekdayPickerDialog.OnWeekdaysPickedListener
|
||||
private class OnTimeSetListener
|
||||
implements TimePickerDialog.OnTimeSetListener
|
||||
{
|
||||
@Override
|
||||
public void onTimeCleared(RadialPickerLayout view)
|
||||
{
|
||||
modifiedHabit.clearReminder();
|
||||
helper.populateReminderFields(modifiedHabit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeSet(RadialPickerLayout view, int hour, int minute)
|
||||
{
|
||||
modifiedHabit.reminderHour = hour;
|
||||
modifiedHabit.reminderMin = minute;
|
||||
modifiedHabit.reminderDays = DateUtils.ALL_WEEK_DAYS;
|
||||
helper.populateReminderFields(modifiedHabit);
|
||||
}
|
||||
}
|
||||
|
||||
private class OnWeekdaysPickedListener
|
||||
implements WeekdayPickerDialog.OnWeekdaysPickedListener
|
||||
{
|
||||
@Override
|
||||
public void onWeekdaysPicked(boolean[] selectedDays)
|
||||
{
|
||||
if(isSelectionEmpty(selectedDays))
|
||||
Arrays.fill(selectedDays, true);
|
||||
if (isSelectionEmpty(selectedDays)) Arrays.fill(selectedDays, true);
|
||||
|
||||
modifiedHabit.reminderDays = DateUtils.packWeekdayList(selectedDays);
|
||||
modifiedHabit.reminderDays =
|
||||
DateUtils.packWeekdayList(selectedDays);
|
||||
helper.populateReminderFields(modifiedHabit);
|
||||
}
|
||||
|
||||
@@ -199,23 +240,4 @@ public abstract class BaseDialogFragment extends AppCompatDialogFragment
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private class OnTimeSetListener implements TimePickerDialog.OnTimeSetListener
|
||||
{
|
||||
@Override
|
||||
public void onTimeSet(RadialPickerLayout view, int hour, int minute)
|
||||
{
|
||||
modifiedHabit.reminderHour = hour;
|
||||
modifiedHabit.reminderMin = minute;
|
||||
modifiedHabit.reminderDays = DateUtils.ALL_WEEK_DAYS;
|
||||
helper.populateReminderFields(modifiedHabit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeCleared(RadialPickerLayout view)
|
||||
{
|
||||
modifiedHabit.clearReminder();
|
||||
helper.populateReminderFields(modifiedHabit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,15 +37,33 @@ import butterknife.ButterKnife;
|
||||
public class BaseDialogHelper
|
||||
{
|
||||
private DialogFragment frag;
|
||||
@BindView(R.id.tvName) TextView tvName;
|
||||
@BindView(R.id.tvDescription) TextView tvDescription;
|
||||
@BindView(R.id.tvFreqNum) TextView tvFreqNum;
|
||||
@BindView(R.id.tvFreqDen) TextView tvFreqDen;
|
||||
@BindView(R.id.tvReminderTime) TextView tvReminderTime;
|
||||
@BindView(R.id.tvReminderDays) TextView tvReminderDays;
|
||||
@BindView(R.id.sFrequency) Spinner sFrequency;
|
||||
@BindView(R.id.llCustomFrequency) ViewGroup llCustomFrequency;
|
||||
@BindView(R.id.llReminderDays) ViewGroup llReminderDays;
|
||||
|
||||
@BindView(R.id.tvName)
|
||||
TextView tvName;
|
||||
|
||||
@BindView(R.id.tvDescription)
|
||||
TextView tvDescription;
|
||||
|
||||
@BindView(R.id.tvFreqNum)
|
||||
TextView tvFreqNum;
|
||||
|
||||
@BindView(R.id.tvFreqDen)
|
||||
TextView tvFreqDen;
|
||||
|
||||
@BindView(R.id.tvReminderTime)
|
||||
TextView tvReminderTime;
|
||||
|
||||
@BindView(R.id.tvReminderDays)
|
||||
TextView tvReminderDays;
|
||||
|
||||
@BindView(R.id.sFrequency)
|
||||
Spinner sFrequency;
|
||||
|
||||
@BindView(R.id.llCustomFrequency)
|
||||
ViewGroup llCustomFrequency;
|
||||
|
||||
@BindView(R.id.llReminderDays)
|
||||
ViewGroup llReminderDays;
|
||||
|
||||
public BaseDialogHelper(DialogFragment frag, View view)
|
||||
{
|
||||
@@ -53,19 +71,55 @@ public class BaseDialogHelper
|
||||
ButterKnife.bind(this, view);
|
||||
}
|
||||
|
||||
void parseFormIntoHabit(Habit habit)
|
||||
{
|
||||
habit.name = tvName.getText().toString().trim();
|
||||
habit.description = tvDescription.getText().toString().trim();
|
||||
String freqNum = tvFreqNum.getText().toString();
|
||||
String freqDen = tvFreqDen.getText().toString();
|
||||
if (!freqNum.isEmpty()) habit.freqNum = Integer.parseInt(freqNum);
|
||||
if (!freqDen.isEmpty()) habit.freqDen = Integer.parseInt(freqDen);
|
||||
}
|
||||
|
||||
void populateColor(int paletteColor)
|
||||
{
|
||||
tvName.setTextColor(
|
||||
ColorUtils.getColor(frag.getContext(), paletteColor));
|
||||
}
|
||||
|
||||
protected void populateForm(final Habit habit)
|
||||
{
|
||||
if(habit.name != null) tvName.setText(habit.name);
|
||||
if(habit.description != null) tvDescription.setText(habit.description);
|
||||
if (habit.name != null) tvName.setText(habit.name);
|
||||
if (habit.description != null) tvDescription.setText(habit.description);
|
||||
|
||||
populateColor(habit.color);
|
||||
populateFrequencyFields(habit);
|
||||
populateReminderFields(habit);
|
||||
}
|
||||
|
||||
void populateColor(int paletteColor)
|
||||
@SuppressLint("SetTextI18n")
|
||||
void populateFrequencyFields(Habit habit)
|
||||
{
|
||||
tvName.setTextColor(ColorUtils.getColor(frag.getContext(), paletteColor));
|
||||
int quickSelectPosition = -1;
|
||||
|
||||
if (habit.freqNum.equals(habit.freqDen)) quickSelectPosition = 0;
|
||||
|
||||
else if (habit.freqNum == 1 && habit.freqDen == 7)
|
||||
quickSelectPosition = 1;
|
||||
|
||||
else if (habit.freqNum == 2 && habit.freqDen == 7)
|
||||
quickSelectPosition = 2;
|
||||
|
||||
else if (habit.freqNum == 5 && habit.freqDen == 7)
|
||||
quickSelectPosition = 3;
|
||||
|
||||
if (quickSelectPosition >= 0)
|
||||
showSimplifiedFrequency(quickSelectPosition);
|
||||
|
||||
else showCustomFrequency();
|
||||
|
||||
tvFreqNum.setText(habit.freqNum.toString());
|
||||
tvFreqDen.setText(habit.freqDen.toString());
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@@ -78,36 +132,15 @@ public class BaseDialogHelper
|
||||
return;
|
||||
}
|
||||
|
||||
String time = DateUtils.formatTime(frag.getContext(), habit.reminderHour, habit.reminderMin);
|
||||
String time =
|
||||
DateUtils.formatTime(frag.getContext(), habit.reminderHour,
|
||||
habit.reminderMin);
|
||||
tvReminderTime.setText(time);
|
||||
llReminderDays.setVisibility(View.VISIBLE);
|
||||
|
||||
boolean weekdays[] = DateUtils.unpackWeekdayList(habit.reminderDays);
|
||||
tvReminderDays.setText(DateUtils.formatWeekdayList(frag.getContext(), weekdays));
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
void populateFrequencyFields(Habit habit)
|
||||
{
|
||||
int quickSelectPosition = -1;
|
||||
|
||||
if(habit.freqNum.equals(habit.freqDen))
|
||||
quickSelectPosition = 0;
|
||||
|
||||
else if(habit.freqNum == 1 && habit.freqDen == 7)
|
||||
quickSelectPosition = 1;
|
||||
|
||||
else if(habit.freqNum == 2 && habit.freqDen == 7)
|
||||
quickSelectPosition = 2;
|
||||
|
||||
else if(habit.freqNum == 5 && habit.freqDen == 7)
|
||||
quickSelectPosition = 3;
|
||||
|
||||
if(quickSelectPosition >= 0) showSimplifiedFrequency(quickSelectPosition);
|
||||
else showCustomFrequency();
|
||||
|
||||
tvFreqNum.setText(habit.freqNum.toString());
|
||||
tvFreqDen.setText(habit.freqDen.toString());
|
||||
tvReminderDays.setText(
|
||||
DateUtils.formatWeekdayList(frag.getContext(), weekdays));
|
||||
}
|
||||
|
||||
private void showCustomFrequency()
|
||||
@@ -130,32 +163,25 @@ public class BaseDialogHelper
|
||||
|
||||
if (habit.name.length() == 0)
|
||||
{
|
||||
tvName.setError(frag.getString(R.string.validation_name_should_not_be_blank));
|
||||
tvName.setError(
|
||||
frag.getString(R.string.validation_name_should_not_be_blank));
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (habit.freqNum <= 0)
|
||||
{
|
||||
tvFreqNum.setError(frag.getString(R.string.validation_number_should_be_positive));
|
||||
tvFreqNum.setError(
|
||||
frag.getString(R.string.validation_number_should_be_positive));
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (habit.freqNum > habit.freqDen)
|
||||
{
|
||||
tvFreqNum.setError(frag.getString(R.string.validation_at_most_one_rep_per_day));
|
||||
tvFreqNum.setError(
|
||||
frag.getString(R.string.validation_at_most_one_rep_per_day));
|
||||
valid = false;
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
void parseFormIntoHabit(Habit habit)
|
||||
{
|
||||
habit.name = tvName.getText().toString().trim();
|
||||
habit.description = tvDescription.getText().toString().trim();
|
||||
String freqNum = tvFreqNum.getText().toString();
|
||||
String freqDen = tvFreqDen.getText().toString();
|
||||
if(!freqNum.isEmpty()) habit.freqNum = Integer.parseInt(freqNum);
|
||||
if(!freqDen.isEmpty()) habit.freqDen = Integer.parseInt(freqDen);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ package org.isoron.uhabits.ui.habits.edit;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.commands.Command;
|
||||
import org.isoron.uhabits.commands.CommandRunner;
|
||||
import org.isoron.uhabits.commands.CreateHabitCommand;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
@@ -45,6 +44,6 @@ public class CreateHabitDialogFragment extends BaseDialogFragment
|
||||
protected void saveHabit()
|
||||
{
|
||||
Command command = new CreateHabitCommand(modifiedHabit);
|
||||
CommandRunner.getInstance().execute(command, null);
|
||||
commandRunner.execute(command, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import android.os.Bundle;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.commands.Command;
|
||||
import org.isoron.uhabits.commands.CommandRunner;
|
||||
import org.isoron.uhabits.commands.EditHabitCommand;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
@@ -48,7 +47,8 @@ public class EditHabitDialogFragment extends BaseDialogFragment
|
||||
protected void initializeHabits()
|
||||
{
|
||||
Long habitId = (Long) getArguments().get("habitId");
|
||||
if(habitId == null) throw new IllegalArgumentException("habitId must be specified");
|
||||
if (habitId == null)
|
||||
throw new IllegalArgumentException("habitId must be specified");
|
||||
|
||||
originalHabit = Habit.get(habitId);
|
||||
modifiedHabit = new Habit(originalHabit);
|
||||
@@ -57,6 +57,6 @@ public class EditHabitDialogFragment extends BaseDialogFragment
|
||||
protected void saveHabit()
|
||||
{
|
||||
Command command = new EditHabitCommand(originalHabit, modifiedHabit);
|
||||
CommandRunner.getInstance().execute(command, originalHabit.getId());
|
||||
commandRunner.execute(command, originalHabit.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,9 @@ public class HistoryEditorDialog extends AppCompatDialogFragment
|
||||
implements DialogInterface.OnClickListener
|
||||
{
|
||||
private Habit habit;
|
||||
|
||||
private Listener listener;
|
||||
|
||||
HabitHistoryView historyView;
|
||||
|
||||
@Override
|
||||
@@ -45,21 +47,23 @@ public class HistoryEditorDialog extends AppCompatDialogFragment
|
||||
Context context = getActivity();
|
||||
historyView = new HabitHistoryView(context, null);
|
||||
|
||||
if(savedInstanceState != null)
|
||||
if (savedInstanceState != null)
|
||||
{
|
||||
long id = savedInstanceState.getLong("habit", -1);
|
||||
if(id > 0) this.habit = Habit.get(id);
|
||||
if (id > 0) this.habit = Habit.get(id);
|
||||
}
|
||||
|
||||
int padding = (int) getResources().getDimension(R.dimen.history_editor_padding);
|
||||
int padding =
|
||||
(int) getResources().getDimension(R.dimen.history_editor_padding);
|
||||
historyView.setPadding(padding, 0, padding, 0);
|
||||
historyView.setHabit(habit);
|
||||
historyView.setIsEditable(true);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
||||
builder.setTitle(R.string.history)
|
||||
.setView(historyView)
|
||||
.setPositiveButton(android.R.string.ok, this);
|
||||
builder
|
||||
.setTitle(R.string.history)
|
||||
.setView(historyView)
|
||||
.setPositiveButton(android.R.string.ok, this);
|
||||
|
||||
refreshData();
|
||||
|
||||
@@ -84,7 +88,8 @@ public class HistoryEditorDialog extends AppCompatDialogFragment
|
||||
super.onResume();
|
||||
|
||||
DisplayMetrics metrics = getResources().getDisplayMetrics();
|
||||
int maxHeight = getResources().getDimensionPixelSize(R.dimen.history_editor_max_height);
|
||||
int maxHeight = getResources().getDimensionPixelSize(
|
||||
R.dimen.history_editor_max_height);
|
||||
int width = metrics.widthPixels;
|
||||
int height = Math.min(metrics.heightPixels, maxHeight);
|
||||
|
||||
@@ -100,14 +105,14 @@ public class HistoryEditorDialog extends AppCompatDialogFragment
|
||||
public void setHabit(Habit habit)
|
||||
{
|
||||
this.habit = habit;
|
||||
if(historyView != null) historyView.setHabit(habit);
|
||||
if (historyView != null) historyView.setHabit(habit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause()
|
||||
{
|
||||
super.onPause();
|
||||
if(listener != null) listener.onHistoryEditorClosed();
|
||||
if (listener != null) listener.onHistoryEditorClosed();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -121,7 +126,8 @@ public class HistoryEditorDialog extends AppCompatDialogFragment
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public interface Listener {
|
||||
public interface Listener
|
||||
{
|
||||
void onHistoryEditorClosed();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,9 @@ import android.support.v7.app.AppCompatDialogFragment;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.utils.DateUtils;
|
||||
|
||||
public class WeekdayPickerDialog extends AppCompatDialogFragment
|
||||
implements DialogInterface.OnMultiChoiceClickListener, DialogInterface.OnClickListener
|
||||
public class WeekdayPickerDialog extends AppCompatDialogFragment implements
|
||||
DialogInterface.OnMultiChoiceClickListener,
|
||||
DialogInterface.OnClickListener
|
||||
{
|
||||
|
||||
public interface OnWeekdaysPickedListener
|
||||
@@ -38,6 +39,7 @@ public class WeekdayPickerDialog extends AppCompatDialogFragment
|
||||
}
|
||||
|
||||
private boolean[] selectedDays;
|
||||
|
||||
private OnWeekdaysPickedListener listener;
|
||||
|
||||
public void setListener(OnWeekdaysPickedListener listener)
|
||||
@@ -54,10 +56,13 @@ public class WeekdayPickerDialog extends AppCompatDialogFragment
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState)
|
||||
{
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
builder.setTitle(R.string.select_weekdays)
|
||||
.setMultiChoiceItems(DateUtils.getLongDayNames(), selectedDays, this)
|
||||
.setPositiveButton(android.R.string.yes, this)
|
||||
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener()
|
||||
builder
|
||||
.setTitle(R.string.select_weekdays)
|
||||
.setMultiChoiceItems(DateUtils.getLongDayNames(), selectedDays,
|
||||
this)
|
||||
.setPositiveButton(android.R.string.yes, this)
|
||||
.setNegativeButton(android.R.string.cancel,
|
||||
new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
@@ -78,6 +83,6 @@ public class WeekdayPickerDialog extends AppCompatDialogFragment
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
{
|
||||
if(listener != null) listener.onWeekdaysPicked(selectedDays);
|
||||
if (listener != null) listener.onWeekdaysPicked(selectedDays);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* 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.ui.habits.list;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.utils.DateUtils;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class HabitListAdapter extends BaseAdapter
|
||||
{
|
||||
private LayoutInflater inflater;
|
||||
private HabitListLoader loader;
|
||||
private ListHabitsHelper helper;
|
||||
private List selectedPositions;
|
||||
private View.OnLongClickListener onCheckmarkLongClickListener;
|
||||
private View.OnClickListener onCheckmarkClickListener;
|
||||
|
||||
public HabitListAdapter(Context context, HabitListLoader loader)
|
||||
{
|
||||
this.loader = loader;
|
||||
|
||||
inflater = LayoutInflater.from(context);
|
||||
helper = new ListHabitsHelper(context, loader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount()
|
||||
{
|
||||
return loader.habits.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Habit getItem(int position)
|
||||
{
|
||||
return loader.habitsList.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position)
|
||||
{
|
||||
return (getItem(position)).getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View view, ViewGroup parent)
|
||||
{
|
||||
final Habit habit = loader.habitsList.get(position);
|
||||
boolean selected = selectedPositions.contains(position);
|
||||
|
||||
if (view == null || (Long) view.getTag(R.id.timestamp_key) != DateUtils.getStartOfToday())
|
||||
{
|
||||
view = helper.inflateHabitCard(inflater, onCheckmarkLongClickListener,
|
||||
onCheckmarkClickListener);
|
||||
}
|
||||
|
||||
helper.updateHabitCard(view, habit, selected);
|
||||
return view;
|
||||
}
|
||||
|
||||
public void setSelectedPositions(List selectedPositions)
|
||||
{
|
||||
this.selectedPositions = selectedPositions;
|
||||
}
|
||||
|
||||
public void setOnCheckmarkLongClickListener(View.OnLongClickListener listener)
|
||||
{
|
||||
this.onCheckmarkLongClickListener = listener;
|
||||
}
|
||||
|
||||
public void setOnCheckmarkClickListener(View.OnClickListener listener)
|
||||
{
|
||||
this.onCheckmarkClickListener = listener;
|
||||
}
|
||||
}
|
||||
@@ -1,223 +0,0 @@
|
||||
/*
|
||||
* 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.ui.habits.list;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.isoron.uhabits.commands.Command;
|
||||
import org.isoron.uhabits.commands.CommandRunner;
|
||||
import org.isoron.uhabits.utils.DateUtils;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.tasks.BaseTask;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class HabitListLoader implements CommandRunner.Listener
|
||||
{
|
||||
public interface Listener
|
||||
{
|
||||
void onLoadFinished();
|
||||
}
|
||||
|
||||
private BaseTask currentFetchTask;
|
||||
private int checkmarkCount;
|
||||
|
||||
@Nullable
|
||||
private Listener listener;
|
||||
private Long lastLoadTimestamp;
|
||||
|
||||
public HashMap<Long, Habit> habits;
|
||||
public List<Habit> habitsList;
|
||||
public HashMap<Long, int[]> checkmarks;
|
||||
public HashMap<Long, Integer> scores;
|
||||
|
||||
boolean includeArchived;
|
||||
|
||||
public void setIncludeArchived(boolean includeArchived)
|
||||
{
|
||||
this.includeArchived = includeArchived;
|
||||
}
|
||||
|
||||
public void setCheckmarkCount(int checkmarkCount)
|
||||
{
|
||||
this.checkmarkCount = checkmarkCount;
|
||||
}
|
||||
|
||||
public void setListener(@Nullable Listener listener)
|
||||
{
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public Long getLastLoadTimestamp()
|
||||
{
|
||||
return lastLoadTimestamp;
|
||||
}
|
||||
|
||||
public HabitListLoader()
|
||||
{
|
||||
habits = new HashMap<>();
|
||||
checkmarks = new HashMap<>();
|
||||
scores = new HashMap<>();
|
||||
}
|
||||
|
||||
public void reorder(int from, int to)
|
||||
{
|
||||
Habit fromHabit = habitsList.get(from);
|
||||
Habit toHabit = habitsList.get(to);
|
||||
|
||||
habitsList.remove(from);
|
||||
habitsList.add(to, fromHabit);
|
||||
|
||||
Habit.reorder(fromHabit, toHabit);
|
||||
}
|
||||
|
||||
public void updateAllHabits(final boolean updateScoresAndCheckmarks)
|
||||
{
|
||||
if (currentFetchTask != null) currentFetchTask.cancel(true);
|
||||
|
||||
currentFetchTask = new BaseTask()
|
||||
{
|
||||
public HashMap<Long, Habit> newHabits;
|
||||
public HashMap<Long, int[]> newCheckmarks;
|
||||
public HashMap<Long, Integer> newScores;
|
||||
public List<Habit> newHabitList;
|
||||
|
||||
@Override
|
||||
protected void doInBackground()
|
||||
{
|
||||
newHabits = new HashMap<>();
|
||||
newCheckmarks = new HashMap<>();
|
||||
newScores = new HashMap<>();
|
||||
newHabitList = Habit.getAll(includeArchived);
|
||||
|
||||
long dateTo = DateUtils.getStartOfDay(DateUtils.getLocalTime());
|
||||
long dateFrom = dateTo - (checkmarkCount - 1) * DateUtils.millisecondsInOneDay;
|
||||
int[] empty = new int[checkmarkCount];
|
||||
|
||||
for(Habit h : newHabitList)
|
||||
{
|
||||
Long id = h.getId();
|
||||
|
||||
newHabits.put(id, h);
|
||||
|
||||
if(checkmarks.containsKey(id))
|
||||
newCheckmarks.put(id, checkmarks.get(id));
|
||||
else
|
||||
newCheckmarks.put(id, empty);
|
||||
|
||||
if(scores.containsKey(id))
|
||||
newScores.put(id, scores.get(id));
|
||||
else
|
||||
newScores.put(id, 0);
|
||||
}
|
||||
|
||||
commit();
|
||||
|
||||
if(!updateScoresAndCheckmarks) return;
|
||||
|
||||
int current = 0;
|
||||
for (Habit h : newHabitList)
|
||||
{
|
||||
if (isCancelled()) return;
|
||||
|
||||
Long id = h.getId();
|
||||
newScores.put(id, h.scores.getTodayValue());
|
||||
newCheckmarks.put(id, h.checkmarks.getValues(dateFrom, dateTo));
|
||||
|
||||
publishProgress(current++, newHabits.size());
|
||||
}
|
||||
}
|
||||
|
||||
private void commit()
|
||||
{
|
||||
habits = newHabits;
|
||||
scores = newScores;
|
||||
checkmarks = newCheckmarks;
|
||||
habitsList = newHabitList;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(Integer... values)
|
||||
{
|
||||
if(listener != null) listener.onLoadFinished();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid)
|
||||
{
|
||||
if (isCancelled()) return;
|
||||
|
||||
lastLoadTimestamp = DateUtils.getStartOfToday();
|
||||
currentFetchTask = null;
|
||||
|
||||
if(listener != null) listener.onLoadFinished();
|
||||
super.onPostExecute(null);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
currentFetchTask.execute();
|
||||
}
|
||||
|
||||
public void updateHabit(final Long id)
|
||||
{
|
||||
new BaseTask()
|
||||
{
|
||||
@Override
|
||||
protected void doInBackground()
|
||||
{
|
||||
long dateTo = DateUtils.getStartOfDay(DateUtils.getLocalTime());
|
||||
long dateFrom = dateTo - (checkmarkCount - 1) * DateUtils.millisecondsInOneDay;
|
||||
|
||||
Habit h = Habit.get(id);
|
||||
if(h == null) return;
|
||||
|
||||
habits.put(id, h);
|
||||
scores.put(id, h.scores.getTodayValue());
|
||||
checkmarks.put(id, h.checkmarks.getValues(dateFrom, dateTo));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid)
|
||||
{
|
||||
if(listener != null) listener.onLoadFinished();
|
||||
super.onPostExecute(null);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
public void onResume()
|
||||
{
|
||||
CommandRunner.getInstance().addListener(this);
|
||||
}
|
||||
|
||||
public void onPause()
|
||||
{
|
||||
CommandRunner.getInstance().removeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCommandExecuted(Command command, Long refreshKey)
|
||||
{
|
||||
if(refreshKey == null) updateAllHabits(true);
|
||||
else updateHabit(refreshKey);
|
||||
}
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
/*
|
||||
* 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.ui.habits.list;
|
||||
|
||||
import android.content.DialogInterface;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.view.ActionMode;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import com.android.colorpicker.ColorPickerDialog;
|
||||
import com.android.colorpicker.ColorPickerSwatch;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.commands.ArchiveHabitsCommand;
|
||||
import org.isoron.uhabits.commands.ChangeHabitColorCommand;
|
||||
import org.isoron.uhabits.commands.CommandRunner;
|
||||
import org.isoron.uhabits.commands.DeleteHabitsCommand;
|
||||
import org.isoron.uhabits.commands.UnarchiveHabitsCommand;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.ui.BaseActivity;
|
||||
import org.isoron.uhabits.ui.habits.edit.BaseDialogFragment;
|
||||
import org.isoron.uhabits.ui.habits.edit.EditHabitDialogFragment;
|
||||
import org.isoron.uhabits.utils.ColorUtils;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class HabitListSelectionCallback implements ActionMode.Callback
|
||||
{
|
||||
private HabitListLoader loader;
|
||||
private List<Integer> selectedPositions;
|
||||
private BaseActivity activity;
|
||||
private Listener listener;
|
||||
|
||||
public interface Listener
|
||||
{
|
||||
void onActionModeDestroyed(ActionMode mode);
|
||||
}
|
||||
|
||||
public HabitListSelectionCallback(BaseActivity activity, HabitListLoader loader)
|
||||
{
|
||||
this.activity = activity;
|
||||
this.loader = loader;
|
||||
selectedPositions = new LinkedList<>();
|
||||
}
|
||||
|
||||
public void setListener(Listener listener)
|
||||
{
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void setSelectedPositions(List<Integer> selectedPositions)
|
||||
{
|
||||
this.selectedPositions = selectedPositions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu)
|
||||
{
|
||||
activity.getMenuInflater().inflate(R.menu.list_habits_selection, menu);
|
||||
updateTitle(mode);
|
||||
updateActions(menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu)
|
||||
{
|
||||
updateTitle(mode);
|
||||
updateActions(menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateActions(Menu menu)
|
||||
{
|
||||
boolean showEdit = (selectedPositions.size() == 1);
|
||||
boolean showArchive = true;
|
||||
boolean showUnarchive = true;
|
||||
for (int i : selectedPositions)
|
||||
{
|
||||
Habit h = loader.habitsList.get(i);
|
||||
if (h.isArchived()) showArchive = false;
|
||||
else showUnarchive = false;
|
||||
}
|
||||
|
||||
MenuItem itemEdit = menu.findItem(R.id.action_edit_habit);
|
||||
MenuItem itemColor = menu.findItem(R.id.action_color);
|
||||
MenuItem itemArchive = menu.findItem(R.id.action_archive_habit);
|
||||
MenuItem itemUnarchive = menu.findItem(R.id.action_unarchive_habit);
|
||||
|
||||
itemColor.setVisible(true);
|
||||
itemEdit.setVisible(showEdit);
|
||||
itemArchive.setVisible(showArchive);
|
||||
itemUnarchive.setVisible(showUnarchive);
|
||||
}
|
||||
|
||||
private void updateTitle(ActionMode mode)
|
||||
{
|
||||
mode.setTitle("" + selectedPositions.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked(final ActionMode mode, MenuItem item)
|
||||
{
|
||||
final LinkedList<Habit> selectedHabits = new LinkedList<>();
|
||||
for (int i : selectedPositions)
|
||||
selectedHabits.add(loader.habitsList.get(i));
|
||||
|
||||
Habit firstHabit = selectedHabits.getFirst();
|
||||
|
||||
switch (item.getItemId())
|
||||
{
|
||||
case R.id.action_archive_habit:
|
||||
archiveHabits(selectedHabits);
|
||||
mode.finish();
|
||||
return true;
|
||||
|
||||
case R.id.action_unarchive_habit:
|
||||
unarchiveHabits(selectedHabits);
|
||||
mode.finish();
|
||||
return true;
|
||||
|
||||
case R.id.action_edit_habit:
|
||||
{
|
||||
editHabit(firstHabit);
|
||||
mode.finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
case R.id.action_color:
|
||||
{
|
||||
showColorPicker(mode, selectedHabits, firstHabit);
|
||||
mode.finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
case R.id.action_delete:
|
||||
{
|
||||
deleteHabits(mode, selectedHabits);
|
||||
mode.finish();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void deleteHabits(final ActionMode mode, final LinkedList<Habit> selectedHabits)
|
||||
{
|
||||
new AlertDialog.Builder(activity).setTitle(R.string.delete_habits)
|
||||
.setMessage(R.string.delete_habits_message)
|
||||
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
{
|
||||
CommandRunner.getInstance()
|
||||
.execute(new DeleteHabitsCommand(selectedHabits), null);
|
||||
mode.finish();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void showColorPicker(final ActionMode mode, final LinkedList<Habit> selectedHabits,
|
||||
Habit firstHabit)
|
||||
{
|
||||
int originalAndroidColor = ColorUtils.getColor(activity, firstHabit.color);
|
||||
|
||||
ColorPickerDialog picker =
|
||||
ColorPickerDialog.newInstance(R.string.color_picker_default_title,
|
||||
ColorUtils.getPalette(activity), originalAndroidColor, 4,
|
||||
ColorPickerDialog.SIZE_SMALL);
|
||||
|
||||
picker.setOnColorSelectedListener(new ColorPickerSwatch.OnColorSelectedListener()
|
||||
{
|
||||
public void onColorSelected(int androidColor)
|
||||
{
|
||||
int paletteColor = ColorUtils.colorToPaletteIndex(activity, androidColor);
|
||||
CommandRunner.getInstance()
|
||||
.execute(new ChangeHabitColorCommand(selectedHabits, paletteColor), null);
|
||||
mode.finish();
|
||||
}
|
||||
});
|
||||
picker.show(activity.getSupportFragmentManager(), "picker");
|
||||
}
|
||||
|
||||
private void editHabit(Habit habit)
|
||||
{
|
||||
BaseDialogFragment
|
||||
frag = EditHabitDialogFragment.newInstance(habit.getId());
|
||||
frag.show(activity.getSupportFragmentManager(), "editHabit");
|
||||
}
|
||||
|
||||
private void unarchiveHabits(LinkedList<Habit> selectedHabits)
|
||||
{
|
||||
CommandRunner.getInstance().execute(new UnarchiveHabitsCommand(selectedHabits), null);
|
||||
}
|
||||
|
||||
private void archiveHabits(LinkedList<Habit> selectedHabits)
|
||||
{
|
||||
CommandRunner.getInstance().execute(new ArchiveHabitsCommand(selectedHabits), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyActionMode(ActionMode mode)
|
||||
{
|
||||
if(listener != null) listener.onActionModeDestroyed(mode);
|
||||
}
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
/*
|
||||
* 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.ui.habits.list;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
|
||||
import com.mobeta.android.dslv.DragSortController;
|
||||
import com.mobeta.android.dslv.DragSortListView;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.utils.Preferences;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class HabitListView extends DragSortListView implements View.OnClickListener,
|
||||
View.OnLongClickListener, DragSortListView.DropListener, AdapterView.OnItemClickListener,
|
||||
AdapterView.OnItemLongClickListener, DragSortListView.DragListener, HabitListLoader.Listener
|
||||
{
|
||||
private final HabitListLoader loader;
|
||||
private final HabitListAdapter adapter;
|
||||
private final ListHabitsHelper helper;
|
||||
private final Preferences prefs;
|
||||
private final List<Integer> selectedPositions;
|
||||
|
||||
@Nullable
|
||||
private Listener listener;
|
||||
private long lastLongClick;
|
||||
private boolean showArchived;
|
||||
|
||||
public HabitListView(Context context, AttributeSet attrs)
|
||||
{
|
||||
super(context, attrs);
|
||||
|
||||
loader = new HabitListLoader();
|
||||
adapter = new HabitListAdapter(context, loader);
|
||||
selectedPositions = new LinkedList<>();
|
||||
prefs = Preferences.getInstance();
|
||||
helper = new ListHabitsHelper(getContext(), loader);
|
||||
|
||||
adapter.setSelectedPositions(selectedPositions);
|
||||
adapter.setOnCheckmarkClickListener(this);
|
||||
adapter.setOnCheckmarkLongClickListener(this);
|
||||
loader.setListener(this);
|
||||
loader.setCheckmarkCount(helper.getButtonCount());
|
||||
|
||||
setAdapter(adapter);
|
||||
setOnItemClickListener(this);
|
||||
setOnItemLongClickListener(this);
|
||||
setDropListener(this);
|
||||
setDragListener(this);
|
||||
setFloatViewManager(new HabitsDragSortController());
|
||||
setDragEnabled(false);
|
||||
setLongClickable(true);
|
||||
}
|
||||
|
||||
public HabitListLoader getLoader()
|
||||
{
|
||||
return loader;
|
||||
}
|
||||
|
||||
public List<Integer> getSelectedPositions()
|
||||
{
|
||||
return selectedPositions;
|
||||
}
|
||||
|
||||
public void setListener(@Nullable Listener l)
|
||||
{
|
||||
this.listener = l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drop(int from, int to)
|
||||
{
|
||||
if(from == to) return;
|
||||
cancelSelection();
|
||||
|
||||
loader.reorder(from, to);
|
||||
adapter.notifyDataSetChanged();
|
||||
loader.updateAllHabits(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
if (v.getId() != R.id.tvCheck) return;
|
||||
|
||||
if (prefs.isShortToggleEnabled()) toggleCheckmark(v);
|
||||
else if(listener != null) listener.onInvalidToggle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View v)
|
||||
{
|
||||
lastLongClick = new Date().getTime();
|
||||
if (v.getId() != R.id.tvCheck) return true;
|
||||
if (prefs.isShortToggleEnabled()) return true;
|
||||
toggleCheckmark(v);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void toggleShowArchived()
|
||||
{
|
||||
showArchived = !showArchived;
|
||||
loader.setIncludeArchived(showArchived);
|
||||
loader.updateAllHabits(true);
|
||||
}
|
||||
|
||||
private void toggleCheckmark(View v)
|
||||
{
|
||||
Long id = helper.getHabitIdFromCheckmarkView(v);
|
||||
Habit habit = loader.habits.get(id);
|
||||
if(habit == null) return;
|
||||
|
||||
float x = v.getX() + v.getWidth() / 2.0f + ((View) v.getParent()).getX();
|
||||
float y = v.getY() + v.getHeight() / 2.0f + ((View) v.getParent()).getY();
|
||||
helper.triggerRipple((View) v.getParent().getParent(), x, y);
|
||||
|
||||
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||
helper.toggleCheckmarkView(v, habit);
|
||||
|
||||
long timestamp = helper.getTimestampFromCheckmarkView(v);
|
||||
|
||||
if(listener != null) listener.onToggleCheckmark(habit, timestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
|
||||
{
|
||||
if (new Date().getTime() - lastLongClick < 1000) return;
|
||||
|
||||
if(selectedPositions.isEmpty())
|
||||
{
|
||||
Habit habit = loader.habitsList.get(position);
|
||||
if(listener != null) listener.onHabitClick(habit);
|
||||
}
|
||||
else
|
||||
{
|
||||
toggleItemSelected(position);
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void toggleItemSelected(int position)
|
||||
{
|
||||
int k = selectedPositions.indexOf(position);
|
||||
if(k < 0) selectedPositions.add(position);
|
||||
else selectedPositions.remove(k);
|
||||
|
||||
if(listener != null)
|
||||
{
|
||||
if (selectedPositions.isEmpty()) listener.onHabitSelectionFinish();
|
||||
else listener.onHabitSelectionChange();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id)
|
||||
{
|
||||
selectHabit(position);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void selectHabit(int position)
|
||||
{
|
||||
if(!selectedPositions.contains(position)) selectedPositions.add(position);
|
||||
adapter.notifyDataSetChanged();
|
||||
|
||||
if(listener != null)
|
||||
{
|
||||
if (selectedPositions.size() == 1) listener.onHabitSelectionStart();
|
||||
else listener.onHabitSelectionChange();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drag(int from, int to)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startDrag(int position)
|
||||
{
|
||||
selectHabit(position);
|
||||
}
|
||||
|
||||
public boolean getShowArchived()
|
||||
{
|
||||
return showArchived;
|
||||
}
|
||||
|
||||
public void cancelSelection()
|
||||
{
|
||||
selectedPositions.clear();
|
||||
adapter.notifyDataSetChanged();
|
||||
setDragEnabled(true);
|
||||
if(listener != null) listener.onHabitSelectionFinish();
|
||||
}
|
||||
|
||||
public void refreshData(Long refreshKey)
|
||||
{
|
||||
if (refreshKey == null) loader.updateAllHabits(true);
|
||||
else loader.updateHabit(refreshKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished()
|
||||
{
|
||||
adapter.notifyDataSetChanged();
|
||||
if(listener != null) listener.onDatasetChanged();
|
||||
}
|
||||
|
||||
private class HabitsDragSortController extends DragSortController
|
||||
{
|
||||
public HabitsDragSortController()
|
||||
{
|
||||
super(HabitListView.this);
|
||||
setRemoveEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateFloatView(int position)
|
||||
{
|
||||
return adapter.getView(position, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyFloatView(View floatView)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public interface Listener
|
||||
{
|
||||
void onToggleCheckmark(Habit habit, long timestamp);
|
||||
|
||||
void onHabitClick(Habit habit);
|
||||
|
||||
void onHabitSelectionStart();
|
||||
|
||||
void onHabitSelectionFinish();
|
||||
|
||||
void onHabitSelectionChange();
|
||||
|
||||
void onInvalidToggle();
|
||||
|
||||
void onDatasetChanged();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.ui.habits.list;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.isoron.uhabits.ui.BaseActivity;
|
||||
import org.isoron.uhabits.ui.BaseSystem;
|
||||
|
||||
/**
|
||||
* Activity that allows the user to see and modify the list of habits.
|
||||
*/
|
||||
public class ListHabitsActivity extends BaseActivity
|
||||
{
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
BaseSystem system = new BaseSystem(this);
|
||||
ListHabitsScreen screen = new ListHabitsScreen(this);
|
||||
ListHabitsController controller =
|
||||
new ListHabitsController(screen, system);
|
||||
|
||||
screen.setController(controller);
|
||||
|
||||
setScreen(screen);
|
||||
controller.onStartup();
|
||||
}
|
||||
}
|
||||
@@ -19,18 +19,162 @@
|
||||
|
||||
package org.isoron.uhabits.ui.habits.list;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.isoron.uhabits.HabitsApplication;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.commands.CommandRunner;
|
||||
import org.isoron.uhabits.commands.ToggleRepetitionCommand;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.tasks.ExportCSVTask;
|
||||
import org.isoron.uhabits.tasks.ExportDBTask;
|
||||
import org.isoron.uhabits.tasks.ImportDataTask;
|
||||
import org.isoron.uhabits.ui.BaseSystem;
|
||||
import org.isoron.uhabits.ui.habits.list.controllers.HabitCardListController;
|
||||
import org.isoron.uhabits.utils.DateUtils;
|
||||
import org.isoron.uhabits.utils.Preferences;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class ListHabitsController
|
||||
implements ImportDataTask.Listener, HabitCardListController.HabitListener
|
||||
{
|
||||
public interface Screen
|
||||
{
|
||||
@NonNull
|
||||
private final ListHabitsScreen screen;
|
||||
|
||||
}
|
||||
@NonNull
|
||||
private final BaseSystem system;
|
||||
|
||||
private Screen screen;
|
||||
@Inject
|
||||
Preferences prefs;
|
||||
|
||||
public void setScreen(Screen screen)
|
||||
@Inject
|
||||
CommandRunner commandRunner;
|
||||
|
||||
public ListHabitsController(@NonNull ListHabitsScreen screen,
|
||||
@NonNull BaseSystem system)
|
||||
{
|
||||
this.screen = screen;
|
||||
this.system = system;
|
||||
HabitsApplication.getComponent().inject(this);
|
||||
}
|
||||
|
||||
public void onExportCSV()
|
||||
{
|
||||
ExportCSVTask task =
|
||||
new ExportCSVTask(Habit.getAll(true), screen.getProgressBar());
|
||||
task.setListener(filename -> {
|
||||
if (filename != null) screen.showSendFileScreen(filename);
|
||||
else screen.showMessage(R.string.could_not_export);
|
||||
});
|
||||
task.execute();
|
||||
}
|
||||
|
||||
public void onExportDB()
|
||||
{
|
||||
ExportDBTask task = new ExportDBTask(screen.getProgressBar());
|
||||
task.setListener(filename -> {
|
||||
if (filename != null) screen.showSendFileScreen(filename);
|
||||
else screen.showMessage(R.string.could_not_export);
|
||||
});
|
||||
task.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHabitClick(@NonNull Habit h)
|
||||
{
|
||||
screen.showHabitScreen(h);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHabitReorder(@NonNull Habit from, @NonNull Habit to)
|
||||
{
|
||||
Habit.reorder(from, to);
|
||||
}
|
||||
|
||||
public void onImportData(File file)
|
||||
{
|
||||
ImportDataTask task = new ImportDataTask(file, screen.getProgressBar());
|
||||
task.setListener(this);
|
||||
task.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImportDataFinished(int result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case ImportDataTask.SUCCESS:
|
||||
screen.invalidate();
|
||||
screen.showMessage(R.string.habits_imported);
|
||||
break;
|
||||
|
||||
case ImportDataTask.NOT_RECOGNIZED:
|
||||
screen.showMessage(R.string.file_not_recognized);
|
||||
break;
|
||||
|
||||
default:
|
||||
screen.showMessage(R.string.could_not_import);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInvalidToggle()
|
||||
{
|
||||
screen.showMessage(R.string.long_press_to_toggle);
|
||||
}
|
||||
|
||||
public void onSendBugReport()
|
||||
{
|
||||
try
|
||||
{
|
||||
system.dumpBugReportToFile();
|
||||
} catch (IOException e)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
String log = "---------- BUG REPORT BEGINS ----------\n";
|
||||
log += system.getBugReport();
|
||||
log += "---------- BUG REPORT ENDS ------------\n";
|
||||
String to = "dev@loophabits.org";
|
||||
String subject = "Bug Report - Loop Habit Tracker";
|
||||
screen.showSendEmailScreen(log, to, subject);
|
||||
} catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
screen.showMessage(R.string.bug_report_failed);
|
||||
}
|
||||
}
|
||||
|
||||
public void onStartup()
|
||||
{
|
||||
prefs.initialize();
|
||||
prefs.incrementLaunchCount();
|
||||
prefs.updateLastAppVersion();
|
||||
if (prefs.isFirstRun()) onFirstRun();
|
||||
|
||||
system.updateWidgets();
|
||||
system.scheduleReminders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onToggle(@NonNull Habit habit, long timestamp)
|
||||
{
|
||||
commandRunner.execute(new ToggleRepetitionCommand(habit, timestamp),
|
||||
null);
|
||||
}
|
||||
|
||||
private void onFirstRun()
|
||||
{
|
||||
prefs.setFirstRun(false);
|
||||
prefs.updateLastHint(-1, DateUtils.getStartOfToday());
|
||||
screen.showIntroScreen();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,236 +0,0 @@
|
||||
/*
|
||||
* 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.ui.habits.list;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v7.view.ActionMode;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.commands.CommandRunner;
|
||||
import org.isoron.uhabits.commands.ToggleRepetitionCommand;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.ui.BaseActivity;
|
||||
import org.isoron.uhabits.ui.HintManager;
|
||||
import org.isoron.uhabits.ui.habits.edit.BaseDialogFragment;
|
||||
import org.isoron.uhabits.ui.habits.edit.CreateHabitDialogFragment;
|
||||
import org.isoron.uhabits.utils.InterfaceUtils;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
|
||||
public class ListHabitsFragment extends Fragment
|
||||
implements HabitListSelectionCallback.Listener, ListHabitsController.Screen
|
||||
{
|
||||
private ActionMode actionMode;
|
||||
private HintManager hintManager;
|
||||
private ListHabitsHelper helper;
|
||||
private Listener habitClickListener;
|
||||
private BaseActivity activity;
|
||||
|
||||
@BindView(R.id.listView) HabitListView listView;
|
||||
@BindView(R.id.llButtonsHeader) LinearLayout llButtonsHeader;
|
||||
@BindView(R.id.progressBar) ProgressBar progressBar;
|
||||
@BindView(R.id.llEmpty) View llEmpty;
|
||||
@BindView(R.id.llHint) View llHint;
|
||||
@BindView(R.id.tvStarEmpty) TextView tvStarEmpty;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState)
|
||||
{
|
||||
View view = inflater.inflate(R.layout.list_habits_fragment, container, false);
|
||||
ButterKnife.bind(this, view);
|
||||
|
||||
helper = new ListHabitsHelper(activity, listView.getLoader());
|
||||
hintManager = new HintManager(activity, llHint);
|
||||
tvStarEmpty.setTypeface(InterfaceUtils.getFontAwesome(activity));
|
||||
listView.setListener(new HabitListViewListener());
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public void onAttach(Activity activity)
|
||||
{
|
||||
super.onAttach(activity);
|
||||
this.activity = (BaseActivity) activity;
|
||||
habitClickListener = (Listener) activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume()
|
||||
{
|
||||
super.onResume();
|
||||
listView.getLoader().onResume();
|
||||
listView.refreshData(null);
|
||||
helper.updateEmptyMessage(llEmpty);
|
||||
helper.updateHeader(llButtonsHeader);
|
||||
hintManager.showHintIfAppropriate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause()
|
||||
{
|
||||
listView.getLoader().onPause();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
|
||||
{
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
inflater.inflate(R.menu.list_habits_fragment, menu);
|
||||
MenuItem showArchivedItem = menu.findItem(R.id.action_show_archived);
|
||||
showArchivedItem.setChecked(listView.getShowArchived());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item)
|
||||
{
|
||||
switch (item.getItemId())
|
||||
{
|
||||
case R.id.action_add:
|
||||
showCreateHabitScreen();
|
||||
return true;
|
||||
|
||||
case R.id.action_show_archived:
|
||||
toggleShowArchived();
|
||||
return true;
|
||||
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void toggleShowArchived()
|
||||
{
|
||||
listView.toggleShowArchived();
|
||||
activity.invalidateOptionsMenu();
|
||||
}
|
||||
|
||||
private void showCreateHabitScreen()
|
||||
{
|
||||
BaseDialogFragment frag = new CreateHabitDialogFragment();
|
||||
frag.show(getFragmentManager(), "editHabit");
|
||||
}
|
||||
|
||||
private void startActionMode()
|
||||
{
|
||||
HabitListSelectionCallback callback =
|
||||
new HabitListSelectionCallback(activity, listView.getLoader());
|
||||
callback.setSelectedPositions(listView.getSelectedPositions());
|
||||
callback.setListener(this);
|
||||
actionMode = activity.startSupportActionMode(callback);
|
||||
}
|
||||
|
||||
private void finishActionMode()
|
||||
{
|
||||
if(actionMode != null) actionMode.finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActionModeDestroyed(ActionMode mode)
|
||||
{
|
||||
actionMode = null;
|
||||
listView.cancelSelection();
|
||||
}
|
||||
|
||||
@OnClick(R.id.llHint)
|
||||
public void onClickHint()
|
||||
{
|
||||
hintManager.dismissHint();
|
||||
}
|
||||
|
||||
public ProgressBar getProgressBar()
|
||||
{
|
||||
return progressBar;
|
||||
}
|
||||
|
||||
public void refresh(Long refreshKey)
|
||||
{
|
||||
listView.refreshData(refreshKey);
|
||||
}
|
||||
|
||||
public interface Listener
|
||||
{
|
||||
void onHabitClick(Habit habit);
|
||||
}
|
||||
|
||||
private class HabitListViewListener implements HabitListView.Listener
|
||||
{
|
||||
@Override
|
||||
public void onToggleCheckmark(Habit habit, long timestamp)
|
||||
{
|
||||
CommandRunner.getInstance().execute(new ToggleRepetitionCommand(habit, timestamp),
|
||||
habit.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHabitClick(Habit habit)
|
||||
{
|
||||
habitClickListener.onHabitClick(habit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHabitSelectionStart()
|
||||
{
|
||||
if(actionMode == null) startActionMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHabitSelectionFinish()
|
||||
{
|
||||
finishActionMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHabitSelectionChange()
|
||||
{
|
||||
if(actionMode != null) actionMode.invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInvalidToggle()
|
||||
{
|
||||
activity.showMessage(R.string.long_press_to_toggle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDatasetChanged()
|
||||
{
|
||||
helper.updateEmptyMessage(llEmpty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,307 +0,0 @@
|
||||
/*
|
||||
* 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.ui.habits.list;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.utils.ColorUtils;
|
||||
import org.isoron.uhabits.utils.DateUtils;
|
||||
import org.isoron.uhabits.utils.InterfaceUtils;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.models.Score;
|
||||
import org.isoron.uhabits.views.RingView;
|
||||
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
public class ListHabitsHelper
|
||||
{
|
||||
private static final int CHECKMARK_LEFT_TO_RIGHT = 0;
|
||||
private static final int CHECKMARK_RIGHT_TO_LEFT = 1;
|
||||
|
||||
private final int lowContrastColor;
|
||||
private final int mediumContrastColor;
|
||||
|
||||
private final Context context;
|
||||
private final HabitListLoader loader;
|
||||
|
||||
public ListHabitsHelper(Context context, HabitListLoader loader)
|
||||
{
|
||||
this.context = context;
|
||||
this.loader = loader;
|
||||
|
||||
lowContrastColor = InterfaceUtils.getStyledColor(context, R.attr.lowContrastTextColor);
|
||||
mediumContrastColor = InterfaceUtils.getStyledColor(context, R.attr.mediumContrastTextColor);
|
||||
}
|
||||
|
||||
public int getButtonCount()
|
||||
{
|
||||
float screenWidth = InterfaceUtils.getScreenWidth(context);
|
||||
float labelWidth = context.getResources().getDimension(R.dimen.habitNameWidth);
|
||||
float buttonWidth = context.getResources().getDimension(R.dimen.checkmarkWidth);
|
||||
return Math.max(0, (int) ((screenWidth - labelWidth) / buttonWidth));
|
||||
}
|
||||
|
||||
public int getHabitNameWidth()
|
||||
{
|
||||
float screenWidth = InterfaceUtils.getScreenWidth(context);
|
||||
float buttonWidth = context.getResources().getDimension(R.dimen.checkmarkWidth);
|
||||
float padding = InterfaceUtils.dpToPixels(context, 15);
|
||||
return (int) (screenWidth - padding - getButtonCount() * buttonWidth);
|
||||
}
|
||||
|
||||
public void updateCheckmarkButtons(Habit habit, LinearLayout llButtons)
|
||||
{
|
||||
int activeColor = getActiveColor(habit);
|
||||
int m = llButtons.getChildCount();
|
||||
Long habitId = habit.getId();
|
||||
|
||||
int isChecked[] = loader.checkmarks.get(habitId);
|
||||
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
int position = i;
|
||||
|
||||
if(getCheckmarkOrder() == CHECKMARK_RIGHT_TO_LEFT)
|
||||
position = m - i - 1;
|
||||
|
||||
TextView tvCheck = (TextView) llButtons.getChildAt(position);
|
||||
tvCheck.setTag(R.string.habit_key, habitId);
|
||||
tvCheck.setTag(R.string.offset_key, i);
|
||||
if(isChecked.length > i)
|
||||
updateCheckmark(activeColor, tvCheck, isChecked[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public int getActiveColor(Habit habit)
|
||||
{
|
||||
int activeColor = ColorUtils.getColor(context, habit.color);
|
||||
if(habit.isArchived()) activeColor = mediumContrastColor;
|
||||
|
||||
return activeColor;
|
||||
}
|
||||
|
||||
public void initializeLabelAndIcon(View itemView)
|
||||
{
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(getHabitNameWidth(),
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT, 1);
|
||||
itemView.findViewById(R.id.label).setLayoutParams(params);
|
||||
}
|
||||
|
||||
public void updateNameAndIcon(Habit habit, RingView ring, TextView tvName)
|
||||
{
|
||||
int activeColor = getActiveColor(habit);
|
||||
|
||||
tvName.setText(habit.name);
|
||||
tvName.setTextColor(activeColor);
|
||||
|
||||
int score = loader.scores.get(habit.getId());
|
||||
float percentage = (float) score / Score.MAX_VALUE;
|
||||
|
||||
ring.setColor(activeColor);
|
||||
ring.setPercentage(percentage);
|
||||
ring.setPrecision(1.0f / 16);
|
||||
}
|
||||
|
||||
public void updateCheckmark(int activeColor, TextView tvCheck, int check)
|
||||
{
|
||||
switch (check)
|
||||
{
|
||||
case 2:
|
||||
tvCheck.setText(R.string.fa_check);
|
||||
tvCheck.setTextColor(activeColor);
|
||||
tvCheck.setTag(R.string.toggle_key, 2);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
tvCheck.setText(R.string.fa_check);
|
||||
tvCheck.setTextColor(lowContrastColor);
|
||||
tvCheck.setTag(R.string.toggle_key, 1);
|
||||
break;
|
||||
|
||||
case 0:
|
||||
tvCheck.setText(R.string.fa_times);
|
||||
tvCheck.setTextColor(lowContrastColor);
|
||||
tvCheck.setTag(R.string.toggle_key, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public View inflateHabitCard(LayoutInflater inflater,
|
||||
View.OnLongClickListener onCheckmarkLongClickListener,
|
||||
View.OnClickListener onCheckmarkClickListener)
|
||||
{
|
||||
View view = inflater.inflate(R.layout.list_habits_item, null);
|
||||
initializeLabelAndIcon(view);
|
||||
inflateCheckmarkButtons(view, onCheckmarkLongClickListener, onCheckmarkClickListener,
|
||||
inflater);
|
||||
return view;
|
||||
}
|
||||
|
||||
public void updateHabitCard(View view, Habit habit, boolean selected)
|
||||
{
|
||||
RingView scoreRing = ((RingView) view.findViewById(R.id.scoreRing));
|
||||
TextView tvName = (TextView) view.findViewById(R.id.label);
|
||||
LinearLayout llInner = (LinearLayout) view.findViewById(R.id.llInner);
|
||||
LinearLayout llButtons = (LinearLayout) view.findViewById(R.id.llButtons);
|
||||
|
||||
llInner.setTag(R.string.habit_key, habit.getId());
|
||||
llInner.setOnTouchListener(new HotspotTouchListener());
|
||||
|
||||
updateNameAndIcon(habit, scoreRing, tvName);
|
||||
updateCheckmarkButtons(habit, llButtons);
|
||||
updateHabitCardBackground(llInner, selected);
|
||||
}
|
||||
|
||||
|
||||
public void updateHabitCardBackground(View view, boolean isSelected)
|
||||
{
|
||||
if (android.os.Build.VERSION.SDK_INT >= 21)
|
||||
{
|
||||
if (isSelected)
|
||||
view.setBackgroundResource(R.drawable.selected_box);
|
||||
else
|
||||
view.setBackgroundResource(R.drawable.ripple);
|
||||
}
|
||||
else
|
||||
{
|
||||
Drawable background;
|
||||
|
||||
if (isSelected)
|
||||
background = InterfaceUtils.getStyledDrawable(context, R.attr.selectedBackground);
|
||||
else
|
||||
background = InterfaceUtils.getStyledDrawable(context, R.attr.cardBackground);
|
||||
|
||||
view.setBackgroundDrawable(background);
|
||||
}
|
||||
}
|
||||
|
||||
public void inflateCheckmarkButtons(View view, View.OnLongClickListener onLongClickListener,
|
||||
View.OnClickListener onClickListener, LayoutInflater inflater)
|
||||
{
|
||||
for (int i = 0; i < getButtonCount(); i++)
|
||||
{
|
||||
View check = inflater.inflate(R.layout.list_habits_item_check, null);
|
||||
TextView btCheck = (TextView) check.findViewById(R.id.tvCheck);
|
||||
btCheck.setTypeface(InterfaceUtils.getFontAwesome(context));
|
||||
btCheck.setOnLongClickListener(onLongClickListener);
|
||||
btCheck.setOnClickListener(onClickListener);
|
||||
btCheck.setHapticFeedbackEnabled(false);
|
||||
((LinearLayout) view.findViewById(R.id.llButtons)).addView(check);
|
||||
}
|
||||
|
||||
view.setTag(R.id.timestamp_key, DateUtils.getStartOfToday());
|
||||
}
|
||||
|
||||
public void updateHeader(ViewGroup header)
|
||||
{
|
||||
LayoutInflater inflater = LayoutInflater.from(context);
|
||||
GregorianCalendar day = DateUtils.getStartOfTodayCalendar();
|
||||
header.removeAllViews();
|
||||
|
||||
for (int i = 0; i < getButtonCount(); i++)
|
||||
{
|
||||
int position = 0;
|
||||
|
||||
if(getCheckmarkOrder() == CHECKMARK_LEFT_TO_RIGHT)
|
||||
position = i;
|
||||
|
||||
View tvDay = inflater.inflate(R.layout.list_habits_header_check, null);
|
||||
TextView btCheck = (TextView) tvDay.findViewById(R.id.tvCheck);
|
||||
btCheck.setText(DateUtils.formatHeaderDate(day));
|
||||
header.addView(tvDay, position);
|
||||
day.add(GregorianCalendar.DAY_OF_MONTH, -1);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateEmptyMessage(View view)
|
||||
{
|
||||
if (loader.getLastLoadTimestamp() == null) view.setVisibility(View.GONE);
|
||||
else view.setVisibility(loader.habits.size() > 0 ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
public void toggleCheckmarkView(View v, Habit habit)
|
||||
{
|
||||
int androidColor = ColorUtils.getColor(context, habit.color);
|
||||
|
||||
if (v.getTag(R.string.toggle_key).equals(2))
|
||||
updateCheckmark(androidColor, (TextView) v, 0);
|
||||
else
|
||||
updateCheckmark(androidColor, (TextView) v, 2);
|
||||
}
|
||||
|
||||
public Long getHabitIdFromCheckmarkView(View v)
|
||||
{
|
||||
return (Long) v.getTag(R.string.habit_key);
|
||||
}
|
||||
|
||||
public long getTimestampFromCheckmarkView(View v)
|
||||
{
|
||||
Integer offset = (Integer) v.getTag(R.string.offset_key);
|
||||
return DateUtils.getStartOfDay(DateUtils.getLocalTime() -
|
||||
offset * DateUtils.millisecondsInOneDay);
|
||||
}
|
||||
|
||||
public void triggerRipple(View v, final float x, final float y)
|
||||
{
|
||||
final Drawable background = v.getBackground();
|
||||
if (android.os.Build.VERSION.SDK_INT >= 21)
|
||||
background.setHotspot(x, y);
|
||||
|
||||
background.setState(new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled});
|
||||
|
||||
new Handler().postDelayed(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
background.setState(new int[]{});
|
||||
}
|
||||
}, 25);
|
||||
}
|
||||
|
||||
private static class HotspotTouchListener implements View.OnTouchListener
|
||||
{
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event)
|
||||
{
|
||||
if (android.os.Build.VERSION.SDK_INT >= 21)
|
||||
v.getBackground().setHotspot(event.getX(), event.getY());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int getCheckmarkOrder()
|
||||
{
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
boolean reverse = prefs.getBoolean("pref_checkmark_reverse_order", false);
|
||||
return reverse ? CHECKMARK_RIGHT_TO_LEFT : CHECKMARK_LEFT_TO_RIGHT;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.ui.habits.list;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.ui.BaseActivity;
|
||||
import org.isoron.uhabits.ui.BaseMenu;
|
||||
import org.isoron.uhabits.utils.InterfaceUtils;
|
||||
|
||||
public class ListHabitsMenu extends BaseMenu
|
||||
{
|
||||
@NonNull
|
||||
private final ListHabitsScreen screen;
|
||||
|
||||
private boolean showArchived;
|
||||
|
||||
public ListHabitsMenu(@NonNull BaseActivity activity,
|
||||
@NonNull ListHabitsScreen screen)
|
||||
{
|
||||
super(activity);
|
||||
this.screen = screen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@NonNull Menu menu)
|
||||
{
|
||||
MenuItem nightModeItem = menu.findItem(R.id.action_night_mode);
|
||||
nightModeItem.setChecked(InterfaceUtils.isNightMode());
|
||||
|
||||
MenuItem showArchivedItem = menu.findItem(R.id.action_show_archived);
|
||||
showArchivedItem.setChecked(showArchived);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemSelected(@NonNull MenuItem item)
|
||||
{
|
||||
switch (item.getItemId())
|
||||
{
|
||||
case R.id.action_night_mode:
|
||||
screen.toggleNightMode();
|
||||
return true;
|
||||
|
||||
case R.id.action_add:
|
||||
screen.showCreateHabitScreen();
|
||||
return true;
|
||||
|
||||
case R.id.action_faq:
|
||||
screen.showFAQScreen();
|
||||
return true;
|
||||
|
||||
case R.id.action_about:
|
||||
screen.showAboutScreen();
|
||||
return true;
|
||||
|
||||
case R.id.action_settings:
|
||||
screen.showSettingsScreen();
|
||||
return true;
|
||||
|
||||
case R.id.action_show_archived:
|
||||
showArchived = !showArchived;
|
||||
screen.getRootView().setShowArchived(showArchived);
|
||||
invalidate();
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getMenuResourceId()
|
||||
{
|
||||
return R.menu.main_activity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* 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.ui.habits.list;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.models.ModelObservable;
|
||||
import org.isoron.uhabits.ui.BaseRootView;
|
||||
import org.isoron.uhabits.ui.habits.list.controllers.HabitCardListController;
|
||||
import org.isoron.uhabits.ui.habits.list.model.HabitCardListAdapter;
|
||||
import org.isoron.uhabits.ui.habits.list.model.HintList;
|
||||
import org.isoron.uhabits.ui.habits.list.views.HabitCardListView;
|
||||
import org.isoron.uhabits.ui.habits.list.views.HintView;
|
||||
import org.isoron.uhabits.utils.InterfaceUtils;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public class ListHabitsRootView extends BaseRootView
|
||||
implements ModelObservable.Listener
|
||||
{
|
||||
@BindView(R.id.listView)
|
||||
HabitCardListView listView;
|
||||
|
||||
@BindView(R.id.llEmpty)
|
||||
ViewGroup llEmpty;
|
||||
|
||||
@BindView(R.id.tvStarEmpty)
|
||||
TextView tvStarEmpty;
|
||||
|
||||
@BindView(R.id.toolbar)
|
||||
Toolbar toolbar;
|
||||
|
||||
@BindView(R.id.progressBar)
|
||||
ProgressBar progressBar;
|
||||
|
||||
@BindView(R.id.hintView)
|
||||
HintView hintView;
|
||||
|
||||
@Nullable
|
||||
private HabitCardListAdapter listAdapter;
|
||||
|
||||
public ListHabitsRootView(@NonNull Context context)
|
||||
{
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public ProgressBar getProgressBar()
|
||||
{
|
||||
return progressBar;
|
||||
}
|
||||
|
||||
public boolean getShowArchived()
|
||||
{
|
||||
if(listAdapter == null) return false;
|
||||
return listAdapter.getIncludeArchived();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Toolbar getToolbar()
|
||||
{
|
||||
return toolbar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getToolbarColor()
|
||||
{
|
||||
return InterfaceUtils.getStyledColor(getContext(), R.attr.colorPrimary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModelChange()
|
||||
{
|
||||
updateEmptyView();
|
||||
}
|
||||
|
||||
public void setShowArchived(boolean showArchived)
|
||||
{
|
||||
if(listAdapter == null) return;
|
||||
listAdapter.setShowArchived(showArchived);
|
||||
}
|
||||
|
||||
private void updateEmptyView()
|
||||
{
|
||||
if (listAdapter == null) return;
|
||||
llEmpty.setVisibility(
|
||||
listAdapter.getCount() > 0 ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
public void setController(@Nullable ListHabitsController controller,
|
||||
@Nullable ListHabitsSelectionMenu menu)
|
||||
{
|
||||
listView.setController(null);
|
||||
if (controller == null || listAdapter == null) return;
|
||||
|
||||
HabitCardListController listController =
|
||||
new HabitCardListController(listAdapter, listView);
|
||||
listController.setHabitListener(controller);
|
||||
listController.setSelectionListener(menu);
|
||||
listView.setController(listController);
|
||||
}
|
||||
|
||||
public void setListAdapter(@NonNull HabitCardListAdapter listAdapter)
|
||||
{
|
||||
if (this.listAdapter != null)
|
||||
listAdapter.getObservable().removeListener(this);
|
||||
|
||||
this.listAdapter = listAdapter;
|
||||
listView.setAdapter(listAdapter);
|
||||
listAdapter.setListView(listView);
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
addView(inflate(getContext(), R.layout.list_habits, null));
|
||||
ButterKnife.bind(this);
|
||||
|
||||
tvStarEmpty.setTypeface(InterfaceUtils.getFontAwesome(getContext()));
|
||||
initToolbar();
|
||||
|
||||
String hints[] =
|
||||
getContext().getResources().getStringArray(R.array.hints);
|
||||
HintList hintList = new HintList(hints);
|
||||
hintView.setHints(hintList);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow()
|
||||
{
|
||||
super.onAttachedToWindow();
|
||||
|
||||
updateEmptyView();
|
||||
|
||||
if (listAdapter != null) listAdapter.getObservable().addListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow()
|
||||
{
|
||||
if (listAdapter != null)
|
||||
listAdapter.getObservable().removeListener(this);
|
||||
super.onDetachedFromWindow();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* 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.ui.habits.list;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
|
||||
import com.android.colorpicker.ColorPickerDialog;
|
||||
|
||||
import org.isoron.uhabits.HabitsApplication;
|
||||
import org.isoron.uhabits.MainActivity;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.ui.BaseActivity;
|
||||
import org.isoron.uhabits.ui.BaseScreen;
|
||||
import org.isoron.uhabits.ui.about.AboutActivity;
|
||||
import org.isoron.uhabits.ui.habits.edit.BaseDialogFragment;
|
||||
import org.isoron.uhabits.ui.habits.edit.CreateHabitDialogFragment;
|
||||
import org.isoron.uhabits.ui.habits.edit.EditHabitDialogFragment;
|
||||
import org.isoron.uhabits.ui.habits.list.model.HabitCardListAdapter;
|
||||
import org.isoron.uhabits.ui.habits.show.ShowHabitActivity;
|
||||
import org.isoron.uhabits.ui.intro.IntroActivity;
|
||||
import org.isoron.uhabits.ui.settings.FilePickerDialog;
|
||||
import org.isoron.uhabits.ui.settings.SettingsActivity;
|
||||
import org.isoron.uhabits.utils.ColorUtils;
|
||||
import org.isoron.uhabits.utils.FileUtils;
|
||||
import org.isoron.uhabits.utils.InterfaceUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class ListHabitsScreen extends BaseScreen
|
||||
{
|
||||
@Nullable
|
||||
ListHabitsController controller;
|
||||
|
||||
@NonNull
|
||||
private final ListHabitsRootView rootView;
|
||||
|
||||
@NonNull
|
||||
private final ListHabitsSelectionMenu selectionMenu;
|
||||
|
||||
public ListHabitsScreen(@NonNull BaseActivity activity)
|
||||
{
|
||||
super(activity);
|
||||
rootView = new ListHabitsRootView(activity);
|
||||
setRootView(rootView);
|
||||
|
||||
ListHabitsMenu menu = new ListHabitsMenu(activity, this);
|
||||
selectionMenu = new ListHabitsSelectionMenu(this);
|
||||
setMenu(menu);
|
||||
setSelectionMenu(selectionMenu);
|
||||
|
||||
HabitCardListAdapter adapter = new HabitCardListAdapter();
|
||||
rootView.setListAdapter(adapter);
|
||||
selectionMenu.setAdapter(adapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
if (controller == null) return;
|
||||
|
||||
switch (resultCode)
|
||||
{
|
||||
case HabitsApplication.RESULT_IMPORT_DATA:
|
||||
showImportScreen();
|
||||
break;
|
||||
|
||||
case HabitsApplication.RESULT_EXPORT_CSV:
|
||||
controller.onExportCSV();
|
||||
break;
|
||||
|
||||
case HabitsApplication.RESULT_EXPORT_DB:
|
||||
controller.onExportDB();
|
||||
break;
|
||||
|
||||
case HabitsApplication.RESULT_BUG_REPORT:
|
||||
controller.onSendBugReport();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void setController(@Nullable ListHabitsController controller)
|
||||
{
|
||||
this.controller = controller;
|
||||
rootView.setController(controller, selectionMenu);
|
||||
}
|
||||
|
||||
public void showAboutScreen()
|
||||
{
|
||||
Intent intent = new Intent(activity, AboutActivity.class);
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
|
||||
public void showColorPicker(Habit habit, OnColorSelectedListener callback)
|
||||
{
|
||||
int color = ColorUtils.getColor(activity, habit.color);
|
||||
|
||||
ColorPickerDialog picker =
|
||||
ColorPickerDialog.newInstance(R.string.color_picker_default_title,
|
||||
ColorUtils.getPalette(activity), color, 4,
|
||||
ColorPickerDialog.SIZE_SMALL);
|
||||
|
||||
picker.setOnColorSelectedListener(c -> {
|
||||
c = ColorUtils.colorToPaletteIndex(activity, c);
|
||||
callback.onColorSelected(c);
|
||||
});
|
||||
picker.show(activity.getSupportFragmentManager(), "picker");
|
||||
}
|
||||
|
||||
public void showCreateHabitScreen()
|
||||
{
|
||||
showDialog(new CreateHabitDialogFragment(), "editHabit");
|
||||
}
|
||||
|
||||
public void showDeleteConfirmationScreen(Callback callback)
|
||||
{
|
||||
new AlertDialog.Builder(activity)
|
||||
.setTitle(R.string.delete_habits)
|
||||
.setMessage(R.string.delete_habits_message)
|
||||
.setPositiveButton(android.R.string.yes,
|
||||
(dialog, which) -> callback.run())
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
public void showEditHabitScreen(Habit habit)
|
||||
{
|
||||
BaseDialogFragment frag =
|
||||
EditHabitDialogFragment.newInstance(habit.getId());
|
||||
frag.show(activity.getSupportFragmentManager(), "editHabit");
|
||||
}
|
||||
|
||||
public void showFAQScreen()
|
||||
{
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(activity.getString(R.string.helpURL)));
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
|
||||
public void showHabitScreen(@NonNull Habit habit)
|
||||
{
|
||||
Intent intent = new Intent(activity, ShowHabitActivity.class);
|
||||
intent.setData(
|
||||
Uri.parse("content://org.isoron.uhabits/habit/" + habit.getId()));
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
|
||||
public void showImportScreen()
|
||||
{
|
||||
if (controller == null) return;
|
||||
|
||||
File dir = FileUtils.getFilesDir(null);
|
||||
if (dir == null)
|
||||
{
|
||||
showMessage(R.string.could_not_import);
|
||||
return;
|
||||
}
|
||||
|
||||
FilePickerDialog picker = new FilePickerDialog(activity, dir);
|
||||
picker.setListener(file -> controller.onImportData(file));
|
||||
picker.show();
|
||||
}
|
||||
|
||||
public void showIntroScreen()
|
||||
{
|
||||
Intent intent = new Intent(activity, IntroActivity.class);
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
|
||||
public void showSettingsScreen()
|
||||
{
|
||||
Intent intent = new Intent(activity, SettingsActivity.class);
|
||||
activity.startActivityForResult(intent, 0);
|
||||
}
|
||||
|
||||
public void toggleNightMode()
|
||||
{
|
||||
if (InterfaceUtils.isNightMode())
|
||||
InterfaceUtils.setCurrentTheme(InterfaceUtils.THEME_LIGHT);
|
||||
else InterfaceUtils.setCurrentTheme(InterfaceUtils.THEME_DARK);
|
||||
|
||||
refreshTheme();
|
||||
}
|
||||
|
||||
private void refreshTheme()
|
||||
{
|
||||
new Handler().postDelayed(() -> {
|
||||
Intent intent = new Intent(activity, MainActivity.class);
|
||||
|
||||
activity.finish();
|
||||
activity.overridePendingTransition(android.R.anim.fade_in,
|
||||
android.R.anim.fade_out);
|
||||
activity.startActivity(intent);
|
||||
|
||||
}, 500); // HACK: Let the menu disappear first
|
||||
}
|
||||
|
||||
interface Callback
|
||||
{
|
||||
void run();
|
||||
}
|
||||
|
||||
public interface OnColorSelectedListener
|
||||
{
|
||||
void onColorSelected(int color);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public ListHabitsRootView getRootView()
|
||||
{
|
||||
return rootView;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* 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.ui.habits.list;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import org.isoron.uhabits.HabitsApplication;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.commands.ArchiveHabitsCommand;
|
||||
import org.isoron.uhabits.commands.ChangeHabitColorCommand;
|
||||
import org.isoron.uhabits.commands.CommandRunner;
|
||||
import org.isoron.uhabits.commands.DeleteHabitsCommand;
|
||||
import org.isoron.uhabits.commands.UnarchiveHabitsCommand;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.ui.BaseSelectionMenu;
|
||||
import org.isoron.uhabits.ui.habits.list.controllers.HabitCardListController;
|
||||
import org.isoron.uhabits.ui.habits.list.model.HabitCardListAdapter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class ListHabitsSelectionMenu extends BaseSelectionMenu
|
||||
implements HabitCardListController.SelectionListener
|
||||
{
|
||||
@NonNull
|
||||
private final ListHabitsScreen screen;
|
||||
|
||||
@Inject
|
||||
CommandRunner commandRunner;
|
||||
|
||||
@Nullable
|
||||
private HabitCardListAdapter adapter;
|
||||
|
||||
public ListHabitsSelectionMenu(@NonNull ListHabitsScreen screen)
|
||||
{
|
||||
this.screen = screen;
|
||||
HabitsApplication.getComponent().inject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy()
|
||||
{
|
||||
if (adapter != null) adapter.clearSelection();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemClicked(@NonNull MenuItem item)
|
||||
{
|
||||
if (adapter == null) return false;
|
||||
|
||||
List<Habit> selected = adapter.getSelected();
|
||||
if (selected.isEmpty()) return false;
|
||||
|
||||
Habit firstHabit = selected.get(0);
|
||||
|
||||
switch (item.getItemId())
|
||||
{
|
||||
case R.id.action_edit_habit:
|
||||
edit(firstHabit);
|
||||
finish();
|
||||
return true;
|
||||
|
||||
case R.id.action_archive_habit:
|
||||
archive(selected);
|
||||
finish();
|
||||
return true;
|
||||
|
||||
case R.id.action_unarchive_habit:
|
||||
unarchive(selected);
|
||||
finish();
|
||||
return true;
|
||||
|
||||
case R.id.action_delete:
|
||||
delete(selected);
|
||||
return true;
|
||||
|
||||
case R.id.action_color:
|
||||
showColorPicker(selected, firstHabit);
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepare(@NonNull Menu menu)
|
||||
{
|
||||
if (adapter == null) return false;
|
||||
List<Habit> selected = adapter.getSelected();
|
||||
|
||||
boolean showEdit = (selected.size() == 1);
|
||||
boolean showArchive = true;
|
||||
boolean showUnarchive = true;
|
||||
for (Habit h : selected)
|
||||
{
|
||||
if (h.isArchived()) showArchive = false;
|
||||
else showUnarchive = false;
|
||||
}
|
||||
|
||||
MenuItem itemEdit = menu.findItem(R.id.action_edit_habit);
|
||||
MenuItem itemColor = menu.findItem(R.id.action_color);
|
||||
MenuItem itemArchive = menu.findItem(R.id.action_archive_habit);
|
||||
MenuItem itemUnarchive = menu.findItem(R.id.action_unarchive_habit);
|
||||
|
||||
itemColor.setVisible(true);
|
||||
itemEdit.setVisible(showEdit);
|
||||
itemArchive.setVisible(showArchive);
|
||||
itemUnarchive.setVisible(showUnarchive);
|
||||
|
||||
setTitle(Integer.toString(selected.size()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectionChange()
|
||||
{
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectionFinish()
|
||||
{
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectionStart()
|
||||
{
|
||||
screen.startSelection();
|
||||
}
|
||||
|
||||
public void setAdapter(@Nullable HabitCardListAdapter adapter)
|
||||
{
|
||||
if (adapter == null) return;
|
||||
this.adapter = adapter;
|
||||
}
|
||||
|
||||
private void archive(@NonNull List<Habit> selected)
|
||||
{
|
||||
commandRunner.execute(new ArchiveHabitsCommand(selected), null);
|
||||
}
|
||||
|
||||
private void delete(@NonNull List<Habit> selected)
|
||||
{
|
||||
screen.showDeleteConfirmationScreen(() -> {
|
||||
commandRunner.execute(new DeleteHabitsCommand(selected), null);
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
private void edit(@NonNull Habit firstHabit)
|
||||
{
|
||||
screen.showEditHabitScreen(firstHabit);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getResourceId()
|
||||
{
|
||||
return R.menu.list_habits_selection;
|
||||
}
|
||||
|
||||
private void showColorPicker(@NonNull List<Habit> selected,
|
||||
@NonNull Habit firstHabit)
|
||||
{
|
||||
screen.showColorPicker(firstHabit, color -> {
|
||||
commandRunner.execute(new ChangeHabitColorCommand(selected, color),
|
||||
null);
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
private void unarchive(@NonNull List<Habit> selected)
|
||||
{
|
||||
commandRunner.execute(new UnarchiveHabitsCommand(selected), null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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.ui.habits.list.controllers;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.isoron.uhabits.HabitsApplication;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.ui.habits.list.views.CheckmarkButtonView;
|
||||
import org.isoron.uhabits.utils.Preferences;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class CheckmarkButtonController
|
||||
{
|
||||
@Nullable
|
||||
private CheckmarkButtonView view;
|
||||
|
||||
@Nullable
|
||||
private Listener listener;
|
||||
|
||||
@Inject
|
||||
Preferences prefs;
|
||||
|
||||
@NonNull
|
||||
private Habit habit;
|
||||
|
||||
private long timestamp;
|
||||
|
||||
public CheckmarkButtonController(@NonNull Habit habit, long timestamp)
|
||||
{
|
||||
this.habit = habit;
|
||||
this.timestamp = timestamp;
|
||||
HabitsApplication.getComponent().inject(this);
|
||||
}
|
||||
|
||||
public void onClick()
|
||||
{
|
||||
if (prefs.isShortToggleEnabled()) performToggle();
|
||||
else performInvalidToggle();
|
||||
}
|
||||
|
||||
public boolean onLongClick()
|
||||
{
|
||||
performToggle();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void performInvalidToggle()
|
||||
{
|
||||
if (listener != null) listener.onInvalidToggle();
|
||||
}
|
||||
|
||||
public void performToggle()
|
||||
{
|
||||
if (view != null) view.toggle();
|
||||
if (listener != null) listener.onToggle(habit, timestamp);
|
||||
}
|
||||
|
||||
public void setListener(@Nullable Listener listener)
|
||||
{
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void setView(@Nullable CheckmarkButtonView view)
|
||||
{
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
public interface Listener
|
||||
{
|
||||
/**
|
||||
* Called when the user's attempt to perform a toggle is rejected.
|
||||
*/
|
||||
void onInvalidToggle();
|
||||
|
||||
|
||||
void onToggle(Habit habit, long timestamp);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.ui.habits.list.controllers;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.ui.habits.list.views.HabitCardView;
|
||||
|
||||
public class HabitCardController implements HabitCardView.Controller
|
||||
{
|
||||
@Nullable
|
||||
private HabitCardView view;
|
||||
|
||||
@Nullable
|
||||
private Listener listener;
|
||||
|
||||
@Override
|
||||
public void onInvalidToggle()
|
||||
{
|
||||
if (listener != null) listener.onInvalidToggle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onToggle(Habit habit, long timestamp)
|
||||
{
|
||||
if (view != null) view.triggerRipple(0, 0);
|
||||
if (listener != null) listener.onToggle(habit, timestamp);
|
||||
}
|
||||
|
||||
public void setListener(@Nullable Listener listener)
|
||||
{
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void setView(@Nullable HabitCardView view)
|
||||
{
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
public interface Listener extends CheckmarkButtonController.Listener
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
* 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.ui.habits.list.controllers;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.mobeta.android.dslv.DragSortListView;
|
||||
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.ui.habits.list.model.HabitCardListAdapter;
|
||||
import org.isoron.uhabits.ui.habits.list.views.HabitCardListView;
|
||||
|
||||
/**
|
||||
* Controller responsible for receiving and processing the events generated by a
|
||||
* HabitListView. These include selecting and reordering items, toggling
|
||||
* checkmarks and clicking habits.
|
||||
*/
|
||||
public class HabitCardListController implements DragSortListView.DropListener,
|
||||
DragSortListView.DragListener,
|
||||
HabitCardListView.Controller
|
||||
{
|
||||
private final Mode NORMAL_MODE = new NormalMode();
|
||||
|
||||
private final Mode SELECTION_MODE = new SelectionMode();
|
||||
|
||||
@NonNull
|
||||
private final HabitCardListAdapter adapter;
|
||||
|
||||
@NonNull
|
||||
private final HabitCardListView view;
|
||||
|
||||
@Nullable
|
||||
private HabitListener habitListener;
|
||||
|
||||
@Nullable
|
||||
private SelectionListener selectionListener;
|
||||
|
||||
@NonNull
|
||||
private Mode activeMode;
|
||||
|
||||
public HabitCardListController(@NonNull HabitCardListAdapter adapter,
|
||||
@NonNull HabitCardListView view)
|
||||
{
|
||||
this.adapter = adapter;
|
||||
this.view = view;
|
||||
this.activeMode = new NormalMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the user is dragging a habit which was originally at position
|
||||
* 'from' and is currently hovering over position 'to'. Note that the user
|
||||
* has not yet finished the dragging operation.
|
||||
*
|
||||
* @param from the original position of the habit
|
||||
* @param to the position where the habit is currently hovering
|
||||
*/
|
||||
@Override
|
||||
public void drag(int from, int to)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the user drags a habit and drops it somewhere. Note that the
|
||||
* dragging operation is already complete.
|
||||
*
|
||||
* @param from the original position of the habit
|
||||
* @param to the position where the habit was released
|
||||
*/
|
||||
@Override
|
||||
public void drop(int from, int to)
|
||||
{
|
||||
if (from == to) return;
|
||||
cancelSelection();
|
||||
|
||||
Habit habitFrom = adapter.getItem(from);
|
||||
Habit habitTo = adapter.getItem(to);
|
||||
adapter.reorder(from, to);
|
||||
|
||||
if (habitListener != null)
|
||||
habitListener.onHabitReorder(habitFrom, habitTo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the user attempts to perform a toggle, but attempt is
|
||||
* rejected.
|
||||
*/
|
||||
@Override
|
||||
public void onInvalidToggle()
|
||||
{
|
||||
if (habitListener != null) habitListener.onInvalidToggle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the user clicks at some item.
|
||||
*
|
||||
* @param position the position of the clicked item
|
||||
*/
|
||||
@Override
|
||||
public void onItemClick(int position)
|
||||
{
|
||||
activeMode.onItemClick(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the user long clicks at some item.
|
||||
*
|
||||
* @param position the position of the clicked item
|
||||
*/
|
||||
@Override
|
||||
public void onItemLongClick(int position)
|
||||
{
|
||||
activeMode.onItemLongClick(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the user wants to toggle a checkmark.
|
||||
*
|
||||
* @param habit the habit of the checkmark
|
||||
* @param timestamp the timestamps of the checkmark
|
||||
*/
|
||||
@Override
|
||||
public void onToggle(Habit habit, long timestamp)
|
||||
{
|
||||
if (habitListener != null) habitListener.onToggle(habit, timestamp);
|
||||
}
|
||||
|
||||
public void setHabitListener(@Nullable HabitListener habitListener)
|
||||
{
|
||||
this.habitListener = habitListener;
|
||||
}
|
||||
|
||||
public void setSelectionListener(@Nullable SelectionListener listener)
|
||||
{
|
||||
this.selectionListener = listener;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when the user starts dragging an item.
|
||||
*
|
||||
* @param position the position of the habit dragged
|
||||
*/
|
||||
@Override
|
||||
public void startDrag(int position)
|
||||
{
|
||||
activeMode.startDrag(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks all items as not selected and finishes the selection operation.
|
||||
*/
|
||||
private void cancelSelection()
|
||||
{
|
||||
adapter.clearSelection();
|
||||
view.setDragEnabled(true);
|
||||
activeMode = new NormalMode();
|
||||
|
||||
if (selectionListener != null) selectionListener.onSelectionFinish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects or deselects the item at a given position
|
||||
*
|
||||
* @param position the position of the item to be selected/deselected
|
||||
*/
|
||||
protected void toggleSelection(int position)
|
||||
{
|
||||
adapter.toggleSelection(position);
|
||||
activeMode = adapter.isSelectionEmpty() ? NORMAL_MODE : SELECTION_MODE;
|
||||
}
|
||||
|
||||
public interface HabitListener extends CheckmarkButtonController.Listener
|
||||
{
|
||||
/**
|
||||
* Called when the user clicks a habit.
|
||||
*
|
||||
* @param habit the habit clicked
|
||||
*/
|
||||
void onHabitClick(Habit habit);
|
||||
|
||||
/**
|
||||
* Called when the user wants to change the position of a habit on the
|
||||
* list.
|
||||
*
|
||||
* @param from habit to be moved
|
||||
* @param to habit that currently occupies the desired position
|
||||
*/
|
||||
void onHabitReorder(Habit from, Habit to);
|
||||
}
|
||||
|
||||
/**
|
||||
* A Mode describes the behaviour of the list upon clicking, long clicking
|
||||
* and dragging an item. This depends on whether some items are already
|
||||
* selected or not.
|
||||
*/
|
||||
private interface Mode
|
||||
{
|
||||
void onItemClick(int position);
|
||||
|
||||
boolean onItemLongClick(int position);
|
||||
|
||||
void startDrag(int position);
|
||||
}
|
||||
|
||||
public interface SelectionListener
|
||||
{
|
||||
/**
|
||||
* Called when the user changes the list of selected item. This is only
|
||||
* called if there were previously selected items. If the selection was
|
||||
* previously empty, then onHabitSelectionStart is called instead.
|
||||
*/
|
||||
void onSelectionChange();
|
||||
|
||||
/**
|
||||
* Called when the user deselects all items or cancels the selection.
|
||||
*/
|
||||
void onSelectionFinish();
|
||||
|
||||
/**
|
||||
* Called after the user selects the first item.
|
||||
*/
|
||||
void onSelectionStart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mode activated when there are no items selected. Clicks trigger habit
|
||||
* click. Long clicks start selection.
|
||||
*/
|
||||
class NormalMode implements Mode
|
||||
{
|
||||
@Override
|
||||
public void onItemClick(int position)
|
||||
{
|
||||
Habit habit = adapter.getItem(position);
|
||||
if (habitListener != null) habitListener.onHabitClick(habit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemLongClick(int position)
|
||||
{
|
||||
startSelection(position);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startDrag(int position)
|
||||
{
|
||||
startSelection(position);
|
||||
}
|
||||
|
||||
protected void startSelection(int position)
|
||||
{
|
||||
toggleSelection(position);
|
||||
activeMode = SELECTION_MODE;
|
||||
if (selectionListener != null) selectionListener.onSelectionStart();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mode activated when some items are already selected.
|
||||
* <p>
|
||||
* Clicks toggle item selection. Long clicks select more items.
|
||||
*/
|
||||
class SelectionMode implements Mode
|
||||
{
|
||||
@Override
|
||||
public void onItemClick(int position)
|
||||
{
|
||||
toggleSelection(position);
|
||||
notifyListener();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemLongClick(int position)
|
||||
{
|
||||
toggleSelection(position);
|
||||
notifyListener();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startDrag(int position)
|
||||
{
|
||||
toggleSelection(position);
|
||||
notifyListener();
|
||||
}
|
||||
|
||||
protected void notifyListener()
|
||||
{
|
||||
if (habitListener == null) return;
|
||||
|
||||
if (activeMode == SELECTION_MODE)
|
||||
selectionListener.onSelectionChange();
|
||||
else
|
||||
selectionListener.onSelectionFinish();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Contains controllers that are specific for ListHabitsActivity
|
||||
*/
|
||||
package org.isoron.uhabits.ui.habits.list.controllers;
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* 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.ui.habits.list.model;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
|
||||
import org.isoron.uhabits.HabitsApplication;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.models.ModelObservable;
|
||||
import org.isoron.uhabits.ui.habits.list.views.HabitCardListView;
|
||||
import org.isoron.uhabits.ui.habits.list.views.HabitCardView;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Provides data that backs a {@link HabitCardListView}. The data if fetched and
|
||||
* cached by a {@link HabitCardListCache}. This adapter also holds a list of
|
||||
* items that have been selected.
|
||||
*/
|
||||
public class HabitCardListAdapter extends BaseAdapter
|
||||
implements HabitCardListCache.Listener
|
||||
{
|
||||
@NonNull
|
||||
private ModelObservable observable;
|
||||
|
||||
@Inject
|
||||
@NonNull
|
||||
HabitCardListCache cache;
|
||||
|
||||
@Nullable
|
||||
private HabitCardListView listView;
|
||||
|
||||
@NonNull
|
||||
private final LinkedList<Habit> selected;
|
||||
|
||||
public HabitCardListAdapter()
|
||||
{
|
||||
this.selected = new LinkedList<>();
|
||||
this.observable = new ModelObservable();
|
||||
|
||||
HabitsApplication.getComponent().inject(this);
|
||||
|
||||
cache.setListener(this);
|
||||
cache.setCheckmarkCount(5); // TODO: make this dynamic somehow
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all items as not selected.
|
||||
*/
|
||||
public void clearSelection()
|
||||
{
|
||||
selected.clear();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount()
|
||||
{
|
||||
return cache.getHabitCount();
|
||||
}
|
||||
|
||||
public boolean getIncludeArchived()
|
||||
{
|
||||
return cache.getIncludeArchived();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the item that occupies a certain position on the list
|
||||
*
|
||||
* @param position position of the item
|
||||
* @return the item at given position
|
||||
* @throws IndexOutOfBoundsException if position is not valid
|
||||
*/
|
||||
@Override
|
||||
@NonNull
|
||||
public Habit getItem(int position)
|
||||
{
|
||||
return cache.getHabitByPosition(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position)
|
||||
{
|
||||
return getItem(position).getId();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public ModelObservable getObservable()
|
||||
{
|
||||
return observable;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<Habit> getSelected()
|
||||
{
|
||||
return new LinkedList<>(selected);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position,
|
||||
@Nullable View view,
|
||||
@Nullable ViewGroup parent)
|
||||
{
|
||||
if (listView == null) return null;
|
||||
|
||||
Habit habit = cache.getHabitByPosition(position);
|
||||
int score = cache.getScore(habit.getId());
|
||||
int checkmarks[] = cache.getCheckmarks(habit.getId());
|
||||
boolean selected = this.selected.contains(habit);
|
||||
|
||||
return listView.buildCardView((HabitCardView) view, habit, score,
|
||||
checkmarks, selected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether list of selected items is empty.
|
||||
*
|
||||
* @return true if selection is empty, false otherwise
|
||||
*/
|
||||
public boolean isSelectionEmpty()
|
||||
{
|
||||
return selected.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the adapter that it has been attached to a ListView.
|
||||
*/
|
||||
public void onAttached()
|
||||
{
|
||||
cache.onAttached();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCacheRefresh()
|
||||
{
|
||||
notifyDataSetChanged();
|
||||
observable.notifyListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the adapter that it has been detached from a ListView.
|
||||
*/
|
||||
public void onDetached()
|
||||
{
|
||||
cache.onDetached();
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the order of habits on the adapter.
|
||||
* <p>
|
||||
* Note that this only has effect on the adapter cache. The database is not
|
||||
* modified, and the change is lost when the cache is refreshed. This method
|
||||
* is useful for making the ListView more responsive: while we wait for the
|
||||
* database operation to finish, the cache can be modified to reflect the
|
||||
* changes immediately.
|
||||
*
|
||||
* @param from the habit that should be moved
|
||||
* @param to the habit that currently occupies the desired position
|
||||
*/
|
||||
public void reorder(int from, int to)
|
||||
{
|
||||
cache.reorder(from, to);
|
||||
cache.refreshAllHabits(false);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the HabitCardListView that this adapter will provide data for.
|
||||
* <p>
|
||||
* This object will be used to generated new HabitCardViews, upon demand.
|
||||
*
|
||||
* @param listView the HabitCardListView associated with this adapter
|
||||
*/
|
||||
public void setListView(@Nullable HabitCardListView listView)
|
||||
{
|
||||
this.listView = listView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects or deselects the item at a given position.
|
||||
*
|
||||
* @param position position of the item to be toggled
|
||||
*/
|
||||
public void toggleSelection(int position)
|
||||
{
|
||||
Habit h = getItem(position);
|
||||
int k = selected.indexOf(h);
|
||||
if (k < 0) selected.add(h);
|
||||
else selected.remove(h);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void setShowArchived(boolean showArchived)
|
||||
{
|
||||
cache.setIncludeArchived(showArchived);
|
||||
cache.refreshAllHabits(true);
|
||||
}
|
||||
}
|
||||
@@ -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.ui.habits.list.model;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.isoron.uhabits.HabitsApplication;
|
||||
import org.isoron.uhabits.commands.Command;
|
||||
import org.isoron.uhabits.commands.CommandRunner;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.tasks.BaseTask;
|
||||
import org.isoron.uhabits.utils.DateUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* A HabitCardListCache fetches and keeps a cache of all the data necessary to
|
||||
* render a HabitCardListView.
|
||||
* <p>
|
||||
* This is needed since performing database lookups during scrolling can make
|
||||
* the ListView very slow. It also registers itself as an observer of the
|
||||
* models, in order to update itself automatically.
|
||||
*/
|
||||
public class HabitCardListCache implements CommandRunner.Listener
|
||||
{
|
||||
boolean includeArchived;
|
||||
|
||||
private int checkmarkCount;
|
||||
|
||||
private BaseTask currentFetchTask;
|
||||
|
||||
@Nullable
|
||||
private Listener listener;
|
||||
|
||||
@Nullable
|
||||
private Long lastLoadTimestamp;
|
||||
|
||||
@NonNull
|
||||
private CacheData data;
|
||||
|
||||
@Inject
|
||||
CommandRunner commandRunner;
|
||||
|
||||
public HabitCardListCache()
|
||||
{
|
||||
data = new CacheData();
|
||||
HabitsApplication.getComponent().inject(this);
|
||||
}
|
||||
|
||||
public int[] getCheckmarks(long habitId)
|
||||
{
|
||||
return data.checkmarks.get(habitId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the habits that occupies a certain position on the list.
|
||||
*
|
||||
* @param position the position of the habit
|
||||
* @return the habit at given position
|
||||
* @throws IndexOutOfBoundsException if position is not valid
|
||||
*/
|
||||
@NonNull
|
||||
public Habit getHabitByPosition(int position)
|
||||
{
|
||||
return data.habitsList.get(position);
|
||||
}
|
||||
|
||||
public int getHabitCount()
|
||||
{
|
||||
return data.habits.size();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Long getLastLoadTimestamp()
|
||||
{
|
||||
return lastLoadTimestamp;
|
||||
}
|
||||
|
||||
public int getScore(long habitId)
|
||||
{
|
||||
return data.scores.get(habitId);
|
||||
}
|
||||
|
||||
public boolean getIncludeArchived()
|
||||
{
|
||||
return includeArchived;
|
||||
}
|
||||
|
||||
public void onAttached()
|
||||
{
|
||||
// refreshAllHabits(true);
|
||||
if (lastLoadTimestamp == null) refreshAllHabits(true);
|
||||
commandRunner.addListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCommandExecuted(@NonNull Command command,
|
||||
@Nullable Long refreshKey)
|
||||
{
|
||||
if (refreshKey == null) refreshAllHabits(true);
|
||||
else refreshHabit(refreshKey);
|
||||
}
|
||||
|
||||
public void onDetached()
|
||||
{
|
||||
// commandRunner.removeListener(this);
|
||||
}
|
||||
|
||||
public void refreshAllHabits(final boolean refreshScoresAndCheckmarks)
|
||||
{
|
||||
if (currentFetchTask != null) currentFetchTask.cancel(true);
|
||||
currentFetchTask = new RefreshAllHabitsTask(refreshScoresAndCheckmarks);
|
||||
currentFetchTask.execute();
|
||||
}
|
||||
|
||||
public void refreshHabit(final Long id)
|
||||
{
|
||||
new RefreshHabitTask(id).execute();
|
||||
}
|
||||
|
||||
public void reorder(int from, int to)
|
||||
{
|
||||
Habit fromHabit = data.habitsList.get(from);
|
||||
Habit toHabit = data.habitsList.get(to);
|
||||
|
||||
data.habitsList.remove(from);
|
||||
data.habitsList.add(to, fromHabit);
|
||||
|
||||
Habit.reorder(fromHabit, toHabit);
|
||||
}
|
||||
|
||||
public void setCheckmarkCount(int checkmarkCount)
|
||||
{
|
||||
this.checkmarkCount = checkmarkCount;
|
||||
}
|
||||
|
||||
public void setIncludeArchived(boolean includeArchived)
|
||||
{
|
||||
this.includeArchived = includeArchived;
|
||||
}
|
||||
|
||||
public void setListener(@Nullable Listener listener)
|
||||
{
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be invoked when the data on the
|
||||
* cache has been modified.
|
||||
*/
|
||||
public interface Listener
|
||||
{
|
||||
/**
|
||||
* Called when the data on the cache has been modified.
|
||||
*/
|
||||
void onCacheRefresh();
|
||||
}
|
||||
|
||||
private class CacheData
|
||||
{
|
||||
@NonNull
|
||||
public HashMap<Long, Habit> habits;
|
||||
|
||||
@NonNull
|
||||
public List<Habit> habitsList;
|
||||
|
||||
@NonNull
|
||||
public HashMap<Long, int[]> checkmarks;
|
||||
|
||||
@NonNull
|
||||
public HashMap<Long, Integer> scores;
|
||||
|
||||
/**
|
||||
* Creates a new CacheData without any content.
|
||||
*/
|
||||
public CacheData()
|
||||
{
|
||||
habits = new HashMap<>();
|
||||
habitsList = new LinkedList<>();
|
||||
checkmarks = new HashMap<>();
|
||||
scores = new HashMap<>();
|
||||
}
|
||||
|
||||
public void copyCheckmarksFrom(@NonNull CacheData oldData)
|
||||
{
|
||||
int[] empty = new int[checkmarkCount];
|
||||
|
||||
for (Long id : habits.keySet())
|
||||
{
|
||||
if (oldData.checkmarks.containsKey(id))
|
||||
checkmarks.put(id, oldData.checkmarks.get(id));
|
||||
else checkmarks.put(id, empty);
|
||||
}
|
||||
}
|
||||
|
||||
public void copyScoresFrom(@NonNull CacheData oldData)
|
||||
{
|
||||
for (Long id : habits.keySet())
|
||||
{
|
||||
if (oldData.scores.containsKey(id))
|
||||
scores.put(id, oldData.scores.get(id));
|
||||
else scores.put(id, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void fetchHabits()
|
||||
{
|
||||
habitsList = Habit.getAll(includeArchived);
|
||||
for (Habit h : habitsList)
|
||||
habits.put(h.getId(), h);
|
||||
}
|
||||
}
|
||||
|
||||
private class RefreshAllHabitsTask extends BaseTask
|
||||
{
|
||||
@NonNull
|
||||
private CacheData newData;
|
||||
|
||||
private final boolean refreshScoresAndCheckmarks;
|
||||
|
||||
public RefreshAllHabitsTask(boolean refreshScoresAndCheckmarks)
|
||||
{
|
||||
this.refreshScoresAndCheckmarks = refreshScoresAndCheckmarks;
|
||||
newData = new CacheData();
|
||||
}
|
||||
|
||||
private void commit()
|
||||
{
|
||||
data = newData;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInBackground()
|
||||
{
|
||||
newData.fetchHabits();
|
||||
newData.copyScoresFrom(data);
|
||||
newData.copyCheckmarksFrom(data);
|
||||
|
||||
commit();
|
||||
|
||||
if (!refreshScoresAndCheckmarks) return;
|
||||
|
||||
long dateTo = DateUtils.getStartOfDay(DateUtils.getLocalTime());
|
||||
long dateFrom =
|
||||
dateTo - (checkmarkCount - 1) * DateUtils.millisecondsInOneDay;
|
||||
|
||||
int current = 0;
|
||||
for (Habit h : newData.habitsList)
|
||||
{
|
||||
if (isCancelled()) return;
|
||||
|
||||
Long id = h.getId();
|
||||
newData.scores.put(id, h.scores.getTodayValue());
|
||||
newData.checkmarks.put(id,
|
||||
h.checkmarks.getValues(dateFrom, dateTo));
|
||||
|
||||
publishProgress(current++, newData.habits.size());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid)
|
||||
{
|
||||
if (isCancelled()) return;
|
||||
|
||||
lastLoadTimestamp = DateUtils.getStartOfToday();
|
||||
currentFetchTask = null;
|
||||
|
||||
if (listener != null) listener.onCacheRefresh();
|
||||
super.onPostExecute(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(Integer... values)
|
||||
{
|
||||
if (listener != null) listener.onCacheRefresh();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class RefreshHabitTask extends BaseTask
|
||||
{
|
||||
private final Long id;
|
||||
|
||||
public RefreshHabitTask(Long id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInBackground()
|
||||
{
|
||||
long dateTo = DateUtils.getStartOfDay(DateUtils.getLocalTime());
|
||||
long dateFrom =
|
||||
dateTo - (checkmarkCount - 1) * DateUtils.millisecondsInOneDay;
|
||||
|
||||
Habit h = Habit.get(id);
|
||||
if (h == null) return;
|
||||
|
||||
data.habits.put(id, h);
|
||||
data.scores.put(id, h.scores.getTodayValue());
|
||||
data.checkmarks.put(id, h.checkmarks.getValues(dateFrom, dateTo));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid)
|
||||
{
|
||||
if (listener != null) listener.onCacheRefresh();
|
||||
super.onPostExecute(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.ui.habits.list.model;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.isoron.uhabits.HabitsApplication;
|
||||
import org.isoron.uhabits.utils.DateUtils;
|
||||
import org.isoron.uhabits.utils.Preferences;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Provides a list of hints to be shown at the application startup, and takes
|
||||
* care of deciding when a new hint should be shown.
|
||||
*/
|
||||
public class HintList
|
||||
{
|
||||
@Inject
|
||||
Preferences prefs;
|
||||
|
||||
@NonNull
|
||||
private final String[] hints;
|
||||
|
||||
/**
|
||||
* Constructs a new list containing the provided hints.
|
||||
*
|
||||
* @param hints initial list of hints
|
||||
*/
|
||||
public HintList(@NonNull String hints[])
|
||||
{
|
||||
this.hints = hints;
|
||||
HabitsApplication.getComponent().inject(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new hint to be shown to the user.
|
||||
* <p>
|
||||
* The hint returned is marked as read on the list, and will not be returned
|
||||
* again. In case all hints have already been read, and there is nothing
|
||||
* left, returns null.
|
||||
*
|
||||
* @return the next hint to be shown, or null if none
|
||||
*/
|
||||
public String pop()
|
||||
{
|
||||
int next = prefs.getLastHintNumber() + 1;
|
||||
if (next >= hints.length) return null;
|
||||
|
||||
prefs.updateLastHint(next, DateUtils.getStartOfToday());
|
||||
return hints[next];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether it is time to show a new hint or not.
|
||||
*
|
||||
* @return true if hint should be shown, false otherwise
|
||||
*/
|
||||
public boolean shouldShow()
|
||||
{
|
||||
long lastHintTimestamp = prefs.getLastHintTimestamp();
|
||||
return (DateUtils.getStartOfToday() > lastHintTimestamp);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Contains model classes that are specific for ListHabitsActivity
|
||||
*/
|
||||
package org.isoron.uhabits.ui.habits.list.model;
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Contains classes for ListHabitsActivity.
|
||||
*/
|
||||
package org.isoron.uhabits.ui.habits.list;
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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.ui.habits.list.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.models.Checkmark;
|
||||
import org.isoron.uhabits.ui.habits.list.controllers.CheckmarkButtonController;
|
||||
import org.isoron.uhabits.utils.InterfaceUtils;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public class CheckmarkButtonView extends FrameLayout
|
||||
{
|
||||
private int color;
|
||||
|
||||
private int value;
|
||||
|
||||
@BindView(R.id.tvCheck)
|
||||
TextView tvCheck;
|
||||
|
||||
public CheckmarkButtonView(Context context)
|
||||
{
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public void setColor(int color)
|
||||
{
|
||||
this.color = color;
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
public void setController(final CheckmarkButtonController controller)
|
||||
{
|
||||
setOnClickListener(v -> controller.onClick());
|
||||
setOnLongClickListener(v -> controller.onLongClick());
|
||||
}
|
||||
|
||||
public void setValue(int value)
|
||||
{
|
||||
this.value = value;
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
public void toggle()
|
||||
{
|
||||
value = (value == Checkmark.CHECKED_EXPLICITLY ? Checkmark.UNCHECKED :
|
||||
Checkmark.CHECKED_EXPLICITLY);
|
||||
|
||||
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
addView(
|
||||
inflate(getContext(), R.layout.list_habits_card_checkmark, null));
|
||||
ButterKnife.bind(this);
|
||||
|
||||
setWillNotDraw(false);
|
||||
setHapticFeedbackEnabled(false);
|
||||
|
||||
tvCheck.setTypeface(InterfaceUtils.getFontAwesome(getContext()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas)
|
||||
{
|
||||
int lowContrastColor = InterfaceUtils.getStyledColor(getContext(),
|
||||
R.attr.lowContrastTextColor);
|
||||
|
||||
if (value == Checkmark.CHECKED_EXPLICITLY)
|
||||
{
|
||||
tvCheck.setText(R.string.fa_check);
|
||||
tvCheck.setTextColor(color);
|
||||
}
|
||||
|
||||
if (value == Checkmark.CHECKED_IMPLICITLY)
|
||||
{
|
||||
tvCheck.setText(R.string.fa_check);
|
||||
tvCheck.setTextColor(lowContrastColor);
|
||||
}
|
||||
|
||||
if (value == Checkmark.UNCHECKED)
|
||||
{
|
||||
tvCheck.setText(R.string.fa_times);
|
||||
tvCheck.setTextColor(lowContrastColor);
|
||||
}
|
||||
|
||||
super.onDraw(canvas);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* 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.ui.habits.list.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import org.isoron.uhabits.HabitsApplication;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.ui.habits.list.controllers.CheckmarkButtonController;
|
||||
import org.isoron.uhabits.utils.DateUtils;
|
||||
import org.isoron.uhabits.utils.Preferences;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class CheckmarkPanelView extends LinearLayout
|
||||
{
|
||||
private static final int CHECKMARK_LEFT_TO_RIGHT = 0;
|
||||
|
||||
private static final int CHECKMARK_RIGHT_TO_LEFT = 1;
|
||||
|
||||
@Inject
|
||||
Preferences prefs;
|
||||
|
||||
private int checkmarkValues[];
|
||||
|
||||
private int nButtons;
|
||||
|
||||
private int color;
|
||||
|
||||
private Controller controller;
|
||||
|
||||
@NonNull
|
||||
private Habit habit;
|
||||
|
||||
public CheckmarkPanelView(Context context)
|
||||
{
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public CheckmarkPanelView(Context context, AttributeSet attrs)
|
||||
{
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public CheckmarkPanelView(Context context,
|
||||
AttributeSet attrs,
|
||||
int defStyleAttr)
|
||||
{
|
||||
super(context, attrs, defStyleAttr);
|
||||
init();
|
||||
}
|
||||
|
||||
public CheckmarkButtonView getButton(int position)
|
||||
{
|
||||
return (CheckmarkButtonView) getChildAt(position);
|
||||
}
|
||||
|
||||
public void setCheckmarkValues(int[] checkmarkValues)
|
||||
{
|
||||
this.checkmarkValues = checkmarkValues;
|
||||
|
||||
if (this.nButtons != checkmarkValues.length)
|
||||
{
|
||||
this.nButtons = checkmarkValues.length;
|
||||
addCheckmarkButtons();
|
||||
}
|
||||
|
||||
setupCheckmarkButtons();
|
||||
}
|
||||
|
||||
public void setColor(int color)
|
||||
{
|
||||
this.color = color;
|
||||
setupCheckmarkButtons();
|
||||
}
|
||||
|
||||
public void setController(Controller controller)
|
||||
{
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
public void setHabit(@NonNull Habit habit)
|
||||
{
|
||||
this.habit = habit;
|
||||
}
|
||||
|
||||
private void addCheckmarkButtons()
|
||||
{
|
||||
removeAllViews();
|
||||
|
||||
for (int i = 0; i < nButtons; i++)
|
||||
addView(new CheckmarkButtonView(getContext()));
|
||||
}
|
||||
|
||||
private int getCheckmarkOrder()
|
||||
{
|
||||
if (isInEditMode()) return CHECKMARK_LEFT_TO_RIGHT;
|
||||
return prefs.shouldReverseCheckmarks() ? CHECKMARK_RIGHT_TO_LEFT :
|
||||
CHECKMARK_LEFT_TO_RIGHT;
|
||||
}
|
||||
|
||||
private CheckmarkButtonView indexToButton(int i)
|
||||
{
|
||||
int position = i;
|
||||
|
||||
if (getCheckmarkOrder() == CHECKMARK_RIGHT_TO_LEFT)
|
||||
position = nButtons - i - 1;
|
||||
|
||||
return (CheckmarkButtonView) getChildAt(position);
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
if (isInEditMode()) return;
|
||||
HabitsApplication.getComponent().inject(this);
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
||||
{
|
||||
float buttonWidth = getResources().getDimension(R.dimen.checkmarkWidth);
|
||||
float buttonHeight =
|
||||
getResources().getDimension(R.dimen.checkmarkHeight);
|
||||
|
||||
float width = buttonWidth * nButtons;
|
||||
|
||||
widthMeasureSpec =
|
||||
MeasureSpec.makeMeasureSpec((int) width, MeasureSpec.EXACTLY);
|
||||
heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) buttonHeight,
|
||||
MeasureSpec.EXACTLY);
|
||||
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
|
||||
private void setupButtonControllers(long timestamp,
|
||||
CheckmarkButtonView buttonView)
|
||||
{
|
||||
if (controller == null) return;
|
||||
|
||||
CheckmarkButtonController buttonController =
|
||||
new CheckmarkButtonController(habit, timestamp);
|
||||
|
||||
buttonController.setListener(controller);
|
||||
buttonController.setView(buttonView);
|
||||
buttonView.setController(buttonController);
|
||||
}
|
||||
|
||||
private void setupCheckmarkButtons()
|
||||
{
|
||||
long timestamp = DateUtils.getStartOfToday();
|
||||
long day = DateUtils.millisecondsInOneDay;
|
||||
|
||||
for (int i = 0; i < nButtons; i++)
|
||||
{
|
||||
CheckmarkButtonView buttonView = indexToButton(i);
|
||||
buttonView.setValue(checkmarkValues[i]);
|
||||
buttonView.setColor(color);
|
||||
setupButtonControllers(timestamp, buttonView);
|
||||
timestamp -= day;
|
||||
}
|
||||
}
|
||||
|
||||
public interface Controller extends CheckmarkButtonController.Listener
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* 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.ui.habits.list.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ListAdapter;
|
||||
|
||||
import com.mobeta.android.dslv.DragSortController;
|
||||
import com.mobeta.android.dslv.DragSortListView;
|
||||
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.ui.habits.list.controllers.CheckmarkButtonController;
|
||||
import org.isoron.uhabits.ui.habits.list.controllers.HabitCardController;
|
||||
import org.isoron.uhabits.ui.habits.list.model.HabitCardListAdapter;
|
||||
|
||||
public class HabitCardListView extends DragSortListView
|
||||
{
|
||||
@Nullable
|
||||
private HabitCardListAdapter adapter;
|
||||
|
||||
@Nullable
|
||||
private Controller controller;
|
||||
|
||||
public HabitCardListView(Context context, AttributeSet attrs)
|
||||
{
|
||||
super(context, attrs);
|
||||
setFloatViewManager(new ViewManager());
|
||||
setDragEnabled(true);
|
||||
setLongClickable(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a new HabitCardView to be eventually added to this list,
|
||||
* containing the given data.
|
||||
*
|
||||
* @param cardView an old HabitCardView that should be reused if possible,
|
||||
* possibly null
|
||||
* @param habit the habit for this card
|
||||
* @param score the current score for the habit
|
||||
* @param checkmarks the list of checkmark values to be included in the
|
||||
* card
|
||||
* @param selected true if the card is selected, false otherwise
|
||||
* @return the HabitCardView generated
|
||||
*/
|
||||
public View buildCardView(@Nullable HabitCardView cardView,
|
||||
@NonNull Habit habit,
|
||||
int score,
|
||||
int[] checkmarks,
|
||||
boolean selected)
|
||||
{
|
||||
if (cardView == null) cardView = new HabitCardView(getContext());
|
||||
|
||||
cardView.setHabit(habit);
|
||||
cardView.setSelected(selected);
|
||||
cardView.setCheckmarkValues(checkmarks);
|
||||
cardView.setScore(score);
|
||||
|
||||
if (controller != null)
|
||||
{
|
||||
HabitCardController cardController = new HabitCardController();
|
||||
cardController.setListener(controller);
|
||||
cardView.setController(cardController);
|
||||
cardController.setView(cardView);
|
||||
}
|
||||
|
||||
return cardView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAdapter(ListAdapter adapter)
|
||||
{
|
||||
this.adapter = (HabitCardListAdapter) adapter;
|
||||
super.setAdapter(adapter);
|
||||
}
|
||||
|
||||
public void setController(@Nullable Controller controller)
|
||||
{
|
||||
this.controller = controller;
|
||||
setDropListener(controller);
|
||||
setDragListener(controller);
|
||||
setOnItemClickListener(null);
|
||||
setOnLongClickListener(null);
|
||||
|
||||
if (controller == null) return;
|
||||
|
||||
setOnItemClickListener((p, v, pos, id) -> controller.onItemClick(pos));
|
||||
setOnItemLongClickListener((p, v, pos, id) -> {
|
||||
controller.onItemLongClick(pos);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public void toggleShowArchived()
|
||||
{
|
||||
// showArchived = !showArchived;
|
||||
// cache.setIncludeArchived(showArchived);
|
||||
// cache.refreshAllHabits(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow()
|
||||
{
|
||||
super.onAttachedToWindow();
|
||||
if (adapter != null) adapter.onAttached();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow()
|
||||
{
|
||||
if (adapter != null) adapter.onDetached();
|
||||
super.onDetachedFromWindow();
|
||||
}
|
||||
|
||||
public interface Controller extends CheckmarkButtonController.Listener,
|
||||
HabitCardController.Listener,
|
||||
DropListener,
|
||||
DragListener
|
||||
{
|
||||
void onItemClick(int pos);
|
||||
|
||||
void onItemLongClick(int pos);
|
||||
}
|
||||
|
||||
private class ViewManager extends DragSortController
|
||||
{
|
||||
public ViewManager()
|
||||
{
|
||||
super(HabitCardListView.this);
|
||||
setRemoveEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateFloatView(int position)
|
||||
{
|
||||
if (adapter == null) return null;
|
||||
return adapter.getView(position, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyFloatView(View floatView)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* 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.ui.habits.list.views;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Handler;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.models.Score;
|
||||
import org.isoron.uhabits.utils.ColorUtils;
|
||||
import org.isoron.uhabits.views.RingView;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
import static org.isoron.uhabits.utils.InterfaceUtils.getStyledColor;
|
||||
import static org.isoron.uhabits.utils.InterfaceUtils.getStyledDrawable;
|
||||
|
||||
public class HabitCardView extends FrameLayout
|
||||
{
|
||||
private Habit habit;
|
||||
|
||||
@BindView(R.id.checkmarkPanel)
|
||||
CheckmarkPanelView checkmarkPanel;
|
||||
|
||||
@BindView(R.id.innerFrame)
|
||||
LinearLayout innerFrame;
|
||||
|
||||
@BindView(R.id.label)
|
||||
TextView label;
|
||||
|
||||
@BindView(R.id.scoreRing)
|
||||
RingView scoreRing;
|
||||
|
||||
private final Context context = getContext();
|
||||
|
||||
public HabitCardView(Context context)
|
||||
{
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public HabitCardView(Context context, AttributeSet attrs)
|
||||
{
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public HabitCardView(Context context, AttributeSet attrs, int defStyleAttr)
|
||||
{
|
||||
super(context, attrs, defStyleAttr);
|
||||
init();
|
||||
}
|
||||
|
||||
public void setCheckmarkValues(int checkmarks[])
|
||||
{
|
||||
checkmarkPanel.setCheckmarkValues(checkmarks);
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
public void setController(Controller controller)
|
||||
{
|
||||
checkmarkPanel.setController(null);
|
||||
if (controller == null) return;
|
||||
|
||||
checkmarkPanel.setController(controller);
|
||||
}
|
||||
|
||||
public void setHabit(Habit habit)
|
||||
{
|
||||
this.habit = habit;
|
||||
int color = getActiveColor(habit);
|
||||
|
||||
label.setText(habit.name);
|
||||
label.setTextColor(color);
|
||||
scoreRing.setColor(color);
|
||||
checkmarkPanel.setColor(color);
|
||||
checkmarkPanel.setHabit(habit);
|
||||
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
public void setScore(int score)
|
||||
{
|
||||
float percentage = (float) score / Score.MAX_VALUE;
|
||||
scoreRing.setPercentage(percentage);
|
||||
scoreRing.setPrecision(1.0f / 16);
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelected(boolean isSelected)
|
||||
{
|
||||
super.setSelected(isSelected);
|
||||
updateBackground(isSelected);
|
||||
}
|
||||
|
||||
public void triggerRipple(final float x, final float y)
|
||||
{
|
||||
final Drawable background = innerFrame.getBackground();
|
||||
if (android.os.Build.VERSION.SDK_INT >= 21) background.setHotspot(x, y);
|
||||
background.setState(new int[]{
|
||||
android.R.attr.state_pressed, android.R.attr.state_enabled
|
||||
});
|
||||
new Handler().postDelayed(() -> background.setState(new int[]{}), 25);
|
||||
}
|
||||
|
||||
private int getActiveColor(Habit habit)
|
||||
{
|
||||
int mediumContrastColor =
|
||||
getStyledColor(context, R.attr.mediumContrastTextColor);
|
||||
int activeColor = ColorUtils.getColor(context, habit.color);
|
||||
if (habit.isArchived()) activeColor = mediumContrastColor;
|
||||
|
||||
return activeColor;
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
|
||||
inflate(context, R.layout.list_habits_card, this);
|
||||
ButterKnife.bind(this);
|
||||
|
||||
innerFrame.setOnTouchListener((v, event) -> {
|
||||
if (android.os.Build.VERSION.SDK_INT >= 21)
|
||||
v.getBackground().setHotspot(event.getX(), event.getY());
|
||||
return false;
|
||||
});
|
||||
|
||||
if (isInEditMode()) initEditMode();
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private void initEditMode()
|
||||
{
|
||||
String habits[] = {
|
||||
"Wake up early",
|
||||
"Wash dishes",
|
||||
"Exercise",
|
||||
"Meditate",
|
||||
"Play guitar",
|
||||
"Wash clothes",
|
||||
"Get a haircut"
|
||||
};
|
||||
|
||||
Random rand = new Random();
|
||||
int color = ColorUtils.CSV_PALETTE[rand.nextInt(10)];
|
||||
int[] values = {
|
||||
rand.nextInt(3),
|
||||
rand.nextInt(3),
|
||||
rand.nextInt(3),
|
||||
rand.nextInt(3),
|
||||
rand.nextInt(3)
|
||||
};
|
||||
|
||||
label.setText(habits[rand.nextInt(habits.length)]);
|
||||
label.setTextColor(color);
|
||||
scoreRing.setColor(color);
|
||||
scoreRing.setPercentage(rand.nextFloat());
|
||||
checkmarkPanel.setColor(color);
|
||||
checkmarkPanel.setCheckmarkValues(values);
|
||||
}
|
||||
|
||||
private void updateBackground(boolean isSelected)
|
||||
{
|
||||
if (android.os.Build.VERSION.SDK_INT >= 21)
|
||||
{
|
||||
if (isSelected)
|
||||
innerFrame.setBackgroundResource(R.drawable.selected_box);
|
||||
else innerFrame.setBackgroundResource(R.drawable.ripple);
|
||||
}
|
||||
else
|
||||
{
|
||||
Drawable background;
|
||||
|
||||
if (isSelected) background =
|
||||
getStyledDrawable(context, R.attr.selectedBackground);
|
||||
else background = getStyledDrawable(context, R.attr.cardBackground);
|
||||
|
||||
innerFrame.setBackgroundDrawable(background);
|
||||
}
|
||||
}
|
||||
|
||||
public interface Controller extends CheckmarkPanelView.Controller
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.ui.habits.list.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.utils.DateUtils;
|
||||
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
public class HeaderView extends LinearLayout
|
||||
{
|
||||
private static final int CHECKMARK_LEFT_TO_RIGHT = 0;
|
||||
|
||||
private static final int CHECKMARK_RIGHT_TO_LEFT = 1;
|
||||
|
||||
private final Context context;
|
||||
|
||||
public HeaderView(Context context, AttributeSet attrs)
|
||||
{
|
||||
super(context, attrs);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
private void createButtons()
|
||||
{
|
||||
removeAllViews();
|
||||
GregorianCalendar day = DateUtils.getStartOfTodayCalendar();
|
||||
|
||||
for (int i = 0; i < getButtonCount(); i++)
|
||||
{
|
||||
int position = 0;
|
||||
|
||||
if (getCheckmarkOrder() == CHECKMARK_LEFT_TO_RIGHT) position = i;
|
||||
|
||||
View tvDay =
|
||||
inflate(context, R.layout.list_habits_header_checkmark, null);
|
||||
TextView btCheck = (TextView) tvDay.findViewById(R.id.tvCheck);
|
||||
btCheck.setText(DateUtils.formatHeaderDate(day));
|
||||
addView(tvDay, position);
|
||||
day.add(GregorianCalendar.DAY_OF_MONTH, -1);
|
||||
}
|
||||
}
|
||||
|
||||
private int getButtonCount()
|
||||
{
|
||||
float labelWidth = getResources().getDimension(R.dimen.habitNameWidth);
|
||||
float buttonWidth = getResources().getDimension(R.dimen.checkmarkWidth);
|
||||
return Math.max(0,
|
||||
(int) ((getMeasuredWidth() - labelWidth) / buttonWidth));
|
||||
}
|
||||
|
||||
private int getCheckmarkOrder()
|
||||
{
|
||||
if (isInEditMode()) return CHECKMARK_LEFT_TO_RIGHT;
|
||||
|
||||
SharedPreferences prefs =
|
||||
PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||
boolean reverse =
|
||||
prefs.getBoolean("pref_checkmark_reverse_order", false);
|
||||
return reverse ? CHECKMARK_RIGHT_TO_LEFT : CHECKMARK_LEFT_TO_RIGHT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
||||
{
|
||||
createButtons();
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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.ui.habits.list.views;
|
||||
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.ui.habits.list.model.HintList;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public class HintView extends FrameLayout
|
||||
{
|
||||
@BindView(R.id.hintContent)
|
||||
TextView hintContent;
|
||||
|
||||
@Nullable
|
||||
private HintList hintList;
|
||||
|
||||
public HintView(Context context)
|
||||
{
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public HintView(Context context, AttributeSet attrs)
|
||||
{
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public HintView(Context context, AttributeSet attrs, int defStyleAttr)
|
||||
{
|
||||
super(context, attrs, defStyleAttr);
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToWindow()
|
||||
{
|
||||
super.onAttachedToWindow();
|
||||
showNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of hints to be shown
|
||||
*
|
||||
* @param hintList the list of hints to be shown
|
||||
*/
|
||||
public void setHints(@Nullable HintList hintList)
|
||||
{
|
||||
this.hintList = hintList;
|
||||
}
|
||||
|
||||
private void dismiss()
|
||||
{
|
||||
animate().alpha(0f).setDuration(500).setListener(new DismissAnimator());
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
addView(inflate(getContext(), R.layout.list_habits_hint, null));
|
||||
ButterKnife.bind(this);
|
||||
|
||||
setVisibility(GONE);
|
||||
setClickable(true);
|
||||
setOnClickListener(v -> dismiss());
|
||||
|
||||
if (isInEditMode()) initEditMode();
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private void initEditMode()
|
||||
{
|
||||
String hints[] = {
|
||||
"Cats are the most popular pet in the United States: There " +
|
||||
"are 88 million pet cats and 74 million dogs.",
|
||||
"A cat has been mayor of Talkeetna, Alaska, for 15 years. " +
|
||||
"His name is Stubbs.",
|
||||
"Cats can’t taste sweetness."
|
||||
};
|
||||
|
||||
int k = new Random().nextInt(hints.length);
|
||||
hintContent.setText(hints[k]);
|
||||
setVisibility(VISIBLE);
|
||||
setAlpha(1.0f);
|
||||
}
|
||||
|
||||
private void showNext()
|
||||
{
|
||||
if (hintList == null) return;
|
||||
if (!hintList.shouldShow()) return;
|
||||
|
||||
String hint = hintList.pop();
|
||||
if (hint == null) return;
|
||||
|
||||
hintContent.setText(hint);
|
||||
setAlpha(0.0f);
|
||||
setVisibility(View.VISIBLE);
|
||||
animate().alpha(1f).setDuration(500);
|
||||
}
|
||||
|
||||
private class DismissAnimator extends AnimatorListenerAdapter
|
||||
{
|
||||
@Override
|
||||
public void onAnimationEnd(android.animation.Animator animation)
|
||||
{
|
||||
setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,10 +25,13 @@ import android.os.Bundle;
|
||||
import android.support.v7.app.ActionBar;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.utils.ColorUtils;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.ui.BaseActivity;
|
||||
|
||||
/**
|
||||
* Activity that allows the user to see more information about a single habit.
|
||||
* Shows all the metadata for the habit, in addition to several charts.
|
||||
*/
|
||||
public class ShowHabitActivity extends BaseActivity
|
||||
{
|
||||
private Habit habit;
|
||||
@@ -42,19 +45,19 @@ public class ShowHabitActivity extends BaseActivity
|
||||
habit = Habit.get(ContentUris.parseId(data));
|
||||
|
||||
setContentView(R.layout.show_habit_activity);
|
||||
setupSupportActionBar(true);
|
||||
// setupSupportActionBar(true);
|
||||
setupHabitActionBar();
|
||||
}
|
||||
|
||||
public void setupHabitActionBar()
|
||||
{
|
||||
if(habit == null) return;
|
||||
if (habit == null) return;
|
||||
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if(actionBar == null) return;
|
||||
if (actionBar == null) return;
|
||||
|
||||
actionBar.setTitle(habit.name);
|
||||
setupActionBarColor(ColorUtils.getColor(this, habit.color));
|
||||
// setupActionBarColor(ColorUtils.getColor(this, habit.color));
|
||||
}
|
||||
|
||||
public Habit getHabit()
|
||||
|
||||
@@ -52,29 +52,47 @@ import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
|
||||
public class ShowHabitFragment extends Fragment implements ModelObservable.Listener
|
||||
public class ShowHabitFragment extends Fragment
|
||||
implements ModelObservable.Listener
|
||||
{
|
||||
Habit habit;
|
||||
|
||||
float todayScore;
|
||||
|
||||
float lastMonthScore;
|
||||
|
||||
float lastYearScore;
|
||||
|
||||
int activeColor;
|
||||
|
||||
int inactiveColor;
|
||||
|
||||
int previousScoreInterval;
|
||||
|
||||
private ShowHabitHelper helper;
|
||||
|
||||
protected ShowHabitActivity activity;
|
||||
|
||||
private List<HabitDataView> dataViews;
|
||||
|
||||
@BindView(R.id.sStrengthInterval) Spinner sStrengthInterval;
|
||||
@BindView(R.id.scoreView) HabitScoreView habitScoreView;
|
||||
@BindView(R.id.historyView) HabitHistoryView habitHistoryView;
|
||||
@BindView(R.id.punchcardView) HabitFrequencyView habitFrequencyView;
|
||||
@BindView(R.id.streakView) HabitStreakView habitStreakView;
|
||||
@BindView(R.id.sStrengthInterval)
|
||||
Spinner sStrengthInterval;
|
||||
|
||||
@BindView(R.id.scoreView)
|
||||
HabitScoreView habitScoreView;
|
||||
|
||||
@BindView(R.id.historyView)
|
||||
HabitHistoryView habitHistoryView;
|
||||
|
||||
@BindView(R.id.punchcardView)
|
||||
HabitFrequencyView habitFrequencyView;
|
||||
|
||||
@BindView(R.id.streakView)
|
||||
HabitStreakView habitStreakView;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
ViewGroup container,
|
||||
Bundle savedInstanceState)
|
||||
{
|
||||
View view = inflater.inflate(R.layout.show_habit, container, false);
|
||||
@@ -87,12 +105,14 @@ public class ShowHabitFragment extends Fragment implements ModelObservable.Liste
|
||||
helper.updateColors();
|
||||
helper.updateMainHeader(view);
|
||||
|
||||
int defaultScoreInterval = InterfaceUtils.getDefaultScoreInterval(getContext());
|
||||
int defaultScoreInterval =
|
||||
InterfaceUtils.getDefaultScoreInterval(getContext());
|
||||
previousScoreInterval = defaultScoreInterval;
|
||||
setScoreBucketSize(defaultScoreInterval);
|
||||
|
||||
sStrengthInterval.setSelection(defaultScoreInterval);
|
||||
sStrengthInterval.setOnItemSelectedListener(new OnItemSelectedListener());
|
||||
sStrengthInterval.setOnItemSelectedListener(
|
||||
new OnItemSelectedListener());
|
||||
|
||||
createDataViews();
|
||||
helper.updateCardHeaders(view);
|
||||
@@ -117,7 +137,7 @@ public class ShowHabitFragment extends Fragment implements ModelObservable.Liste
|
||||
dataViews.add(habitFrequencyView);
|
||||
dataViews.add(habitStreakView);
|
||||
|
||||
for(HabitDataView dataView : dataViews)
|
||||
for (HabitDataView dataView : dataViews)
|
||||
dataView.setHabit(habit);
|
||||
}
|
||||
|
||||
@@ -131,7 +151,7 @@ public class ShowHabitFragment extends Fragment implements ModelObservable.Liste
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
|
||||
{
|
||||
inflater.inflate(R.menu.show_habit_fragment, menu);
|
||||
// inflater.inflate(R.menu.show_habit_fragment, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -145,9 +165,10 @@ public class ShowHabitFragment extends Fragment implements ModelObservable.Liste
|
||||
|
||||
private boolean showEditHabitDialog()
|
||||
{
|
||||
if(habit == null) return false;
|
||||
if (habit == null) return false;
|
||||
|
||||
BaseDialogFragment frag = EditHabitDialogFragment.newInstance(habit.getId());
|
||||
BaseDialogFragment frag =
|
||||
EditHabitDialogFragment.newInstance(habit.getId());
|
||||
frag.show(getFragmentManager(), "editHabit");
|
||||
return true;
|
||||
}
|
||||
@@ -159,12 +180,12 @@ public class ShowHabitFragment extends Fragment implements ModelObservable.Liste
|
||||
|
||||
private void setScoreBucketSize(int position)
|
||||
{
|
||||
if(getView() == null) return;
|
||||
if (getView() == null) return;
|
||||
|
||||
habitScoreView.setBucketSize(HabitScoreView.DEFAULT_BUCKET_SIZES[position]);
|
||||
habitScoreView.setBucketSize(
|
||||
HabitScoreView.DEFAULT_BUCKET_SIZES[position]);
|
||||
|
||||
if(position != previousScoreInterval)
|
||||
refreshData();
|
||||
if (position != previousScoreInterval) refreshData();
|
||||
|
||||
InterfaceUtils.setDefaultScoreInterval(getContext(), position);
|
||||
previousScoreInterval = position;
|
||||
@@ -182,7 +203,7 @@ public class ShowHabitFragment extends Fragment implements ModelObservable.Liste
|
||||
helper.updateColors();
|
||||
helper.updateMainHeader(getView());
|
||||
helper.updateCardHeaders(getView());
|
||||
if(activity != null) activity.setupHabitActionBar();
|
||||
if (activity != null) activity.setupHabitActionBar();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -206,8 +227,8 @@ public class ShowHabitFragment extends Fragment implements ModelObservable.Liste
|
||||
@Override
|
||||
protected void doInBackground()
|
||||
{
|
||||
if(habit == null) return;
|
||||
if(dataViews == null) return;
|
||||
if (habit == null) return;
|
||||
if (dataViews == null) return;
|
||||
|
||||
long today = DateUtils.getStartOfToday();
|
||||
long lastMonth = today - 30 * DateUtils.millisecondsInOneDay;
|
||||
@@ -226,10 +247,14 @@ public class ShowHabitFragment extends Fragment implements ModelObservable.Liste
|
||||
}
|
||||
}
|
||||
|
||||
private class OnItemSelectedListener implements AdapterView.OnItemSelectedListener
|
||||
private class OnItemSelectedListener
|
||||
implements AdapterView.OnItemSelectedListener
|
||||
{
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
|
||||
public void onItemSelected(AdapterView<?> parent,
|
||||
View view,
|
||||
int position,
|
||||
long id)
|
||||
{
|
||||
setScoreBucketSize(position);
|
||||
}
|
||||
|
||||
@@ -41,24 +41,26 @@ public class ShowHabitHelper
|
||||
|
||||
String getFreqText()
|
||||
{
|
||||
if(fragment.habit == null) return "";
|
||||
if (fragment.habit == null) return "";
|
||||
|
||||
Resources resources = fragment.getResources();
|
||||
Integer freqNum = fragment.habit.freqNum;
|
||||
Integer freqDen = fragment.habit.freqDen;
|
||||
|
||||
if (freqNum.equals(freqDen)) return resources.getString(R.string.every_day);
|
||||
if (freqNum.equals(freqDen))
|
||||
return resources.getString(R.string.every_day);
|
||||
|
||||
if (freqNum == 1)
|
||||
{
|
||||
if (freqDen == 7) return resources.getString(R.string.every_week);
|
||||
if (freqDen % 7 == 0) return resources.getString(R.string.every_x_weeks, freqDen / 7);
|
||||
if (freqDen % 7 == 0)
|
||||
return resources.getString(R.string.every_x_weeks, freqDen / 7);
|
||||
return resources.getString(R.string.every_x_days, freqDen);
|
||||
}
|
||||
|
||||
String times_every = resources.getString(R.string.time_every);
|
||||
return String.format("%d %s %d %s", freqNum, times_every, freqDen,
|
||||
resources.getString(R.string.days));
|
||||
resources.getString(R.string.days));
|
||||
}
|
||||
|
||||
void updateScore(View view)
|
||||
@@ -67,48 +69,62 @@ public class ShowHabitHelper
|
||||
if (view == null) return;
|
||||
|
||||
float todayPercentage = fragment.todayScore / Score.MAX_VALUE;
|
||||
float monthDiff = todayPercentage - (fragment.lastMonthScore / Score.MAX_VALUE);
|
||||
float yearDiff = todayPercentage - (fragment.lastYearScore / Score.MAX_VALUE);
|
||||
float monthDiff =
|
||||
todayPercentage - (fragment.lastMonthScore / Score.MAX_VALUE);
|
||||
float yearDiff =
|
||||
todayPercentage - (fragment.lastYearScore / Score.MAX_VALUE);
|
||||
|
||||
RingView scoreRing = (RingView) view.findViewById(R.id.scoreRing);
|
||||
int androidColor = ColorUtils.getColor(fragment.getActivity(), fragment.habit.color);
|
||||
int androidColor =
|
||||
ColorUtils.getColor(fragment.getActivity(), fragment.habit.color);
|
||||
scoreRing.setColor(androidColor);
|
||||
scoreRing.setPercentage(todayPercentage);
|
||||
|
||||
TextView scoreLabel = (TextView) view.findViewById(R.id.scoreLabel);
|
||||
TextView monthDiffLabel = (TextView) view.findViewById(R.id.monthDiffLabel);
|
||||
TextView yearDiffLabel = (TextView) view.findViewById(R.id.yearDiffLabel);
|
||||
TextView monthDiffLabel =
|
||||
(TextView) view.findViewById(R.id.monthDiffLabel);
|
||||
TextView yearDiffLabel =
|
||||
(TextView) view.findViewById(R.id.yearDiffLabel);
|
||||
|
||||
scoreLabel.setText(String.format("%.0f%%", todayPercentage * 100));
|
||||
|
||||
String minus = "\u2212";
|
||||
monthDiffLabel.setText(String.format("%s%.0f%%", (monthDiff >= 0 ? "+" : minus),
|
||||
monthDiffLabel.setText(
|
||||
String.format("%s%.0f%%", (monthDiff >= 0 ? "+" : minus),
|
||||
Math.abs(monthDiff) * 100));
|
||||
yearDiffLabel.setText(
|
||||
String.format("%s%.0f%%", (yearDiff >= 0 ? "+" : minus), Math.abs(yearDiff) * 100));
|
||||
String.format("%s%.0f%%", (yearDiff >= 0 ? "+" : minus),
|
||||
Math.abs(yearDiff) * 100));
|
||||
|
||||
monthDiffLabel.setTextColor(monthDiff >= 0 ? fragment.activeColor : fragment.inactiveColor);
|
||||
yearDiffLabel.setTextColor(yearDiff >= 0 ? fragment.activeColor : fragment.inactiveColor);
|
||||
monthDiffLabel.setTextColor(
|
||||
monthDiff >= 0 ? fragment.activeColor : fragment.inactiveColor);
|
||||
yearDiffLabel.setTextColor(
|
||||
yearDiff >= 0 ? fragment.activeColor : fragment.inactiveColor);
|
||||
}
|
||||
|
||||
void updateMainHeader(View view)
|
||||
{
|
||||
if (fragment.habit == null) return;
|
||||
|
||||
TextView questionLabel = (TextView) view.findViewById(R.id.questionLabel);
|
||||
TextView questionLabel =
|
||||
(TextView) view.findViewById(R.id.questionLabel);
|
||||
questionLabel.setTextColor(fragment.activeColor);
|
||||
questionLabel.setText(fragment.habit.description);
|
||||
|
||||
TextView reminderLabel = (TextView) view.findViewById(R.id.reminderLabel);
|
||||
TextView reminderLabel =
|
||||
(TextView) view.findViewById(R.id.reminderLabel);
|
||||
if (fragment.habit.hasReminder()) reminderLabel.setText(
|
||||
DateUtils.formatTime(fragment.getActivity(), fragment.habit.reminderHour,
|
||||
fragment.habit.reminderMin));
|
||||
else reminderLabel.setText(fragment.getResources().getString(R.string.reminder_off));
|
||||
DateUtils.formatTime(fragment.getActivity(),
|
||||
fragment.habit.reminderHour, fragment.habit.reminderMin));
|
||||
else reminderLabel.setText(
|
||||
fragment.getResources().getString(R.string.reminder_off));
|
||||
|
||||
TextView frequencyLabel = (TextView) view.findViewById(R.id.frequencyLabel);
|
||||
TextView frequencyLabel =
|
||||
(TextView) view.findViewById(R.id.frequencyLabel);
|
||||
frequencyLabel.setText(getFreqText());
|
||||
|
||||
if (fragment.habit.description.isEmpty()) questionLabel.setVisibility(View.GONE);
|
||||
if (fragment.habit.description.isEmpty())
|
||||
questionLabel.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
void updateCardHeaders(View view)
|
||||
@@ -123,17 +139,20 @@ public class ShowHabitHelper
|
||||
|
||||
void updateColor(View view, int viewId)
|
||||
{
|
||||
if(fragment.habit == null || fragment.activity == null) return;
|
||||
if (fragment.habit == null || fragment.activity == null) return;
|
||||
|
||||
TextView textView = (TextView) view.findViewById(viewId);
|
||||
int androidColor = ColorUtils.getColor(fragment.activity, fragment.habit.color);
|
||||
int androidColor =
|
||||
ColorUtils.getColor(fragment.activity, fragment.habit.color);
|
||||
textView.setTextColor(androidColor);
|
||||
}
|
||||
|
||||
void updateColors()
|
||||
{
|
||||
fragment.activeColor = ColorUtils.getColor(fragment.getContext(), fragment.habit.color);
|
||||
fragment.inactiveColor = InterfaceUtils.getStyledColor(fragment.getContext(),
|
||||
fragment.activeColor =
|
||||
ColorUtils.getColor(fragment.getContext(), fragment.habit.color);
|
||||
fragment.inactiveColor =
|
||||
InterfaceUtils.getStyledColor(fragment.getContext(),
|
||||
R.attr.mediumContrastTextColor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,10 @@ import com.github.paolorotolo.appintro.AppIntroFragment;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
|
||||
/**
|
||||
* Activity that introduces the app to the user, shown only after the app is
|
||||
* launched for the first time.
|
||||
*/
|
||||
public class IntroActivity extends AppIntro2
|
||||
{
|
||||
@Override
|
||||
@@ -35,16 +39,16 @@ public class IntroActivity extends AppIntro2
|
||||
showStatusBar(false);
|
||||
|
||||
addSlide(AppIntroFragment.newInstance(getString(R.string.intro_title_1),
|
||||
getString(R.string.intro_description_1), R.drawable.intro_icon_1,
|
||||
Color.parseColor("#194673")));
|
||||
getString(R.string.intro_description_1), R.drawable.intro_icon_1,
|
||||
Color.parseColor("#194673")));
|
||||
|
||||
addSlide(AppIntroFragment.newInstance(getString(R.string.intro_title_2),
|
||||
getString(R.string.intro_description_2), R.drawable.intro_icon_2,
|
||||
Color.parseColor("#ffa726")));
|
||||
getString(R.string.intro_description_2), R.drawable.intro_icon_2,
|
||||
Color.parseColor("#ffa726")));
|
||||
|
||||
addSlide(AppIntroFragment.newInstance(getString(R.string.intro_title_4),
|
||||
getString(R.string.intro_description_4), R.drawable.intro_icon_4,
|
||||
Color.parseColor("#9575cd")));
|
||||
getString(R.string.intro_description_4), R.drawable.intro_icon_4,
|
||||
Color.parseColor("#9575cd")));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Contains classes for the IntroActivity.
|
||||
*/
|
||||
package org.isoron.uhabits.ui.intro;
|
||||
@@ -39,8 +39,11 @@ public class FilePickerDialog implements AdapterView.OnItemClickListener
|
||||
private static final String PARENT_DIR = "..";
|
||||
|
||||
private final Activity activity;
|
||||
|
||||
private ListView list;
|
||||
|
||||
private Dialog dialog;
|
||||
|
||||
private File currentPath;
|
||||
|
||||
public interface OnFileSelectedListener
|
||||
@@ -59,21 +62,24 @@ public class FilePickerDialog implements AdapterView.OnItemClickListener
|
||||
|
||||
dialog = new Dialog(activity);
|
||||
dialog.setContentView(list);
|
||||
dialog.getWindow().setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||
dialog
|
||||
.getWindow()
|
||||
.setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||
|
||||
navigateTo(initialDirectory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int which, long id)
|
||||
public void onItemClick(AdapterView<?> parent,
|
||||
View view,
|
||||
int which,
|
||||
long id)
|
||||
{
|
||||
String filename = (String) list.getItemAtPosition(which);
|
||||
File file;
|
||||
|
||||
if (filename.equals(PARENT_DIR))
|
||||
file = currentPath.getParentFile();
|
||||
else
|
||||
file = new File(currentPath, filename);
|
||||
if (filename.equals(PARENT_DIR)) file = currentPath.getParentFile();
|
||||
else file = new File(currentPath, filename);
|
||||
|
||||
if (file.isDirectory())
|
||||
{
|
||||
@@ -102,7 +108,7 @@ public class FilePickerDialog implements AdapterView.OnItemClickListener
|
||||
|
||||
File[] dirs = path.listFiles(new ReadableDirFilter());
|
||||
File[] files = path.listFiles(new RegularReadableFileFilter());
|
||||
if(dirs == null || files == null) return;
|
||||
if (dirs == null || files == null) return;
|
||||
|
||||
this.currentPath = path;
|
||||
dialog.setTitle(currentPath.getPath());
|
||||
@@ -142,7 +148,8 @@ public class FilePickerDialog implements AdapterView.OnItemClickListener
|
||||
{
|
||||
public FilePickerAdapter(@NonNull String[] fileList)
|
||||
{
|
||||
super(FilePickerDialog.this.activity, android.R.layout.simple_list_item_1, fileList);
|
||||
super(FilePickerDialog.this.activity,
|
||||
android.R.layout.simple_list_item_1, fileList);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -22,9 +22,12 @@ package org.isoron.uhabits.ui.settings;
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.utils.InterfaceUtils;
|
||||
import org.isoron.uhabits.ui.BaseActivity;
|
||||
import org.isoron.uhabits.utils.InterfaceUtils;
|
||||
|
||||
/**
|
||||
* Activity that allows the user to view and modify the app settings.
|
||||
*/
|
||||
public class SettingsActivity extends BaseActivity
|
||||
{
|
||||
@Override
|
||||
@@ -32,9 +35,10 @@ public class SettingsActivity extends BaseActivity
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.settings_activity);
|
||||
setupSupportActionBar(true);
|
||||
// setupSupportActionBar(true);
|
||||
|
||||
int color = InterfaceUtils.getStyledColor(this, R.attr.aboutScreenColor);
|
||||
setupActionBarColor(color);
|
||||
int color =
|
||||
InterfaceUtils.getStyledColor(this, R.attr.aboutScreenColor);
|
||||
// setupActionBarColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,11 +29,11 @@ import android.support.v7.preference.PreferenceFragmentCompat;
|
||||
|
||||
import org.isoron.uhabits.HabitsApplication;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.utils.ReminderUtils;
|
||||
import org.isoron.uhabits.utils.InterfaceUtils;
|
||||
import org.isoron.uhabits.utils.ReminderUtils;
|
||||
|
||||
public class SettingsFragment extends PreferenceFragmentCompat
|
||||
implements SharedPreferences.OnSharedPreferenceChangeListener
|
||||
implements SharedPreferences.OnSharedPreferenceChangeListener
|
||||
{
|
||||
private static int RINGTONE_REQUEST_CODE = 1;
|
||||
|
||||
@@ -43,14 +43,18 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
super.onCreate(savedInstanceState);
|
||||
addPreferencesFromResource(R.xml.preferences);
|
||||
|
||||
setResultOnPreferenceClick("importData", HabitsApplication.RESULT_IMPORT_DATA);
|
||||
setResultOnPreferenceClick("exportCSV", HabitsApplication.RESULT_EXPORT_CSV);
|
||||
setResultOnPreferenceClick("exportDB", HabitsApplication.RESULT_EXPORT_DB);
|
||||
setResultOnPreferenceClick("bugReport", HabitsApplication.RESULT_BUG_REPORT);
|
||||
setResultOnPreferenceClick("importData",
|
||||
HabitsApplication.RESULT_IMPORT_DATA);
|
||||
setResultOnPreferenceClick("exportCSV",
|
||||
HabitsApplication.RESULT_EXPORT_CSV);
|
||||
setResultOnPreferenceClick("exportDB",
|
||||
HabitsApplication.RESULT_EXPORT_DB);
|
||||
setResultOnPreferenceClick("bugReport",
|
||||
HabitsApplication.RESULT_BUG_REPORT);
|
||||
|
||||
updateRingtoneDescription();
|
||||
|
||||
if(InterfaceUtils.isLocaleFullyTranslated())
|
||||
if (InterfaceUtils.isLocaleFullyTranslated())
|
||||
removePreference("translate", "linksCategory");
|
||||
}
|
||||
|
||||
@@ -62,7 +66,8 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
|
||||
private void removePreference(String preferenceKey, String categoryKey)
|
||||
{
|
||||
PreferenceCategory cat = (PreferenceCategory) findPreference(categoryKey);
|
||||
PreferenceCategory cat =
|
||||
(PreferenceCategory) findPreference(categoryKey);
|
||||
Preference pref = findPreference(preferenceKey);
|
||||
cat.removePreference(pref);
|
||||
}
|
||||
@@ -70,16 +75,17 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
private void setResultOnPreferenceClick(String key, final int result)
|
||||
{
|
||||
Preference pref = findPreference(key);
|
||||
pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener()
|
||||
{
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference)
|
||||
pref.setOnPreferenceClickListener(
|
||||
new Preference.OnPreferenceClickListener()
|
||||
{
|
||||
getActivity().setResult(result);
|
||||
getActivity().finish();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference)
|
||||
{
|
||||
getActivity().setResult(result);
|
||||
getActivity().finish();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -87,19 +93,20 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
{
|
||||
super.onResume();
|
||||
getPreferenceManager().getSharedPreferences().
|
||||
registerOnSharedPreferenceChangeListener(this);
|
||||
registerOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause()
|
||||
{
|
||||
getPreferenceManager().getSharedPreferences().
|
||||
unregisterOnSharedPreferenceChangeListener(this);
|
||||
unregisterOnSharedPreferenceChangeListener(this);
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key)
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
|
||||
String key)
|
||||
{
|
||||
BackupManager.dataChanged("org.isoron.uhabits");
|
||||
}
|
||||
@@ -107,11 +114,12 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
@Override
|
||||
public boolean onPreferenceTreeClick(Preference preference)
|
||||
{
|
||||
if(preference.getKey() == null) return false;
|
||||
if (preference.getKey() == null) return false;
|
||||
|
||||
if (preference.getKey().equals("reminderSound"))
|
||||
{
|
||||
ReminderUtils.startRingtonePickerActivity(this, RINGTONE_REQUEST_CODE);
|
||||
ReminderUtils.startRingtonePickerActivity(this,
|
||||
RINGTONE_REQUEST_CODE);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -121,7 +129,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
if(requestCode == RINGTONE_REQUEST_CODE)
|
||||
if (requestCode == RINGTONE_REQUEST_CODE)
|
||||
{
|
||||
ReminderUtils.parseRingtoneData(getContext(), data);
|
||||
updateRingtoneDescription();
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Contains classes for the SettingsActivity.
|
||||
*/
|
||||
package org.isoron.uhabits.ui.settings;
|
||||
@@ -29,28 +29,40 @@ import org.isoron.uhabits.R;
|
||||
|
||||
public class Preferences
|
||||
{
|
||||
private static Preferences singleton;
|
||||
|
||||
private Context context;
|
||||
|
||||
private SharedPreferences prefs;
|
||||
|
||||
private Preferences()
|
||||
public Preferences()
|
||||
{
|
||||
this.context = HabitsApplication.getContext();
|
||||
prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
}
|
||||
|
||||
public static Preferences getInstance()
|
||||
public Integer getDefaultHabitColor(int fallbackColor)
|
||||
{
|
||||
if(singleton == null) singleton = new Preferences();
|
||||
return singleton;
|
||||
return prefs.getInt("pref_default_habit_palette_color", fallbackColor);
|
||||
}
|
||||
|
||||
public void initialize()
|
||||
/**
|
||||
* Returns the number of the last hint shown to the user.
|
||||
*
|
||||
* @return number of last hint shown
|
||||
*/
|
||||
public int getLastHintNumber()
|
||||
{
|
||||
PreferenceManager.setDefaultValues(context, R.xml.preferences, false);
|
||||
return prefs.getInt("last_hint_number", -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time when the last hint was shown to the user.
|
||||
*
|
||||
* @return timestamp of the day the last hint was shown
|
||||
*/
|
||||
public long getLastHintTimestamp()
|
||||
{
|
||||
return prefs.getLong("last_hint_timestamp", -1);
|
||||
}
|
||||
|
||||
public void incrementLaunchCount()
|
||||
{
|
||||
@@ -58,9 +70,9 @@ public class Preferences
|
||||
prefs.edit().putInt("launch_count", count + 1).apply();
|
||||
}
|
||||
|
||||
public void updateLastAppVersion()
|
||||
public void initialize()
|
||||
{
|
||||
prefs.edit().putInt("last_version", BuildConfig.VERSION_CODE).apply();
|
||||
PreferenceManager.setDefaultValues(context, R.xml.preferences, false);
|
||||
}
|
||||
|
||||
public boolean isFirstRun()
|
||||
@@ -73,19 +85,14 @@ public class Preferences
|
||||
prefs.edit().putBoolean("pref_first_run", isFirstRun).apply();
|
||||
}
|
||||
|
||||
public void setLastHintTimestamp(long timestamp)
|
||||
{
|
||||
prefs.edit().putLong("last_hint_timestamp", timestamp).apply();
|
||||
}
|
||||
|
||||
public boolean isShortToggleEnabled()
|
||||
{
|
||||
return prefs.getBoolean("pref_short_toggle", false);
|
||||
}
|
||||
|
||||
public Integer getDefaultHabitColor(int defaultColor)
|
||||
public void setShortToggleEnabled(boolean enabled)
|
||||
{
|
||||
return prefs.getInt("pref_default_habit_palette_color", defaultColor);
|
||||
prefs.edit().putBoolean("pref_short_toggle", enabled).apply();
|
||||
}
|
||||
|
||||
public void setDefaultHabitColor(int color)
|
||||
@@ -93,4 +100,38 @@ public class Preferences
|
||||
prefs.edit().putInt("pref_default_habit_palette_color", color).apply();
|
||||
}
|
||||
|
||||
public void setShouldReverseCheckmarks(boolean shouldReverse)
|
||||
{
|
||||
prefs
|
||||
.edit()
|
||||
.putBoolean("pref_checkmark_reverse_order", shouldReverse)
|
||||
.apply();
|
||||
}
|
||||
|
||||
public boolean shouldReverseCheckmarks()
|
||||
{
|
||||
return prefs.getBoolean("pref_checkmark_reverse_order", false);
|
||||
}
|
||||
|
||||
public void updateLastAppVersion()
|
||||
{
|
||||
prefs.edit().putInt("last_version", BuildConfig.VERSION_CODE).apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the last hint shown to the user, and the time that it was shown.
|
||||
*
|
||||
* @param number number of the last hint shown
|
||||
* @param timestamp timestamp for the day the last hint was shown
|
||||
*/
|
||||
public void updateLastHint(int number, long timestamp)
|
||||
{
|
||||
prefs
|
||||
.edit()
|
||||
.putInt("last_hint_number", number)
|
||||
.putLong("last_hint_timestamp", timestamp)
|
||||
.apply();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user