@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
find uhabits-android/build/outputs/failed/test-screenshots -name '*.expected*' -delete
|
||||
rsync -av uhabits-android/build/outputs/failed/test-screenshots/ uhabits-android/src/androidTest/assets/
|
@ -1,3 +1,3 @@
|
||||
org.gradle.parallel=true
|
||||
org.gradle.parallel=false
|
||||
org.gradle.daemon=true
|
||||
org.gradle.jvmargs=-Xms1024m -Xmx4096m -XX:MaxPermSize=2048m
|
||||
org.gradle.jvmargs=-Xms2048m -Xmx2048m -XX:MaxPermSize=2048m
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 7.2 KiB |
After Width: | Height: | Size: 7.2 KiB |
After Width: | Height: | Size: 7.6 KiB |
After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 577 B After Width: | Height: | Size: 585 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 7.4 KiB |
After Width: | Height: | Size: 7.3 KiB |
After Width: | Height: | Size: 7.9 KiB |
After Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 21 KiB |
@ -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 dagger.*
|
||||
import org.isoron.androidbase.activities.*
|
||||
import org.isoron.uhabits.activities.*
|
||||
import org.isoron.uhabits.activities.about.*
|
||||
import org.isoron.uhabits.activities.habits.list.*
|
||||
import org.isoron.uhabits.activities.habits.list.views.*
|
||||
import org.isoron.uhabits.activities.habits.show.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*
|
||||
import org.mockito.Mockito.*
|
||||
|
||||
@Module
|
||||
class TestModule {
|
||||
@Provides fun ListHabitsBehavior() = mock(ListHabitsBehavior::class.java)
|
||||
}
|
||||
|
||||
@ActivityScope
|
||||
@Component(modules = arrayOf(
|
||||
ActivityContextModule::class,
|
||||
AboutModule::class,
|
||||
HabitsActivityModule::class,
|
||||
ListHabitsModule::class,
|
||||
ShowHabitModule::class,
|
||||
HabitModule::class,
|
||||
TestModule::class
|
||||
), dependencies = arrayOf(HabitsApplicationComponent::class))
|
||||
interface HabitsActivityTestComponent {
|
||||
fun getCheckmarkPanelViewFactory(): CheckmarkPanelViewFactory
|
||||
fun getHabitCardViewFactory(): HabitCardViewFactory
|
||||
fun getCheckmarkButtonViewFactory(): CheckmarkButtonViewFactory
|
||||
fun getNumberButtonViewFactory(): NumberButtonViewFactory
|
||||
fun getNumberPanelViewFactory(): NumberPanelViewFactory
|
||||
}
|
@ -1,87 +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.activities.habits.list.views;
|
||||
|
||||
import android.support.test.runner.*;
|
||||
import android.test.suitebuilder.annotation.*;
|
||||
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.core.models.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@MediumTest
|
||||
public class CheckmarkButtonViewTest extends BaseViewTest
|
||||
{
|
||||
public static final String PATH = "habits/list/CheckmarkButtonView/";
|
||||
|
||||
private CheckmarkButtonView view;
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp()
|
||||
{
|
||||
super.setUp();
|
||||
view = new CheckmarkButtonView(targetContext);
|
||||
view.setValue(Checkmark.UNCHECKED);
|
||||
view.setColor(PaletteUtils.getAndroidTestColor(5));
|
||||
measureView(view, dpToPixels(48), dpToPixels(48));
|
||||
}
|
||||
|
||||
@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 testRender_unchecked() throws Exception
|
||||
{
|
||||
view.setValue(Checkmark.UNCHECKED);
|
||||
assertRendersUnchecked();
|
||||
}
|
||||
|
||||
protected void assertRendersCheckedExplicitly() throws IOException
|
||||
{
|
||||
assertRenders(view, PATH + "render_explicit_check.png");
|
||||
}
|
||||
|
||||
protected void assertRendersCheckedImplicitly() throws IOException
|
||||
{
|
||||
assertRenders(view, PATH + "render_implicit_check.png");
|
||||
}
|
||||
|
||||
protected void assertRendersUnchecked() throws IOException
|
||||
{
|
||||
assertRenders(view, PATH + "render_unchecked.png");
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.activities.habits.list.views
|
||||
|
||||
import android.support.test.filters.*
|
||||
import android.support.test.runner.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import org.junit.*
|
||||
import org.junit.runner.*
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@MediumTest
|
||||
class CheckmarkButtonViewTest : BaseViewTest() {
|
||||
|
||||
private val PATH = "habits/list/CheckmarkButtonView"
|
||||
lateinit var view: CheckmarkButtonView
|
||||
|
||||
var toggled = false
|
||||
|
||||
@Before
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
view = component.getCheckmarkButtonViewFactory().create().apply {
|
||||
value = Checkmark.UNCHECKED
|
||||
color = PaletteUtils.getAndroidTestColor(5)
|
||||
onToggle = { toggled = true }
|
||||
}
|
||||
measureView(view, dpToPixels(48), dpToPixels(48))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRender_explicitCheck() {
|
||||
view.value = Checkmark.CHECKED_EXPLICITLY
|
||||
assertRendersCheckedExplicitly()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRender_implicitCheck() {
|
||||
view.value = Checkmark.CHECKED_IMPLICITLY
|
||||
assertRendersCheckedImplicitly()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRender_unchecked() {
|
||||
view.value = Checkmark.UNCHECKED
|
||||
assertRendersUnchecked()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testClick_withShortToggleDisabled() {
|
||||
prefs.isShortToggleEnabled = false
|
||||
view.performClick()
|
||||
assertFalse(toggled)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testClick_withShortToggleEnabled() {
|
||||
prefs.isShortToggleEnabled = true
|
||||
view.performClick()
|
||||
assertTrue(toggled)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLongClick() {
|
||||
view.performLongClick()
|
||||
assertTrue(toggled)
|
||||
}
|
||||
|
||||
private fun assertRendersCheckedExplicitly() {
|
||||
assertRenders(view, "$PATH/render_explicit_check.png")
|
||||
}
|
||||
|
||||
private fun assertRendersCheckedImplicitly() {
|
||||
assertRenders(view, "$PATH/render_implicit_check.png")
|
||||
}
|
||||
|
||||
private fun assertRendersUnchecked() {
|
||||
assertRenders(view, "$PATH/render_unchecked.png")
|
||||
}
|
||||
}
|
@ -1,96 +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.activities.habits.list.views;
|
||||
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.*;
|
||||
|
||||
import org.isoron.uhabits.core.models.Checkmark;
|
||||
import org.isoron.uhabits.core.models.Habit;
|
||||
import org.isoron.uhabits.BaseViewTest;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@MediumTest
|
||||
public class CheckmarkPanelViewTest extends BaseViewTest
|
||||
{
|
||||
public static final String PATH = "habits/list/CheckmarkPanelView/";
|
||||
|
||||
private CountDownLatch latch;
|
||||
private CheckmarkPanelView view;
|
||||
private int checkmarks[];
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp()
|
||||
{
|
||||
super.setUp();
|
||||
prefs.setShouldReverseCheckmarks(false);
|
||||
|
||||
Habit habit = fixtures.createEmptyHabit();
|
||||
|
||||
latch = new CountDownLatch(1);
|
||||
checkmarks = new int[]{
|
||||
Checkmark.CHECKED_EXPLICITLY, Checkmark.UNCHECKED,
|
||||
Checkmark.CHECKED_IMPLICITLY, Checkmark.CHECKED_EXPLICITLY};
|
||||
|
||||
view = new CheckmarkPanelView(targetContext);
|
||||
view.setValues(checkmarks);
|
||||
view.setButtonCount(4);
|
||||
view.setColor(PaletteUtils.getAndroidTestColor(7));
|
||||
|
||||
measureView(view, dpToPixels(200), dpToPixels(200));
|
||||
}
|
||||
|
||||
// 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();
|
||||
// }
|
||||
}
|
@ -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.activities.habits.list.views
|
||||
|
||||
import android.support.test.filters.*
|
||||
import android.support.test.runner.*
|
||||
import org.hamcrest.CoreMatchers.*
|
||||
import org.hamcrest.MatcherAssert.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.models.Checkmark.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import org.junit.*
|
||||
import org.junit.runner.*
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@MediumTest
|
||||
class CheckmarkPanelViewTest : BaseViewTest() {
|
||||
|
||||
private val PATH = "habits/list/CheckmarkPanelView"
|
||||
private lateinit var view: CheckmarkPanelView
|
||||
|
||||
@Before
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
prefs.isCheckmarkSequenceReversed = false
|
||||
|
||||
val checkmarks = intArrayOf(CHECKED_EXPLICITLY,
|
||||
CHECKED_EXPLICITLY,
|
||||
CHECKED_IMPLICITLY,
|
||||
UNCHECKED,
|
||||
UNCHECKED,
|
||||
UNCHECKED,
|
||||
CHECKED_EXPLICITLY)
|
||||
|
||||
view = component.getCheckmarkPanelViewFactory().create().apply {
|
||||
values = checkmarks
|
||||
buttonCount = 4
|
||||
color = PaletteUtils.getAndroidTestColor(7)
|
||||
}
|
||||
view.onAttachedToWindow()
|
||||
measureView(view, dpToPixels(200), dpToPixels(200))
|
||||
}
|
||||
|
||||
@After
|
||||
public override fun tearDown() {
|
||||
// view.onDetachedFromWindow()
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRender() {
|
||||
assertRenders(view, "$PATH/render.png")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRender_withDifferentColor() {
|
||||
view.color = PaletteUtils.getAndroidTestColor(1)
|
||||
assertRenders(view, "$PATH/render_different_color.png")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRender_Reversed() {
|
||||
prefs.isCheckmarkSequenceReversed = true
|
||||
assertRenders(view, "$PATH/render_reversed.png")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRender_withOffset() {
|
||||
view.dataOffset = 3
|
||||
assertRenders(view, "$PATH/render_offset.png")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testToggle() {
|
||||
var timestamps = mutableListOf<Long>()
|
||||
view.onToggle = { timestamps.add(it) }
|
||||
view.buttons[0].performLongClick()
|
||||
view.buttons[2].performLongClick()
|
||||
view.buttons[3].performLongClick()
|
||||
assertThat(timestamps, equalTo(listOf(day(0), day(2), day(3))))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testToggle_withOffset() {
|
||||
var timestamps = LongArray(0)
|
||||
view.dataOffset = 3
|
||||
view.onToggle = { timestamps += it }
|
||||
view.buttons[0].performLongClick()
|
||||
view.buttons[2].performLongClick()
|
||||
view.buttons[3].performLongClick()
|
||||
assertThat(timestamps, equalTo(longArrayOf(day(3), day(5), day(6))))
|
||||
}
|
||||
}
|
@ -1,86 +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.activities.habits.list.views;
|
||||
|
||||
import android.support.test.runner.*;
|
||||
import android.test.suitebuilder.annotation.*;
|
||||
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.core.models.*;
|
||||
import org.isoron.uhabits.core.utils.*;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.*;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@MediumTest
|
||||
public class HabitCardViewTest extends BaseViewTest
|
||||
{
|
||||
private HabitCardView view;
|
||||
|
||||
public static final String PATH = "habits/list/HabitCardView/";
|
||||
|
||||
private Habit habit;
|
||||
|
||||
@Override
|
||||
public void setUp()
|
||||
{
|
||||
super.setUp();
|
||||
setTheme(R.style.AppBaseTheme);
|
||||
|
||||
habit = fixtures.createLongHabit();
|
||||
CheckmarkList checkmarks = habit.getCheckmarks();
|
||||
|
||||
long today = DateUtils.getStartOfToday();
|
||||
long day = DateUtils.millisecondsInOneDay;
|
||||
int[] values = checkmarks.getValues(today - 5 * day, today);
|
||||
|
||||
view = new HabitCardView(targetContext);
|
||||
view.setHabit(habit);
|
||||
view.setValues(values);
|
||||
view.setSelected(false);
|
||||
view.setScore(habit.getScores().getTodayValue());
|
||||
view.setButtonCount(6);
|
||||
measureView(view, dpToPixels(400), dpToPixels(50));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender() throws Exception
|
||||
{
|
||||
assertRenders(view, PATH + "render.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_selected() throws Exception
|
||||
{
|
||||
view.setSelected(true);
|
||||
measureView(view, dpToPixels(400), dpToPixels(50));
|
||||
assertRenders(view, PATH + "render_selected.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangeModel() throws Exception
|
||||
{
|
||||
habit.setName("Wake up early");
|
||||
habit.setColor(2);
|
||||
habit.getObservable().notifyListeners();
|
||||
Thread.sleep(500);
|
||||
assertRenders(view, PATH + "render_changed.png");
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.activities.habits.list.views
|
||||
|
||||
import android.support.test.filters.*
|
||||
import android.support.test.runner.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.junit.*
|
||||
import org.junit.runner.*
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@MediumTest
|
||||
class HabitCardViewTest : BaseViewTest() {
|
||||
|
||||
val PATH = "habits/list/HabitCardView"
|
||||
lateinit private var view: HabitCardView
|
||||
lateinit private var habit1: Habit
|
||||
lateinit private var habit2: Habit
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
setTheme(R.style.AppBaseTheme)
|
||||
|
||||
habit1 = fixtures.createLongHabit()
|
||||
habit2 = fixtures.createLongNumericalHabit()
|
||||
view = component.getHabitCardViewFactory().create().apply {
|
||||
habit = habit1
|
||||
values = habit1.checkmarks.allValues
|
||||
score = habit1.scores.todayValue
|
||||
isSelected = false
|
||||
buttonCount = 5
|
||||
}
|
||||
latch.countDown()
|
||||
|
||||
latch.await()
|
||||
measureView(view, dpToPixels(400), dpToPixels(50))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRender() {
|
||||
assertRenders(view, "$PATH/render.png")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRender_selected() {
|
||||
view.isSelected = true
|
||||
measureView(view, dpToPixels(400), dpToPixels(50))
|
||||
assertRenders(view, "$PATH/render_selected.png")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRender_numerical() {
|
||||
view.apply {
|
||||
habit = habit2
|
||||
values = habit2.checkmarks.allValues
|
||||
}
|
||||
assertRenders(view, "$PATH/render_numerical.png")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testChangeModel() {
|
||||
habit1.name = "Wake up early"
|
||||
habit1.color = 2
|
||||
habit1.observable.notifyListeners()
|
||||
Thread.sleep(500)
|
||||
assertRenders(view, "$PATH/render_changed.png")
|
||||
}
|
||||
}
|
@ -1,95 +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.activities.habits.list.views;
|
||||
|
||||
import android.support.test.filters.*;
|
||||
import android.support.test.runner.*;
|
||||
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.*;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.*;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@MediumTest
|
||||
public class NumberButtonViewTest extends BaseViewTest
|
||||
{
|
||||
public static final String PATH = "habits/list/NumberButtonView/";
|
||||
|
||||
private NumberButtonView view;
|
||||
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp()
|
||||
{
|
||||
super.setUp();
|
||||
|
||||
view = new NumberButtonView(targetContext);
|
||||
view.setUnit("steps");
|
||||
view.setThreshold(100.0);
|
||||
view.setColor(PaletteUtils.getAndroidTestColor(8));
|
||||
|
||||
measureView(view, dpToPixels(48), dpToPixels(48));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormatValue()
|
||||
{
|
||||
assertThat(NumberButtonView.formatValue(0.1235), equalTo("0.12"));
|
||||
assertThat(NumberButtonView.formatValue(0.1000), equalTo("0.1"));
|
||||
assertThat(NumberButtonView.formatValue(5.0), equalTo("5"));
|
||||
assertThat(NumberButtonView.formatValue(5.25), equalTo("5.25"));
|
||||
assertThat(NumberButtonView.formatValue(12.3456), equalTo("12.3"));
|
||||
assertThat(NumberButtonView.formatValue(123.123), equalTo("123"));
|
||||
assertThat(NumberButtonView.formatValue(321.2), equalTo("321"));
|
||||
assertThat(NumberButtonView.formatValue(4321.2), equalTo("4.3k"));
|
||||
assertThat(NumberButtonView.formatValue(54321.2), equalTo("54.3k"));
|
||||
assertThat(NumberButtonView.formatValue(654321.2), equalTo("654k"));
|
||||
assertThat(NumberButtonView.formatValue(7654321.2), equalTo("7.7M"));
|
||||
assertThat(NumberButtonView.formatValue(87654321.2), equalTo("87.7M"));
|
||||
assertThat(NumberButtonView.formatValue(987654321.2), equalTo("988M"));
|
||||
assertThat(NumberButtonView.formatValue(1987654321.2), equalTo("2.0G"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_aboveThreshold() throws Exception
|
||||
{
|
||||
view.setValue(500);
|
||||
assertRenders(view, PATH + "render_above.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_belowThreshold() throws Exception
|
||||
{
|
||||
view.setValue(99);
|
||||
assertRenders(view, PATH + "render_below.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_zero() throws Exception
|
||||
{
|
||||
view.setValue(0);
|
||||
assertRenders(view, PATH + "render_zero.png");
|
||||
}
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.activities.habits.list.views
|
||||
|
||||
import android.support.test.filters.*
|
||||
import android.support.test.runner.*
|
||||
import org.hamcrest.CoreMatchers.*
|
||||
import org.hamcrest.MatcherAssert.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import org.junit.*
|
||||
import org.junit.runner.*
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@MediumTest
|
||||
class NumberButtonViewTest : BaseViewTest() {
|
||||
|
||||
private val PATH = "habits/list/NumberButtonView"
|
||||
private lateinit var view: NumberButtonView
|
||||
var edited = false
|
||||
|
||||
@Before
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
view = component.getNumberButtonViewFactory().create().apply {
|
||||
units = "steps"
|
||||
threshold = 100.0
|
||||
color = PaletteUtils.getAndroidTestColor(8)
|
||||
onEdit = { edited = true }
|
||||
}
|
||||
measureView(view, dpToPixels(48), dpToPixels(48))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFormatValue() {
|
||||
assertThat(0.1235.toShortString(), equalTo("0.12"))
|
||||
assertThat(0.1000.toShortString(), equalTo("0.1"))
|
||||
assertThat(5.0.toShortString(), equalTo("5"))
|
||||
assertThat(5.25.toShortString(), equalTo("5.25"))
|
||||
assertThat(12.3456.toShortString(), equalTo("12.3"))
|
||||
assertThat(123.123.toShortString(), equalTo("123"))
|
||||
assertThat(321.2.toShortString(), equalTo("321"))
|
||||
assertThat(4321.2.toShortString(), equalTo("4.3k"))
|
||||
assertThat(54321.2.toShortString(), equalTo("54.3k"))
|
||||
assertThat(654321.2.toShortString(), equalTo("654k"))
|
||||
assertThat(7654321.2.toShortString(), equalTo("7.7M"))
|
||||
assertThat(87654321.2.toShortString(), equalTo("87.7M"))
|
||||
assertThat(987654321.2.toShortString(), equalTo("988M"))
|
||||
assertThat(1987654321.2.toShortString(), equalTo("2.0G"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRender_aboveThreshold() {
|
||||
view.value = 500.0
|
||||
assertRenders(view, "$PATH/render_above.png")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRender_belowThreshold() {
|
||||
view.value = 99.0
|
||||
assertRenders(view, "$PATH/render_below.png")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRender_zero() {
|
||||
view.value = 0.0
|
||||
assertRenders(view, "$PATH/render_zero.png")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testClick_shortToggleDisabled() {
|
||||
prefs.isShortToggleEnabled = false
|
||||
view.performClick()
|
||||
assertFalse(edited)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testClick_shortToggleEnabled() {
|
||||
prefs.isShortToggleEnabled = true
|
||||
view.performClick()
|
||||
assertTrue(edited)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLongClick() {
|
||||
view.performLongClick()
|
||||
assertTrue(edited)
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.activities.habits.list.views
|
||||
|
||||
import android.support.test.filters.*
|
||||
import android.support.test.runner.*
|
||||
import org.hamcrest.CoreMatchers.*
|
||||
import org.hamcrest.MatcherAssert.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import org.junit.*
|
||||
import org.junit.runner.*
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@MediumTest
|
||||
class NumberPanelViewTest : BaseViewTest() {
|
||||
|
||||
private val PATH = "habits/list/NumberPanelView"
|
||||
private lateinit var view: NumberPanelView
|
||||
|
||||
@Before
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
prefs.isCheckmarkSequenceReversed = false
|
||||
|
||||
val checkmarks = doubleArrayOf(1400.0, 5300.0, 0.0,
|
||||
14600.0, 2500.0, 45000.0)
|
||||
|
||||
view = component.getNumberPanelViewFactory().create().apply {
|
||||
values = checkmarks
|
||||
buttonCount = 4
|
||||
color = PaletteUtils.getAndroidTestColor(7)
|
||||
units = "steps"
|
||||
threshold = 5000.0
|
||||
}
|
||||
view.onAttachedToWindow()
|
||||
measureView(view, dpToPixels(200), dpToPixels(200))
|
||||
}
|
||||
|
||||
@After
|
||||
public override fun tearDown() {
|
||||
view.onDetachedFromWindow()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRender() {
|
||||
assertRenders(view, "$PATH/render.png")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRender_withDifferentColor() {
|
||||
view.color = PaletteUtils.getAndroidTestColor(1)
|
||||
assertRenders(view, "$PATH/render_different_color.png")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRender_Reversed() {
|
||||
prefs.isCheckmarkSequenceReversed = true
|
||||
assertRenders(view, "$PATH/render_reversed.png")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRender_withOffset() {
|
||||
view.dataOffset = 3
|
||||
assertRenders(view, "$PATH/render_offset.png")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEdit() {
|
||||
var timestamps = LongArray(0)
|
||||
view.onEdit = { timestamps += it }
|
||||
view.buttons[0].performLongClick()
|
||||
view.buttons[2].performLongClick()
|
||||
view.buttons[3].performLongClick()
|
||||
assertThat(timestamps, equalTo(longArrayOf(day(0), day(2), day(3))))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEdit_withOffset() {
|
||||
var timestamps = LongArray(0)
|
||||
view.dataOffset = 3
|
||||
view.onEdit = { timestamps += it }
|
||||
view.buttons[0].performLongClick()
|
||||
view.buttons[2].performLongClick()
|
||||
view.buttons[3].performLongClick()
|
||||
assertThat(timestamps, equalTo(longArrayOf(day(3), day(5), day(6))))
|
||||
}
|
||||
}
|
@ -1,63 +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.activities;
|
||||
|
||||
import android.support.annotation.*;
|
||||
|
||||
import org.isoron.androidbase.activities.*;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.core.preferences.*;
|
||||
import org.isoron.uhabits.core.ui.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
@ActivityScope
|
||||
public class AndroidThemeSwitcher extends ThemeSwitcher
|
||||
{
|
||||
@NonNull
|
||||
private final BaseActivity activity;
|
||||
|
||||
@Inject
|
||||
public AndroidThemeSwitcher(@NonNull BaseActivity activity,
|
||||
@NonNull Preferences preferences)
|
||||
{
|
||||
super(preferences);
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyDarkTheme()
|
||||
{
|
||||
activity.setTheme(R.style.AppBaseThemeDark);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyLightTheme()
|
||||
{
|
||||
activity.setTheme(R.style.AppBaseTheme);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyPureBlackTheme()
|
||||
{
|
||||
activity.setTheme(R.style.AppBaseThemeDark_PureBlack);
|
||||
|
||||
}
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Á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.activities;
|
||||
|
||||
import android.content.*;
|
||||
import android.net.*;
|
||||
import android.os.*;
|
||||
import android.support.annotation.*;
|
||||
|
||||
import org.isoron.androidbase.activities.*;
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.core.models.*;
|
||||
|
||||
public abstract class HabitsActivity extends BaseActivity
|
||||
{
|
||||
private HabitsActivityComponent component;
|
||||
|
||||
private HabitsApplicationComponent appComponent;
|
||||
|
||||
public HabitsActivityComponent getActivityComponent()
|
||||
{
|
||||
return component;
|
||||
}
|
||||
|
||||
public HabitsApplicationComponent getAppComponent()
|
||||
{
|
||||
return appComponent;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
appComponent =
|
||||
((HabitsApplication) getApplicationContext()).getComponent();
|
||||
|
||||
Habit habit = getHabitFromIntent(appComponent.getHabitList());
|
||||
|
||||
component = DaggerHabitsActivityComponent
|
||||
.builder()
|
||||
.activityModule(new ActivityModule(this))
|
||||
.habitModule(new HabitModule(habit))
|
||||
.habitsApplicationComponent(appComponent)
|
||||
.build();
|
||||
|
||||
component.getThemeSwitcher().apply();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Habit getHabitFromIntent(@NonNull HabitList habitList)
|
||||
{
|
||||
Uri data = getIntent().getData();
|
||||
if(data == null) return null;
|
||||
|
||||
Habit habit = habitList.getById(ContentUris.parseId(data));
|
||||
if (habit == null) throw new RuntimeException("habit not found");
|
||||
|
||||
return habit;
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Á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.activities
|
||||
|
||||
import android.content.*
|
||||
import android.os.*
|
||||
import org.isoron.androidbase.activities.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
|
||||
abstract class HabitsActivity : BaseActivity() {
|
||||
lateinit var component: HabitsActivityComponent
|
||||
lateinit var appComponent: HabitsApplicationComponent
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
appComponent = (applicationContext as HabitsApplication).component
|
||||
|
||||
val habit = getHabitFromIntent(appComponent.habitList)
|
||||
?: appComponent.modelFactory.buildHabit()
|
||||
|
||||
component = DaggerHabitsActivityComponent
|
||||
.builder()
|
||||
.activityContextModule(ActivityContextModule(this))
|
||||
.baseActivityModule(BaseActivityModule(this))
|
||||
.habitModule(HabitModule(habit))
|
||||
.habitsApplicationComponent(appComponent)
|
||||
.build()
|
||||
|
||||
component.themeSwitcher.apply()
|
||||
}
|
||||
|
||||
private fun getHabitFromIntent(habitList: HabitList): Habit? {
|
||||
val data = intent.data ?: return null
|
||||
val habit = habitList.getById(ContentUris.parseId(data))
|
||||
?: throw RuntimeException("habit not found")
|
||||
return habit
|
||||
}
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Á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.activities;
|
||||
|
||||
import org.isoron.androidbase.activities.*;
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.activities.about.*;
|
||||
import org.isoron.uhabits.activities.common.dialogs.*;
|
||||
import org.isoron.uhabits.activities.habits.list.*;
|
||||
import org.isoron.uhabits.activities.habits.list.views.*;
|
||||
import org.isoron.uhabits.activities.habits.show.*;
|
||||
import org.isoron.uhabits.core.ui.*;
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*;
|
||||
|
||||
import dagger.*;
|
||||
|
||||
@ActivityScope
|
||||
@Component(modules = {
|
||||
ActivityModule.class,
|
||||
AboutModule.class,
|
||||
HabitsActivityModule.class,
|
||||
ListHabitsModule.class,
|
||||
ShowHabitModule.class,
|
||||
HabitModule.class
|
||||
}, dependencies = { HabitsApplicationComponent.class })
|
||||
public interface HabitsActivityComponent
|
||||
{
|
||||
AboutRootView getAboutRootView();
|
||||
|
||||
AboutScreen getAboutScreen();
|
||||
|
||||
ColorPickerDialogFactory getColorPickerDialogFactory();
|
||||
|
||||
HabitCardListAdapter getHabitCardListAdapter();
|
||||
|
||||
ListHabitsBehavior getListHabitsBehavior();
|
||||
|
||||
ListHabitsController getListHabitsController();
|
||||
|
||||
ListHabitsMenu getListHabitsMenu();
|
||||
|
||||
ListHabitsRootView getListHabitsRootView();
|
||||
|
||||
ListHabitsScreen getListHabitsScreen();
|
||||
|
||||
ListHabitsSelectionMenu getListHabitsSelectionMenu();
|
||||
|
||||
ShowHabitScreen getShowHabitScreen();
|
||||
|
||||
ThemeSwitcher getThemeSwitcher();
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Á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.activities
|
||||
|
||||
import dagger.*
|
||||
import org.isoron.androidbase.activities.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.activities.about.*
|
||||
import org.isoron.uhabits.activities.common.dialogs.*
|
||||
import org.isoron.uhabits.activities.habits.list.*
|
||||
import org.isoron.uhabits.activities.habits.list.views.*
|
||||
import org.isoron.uhabits.activities.habits.show.*
|
||||
import org.isoron.uhabits.core.ui.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*
|
||||
|
||||
@ActivityScope
|
||||
@Component(modules = arrayOf(
|
||||
ActivityContextModule::class,
|
||||
BaseActivityModule::class,
|
||||
AboutModule::class,
|
||||
HabitsActivityModule::class,
|
||||
ListHabitsModule::class,
|
||||
ShowHabitModule::class,
|
||||
HabitModule::class
|
||||
), dependencies = arrayOf(HabitsApplicationComponent::class))
|
||||
interface HabitsActivityComponent {
|
||||
val aboutRootView: AboutRootView
|
||||
val aboutScreen: AboutScreen
|
||||
val colorPickerDialogFactory: ColorPickerDialogFactory
|
||||
val habitCardListAdapter: HabitCardListAdapter
|
||||
val listHabitsBehavior: ListHabitsBehavior
|
||||
val listHabitsMenu: ListHabitsMenu
|
||||
val listHabitsRootView: ListHabitsRootView
|
||||
val listHabitsScreen: ListHabitsScreen
|
||||
val listHabitsSelectionMenu: ListHabitsSelectionMenu
|
||||
val showHabitScreen: ShowHabitScreen
|
||||
val themeSwitcher: ThemeSwitcher
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Á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.activities;
|
||||
|
||||
import android.support.annotation.*;
|
||||
|
||||
import org.isoron.androidbase.*;
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*;
|
||||
import org.isoron.uhabits.core.ui.screens.habits.show.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
public class HabitsDirFinder
|
||||
implements ShowHabitMenuBehavior.System, ListHabitsBehavior.DirFinder
|
||||
{
|
||||
private AndroidDirFinder androidDirFinder;
|
||||
|
||||
@Inject
|
||||
public HabitsDirFinder(@NonNull AndroidDirFinder androidDirFinder)
|
||||
{
|
||||
this.androidDirFinder = androidDirFinder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getCSVOutputDir()
|
||||
{
|
||||
return androidDirFinder.getFilesDir("CSV");
|
||||
}
|
||||
}
|
@ -1,23 +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/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides activity that shows information about the app.
|
||||
*/
|
||||
package org.isoron.uhabits.activities.about;
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Álinson Santos Xavier
|
||||
*
|
||||
* 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 .
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.activities.common.dialogs
|
||||
|
||||
import android.content.*
|
||||
import android.support.v7.app.*
|
||||
import android.text.*
|
||||
import android.view.*
|
||||
import android.view.inputmethod.*
|
||||
import android.widget.*
|
||||
import org.isoron.androidbase.activities.*
|
||||
import org.isoron.androidbase.utils.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*
|
||||
import javax.inject.*
|
||||
|
||||
class NumberPickerFactory
|
||||
@Inject constructor(
|
||||
@ActivityContext private val context: Context
|
||||
) {
|
||||
fun create(value: Double,
|
||||
unit: String,
|
||||
callback: ListHabitsBehavior.NumberPickerCallback): AlertDialog {
|
||||
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val view = inflater.inflate(R.layout.number_picker_dialog, null)
|
||||
|
||||
val picker = view.findViewById(R.id.picker) as NumberPicker
|
||||
val picker2 = view.findViewById(R.id.picker2) as NumberPicker
|
||||
val tvUnit = view.findViewById(R.id.tvUnit) as TextView
|
||||
|
||||
val intValue = Math.round(value * 100).toInt()
|
||||
|
||||
picker.minValue = 0
|
||||
picker.maxValue = Integer.MAX_VALUE / 100
|
||||
picker.value = intValue / 100
|
||||
picker.wrapSelectorWheel = false
|
||||
|
||||
picker2.minValue = 0
|
||||
picker2.maxValue = 19
|
||||
picker2.setFormatter { v -> String.format("%02d", 5 * v) }
|
||||
picker2.value = intValue % 100 / 5
|
||||
refreshInitialValue(picker2)
|
||||
|
||||
tvUnit.text = unit
|
||||
|
||||
val dialog = AlertDialog.Builder(context)
|
||||
.setView(view)
|
||||
.setTitle(R.string.change_value)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
picker.clearFocus()
|
||||
val v = picker.value + 0.05 * picker2.value
|
||||
callback.onNumberPicked(v)
|
||||
}
|
||||
.create()
|
||||
|
||||
InterfaceUtils.setupEditorAction(picker) { _, actionId, _ ->
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE)
|
||||
dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick()
|
||||
false
|
||||
}
|
||||
|
||||
return dialog
|
||||
}
|
||||
|
||||
private fun refreshInitialValue(picker: NumberPicker) {
|
||||
// Workaround for Android bug:
|
||||
// https://code.google.com/p/android/issues/detail?id=35482
|
||||
val f = NumberPicker::class.java.getDeclaredField("mInputText")
|
||||
f.isAccessible = true
|
||||
val inputText = f.get(picker) as EditText
|
||||
inputText.filters = arrayOfNulls<InputFilter>(0)
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Á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.activities.common.views
|
||||
|
||||
import android.content.*
|
||||
import android.widget.*
|
||||
import org.isoron.androidbase.activities.*
|
||||
import org.isoron.uhabits.core.tasks.*
|
||||
|
||||
class TaskProgressBar(
|
||||
context: Context,
|
||||
private val runner: TaskRunner
|
||||
) : ProgressBar(
|
||||
context,
|
||||
null,
|
||||
android.R.attr.progressBarStyleHorizontal
|
||||
), TaskRunner.Listener {
|
||||
|
||||
init {
|
||||
visibility = BaseRootView.GONE
|
||||
isIndeterminate = true
|
||||
}
|
||||
|
||||
override fun onTaskStarted(task: Task?) = update()
|
||||
override fun onTaskFinished(task: Task?) = update()
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
runner.addListener(this)
|
||||
update()
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
runner.removeListener(this)
|
||||
super.onDetachedFromWindow()
|
||||
}
|
||||
|
||||
fun update() {
|
||||
val callback = {
|
||||
val activeTaskCount = runner.activeTaskCount
|
||||
val newVisibility = when (activeTaskCount) {
|
||||
0 -> GONE
|
||||
else -> VISIBLE
|
||||
}
|
||||
if (visibility != newVisibility) visibility = newVisibility
|
||||
}
|
||||
postDelayed(callback, 500)
|
||||
}
|
||||
}
|
@ -1,23 +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/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides views that are used across the app, such as RingView.
|
||||
*/
|
||||
package org.isoron.uhabits.activities.common.views;
|
@ -1,23 +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/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides dialogs for editing habits and related classes.
|
||||
*/
|
||||
package org.isoron.uhabits.activities.habits.edit;
|
@ -1,102 +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.activities.habits.list;
|
||||
|
||||
import android.os.*;
|
||||
|
||||
import org.isoron.uhabits.activities.*;
|
||||
import org.isoron.uhabits.activities.habits.list.views.*;
|
||||
import org.isoron.uhabits.core.preferences.*;
|
||||
import org.isoron.uhabits.core.ui.*;
|
||||
import org.isoron.uhabits.core.utils.*;
|
||||
|
||||
/**
|
||||
* Activity that allows the user to see and modify the list of habits.
|
||||
*/
|
||||
public class ListHabitsActivity extends HabitsActivity
|
||||
{
|
||||
private HabitCardListAdapter adapter;
|
||||
|
||||
private ListHabitsRootView rootView;
|
||||
|
||||
private ListHabitsScreen screen;
|
||||
|
||||
private boolean pureBlack;
|
||||
|
||||
private Preferences prefs;
|
||||
|
||||
private MidnightTimer midnightTimer;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
midnightTimer = getAppComponent().getMidnightTimer();
|
||||
HabitsActivityComponent component = getActivityComponent();
|
||||
|
||||
ListHabitsMenu menu = component.getListHabitsMenu();
|
||||
ListHabitsSelectionMenu selectionMenu = component.getListHabitsSelectionMenu();
|
||||
ListHabitsController controller = component.getListHabitsController();
|
||||
|
||||
adapter = component.getHabitCardListAdapter();
|
||||
rootView = component.getListHabitsRootView();
|
||||
screen = component.getListHabitsScreen();
|
||||
|
||||
prefs = getAppComponent().getPreferences();
|
||||
pureBlack = prefs.isPureBlackEnabled();
|
||||
|
||||
screen.setMenu(menu);
|
||||
screen.setController(controller);
|
||||
screen.setSelectionMenu(selectionMenu);
|
||||
rootView.setController(controller, selectionMenu);
|
||||
|
||||
// if(prefs.isSyncEnabled())
|
||||
// startService(new Intent(this, SyncService.class));
|
||||
|
||||
setScreen(screen);
|
||||
controller.onStartup();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause()
|
||||
{
|
||||
midnightTimer.onPause();
|
||||
screen.onDettached();
|
||||
adapter.cancelRefresh();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume()
|
||||
{
|
||||
adapter.refresh();
|
||||
screen.onAttached();
|
||||
rootView.postInvalidate();
|
||||
midnightTimer.onResume();
|
||||
|
||||
if (prefs.getTheme() == ThemeSwitcher.THEME_DARK &&
|
||||
prefs.isPureBlackEnabled() != pureBlack)
|
||||
{
|
||||
restartWithFade(ListHabitsActivity.class);
|
||||
}
|
||||
|
||||
super.onResume();
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.activities.habits.list
|
||||
|
||||
import android.os.*
|
||||
import org.isoron.uhabits.activities.*
|
||||
import org.isoron.uhabits.activities.habits.list.views.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.core.ui.ThemeSwitcher.*
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
|
||||
class ListHabitsActivity : HabitsActivity() {
|
||||
|
||||
var pureBlack: Boolean = false
|
||||
lateinit var adapter: HabitCardListAdapter
|
||||
lateinit var rootView: ListHabitsRootView
|
||||
lateinit var screen: ListHabitsScreen
|
||||
lateinit var prefs: Preferences
|
||||
lateinit var midnightTimer: MidnightTimer
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
prefs = appComponent.preferences
|
||||
pureBlack = prefs.isPureBlackEnabled
|
||||
midnightTimer = appComponent.midnightTimer
|
||||
rootView = component.listHabitsRootView
|
||||
screen = component.listHabitsScreen
|
||||
adapter = component.habitCardListAdapter
|
||||
|
||||
setScreen(screen)
|
||||
component.listHabitsBehavior.onStartup()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
midnightTimer.onPause()
|
||||
screen.onDettached()
|
||||
adapter.cancelRefresh()
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
adapter.refresh()
|
||||
screen.onAttached()
|
||||
rootView.postInvalidate()
|
||||
midnightTimer.onResume()
|
||||
|
||||
if (prefs.theme == THEME_DARK && prefs.isPureBlackEnabled != pureBlack) {
|
||||
restartWithFade(ListHabitsActivity::class.java)
|
||||
}
|
||||
|
||||
super.onResume()
|
||||
}
|
||||
}
|
@ -1,164 +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.activities.habits.list;
|
||||
|
||||
import android.support.annotation.*;
|
||||
|
||||
import org.isoron.androidbase.activities.*;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.activities.habits.list.views.*;
|
||||
import org.isoron.uhabits.core.models.*;
|
||||
import org.isoron.uhabits.core.tasks.*;
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*;
|
||||
import org.isoron.uhabits.tasks.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
@ActivityScope
|
||||
public class ListHabitsController
|
||||
implements HabitCardListController.HabitListener
|
||||
{
|
||||
@NonNull
|
||||
private final ListHabitsBehavior behavior;
|
||||
|
||||
@NonNull
|
||||
private final ListHabitsScreen screen;
|
||||
|
||||
@NonNull
|
||||
private final HabitCardListAdapter adapter;
|
||||
|
||||
@NonNull
|
||||
private final TaskRunner taskRunner;
|
||||
|
||||
private ImportDataTaskFactory importTaskFactory;
|
||||
|
||||
private ExportDBTaskFactory exportDBFactory;
|
||||
|
||||
@Inject
|
||||
public ListHabitsController(@NonNull ListHabitsBehavior behavior,
|
||||
@NonNull HabitCardListAdapter adapter,
|
||||
@NonNull ListHabitsScreen screen,
|
||||
@NonNull TaskRunner taskRunner,
|
||||
@NonNull ImportDataTaskFactory importTaskFactory,
|
||||
@NonNull ExportDBTaskFactory exportDBFactory)
|
||||
{
|
||||
this.behavior = behavior;
|
||||
this.adapter = adapter;
|
||||
this.screen = screen;
|
||||
this.taskRunner = taskRunner;
|
||||
this.importTaskFactory = importTaskFactory;
|
||||
this.exportDBFactory = exportDBFactory;
|
||||
}
|
||||
|
||||
public void onEdit(@NonNull Habit habit, long timestamp)
|
||||
{
|
||||
behavior.onEdit(habit, timestamp);
|
||||
}
|
||||
|
||||
public void onExportCSV()
|
||||
{
|
||||
behavior.onExportCSV();
|
||||
}
|
||||
|
||||
public void onExportDB()
|
||||
{
|
||||
taskRunner.execute(exportDBFactory.create(filename ->
|
||||
{
|
||||
if (filename != null) screen.showSendFileScreen(filename);
|
||||
else screen.showMessage(R.string.could_not_export);
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHabitClick(@NonNull Habit h)
|
||||
{
|
||||
behavior.onClickHabit(h);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHabitReorder(@NonNull Habit from, @NonNull Habit to)
|
||||
{
|
||||
behavior.onReorderHabit(from, to);
|
||||
}
|
||||
|
||||
public void onImportData(@NonNull File file,
|
||||
@NonNull OnFinishedListener finishedListener)
|
||||
{
|
||||
taskRunner.execute(importTaskFactory.create(file, result ->
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case ImportDataTask.SUCCESS:
|
||||
adapter.refresh();
|
||||
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;
|
||||
}
|
||||
|
||||
finishedListener.onFinish();
|
||||
}));
|
||||
}
|
||||
|
||||
public void onInvalidEdit()
|
||||
{
|
||||
screen.showMessage(R.string.long_press_to_edit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInvalidToggle()
|
||||
{
|
||||
screen.showMessage(R.string.long_press_to_toggle);
|
||||
}
|
||||
|
||||
public void onRepairDB()
|
||||
{
|
||||
behavior.onRepairDB();
|
||||
}
|
||||
|
||||
public void onSendBugReport()
|
||||
{
|
||||
behavior.onSendBugReport();
|
||||
}
|
||||
|
||||
public void onStartup()
|
||||
{
|
||||
behavior.onStartup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onToggle(@NonNull Habit habit, long timestamp)
|
||||
{
|
||||
behavior.onToggle(habit, timestamp);
|
||||
}
|
||||
|
||||
public interface OnFinishedListener
|
||||
{
|
||||
void onFinish();
|
||||
}
|
||||
}
|
@ -1,129 +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.activities.habits.list;
|
||||
|
||||
import android.support.annotation.*;
|
||||
import android.view.*;
|
||||
|
||||
import org.isoron.androidbase.activities.*;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.core.preferences.*;
|
||||
import org.isoron.uhabits.core.ui.*;
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
@ActivityScope
|
||||
public class ListHabitsMenu extends BaseMenu
|
||||
{
|
||||
|
||||
@NonNull
|
||||
private final ListHabitsMenuBehavior behavior;
|
||||
|
||||
private final Preferences preferences;
|
||||
|
||||
private ThemeSwitcher themeSwitcher;
|
||||
|
||||
@Inject
|
||||
public ListHabitsMenu(@NonNull BaseActivity activity,
|
||||
@NonNull Preferences preferences,
|
||||
@NonNull ThemeSwitcher themeSwitcher,
|
||||
@NonNull ListHabitsMenuBehavior behavior)
|
||||
{
|
||||
super(activity);
|
||||
this.preferences = preferences;
|
||||
this.themeSwitcher = themeSwitcher;
|
||||
this.behavior = behavior;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@NonNull Menu menu)
|
||||
{
|
||||
MenuItem nightModeItem = menu.findItem(R.id.actionToggleNightMode);
|
||||
MenuItem hideArchivedItem = menu.findItem(R.id.actionHideArchived);
|
||||
MenuItem hideCompletedItem = menu.findItem(R.id.actionHideCompleted);
|
||||
nightModeItem.setChecked(themeSwitcher.isNightMode());
|
||||
hideArchivedItem.setChecked(!preferences.getShowArchived());
|
||||
hideCompletedItem.setChecked(!preferences.getShowCompleted());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemSelected(@NonNull MenuItem item)
|
||||
{
|
||||
switch (item.getItemId())
|
||||
{
|
||||
case R.id.actionToggleNightMode:
|
||||
behavior.onToggleNightMode();
|
||||
return true;
|
||||
|
||||
case R.id.actionAdd:
|
||||
behavior.onCreateHabit();
|
||||
return true;
|
||||
|
||||
case R.id.actionFAQ:
|
||||
behavior.onViewFAQ();
|
||||
return true;
|
||||
|
||||
case R.id.actionAbout:
|
||||
behavior.onViewAbout();
|
||||
return true;
|
||||
|
||||
case R.id.actionSettings:
|
||||
behavior.onViewSettings();
|
||||
return true;
|
||||
|
||||
case R.id.actionHideArchived:
|
||||
behavior.onToggleShowArchived();
|
||||
invalidate();
|
||||
return true;
|
||||
|
||||
case R.id.actionHideCompleted:
|
||||
behavior.onToggleShowCompleted();
|
||||
invalidate();
|
||||
return true;
|
||||
|
||||
case R.id.actionSortColor:
|
||||
behavior.onSortByColor();
|
||||
return true;
|
||||
|
||||
case R.id.actionSortManual:
|
||||
behavior.onSortByManually();
|
||||
return true;
|
||||
|
||||
case R.id.actionSortName:
|
||||
behavior.onSortByName();
|
||||
return true;
|
||||
|
||||
case R.id.actionSortScore:
|
||||
behavior.onSortByScore();
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getMenuResourceId()
|
||||
{
|
||||
return R.menu.list_habits;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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.activities.habits.list
|
||||
|
||||
import android.view.*
|
||||
import org.isoron.androidbase.activities.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.core.ui.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*
|
||||
import javax.inject.*
|
||||
|
||||
@ActivityScope
|
||||
class ListHabitsMenu @Inject constructor(
|
||||
activity: BaseActivity,
|
||||
private val preferences: Preferences,
|
||||
private val themeSwitcher: ThemeSwitcher,
|
||||
private val behavior: ListHabitsMenuBehavior
|
||||
) : BaseMenu(activity) {
|
||||
|
||||
override fun onCreate(menu: Menu) {
|
||||
val nightModeItem = menu.findItem(R.id.actionToggleNightMode)
|
||||
val hideArchivedItem = menu.findItem(R.id.actionHideArchived)
|
||||
val hideCompletedItem = menu.findItem(R.id.actionHideCompleted)
|
||||
nightModeItem.isChecked = themeSwitcher.isNightMode
|
||||
hideArchivedItem.isChecked = !preferences.showArchived
|
||||
hideCompletedItem.isChecked = !preferences.showCompleted
|
||||
}
|
||||
|
||||
override fun onItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.actionToggleNightMode -> {
|
||||
behavior.onToggleNightMode()
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.actionAdd -> {
|
||||
behavior.onCreateHabit()
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.actionFAQ -> {
|
||||
behavior.onViewFAQ()
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.actionAbout -> {
|
||||
behavior.onViewAbout()
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.actionSettings -> {
|
||||
behavior.onViewSettings()
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.actionHideArchived -> {
|
||||
behavior.onToggleShowArchived()
|
||||
invalidate()
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.actionHideCompleted -> {
|
||||
behavior.onToggleShowCompleted()
|
||||
invalidate()
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.actionSortColor -> {
|
||||
behavior.onSortByColor()
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.actionSortManual -> {
|
||||
behavior.onSortByManually()
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.actionSortName -> {
|
||||
behavior.onSortByName()
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.actionSortScore -> {
|
||||
behavior.onSortByScore()
|
||||
return true
|
||||
}
|
||||
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMenuResourceId() = R.menu.list_habits
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Á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.activities.habits.list;
|
||||
|
||||
import android.content.*;
|
||||
import android.support.annotation.*;
|
||||
|
||||
import org.isoron.androidbase.*;
|
||||
import org.isoron.uhabits.activities.*;
|
||||
import org.isoron.uhabits.activities.habits.list.views.*;
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
import dagger.*;
|
||||
|
||||
class BugReporterProxy extends AndroidBugReporter
|
||||
implements ListHabitsBehavior.BugReporter
|
||||
{
|
||||
@Inject
|
||||
public BugReporterProxy(@AppContext @NonNull Context context)
|
||||
{
|
||||
super(context);
|
||||
}
|
||||
}
|
||||
|
||||
@Module
|
||||
public abstract class ListHabitsModule
|
||||
{
|
||||
@Binds
|
||||
abstract ListHabitsMenuBehavior.Adapter getAdapter(HabitCardListAdapter adapter);
|
||||
|
||||
@Binds
|
||||
abstract ListHabitsBehavior.BugReporter getBugReporter(BugReporterProxy proxy);
|
||||
|
||||
@Binds
|
||||
abstract ListHabitsMenuBehavior.Screen getMenuScreen(ListHabitsScreen screen);
|
||||
|
||||
@Binds
|
||||
abstract ListHabitsBehavior.Screen getScreen(ListHabitsScreen screen);
|
||||
|
||||
@Binds
|
||||
abstract ListHabitsSelectionMenuBehavior.Adapter getSelMenuAdapter(
|
||||
HabitCardListAdapter adapter);
|
||||
|
||||
@Binds
|
||||
abstract ListHabitsSelectionMenuBehavior.Screen getSelMenuScreen(
|
||||
ListHabitsScreen screen);
|
||||
|
||||
@Binds
|
||||
abstract ListHabitsBehavior.DirFinder getSystem(HabitsDirFinder system);
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Á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.activities.habits.list
|
||||
|
||||
import android.content.*
|
||||
import dagger.*
|
||||
import org.isoron.androidbase.*
|
||||
import org.isoron.uhabits.activities.*
|
||||
import org.isoron.uhabits.activities.habits.list.views.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*
|
||||
import javax.inject.*
|
||||
|
||||
class BugReporterProxy
|
||||
@Inject constructor(
|
||||
@AppContext context: Context
|
||||
) : AndroidBugReporter(context), ListHabitsBehavior.BugReporter
|
||||
|
||||
@Module
|
||||
abstract class ListHabitsModule {
|
||||
|
||||
@Binds
|
||||
abstract fun getAdapter(adapter: HabitCardListAdapter): ListHabitsMenuBehavior.Adapter
|
||||
|
||||
@Binds
|
||||
abstract fun getBugReporter(proxy: BugReporterProxy): ListHabitsBehavior.BugReporter
|
||||
|
||||
@Binds
|
||||
abstract fun getMenuScreen(screen: ListHabitsScreen): ListHabitsMenuBehavior.Screen
|
||||
|
||||
@Binds
|
||||
abstract fun getScreen(screen: ListHabitsScreen): ListHabitsBehavior.Screen
|
||||
|
||||
@Binds
|
||||
abstract fun getSelMenuAdapter(adapter: HabitCardListAdapter): ListHabitsSelectionMenuBehavior.Adapter
|
||||
|
||||
@Binds
|
||||
abstract fun getSelMenuScreen(screen: ListHabitsScreen): ListHabitsSelectionMenuBehavior.Screen
|
||||
|
||||
@Binds
|
||||
abstract fun getSystem(system: HabitsDirFinder): ListHabitsBehavior.DirFinder
|
||||
}
|
@ -1,198 +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.activities.habits.list;
|
||||
|
||||
import android.content.*;
|
||||
import android.support.annotation.*;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
|
||||
import org.isoron.androidbase.activities.*;
|
||||
import org.isoron.androidbase.utils.*;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.activities.common.views.*;
|
||||
import org.isoron.uhabits.activities.habits.list.views.*;
|
||||
import org.isoron.uhabits.core.models.*;
|
||||
import org.isoron.uhabits.core.tasks.*;
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
import butterknife.*;
|
||||
|
||||
import static org.isoron.androidbase.utils.InterfaceUtils.*;
|
||||
|
||||
@ActivityScope
|
||||
public class ListHabitsRootView extends BaseRootView
|
||||
implements ModelObservable.Listener, TaskRunner.Listener
|
||||
{
|
||||
public static final int MAX_CHECKMARK_COUNT = 60;
|
||||
|
||||
@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;
|
||||
|
||||
@BindView(R.id.header)
|
||||
HeaderView header;
|
||||
|
||||
@NonNull
|
||||
private final HabitCardListAdapter listAdapter;
|
||||
|
||||
private final TaskRunner runner;
|
||||
|
||||
@Inject
|
||||
public ListHabitsRootView(@ActivityContext Context context,
|
||||
@NonNull HintListFactory hintListFactory,
|
||||
@NonNull HabitCardListAdapter listAdapter,
|
||||
@NonNull TaskRunner runner)
|
||||
{
|
||||
super(context);
|
||||
addView(inflate(getContext(), R.layout.list_habits, null));
|
||||
ButterKnife.bind(this);
|
||||
|
||||
this.listAdapter = listAdapter;
|
||||
listView.setAdapter(listAdapter);
|
||||
listAdapter.setListView(listView);
|
||||
|
||||
this.runner = runner;
|
||||
progressBar.setIndeterminate(true);
|
||||
tvStarEmpty.setTypeface(InterfaceUtils.getFontAwesome(getContext()));
|
||||
|
||||
String hints[] =
|
||||
getContext().getResources().getStringArray(R.array.hints);
|
||||
HintList hintList = hintListFactory.create(hints);
|
||||
hintView.setHints(hintList);
|
||||
|
||||
initToolbar();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Toolbar getToolbar()
|
||||
{
|
||||
return toolbar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModelChange()
|
||||
{
|
||||
updateEmptyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskFinished(Task task)
|
||||
{
|
||||
updateProgressBar();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskStarted(Task task)
|
||||
{
|
||||
updateProgressBar();
|
||||
}
|
||||
|
||||
public void setController(@NonNull ListHabitsController controller,
|
||||
@NonNull ListHabitsSelectionMenu menu)
|
||||
{
|
||||
HabitCardListController listController =
|
||||
new HabitCardListController(listAdapter);
|
||||
|
||||
listController.setHabitListener(controller);
|
||||
listController.setSelectionListener(menu);
|
||||
listView.setController(listController);
|
||||
menu.setListController(listController);
|
||||
header.setScrollController(new ScrollableChart.ScrollController()
|
||||
{
|
||||
@Override
|
||||
public void onDataOffsetChanged(int newDataOffset)
|
||||
{
|
||||
listView.setDataOffset(newDataOffset);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow()
|
||||
{
|
||||
super.onAttachedToWindow();
|
||||
runner.addListener(this);
|
||||
updateProgressBar();
|
||||
listAdapter.getObservable().addListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow()
|
||||
{
|
||||
listAdapter.getObservable().removeListener(this);
|
||||
runner.removeListener(this);
|
||||
super.onDetachedFromWindow();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh)
|
||||
{
|
||||
int count = getCheckmarkCount();
|
||||
header.setButtonCount(count);
|
||||
header.setMaxDataOffset(Math.max(MAX_CHECKMARK_COUNT - count, 0));
|
||||
listView.setCheckmarkCount(count);
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
}
|
||||
|
||||
private int getCheckmarkCount()
|
||||
{
|
||||
float nameWidth = getDimension(getContext(), R.dimen.habitNameWidth);
|
||||
float labelWidth = Math.max(getMeasuredWidth() / 3, nameWidth);
|
||||
float buttonWidth = getDimension(getContext(), R.dimen.checkmarkWidth);
|
||||
return Math.min(MAX_CHECKMARK_COUNT, Math.max(0,
|
||||
(int) ((getMeasuredWidth() - labelWidth) / buttonWidth)));
|
||||
}
|
||||
|
||||
private void updateEmptyView()
|
||||
{
|
||||
llEmpty.setVisibility(
|
||||
listAdapter.getItemCount() > 0 ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
private void updateProgressBar()
|
||||
{
|
||||
postDelayed(() ->
|
||||
{
|
||||
int activeTaskCount = runner.getActiveTaskCount();
|
||||
int newVisibility = activeTaskCount > 0 ? VISIBLE : GONE;
|
||||
if (progressBar.getVisibility() != newVisibility)
|
||||
progressBar.setVisibility(newVisibility);
|
||||
}, 500);
|
||||
}
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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.activities.habits.list
|
||||
|
||||
import android.content.*
|
||||
import android.os.Build.VERSION.*
|
||||
import android.os.Build.VERSION_CODES.*
|
||||
import android.support.v7.widget.Toolbar
|
||||
import android.view.ViewGroup.LayoutParams.*
|
||||
import android.widget.*
|
||||
import org.isoron.androidbase.activities.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.activities.common.views.*
|
||||
import org.isoron.uhabits.activities.habits.list.views.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.core.tasks.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
import java.lang.Math.*
|
||||
import javax.inject.*
|
||||
|
||||
const val MAX_CHECKMARK_COUNT = 60
|
||||
|
||||
@ActivityScope
|
||||
class ListHabitsRootView @Inject constructor(
|
||||
@ActivityContext context: Context,
|
||||
hintListFactory: HintListFactory,
|
||||
preferences: Preferences,
|
||||
midnightTimer: MidnightTimer,
|
||||
runner: TaskRunner,
|
||||
private val listAdapter: HabitCardListAdapter,
|
||||
habitCardListViewFactory: HabitCardListViewFactory
|
||||
) : BaseRootView(context), ModelObservable.Listener {
|
||||
|
||||
val listView: HabitCardListView = habitCardListViewFactory.create()
|
||||
val llEmpty = EmptyListView(context)
|
||||
val tbar = buildToolbar()
|
||||
val progressBar = TaskProgressBar(context, runner)
|
||||
val hintView: HintView
|
||||
val header = HeaderView(context, preferences, midnightTimer)
|
||||
|
||||
init {
|
||||
val hints = resources.getStringArray(R.array.hints)
|
||||
val hintList = hintListFactory.create(hints)
|
||||
hintView = HintView(context, hintList)
|
||||
|
||||
addView(RelativeLayout(context).apply {
|
||||
background = sres.getDrawable(R.attr.windowBackgroundColor)
|
||||
addAtTop(tbar)
|
||||
addBelow(header, tbar)
|
||||
addBelow(listView, header, height = MATCH_PARENT)
|
||||
addBelow(llEmpty, header, height = MATCH_PARENT)
|
||||
addBelow(progressBar, header) {
|
||||
it.topMargin = dp(-6.0f).toInt()
|
||||
}
|
||||
addAtBottom(hintView)
|
||||
if (SDK_INT < LOLLIPOP) {
|
||||
addBelow(ShadowView(context), tbar)
|
||||
addBelow(ShadowView(context), header)
|
||||
}
|
||||
}, MATCH_PARENT, MATCH_PARENT)
|
||||
|
||||
listAdapter.setListView(listView)
|
||||
initToolbar()
|
||||
}
|
||||
|
||||
override fun getToolbar(): Toolbar {
|
||||
return tbar
|
||||
}
|
||||
|
||||
override fun onModelChange() {
|
||||
updateEmptyView()
|
||||
}
|
||||
|
||||
private fun setupControllers() {
|
||||
header.setScrollController(object : ScrollableChart.ScrollController {
|
||||
override fun onDataOffsetChanged(newDataOffset: Int) {
|
||||
listView.dataOffset = newDataOffset
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
setupControllers()
|
||||
listAdapter.observable.addListener(this)
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
listAdapter.observable.removeListener(this)
|
||||
super.onDetachedFromWindow()
|
||||
}
|
||||
|
||||
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
||||
val count = getCheckmarkCount()
|
||||
header.buttonCount = count
|
||||
header.setMaxDataOffset(max(MAX_CHECKMARK_COUNT - count, 0))
|
||||
listView.checkmarkCount = count
|
||||
super.onSizeChanged(w, h, oldw, oldh)
|
||||
}
|
||||
|
||||
private fun getCheckmarkCount(): Int {
|
||||
val nameWidth = dim(R.dimen.habitNameWidth)
|
||||
val buttonWidth = dim(R.dimen.checkmarkWidth)
|
||||
val labelWidth = max((measuredWidth / 3).toFloat(), nameWidth)
|
||||
val buttonCount = ((measuredWidth - labelWidth) / buttonWidth).toInt()
|
||||
return min(MAX_CHECKMARK_COUNT, max(0, buttonCount))
|
||||
}
|
||||
|
||||
private fun updateEmptyView() {
|
||||
llEmpty.visibility = when (listAdapter.itemCount) {
|
||||
0 -> VISIBLE
|
||||
else -> GONE
|
||||
}
|
||||
}
|
||||
}
|
@ -1,441 +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.activities.habits.list;
|
||||
|
||||
import android.app.*;
|
||||
import android.content.*;
|
||||
import android.net.*;
|
||||
import android.support.annotation.*;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.text.*;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
|
||||
import org.isoron.androidbase.activities.*;
|
||||
import org.isoron.androidbase.utils.*;
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.activities.common.dialogs.*;
|
||||
import org.isoron.uhabits.activities.habits.edit.*;
|
||||
import org.isoron.uhabits.core.commands.*;
|
||||
import org.isoron.uhabits.core.models.*;
|
||||
import org.isoron.uhabits.core.preferences.*;
|
||||
import org.isoron.uhabits.core.ui.*;
|
||||
import org.isoron.uhabits.core.ui.callbacks.*;
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*;
|
||||
import org.isoron.uhabits.intents.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
import static android.content.DialogInterface.BUTTON_POSITIVE;
|
||||
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
|
||||
|
||||
@ActivityScope
|
||||
public class ListHabitsScreen extends BaseScreen
|
||||
implements CommandRunner.Listener, ListHabitsBehavior.Screen,
|
||||
ListHabitsMenuBehavior.Screen,
|
||||
ListHabitsSelectionMenuBehavior.Screen
|
||||
{
|
||||
public static final int REQUEST_OPEN_DOCUMENT = 6;
|
||||
|
||||
public static final int REQUEST_SETTINGS = 7;
|
||||
|
||||
public static final int RESULT_BUG_REPORT = 4;
|
||||
|
||||
public static final int RESULT_EXPORT_CSV = 2;
|
||||
|
||||
public static final int RESULT_EXPORT_DB = 3;
|
||||
|
||||
public static final int RESULT_IMPORT_DATA = 1;
|
||||
|
||||
public static final int RESULT_REPAIR_DB = 5;
|
||||
|
||||
@Nullable
|
||||
private ListHabitsController controller;
|
||||
|
||||
@NonNull
|
||||
private final IntentFactory intentFactory;
|
||||
|
||||
@NonNull
|
||||
private final CommandRunner commandRunner;
|
||||
|
||||
@NonNull
|
||||
private final ConfirmDeleteDialogFactory confirmDeleteDialogFactory;
|
||||
|
||||
@NonNull
|
||||
private final ColorPickerDialogFactory colorPickerFactory;
|
||||
|
||||
@NonNull
|
||||
private final EditHabitDialogFactory editHabitDialogFactory;
|
||||
|
||||
@NonNull
|
||||
private final ThemeSwitcher themeSwitcher;
|
||||
|
||||
@NonNull
|
||||
private Preferences prefs;
|
||||
|
||||
@Inject
|
||||
public ListHabitsScreen(@NonNull BaseActivity activity,
|
||||
@NonNull CommandRunner commandRunner,
|
||||
@NonNull ListHabitsRootView rootView,
|
||||
@NonNull IntentFactory intentFactory,
|
||||
@NonNull ThemeSwitcher themeSwitcher,
|
||||
@NonNull ConfirmDeleteDialogFactory confirmDeleteDialogFactory,
|
||||
@NonNull ColorPickerDialogFactory colorPickerFactory,
|
||||
@NonNull EditHabitDialogFactory editHabitDialogFactory,
|
||||
@NonNull Preferences prefs)
|
||||
{
|
||||
super(activity);
|
||||
setRootView(rootView);
|
||||
this.prefs = prefs;
|
||||
this.colorPickerFactory = colorPickerFactory;
|
||||
this.commandRunner = commandRunner;
|
||||
this.confirmDeleteDialogFactory = confirmDeleteDialogFactory;
|
||||
this.editHabitDialogFactory = editHabitDialogFactory;
|
||||
this.intentFactory = intentFactory;
|
||||
this.themeSwitcher = themeSwitcher;
|
||||
}
|
||||
|
||||
@StringRes
|
||||
private Integer getExecuteString(@NonNull Command command)
|
||||
{
|
||||
if(command instanceof ArchiveHabitsCommand)
|
||||
return R.string.toast_habit_archived;
|
||||
|
||||
if(command instanceof ChangeHabitColorCommand)
|
||||
return R.string.toast_habit_changed;
|
||||
|
||||
if(command instanceof CreateHabitCommand)
|
||||
return R.string.toast_habit_created;
|
||||
|
||||
if(command instanceof DeleteHabitsCommand)
|
||||
return R.string.toast_habit_deleted;
|
||||
|
||||
if(command instanceof EditHabitCommand)
|
||||
return R.string.toast_habit_changed;
|
||||
|
||||
if(command instanceof UnarchiveHabitsCommand)
|
||||
return R.string.toast_habit_unarchived;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void onAttached()
|
||||
{
|
||||
commandRunner.addListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCommandExecuted(@NonNull Command command,
|
||||
@Nullable Long refreshKey)
|
||||
{
|
||||
if (command.isRemote()) return;
|
||||
showMessage(getExecuteString(command));
|
||||
}
|
||||
|
||||
public void onDettached()
|
||||
{
|
||||
commandRunner.removeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
if (requestCode == REQUEST_OPEN_DOCUMENT)
|
||||
onOpenDocumentResult(resultCode, data);
|
||||
|
||||
if (requestCode == REQUEST_SETTINGS) onSettingsResult(resultCode);
|
||||
}
|
||||
|
||||
public void setController(@Nullable ListHabitsController controller)
|
||||
{
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyTheme()
|
||||
{
|
||||
themeSwitcher.apply();
|
||||
activity.restartWithFade(ListHabitsActivity.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showAboutScreen()
|
||||
{
|
||||
Intent intent = intentFactory.startAboutActivity(activity);
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showColorPicker(int defaultColor,
|
||||
@NonNull OnColorPickedCallback callback) {
|
||||
ColorPickerDialog picker = colorPickerFactory.create(defaultColor);
|
||||
picker.setListener(callback);
|
||||
activity.showDialog(picker, "picker");
|
||||
}
|
||||
|
||||
public void showCreateBooleanHabitScreen()
|
||||
{
|
||||
EditHabitDialog dialog;
|
||||
dialog = editHabitDialogFactory.createBoolean();
|
||||
activity.showDialog(dialog, "editHabit");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showCreateHabitScreen()
|
||||
{
|
||||
if (!prefs.isNumericalHabitsFeatureEnabled())
|
||||
{
|
||||
showCreateBooleanHabitScreen();
|
||||
return;
|
||||
}
|
||||
|
||||
Dialog dialog = new AlertDialog.Builder(activity)
|
||||
.setTitle("Type of habit")
|
||||
.setItems(R.array.habitTypes, (d, which) ->
|
||||
{
|
||||
if (which == 0) showCreateBooleanHabitScreen();
|
||||
else showCreateNumericalHabitScreen();
|
||||
})
|
||||
.create();
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showDeleteConfirmationScreen(
|
||||
@NonNull OnConfirmedCallback callback)
|
||||
{
|
||||
activity.showDialog(confirmDeleteDialogFactory.create(callback));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showEditHabitsScreen(List<Habit> habits)
|
||||
{
|
||||
EditHabitDialog dialog;
|
||||
dialog = editHabitDialogFactory.edit(habits.get(0));
|
||||
activity.showDialog(dialog, "editNumericalHabit");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showFAQScreen()
|
||||
{
|
||||
Intent intent = intentFactory.viewFAQ(activity);
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showHabitScreen(@NonNull Habit habit)
|
||||
{
|
||||
Intent intent = intentFactory.startShowHabitActivity(activity, habit);
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
|
||||
public void showImportScreen()
|
||||
{
|
||||
Intent intent = intentFactory.openDocument();
|
||||
activity.startActivityForResult(intent, REQUEST_OPEN_DOCUMENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showIntroScreen()
|
||||
{
|
||||
Intent intent = intentFactory.startIntroActivity(activity);
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showMessage(@NonNull ListHabitsBehavior.Message m)
|
||||
{
|
||||
switch (m)
|
||||
{
|
||||
case COULD_NOT_EXPORT:
|
||||
showMessage(R.string.could_not_export);
|
||||
break;
|
||||
|
||||
case IMPORT_SUCCESSFUL:
|
||||
showMessage(R.string.habits_imported);
|
||||
break;
|
||||
|
||||
case IMPORT_FAILED:
|
||||
showMessage(R.string.could_not_import);
|
||||
break;
|
||||
|
||||
case DATABASE_REPAIRED:
|
||||
showMessage(R.string.database_repaired);
|
||||
break;
|
||||
|
||||
case COULD_NOT_GENERATE_BUG_REPORT:
|
||||
showMessage(R.string.bug_report_failed);
|
||||
break;
|
||||
|
||||
case FILE_NOT_RECOGNIZED:
|
||||
showMessage(R.string.file_not_recognized);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showNumberPicker(double value,
|
||||
@NonNull String unit,
|
||||
@NonNull
|
||||
ListHabitsBehavior.NumberPickerCallback callback)
|
||||
{
|
||||
LayoutInflater inflater = activity.getLayoutInflater();
|
||||
View view = inflater.inflate(R.layout.number_picker_dialog, null);
|
||||
|
||||
final NumberPicker picker;
|
||||
final NumberPicker picker2;
|
||||
final TextView tvUnit;
|
||||
|
||||
picker = (NumberPicker) view.findViewById(R.id.picker);
|
||||
picker2 = (NumberPicker) view.findViewById(R.id.picker2);
|
||||
tvUnit = (TextView) view.findViewById(R.id.tvUnit);
|
||||
|
||||
int intValue = (int) Math.round(value * 100);
|
||||
|
||||
picker.setMinValue(0);
|
||||
picker.setMaxValue(Integer.MAX_VALUE / 100);
|
||||
picker.setValue(intValue / 100);
|
||||
picker.setWrapSelectorWheel(false);
|
||||
|
||||
picker2.setMinValue(0);
|
||||
picker2.setMaxValue(19);
|
||||
picker2.setFormatter(v -> String.format("%02d", 5 * v));
|
||||
picker2.setValue((intValue % 100) / 5);
|
||||
refreshInitialValue(picker2);
|
||||
|
||||
tvUnit.setText(unit);
|
||||
|
||||
AlertDialog dialog = new AlertDialog.Builder(activity)
|
||||
.setView(view)
|
||||
.setTitle(R.string.change_value)
|
||||
.setPositiveButton(android.R.string.ok, (d, which) ->
|
||||
{
|
||||
picker.clearFocus();
|
||||
double v = picker.getValue() + 0.05 * picker2.getValue();
|
||||
callback.onNumberPicked(v);
|
||||
})
|
||||
.create();
|
||||
|
||||
InterfaceUtils.setupEditorAction(picker, (v, actionId, event) ->
|
||||
{
|
||||
if (actionId == IME_ACTION_DONE)
|
||||
dialog.getButton(BUTTON_POSITIVE).performClick();
|
||||
return false;
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showSendBugReportToDeveloperScreen(String log)
|
||||
{
|
||||
int to = R.string.bugReportTo;
|
||||
int subject = R.string.bugReportSubject;
|
||||
showSendEmailScreen(to, subject, log);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showSettingsScreen()
|
||||
{
|
||||
Intent intent = intentFactory.startSettingsActivity(activity);
|
||||
activity.startActivityForResult(intent, REQUEST_SETTINGS);
|
||||
}
|
||||
|
||||
private void onOpenDocumentResult(int resultCode, Intent data)
|
||||
{
|
||||
if (controller == null) return;
|
||||
if (resultCode != Activity.RESULT_OK) return;
|
||||
|
||||
try
|
||||
{
|
||||
Uri uri = data.getData();
|
||||
ContentResolver cr = activity.getContentResolver();
|
||||
InputStream is = cr.openInputStream(uri);
|
||||
|
||||
File cacheDir = activity.getExternalCacheDir();
|
||||
File tempFile = File.createTempFile("import", "", cacheDir);
|
||||
|
||||
FileUtils.copy(is, tempFile);
|
||||
controller.onImportData(tempFile, () -> tempFile.delete());
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
showMessage(R.string.could_not_import);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void onSettingsResult(int resultCode)
|
||||
{
|
||||
if (controller == null) return;
|
||||
|
||||
switch (resultCode)
|
||||
{
|
||||
case RESULT_IMPORT_DATA:
|
||||
showImportScreen();
|
||||
break;
|
||||
|
||||
case RESULT_EXPORT_CSV:
|
||||
controller.onExportCSV();
|
||||
break;
|
||||
|
||||
case RESULT_EXPORT_DB:
|
||||
controller.onExportDB();
|
||||
break;
|
||||
|
||||
case RESULT_BUG_REPORT:
|
||||
controller.onSendBugReport();
|
||||
break;
|
||||
|
||||
case RESULT_REPAIR_DB:
|
||||
controller.onRepairDB();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshInitialValue(NumberPicker picker2)
|
||||
{
|
||||
// Workaround for a bug on Android:
|
||||
// https://code.google.com/p/android/issues/detail?id=35482
|
||||
try
|
||||
{
|
||||
Field f = NumberPicker.class.getDeclaredField("mInputText");
|
||||
f.setAccessible(true);
|
||||
EditText inputText = (EditText) f.get(picker2);
|
||||
inputText.setFilters(new InputFilter[0]);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void showCreateNumericalHabitScreen()
|
||||
{
|
||||
EditHabitDialog dialog;
|
||||
dialog = editHabitDialogFactory.createNumerical();
|
||||
activity.showDialog(dialog, "editHabit");
|
||||
}
|
||||
}
|
@ -0,0 +1,264 @@
|
||||
/*
|
||||
* 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.activities.habits.list
|
||||
|
||||
import android.app.*
|
||||
import android.content.*
|
||||
import android.support.annotation.*
|
||||
import dagger.*
|
||||
import org.isoron.androidbase.activities.*
|
||||
import org.isoron.androidbase.utils.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.activities.common.dialogs.*
|
||||
import org.isoron.uhabits.activities.habits.edit.*
|
||||
import org.isoron.uhabits.activities.habits.list.views.*
|
||||
import org.isoron.uhabits.core.commands.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.core.tasks.*
|
||||
import org.isoron.uhabits.core.ui.*
|
||||
import org.isoron.uhabits.core.ui.callbacks.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior.Message.*
|
||||
import org.isoron.uhabits.intents.*
|
||||
import org.isoron.uhabits.tasks.*
|
||||
import java.io.*
|
||||
import javax.inject.*
|
||||
|
||||
const val RESULT_IMPORT_DATA = 1
|
||||
const val RESULT_EXPORT_CSV = 2
|
||||
const val RESULT_EXPORT_DB = 3
|
||||
const val RESULT_BUG_REPORT = 4
|
||||
const val RESULT_REPAIR_DB = 5
|
||||
const val REQUEST_OPEN_DOCUMENT = 6
|
||||
const val REQUEST_SETTINGS = 7
|
||||
|
||||
@ActivityScope
|
||||
class ListHabitsScreen
|
||||
@Inject constructor(
|
||||
activity: BaseActivity,
|
||||
rootView: ListHabitsRootView,
|
||||
private val commandRunner: CommandRunner,
|
||||
private val intentFactory: IntentFactory,
|
||||
private val themeSwitcher: ThemeSwitcher,
|
||||
private val preferences: Preferences,
|
||||
private val adapter: HabitCardListAdapter,
|
||||
private val taskRunner: TaskRunner,
|
||||
private val exportDBFactory: ExportDBTaskFactory,
|
||||
private val importTaskFactory: ImportDataTaskFactory,
|
||||
private val confirmDeleteDialogFactory: ConfirmDeleteDialogFactory,
|
||||
private val colorPickerFactory: ColorPickerDialogFactory,
|
||||
private val editHabitDialogFactory: EditHabitDialogFactory,
|
||||
private val numberPickerFactory: NumberPickerFactory,
|
||||
private val behavior: Lazy<ListHabitsBehavior>,
|
||||
private val menu: Lazy<ListHabitsMenu>,
|
||||
private val selectionMenu: Lazy<ListHabitsSelectionMenu>
|
||||
) : BaseScreen(activity),
|
||||
CommandRunner.Listener,
|
||||
ListHabitsBehavior.Screen,
|
||||
ListHabitsMenuBehavior.Screen,
|
||||
ListHabitsSelectionMenuBehavior.Screen {
|
||||
|
||||
init {
|
||||
setRootView(rootView)
|
||||
}
|
||||
|
||||
fun onAttached() {
|
||||
setMenu(menu.get())
|
||||
setSelectionMenu(selectionMenu.get())
|
||||
commandRunner.addListener(this)
|
||||
}
|
||||
|
||||
fun onDettached() {
|
||||
commandRunner.removeListener(this)
|
||||
}
|
||||
|
||||
override fun onCommandExecuted(command: Command, refreshKey: Long?) {
|
||||
if (command.isRemote) return
|
||||
showMessage(getExecuteString(command))
|
||||
}
|
||||
|
||||
override fun onResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
when (requestCode) {
|
||||
REQUEST_OPEN_DOCUMENT -> onOpenDocumentResult(resultCode, data)
|
||||
REQUEST_SETTINGS -> onSettingsResult(resultCode)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onOpenDocumentResult(resultCode: Int, data: Intent?) {
|
||||
if (data == null) return
|
||||
if (resultCode != Activity.RESULT_OK) return
|
||||
try {
|
||||
val inStream = activity.contentResolver.openInputStream(data.data)
|
||||
val cacheDir = activity.externalCacheDir
|
||||
val tempFile = File.createTempFile("import", "", cacheDir)
|
||||
FileUtils.copy(inStream, tempFile)
|
||||
onImportData(tempFile) { tempFile.delete() }
|
||||
} catch (e: IOException) {
|
||||
showMessage(R.string.could_not_import)
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onSettingsResult(resultCode: Int) {
|
||||
when (resultCode) {
|
||||
RESULT_IMPORT_DATA -> showImportScreen()
|
||||
RESULT_EXPORT_CSV -> behavior.get().onExportCSV()
|
||||
RESULT_EXPORT_DB -> onExportDB()
|
||||
RESULT_BUG_REPORT -> behavior.get().onSendBugReport()
|
||||
RESULT_REPAIR_DB -> behavior.get().onRepairDB()
|
||||
}
|
||||
}
|
||||
|
||||
override fun applyTheme() {
|
||||
themeSwitcher.apply()
|
||||
activity.restartWithFade(ListHabitsActivity::class.java)
|
||||
}
|
||||
|
||||
override fun showAboutScreen() {
|
||||
val intent = intentFactory.startAboutActivity(activity)
|
||||
activity.startActivity(intent)
|
||||
}
|
||||
|
||||
fun showCreateBooleanHabitScreen() {
|
||||
val dialog = editHabitDialogFactory.createBoolean()
|
||||
activity.showDialog(dialog, "editHabit")
|
||||
}
|
||||
|
||||
override fun showCreateHabitScreen() {
|
||||
if (!preferences.isNumericalHabitsFeatureEnabled) {
|
||||
showCreateBooleanHabitScreen()
|
||||
return
|
||||
}
|
||||
|
||||
val dialog = AlertDialog.Builder(activity)
|
||||
.setTitle("Type of habit")
|
||||
.setItems(R.array.habitTypes) { _, which ->
|
||||
if (which == 0) showCreateBooleanHabitScreen()
|
||||
else showCreateNumericalHabitScreen()
|
||||
}
|
||||
.create()
|
||||
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
override fun showDeleteConfirmationScreen(callback: OnConfirmedCallback) {
|
||||
activity.showDialog(confirmDeleteDialogFactory.create(callback))
|
||||
}
|
||||
|
||||
override fun showEditHabitsScreen(habits: List<Habit>) {
|
||||
val dialog = editHabitDialogFactory.edit(habits[0])
|
||||
activity.showDialog(dialog, "editNumericalHabit")
|
||||
}
|
||||
|
||||
override fun showFAQScreen() {
|
||||
val intent = intentFactory.viewFAQ(activity)
|
||||
activity.startActivity(intent)
|
||||
}
|
||||
|
||||
override fun showHabitScreen(habit: Habit) {
|
||||
val intent = intentFactory.startShowHabitActivity(activity, habit)
|
||||
activity.startActivity(intent)
|
||||
}
|
||||
|
||||
fun showImportScreen() {
|
||||
val intent = intentFactory.openDocument()
|
||||
activity.startActivityForResult(intent, REQUEST_OPEN_DOCUMENT)
|
||||
}
|
||||
|
||||
override fun showIntroScreen() {
|
||||
val intent = intentFactory.startIntroActivity(activity)
|
||||
activity.startActivity(intent)
|
||||
}
|
||||
|
||||
override fun showMessage(m: ListHabitsBehavior.Message) {
|
||||
showMessage(when (m) {
|
||||
COULD_NOT_EXPORT -> R.string.could_not_export
|
||||
IMPORT_SUCCESSFUL -> R.string.habits_imported
|
||||
IMPORT_FAILED -> R.string.could_not_import
|
||||
DATABASE_REPAIRED -> R.string.database_repaired
|
||||
COULD_NOT_GENERATE_BUG_REPORT -> R.string.bug_report_failed
|
||||
FILE_NOT_RECOGNIZED -> R.string.file_not_recognized
|
||||
})
|
||||
}
|
||||
|
||||
override fun showSendBugReportToDeveloperScreen(log: String) {
|
||||
val to = R.string.bugReportTo
|
||||
val subject = R.string.bugReportSubject
|
||||
showSendEmailScreen(to, subject, log)
|
||||
}
|
||||
|
||||
override fun showSettingsScreen() {
|
||||
val intent = intentFactory.startSettingsActivity(activity)
|
||||
activity.startActivityForResult(intent, REQUEST_SETTINGS)
|
||||
}
|
||||
|
||||
override fun showColorPicker(defaultColor: Int,
|
||||
callback: OnColorPickedCallback) {
|
||||
val picker = colorPickerFactory.create(defaultColor)
|
||||
picker.setListener(callback)
|
||||
activity.showDialog(picker, "picker")
|
||||
}
|
||||
|
||||
override fun showNumberPicker(value: Double,
|
||||
unit: String,
|
||||
callback: ListHabitsBehavior.NumberPickerCallback) {
|
||||
numberPickerFactory.create(value, unit, callback).show()
|
||||
}
|
||||
|
||||
@StringRes
|
||||
private fun getExecuteString(command: Command): Int? {
|
||||
when (command) {
|
||||
is ArchiveHabitsCommand -> return R.string.toast_habit_archived
|
||||
is ChangeHabitColorCommand -> return R.string.toast_habit_changed
|
||||
is CreateHabitCommand -> return R.string.toast_habit_created
|
||||
is DeleteHabitsCommand -> return R.string.toast_habit_deleted
|
||||
is EditHabitCommand -> return R.string.toast_habit_changed
|
||||
is UnarchiveHabitsCommand -> return R.string.toast_habit_unarchived
|
||||
else -> return null
|
||||
}
|
||||
}
|
||||
|
||||
private fun showCreateNumericalHabitScreen() {
|
||||
val dialog = editHabitDialogFactory.createNumerical()
|
||||
activity.showDialog(dialog, "editHabit")
|
||||
}
|
||||
|
||||
private fun onImportData(file: File, onFinished: () -> Unit) {
|
||||
taskRunner.execute(importTaskFactory.create(file) { result ->
|
||||
if (result == ImportDataTask.SUCCESS) {
|
||||
adapter.refresh()
|
||||
showMessage(R.string.habits_imported)
|
||||
} else if (result == ImportDataTask.NOT_RECOGNIZED) {
|
||||
showMessage(R.string.file_not_recognized)
|
||||
} else {
|
||||
showMessage(R.string.could_not_import)
|
||||
}
|
||||
onFinished()
|
||||
})
|
||||
}
|
||||
|
||||
private fun onExportDB() {
|
||||
taskRunner.execute(exportDBFactory.create { filename ->
|
||||
if (filename != null) showSendFileScreen(filename)
|
||||
else showMessage(R.string.could_not_export)
|
||||
})
|
||||
}
|
||||
}
|
@ -1,145 +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.activities.habits.list;
|
||||
|
||||
import android.support.annotation.*;
|
||||
import android.view.*;
|
||||
|
||||
import org.isoron.androidbase.activities.*;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.activities.habits.list.views.*;
|
||||
import org.isoron.uhabits.core.commands.*;
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
@ActivityScope
|
||||
public class ListHabitsSelectionMenu extends BaseSelectionMenu
|
||||
implements HabitCardListController.SelectionListener
|
||||
{
|
||||
@NonNull
|
||||
private final ListHabitsScreen screen;
|
||||
|
||||
@NonNull
|
||||
CommandRunner commandRunner;
|
||||
|
||||
private ListHabitsSelectionMenuBehavior behavior;
|
||||
|
||||
@NonNull
|
||||
private final HabitCardListAdapter listAdapter;
|
||||
|
||||
@Nullable
|
||||
private HabitCardListController listController;
|
||||
|
||||
@Inject
|
||||
public ListHabitsSelectionMenu(@NonNull ListHabitsScreen screen,
|
||||
@NonNull HabitCardListAdapter listAdapter,
|
||||
@NonNull CommandRunner commandRunner,
|
||||
@NonNull ListHabitsSelectionMenuBehavior behavior)
|
||||
{
|
||||
this.screen = screen;
|
||||
this.listAdapter = listAdapter;
|
||||
this.commandRunner = commandRunner;
|
||||
this.behavior = behavior;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish()
|
||||
{
|
||||
if (listController != null) listController.onSelectionFinished();
|
||||
super.onFinish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemClicked(@NonNull MenuItem item)
|
||||
{
|
||||
switch (item.getItemId())
|
||||
{
|
||||
case R.id.action_edit_habit:
|
||||
behavior.onEditHabits();
|
||||
return true;
|
||||
|
||||
case R.id.action_archive_habit:
|
||||
behavior.onArchiveHabits();
|
||||
return true;
|
||||
|
||||
case R.id.action_unarchive_habit:
|
||||
behavior.onUnarchiveHabits();
|
||||
return true;
|
||||
|
||||
case R.id.action_delete:
|
||||
behavior.onDeleteHabits();
|
||||
return true;
|
||||
|
||||
case R.id.action_color:
|
||||
behavior.onChangeColor();
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepare(@NonNull Menu menu)
|
||||
{
|
||||
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(behavior.canEdit());
|
||||
itemArchive.setVisible(behavior.canArchive());
|
||||
itemUnarchive.setVisible(behavior.canUnarchive());
|
||||
setTitle(Integer.toString(listAdapter.getSelected().size()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectionChange()
|
||||
{
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectionFinish()
|
||||
{
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectionStart()
|
||||
{
|
||||
screen.startSelection();
|
||||
}
|
||||
|
||||
public void setListController(HabitCardListController listController)
|
||||
{
|
||||
this.listController = listController;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getResourceId()
|
||||
{
|
||||
return R.menu.list_habits_selection;
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.activities.habits.list
|
||||
|
||||
import android.view.*
|
||||
import dagger.*
|
||||
import org.isoron.androidbase.activities.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.activities.habits.list.views.*
|
||||
import org.isoron.uhabits.core.commands.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*
|
||||
import javax.inject.*
|
||||
|
||||
@ActivityScope
|
||||
class ListHabitsSelectionMenu @Inject constructor(
|
||||
private val screen: ListHabitsScreen,
|
||||
private val listAdapter: HabitCardListAdapter,
|
||||
var commandRunner: CommandRunner,
|
||||
private val behavior: ListHabitsSelectionMenuBehavior,
|
||||
private val listController: Lazy<HabitCardListController>
|
||||
) : BaseSelectionMenu() {
|
||||
|
||||
override fun onFinish() {
|
||||
listController.get().onSelectionFinished()
|
||||
super.onFinish()
|
||||
}
|
||||
|
||||
override fun onItemClicked(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_edit_habit -> {
|
||||
behavior.onEditHabits()
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.action_archive_habit -> {
|
||||
behavior.onArchiveHabits()
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.action_unarchive_habit -> {
|
||||
behavior.onUnarchiveHabits()
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.action_delete -> {
|
||||
behavior.onDeleteHabits()
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.action_color -> {
|
||||
behavior.onChangeColor()
|
||||
return true
|
||||
}
|
||||
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPrepare(menu: Menu): Boolean {
|
||||
val itemEdit = menu.findItem(R.id.action_edit_habit)
|
||||
val itemColor = menu.findItem(R.id.action_color)
|
||||
val itemArchive = menu.findItem(R.id.action_archive_habit)
|
||||
val itemUnarchive = menu.findItem(R.id.action_unarchive_habit)
|
||||
|
||||
itemColor.isVisible = true
|
||||
itemEdit.isVisible = behavior.canEdit()
|
||||
itemArchive.isVisible = behavior.canArchive()
|
||||
itemUnarchive.isVisible = behavior.canUnarchive()
|
||||
setTitle(Integer.toString(listAdapter.selected.size))
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun onSelectionStart() = screen.startSelection()
|
||||
fun onSelectionChange() = invalidate()
|
||||
fun onSelectionFinish() = finish()
|
||||
override fun getResourceId() = R.menu.list_habits_selection
|
||||
}
|
@ -1,23 +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/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides acitivity for listing habits and related classes.
|
||||
*/
|
||||
package org.isoron.uhabits.activities.habits.list;
|
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Á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.activities.habits.list.views
|
||||
|
||||
import android.content.*
|
||||
import android.view.*
|
||||
import android.view.View.MeasureSpec.*
|
||||
import android.widget.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
|
||||
abstract class ButtonPanelView<T : View>(
|
||||
context: Context,
|
||||
val preferences: Preferences
|
||||
) : LinearLayout(context),
|
||||
Preferences.Listener {
|
||||
|
||||
var buttonCount = 0
|
||||
set(value) {
|
||||
field = value
|
||||
inflateButtons()
|
||||
}
|
||||
|
||||
var dataOffset = 0
|
||||
set(value) {
|
||||
field = value
|
||||
setupButtons()
|
||||
}
|
||||
|
||||
var buttons = mutableListOf<T>()
|
||||
|
||||
override fun onCheckmarkSequenceChanged() {
|
||||
inflateButtons()
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
protected fun inflateButtons() {
|
||||
val reverse = preferences.isCheckmarkSequenceReversed
|
||||
|
||||
buttons.clear()
|
||||
repeat(buttonCount) { buttons.add(createButton()) }
|
||||
|
||||
removeAllViews()
|
||||
if (reverse) buttons.reversed().forEach { addView(it) }
|
||||
else buttons.forEach { addView(it) }
|
||||
setupButtons()
|
||||
requestLayout()
|
||||
}
|
||||
|
||||
public override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
preferences.addListener(this)
|
||||
}
|
||||
|
||||
public override fun onDetachedFromWindow() {
|
||||
preferences.removeListener(this)
|
||||
super.onDetachedFromWindow()
|
||||
}
|
||||
|
||||
override fun onMeasure(widthSpec: Int, heightSpec: Int) {
|
||||
val buttonWidth = dim(R.dimen.checkmarkWidth)
|
||||
val buttonHeight = dim(R.dimen.checkmarkHeight)
|
||||
val width = (buttonWidth * buttonCount)
|
||||
super.onMeasure(width.toMeasureSpec(EXACTLY),
|
||||
buttonHeight.toMeasureSpec(EXACTLY))
|
||||
}
|
||||
|
||||
protected abstract fun setupButtons()
|
||||
protected abstract fun createButton(): T
|
||||
}
|
@ -1,194 +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.activities.habits.list.views;
|
||||
|
||||
import android.content.*;
|
||||
import android.content.res.*;
|
||||
import android.graphics.*;
|
||||
import android.support.annotation.*;
|
||||
import android.text.*;
|
||||
import android.util.*;
|
||||
import android.view.*;
|
||||
|
||||
import org.isoron.androidbase.utils.*;
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.activities.*;
|
||||
import org.isoron.uhabits.core.preferences.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
|
||||
import static android.view.View.MeasureSpec.EXACTLY;
|
||||
import static android.view.View.MeasureSpec.makeMeasureSpec;
|
||||
import static org.isoron.androidbase.utils.InterfaceUtils.getDimension;
|
||||
import static org.isoron.androidbase.utils.InterfaceUtils.getFontAwesome;
|
||||
import static org.isoron.uhabits.core.models.Checkmark.CHECKED_EXPLICITLY;
|
||||
import static org.isoron.uhabits.core.models.Checkmark.UNCHECKED;
|
||||
import static org.isoron.uhabits.utils.AttributeSetUtils.getIntAttribute;
|
||||
|
||||
public class CheckmarkButtonView extends View
|
||||
{
|
||||
private int color;
|
||||
|
||||
private int value;
|
||||
|
||||
private StyledResources styledRes;
|
||||
|
||||
private TextPaint paint;
|
||||
|
||||
private int lowContrastColor;
|
||||
|
||||
private RectF rect;
|
||||
|
||||
@Nullable
|
||||
private Preferences prefs;
|
||||
|
||||
@NonNull
|
||||
private OnToggleListener onToggleListener;
|
||||
|
||||
@NonNull
|
||||
private OnInvalidToggleListener onInvalidToggleListener;
|
||||
|
||||
public CheckmarkButtonView(@Nullable Context context)
|
||||
{
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public CheckmarkButtonView(@Nullable Context ctx, @Nullable AttributeSet attrs)
|
||||
{
|
||||
super(ctx, attrs);
|
||||
init();
|
||||
|
||||
if(ctx == null) throw new IllegalStateException();
|
||||
if(attrs == null) throw new IllegalStateException();
|
||||
|
||||
int paletteColor = getIntAttribute(ctx, attrs, "color", 0);
|
||||
setColor(PaletteUtils.getAndroidTestColor(paletteColor));
|
||||
int value = getIntAttribute(ctx, attrs, "value", 0);
|
||||
setValue(value);
|
||||
}
|
||||
|
||||
public void setColor(int color)
|
||||
{
|
||||
this.color = color;
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
public void setValue(int value)
|
||||
{
|
||||
this.value = value;
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
public void performToggle()
|
||||
{
|
||||
onToggleListener.onToggle();
|
||||
value = (value == CHECKED_EXPLICITLY ? UNCHECKED : CHECKED_EXPLICITLY);
|
||||
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas)
|
||||
{
|
||||
super.onDraw(canvas);
|
||||
Resources resources = getResources();
|
||||
|
||||
paint.setColor(value == CHECKED_EXPLICITLY ? color : lowContrastColor);
|
||||
int id = (value == UNCHECKED ? R.string.fa_times : R.string.fa_check);
|
||||
String label = resources.getString(id);
|
||||
float em = paint.measureText("m");
|
||||
|
||||
rect.set(0, 0, getWidth(), getHeight());
|
||||
rect.offset(0, 0.4f * em);
|
||||
canvas.drawText(label, rect.centerX(), rect.centerY(), paint);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
||||
{
|
||||
Resources res = getResources();
|
||||
int height = res.getDimensionPixelSize(R.dimen.checkmarkHeight);
|
||||
int width = res.getDimensionPixelSize(R.dimen.checkmarkWidth);
|
||||
|
||||
widthMeasureSpec = makeMeasureSpec(width, EXACTLY);
|
||||
heightMeasureSpec = makeMeasureSpec(height, EXACTLY);
|
||||
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setFocusable(false);
|
||||
|
||||
styledRes = new StyledResources(getContext());
|
||||
|
||||
paint = new TextPaint();
|
||||
paint.setTypeface(getFontAwesome(getContext()));
|
||||
paint.setAntiAlias(true);
|
||||
paint.setTextAlign(Paint.Align.CENTER);
|
||||
paint.setTextSize(getDimension(getContext(), R.dimen.smallTextSize));
|
||||
|
||||
rect = new RectF();
|
||||
color = Color.BLACK;
|
||||
lowContrastColor = styledRes.getColor(R.attr.lowContrastTextColor);
|
||||
|
||||
onToggleListener = () -> {};
|
||||
onInvalidToggleListener = () -> {};
|
||||
|
||||
if (getContext() instanceof HabitsActivity)
|
||||
{
|
||||
HabitsApplicationComponent component =
|
||||
((HabitsActivity) getContext()).getAppComponent();
|
||||
prefs = component.getPreferences();
|
||||
}
|
||||
|
||||
setOnClickListener((v) -> {
|
||||
if (prefs == null) return;
|
||||
if (prefs.isShortToggleEnabled()) performToggle();
|
||||
else onInvalidToggleListener.onInvalidToggle();
|
||||
});
|
||||
|
||||
setOnLongClickListener(v -> {
|
||||
performToggle();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public void setOnInvalidToggleListener(
|
||||
@NonNull OnInvalidToggleListener onInvalidToggleListener)
|
||||
{
|
||||
this.onInvalidToggleListener = onInvalidToggleListener;
|
||||
}
|
||||
|
||||
public void setOnToggleListener(@NonNull OnToggleListener onToggleListener)
|
||||
{
|
||||
this.onToggleListener = onToggleListener;
|
||||
}
|
||||
|
||||
public interface OnInvalidToggleListener
|
||||
{
|
||||
void onInvalidToggle();
|
||||
}
|
||||
|
||||
public interface OnToggleListener
|
||||
{
|
||||
void onToggle();
|
||||
}
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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.activities.habits.list.views
|
||||
|
||||
import android.content.*
|
||||
import android.graphics.*
|
||||
import android.text.*
|
||||
import android.view.*
|
||||
import android.view.View.MeasureSpec.*
|
||||
import com.google.auto.factory.*
|
||||
import org.isoron.androidbase.activities.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.core.models.Checkmark.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
|
||||
@AutoFactory
|
||||
class CheckmarkButtonView(
|
||||
@Provided @ActivityContext context: Context,
|
||||
@Provided val preferences: Preferences
|
||||
) : View(context),
|
||||
View.OnClickListener,
|
||||
View.OnLongClickListener {
|
||||
|
||||
var color: Int = Color.BLACK
|
||||
set(value) {
|
||||
field = value
|
||||
invalidate()
|
||||
}
|
||||
|
||||
var value: Int = 0
|
||||
set(value) {
|
||||
field = value
|
||||
invalidate()
|
||||
}
|
||||
|
||||
var onToggle: () -> Unit = {}
|
||||
private var drawer = Drawer()
|
||||
|
||||
init {
|
||||
isFocusable = false
|
||||
setOnClickListener(this)
|
||||
setOnLongClickListener(this)
|
||||
}
|
||||
|
||||
fun performToggle() {
|
||||
onToggle()
|
||||
value = when (value) {
|
||||
CHECKED_EXPLICITLY -> UNCHECKED
|
||||
else -> CHECKED_EXPLICITLY
|
||||
}
|
||||
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
||||
invalidate()
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
if (preferences.isShortToggleEnabled) performToggle()
|
||||
else showMessage(R.string.long_press_to_toggle)
|
||||
}
|
||||
|
||||
override fun onLongClick(v: View): Boolean {
|
||||
performToggle()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
super.onDraw(canvas)
|
||||
drawer.draw(canvas)
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
val height = resources.getDimensionPixelSize(R.dimen.checkmarkHeight)
|
||||
val width = resources.getDimensionPixelSize(R.dimen.checkmarkWidth)
|
||||
super.onMeasure(width.toMeasureSpec(EXACTLY),
|
||||
height.toMeasureSpec(EXACTLY))
|
||||
}
|
||||
|
||||
private inner class Drawer {
|
||||
private val rect = RectF()
|
||||
private val lowContrastColor = sres.getColor(R.attr.lowContrastTextColor)
|
||||
|
||||
private val paint = TextPaint().apply {
|
||||
typeface = getFontAwesome()
|
||||
isAntiAlias = true
|
||||
textAlign = Paint.Align.CENTER
|
||||
textSize = dim(R.dimen.smallTextSize)
|
||||
}
|
||||
|
||||
fun draw(canvas: Canvas) {
|
||||
paint.color = when (value) {
|
||||
CHECKED_EXPLICITLY -> color
|
||||
else -> lowContrastColor
|
||||
}
|
||||
val id = when (value) {
|
||||
UNCHECKED -> R.string.fa_times
|
||||
else -> R.string.fa_check
|
||||
}
|
||||
val label = resources.getString(id)
|
||||
val em = paint.measureText("m")
|
||||
|
||||
rect.set(0f, 0f, width.toFloat(), height.toFloat())
|
||||
rect.offset(0f, 0.4f * em)
|
||||
canvas.drawText(label, rect.centerX(), rect.centerY(), paint)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,243 +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.activities.habits.list.views;
|
||||
|
||||
import android.content.*;
|
||||
import android.support.annotation.*;
|
||||
import android.util.*;
|
||||
import android.widget.*;
|
||||
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.core.preferences.*;
|
||||
import org.isoron.uhabits.core.utils.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static android.view.View.MeasureSpec.EXACTLY;
|
||||
import static android.view.View.MeasureSpec.makeMeasureSpec;
|
||||
import static org.isoron.androidbase.utils.InterfaceUtils.getDimension;
|
||||
import static org.isoron.uhabits.utils.AttributeSetUtils.getIntAttribute;
|
||||
import static org.isoron.uhabits.utils.PaletteUtils.getAndroidTestColor;
|
||||
|
||||
public class CheckmarkPanelView extends LinearLayout
|
||||
implements Preferences.Listener
|
||||
{
|
||||
private static final int LEFT_TO_RIGHT = 0;
|
||||
|
||||
private static final int RIGHT_TO_LEFT = 1;
|
||||
|
||||
@Nullable
|
||||
private Preferences prefs;
|
||||
|
||||
private int values[];
|
||||
|
||||
private int nButtons;
|
||||
|
||||
private int color;
|
||||
|
||||
private int dataOffset;
|
||||
|
||||
@NonNull
|
||||
private OnInvalidToggleListener onInvalidToggleListener;
|
||||
|
||||
@NonNull
|
||||
private OnToggleListener onToggleLister;
|
||||
|
||||
public CheckmarkPanelView(Context context)
|
||||
{
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public CheckmarkPanelView(Context ctx, AttributeSet attrs)
|
||||
{
|
||||
super(ctx, attrs);
|
||||
init();
|
||||
|
||||
if (ctx != null && attrs != null)
|
||||
{
|
||||
int paletteColor = getIntAttribute(ctx, attrs, "color", 0);
|
||||
setColor(getAndroidTestColor(paletteColor));
|
||||
setButtonCount(getIntAttribute(ctx, attrs, "button_count", 5));
|
||||
}
|
||||
|
||||
if (isInEditMode()) initEditMode();
|
||||
}
|
||||
|
||||
public CheckmarkButtonView indexToButton(int i)
|
||||
{
|
||||
int position = i;
|
||||
|
||||
if (getCheckmarkOrder() == RIGHT_TO_LEFT) position = nButtons - i - 1;
|
||||
|
||||
return (CheckmarkButtonView) getChildAt(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCheckmarkOrderChanged()
|
||||
{
|
||||
setupButtons();
|
||||
}
|
||||
|
||||
public void setButtonCount(int newButtonCount)
|
||||
{
|
||||
if (nButtons != newButtonCount)
|
||||
{
|
||||
nButtons = newButtonCount;
|
||||
addButtons();
|
||||
}
|
||||
|
||||
setupButtons();
|
||||
}
|
||||
|
||||
public void setColor(int color)
|
||||
{
|
||||
this.color = color;
|
||||
setupButtons();
|
||||
}
|
||||
|
||||
public void setDataOffset(int dataOffset)
|
||||
{
|
||||
this.dataOffset = dataOffset;
|
||||
setupButtons();
|
||||
}
|
||||
|
||||
public void setOnInvalidToggleListener(
|
||||
@NonNull OnInvalidToggleListener onInvalidToggleListener)
|
||||
{
|
||||
this.onInvalidToggleListener = onInvalidToggleListener;
|
||||
}
|
||||
|
||||
public void setOnToggleLister(@NonNull OnToggleListener onToggleLister)
|
||||
{
|
||||
this.onToggleLister = onToggleLister;
|
||||
}
|
||||
|
||||
public void setValues(int[] values)
|
||||
{
|
||||
this.values = values;
|
||||
setupButtons();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow()
|
||||
{
|
||||
super.onAttachedToWindow();
|
||||
if (prefs != null) prefs.addListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow()
|
||||
{
|
||||
if (prefs != null) prefs.removeListener(this);
|
||||
super.onDetachedFromWindow();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthSpec, int heightSpec)
|
||||
{
|
||||
float buttonWidth = getDimension(getContext(), R.dimen.checkmarkWidth);
|
||||
float buttonHeight =
|
||||
getDimension(getContext(), R.dimen.checkmarkHeight);
|
||||
|
||||
float width = buttonWidth * nButtons;
|
||||
|
||||
widthSpec = makeMeasureSpec((int) width, EXACTLY);
|
||||
heightSpec = makeMeasureSpec((int) buttonHeight, EXACTLY);
|
||||
|
||||
super.onMeasure(widthSpec, heightSpec);
|
||||
}
|
||||
|
||||
private void addButtons()
|
||||
{
|
||||
removeAllViews();
|
||||
|
||||
for (int i = 0; i < nButtons; i++)
|
||||
addView(new CheckmarkButtonView(getContext()));
|
||||
}
|
||||
|
||||
private int getCheckmarkOrder()
|
||||
{
|
||||
if (prefs == null) return LEFT_TO_RIGHT;
|
||||
return prefs.shouldReverseCheckmarks() ? RIGHT_TO_LEFT : LEFT_TO_RIGHT;
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
Context appContext = getContext().getApplicationContext();
|
||||
if (appContext instanceof HabitsApplication)
|
||||
{
|
||||
HabitsApplication app = (HabitsApplication) appContext;
|
||||
prefs = app.getComponent().getPreferences();
|
||||
}
|
||||
|
||||
onInvalidToggleListener = () -> {};
|
||||
onToggleLister = (t) -> {};
|
||||
|
||||
setWillNotDraw(false);
|
||||
values = new int[0];
|
||||
}
|
||||
|
||||
private void initEditMode()
|
||||
{
|
||||
int values[] = new int[nButtons];
|
||||
|
||||
for (int i = 0; i < nButtons; i++)
|
||||
values[i] = Math.min(2, new Random().nextInt(4));
|
||||
|
||||
setValues(values);
|
||||
}
|
||||
|
||||
private void setupButtonControllers(long timestamp,
|
||||
CheckmarkButtonView buttonView)
|
||||
{
|
||||
buttonView.setOnInvalidToggleListener(
|
||||
() -> onInvalidToggleListener.onInvalidToggle());
|
||||
buttonView.setOnToggleListener(
|
||||
() -> onToggleLister.onToggle(timestamp));
|
||||
}
|
||||
|
||||
private void setupButtons()
|
||||
{
|
||||
long timestamp = DateUtils.getStartOfToday();
|
||||
long day = DateUtils.millisecondsInOneDay;
|
||||
timestamp -= day * dataOffset;
|
||||
|
||||
for (int i = 0; i < nButtons; i++)
|
||||
{
|
||||
CheckmarkButtonView buttonView = indexToButton(i);
|
||||
if (i + dataOffset >= values.length) break;
|
||||
buttonView.setValue(values[i + dataOffset]);
|
||||
buttonView.setColor(color);
|
||||
setupButtonControllers(timestamp, buttonView);
|
||||
timestamp -= day;
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnInvalidToggleListener
|
||||
{
|
||||
void onInvalidToggle();
|
||||
}
|
||||
|
||||
public interface OnToggleListener
|
||||
{
|
||||
void onToggle(long timestamp);
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.activities.habits.list.views
|
||||
|
||||
import android.content.*
|
||||
import com.google.auto.factory.*
|
||||
import org.isoron.androidbase.activities.*
|
||||
import org.isoron.uhabits.core.models.Checkmark.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.core.utils.*
|
||||
|
||||
@AutoFactory
|
||||
class CheckmarkPanelView(
|
||||
@Provided @ActivityContext context: Context,
|
||||
@Provided preferences: Preferences,
|
||||
@Provided private val buttonFactory: CheckmarkButtonViewFactory
|
||||
) : ButtonPanelView<CheckmarkButtonView>(context, preferences) {
|
||||
|
||||
var values = IntArray(0)
|
||||
set(values) {
|
||||
field = values
|
||||
setupButtons()
|
||||
}
|
||||
|
||||
var color = 0
|
||||
set(value) {
|
||||
field = value
|
||||
setupButtons()
|
||||
}
|
||||
|
||||
var onToggle: (Long) -> Unit = {}
|
||||
set(value) {
|
||||
field = value
|
||||
setupButtons()
|
||||
}
|
||||
|
||||
override fun createButton(): CheckmarkButtonView = buttonFactory.create()
|
||||
|
||||
@Synchronized
|
||||
override fun setupButtons() {
|
||||
val today = DateUtils.getStartOfToday()
|
||||
val day = DateUtils.millisecondsInOneDay
|
||||
|
||||
buttons.forEachIndexed { index, button ->
|
||||
val timestamp = today - (index + dataOffset) * day
|
||||
button.value = when {
|
||||
index + dataOffset < values.size -> values[index + dataOffset]
|
||||
else -> UNCHECKED
|
||||
}
|
||||
button.color = color
|
||||
button.onToggle = { onToggle(timestamp) }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Á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.activities.habits.list.views
|
||||
|
||||
import android.content.*
|
||||
import android.view.*
|
||||
import android.view.Gravity.*
|
||||
import android.view.ViewGroup.LayoutParams.*
|
||||
import android.widget.*
|
||||
import org.isoron.androidbase.activities.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.utils.*
|
||||
|
||||
class EmptyListView(context: Context) : LinearLayout(context) {
|
||||
init {
|
||||
orientation = VERTICAL
|
||||
gravity = Gravity.CENTER
|
||||
visibility = BaseRootView.GONE
|
||||
|
||||
addView(TextView(context).apply {
|
||||
text = str(R.string.fa_star_half_o)
|
||||
typeface = getFontAwesome()
|
||||
textSize = sp(40.0f)
|
||||
gravity = CENTER
|
||||
setTextColor(sres.getColor(R.attr.mediumContrastTextColor))
|
||||
}, MATCH_PARENT, WRAP_CONTENT)
|
||||
|
||||
addView(TextView(context).apply {
|
||||
text = str(R.string.no_habits_found)
|
||||
gravity = CENTER
|
||||
setPadding(0, dp(20.0f).toInt(), 0, 0)
|
||||
setTextColor(sres.getColor(R.attr.mediumContrastTextColor))
|
||||
}, MATCH_PARENT, WRAP_CONTENT)
|
||||
}
|
||||
}
|
@ -1,334 +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.activities.habits.list.views;
|
||||
|
||||
import android.support.annotation.*;
|
||||
|
||||
import org.isoron.androidbase.activities.*;
|
||||
import org.isoron.uhabits.core.models.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
/**
|
||||
* Controller responsible for receiving and processing the events generated by a
|
||||
* HabitListView. These include selecting and reordering items, toggling
|
||||
* checkmarks and clicking habits.
|
||||
*/
|
||||
@ActivityScope
|
||||
public class HabitCardListController implements HabitCardListView.Controller,
|
||||
ModelObservable.Listener
|
||||
{
|
||||
private final Mode NORMAL_MODE = new NormalMode();
|
||||
|
||||
private final Mode SELECTION_MODE = new SelectionMode();
|
||||
|
||||
@NonNull
|
||||
private final HabitCardListAdapter adapter;
|
||||
|
||||
@Nullable
|
||||
private HabitListener habitListener;
|
||||
|
||||
@Nullable
|
||||
private SelectionListener selectionListener;
|
||||
|
||||
@NonNull
|
||||
private Mode activeMode;
|
||||
|
||||
@Inject
|
||||
public HabitCardListController(@NonNull HabitCardListAdapter adapter)
|
||||
{
|
||||
this.adapter = adapter;
|
||||
this.activeMode = new NormalMode();
|
||||
adapter.getObservable().addListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.performReorder(from, to);
|
||||
|
||||
if (habitListener != null)
|
||||
habitListener.onHabitReorder(habitFrom, habitTo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEdit(@NonNull Habit habit, long timestamp)
|
||||
{
|
||||
if (habitListener != null) habitListener.onEdit(habit, timestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInvalidEdit()
|
||||
{
|
||||
if (habitListener != null) habitListener.onInvalidEdit();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModelChange()
|
||||
{
|
||||
if(adapter.isSelectionEmpty())
|
||||
{
|
||||
activeMode = new NormalMode();
|
||||
if (selectionListener != null) selectionListener.onSelectionFinish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the selection operation is cancelled externally, by something
|
||||
* other than this controller. This happens, for example, when the user
|
||||
* presses the back button.
|
||||
*/
|
||||
public void onSelectionFinished()
|
||||
{
|
||||
cancelSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(@NonNull 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks all items as not selected and finishes the selection operation.
|
||||
*/
|
||||
private void cancelSelection()
|
||||
{
|
||||
adapter.clearSelection();
|
||||
activeMode = new NormalMode();
|
||||
if (selectionListener != null) selectionListener.onSelectionFinish();
|
||||
}
|
||||
|
||||
public interface HabitListener
|
||||
{
|
||||
void onEdit(Habit habit, long timestamp);
|
||||
|
||||
/**
|
||||
* Called when the user clicks a habit.
|
||||
*
|
||||
* @param habit the habit clicked
|
||||
*/
|
||||
void onHabitClick(@NonNull 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(@NonNull Habit from, @NonNull Habit to);
|
||||
|
||||
void onInvalidEdit();
|
||||
|
||||
void onInvalidToggle();
|
||||
|
||||
void onToggle(Habit habit, long timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (selectionListener == null) return;
|
||||
|
||||
if (activeMode == SELECTION_MODE)
|
||||
selectionListener.onSelectionChange();
|
||||
else selectionListener.onSelectionFinish();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* 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.activities.habits.list.views
|
||||
|
||||
import dagger.*
|
||||
import org.isoron.androidbase.activities.*
|
||||
import org.isoron.uhabits.activities.habits.list.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.*
|
||||
import javax.inject.*
|
||||
|
||||
/**
|
||||
* Controller responsible for receiving and processing the events generated by a
|
||||
* HabitListView. These include selecting and reordering items, toggling
|
||||
* checkmarks and clicking habits.
|
||||
*/
|
||||
@ActivityScope
|
||||
class HabitCardListController @Inject constructor(
|
||||
private val adapter: HabitCardListAdapter,
|
||||
private val behavior: ListHabitsBehavior,
|
||||
private val selectionMenu: Lazy<ListHabitsSelectionMenu>
|
||||
) : HabitCardListView.Controller, ModelObservable.Listener {
|
||||
|
||||
private val NORMAL_MODE = NormalMode()
|
||||
private val SELECTION_MODE = SelectionMode()
|
||||
private var activeMode: Mode
|
||||
|
||||
init {
|
||||
this.activeMode = NORMAL_MODE
|
||||
adapter.observable.addListener(this)
|
||||
}
|
||||
|
||||
override fun drop(from: Int, to: Int) {
|
||||
if (from == to) return
|
||||
cancelSelection()
|
||||
|
||||
val habitFrom = adapter.getItem(from)
|
||||
val habitTo = adapter.getItem(to)
|
||||
if(habitFrom == null || habitTo == null) return
|
||||
|
||||
adapter.performReorder(from, to)
|
||||
behavior.onReorderHabit(habitFrom, habitTo)
|
||||
}
|
||||
|
||||
override fun onItemClick(position: Int) {
|
||||
activeMode.onItemClick(position)
|
||||
}
|
||||
|
||||
override fun onItemLongClick(position: Int) {
|
||||
activeMode.onItemLongClick(position)
|
||||
}
|
||||
|
||||
override fun onModelChange() {
|
||||
if (adapter.isSelectionEmpty) {
|
||||
activeMode = NormalMode()
|
||||
selectionMenu.get().onSelectionFinish()
|
||||
}
|
||||
}
|
||||
|
||||
fun onSelectionFinished() {
|
||||
cancelSelection()
|
||||
}
|
||||
|
||||
override fun startDrag(position: Int) {
|
||||
activeMode.startDrag(position)
|
||||
}
|
||||
|
||||
protected fun toggleSelection(position: Int) {
|
||||
adapter.toggleSelection(position)
|
||||
activeMode = if (adapter.isSelectionEmpty) NORMAL_MODE else SELECTION_MODE
|
||||
}
|
||||
|
||||
private fun cancelSelection() {
|
||||
adapter.clearSelection()
|
||||
activeMode = NormalMode()
|
||||
selectionMenu.get().onSelectionFinish()
|
||||
}
|
||||
|
||||
interface HabitListener {
|
||||
fun onHabitClick(habit: Habit)
|
||||
fun onHabitReorder(from: Habit, to: Habit)
|
||||
}
|
||||
|
||||
/**
|
||||
* A Mode describes the behavior 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 {
|
||||
fun onItemClick(position: Int)
|
||||
fun onItemLongClick(position: Int): Boolean
|
||||
fun startDrag(position: Int)
|
||||
}
|
||||
|
||||
/**
|
||||
* Mode activated when there are no items selected. Clicks trigger habit
|
||||
* click. Long clicks start selection.
|
||||
*/
|
||||
internal inner class NormalMode : Mode {
|
||||
override fun onItemClick(position: Int) {
|
||||
val habit = adapter.getItem(position)
|
||||
if (habit == null) return
|
||||
behavior.onClickHabit(habit)
|
||||
}
|
||||
|
||||
override fun onItemLongClick(position: Int): Boolean {
|
||||
startSelection(position)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun startDrag(position: Int) {
|
||||
startSelection(position)
|
||||
}
|
||||
|
||||
protected fun startSelection(position: Int) {
|
||||
toggleSelection(position)
|
||||
activeMode = SELECTION_MODE
|
||||
selectionMenu.get().onSelectionStart()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mode activated when some items are already selected. Clicks toggle
|
||||
* item selection. Long clicks select more items.
|
||||
*/
|
||||
internal inner class SelectionMode : Mode {
|
||||
override fun onItemClick(position: Int) {
|
||||
toggleSelection(position)
|
||||
notifyListener()
|
||||
}
|
||||
|
||||
override fun onItemLongClick(position: Int): Boolean {
|
||||
toggleSelection(position)
|
||||
notifyListener()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun startDrag(position: Int) {
|
||||
toggleSelection(position)
|
||||
notifyListener()
|
||||
}
|
||||
|
||||
protected fun notifyListener() {
|
||||
if (activeMode === SELECTION_MODE)
|
||||
selectionMenu.get().onSelectionChange()
|
||||
else
|
||||
selectionMenu.get().onSelectionFinish()
|
||||
}
|
||||
}
|
||||
}
|