mirror of https://github.com/iSoron/uhabits.git
parent
3ffa079e24
commit
7e8a2a0c1c
After Width: | Height: | Size: 551 B |
After Width: | Height: | Size: 505 B |
After Width: | Height: | Size: 559 B |
After Width: | Height: | Size: 1.4 KiB |
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import dagger.Component;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Component(modules = {AndroidModule.class})
|
||||||
|
public interface AndroidTestComponent extends BaseComponent
|
||||||
|
{
|
||||||
|
void inject(BaseAndroidTest baseAndroidTest);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,187 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.ui.habits.list.view;
|
||||||
|
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.models.Checkmark;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.views.CheckmarkButtonView;
|
||||||
|
import org.isoron.uhabits.unit.views.ViewTest;
|
||||||
|
import org.isoron.uhabits.utils.ColorUtils;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class CheckmarkButtonViewTest extends ViewTest
|
||||||
|
{
|
||||||
|
public static final String PATH = "ui/habits/list/CheckmarkButtonView/";
|
||||||
|
|
||||||
|
private CountDownLatch latch;
|
||||||
|
private CheckmarkButtonView view;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp()
|
||||||
|
{
|
||||||
|
super.setUp();
|
||||||
|
setSimilarityCutoff(0.03f);
|
||||||
|
|
||||||
|
latch = new CountDownLatch(1);
|
||||||
|
view = new CheckmarkButtonView(targetContext);
|
||||||
|
view.setValue(Checkmark.UNCHECKED);
|
||||||
|
view.setColor(ColorUtils.CSV_PALETTE[7]);
|
||||||
|
|
||||||
|
measureView(dpToPixels(40), dpToPixels(40), view);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void assertRendersCheckedExplicitly() throws IOException
|
||||||
|
{
|
||||||
|
assertRenders(view, PATH + "render_explicit_check.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void assertRendersUnchecked() throws IOException
|
||||||
|
{
|
||||||
|
assertRenders(view, PATH + "render_unchecked.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void assertRendersCheckedImplicitly() throws IOException
|
||||||
|
{
|
||||||
|
assertRenders(view, PATH + "render_implicit_check.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender_unchecked() throws Exception
|
||||||
|
{
|
||||||
|
view.setValue(Checkmark.UNCHECKED);
|
||||||
|
assertRendersUnchecked();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender_explicitCheck() throws Exception
|
||||||
|
{
|
||||||
|
view.setValue(Checkmark.CHECKED_EXPLICITLY);
|
||||||
|
assertRendersCheckedExplicitly();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender_implicitCheck() throws Exception
|
||||||
|
{
|
||||||
|
view.setValue(Checkmark.CHECKED_IMPLICITLY);
|
||||||
|
assertRendersCheckedImplicitly();
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Test
|
||||||
|
// public void testLongClick() throws Exception
|
||||||
|
// {
|
||||||
|
// setOnToggleListener();
|
||||||
|
// view.performLongClick();
|
||||||
|
// waitForLatch();
|
||||||
|
// assertRendersCheckedExplicitly();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Test
|
||||||
|
// public void testClick_withShortToggle_fromUnchecked() throws Exception
|
||||||
|
// {
|
||||||
|
// Preferences.getInstance().setShortToggleEnabled(true);
|
||||||
|
// view.setValue(Checkmark.UNCHECKED);
|
||||||
|
// setOnToggleListenerAndPerformClick();
|
||||||
|
// assertRendersCheckedExplicitly();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Test
|
||||||
|
// public void testClick_withShortToggle_fromChecked() throws Exception
|
||||||
|
// {
|
||||||
|
// Preferences.getInstance().setShortToggleEnabled(true);
|
||||||
|
// view.setValue(Checkmark.CHECKED_EXPLICITLY);
|
||||||
|
// setOnToggleListenerAndPerformClick();
|
||||||
|
// assertRendersUnchecked();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Test
|
||||||
|
// public void testClick_withShortToggle_withoutListener() throws Exception
|
||||||
|
// {
|
||||||
|
// Preferences.getInstance().setShortToggleEnabled(true);
|
||||||
|
// view.setValue(Checkmark.CHECKED_EXPLICITLY);
|
||||||
|
// view.setController(null);
|
||||||
|
// view.performClick();
|
||||||
|
// assertRendersUnchecked();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// protected void setOnToggleListenerAndPerformClick() throws InterruptedException
|
||||||
|
// {
|
||||||
|
// setOnToggleListener();
|
||||||
|
// view.performClick();
|
||||||
|
// waitForLatch();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Test
|
||||||
|
// public void testClick_withoutShortToggle() throws Exception
|
||||||
|
// {
|
||||||
|
// Preferences.getInstance().setShortToggleEnabled(false);
|
||||||
|
// setOnInvalidToggleListener();
|
||||||
|
// view.performClick();
|
||||||
|
// waitForLatch();
|
||||||
|
// assertRendersUnchecked();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// protected void setOnInvalidToggleListener()
|
||||||
|
// {
|
||||||
|
// view.setController(new CheckmarkButtonView.Controller()
|
||||||
|
// {
|
||||||
|
// @Override
|
||||||
|
// public void onToggleCheckmark(CheckmarkButtonView view, long timestamp)
|
||||||
|
// {
|
||||||
|
// fail();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public void onInvalidToggle(CheckmarkButtonView v)
|
||||||
|
// {
|
||||||
|
// assertThat(v, equalTo(view));
|
||||||
|
// latch.countDown();
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// protected void setOnToggleListener()
|
||||||
|
// {
|
||||||
|
// view.setController(new CheckmarkButtonView.Controller()
|
||||||
|
// {
|
||||||
|
// @Override
|
||||||
|
// public void onToggleCheckmark(CheckmarkButtonView v, long t)
|
||||||
|
// {
|
||||||
|
// assertThat(v, equalTo(view));
|
||||||
|
// assertThat(t, equalTo(DateUtils.getStartOfToday()));
|
||||||
|
// latch.countDown();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public void onInvalidToggle(CheckmarkButtonView view)
|
||||||
|
// {
|
||||||
|
// fail();
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
}
|
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.ui.habits.list.view;
|
||||||
|
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.models.Checkmark;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.views.CheckmarkPanelView;
|
||||||
|
import org.isoron.uhabits.unit.views.ViewTest;
|
||||||
|
import org.isoron.uhabits.utils.ColorUtils;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class CheckmarkPanelViewTest extends ViewTest
|
||||||
|
{
|
||||||
|
public static final String PATH = "ui/habits/list/CheckmarkPanelView/";
|
||||||
|
|
||||||
|
private CountDownLatch latch;
|
||||||
|
private CheckmarkPanelView view;
|
||||||
|
private int checkmarks[];
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Before
|
||||||
|
public void setUp()
|
||||||
|
{
|
||||||
|
super.setUp();
|
||||||
|
setSimilarityCutoff(0.03f);
|
||||||
|
prefs.setShouldReverseCheckmarks(false);
|
||||||
|
|
||||||
|
Habit habit = new Habit();
|
||||||
|
|
||||||
|
latch = new CountDownLatch(1);
|
||||||
|
checkmarks = new int[]{Checkmark.CHECKED_EXPLICITLY, Checkmark.UNCHECKED,
|
||||||
|
Checkmark.CHECKED_IMPLICITLY, Checkmark.CHECKED_EXPLICITLY};
|
||||||
|
|
||||||
|
view = new CheckmarkPanelView(targetContext);
|
||||||
|
view.setHabit(habit);
|
||||||
|
view.setCheckmarkValues(checkmarks);
|
||||||
|
view.setColor(ColorUtils.CSV_PALETTE[7]);
|
||||||
|
|
||||||
|
measureView(dpToPixels(200), dpToPixels(200), view);
|
||||||
|
}
|
||||||
|
|
||||||
|
// protected void waitForLatch() throws InterruptedException
|
||||||
|
// {
|
||||||
|
// assertTrue("Latch timeout", latch.await(1, TimeUnit.SECONDS));
|
||||||
|
// }
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender() throws Exception
|
||||||
|
{
|
||||||
|
assertRenders(view, PATH + "render.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Test
|
||||||
|
// public void testToggleCheckmark_withLeftToRight() throws Exception
|
||||||
|
// {
|
||||||
|
// setToggleListener();
|
||||||
|
// view.getButton(1).performToggle();
|
||||||
|
// waitForLatch();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Test
|
||||||
|
// public void testToggleCheckmark_withReverseCheckmarks() throws Exception
|
||||||
|
// {
|
||||||
|
// prefs.setShouldReverseCheckmarks(true);
|
||||||
|
// view.setCheckmarkValues(checkmarks); // refresh after preference change
|
||||||
|
//
|
||||||
|
// setToggleListener();
|
||||||
|
// view.getButton(2).performToggle();
|
||||||
|
// waitForLatch();
|
||||||
|
// }
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import dagger.Component;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Component(modules = {AndroidModule.class})
|
||||||
|
public interface AndroidComponent extends BaseComponent
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.commands.CommandRunner;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.model.HabitCardListCache;
|
||||||
|
import org.isoron.uhabits.utils.Preferences;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
public class AndroidModule
|
||||||
|
{
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
Preferences providePreferences()
|
||||||
|
{
|
||||||
|
return new Preferences();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
CommandRunner provideCommandRunner()
|
||||||
|
{
|
||||||
|
return new CommandRunner();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
HabitCardListCache provideHabitCardListCache()
|
||||||
|
{
|
||||||
|
return new HabitCardListCache();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.tasks.ToggleRepetitionTask;
|
||||||
|
import org.isoron.uhabits.ui.habits.edit.BaseDialogFragment;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.ListHabitsSelectionMenu;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.model.HabitCardListAdapter;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.model.HabitCardListCache;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.ListHabitsController;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.controllers.CheckmarkButtonController;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.model.HintList;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.views.CheckmarkPanelView;
|
||||||
|
|
||||||
|
public interface BaseComponent
|
||||||
|
{
|
||||||
|
void inject(CheckmarkButtonController checkmarkButtonController);
|
||||||
|
|
||||||
|
void inject(ListHabitsController listHabitsController);
|
||||||
|
|
||||||
|
void inject(CheckmarkPanelView checkmarkPanelView);
|
||||||
|
|
||||||
|
void inject(ToggleRepetitionTask toggleRepetitionTask);
|
||||||
|
|
||||||
|
void inject(BaseDialogFragment baseDialogFragment);
|
||||||
|
|
||||||
|
void inject(HabitCardListCache habitCardListCache);
|
||||||
|
|
||||||
|
void inject(HabitBroadcastReceiver habitBroadcastReceiver);
|
||||||
|
|
||||||
|
void inject(ListHabitsSelectionMenu listHabitsSelectionMenu);
|
||||||
|
|
||||||
|
void inject(HintList hintList);
|
||||||
|
|
||||||
|
void inject(HabitCardListAdapter habitCardListAdapter);
|
||||||
|
}
|
@ -1,180 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.uhabits;
|
|
||||||
|
|
||||||
import org.isoron.uhabits.models.Habit;
|
|
||||||
import org.isoron.uhabits.tasks.ExportCSVTask;
|
|
||||||
import org.isoron.uhabits.tasks.ExportDBTask;
|
|
||||||
import org.isoron.uhabits.tasks.ImportDataTask;
|
|
||||||
import org.isoron.uhabits.tasks.ProgressBar;
|
|
||||||
import org.isoron.uhabits.utils.DateUtils;
|
|
||||||
import org.isoron.uhabits.utils.Preferences;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class MainController implements ImportDataTask.Listener, ExportCSVTask.Listener,
|
|
||||||
ExportDBTask.Listener
|
|
||||||
{
|
|
||||||
public interface Screen
|
|
||||||
{
|
|
||||||
void showIntroScreen();
|
|
||||||
|
|
||||||
void showMessage(Integer stringId);
|
|
||||||
|
|
||||||
void refresh(Long refreshKey);
|
|
||||||
|
|
||||||
void sendFile(String filename);
|
|
||||||
|
|
||||||
void sendEmail(String to, String subject, String content);
|
|
||||||
|
|
||||||
ProgressBar getProgressBar();
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface System
|
|
||||||
{
|
|
||||||
void scheduleReminders();
|
|
||||||
|
|
||||||
void updateWidgets();
|
|
||||||
|
|
||||||
File dumpBugReportToFile() throws IOException;
|
|
||||||
|
|
||||||
String getBugReport() throws IOException;
|
|
||||||
}
|
|
||||||
|
|
||||||
System sys;
|
|
||||||
Screen screen;
|
|
||||||
Preferences prefs;
|
|
||||||
|
|
||||||
public MainController()
|
|
||||||
{
|
|
||||||
prefs = Preferences.getInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setScreen(Screen screen)
|
|
||||||
{
|
|
||||||
this.screen = screen;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSystem(System sys)
|
|
||||||
{
|
|
||||||
this.sys = sys;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onStartup()
|
|
||||||
{
|
|
||||||
prefs.initialize();
|
|
||||||
prefs.incrementLaunchCount();
|
|
||||||
prefs.updateLastAppVersion();
|
|
||||||
if(prefs.isFirstRun()) onFirstRun();
|
|
||||||
|
|
||||||
sys.updateWidgets();
|
|
||||||
sys.scheduleReminders();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onFirstRun()
|
|
||||||
{
|
|
||||||
prefs.setFirstRun(false);
|
|
||||||
prefs.setLastHintTimestamp(DateUtils.getStartOfToday());
|
|
||||||
screen.showIntroScreen();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void importData(File file)
|
|
||||||
{
|
|
||||||
ImportDataTask task = new ImportDataTask(file, screen.getProgressBar());
|
|
||||||
task.setListener(this);
|
|
||||||
task.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onImportDataFinished(int result)
|
|
||||||
{
|
|
||||||
switch (result)
|
|
||||||
{
|
|
||||||
case ImportDataTask.SUCCESS:
|
|
||||||
screen.refresh(null);
|
|
||||||
screen.showMessage(R.string.habits_imported);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ImportDataTask.NOT_RECOGNIZED:
|
|
||||||
screen.showMessage(R.string.file_not_recognized);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
screen.showMessage(R.string.could_not_import);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void exportCSV()
|
|
||||||
{
|
|
||||||
ExportCSVTask task = new ExportCSVTask(Habit.getAll(true), screen.getProgressBar());
|
|
||||||
task.setListener(this);
|
|
||||||
task.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onExportCSVFinished(String filename)
|
|
||||||
{
|
|
||||||
if(filename != null) screen.sendFile(filename);
|
|
||||||
else screen.showMessage(R.string.could_not_export);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void exportDB()
|
|
||||||
{
|
|
||||||
ExportDBTask task = new ExportDBTask(screen.getProgressBar());
|
|
||||||
task.setListener(this);
|
|
||||||
task.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onExportDBFinished(String filename)
|
|
||||||
{
|
|
||||||
if(filename != null) screen.sendFile(filename);
|
|
||||||
else screen.showMessage(R.string.could_not_export);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendBugReport()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
sys.dumpBugReportToFile();
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
String log = "---------- BUG REPORT BEGINS ----------\n";
|
|
||||||
log += sys.getBugReport();
|
|
||||||
log += "---------- BUG REPORT ENDS ------------\n";
|
|
||||||
String to = "dev@loophabits.org";
|
|
||||||
String subject = "Bug Report - Loop Habit Tracker";
|
|
||||||
screen.sendEmail(log, to, subject);
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
e.printStackTrace();
|
|
||||||
screen.showMessage(R.string.bug_report_failed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.ui;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
|
||||||
|
public abstract class BaseMenu
|
||||||
|
{
|
||||||
|
private final BaseActivity activity;
|
||||||
|
|
||||||
|
public BaseMenu(BaseActivity activity)
|
||||||
|
{
|
||||||
|
this.activity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean onCreate(@NonNull MenuInflater inflater,
|
||||||
|
@NonNull Menu menu)
|
||||||
|
{
|
||||||
|
menu.clear();
|
||||||
|
inflater.inflate(getMenuResourceId(), menu);
|
||||||
|
onCreate(menu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCreate(@NonNull Menu menu)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onItemSelected(@NonNull MenuItem item)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract int getMenuResourceId();
|
||||||
|
|
||||||
|
public void invalidate()
|
||||||
|
{
|
||||||
|
activity.invalidateOptionsMenu();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.ui;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
|
import org.isoron.uhabits.utils.InterfaceUtils;
|
||||||
|
|
||||||
|
public abstract class BaseRootView extends FrameLayout
|
||||||
|
{
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
|
public BaseRootView(Context context)
|
||||||
|
{
|
||||||
|
super(context);
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseRootView(Context context, AttributeSet attrs)
|
||||||
|
{
|
||||||
|
super(context, attrs);
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseRootView(Context context, AttributeSet attrs, int defStyleAttr)
|
||||||
|
{
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
public BaseRootView(Context context,
|
||||||
|
AttributeSet attrs,
|
||||||
|
int defStyleAttr,
|
||||||
|
int defStyleRes)
|
||||||
|
{
|
||||||
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getDisplayHomeAsUp()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public ProgressBar getProgressBar()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public abstract Toolbar getToolbar();
|
||||||
|
|
||||||
|
public int getToolbarColor()
|
||||||
|
{
|
||||||
|
// if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
// {
|
||||||
|
// if (InterfaceUtils.isNightMode()) return;
|
||||||
|
// int color = activity.getResources().getColor(R.color.grey_900);
|
||||||
|
// }
|
||||||
|
// if (!InterfaceUtils.getStyledBoolean(activity, R.attr.useHabitColorAsPrimary)) return;
|
||||||
|
return Color.BLACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hideFakeToolbarShadow()
|
||||||
|
{
|
||||||
|
View view = findViewById(R.id.toolbarShadow);
|
||||||
|
if (view != null) view.setVisibility(View.GONE);
|
||||||
|
|
||||||
|
view = findViewById(R.id.headerShadow);
|
||||||
|
if (view != null) view.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void initToolbar()
|
||||||
|
{
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
getToolbar().setElevation(InterfaceUtils.dpToPixels(context, 2));
|
||||||
|
|
||||||
|
hideFakeToolbarShadow();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,217 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.ui;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.drawable.ColorDrawable;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v7.app.ActionBar;
|
||||||
|
import android.support.v7.app.AppCompatDialogFragment;
|
||||||
|
import android.support.v7.view.ActionMode;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.tasks.ProgressBar;
|
||||||
|
import org.isoron.uhabits.utils.ColorUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public abstract class BaseScreen
|
||||||
|
{
|
||||||
|
protected BaseActivity activity;
|
||||||
|
|
||||||
|
private Toast toast;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private BaseRootView rootView;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private BaseSelectionMenu selectionMenu;
|
||||||
|
|
||||||
|
public BaseScreen(BaseActivity activity)
|
||||||
|
{
|
||||||
|
this.activity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void finishSelection()
|
||||||
|
{
|
||||||
|
if (selectionMenu == null) return;
|
||||||
|
selectionMenu.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public ProgressBar getProgressBar()
|
||||||
|
{
|
||||||
|
if (rootView == null) return null;
|
||||||
|
return new ProgressBarWrapper(rootView.getProgressBar());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void invalidate()
|
||||||
|
{
|
||||||
|
if (rootView == null) return;
|
||||||
|
rootView.invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onResult(int requestCode, int resultCode, Intent data)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMenu(@Nullable BaseMenu menu)
|
||||||
|
{
|
||||||
|
activity.setBaseMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRootView(@Nullable BaseRootView rootView)
|
||||||
|
{
|
||||||
|
this.rootView = rootView;
|
||||||
|
activity.setContentView(rootView);
|
||||||
|
if (rootView == null) return;
|
||||||
|
|
||||||
|
initToolbar();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the menu to be shown when a selection is active on the screen.
|
||||||
|
*
|
||||||
|
* @param menu the menu to be shown during a selection
|
||||||
|
*/
|
||||||
|
public void setSelectionMenu(@Nullable BaseSelectionMenu menu)
|
||||||
|
{
|
||||||
|
this.selectionMenu = menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showMessage(@Nullable Integer stringId)
|
||||||
|
{
|
||||||
|
if (stringId == null) return;
|
||||||
|
if (toast == null)
|
||||||
|
toast = Toast.makeText(activity, stringId, Toast.LENGTH_SHORT);
|
||||||
|
else toast.setText(stringId);
|
||||||
|
toast.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showSendEmailScreen(String to, String subject, String content)
|
||||||
|
{
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setAction(Intent.ACTION_SEND);
|
||||||
|
intent.setType("message/rfc822");
|
||||||
|
intent.putExtra(Intent.EXTRA_EMAIL, new String[]{to});
|
||||||
|
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
|
||||||
|
intent.putExtra(Intent.EXTRA_TEXT, content);
|
||||||
|
activity.startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showSendFileScreen(@NonNull String archiveFilename)
|
||||||
|
{
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setAction(Intent.ACTION_SEND);
|
||||||
|
intent.setType("application/zip");
|
||||||
|
intent.putExtra(Intent.EXTRA_STREAM,
|
||||||
|
Uri.fromFile(new File(archiveFilename)));
|
||||||
|
activity.startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instructs the screen to start a selection. If a selection menu was
|
||||||
|
* provided, this menu will be shown instead of the regular one.
|
||||||
|
*/
|
||||||
|
public void startSelection()
|
||||||
|
{
|
||||||
|
activity.startSupportActionMode(new ActionModeWrapper());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initToolbar()
|
||||||
|
{
|
||||||
|
if (rootView == null) return;
|
||||||
|
|
||||||
|
Toolbar toolbar = rootView.getToolbar();
|
||||||
|
activity.setSupportActionBar(toolbar);
|
||||||
|
ActionBar actionBar = activity.getSupportActionBar();
|
||||||
|
if (actionBar == null) return;
|
||||||
|
|
||||||
|
actionBar.setDisplayHomeAsUpEnabled(rootView.getDisplayHomeAsUp());
|
||||||
|
|
||||||
|
int color = rootView.getToolbarColor();
|
||||||
|
setActionBarColor(actionBar, color);
|
||||||
|
setStatusBarColor(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setActionBarColor(@NonNull ActionBar actionBar, int color)
|
||||||
|
{
|
||||||
|
ColorDrawable drawable = new ColorDrawable(color);
|
||||||
|
actionBar.setBackgroundDrawable(drawable);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setStatusBarColor(int baseColor)
|
||||||
|
{
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
{
|
||||||
|
int darkerColor =
|
||||||
|
ColorUtils.mixColors(baseColor, Color.BLACK, 0.75f);
|
||||||
|
activity.getWindow().setStatusBarColor(darkerColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void showDialog(AppCompatDialogFragment dialog, String tag)
|
||||||
|
{
|
||||||
|
dialog.show(activity.getSupportFragmentManager(), tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ActionModeWrapper implements ActionMode.Callback
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public boolean onActionItemClicked(@Nullable ActionMode mode,
|
||||||
|
@Nullable MenuItem item)
|
||||||
|
{
|
||||||
|
if (item == null || selectionMenu == null) return false;
|
||||||
|
return selectionMenu.onItemClicked(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateActionMode(@Nullable ActionMode mode,
|
||||||
|
@Nullable Menu menu)
|
||||||
|
{
|
||||||
|
if (selectionMenu == null) return false;
|
||||||
|
if (mode == null || menu == null) return false;
|
||||||
|
selectionMenu.onCreate(activity.getMenuInflater(), mode, menu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyActionMode(@Nullable ActionMode mode)
|
||||||
|
{
|
||||||
|
if (selectionMenu == null) return;
|
||||||
|
selectionMenu.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPrepareActionMode(@Nullable ActionMode mode,
|
||||||
|
@Nullable Menu menu)
|
||||||
|
{
|
||||||
|
if (selectionMenu == null || menu == null) return false;
|
||||||
|
return selectionMenu.onPrepare(menu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.ui;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v7.view.ActionMode;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
|
||||||
|
public abstract class BaseSelectionMenu
|
||||||
|
{
|
||||||
|
@Nullable
|
||||||
|
private ActionMode actionMode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finishes the selection operation.
|
||||||
|
*/
|
||||||
|
public void finish()
|
||||||
|
{
|
||||||
|
if (actionMode != null) actionMode.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void invalidate()
|
||||||
|
{
|
||||||
|
if (actionMode != null) actionMode.invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void onCreate(@NonNull MenuInflater menuInflater,
|
||||||
|
@NonNull ActionMode mode,
|
||||||
|
@NonNull Menu menu)
|
||||||
|
{
|
||||||
|
this.actionMode = mode;
|
||||||
|
menuInflater.inflate(getResourceId(), menu);
|
||||||
|
onCreate(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDestroy()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onItemClicked(@NonNull MenuItem item)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onPrepare(@NonNull Menu menu)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String title)
|
||||||
|
{
|
||||||
|
if (actionMode != null) actionMode.setTitle(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract int getResourceId();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the menu is first created, right after the menu has been
|
||||||
|
* inflated.
|
||||||
|
*
|
||||||
|
* @param menu the menu containing the buttons
|
||||||
|
*/
|
||||||
|
protected void onCreate(@NonNull Menu menu)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.ui;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.BuildConfig;
|
||||||
|
import org.isoron.uhabits.tasks.BaseTask;
|
||||||
|
import org.isoron.uhabits.utils.DateUtils;
|
||||||
|
import org.isoron.uhabits.utils.FileUtils;
|
||||||
|
import org.isoron.uhabits.utils.ReminderUtils;
|
||||||
|
import org.isoron.uhabits.widgets.WidgetManager;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
public class BaseSystem
|
||||||
|
{
|
||||||
|
private Context context;
|
||||||
|
|
||||||
|
public BaseSystem(Context context)
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLogcat() throws IOException
|
||||||
|
{
|
||||||
|
int maxNLines = 250;
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
String[] command = new String[]{"logcat", "-d"};
|
||||||
|
Process process = Runtime.getRuntime().exec(command);
|
||||||
|
|
||||||
|
InputStreamReader in = new InputStreamReader(process.getInputStream());
|
||||||
|
BufferedReader bufferedReader = new BufferedReader(in);
|
||||||
|
|
||||||
|
LinkedList<String> log = new LinkedList<>();
|
||||||
|
|
||||||
|
String line;
|
||||||
|
while ((line = bufferedReader.readLine()) != null)
|
||||||
|
{
|
||||||
|
log.addLast(line);
|
||||||
|
if (log.size() > maxNLines) log.removeFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String l : log)
|
||||||
|
{
|
||||||
|
builder.append(l);
|
||||||
|
builder.append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDeviceInfo()
|
||||||
|
{
|
||||||
|
if (context == null) return "";
|
||||||
|
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
WindowManager wm =
|
||||||
|
(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||||
|
|
||||||
|
b.append(
|
||||||
|
String.format("App Version Name: %s\n", BuildConfig.VERSION_NAME));
|
||||||
|
b.append(
|
||||||
|
String.format("App Version Code: %s\n", BuildConfig.VERSION_CODE));
|
||||||
|
b.append(String.format("OS Version: %s (%s)\n",
|
||||||
|
java.lang.System.getProperty("os.version"),
|
||||||
|
android.os.Build.VERSION.INCREMENTAL));
|
||||||
|
b.append(
|
||||||
|
String.format("OS API Level: %s\n", android.os.Build.VERSION.SDK));
|
||||||
|
b.append(String.format("Device: %s\n", android.os.Build.DEVICE));
|
||||||
|
b.append(
|
||||||
|
String.format("Model (Product): %s (%s)\n", android.os.Build.MODEL,
|
||||||
|
android.os.Build.PRODUCT));
|
||||||
|
b.append(
|
||||||
|
String.format("Manufacturer: %s\n", android.os.Build.MANUFACTURER));
|
||||||
|
b.append(String.format("Other tags: %s\n", android.os.Build.TAGS));
|
||||||
|
b.append(String.format("Screen Width: %s\n",
|
||||||
|
wm.getDefaultDisplay().getWidth()));
|
||||||
|
b.append(String.format("Screen Height: %s\n",
|
||||||
|
wm.getDefaultDisplay().getHeight()));
|
||||||
|
b.append(String.format("SD Card state: %s\n\n",
|
||||||
|
Environment.getExternalStorageState()));
|
||||||
|
|
||||||
|
return b.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public File dumpBugReportToFile() throws IOException
|
||||||
|
{
|
||||||
|
String date =
|
||||||
|
DateUtils.getBackupDateFormat().format(DateUtils.getLocalTime());
|
||||||
|
|
||||||
|
if (context == null) throw new RuntimeException(
|
||||||
|
"application context should not be null");
|
||||||
|
File dir = FileUtils.getFilesDir("Logs");
|
||||||
|
if (dir == null) throw new IOException("log dir should not be null");
|
||||||
|
|
||||||
|
File logFile =
|
||||||
|
new File(String.format("%s/Log %s.txt", dir.getPath(), date));
|
||||||
|
FileWriter output = new FileWriter(logFile);
|
||||||
|
output.write(getBugReport());
|
||||||
|
output.close();
|
||||||
|
|
||||||
|
return logFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public String getBugReport() throws IOException
|
||||||
|
{
|
||||||
|
String logcat = getLogcat();
|
||||||
|
String deviceInfo = getDeviceInfo();
|
||||||
|
return deviceInfo + "\n" + logcat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void scheduleReminders()
|
||||||
|
{
|
||||||
|
new BaseTask()
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doInBackground()
|
||||||
|
{
|
||||||
|
ReminderUtils.createReminderAlarms(context);
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateWidgets()
|
||||||
|
{
|
||||||
|
new BaseTask()
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doInBackground()
|
||||||
|
{
|
||||||
|
WidgetManager.updateWidgets(context);
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
|
}
|
||||||
|
}
|
@ -1,81 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.uhabits.ui;
|
|
||||||
|
|
||||||
import android.animation.Animator;
|
|
||||||
import android.animation.AnimatorListenerAdapter;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import org.isoron.uhabits.R;
|
|
||||||
import org.isoron.uhabits.utils.DateUtils;
|
|
||||||
|
|
||||||
public class HintManager
|
|
||||||
{
|
|
||||||
private Context context;
|
|
||||||
private SharedPreferences prefs;
|
|
||||||
private View hintView;
|
|
||||||
|
|
||||||
public HintManager(Context context, View hintView)
|
|
||||||
{
|
|
||||||
this.context = context;
|
|
||||||
this.hintView = hintView;
|
|
||||||
prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dismissHint()
|
|
||||||
{
|
|
||||||
hintView.animate().alpha(0f).setDuration(500).setListener(new AnimatorListenerAdapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void onAnimationEnd(Animator animation)
|
|
||||||
{
|
|
||||||
hintView.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void showHintIfAppropriate()
|
|
||||||
{
|
|
||||||
Integer lastHintNumber = prefs.getInt("last_hint_number", -1);
|
|
||||||
Long lastHintTimestamp = prefs.getLong("last_hint_timestamp", -1);
|
|
||||||
|
|
||||||
if (DateUtils.getStartOfToday() > lastHintTimestamp) showHint(lastHintNumber + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showHint(int hintNumber)
|
|
||||||
{
|
|
||||||
String[] hints = context.getResources().getStringArray(R.array.hints);
|
|
||||||
if (hintNumber >= hints.length) return;
|
|
||||||
|
|
||||||
prefs.edit().putInt("last_hint_number", hintNumber).apply();
|
|
||||||
prefs.edit().putLong("last_hint_timestamp", DateUtils.getStartOfToday()).apply();
|
|
||||||
|
|
||||||
TextView tvContent = (TextView) hintView.findViewById(R.id.hintContent);
|
|
||||||
tvContent.setText(hints[hintNumber]);
|
|
||||||
|
|
||||||
hintView.setAlpha(0.0f);
|
|
||||||
hintView.setVisibility(View.VISIBLE);
|
|
||||||
hintView.animate().alpha(1f).setDuration(500);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains classes for AboutActivity
|
||||||
|
*/
|
||||||
|
package org.isoron.uhabits.ui.about;
|
@ -1,99 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.uhabits.ui.habits.list;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.BaseAdapter;
|
|
||||||
|
|
||||||
import org.isoron.uhabits.R;
|
|
||||||
import org.isoron.uhabits.utils.DateUtils;
|
|
||||||
import org.isoron.uhabits.models.Habit;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
class HabitListAdapter extends BaseAdapter
|
|
||||||
{
|
|
||||||
private LayoutInflater inflater;
|
|
||||||
private HabitListLoader loader;
|
|
||||||
private ListHabitsHelper helper;
|
|
||||||
private List selectedPositions;
|
|
||||||
private View.OnLongClickListener onCheckmarkLongClickListener;
|
|
||||||
private View.OnClickListener onCheckmarkClickListener;
|
|
||||||
|
|
||||||
public HabitListAdapter(Context context, HabitListLoader loader)
|
|
||||||
{
|
|
||||||
this.loader = loader;
|
|
||||||
|
|
||||||
inflater = LayoutInflater.from(context);
|
|
||||||
helper = new ListHabitsHelper(context, loader);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getCount()
|
|
||||||
{
|
|
||||||
return loader.habits.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Habit getItem(int position)
|
|
||||||
{
|
|
||||||
return loader.habitsList.get(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getItemId(int position)
|
|
||||||
{
|
|
||||||
return (getItem(position)).getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View getView(int position, View view, ViewGroup parent)
|
|
||||||
{
|
|
||||||
final Habit habit = loader.habitsList.get(position);
|
|
||||||
boolean selected = selectedPositions.contains(position);
|
|
||||||
|
|
||||||
if (view == null || (Long) view.getTag(R.id.timestamp_key) != DateUtils.getStartOfToday())
|
|
||||||
{
|
|
||||||
view = helper.inflateHabitCard(inflater, onCheckmarkLongClickListener,
|
|
||||||
onCheckmarkClickListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
helper.updateHabitCard(view, habit, selected);
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSelectedPositions(List selectedPositions)
|
|
||||||
{
|
|
||||||
this.selectedPositions = selectedPositions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOnCheckmarkLongClickListener(View.OnLongClickListener listener)
|
|
||||||
{
|
|
||||||
this.onCheckmarkLongClickListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOnCheckmarkClickListener(View.OnClickListener listener)
|
|
||||||
{
|
|
||||||
this.onCheckmarkClickListener = listener;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,223 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.uhabits.ui.habits.list;
|
|
||||||
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.isoron.uhabits.commands.Command;
|
|
||||||
import org.isoron.uhabits.commands.CommandRunner;
|
|
||||||
import org.isoron.uhabits.utils.DateUtils;
|
|
||||||
import org.isoron.uhabits.models.Habit;
|
|
||||||
import org.isoron.uhabits.tasks.BaseTask;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class HabitListLoader implements CommandRunner.Listener
|
|
||||||
{
|
|
||||||
public interface Listener
|
|
||||||
{
|
|
||||||
void onLoadFinished();
|
|
||||||
}
|
|
||||||
|
|
||||||
private BaseTask currentFetchTask;
|
|
||||||
private int checkmarkCount;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Listener listener;
|
|
||||||
private Long lastLoadTimestamp;
|
|
||||||
|
|
||||||
public HashMap<Long, Habit> habits;
|
|
||||||
public List<Habit> habitsList;
|
|
||||||
public HashMap<Long, int[]> checkmarks;
|
|
||||||
public HashMap<Long, Integer> scores;
|
|
||||||
|
|
||||||
boolean includeArchived;
|
|
||||||
|
|
||||||
public void setIncludeArchived(boolean includeArchived)
|
|
||||||
{
|
|
||||||
this.includeArchived = includeArchived;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCheckmarkCount(int checkmarkCount)
|
|
||||||
{
|
|
||||||
this.checkmarkCount = checkmarkCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setListener(@Nullable Listener listener)
|
|
||||||
{
|
|
||||||
this.listener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getLastLoadTimestamp()
|
|
||||||
{
|
|
||||||
return lastLoadTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HabitListLoader()
|
|
||||||
{
|
|
||||||
habits = new HashMap<>();
|
|
||||||
checkmarks = new HashMap<>();
|
|
||||||
scores = new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reorder(int from, int to)
|
|
||||||
{
|
|
||||||
Habit fromHabit = habitsList.get(from);
|
|
||||||
Habit toHabit = habitsList.get(to);
|
|
||||||
|
|
||||||
habitsList.remove(from);
|
|
||||||
habitsList.add(to, fromHabit);
|
|
||||||
|
|
||||||
Habit.reorder(fromHabit, toHabit);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateAllHabits(final boolean updateScoresAndCheckmarks)
|
|
||||||
{
|
|
||||||
if (currentFetchTask != null) currentFetchTask.cancel(true);
|
|
||||||
|
|
||||||
currentFetchTask = new BaseTask()
|
|
||||||
{
|
|
||||||
public HashMap<Long, Habit> newHabits;
|
|
||||||
public HashMap<Long, int[]> newCheckmarks;
|
|
||||||
public HashMap<Long, Integer> newScores;
|
|
||||||
public List<Habit> newHabitList;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doInBackground()
|
|
||||||
{
|
|
||||||
newHabits = new HashMap<>();
|
|
||||||
newCheckmarks = new HashMap<>();
|
|
||||||
newScores = new HashMap<>();
|
|
||||||
newHabitList = Habit.getAll(includeArchived);
|
|
||||||
|
|
||||||
long dateTo = DateUtils.getStartOfDay(DateUtils.getLocalTime());
|
|
||||||
long dateFrom = dateTo - (checkmarkCount - 1) * DateUtils.millisecondsInOneDay;
|
|
||||||
int[] empty = new int[checkmarkCount];
|
|
||||||
|
|
||||||
for(Habit h : newHabitList)
|
|
||||||
{
|
|
||||||
Long id = h.getId();
|
|
||||||
|
|
||||||
newHabits.put(id, h);
|
|
||||||
|
|
||||||
if(checkmarks.containsKey(id))
|
|
||||||
newCheckmarks.put(id, checkmarks.get(id));
|
|
||||||
else
|
|
||||||
newCheckmarks.put(id, empty);
|
|
||||||
|
|
||||||
if(scores.containsKey(id))
|
|
||||||
newScores.put(id, scores.get(id));
|
|
||||||
else
|
|
||||||
newScores.put(id, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
commit();
|
|
||||||
|
|
||||||
if(!updateScoresAndCheckmarks) return;
|
|
||||||
|
|
||||||
int current = 0;
|
|
||||||
for (Habit h : newHabitList)
|
|
||||||
{
|
|
||||||
if (isCancelled()) return;
|
|
||||||
|
|
||||||
Long id = h.getId();
|
|
||||||
newScores.put(id, h.scores.getTodayValue());
|
|
||||||
newCheckmarks.put(id, h.checkmarks.getValues(dateFrom, dateTo));
|
|
||||||
|
|
||||||
publishProgress(current++, newHabits.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void commit()
|
|
||||||
{
|
|
||||||
habits = newHabits;
|
|
||||||
scores = newScores;
|
|
||||||
checkmarks = newCheckmarks;
|
|
||||||
habitsList = newHabitList;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onProgressUpdate(Integer... values)
|
|
||||||
{
|
|
||||||
if(listener != null) listener.onLoadFinished();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Void aVoid)
|
|
||||||
{
|
|
||||||
if (isCancelled()) return;
|
|
||||||
|
|
||||||
lastLoadTimestamp = DateUtils.getStartOfToday();
|
|
||||||
currentFetchTask = null;
|
|
||||||
|
|
||||||
if(listener != null) listener.onLoadFinished();
|
|
||||||
super.onPostExecute(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
currentFetchTask.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateHabit(final Long id)
|
|
||||||
{
|
|
||||||
new BaseTask()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
protected void doInBackground()
|
|
||||||
{
|
|
||||||
long dateTo = DateUtils.getStartOfDay(DateUtils.getLocalTime());
|
|
||||||
long dateFrom = dateTo - (checkmarkCount - 1) * DateUtils.millisecondsInOneDay;
|
|
||||||
|
|
||||||
Habit h = Habit.get(id);
|
|
||||||
if(h == null) return;
|
|
||||||
|
|
||||||
habits.put(id, h);
|
|
||||||
scores.put(id, h.scores.getTodayValue());
|
|
||||||
checkmarks.put(id, h.checkmarks.getValues(dateFrom, dateTo));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Void aVoid)
|
|
||||||
{
|
|
||||||
if(listener != null) listener.onLoadFinished();
|
|
||||||
super.onPostExecute(null);
|
|
||||||
}
|
|
||||||
}.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onResume()
|
|
||||||
{
|
|
||||||
CommandRunner.getInstance().addListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onPause()
|
|
||||||
{
|
|
||||||
CommandRunner.getInstance().removeListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCommandExecuted(Command command, Long refreshKey)
|
|
||||||
{
|
|
||||||
if(refreshKey == null) updateAllHabits(true);
|
|
||||||
else updateHabit(refreshKey);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,229 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.uhabits.ui.habits.list;
|
|
||||||
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.support.v7.app.AlertDialog;
|
|
||||||
import android.support.v7.view.ActionMode;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
|
|
||||||
import com.android.colorpicker.ColorPickerDialog;
|
|
||||||
import com.android.colorpicker.ColorPickerSwatch;
|
|
||||||
|
|
||||||
import org.isoron.uhabits.R;
|
|
||||||
import org.isoron.uhabits.commands.ArchiveHabitsCommand;
|
|
||||||
import org.isoron.uhabits.commands.ChangeHabitColorCommand;
|
|
||||||
import org.isoron.uhabits.commands.CommandRunner;
|
|
||||||
import org.isoron.uhabits.commands.DeleteHabitsCommand;
|
|
||||||
import org.isoron.uhabits.commands.UnarchiveHabitsCommand;
|
|
||||||
import org.isoron.uhabits.models.Habit;
|
|
||||||
import org.isoron.uhabits.ui.BaseActivity;
|
|
||||||
import org.isoron.uhabits.ui.habits.edit.BaseDialogFragment;
|
|
||||||
import org.isoron.uhabits.ui.habits.edit.EditHabitDialogFragment;
|
|
||||||
import org.isoron.uhabits.utils.ColorUtils;
|
|
||||||
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class HabitListSelectionCallback implements ActionMode.Callback
|
|
||||||
{
|
|
||||||
private HabitListLoader loader;
|
|
||||||
private List<Integer> selectedPositions;
|
|
||||||
private BaseActivity activity;
|
|
||||||
private Listener listener;
|
|
||||||
|
|
||||||
public interface Listener
|
|
||||||
{
|
|
||||||
void onActionModeDestroyed(ActionMode mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public HabitListSelectionCallback(BaseActivity activity, HabitListLoader loader)
|
|
||||||
{
|
|
||||||
this.activity = activity;
|
|
||||||
this.loader = loader;
|
|
||||||
selectedPositions = new LinkedList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setListener(Listener listener)
|
|
||||||
{
|
|
||||||
this.listener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSelectedPositions(List<Integer> selectedPositions)
|
|
||||||
{
|
|
||||||
this.selectedPositions = selectedPositions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateActionMode(ActionMode mode, Menu menu)
|
|
||||||
{
|
|
||||||
activity.getMenuInflater().inflate(R.menu.list_habits_selection, menu);
|
|
||||||
updateTitle(mode);
|
|
||||||
updateActions(menu);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu)
|
|
||||||
{
|
|
||||||
updateTitle(mode);
|
|
||||||
updateActions(menu);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateActions(Menu menu)
|
|
||||||
{
|
|
||||||
boolean showEdit = (selectedPositions.size() == 1);
|
|
||||||
boolean showArchive = true;
|
|
||||||
boolean showUnarchive = true;
|
|
||||||
for (int i : selectedPositions)
|
|
||||||
{
|
|
||||||
Habit h = loader.habitsList.get(i);
|
|
||||||
if (h.isArchived()) showArchive = false;
|
|
||||||
else showUnarchive = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
MenuItem itemEdit = menu.findItem(R.id.action_edit_habit);
|
|
||||||
MenuItem itemColor = menu.findItem(R.id.action_color);
|
|
||||||
MenuItem itemArchive = menu.findItem(R.id.action_archive_habit);
|
|
||||||
MenuItem itemUnarchive = menu.findItem(R.id.action_unarchive_habit);
|
|
||||||
|
|
||||||
itemColor.setVisible(true);
|
|
||||||
itemEdit.setVisible(showEdit);
|
|
||||||
itemArchive.setVisible(showArchive);
|
|
||||||
itemUnarchive.setVisible(showUnarchive);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateTitle(ActionMode mode)
|
|
||||||
{
|
|
||||||
mode.setTitle("" + selectedPositions.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onActionItemClicked(final ActionMode mode, MenuItem item)
|
|
||||||
{
|
|
||||||
final LinkedList<Habit> selectedHabits = new LinkedList<>();
|
|
||||||
for (int i : selectedPositions)
|
|
||||||
selectedHabits.add(loader.habitsList.get(i));
|
|
||||||
|
|
||||||
Habit firstHabit = selectedHabits.getFirst();
|
|
||||||
|
|
||||||
switch (item.getItemId())
|
|
||||||
{
|
|
||||||
case R.id.action_archive_habit:
|
|
||||||
archiveHabits(selectedHabits);
|
|
||||||
mode.finish();
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case R.id.action_unarchive_habit:
|
|
||||||
unarchiveHabits(selectedHabits);
|
|
||||||
mode.finish();
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case R.id.action_edit_habit:
|
|
||||||
{
|
|
||||||
editHabit(firstHabit);
|
|
||||||
mode.finish();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
case R.id.action_color:
|
|
||||||
{
|
|
||||||
showColorPicker(mode, selectedHabits, firstHabit);
|
|
||||||
mode.finish();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
case R.id.action_delete:
|
|
||||||
{
|
|
||||||
deleteHabits(mode, selectedHabits);
|
|
||||||
mode.finish();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void deleteHabits(final ActionMode mode, final LinkedList<Habit> selectedHabits)
|
|
||||||
{
|
|
||||||
new AlertDialog.Builder(activity).setTitle(R.string.delete_habits)
|
|
||||||
.setMessage(R.string.delete_habits_message)
|
|
||||||
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which)
|
|
||||||
{
|
|
||||||
CommandRunner.getInstance()
|
|
||||||
.execute(new DeleteHabitsCommand(selectedHabits), null);
|
|
||||||
mode.finish();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.setNegativeButton(android.R.string.no, null)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showColorPicker(final ActionMode mode, final LinkedList<Habit> selectedHabits,
|
|
||||||
Habit firstHabit)
|
|
||||||
{
|
|
||||||
int originalAndroidColor = ColorUtils.getColor(activity, firstHabit.color);
|
|
||||||
|
|
||||||
ColorPickerDialog picker =
|
|
||||||
ColorPickerDialog.newInstance(R.string.color_picker_default_title,
|
|
||||||
ColorUtils.getPalette(activity), originalAndroidColor, 4,
|
|
||||||
ColorPickerDialog.SIZE_SMALL);
|
|
||||||
|
|
||||||
picker.setOnColorSelectedListener(new ColorPickerSwatch.OnColorSelectedListener()
|
|
||||||
{
|
|
||||||
public void onColorSelected(int androidColor)
|
|
||||||
{
|
|
||||||
int paletteColor = ColorUtils.colorToPaletteIndex(activity, androidColor);
|
|
||||||
CommandRunner.getInstance()
|
|
||||||
.execute(new ChangeHabitColorCommand(selectedHabits, paletteColor), null);
|
|
||||||
mode.finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
picker.show(activity.getSupportFragmentManager(), "picker");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void editHabit(Habit habit)
|
|
||||||
{
|
|
||||||
BaseDialogFragment
|
|
||||||
frag = EditHabitDialogFragment.newInstance(habit.getId());
|
|
||||||
frag.show(activity.getSupportFragmentManager(), "editHabit");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void unarchiveHabits(LinkedList<Habit> selectedHabits)
|
|
||||||
{
|
|
||||||
CommandRunner.getInstance().execute(new UnarchiveHabitsCommand(selectedHabits), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void archiveHabits(LinkedList<Habit> selectedHabits)
|
|
||||||
{
|
|
||||||
CommandRunner.getInstance().execute(new ArchiveHabitsCommand(selectedHabits), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyActionMode(ActionMode mode)
|
|
||||||
{
|
|
||||||
if(listener != null) listener.onActionModeDestroyed(mode);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,273 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.uhabits.ui.habits.list;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.view.HapticFeedbackConstants;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
|
|
||||||
import com.mobeta.android.dslv.DragSortController;
|
|
||||||
import com.mobeta.android.dslv.DragSortListView;
|
|
||||||
|
|
||||||
import org.isoron.uhabits.R;
|
|
||||||
import org.isoron.uhabits.models.Habit;
|
|
||||||
import org.isoron.uhabits.utils.Preferences;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class HabitListView extends DragSortListView implements View.OnClickListener,
|
|
||||||
View.OnLongClickListener, DragSortListView.DropListener, AdapterView.OnItemClickListener,
|
|
||||||
AdapterView.OnItemLongClickListener, DragSortListView.DragListener, HabitListLoader.Listener
|
|
||||||
{
|
|
||||||
private final HabitListLoader loader;
|
|
||||||
private final HabitListAdapter adapter;
|
|
||||||
private final ListHabitsHelper helper;
|
|
||||||
private final Preferences prefs;
|
|
||||||
private final List<Integer> selectedPositions;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Listener listener;
|
|
||||||
private long lastLongClick;
|
|
||||||
private boolean showArchived;
|
|
||||||
|
|
||||||
public HabitListView(Context context, AttributeSet attrs)
|
|
||||||
{
|
|
||||||
super(context, attrs);
|
|
||||||
|
|
||||||
loader = new HabitListLoader();
|
|
||||||
adapter = new HabitListAdapter(context, loader);
|
|
||||||
selectedPositions = new LinkedList<>();
|
|
||||||
prefs = Preferences.getInstance();
|
|
||||||
helper = new ListHabitsHelper(getContext(), loader);
|
|
||||||
|
|
||||||
adapter.setSelectedPositions(selectedPositions);
|
|
||||||
adapter.setOnCheckmarkClickListener(this);
|
|
||||||
adapter.setOnCheckmarkLongClickListener(this);
|
|
||||||
loader.setListener(this);
|
|
||||||
loader.setCheckmarkCount(helper.getButtonCount());
|
|
||||||
|
|
||||||
setAdapter(adapter);
|
|
||||||
setOnItemClickListener(this);
|
|
||||||
setOnItemLongClickListener(this);
|
|
||||||
setDropListener(this);
|
|
||||||
setDragListener(this);
|
|
||||||
setFloatViewManager(new HabitsDragSortController());
|
|
||||||
setDragEnabled(false);
|
|
||||||
setLongClickable(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public HabitListLoader getLoader()
|
|
||||||
{
|
|
||||||
return loader;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Integer> getSelectedPositions()
|
|
||||||
{
|
|
||||||
return selectedPositions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setListener(@Nullable Listener l)
|
|
||||||
{
|
|
||||||
this.listener = l;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void drop(int from, int to)
|
|
||||||
{
|
|
||||||
if(from == to) return;
|
|
||||||
cancelSelection();
|
|
||||||
|
|
||||||
loader.reorder(from, to);
|
|
||||||
adapter.notifyDataSetChanged();
|
|
||||||
loader.updateAllHabits(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(View v)
|
|
||||||
{
|
|
||||||
if (v.getId() != R.id.tvCheck) return;
|
|
||||||
|
|
||||||
if (prefs.isShortToggleEnabled()) toggleCheckmark(v);
|
|
||||||
else if(listener != null) listener.onInvalidToggle();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onLongClick(View v)
|
|
||||||
{
|
|
||||||
lastLongClick = new Date().getTime();
|
|
||||||
if (v.getId() != R.id.tvCheck) return true;
|
|
||||||
if (prefs.isShortToggleEnabled()) return true;
|
|
||||||
toggleCheckmark(v);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void toggleShowArchived()
|
|
||||||
{
|
|
||||||
showArchived = !showArchived;
|
|
||||||
loader.setIncludeArchived(showArchived);
|
|
||||||
loader.updateAllHabits(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void toggleCheckmark(View v)
|
|
||||||
{
|
|
||||||
Long id = helper.getHabitIdFromCheckmarkView(v);
|
|
||||||
Habit habit = loader.habits.get(id);
|
|
||||||
if(habit == null) return;
|
|
||||||
|
|
||||||
float x = v.getX() + v.getWidth() / 2.0f + ((View) v.getParent()).getX();
|
|
||||||
float y = v.getY() + v.getHeight() / 2.0f + ((View) v.getParent()).getY();
|
|
||||||
helper.triggerRipple((View) v.getParent().getParent(), x, y);
|
|
||||||
|
|
||||||
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
|
||||||
helper.toggleCheckmarkView(v, habit);
|
|
||||||
|
|
||||||
long timestamp = helper.getTimestampFromCheckmarkView(v);
|
|
||||||
|
|
||||||
if(listener != null) listener.onToggleCheckmark(habit, timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
|
|
||||||
{
|
|
||||||
if (new Date().getTime() - lastLongClick < 1000) return;
|
|
||||||
|
|
||||||
if(selectedPositions.isEmpty())
|
|
||||||
{
|
|
||||||
Habit habit = loader.habitsList.get(position);
|
|
||||||
if(listener != null) listener.onHabitClick(habit);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
toggleItemSelected(position);
|
|
||||||
adapter.notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void toggleItemSelected(int position)
|
|
||||||
{
|
|
||||||
int k = selectedPositions.indexOf(position);
|
|
||||||
if(k < 0) selectedPositions.add(position);
|
|
||||||
else selectedPositions.remove(k);
|
|
||||||
|
|
||||||
if(listener != null)
|
|
||||||
{
|
|
||||||
if (selectedPositions.isEmpty()) listener.onHabitSelectionFinish();
|
|
||||||
else listener.onHabitSelectionChange();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id)
|
|
||||||
{
|
|
||||||
selectHabit(position);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void selectHabit(int position)
|
|
||||||
{
|
|
||||||
if(!selectedPositions.contains(position)) selectedPositions.add(position);
|
|
||||||
adapter.notifyDataSetChanged();
|
|
||||||
|
|
||||||
if(listener != null)
|
|
||||||
{
|
|
||||||
if (selectedPositions.size() == 1) listener.onHabitSelectionStart();
|
|
||||||
else listener.onHabitSelectionChange();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void drag(int from, int to)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startDrag(int position)
|
|
||||||
{
|
|
||||||
selectHabit(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getShowArchived()
|
|
||||||
{
|
|
||||||
return showArchived;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cancelSelection()
|
|
||||||
{
|
|
||||||
selectedPositions.clear();
|
|
||||||
adapter.notifyDataSetChanged();
|
|
||||||
setDragEnabled(true);
|
|
||||||
if(listener != null) listener.onHabitSelectionFinish();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void refreshData(Long refreshKey)
|
|
||||||
{
|
|
||||||
if (refreshKey == null) loader.updateAllHabits(true);
|
|
||||||
else loader.updateHabit(refreshKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoadFinished()
|
|
||||||
{
|
|
||||||
adapter.notifyDataSetChanged();
|
|
||||||
if(listener != null) listener.onDatasetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class HabitsDragSortController extends DragSortController
|
|
||||||
{
|
|
||||||
public HabitsDragSortController()
|
|
||||||
{
|
|
||||||
super(HabitListView.this);
|
|
||||||
setRemoveEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View onCreateFloatView(int position)
|
|
||||||
{
|
|
||||||
return adapter.getView(position, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyFloatView(View floatView)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface Listener
|
|
||||||
{
|
|
||||||
void onToggleCheckmark(Habit habit, long timestamp);
|
|
||||||
|
|
||||||
void onHabitClick(Habit habit);
|
|
||||||
|
|
||||||
void onHabitSelectionStart();
|
|
||||||
|
|
||||||
void onHabitSelectionFinish();
|
|
||||||
|
|
||||||
void onHabitSelectionChange();
|
|
||||||
|
|
||||||
void onInvalidToggle();
|
|
||||||
|
|
||||||
void onDatasetChanged();
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.ui.habits.list;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.ui.BaseActivity;
|
||||||
|
import org.isoron.uhabits.ui.BaseSystem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activity that allows the user to see and modify the list of habits.
|
||||||
|
*/
|
||||||
|
public class ListHabitsActivity extends BaseActivity
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState)
|
||||||
|
{
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
BaseSystem system = new BaseSystem(this);
|
||||||
|
ListHabitsScreen screen = new ListHabitsScreen(this);
|
||||||
|
ListHabitsController controller =
|
||||||
|
new ListHabitsController(screen, system);
|
||||||
|
|
||||||
|
screen.setController(controller);
|
||||||
|
|
||||||
|
setScreen(screen);
|
||||||
|
controller.onStartup();
|
||||||
|
}
|
||||||
|
}
|
@ -1,236 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.uhabits.ui.habits.list;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v7.view.ActionMode;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuInflater;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.ProgressBar;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import org.isoron.uhabits.R;
|
|
||||||
import org.isoron.uhabits.commands.CommandRunner;
|
|
||||||
import org.isoron.uhabits.commands.ToggleRepetitionCommand;
|
|
||||||
import org.isoron.uhabits.models.Habit;
|
|
||||||
import org.isoron.uhabits.ui.BaseActivity;
|
|
||||||
import org.isoron.uhabits.ui.HintManager;
|
|
||||||
import org.isoron.uhabits.ui.habits.edit.BaseDialogFragment;
|
|
||||||
import org.isoron.uhabits.ui.habits.edit.CreateHabitDialogFragment;
|
|
||||||
import org.isoron.uhabits.utils.InterfaceUtils;
|
|
||||||
|
|
||||||
import butterknife.BindView;
|
|
||||||
import butterknife.ButterKnife;
|
|
||||||
import butterknife.OnClick;
|
|
||||||
|
|
||||||
public class ListHabitsFragment extends Fragment
|
|
||||||
implements HabitListSelectionCallback.Listener, ListHabitsController.Screen
|
|
||||||
{
|
|
||||||
private ActionMode actionMode;
|
|
||||||
private HintManager hintManager;
|
|
||||||
private ListHabitsHelper helper;
|
|
||||||
private Listener habitClickListener;
|
|
||||||
private BaseActivity activity;
|
|
||||||
|
|
||||||
@BindView(R.id.listView) HabitListView listView;
|
|
||||||
@BindView(R.id.llButtonsHeader) LinearLayout llButtonsHeader;
|
|
||||||
@BindView(R.id.progressBar) ProgressBar progressBar;
|
|
||||||
@BindView(R.id.llEmpty) View llEmpty;
|
|
||||||
@BindView(R.id.llHint) View llHint;
|
|
||||||
@BindView(R.id.tvStarEmpty) TextView tvStarEmpty;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
|
||||||
Bundle savedInstanceState)
|
|
||||||
{
|
|
||||||
View view = inflater.inflate(R.layout.list_habits_fragment, container, false);
|
|
||||||
ButterKnife.bind(this, view);
|
|
||||||
|
|
||||||
helper = new ListHabitsHelper(activity, listView.getLoader());
|
|
||||||
hintManager = new HintManager(activity, llHint);
|
|
||||||
tvStarEmpty.setTypeface(InterfaceUtils.getFontAwesome(activity));
|
|
||||||
listView.setListener(new HabitListViewListener());
|
|
||||||
setHasOptionsMenu(true);
|
|
||||||
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public void onAttach(Activity activity)
|
|
||||||
{
|
|
||||||
super.onAttach(activity);
|
|
||||||
this.activity = (BaseActivity) activity;
|
|
||||||
habitClickListener = (Listener) activity;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume()
|
|
||||||
{
|
|
||||||
super.onResume();
|
|
||||||
listView.getLoader().onResume();
|
|
||||||
listView.refreshData(null);
|
|
||||||
helper.updateEmptyMessage(llEmpty);
|
|
||||||
helper.updateHeader(llButtonsHeader);
|
|
||||||
hintManager.showHintIfAppropriate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPause()
|
|
||||||
{
|
|
||||||
listView.getLoader().onPause();
|
|
||||||
super.onPause();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
|
|
||||||
{
|
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
|
||||||
inflater.inflate(R.menu.list_habits_fragment, menu);
|
|
||||||
MenuItem showArchivedItem = menu.findItem(R.id.action_show_archived);
|
|
||||||
showArchivedItem.setChecked(listView.getShowArchived());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item)
|
|
||||||
{
|
|
||||||
switch (item.getItemId())
|
|
||||||
{
|
|
||||||
case R.id.action_add:
|
|
||||||
showCreateHabitScreen();
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case R.id.action_show_archived:
|
|
||||||
toggleShowArchived();
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void toggleShowArchived()
|
|
||||||
{
|
|
||||||
listView.toggleShowArchived();
|
|
||||||
activity.invalidateOptionsMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showCreateHabitScreen()
|
|
||||||
{
|
|
||||||
BaseDialogFragment frag = new CreateHabitDialogFragment();
|
|
||||||
frag.show(getFragmentManager(), "editHabit");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startActionMode()
|
|
||||||
{
|
|
||||||
HabitListSelectionCallback callback =
|
|
||||||
new HabitListSelectionCallback(activity, listView.getLoader());
|
|
||||||
callback.setSelectedPositions(listView.getSelectedPositions());
|
|
||||||
callback.setListener(this);
|
|
||||||
actionMode = activity.startSupportActionMode(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void finishActionMode()
|
|
||||||
{
|
|
||||||
if(actionMode != null) actionMode.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onActionModeDestroyed(ActionMode mode)
|
|
||||||
{
|
|
||||||
actionMode = null;
|
|
||||||
listView.cancelSelection();
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnClick(R.id.llHint)
|
|
||||||
public void onClickHint()
|
|
||||||
{
|
|
||||||
hintManager.dismissHint();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProgressBar getProgressBar()
|
|
||||||
{
|
|
||||||
return progressBar;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void refresh(Long refreshKey)
|
|
||||||
{
|
|
||||||
listView.refreshData(refreshKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface Listener
|
|
||||||
{
|
|
||||||
void onHabitClick(Habit habit);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class HabitListViewListener implements HabitListView.Listener
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void onToggleCheckmark(Habit habit, long timestamp)
|
|
||||||
{
|
|
||||||
CommandRunner.getInstance().execute(new ToggleRepetitionCommand(habit, timestamp),
|
|
||||||
habit.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onHabitClick(Habit habit)
|
|
||||||
{
|
|
||||||
habitClickListener.onHabitClick(habit);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onHabitSelectionStart()
|
|
||||||
{
|
|
||||||
if(actionMode == null) startActionMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onHabitSelectionFinish()
|
|
||||||
{
|
|
||||||
finishActionMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onHabitSelectionChange()
|
|
||||||
{
|
|
||||||
if(actionMode != null) actionMode.invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onInvalidToggle()
|
|
||||||
{
|
|
||||||
activity.showMessage(R.string.long_press_to_toggle);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDatasetChanged()
|
|
||||||
{
|
|
||||||
helper.updateEmptyMessage(llEmpty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,307 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.uhabits.ui.habits.list;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import org.isoron.uhabits.R;
|
|
||||||
import org.isoron.uhabits.utils.ColorUtils;
|
|
||||||
import org.isoron.uhabits.utils.DateUtils;
|
|
||||||
import org.isoron.uhabits.utils.InterfaceUtils;
|
|
||||||
import org.isoron.uhabits.models.Habit;
|
|
||||||
import org.isoron.uhabits.models.Score;
|
|
||||||
import org.isoron.uhabits.views.RingView;
|
|
||||||
|
|
||||||
import java.util.GregorianCalendar;
|
|
||||||
|
|
||||||
public class ListHabitsHelper
|
|
||||||
{
|
|
||||||
private static final int CHECKMARK_LEFT_TO_RIGHT = 0;
|
|
||||||
private static final int CHECKMARK_RIGHT_TO_LEFT = 1;
|
|
||||||
|
|
||||||
private final int lowContrastColor;
|
|
||||||
private final int mediumContrastColor;
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
private final HabitListLoader loader;
|
|
||||||
|
|
||||||
public ListHabitsHelper(Context context, HabitListLoader loader)
|
|
||||||
{
|
|
||||||
this.context = context;
|
|
||||||
this.loader = loader;
|
|
||||||
|
|
||||||
lowContrastColor = InterfaceUtils.getStyledColor(context, R.attr.lowContrastTextColor);
|
|
||||||
mediumContrastColor = InterfaceUtils.getStyledColor(context, R.attr.mediumContrastTextColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getButtonCount()
|
|
||||||
{
|
|
||||||
float screenWidth = InterfaceUtils.getScreenWidth(context);
|
|
||||||
float labelWidth = context.getResources().getDimension(R.dimen.habitNameWidth);
|
|
||||||
float buttonWidth = context.getResources().getDimension(R.dimen.checkmarkWidth);
|
|
||||||
return Math.max(0, (int) ((screenWidth - labelWidth) / buttonWidth));
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getHabitNameWidth()
|
|
||||||
{
|
|
||||||
float screenWidth = InterfaceUtils.getScreenWidth(context);
|
|
||||||
float buttonWidth = context.getResources().getDimension(R.dimen.checkmarkWidth);
|
|
||||||
float padding = InterfaceUtils.dpToPixels(context, 15);
|
|
||||||
return (int) (screenWidth - padding - getButtonCount() * buttonWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateCheckmarkButtons(Habit habit, LinearLayout llButtons)
|
|
||||||
{
|
|
||||||
int activeColor = getActiveColor(habit);
|
|
||||||
int m = llButtons.getChildCount();
|
|
||||||
Long habitId = habit.getId();
|
|
||||||
|
|
||||||
int isChecked[] = loader.checkmarks.get(habitId);
|
|
||||||
|
|
||||||
for (int i = 0; i < m; i++)
|
|
||||||
{
|
|
||||||
int position = i;
|
|
||||||
|
|
||||||
if(getCheckmarkOrder() == CHECKMARK_RIGHT_TO_LEFT)
|
|
||||||
position = m - i - 1;
|
|
||||||
|
|
||||||
TextView tvCheck = (TextView) llButtons.getChildAt(position);
|
|
||||||
tvCheck.setTag(R.string.habit_key, habitId);
|
|
||||||
tvCheck.setTag(R.string.offset_key, i);
|
|
||||||
if(isChecked.length > i)
|
|
||||||
updateCheckmark(activeColor, tvCheck, isChecked[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getActiveColor(Habit habit)
|
|
||||||
{
|
|
||||||
int activeColor = ColorUtils.getColor(context, habit.color);
|
|
||||||
if(habit.isArchived()) activeColor = mediumContrastColor;
|
|
||||||
|
|
||||||
return activeColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initializeLabelAndIcon(View itemView)
|
|
||||||
{
|
|
||||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(getHabitNameWidth(),
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT, 1);
|
|
||||||
itemView.findViewById(R.id.label).setLayoutParams(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateNameAndIcon(Habit habit, RingView ring, TextView tvName)
|
|
||||||
{
|
|
||||||
int activeColor = getActiveColor(habit);
|
|
||||||
|
|
||||||
tvName.setText(habit.name);
|
|
||||||
tvName.setTextColor(activeColor);
|
|
||||||
|
|
||||||
int score = loader.scores.get(habit.getId());
|
|
||||||
float percentage = (float) score / Score.MAX_VALUE;
|
|
||||||
|
|
||||||
ring.setColor(activeColor);
|
|
||||||
ring.setPercentage(percentage);
|
|
||||||
ring.setPrecision(1.0f / 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateCheckmark(int activeColor, TextView tvCheck, int check)
|
|
||||||
{
|
|
||||||
switch (check)
|
|
||||||
{
|
|
||||||
case 2:
|
|
||||||
tvCheck.setText(R.string.fa_check);
|
|
||||||
tvCheck.setTextColor(activeColor);
|
|
||||||
tvCheck.setTag(R.string.toggle_key, 2);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
tvCheck.setText(R.string.fa_check);
|
|
||||||
tvCheck.setTextColor(lowContrastColor);
|
|
||||||
tvCheck.setTag(R.string.toggle_key, 1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0:
|
|
||||||
tvCheck.setText(R.string.fa_times);
|
|
||||||
tvCheck.setTextColor(lowContrastColor);
|
|
||||||
tvCheck.setTag(R.string.toggle_key, 0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public View inflateHabitCard(LayoutInflater inflater,
|
|
||||||
View.OnLongClickListener onCheckmarkLongClickListener,
|
|
||||||
View.OnClickListener onCheckmarkClickListener)
|
|
||||||
{
|
|
||||||
View view = inflater.inflate(R.layout.list_habits_item, null);
|
|
||||||
initializeLabelAndIcon(view);
|
|
||||||
inflateCheckmarkButtons(view, onCheckmarkLongClickListener, onCheckmarkClickListener,
|
|
||||||
inflater);
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateHabitCard(View view, Habit habit, boolean selected)
|
|
||||||
{
|
|
||||||
RingView scoreRing = ((RingView) view.findViewById(R.id.scoreRing));
|
|
||||||
TextView tvName = (TextView) view.findViewById(R.id.label);
|
|
||||||
LinearLayout llInner = (LinearLayout) view.findViewById(R.id.llInner);
|
|
||||||
LinearLayout llButtons = (LinearLayout) view.findViewById(R.id.llButtons);
|
|
||||||
|
|
||||||
llInner.setTag(R.string.habit_key, habit.getId());
|
|
||||||
llInner.setOnTouchListener(new HotspotTouchListener());
|
|
||||||
|
|
||||||
updateNameAndIcon(habit, scoreRing, tvName);
|
|
||||||
updateCheckmarkButtons(habit, llButtons);
|
|
||||||
updateHabitCardBackground(llInner, selected);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void updateHabitCardBackground(View view, boolean isSelected)
|
|
||||||
{
|
|
||||||
if (android.os.Build.VERSION.SDK_INT >= 21)
|
|
||||||
{
|
|
||||||
if (isSelected)
|
|
||||||
view.setBackgroundResource(R.drawable.selected_box);
|
|
||||||
else
|
|
||||||
view.setBackgroundResource(R.drawable.ripple);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Drawable background;
|
|
||||||
|
|
||||||
if (isSelected)
|
|
||||||
background = InterfaceUtils.getStyledDrawable(context, R.attr.selectedBackground);
|
|
||||||
else
|
|
||||||
background = InterfaceUtils.getStyledDrawable(context, R.attr.cardBackground);
|
|
||||||
|
|
||||||
view.setBackgroundDrawable(background);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void inflateCheckmarkButtons(View view, View.OnLongClickListener onLongClickListener,
|
|
||||||
View.OnClickListener onClickListener, LayoutInflater inflater)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < getButtonCount(); i++)
|
|
||||||
{
|
|
||||||
View check = inflater.inflate(R.layout.list_habits_item_check, null);
|
|
||||||
TextView btCheck = (TextView) check.findViewById(R.id.tvCheck);
|
|
||||||
btCheck.setTypeface(InterfaceUtils.getFontAwesome(context));
|
|
||||||
btCheck.setOnLongClickListener(onLongClickListener);
|
|
||||||
btCheck.setOnClickListener(onClickListener);
|
|
||||||
btCheck.setHapticFeedbackEnabled(false);
|
|
||||||
((LinearLayout) view.findViewById(R.id.llButtons)).addView(check);
|
|
||||||
}
|
|
||||||
|
|
||||||
view.setTag(R.id.timestamp_key, DateUtils.getStartOfToday());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateHeader(ViewGroup header)
|
|
||||||
{
|
|
||||||
LayoutInflater inflater = LayoutInflater.from(context);
|
|
||||||
GregorianCalendar day = DateUtils.getStartOfTodayCalendar();
|
|
||||||
header.removeAllViews();
|
|
||||||
|
|
||||||
for (int i = 0; i < getButtonCount(); i++)
|
|
||||||
{
|
|
||||||
int position = 0;
|
|
||||||
|
|
||||||
if(getCheckmarkOrder() == CHECKMARK_LEFT_TO_RIGHT)
|
|
||||||
position = i;
|
|
||||||
|
|
||||||
View tvDay = inflater.inflate(R.layout.list_habits_header_check, null);
|
|
||||||
TextView btCheck = (TextView) tvDay.findViewById(R.id.tvCheck);
|
|
||||||
btCheck.setText(DateUtils.formatHeaderDate(day));
|
|
||||||
header.addView(tvDay, position);
|
|
||||||
day.add(GregorianCalendar.DAY_OF_MONTH, -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateEmptyMessage(View view)
|
|
||||||
{
|
|
||||||
if (loader.getLastLoadTimestamp() == null) view.setVisibility(View.GONE);
|
|
||||||
else view.setVisibility(loader.habits.size() > 0 ? View.GONE : View.VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void toggleCheckmarkView(View v, Habit habit)
|
|
||||||
{
|
|
||||||
int androidColor = ColorUtils.getColor(context, habit.color);
|
|
||||||
|
|
||||||
if (v.getTag(R.string.toggle_key).equals(2))
|
|
||||||
updateCheckmark(androidColor, (TextView) v, 0);
|
|
||||||
else
|
|
||||||
updateCheckmark(androidColor, (TextView) v, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getHabitIdFromCheckmarkView(View v)
|
|
||||||
{
|
|
||||||
return (Long) v.getTag(R.string.habit_key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getTimestampFromCheckmarkView(View v)
|
|
||||||
{
|
|
||||||
Integer offset = (Integer) v.getTag(R.string.offset_key);
|
|
||||||
return DateUtils.getStartOfDay(DateUtils.getLocalTime() -
|
|
||||||
offset * DateUtils.millisecondsInOneDay);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void triggerRipple(View v, final float x, final float y)
|
|
||||||
{
|
|
||||||
final Drawable background = v.getBackground();
|
|
||||||
if (android.os.Build.VERSION.SDK_INT >= 21)
|
|
||||||
background.setHotspot(x, y);
|
|
||||||
|
|
||||||
background.setState(new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled});
|
|
||||||
|
|
||||||
new Handler().postDelayed(new Runnable()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
background.setState(new int[]{});
|
|
||||||
}
|
|
||||||
}, 25);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class HotspotTouchListener implements View.OnTouchListener
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public boolean onTouch(View v, MotionEvent event)
|
|
||||||
{
|
|
||||||
if (android.os.Build.VERSION.SDK_INT >= 21)
|
|
||||||
v.getBackground().setHotspot(event.getX(), event.getY());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getCheckmarkOrder()
|
|
||||||
{
|
|
||||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
|
||||||
boolean reverse = prefs.getBoolean("pref_checkmark_reverse_order", false);
|
|
||||||
return reverse ? CHECKMARK_RIGHT_TO_LEFT : CHECKMARK_LEFT_TO_RIGHT;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.ui.habits.list;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
|
import org.isoron.uhabits.ui.BaseActivity;
|
||||||
|
import org.isoron.uhabits.ui.BaseMenu;
|
||||||
|
import org.isoron.uhabits.utils.InterfaceUtils;
|
||||||
|
|
||||||
|
public class ListHabitsMenu extends BaseMenu
|
||||||
|
{
|
||||||
|
@NonNull
|
||||||
|
private final ListHabitsScreen screen;
|
||||||
|
|
||||||
|
private boolean showArchived;
|
||||||
|
|
||||||
|
public ListHabitsMenu(@NonNull BaseActivity activity,
|
||||||
|
@NonNull ListHabitsScreen screen)
|
||||||
|
{
|
||||||
|
super(activity);
|
||||||
|
this.screen = screen;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@NonNull Menu menu)
|
||||||
|
{
|
||||||
|
MenuItem nightModeItem = menu.findItem(R.id.action_night_mode);
|
||||||
|
nightModeItem.setChecked(InterfaceUtils.isNightMode());
|
||||||
|
|
||||||
|
MenuItem showArchivedItem = menu.findItem(R.id.action_show_archived);
|
||||||
|
showArchivedItem.setChecked(showArchived);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onItemSelected(@NonNull MenuItem item)
|
||||||
|
{
|
||||||
|
switch (item.getItemId())
|
||||||
|
{
|
||||||
|
case R.id.action_night_mode:
|
||||||
|
screen.toggleNightMode();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case R.id.action_add:
|
||||||
|
screen.showCreateHabitScreen();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case R.id.action_faq:
|
||||||
|
screen.showFAQScreen();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case R.id.action_about:
|
||||||
|
screen.showAboutScreen();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case R.id.action_settings:
|
||||||
|
screen.showSettingsScreen();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case R.id.action_show_archived:
|
||||||
|
showArchived = !showArchived;
|
||||||
|
screen.getRootView().setShowArchived(showArchived);
|
||||||
|
invalidate();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getMenuResourceId()
|
||||||
|
{
|
||||||
|
return R.menu.main_activity;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,173 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.ui.habits.list;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
|
import org.isoron.uhabits.models.ModelObservable;
|
||||||
|
import org.isoron.uhabits.ui.BaseRootView;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.controllers.HabitCardListController;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.model.HabitCardListAdapter;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.model.HintList;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.views.HabitCardListView;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.views.HintView;
|
||||||
|
import org.isoron.uhabits.utils.InterfaceUtils;
|
||||||
|
|
||||||
|
import butterknife.BindView;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
|
||||||
|
public class ListHabitsRootView extends BaseRootView
|
||||||
|
implements ModelObservable.Listener
|
||||||
|
{
|
||||||
|
@BindView(R.id.listView)
|
||||||
|
HabitCardListView listView;
|
||||||
|
|
||||||
|
@BindView(R.id.llEmpty)
|
||||||
|
ViewGroup llEmpty;
|
||||||
|
|
||||||
|
@BindView(R.id.tvStarEmpty)
|
||||||
|
TextView tvStarEmpty;
|
||||||
|
|
||||||
|
@BindView(R.id.toolbar)
|
||||||
|
Toolbar toolbar;
|
||||||
|
|
||||||
|
@BindView(R.id.progressBar)
|
||||||
|
ProgressBar progressBar;
|
||||||
|
|
||||||
|
@BindView(R.id.hintView)
|
||||||
|
HintView hintView;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private HabitCardListAdapter listAdapter;
|
||||||
|
|
||||||
|
public ListHabitsRootView(@NonNull Context context)
|
||||||
|
{
|
||||||
|
super(context);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
|
public ProgressBar getProgressBar()
|
||||||
|
{
|
||||||
|
return progressBar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getShowArchived()
|
||||||
|
{
|
||||||
|
if(listAdapter == null) return false;
|
||||||
|
return listAdapter.getIncludeArchived();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Toolbar getToolbar()
|
||||||
|
{
|
||||||
|
return toolbar;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getToolbarColor()
|
||||||
|
{
|
||||||
|
return InterfaceUtils.getStyledColor(getContext(), R.attr.colorPrimary);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onModelChange()
|
||||||
|
{
|
||||||
|
updateEmptyView();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShowArchived(boolean showArchived)
|
||||||
|
{
|
||||||
|
if(listAdapter == null) return;
|
||||||
|
listAdapter.setShowArchived(showArchived);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateEmptyView()
|
||||||
|
{
|
||||||
|
if (listAdapter == null) return;
|
||||||
|
llEmpty.setVisibility(
|
||||||
|
listAdapter.getCount() > 0 ? View.GONE : View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setController(@Nullable ListHabitsController controller,
|
||||||
|
@Nullable ListHabitsSelectionMenu menu)
|
||||||
|
{
|
||||||
|
listView.setController(null);
|
||||||
|
if (controller == null || listAdapter == null) return;
|
||||||
|
|
||||||
|
HabitCardListController listController =
|
||||||
|
new HabitCardListController(listAdapter, listView);
|
||||||
|
listController.setHabitListener(controller);
|
||||||
|
listController.setSelectionListener(menu);
|
||||||
|
listView.setController(listController);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setListAdapter(@NonNull HabitCardListAdapter listAdapter)
|
||||||
|
{
|
||||||
|
if (this.listAdapter != null)
|
||||||
|
listAdapter.getObservable().removeListener(this);
|
||||||
|
|
||||||
|
this.listAdapter = listAdapter;
|
||||||
|
listView.setAdapter(listAdapter);
|
||||||
|
listAdapter.setListView(listView);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init()
|
||||||
|
{
|
||||||
|
addView(inflate(getContext(), R.layout.list_habits, null));
|
||||||
|
ButterKnife.bind(this);
|
||||||
|
|
||||||
|
tvStarEmpty.setTypeface(InterfaceUtils.getFontAwesome(getContext()));
|
||||||
|
initToolbar();
|
||||||
|
|
||||||
|
String hints[] =
|
||||||
|
getContext().getResources().getStringArray(R.array.hints);
|
||||||
|
HintList hintList = new HintList(hints);
|
||||||
|
hintView.setHints(hintList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onAttachedToWindow()
|
||||||
|
{
|
||||||
|
super.onAttachedToWindow();
|
||||||
|
|
||||||
|
updateEmptyView();
|
||||||
|
|
||||||
|
if (listAdapter != null) listAdapter.getObservable().addListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDetachedFromWindow()
|
||||||
|
{
|
||||||
|
if (listAdapter != null)
|
||||||
|
listAdapter.getObservable().removeListener(this);
|
||||||
|
super.onDetachedFromWindow();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,236 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.ui.habits.list;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
|
||||||
|
import com.android.colorpicker.ColorPickerDialog;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.HabitsApplication;
|
||||||
|
import org.isoron.uhabits.MainActivity;
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.ui.BaseActivity;
|
||||||
|
import org.isoron.uhabits.ui.BaseScreen;
|
||||||
|
import org.isoron.uhabits.ui.about.AboutActivity;
|
||||||
|
import org.isoron.uhabits.ui.habits.edit.BaseDialogFragment;
|
||||||
|
import org.isoron.uhabits.ui.habits.edit.CreateHabitDialogFragment;
|
||||||
|
import org.isoron.uhabits.ui.habits.edit.EditHabitDialogFragment;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.model.HabitCardListAdapter;
|
||||||
|
import org.isoron.uhabits.ui.habits.show.ShowHabitActivity;
|
||||||
|
import org.isoron.uhabits.ui.intro.IntroActivity;
|
||||||
|
import org.isoron.uhabits.ui.settings.FilePickerDialog;
|
||||||
|
import org.isoron.uhabits.ui.settings.SettingsActivity;
|
||||||
|
import org.isoron.uhabits.utils.ColorUtils;
|
||||||
|
import org.isoron.uhabits.utils.FileUtils;
|
||||||
|
import org.isoron.uhabits.utils.InterfaceUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public class ListHabitsScreen extends BaseScreen
|
||||||
|
{
|
||||||
|
@Nullable
|
||||||
|
ListHabitsController controller;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private final ListHabitsRootView rootView;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private final ListHabitsSelectionMenu selectionMenu;
|
||||||
|
|
||||||
|
public ListHabitsScreen(@NonNull BaseActivity activity)
|
||||||
|
{
|
||||||
|
super(activity);
|
||||||
|
rootView = new ListHabitsRootView(activity);
|
||||||
|
setRootView(rootView);
|
||||||
|
|
||||||
|
ListHabitsMenu menu = new ListHabitsMenu(activity, this);
|
||||||
|
selectionMenu = new ListHabitsSelectionMenu(this);
|
||||||
|
setMenu(menu);
|
||||||
|
setSelectionMenu(selectionMenu);
|
||||||
|
|
||||||
|
HabitCardListAdapter adapter = new HabitCardListAdapter();
|
||||||
|
rootView.setListAdapter(adapter);
|
||||||
|
selectionMenu.setAdapter(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResult(int requestCode, int resultCode, Intent data)
|
||||||
|
{
|
||||||
|
if (controller == null) return;
|
||||||
|
|
||||||
|
switch (resultCode)
|
||||||
|
{
|
||||||
|
case HabitsApplication.RESULT_IMPORT_DATA:
|
||||||
|
showImportScreen();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HabitsApplication.RESULT_EXPORT_CSV:
|
||||||
|
controller.onExportCSV();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HabitsApplication.RESULT_EXPORT_DB:
|
||||||
|
controller.onExportDB();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HabitsApplication.RESULT_BUG_REPORT:
|
||||||
|
controller.onSendBugReport();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setController(@Nullable ListHabitsController controller)
|
||||||
|
{
|
||||||
|
this.controller = controller;
|
||||||
|
rootView.setController(controller, selectionMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showAboutScreen()
|
||||||
|
{
|
||||||
|
Intent intent = new Intent(activity, AboutActivity.class);
|
||||||
|
activity.startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showColorPicker(Habit habit, OnColorSelectedListener callback)
|
||||||
|
{
|
||||||
|
int color = ColorUtils.getColor(activity, habit.color);
|
||||||
|
|
||||||
|
ColorPickerDialog picker =
|
||||||
|
ColorPickerDialog.newInstance(R.string.color_picker_default_title,
|
||||||
|
ColorUtils.getPalette(activity), color, 4,
|
||||||
|
ColorPickerDialog.SIZE_SMALL);
|
||||||
|
|
||||||
|
picker.setOnColorSelectedListener(c -> {
|
||||||
|
c = ColorUtils.colorToPaletteIndex(activity, c);
|
||||||
|
callback.onColorSelected(c);
|
||||||
|
});
|
||||||
|
picker.show(activity.getSupportFragmentManager(), "picker");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showCreateHabitScreen()
|
||||||
|
{
|
||||||
|
showDialog(new CreateHabitDialogFragment(), "editHabit");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showDeleteConfirmationScreen(Callback callback)
|
||||||
|
{
|
||||||
|
new AlertDialog.Builder(activity)
|
||||||
|
.setTitle(R.string.delete_habits)
|
||||||
|
.setMessage(R.string.delete_habits_message)
|
||||||
|
.setPositiveButton(android.R.string.yes,
|
||||||
|
(dialog, which) -> callback.run())
|
||||||
|
.setNegativeButton(android.R.string.no, null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showEditHabitScreen(Habit habit)
|
||||||
|
{
|
||||||
|
BaseDialogFragment frag =
|
||||||
|
EditHabitDialogFragment.newInstance(habit.getId());
|
||||||
|
frag.show(activity.getSupportFragmentManager(), "editHabit");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showFAQScreen()
|
||||||
|
{
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setAction(Intent.ACTION_VIEW);
|
||||||
|
intent.setData(Uri.parse(activity.getString(R.string.helpURL)));
|
||||||
|
activity.startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showHabitScreen(@NonNull Habit habit)
|
||||||
|
{
|
||||||
|
Intent intent = new Intent(activity, ShowHabitActivity.class);
|
||||||
|
intent.setData(
|
||||||
|
Uri.parse("content://org.isoron.uhabits/habit/" + habit.getId()));
|
||||||
|
activity.startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showImportScreen()
|
||||||
|
{
|
||||||
|
if (controller == null) return;
|
||||||
|
|
||||||
|
File dir = FileUtils.getFilesDir(null);
|
||||||
|
if (dir == null)
|
||||||
|
{
|
||||||
|
showMessage(R.string.could_not_import);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FilePickerDialog picker = new FilePickerDialog(activity, dir);
|
||||||
|
picker.setListener(file -> controller.onImportData(file));
|
||||||
|
picker.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showIntroScreen()
|
||||||
|
{
|
||||||
|
Intent intent = new Intent(activity, IntroActivity.class);
|
||||||
|
activity.startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showSettingsScreen()
|
||||||
|
{
|
||||||
|
Intent intent = new Intent(activity, SettingsActivity.class);
|
||||||
|
activity.startActivityForResult(intent, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void toggleNightMode()
|
||||||
|
{
|
||||||
|
if (InterfaceUtils.isNightMode())
|
||||||
|
InterfaceUtils.setCurrentTheme(InterfaceUtils.THEME_LIGHT);
|
||||||
|
else InterfaceUtils.setCurrentTheme(InterfaceUtils.THEME_DARK);
|
||||||
|
|
||||||
|
refreshTheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshTheme()
|
||||||
|
{
|
||||||
|
new Handler().postDelayed(() -> {
|
||||||
|
Intent intent = new Intent(activity, MainActivity.class);
|
||||||
|
|
||||||
|
activity.finish();
|
||||||
|
activity.overridePendingTransition(android.R.anim.fade_in,
|
||||||
|
android.R.anim.fade_out);
|
||||||
|
activity.startActivity(intent);
|
||||||
|
|
||||||
|
}, 500); // HACK: Let the menu disappear first
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Callback
|
||||||
|
{
|
||||||
|
void run();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface OnColorSelectedListener
|
||||||
|
{
|
||||||
|
void onColorSelected(int color);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public ListHabitsRootView getRootView()
|
||||||
|
{
|
||||||
|
return rootView;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,200 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.ui.habits.list;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.HabitsApplication;
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
|
import org.isoron.uhabits.commands.ArchiveHabitsCommand;
|
||||||
|
import org.isoron.uhabits.commands.ChangeHabitColorCommand;
|
||||||
|
import org.isoron.uhabits.commands.CommandRunner;
|
||||||
|
import org.isoron.uhabits.commands.DeleteHabitsCommand;
|
||||||
|
import org.isoron.uhabits.commands.UnarchiveHabitsCommand;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.ui.BaseSelectionMenu;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.controllers.HabitCardListController;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.model.HabitCardListAdapter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
public class ListHabitsSelectionMenu extends BaseSelectionMenu
|
||||||
|
implements HabitCardListController.SelectionListener
|
||||||
|
{
|
||||||
|
@NonNull
|
||||||
|
private final ListHabitsScreen screen;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CommandRunner commandRunner;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private HabitCardListAdapter adapter;
|
||||||
|
|
||||||
|
public ListHabitsSelectionMenu(@NonNull ListHabitsScreen screen)
|
||||||
|
{
|
||||||
|
this.screen = screen;
|
||||||
|
HabitsApplication.getComponent().inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy()
|
||||||
|
{
|
||||||
|
if (adapter != null) adapter.clearSelection();
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onItemClicked(@NonNull MenuItem item)
|
||||||
|
{
|
||||||
|
if (adapter == null) return false;
|
||||||
|
|
||||||
|
List<Habit> selected = adapter.getSelected();
|
||||||
|
if (selected.isEmpty()) return false;
|
||||||
|
|
||||||
|
Habit firstHabit = selected.get(0);
|
||||||
|
|
||||||
|
switch (item.getItemId())
|
||||||
|
{
|
||||||
|
case R.id.action_edit_habit:
|
||||||
|
edit(firstHabit);
|
||||||
|
finish();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case R.id.action_archive_habit:
|
||||||
|
archive(selected);
|
||||||
|
finish();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case R.id.action_unarchive_habit:
|
||||||
|
unarchive(selected);
|
||||||
|
finish();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case R.id.action_delete:
|
||||||
|
delete(selected);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case R.id.action_color:
|
||||||
|
showColorPicker(selected, firstHabit);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPrepare(@NonNull Menu menu)
|
||||||
|
{
|
||||||
|
if (adapter == null) return false;
|
||||||
|
List<Habit> selected = adapter.getSelected();
|
||||||
|
|
||||||
|
boolean showEdit = (selected.size() == 1);
|
||||||
|
boolean showArchive = true;
|
||||||
|
boolean showUnarchive = true;
|
||||||
|
for (Habit h : selected)
|
||||||
|
{
|
||||||
|
if (h.isArchived()) showArchive = false;
|
||||||
|
else showUnarchive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuItem itemEdit = menu.findItem(R.id.action_edit_habit);
|
||||||
|
MenuItem itemColor = menu.findItem(R.id.action_color);
|
||||||
|
MenuItem itemArchive = menu.findItem(R.id.action_archive_habit);
|
||||||
|
MenuItem itemUnarchive = menu.findItem(R.id.action_unarchive_habit);
|
||||||
|
|
||||||
|
itemColor.setVisible(true);
|
||||||
|
itemEdit.setVisible(showEdit);
|
||||||
|
itemArchive.setVisible(showArchive);
|
||||||
|
itemUnarchive.setVisible(showUnarchive);
|
||||||
|
|
||||||
|
setTitle(Integer.toString(selected.size()));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectionChange()
|
||||||
|
{
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectionFinish()
|
||||||
|
{
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectionStart()
|
||||||
|
{
|
||||||
|
screen.startSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAdapter(@Nullable HabitCardListAdapter adapter)
|
||||||
|
{
|
||||||
|
if (adapter == null) return;
|
||||||
|
this.adapter = adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void archive(@NonNull List<Habit> selected)
|
||||||
|
{
|
||||||
|
commandRunner.execute(new ArchiveHabitsCommand(selected), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void delete(@NonNull List<Habit> selected)
|
||||||
|
{
|
||||||
|
screen.showDeleteConfirmationScreen(() -> {
|
||||||
|
commandRunner.execute(new DeleteHabitsCommand(selected), null);
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void edit(@NonNull Habit firstHabit)
|
||||||
|
{
|
||||||
|
screen.showEditHabitScreen(firstHabit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getResourceId()
|
||||||
|
{
|
||||||
|
return R.menu.list_habits_selection;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showColorPicker(@NonNull List<Habit> selected,
|
||||||
|
@NonNull Habit firstHabit)
|
||||||
|
{
|
||||||
|
screen.showColorPicker(firstHabit, color -> {
|
||||||
|
commandRunner.execute(new ChangeHabitColorCommand(selected, color),
|
||||||
|
null);
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unarchive(@NonNull List<Habit> selected)
|
||||||
|
{
|
||||||
|
commandRunner.execute(new UnarchiveHabitsCommand(selected), null);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.ui.habits.list.controllers;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.HabitsApplication;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.views.CheckmarkButtonView;
|
||||||
|
import org.isoron.uhabits.utils.Preferences;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
public class CheckmarkButtonController
|
||||||
|
{
|
||||||
|
@Nullable
|
||||||
|
private CheckmarkButtonView view;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Listener listener;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
Preferences prefs;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private Habit habit;
|
||||||
|
|
||||||
|
private long timestamp;
|
||||||
|
|
||||||
|
public CheckmarkButtonController(@NonNull Habit habit, long timestamp)
|
||||||
|
{
|
||||||
|
this.habit = habit;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
HabitsApplication.getComponent().inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onClick()
|
||||||
|
{
|
||||||
|
if (prefs.isShortToggleEnabled()) performToggle();
|
||||||
|
else performInvalidToggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onLongClick()
|
||||||
|
{
|
||||||
|
performToggle();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void performInvalidToggle()
|
||||||
|
{
|
||||||
|
if (listener != null) listener.onInvalidToggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void performToggle()
|
||||||
|
{
|
||||||
|
if (view != null) view.toggle();
|
||||||
|
if (listener != null) listener.onToggle(habit, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setListener(@Nullable Listener listener)
|
||||||
|
{
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setView(@Nullable CheckmarkButtonView view)
|
||||||
|
{
|
||||||
|
this.view = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Listener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Called when the user's attempt to perform a toggle is rejected.
|
||||||
|
*/
|
||||||
|
void onInvalidToggle();
|
||||||
|
|
||||||
|
|
||||||
|
void onToggle(Habit habit, long timestamp);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.ui.habits.list.controllers;
|
||||||
|
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.views.HabitCardView;
|
||||||
|
|
||||||
|
public class HabitCardController implements HabitCardView.Controller
|
||||||
|
{
|
||||||
|
@Nullable
|
||||||
|
private HabitCardView view;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Listener listener;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInvalidToggle()
|
||||||
|
{
|
||||||
|
if (listener != null) listener.onInvalidToggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onToggle(Habit habit, long timestamp)
|
||||||
|
{
|
||||||
|
if (view != null) view.triggerRipple(0, 0);
|
||||||
|
if (listener != null) listener.onToggle(habit, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setListener(@Nullable Listener listener)
|
||||||
|
{
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setView(@Nullable HabitCardView view)
|
||||||
|
{
|
||||||
|
this.view = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Listener extends CheckmarkButtonController.Listener
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,317 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.ui.habits.list.controllers;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.mobeta.android.dslv.DragSortListView;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.model.HabitCardListAdapter;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.views.HabitCardListView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller responsible for receiving and processing the events generated by a
|
||||||
|
* HabitListView. These include selecting and reordering items, toggling
|
||||||
|
* checkmarks and clicking habits.
|
||||||
|
*/
|
||||||
|
public class HabitCardListController implements DragSortListView.DropListener,
|
||||||
|
DragSortListView.DragListener,
|
||||||
|
HabitCardListView.Controller
|
||||||
|
{
|
||||||
|
private final Mode NORMAL_MODE = new NormalMode();
|
||||||
|
|
||||||
|
private final Mode SELECTION_MODE = new SelectionMode();
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private final HabitCardListAdapter adapter;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private final HabitCardListView view;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private HabitListener habitListener;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private SelectionListener selectionListener;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private Mode activeMode;
|
||||||
|
|
||||||
|
public HabitCardListController(@NonNull HabitCardListAdapter adapter,
|
||||||
|
@NonNull HabitCardListView view)
|
||||||
|
{
|
||||||
|
this.adapter = adapter;
|
||||||
|
this.view = view;
|
||||||
|
this.activeMode = new NormalMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the user is dragging a habit which was originally at position
|
||||||
|
* 'from' and is currently hovering over position 'to'. Note that the user
|
||||||
|
* has not yet finished the dragging operation.
|
||||||
|
*
|
||||||
|
* @param from the original position of the habit
|
||||||
|
* @param to the position where the habit is currently hovering
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void drag(int from, int to)
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the user drags a habit and drops it somewhere. Note that the
|
||||||
|
* dragging operation is already complete.
|
||||||
|
*
|
||||||
|
* @param from the original position of the habit
|
||||||
|
* @param to the position where the habit was released
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void drop(int from, int to)
|
||||||
|
{
|
||||||
|
if (from == to) return;
|
||||||
|
cancelSelection();
|
||||||
|
|
||||||
|
Habit habitFrom = adapter.getItem(from);
|
||||||
|
Habit habitTo = adapter.getItem(to);
|
||||||
|
adapter.reorder(from, to);
|
||||||
|
|
||||||
|
if (habitListener != null)
|
||||||
|
habitListener.onHabitReorder(habitFrom, habitTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the user attempts to perform a toggle, but attempt is
|
||||||
|
* rejected.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onInvalidToggle()
|
||||||
|
{
|
||||||
|
if (habitListener != null) habitListener.onInvalidToggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the user clicks at some item.
|
||||||
|
*
|
||||||
|
* @param position the position of the clicked item
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onItemClick(int position)
|
||||||
|
{
|
||||||
|
activeMode.onItemClick(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the user long clicks at some item.
|
||||||
|
*
|
||||||
|
* @param position the position of the clicked item
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onItemLongClick(int position)
|
||||||
|
{
|
||||||
|
activeMode.onItemLongClick(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the user wants to toggle a checkmark.
|
||||||
|
*
|
||||||
|
* @param habit the habit of the checkmark
|
||||||
|
* @param timestamp the timestamps of the checkmark
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onToggle(Habit habit, long timestamp)
|
||||||
|
{
|
||||||
|
if (habitListener != null) habitListener.onToggle(habit, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHabitListener(@Nullable HabitListener habitListener)
|
||||||
|
{
|
||||||
|
this.habitListener = habitListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelectionListener(@Nullable SelectionListener listener)
|
||||||
|
{
|
||||||
|
this.selectionListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the user starts dragging an item.
|
||||||
|
*
|
||||||
|
* @param position the position of the habit dragged
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void startDrag(int position)
|
||||||
|
{
|
||||||
|
activeMode.startDrag(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks all items as not selected and finishes the selection operation.
|
||||||
|
*/
|
||||||
|
private void cancelSelection()
|
||||||
|
{
|
||||||
|
adapter.clearSelection();
|
||||||
|
view.setDragEnabled(true);
|
||||||
|
activeMode = new NormalMode();
|
||||||
|
|
||||||
|
if (selectionListener != null) selectionListener.onSelectionFinish();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects or deselects the item at a given position
|
||||||
|
*
|
||||||
|
* @param position the position of the item to be selected/deselected
|
||||||
|
*/
|
||||||
|
protected void toggleSelection(int position)
|
||||||
|
{
|
||||||
|
adapter.toggleSelection(position);
|
||||||
|
activeMode = adapter.isSelectionEmpty() ? NORMAL_MODE : SELECTION_MODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface HabitListener extends CheckmarkButtonController.Listener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Called when the user clicks a habit.
|
||||||
|
*
|
||||||
|
* @param habit the habit clicked
|
||||||
|
*/
|
||||||
|
void onHabitClick(Habit habit);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the user wants to change the position of a habit on the
|
||||||
|
* list.
|
||||||
|
*
|
||||||
|
* @param from habit to be moved
|
||||||
|
* @param to habit that currently occupies the desired position
|
||||||
|
*/
|
||||||
|
void onHabitReorder(Habit from, Habit to);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Mode describes the behaviour of the list upon clicking, long clicking
|
||||||
|
* and dragging an item. This depends on whether some items are already
|
||||||
|
* selected or not.
|
||||||
|
*/
|
||||||
|
private interface Mode
|
||||||
|
{
|
||||||
|
void onItemClick(int position);
|
||||||
|
|
||||||
|
boolean onItemLongClick(int position);
|
||||||
|
|
||||||
|
void startDrag(int position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface SelectionListener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Called when the user changes the list of selected item. This is only
|
||||||
|
* called if there were previously selected items. If the selection was
|
||||||
|
* previously empty, then onHabitSelectionStart is called instead.
|
||||||
|
*/
|
||||||
|
void onSelectionChange();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the user deselects all items or cancels the selection.
|
||||||
|
*/
|
||||||
|
void onSelectionFinish();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after the user selects the first item.
|
||||||
|
*/
|
||||||
|
void onSelectionStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mode activated when there are no items selected. Clicks trigger habit
|
||||||
|
* click. Long clicks start selection.
|
||||||
|
*/
|
||||||
|
class NormalMode implements Mode
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onItemClick(int position)
|
||||||
|
{
|
||||||
|
Habit habit = adapter.getItem(position);
|
||||||
|
if (habitListener != null) habitListener.onHabitClick(habit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onItemLongClick(int position)
|
||||||
|
{
|
||||||
|
startSelection(position);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startDrag(int position)
|
||||||
|
{
|
||||||
|
startSelection(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void startSelection(int position)
|
||||||
|
{
|
||||||
|
toggleSelection(position);
|
||||||
|
activeMode = SELECTION_MODE;
|
||||||
|
if (selectionListener != null) selectionListener.onSelectionStart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mode activated when some items are already selected.
|
||||||
|
* <p>
|
||||||
|
* Clicks toggle item selection. Long clicks select more items.
|
||||||
|
*/
|
||||||
|
class SelectionMode implements Mode
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onItemClick(int position)
|
||||||
|
{
|
||||||
|
toggleSelection(position);
|
||||||
|
notifyListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onItemLongClick(int position)
|
||||||
|
{
|
||||||
|
toggleSelection(position);
|
||||||
|
notifyListener();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startDrag(int position)
|
||||||
|
{
|
||||||
|
toggleSelection(position);
|
||||||
|
notifyListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void notifyListener()
|
||||||
|
{
|
||||||
|
if (habitListener == null) return;
|
||||||
|
|
||||||
|
if (activeMode == SELECTION_MODE)
|
||||||
|
selectionListener.onSelectionChange();
|
||||||
|
else
|
||||||
|
selectionListener.onSelectionFinish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains controllers that are specific for ListHabitsActivity
|
||||||
|
*/
|
||||||
|
package org.isoron.uhabits.ui.habits.list.controllers;
|
@ -0,0 +1,222 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.ui.habits.list.model;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.BaseAdapter;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.HabitsApplication;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.models.ModelObservable;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.views.HabitCardListView;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.views.HabitCardView;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides data that backs a {@link HabitCardListView}. The data if fetched and
|
||||||
|
* cached by a {@link HabitCardListCache}. This adapter also holds a list of
|
||||||
|
* items that have been selected.
|
||||||
|
*/
|
||||||
|
public class HabitCardListAdapter extends BaseAdapter
|
||||||
|
implements HabitCardListCache.Listener
|
||||||
|
{
|
||||||
|
@NonNull
|
||||||
|
private ModelObservable observable;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@NonNull
|
||||||
|
HabitCardListCache cache;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private HabitCardListView listView;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private final LinkedList<Habit> selected;
|
||||||
|
|
||||||
|
public HabitCardListAdapter()
|
||||||
|
{
|
||||||
|
this.selected = new LinkedList<>();
|
||||||
|
this.observable = new ModelObservable();
|
||||||
|
|
||||||
|
HabitsApplication.getComponent().inject(this);
|
||||||
|
|
||||||
|
cache.setListener(this);
|
||||||
|
cache.setCheckmarkCount(5); // TODO: make this dynamic somehow
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets all items as not selected.
|
||||||
|
*/
|
||||||
|
public void clearSelection()
|
||||||
|
{
|
||||||
|
selected.clear();
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount()
|
||||||
|
{
|
||||||
|
return cache.getHabitCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getIncludeArchived()
|
||||||
|
{
|
||||||
|
return cache.getIncludeArchived();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the item that occupies a certain position on the list
|
||||||
|
*
|
||||||
|
* @param position position of the item
|
||||||
|
* @return the item at given position
|
||||||
|
* @throws IndexOutOfBoundsException if position is not valid
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
|
public Habit getItem(int position)
|
||||||
|
{
|
||||||
|
return cache.getHabitByPosition(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getItemId(int position)
|
||||||
|
{
|
||||||
|
return getItem(position).getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public ModelObservable getObservable()
|
||||||
|
{
|
||||||
|
return observable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public List<Habit> getSelected()
|
||||||
|
{
|
||||||
|
return new LinkedList<>(selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getView(int position,
|
||||||
|
@Nullable View view,
|
||||||
|
@Nullable ViewGroup parent)
|
||||||
|
{
|
||||||
|
if (listView == null) return null;
|
||||||
|
|
||||||
|
Habit habit = cache.getHabitByPosition(position);
|
||||||
|
int score = cache.getScore(habit.getId());
|
||||||
|
int checkmarks[] = cache.getCheckmarks(habit.getId());
|
||||||
|
boolean selected = this.selected.contains(habit);
|
||||||
|
|
||||||
|
return listView.buildCardView((HabitCardView) view, habit, score,
|
||||||
|
checkmarks, selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether list of selected items is empty.
|
||||||
|
*
|
||||||
|
* @return true if selection is empty, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean isSelectionEmpty()
|
||||||
|
{
|
||||||
|
return selected.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the adapter that it has been attached to a ListView.
|
||||||
|
*/
|
||||||
|
public void onAttached()
|
||||||
|
{
|
||||||
|
cache.onAttached();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCacheRefresh()
|
||||||
|
{
|
||||||
|
notifyDataSetChanged();
|
||||||
|
observable.notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the adapter that it has been detached from a ListView.
|
||||||
|
*/
|
||||||
|
public void onDetached()
|
||||||
|
{
|
||||||
|
cache.onDetached();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the order of habits on the adapter.
|
||||||
|
* <p>
|
||||||
|
* Note that this only has effect on the adapter cache. The database is not
|
||||||
|
* modified, and the change is lost when the cache is refreshed. This method
|
||||||
|
* is useful for making the ListView more responsive: while we wait for the
|
||||||
|
* database operation to finish, the cache can be modified to reflect the
|
||||||
|
* changes immediately.
|
||||||
|
*
|
||||||
|
* @param from the habit that should be moved
|
||||||
|
* @param to the habit that currently occupies the desired position
|
||||||
|
*/
|
||||||
|
public void reorder(int from, int to)
|
||||||
|
{
|
||||||
|
cache.reorder(from, to);
|
||||||
|
cache.refreshAllHabits(false);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the HabitCardListView that this adapter will provide data for.
|
||||||
|
* <p>
|
||||||
|
* This object will be used to generated new HabitCardViews, upon demand.
|
||||||
|
*
|
||||||
|
* @param listView the HabitCardListView associated with this adapter
|
||||||
|
*/
|
||||||
|
public void setListView(@Nullable HabitCardListView listView)
|
||||||
|
{
|
||||||
|
this.listView = listView;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects or deselects the item at a given position.
|
||||||
|
*
|
||||||
|
* @param position position of the item to be toggled
|
||||||
|
*/
|
||||||
|
public void toggleSelection(int position)
|
||||||
|
{
|
||||||
|
Habit h = getItem(position);
|
||||||
|
int k = selected.indexOf(h);
|
||||||
|
if (k < 0) selected.add(h);
|
||||||
|
else selected.remove(h);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShowArchived(boolean showArchived)
|
||||||
|
{
|
||||||
|
cache.setIncludeArchived(showArchived);
|
||||||
|
cache.refreshAllHabits(true);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,334 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.ui.habits.list.model;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.HabitsApplication;
|
||||||
|
import org.isoron.uhabits.commands.Command;
|
||||||
|
import org.isoron.uhabits.commands.CommandRunner;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.tasks.BaseTask;
|
||||||
|
import org.isoron.uhabits.utils.DateUtils;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A HabitCardListCache fetches and keeps a cache of all the data necessary to
|
||||||
|
* render a HabitCardListView.
|
||||||
|
* <p>
|
||||||
|
* This is needed since performing database lookups during scrolling can make
|
||||||
|
* the ListView very slow. It also registers itself as an observer of the
|
||||||
|
* models, in order to update itself automatically.
|
||||||
|
*/
|
||||||
|
public class HabitCardListCache implements CommandRunner.Listener
|
||||||
|
{
|
||||||
|
boolean includeArchived;
|
||||||
|
|
||||||
|
private int checkmarkCount;
|
||||||
|
|
||||||
|
private BaseTask currentFetchTask;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Listener listener;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Long lastLoadTimestamp;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private CacheData data;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CommandRunner commandRunner;
|
||||||
|
|
||||||
|
public HabitCardListCache()
|
||||||
|
{
|
||||||
|
data = new CacheData();
|
||||||
|
HabitsApplication.getComponent().inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] getCheckmarks(long habitId)
|
||||||
|
{
|
||||||
|
return data.checkmarks.get(habitId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the habits that occupies a certain position on the list.
|
||||||
|
*
|
||||||
|
* @param position the position of the habit
|
||||||
|
* @return the habit at given position
|
||||||
|
* @throws IndexOutOfBoundsException if position is not valid
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public Habit getHabitByPosition(int position)
|
||||||
|
{
|
||||||
|
return data.habitsList.get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHabitCount()
|
||||||
|
{
|
||||||
|
return data.habits.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Long getLastLoadTimestamp()
|
||||||
|
{
|
||||||
|
return lastLoadTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getScore(long habitId)
|
||||||
|
{
|
||||||
|
return data.scores.get(habitId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getIncludeArchived()
|
||||||
|
{
|
||||||
|
return includeArchived;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onAttached()
|
||||||
|
{
|
||||||
|
// refreshAllHabits(true);
|
||||||
|
if (lastLoadTimestamp == null) refreshAllHabits(true);
|
||||||
|
commandRunner.addListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCommandExecuted(@NonNull Command command,
|
||||||
|
@Nullable Long refreshKey)
|
||||||
|
{
|
||||||
|
if (refreshKey == null) refreshAllHabits(true);
|
||||||
|
else refreshHabit(refreshKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDetached()
|
||||||
|
{
|
||||||
|
// commandRunner.removeListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refreshAllHabits(final boolean refreshScoresAndCheckmarks)
|
||||||
|
{
|
||||||
|
if (currentFetchTask != null) currentFetchTask.cancel(true);
|
||||||
|
currentFetchTask = new RefreshAllHabitsTask(refreshScoresAndCheckmarks);
|
||||||
|
currentFetchTask.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refreshHabit(final Long id)
|
||||||
|
{
|
||||||
|
new RefreshHabitTask(id).execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reorder(int from, int to)
|
||||||
|
{
|
||||||
|
Habit fromHabit = data.habitsList.get(from);
|
||||||
|
Habit toHabit = data.habitsList.get(to);
|
||||||
|
|
||||||
|
data.habitsList.remove(from);
|
||||||
|
data.habitsList.add(to, fromHabit);
|
||||||
|
|
||||||
|
Habit.reorder(fromHabit, toHabit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCheckmarkCount(int checkmarkCount)
|
||||||
|
{
|
||||||
|
this.checkmarkCount = checkmarkCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIncludeArchived(boolean includeArchived)
|
||||||
|
{
|
||||||
|
this.includeArchived = includeArchived;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setListener(@Nullable Listener listener)
|
||||||
|
{
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface definition for a callback to be invoked when the data on the
|
||||||
|
* cache has been modified.
|
||||||
|
*/
|
||||||
|
public interface Listener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Called when the data on the cache has been modified.
|
||||||
|
*/
|
||||||
|
void onCacheRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CacheData
|
||||||
|
{
|
||||||
|
@NonNull
|
||||||
|
public HashMap<Long, Habit> habits;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public List<Habit> habitsList;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public HashMap<Long, int[]> checkmarks;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public HashMap<Long, Integer> scores;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new CacheData without any content.
|
||||||
|
*/
|
||||||
|
public CacheData()
|
||||||
|
{
|
||||||
|
habits = new HashMap<>();
|
||||||
|
habitsList = new LinkedList<>();
|
||||||
|
checkmarks = new HashMap<>();
|
||||||
|
scores = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void copyCheckmarksFrom(@NonNull CacheData oldData)
|
||||||
|
{
|
||||||
|
int[] empty = new int[checkmarkCount];
|
||||||
|
|
||||||
|
for (Long id : habits.keySet())
|
||||||
|
{
|
||||||
|
if (oldData.checkmarks.containsKey(id))
|
||||||
|
checkmarks.put(id, oldData.checkmarks.get(id));
|
||||||
|
else checkmarks.put(id, empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void copyScoresFrom(@NonNull CacheData oldData)
|
||||||
|
{
|
||||||
|
for (Long id : habits.keySet())
|
||||||
|
{
|
||||||
|
if (oldData.scores.containsKey(id))
|
||||||
|
scores.put(id, oldData.scores.get(id));
|
||||||
|
else scores.put(id, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fetchHabits()
|
||||||
|
{
|
||||||
|
habitsList = Habit.getAll(includeArchived);
|
||||||
|
for (Habit h : habitsList)
|
||||||
|
habits.put(h.getId(), h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RefreshAllHabitsTask extends BaseTask
|
||||||
|
{
|
||||||
|
@NonNull
|
||||||
|
private CacheData newData;
|
||||||
|
|
||||||
|
private final boolean refreshScoresAndCheckmarks;
|
||||||
|
|
||||||
|
public RefreshAllHabitsTask(boolean refreshScoresAndCheckmarks)
|
||||||
|
{
|
||||||
|
this.refreshScoresAndCheckmarks = refreshScoresAndCheckmarks;
|
||||||
|
newData = new CacheData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void commit()
|
||||||
|
{
|
||||||
|
data = newData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doInBackground()
|
||||||
|
{
|
||||||
|
newData.fetchHabits();
|
||||||
|
newData.copyScoresFrom(data);
|
||||||
|
newData.copyCheckmarksFrom(data);
|
||||||
|
|
||||||
|
commit();
|
||||||
|
|
||||||
|
if (!refreshScoresAndCheckmarks) return;
|
||||||
|
|
||||||
|
long dateTo = DateUtils.getStartOfDay(DateUtils.getLocalTime());
|
||||||
|
long dateFrom =
|
||||||
|
dateTo - (checkmarkCount - 1) * DateUtils.millisecondsInOneDay;
|
||||||
|
|
||||||
|
int current = 0;
|
||||||
|
for (Habit h : newData.habitsList)
|
||||||
|
{
|
||||||
|
if (isCancelled()) return;
|
||||||
|
|
||||||
|
Long id = h.getId();
|
||||||
|
newData.scores.put(id, h.scores.getTodayValue());
|
||||||
|
newData.checkmarks.put(id,
|
||||||
|
h.checkmarks.getValues(dateFrom, dateTo));
|
||||||
|
|
||||||
|
publishProgress(current++, newData.habits.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Void aVoid)
|
||||||
|
{
|
||||||
|
if (isCancelled()) return;
|
||||||
|
|
||||||
|
lastLoadTimestamp = DateUtils.getStartOfToday();
|
||||||
|
currentFetchTask = null;
|
||||||
|
|
||||||
|
if (listener != null) listener.onCacheRefresh();
|
||||||
|
super.onPostExecute(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onProgressUpdate(Integer... values)
|
||||||
|
{
|
||||||
|
if (listener != null) listener.onCacheRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RefreshHabitTask extends BaseTask
|
||||||
|
{
|
||||||
|
private final Long id;
|
||||||
|
|
||||||
|
public RefreshHabitTask(Long id)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doInBackground()
|
||||||
|
{
|
||||||
|
long dateTo = DateUtils.getStartOfDay(DateUtils.getLocalTime());
|
||||||
|
long dateFrom =
|
||||||
|
dateTo - (checkmarkCount - 1) * DateUtils.millisecondsInOneDay;
|
||||||
|
|
||||||
|
Habit h = Habit.get(id);
|
||||||
|
if (h == null) return;
|
||||||
|
|
||||||
|
data.habits.put(id, h);
|
||||||
|
data.scores.put(id, h.scores.getTodayValue());
|
||||||
|
data.checkmarks.put(id, h.checkmarks.getValues(dateFrom, dateTo));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Void aVoid)
|
||||||
|
{
|
||||||
|
if (listener != null) listener.onCacheRefresh();
|
||||||
|
super.onPostExecute(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.ui.habits.list.model;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.HabitsApplication;
|
||||||
|
import org.isoron.uhabits.utils.DateUtils;
|
||||||
|
import org.isoron.uhabits.utils.Preferences;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a list of hints to be shown at the application startup, and takes
|
||||||
|
* care of deciding when a new hint should be shown.
|
||||||
|
*/
|
||||||
|
public class HintList
|
||||||
|
{
|
||||||
|
@Inject
|
||||||
|
Preferences prefs;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private final String[] hints;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new list containing the provided hints.
|
||||||
|
*
|
||||||
|
* @param hints initial list of hints
|
||||||
|
*/
|
||||||
|
public HintList(@NonNull String hints[])
|
||||||
|
{
|
||||||
|
this.hints = hints;
|
||||||
|
HabitsApplication.getComponent().inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new hint to be shown to the user.
|
||||||
|
* <p>
|
||||||
|
* The hint returned is marked as read on the list, and will not be returned
|
||||||
|
* again. In case all hints have already been read, and there is nothing
|
||||||
|
* left, returns null.
|
||||||
|
*
|
||||||
|
* @return the next hint to be shown, or null if none
|
||||||
|
*/
|
||||||
|
public String pop()
|
||||||
|
{
|
||||||
|
int next = prefs.getLastHintNumber() + 1;
|
||||||
|
if (next >= hints.length) return null;
|
||||||
|
|
||||||
|
prefs.updateLastHint(next, DateUtils.getStartOfToday());
|
||||||
|
return hints[next];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether it is time to show a new hint or not.
|
||||||
|
*
|
||||||
|
* @return true if hint should be shown, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean shouldShow()
|
||||||
|
{
|
||||||
|
long lastHintTimestamp = prefs.getLastHintTimestamp();
|
||||||
|
return (DateUtils.getStartOfToday() > lastHintTimestamp);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains model classes that are specific for ListHabitsActivity
|
||||||
|
*/
|
||||||
|
package org.isoron.uhabits.ui.habits.list.model;
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains classes for ListHabitsActivity.
|
||||||
|
*/
|
||||||
|
package org.isoron.uhabits.ui.habits.list;
|
@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.ui.habits.list.views;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.view.HapticFeedbackConstants;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
|
import org.isoron.uhabits.models.Checkmark;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.controllers.CheckmarkButtonController;
|
||||||
|
import org.isoron.uhabits.utils.InterfaceUtils;
|
||||||
|
|
||||||
|
import butterknife.BindView;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
|
||||||
|
public class CheckmarkButtonView extends FrameLayout
|
||||||
|
{
|
||||||
|
private int color;
|
||||||
|
|
||||||
|
private int value;
|
||||||
|
|
||||||
|
@BindView(R.id.tvCheck)
|
||||||
|
TextView tvCheck;
|
||||||
|
|
||||||
|
public CheckmarkButtonView(Context context)
|
||||||
|
{
|
||||||
|
super(context);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setColor(int color)
|
||||||
|
{
|
||||||
|
this.color = color;
|
||||||
|
postInvalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setController(final CheckmarkButtonController controller)
|
||||||
|
{
|
||||||
|
setOnClickListener(v -> controller.onClick());
|
||||||
|
setOnLongClickListener(v -> controller.onLongClick());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(int value)
|
||||||
|
{
|
||||||
|
this.value = value;
|
||||||
|
postInvalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void toggle()
|
||||||
|
{
|
||||||
|
value = (value == Checkmark.CHECKED_EXPLICITLY ? Checkmark.UNCHECKED :
|
||||||
|
Checkmark.CHECKED_EXPLICITLY);
|
||||||
|
|
||||||
|
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||||
|
postInvalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init()
|
||||||
|
{
|
||||||
|
addView(
|
||||||
|
inflate(getContext(), R.layout.list_habits_card_checkmark, null));
|
||||||
|
ButterKnife.bind(this);
|
||||||
|
|
||||||
|
setWillNotDraw(false);
|
||||||
|
setHapticFeedbackEnabled(false);
|
||||||
|
|
||||||
|
tvCheck.setTypeface(InterfaceUtils.getFontAwesome(getContext()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas)
|
||||||
|
{
|
||||||
|
int lowContrastColor = InterfaceUtils.getStyledColor(getContext(),
|
||||||
|
R.attr.lowContrastTextColor);
|
||||||
|
|
||||||
|
if (value == Checkmark.CHECKED_EXPLICITLY)
|
||||||
|
{
|
||||||
|
tvCheck.setText(R.string.fa_check);
|
||||||
|
tvCheck.setTextColor(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == Checkmark.CHECKED_IMPLICITLY)
|
||||||
|
{
|
||||||
|
tvCheck.setText(R.string.fa_check);
|
||||||
|
tvCheck.setTextColor(lowContrastColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == Checkmark.UNCHECKED)
|
||||||
|
{
|
||||||
|
tvCheck.setText(R.string.fa_times);
|
||||||
|
tvCheck.setTextColor(lowContrastColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.onDraw(canvas);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,190 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.ui.habits.list.views;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.HabitsApplication;
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.controllers.CheckmarkButtonController;
|
||||||
|
import org.isoron.uhabits.utils.DateUtils;
|
||||||
|
import org.isoron.uhabits.utils.Preferences;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
public class CheckmarkPanelView extends LinearLayout
|
||||||
|
{
|
||||||
|
private static final int CHECKMARK_LEFT_TO_RIGHT = 0;
|
||||||
|
|
||||||
|
private static final int CHECKMARK_RIGHT_TO_LEFT = 1;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
Preferences prefs;
|
||||||
|
|
||||||
|
private int checkmarkValues[];
|
||||||
|
|
||||||
|
private int nButtons;
|
||||||
|
|
||||||
|
private int color;
|
||||||
|
|
||||||
|
private Controller controller;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private Habit habit;
|
||||||
|
|
||||||
|
public CheckmarkPanelView(Context context)
|
||||||
|
{
|
||||||
|
super(context);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CheckmarkPanelView(Context context, AttributeSet attrs)
|
||||||
|
{
|
||||||
|
super(context, attrs);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CheckmarkPanelView(Context context,
|
||||||
|
AttributeSet attrs,
|
||||||
|
int defStyleAttr)
|
||||||
|
{
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CheckmarkButtonView getButton(int position)
|
||||||
|
{
|
||||||
|
return (CheckmarkButtonView) getChildAt(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCheckmarkValues(int[] checkmarkValues)
|
||||||
|
{
|
||||||
|
this.checkmarkValues = checkmarkValues;
|
||||||
|
|
||||||
|
if (this.nButtons != checkmarkValues.length)
|
||||||
|
{
|
||||||
|
this.nButtons = checkmarkValues.length;
|
||||||
|
addCheckmarkButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
setupCheckmarkButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setColor(int color)
|
||||||
|
{
|
||||||
|
this.color = color;
|
||||||
|
setupCheckmarkButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setController(Controller controller)
|
||||||
|
{
|
||||||
|
this.controller = controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHabit(@NonNull Habit habit)
|
||||||
|
{
|
||||||
|
this.habit = habit;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addCheckmarkButtons()
|
||||||
|
{
|
||||||
|
removeAllViews();
|
||||||
|
|
||||||
|
for (int i = 0; i < nButtons; i++)
|
||||||
|
addView(new CheckmarkButtonView(getContext()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getCheckmarkOrder()
|
||||||
|
{
|
||||||
|
if (isInEditMode()) return CHECKMARK_LEFT_TO_RIGHT;
|
||||||
|
return prefs.shouldReverseCheckmarks() ? CHECKMARK_RIGHT_TO_LEFT :
|
||||||
|
CHECKMARK_LEFT_TO_RIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CheckmarkButtonView indexToButton(int i)
|
||||||
|
{
|
||||||
|
int position = i;
|
||||||
|
|
||||||
|
if (getCheckmarkOrder() == CHECKMARK_RIGHT_TO_LEFT)
|
||||||
|
position = nButtons - i - 1;
|
||||||
|
|
||||||
|
return (CheckmarkButtonView) getChildAt(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init()
|
||||||
|
{
|
||||||
|
if (isInEditMode()) return;
|
||||||
|
HabitsApplication.getComponent().inject(this);
|
||||||
|
setWillNotDraw(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
||||||
|
{
|
||||||
|
float buttonWidth = getResources().getDimension(R.dimen.checkmarkWidth);
|
||||||
|
float buttonHeight =
|
||||||
|
getResources().getDimension(R.dimen.checkmarkHeight);
|
||||||
|
|
||||||
|
float width = buttonWidth * nButtons;
|
||||||
|
|
||||||
|
widthMeasureSpec =
|
||||||
|
MeasureSpec.makeMeasureSpec((int) width, MeasureSpec.EXACTLY);
|
||||||
|
heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) buttonHeight,
|
||||||
|
MeasureSpec.EXACTLY);
|
||||||
|
|
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupButtonControllers(long timestamp,
|
||||||
|
CheckmarkButtonView buttonView)
|
||||||
|
{
|
||||||
|
if (controller == null) return;
|
||||||
|
|
||||||
|
CheckmarkButtonController buttonController =
|
||||||
|
new CheckmarkButtonController(habit, timestamp);
|
||||||
|
|
||||||
|
buttonController.setListener(controller);
|
||||||
|
buttonController.setView(buttonView);
|
||||||
|
buttonView.setController(buttonController);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupCheckmarkButtons()
|
||||||
|
{
|
||||||
|
long timestamp = DateUtils.getStartOfToday();
|
||||||
|
long day = DateUtils.millisecondsInOneDay;
|
||||||
|
|
||||||
|
for (int i = 0; i < nButtons; i++)
|
||||||
|
{
|
||||||
|
CheckmarkButtonView buttonView = indexToButton(i);
|
||||||
|
buttonView.setValue(checkmarkValues[i]);
|
||||||
|
buttonView.setColor(color);
|
||||||
|
setupButtonControllers(timestamp, buttonView);
|
||||||
|
timestamp -= day;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Controller extends CheckmarkButtonController.Listener
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.ui.habits.list.views;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ListAdapter;
|
||||||
|
|
||||||
|
import com.mobeta.android.dslv.DragSortController;
|
||||||
|
import com.mobeta.android.dslv.DragSortListView;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.controllers.CheckmarkButtonController;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.controllers.HabitCardController;
|
||||||
|
import org.isoron.uhabits.ui.habits.list.model.HabitCardListAdapter;
|
||||||
|
|
||||||
|
public class HabitCardListView extends DragSortListView
|
||||||
|
{
|
||||||
|
@Nullable
|
||||||
|
private HabitCardListAdapter adapter;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Controller controller;
|
||||||
|
|
||||||
|
public HabitCardListView(Context context, AttributeSet attrs)
|
||||||
|
{
|
||||||
|
super(context, attrs);
|
||||||
|
setFloatViewManager(new ViewManager());
|
||||||
|
setDragEnabled(true);
|
||||||
|
setLongClickable(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a new HabitCardView to be eventually added to this list,
|
||||||
|
* containing the given data.
|
||||||
|
*
|
||||||
|
* @param cardView an old HabitCardView that should be reused if possible,
|
||||||
|
* possibly null
|
||||||
|
* @param habit the habit for this card
|
||||||
|
* @param score the current score for the habit
|
||||||
|
* @param checkmarks the list of checkmark values to be included in the
|
||||||
|
* card
|
||||||
|
* @param selected true if the card is selected, false otherwise
|
||||||
|
* @return the HabitCardView generated
|
||||||
|
*/
|
||||||
|
public View buildCardView(@Nullable HabitCardView cardView,
|
||||||
|
@NonNull Habit habit,
|
||||||
|
int score,
|
||||||
|
int[] checkmarks,
|
||||||
|
boolean selected)
|
||||||
|
{
|
||||||
|
if (cardView == null) cardView = new HabitCardView(getContext());
|
||||||
|
|
||||||
|
cardView.setHabit(habit);
|
||||||
|
cardView.setSelected(selected);
|
||||||
|
cardView.setCheckmarkValues(checkmarks);
|
||||||
|
cardView.setScore(score);
|
||||||
|
|
||||||
|
if (controller != null)
|
||||||
|
{
|
||||||
|
HabitCardController cardController = new HabitCardController();
|
||||||
|
cardController.setListener(controller);
|
||||||
|
cardView.setController(cardController);
|
||||||
|
cardController.setView(cardView);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cardView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAdapter(ListAdapter adapter)
|
||||||
|
{
|
||||||
|
this.adapter = (HabitCardListAdapter) adapter;
|
||||||
|
super.setAdapter(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setController(@Nullable Controller controller)
|
||||||
|
{
|
||||||
|
this.controller = controller;
|
||||||
|
setDropListener(controller);
|
||||||
|
setDragListener(controller);
|
||||||
|
setOnItemClickListener(null);
|
||||||
|
setOnLongClickListener(null);
|
||||||
|
|
||||||
|
if (controller == null) return;
|
||||||
|
|
||||||
|
setOnItemClickListener((p, v, pos, id) -> controller.onItemClick(pos));
|
||||||
|
setOnItemLongClickListener((p, v, pos, id) -> {
|
||||||
|
controller.onItemLongClick(pos);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void toggleShowArchived()
|
||||||
|
{
|
||||||
|
// showArchived = !showArchived;
|
||||||
|
// cache.setIncludeArchived(showArchived);
|
||||||
|
// cache.refreshAllHabits(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onAttachedToWindow()
|
||||||
|
{
|
||||||
|
super.onAttachedToWindow();
|
||||||
|
if (adapter != null) adapter.onAttached();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDetachedFromWindow()
|
||||||
|
{
|
||||||
|
if (adapter != null) adapter.onDetached();
|
||||||
|
super.onDetachedFromWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Controller extends CheckmarkButtonController.Listener,
|
||||||
|
HabitCardController.Listener,
|
||||||
|
DropListener,
|
||||||
|
DragListener
|
||||||
|
{
|
||||||
|
void onItemClick(int pos);
|
||||||
|
|
||||||
|
void onItemLongClick(int pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ViewManager extends DragSortController
|
||||||
|
{
|
||||||
|
public ViewManager()
|
||||||
|
{
|
||||||
|
super(HabitCardListView.this);
|
||||||
|
setRemoveEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateFloatView(int position)
|
||||||
|
{
|
||||||
|
if (adapter == null) return null;
|
||||||
|
return adapter.getView(position, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyFloatView(View floatView)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,216 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.ui.habits.list.views;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.models.Score;
|
||||||
|
import org.isoron.uhabits.utils.ColorUtils;
|
||||||
|
import org.isoron.uhabits.views.RingView;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import butterknife.BindView;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
|
||||||
|
import static org.isoron.uhabits.utils.InterfaceUtils.getStyledColor;
|
||||||
|
import static org.isoron.uhabits.utils.InterfaceUtils.getStyledDrawable;
|
||||||
|
|
||||||
|
public class HabitCardView extends FrameLayout
|
||||||
|
{
|
||||||
|
private Habit habit;
|
||||||
|
|
||||||
|
@BindView(R.id.checkmarkPanel)
|
||||||
|
CheckmarkPanelView checkmarkPanel;
|
||||||
|
|
||||||
|
@BindView(R.id.innerFrame)
|
||||||
|
LinearLayout innerFrame;
|
||||||
|
|
||||||
|
@BindView(R.id.label)
|
||||||
|
TextView label;
|
||||||
|
|
||||||
|
@BindView(R.id.scoreRing)
|
||||||
|
RingView scoreRing;
|
||||||
|
|
||||||
|
private final Context context = getContext();
|
||||||
|
|
||||||
|
public HabitCardView(Context context)
|
||||||
|
{
|
||||||
|
super(context);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public HabitCardView(Context context, AttributeSet attrs)
|
||||||
|
{
|
||||||
|
super(context, attrs);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public HabitCardView(Context context, AttributeSet attrs, int defStyleAttr)
|
||||||
|
{
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCheckmarkValues(int checkmarks[])
|
||||||
|
{
|
||||||
|
checkmarkPanel.setCheckmarkValues(checkmarks);
|
||||||
|
postInvalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setController(Controller controller)
|
||||||
|
{
|
||||||
|
checkmarkPanel.setController(null);
|
||||||
|
if (controller == null) return;
|
||||||
|
|
||||||
|
checkmarkPanel.setController(controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHabit(Habit habit)
|
||||||
|
{
|
||||||
|
this.habit = habit;
|
||||||
|
int color = getActiveColor(habit);
|
||||||
|
|
||||||
|
label.setText(habit.name);
|
||||||
|
label.setTextColor(color);
|
||||||
|
scoreRing.setColor(color);
|
||||||
|
checkmarkPanel.setColor(color);
|
||||||
|
checkmarkPanel.setHabit(habit);
|
||||||
|
|
||||||
|
postInvalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScore(int score)
|
||||||
|
{
|
||||||
|
float percentage = (float) score / Score.MAX_VALUE;
|
||||||
|
scoreRing.setPercentage(percentage);
|
||||||
|
scoreRing.setPrecision(1.0f / 16);
|
||||||
|
postInvalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSelected(boolean isSelected)
|
||||||
|
{
|
||||||
|
super.setSelected(isSelected);
|
||||||
|
updateBackground(isSelected);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void triggerRipple(final float x, final float y)
|
||||||
|
{
|
||||||
|
final Drawable background = innerFrame.getBackground();
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= 21) background.setHotspot(x, y);
|
||||||
|
background.setState(new int[]{
|
||||||
|
android.R.attr.state_pressed, android.R.attr.state_enabled
|
||||||
|
});
|
||||||
|
new Handler().postDelayed(() -> background.setState(new int[]{}), 25);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getActiveColor(Habit habit)
|
||||||
|
{
|
||||||
|
int mediumContrastColor =
|
||||||
|
getStyledColor(context, R.attr.mediumContrastTextColor);
|
||||||
|
int activeColor = ColorUtils.getColor(context, habit.color);
|
||||||
|
if (habit.isArchived()) activeColor = mediumContrastColor;
|
||||||
|
|
||||||
|
return activeColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init()
|
||||||
|
{
|
||||||
|
setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
|
||||||
|
inflate(context, R.layout.list_habits_card, this);
|
||||||
|
ButterKnife.bind(this);
|
||||||
|
|
||||||
|
innerFrame.setOnTouchListener((v, event) -> {
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= 21)
|
||||||
|
v.getBackground().setHotspot(event.getX(), event.getY());
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isInEditMode()) initEditMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
|
private void initEditMode()
|
||||||
|
{
|
||||||
|
String habits[] = {
|
||||||
|
"Wake up early",
|
||||||
|
"Wash dishes",
|
||||||
|
"Exercise",
|
||||||
|
"Meditate",
|
||||||
|
"Play guitar",
|
||||||
|
"Wash clothes",
|
||||||
|
"Get a haircut"
|
||||||
|
};
|
||||||
|
|
||||||
|
Random rand = new Random();
|
||||||
|
int color = ColorUtils.CSV_PALETTE[rand.nextInt(10)];
|
||||||
|
int[] values = {
|
||||||
|
rand.nextInt(3),
|
||||||
|
rand.nextInt(3),
|
||||||
|
rand.nextInt(3),
|
||||||
|
rand.nextInt(3),
|
||||||
|
rand.nextInt(3)
|
||||||
|
};
|
||||||
|
|
||||||
|
label.setText(habits[rand.nextInt(habits.length)]);
|
||||||
|
label.setTextColor(color);
|
||||||
|
scoreRing.setColor(color);
|
||||||
|
scoreRing.setPercentage(rand.nextFloat());
|
||||||
|
checkmarkPanel.setColor(color);
|
||||||
|
checkmarkPanel.setCheckmarkValues(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateBackground(boolean isSelected)
|
||||||
|
{
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= 21)
|
||||||
|
{
|
||||||
|
if (isSelected)
|
||||||
|
innerFrame.setBackgroundResource(R.drawable.selected_box);
|
||||||
|
else innerFrame.setBackgroundResource(R.drawable.ripple);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Drawable background;
|
||||||
|
|
||||||
|
if (isSelected) background =
|
||||||
|
getStyledDrawable(context, R.attr.selectedBackground);
|
||||||
|
else background = getStyledDrawable(context, R.attr.cardBackground);
|
||||||
|
|
||||||
|
innerFrame.setBackgroundDrawable(background);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Controller extends CheckmarkPanelView.Controller
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.ui.habits.list.views;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
|
import org.isoron.uhabits.utils.DateUtils;
|
||||||
|
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
|
||||||
|
public class HeaderView extends LinearLayout
|
||||||
|
{
|
||||||
|
private static final int CHECKMARK_LEFT_TO_RIGHT = 0;
|
||||||
|
|
||||||
|
private static final int CHECKMARK_RIGHT_TO_LEFT = 1;
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
|
public HeaderView(Context context, AttributeSet attrs)
|
||||||
|
{
|
||||||
|
super(context, attrs);
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createButtons()
|
||||||
|
{
|
||||||
|
removeAllViews();
|
||||||
|
GregorianCalendar day = DateUtils.getStartOfTodayCalendar();
|
||||||
|
|
||||||
|
for (int i = 0; i < getButtonCount(); i++)
|
||||||
|
{
|
||||||
|
int position = 0;
|
||||||
|
|
||||||
|
if (getCheckmarkOrder() == CHECKMARK_LEFT_TO_RIGHT) position = i;
|
||||||
|
|
||||||
|
View tvDay =
|
||||||
|
inflate(context, R.layout.list_habits_header_checkmark, null);
|
||||||
|
TextView btCheck = (TextView) tvDay.findViewById(R.id.tvCheck);
|
||||||
|
btCheck.setText(DateUtils.formatHeaderDate(day));
|
||||||
|
addView(tvDay, position);
|
||||||
|
day.add(GregorianCalendar.DAY_OF_MONTH, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getButtonCount()
|
||||||
|
{
|
||||||
|
float labelWidth = getResources().getDimension(R.dimen.habitNameWidth);
|
||||||
|
float buttonWidth = getResources().getDimension(R.dimen.checkmarkWidth);
|
||||||
|
return Math.max(0,
|
||||||
|
(int) ((getMeasuredWidth() - labelWidth) / buttonWidth));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getCheckmarkOrder()
|
||||||
|
{
|
||||||
|
if (isInEditMode()) return CHECKMARK_LEFT_TO_RIGHT;
|
||||||
|
|
||||||
|
SharedPreferences prefs =
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||||
|
boolean reverse =
|
||||||
|
prefs.getBoolean("pref_checkmark_reverse_order", false);
|
||||||
|
return reverse ? CHECKMARK_RIGHT_TO_LEFT : CHECKMARK_LEFT_TO_RIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
||||||
|
{
|
||||||
|
createButtons();
|
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains classes for the IntroActivity.
|
||||||
|
*/
|
||||||
|
package org.isoron.uhabits.ui.intro;
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains classes for the SettingsActivity.
|
||||||
|
*/
|
||||||
|
package org.isoron.uhabits.ui.settings;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue