Move ListHabits controllers to uhabits-core

This commit is contained in:
2017-05-27 12:11:05 -04:00
parent 94c70485b7
commit 70423ddb0a
60 changed files with 799 additions and 367 deletions

View File

@@ -23,10 +23,10 @@ import android.view.*;
import org.isoron.androidbase.activities.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.activities.*;
import org.isoron.uhabits.activities.habits.list.model.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.preferences.*;
import org.isoron.uhabits.ui.*;
import org.junit.*;
import org.mockito.*;
@@ -63,7 +63,7 @@ public class ListHabitsMenuTest extends BaseAndroidTest
when(preferences.getShowCompleted()).thenReturn(false);
when(themeSwitcher.isNightMode()).thenReturn(false);
menu = new ListHabitsMenu(activity, screen, adapter, preferences,
menu = new ListHabitsMenu(activity, preferences,
themeSwitcher);
matcherCaptor = ArgumentCaptor.forClass(HabitMatcher.class);

View File

@@ -24,7 +24,6 @@ import android.content.*;
import org.isoron.androidbase.activities.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.activities.*;
import org.isoron.uhabits.activities.common.dialogs.*;
import org.isoron.uhabits.activities.common.dialogs.ColorPickerDialog.*;
import org.isoron.uhabits.activities.habits.edit.*;
@@ -32,6 +31,7 @@ import org.isoron.uhabits.commands.*;
import org.isoron.uhabits.intents.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.preferences.*;
import org.isoron.uhabits.ui.*;
import org.junit.*;
import org.junit.runner.*;
import org.junit.runners.*;
@@ -89,7 +89,7 @@ public class ListHabitsScreenTest extends BaseAndroidTest
screen = spy(new ListHabitsScreen(activity, commandRunner, rootView,
intentFactory, themeSwitcher, confirmDeleteDialogFactory,
colorPickerDialogFactory, dialogFactory, prefs, commandParser));
colorPickerDialogFactory, dialogFactory, prefs));
doNothing().when(screen).showMessage(anyInt());
@@ -122,7 +122,7 @@ public class ListHabitsScreenTest extends BaseAndroidTest
public void testOnCommand()
{
Command c = mock(Command.class);
when(commandParser.getExecuteString(c)).thenReturn(
when(getExecuteString(c)).thenReturn(
R.string.toast_habit_deleted);
screen.onCommandExecuted(c, null);
verify(screen).showMessage(R.string.toast_habit_deleted);
@@ -251,10 +251,9 @@ public class ListHabitsScreenTest extends BaseAndroidTest
}
@Test
public void testToggleNightMode()
public void testApplyTheme()
{
screen.toggleNightMode();
verify(themeSwitcher).toggleNightMode();
screen.applyTheme();
verify(activity).restartWithFade(ListHabitsActivity.class);
}
}

View File

@@ -20,8 +20,8 @@
package org.isoron.uhabits.activities.habits.list.controllers;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.activities.habits.list.model.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.activities.habits.list.views.*;
import org.junit.*;

View File

@@ -23,6 +23,7 @@ import org.isoron.uhabits.*;
import org.isoron.uhabits.commands.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.tasks.*;
import org.isoron.uhabits.ui.screens.habits.list.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;

View File

@@ -23,7 +23,7 @@ import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.activities.habits.list.model.*;
import org.isoron.uhabits.ui.screens.habits.list.*;
import org.junit.*;
import org.junit.runner.*;

View File

@@ -35,7 +35,7 @@ public class ReminderControllerTest extends BaseAndroidTest
private ReminderScheduler reminderScheduler;
private NotificationTray notificationTray;
private AndroidNotificationTray notificationTray;
private AndroidPreferences preferences;
@@ -45,7 +45,7 @@ public class ReminderControllerTest extends BaseAndroidTest
super.setUp();
reminderScheduler = mock(ReminderScheduler.class);
notificationTray = mock(NotificationTray.class);
notificationTray = mock(AndroidNotificationTray.class);
preferences = mock(AndroidPreferences.class);
controller = new ReminderController(reminderScheduler,

View File

@@ -23,6 +23,7 @@ import org.isoron.uhabits.*;
import org.isoron.uhabits.commands.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.notifications.*;
import org.isoron.uhabits.ui.widgets.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
@@ -33,7 +34,7 @@ import static org.mockito.Mockito.*;
public class WidgetControllerTest extends BaseAndroidTest
{
private WidgetController controller;
private WidgetBehavior controller;
private CommandRunner commandRunner;
@@ -41,7 +42,7 @@ public class WidgetControllerTest extends BaseAndroidTest
private long today;
private NotificationTray notificationTray;
private AndroidNotificationTray notificationTray;
@Override
public void setUp()
@@ -52,8 +53,8 @@ public class WidgetControllerTest extends BaseAndroidTest
habit = fixtures.createEmptyHabit();
habitList.add(habit);
commandRunner = mock(CommandRunner.class);
notificationTray = mock(NotificationTray.class);
controller = new WidgetController(commandRunner, notificationTray);
notificationTray = mock(AndroidNotificationTray.class);
controller = new WidgetBehavior(commandRunner, notificationTray);
}
@Test

View File

@@ -28,8 +28,8 @@ import android.view.*;
import org.isoron.androidbase.utils.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.ui.habits.list.*;
import org.isoron.uhabits.ui.habits.show.*;
import org.isoron.uhabits.ui.screens.habits.list.*;
import org.isoron.uhabits.ui.screens.habits.show.*;
import org.isoron.uhabits.utils.*;
import java.io.*;

View File

@@ -20,8 +20,8 @@
package org.isoron.androidbase.activities;
import org.isoron.uhabits.*;
import org.isoron.uhabits.activities.*;
import org.isoron.uhabits.activities.common.dialogs.*;
import org.isoron.uhabits.ui.*;
import dagger.*;

View File

@@ -21,6 +21,9 @@ package org.isoron.androidbase.activities;
import android.content.*;
import org.isoron.uhabits.activities.*;
import org.isoron.uhabits.ui.*;
import dagger.*;
@Module
@@ -45,4 +48,11 @@ public class ActivityModule
{
return activity;
}
@Provides
@ActivityScope
public static ThemeSwitcher getThemeSwitcher(AndroidThemeSwitcher t)
{
return t;
}
}

View File

@@ -27,7 +27,7 @@ import android.view.*;
import android.widget.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.activities.*;
import org.isoron.uhabits.ui.*;
import org.isoron.uhabits.utils.*;
import static android.os.Build.VERSION.*;

View File

@@ -47,7 +47,7 @@ public class HabitsApplication extends Application
private ReminderScheduler reminderScheduler;
private NotificationTray notificationTray;
private AndroidNotificationTray notificationTray;
public HabitsComponent getComponent()
{
@@ -106,7 +106,7 @@ public class HabitsApplication extends Application
reminderScheduler = component.getReminderScheduler();
reminderScheduler.startListening();
notificationTray = component.getNotificationTray();
notificationTray = component.getAndroidNotificationTray();
notificationTray.startListening();
AndroidPreferences prefs = component.getPreferences();

View File

@@ -22,7 +22,6 @@ package org.isoron.uhabits;
import android.content.*;
import org.isoron.androidbase.*;
import org.isoron.uhabits.activities.habits.list.model.*;
import org.isoron.uhabits.commands.*;
import org.isoron.uhabits.intents.*;
import org.isoron.uhabits.io.*;
@@ -33,6 +32,8 @@ import org.isoron.uhabits.preferences.*;
import org.isoron.uhabits.sync.*;
import org.isoron.uhabits.tasks.*;
import org.isoron.uhabits.tasks.android.*;
import org.isoron.uhabits.ui.*;
import org.isoron.uhabits.ui.screens.habits.list.*;
import org.isoron.uhabits.utils.*;
import org.isoron.uhabits.widgets.*;
@@ -47,8 +48,6 @@ import dagger.*;
})
public interface HabitsComponent
{
AndroidPreferences getPreferences();
BaseSystem getBaseSystem();
CommandRunner getCommandRunner();
@@ -56,6 +55,8 @@ public interface HabitsComponent
@AppContext
Context getContext();
Preferences getCorePreferences();
CreateHabitCommandFactory getCreateHabitCommandFactory();
EditHabitCommandFactory getEditHabitCommandFactory();
@@ -80,7 +81,9 @@ public interface HabitsComponent
PendingIntentFactory getPendingIntentFactory();
Preferences getCorePreferences();
AndroidPreferences getPreferences();
AndroidNotificationTray getAndroidNotificationTray();
ReminderScheduler getReminderScheduler();

View File

@@ -19,7 +19,9 @@
package org.isoron.uhabits;
import org.isoron.uhabits.notifications.*;
import org.isoron.uhabits.preferences.*;
import org.isoron.uhabits.ui.*;
import dagger.*;
@@ -32,4 +34,13 @@ public class HabitsModule
{
return preferences;
}
@Provides
@AppScope
public static NotificationTray getTray(AndroidNotificationTray tray)
{
return tray;
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.activities;
import android.support.annotation.*;
import org.isoron.androidbase.activities.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.preferences.*;
import org.isoron.uhabits.ui.*;
import javax.inject.*;
@ActivityScope
public class AndroidThemeSwitcher extends ThemeSwitcher
{
@NonNull
private final BaseActivity activity;
@Inject
public AndroidThemeSwitcher(@NonNull BaseActivity activity,
@NonNull Preferences preferences)
{
super(preferences);
this.activity = activity;
}
@Override
public void applyDarkTheme()
{
activity.setTheme(R.style.AppBaseThemeDark);
}
@Override
public void applyLightTheme()
{
activity.setTheme(R.style.AppBaseTheme);
}
@Override
public void applyPureBlackTheme()
{
activity.setTheme(R.style.AppBaseThemeDark_PureBlack);
}
}

View File

@@ -1,102 +0,0 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.activities;
import android.support.annotation.*;
import org.isoron.androidbase.activities.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.preferences.*;
import javax.inject.*;
@ActivityScope
public class ThemeSwitcher
{
public static final int THEME_DARK = 1;
public static final int THEME_LIGHT = 0;
@NonNull
private final BaseActivity activity;
private AndroidPreferences preferences;
@Inject
public ThemeSwitcher(@NonNull BaseActivity activity,
@NonNull AndroidPreferences preferences)
{
this.activity = activity;
this.preferences = preferences;
}
public void apply()
{
switch (getTheme())
{
case THEME_DARK:
applyDarkTheme();
break;
case THEME_LIGHT:
default:
applyLightTheme();
break;
}
}
public boolean isNightMode()
{
return getTheme() == THEME_DARK;
}
public void refreshTheme()
{
}
public void toggleNightMode()
{
if (isNightMode()) setTheme(THEME_LIGHT);
else setTheme(THEME_DARK);
}
private void applyDarkTheme()
{
if (preferences.isPureBlackEnabled())
activity.setTheme(R.style.AppBaseThemeDark_PureBlack);
else activity.setTheme(R.style.AppBaseThemeDark);
}
private void applyLightTheme()
{
activity.setTheme(R.style.AppBaseTheme);
}
private int getTheme()
{
return preferences.getTheme();
}
public void setTheme(int theme)
{
preferences.setTheme(theme);
}
}

View File

@@ -23,7 +23,7 @@ import android.os.*;
import org.isoron.androidbase.activities.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.ui.about.*;
import org.isoron.uhabits.ui.screens.about.*;
/**
* Activity that allows the user to see information about the app itself.

View File

@@ -26,7 +26,7 @@ import android.widget.*;
import org.isoron.androidbase.activities.*;
import org.isoron.uhabits.BuildConfig;
import org.isoron.uhabits.R;
import org.isoron.uhabits.ui.about.*;
import org.isoron.uhabits.ui.screens.about.*;
import org.isoron.uhabits.utils.*;
import butterknife.*;
@@ -36,15 +36,6 @@ public class AboutRootView extends BaseRootView
@BindView(R.id.tvVersion)
TextView tvVersion;
@BindView(R.id.tvRate)
TextView tvRate;
@BindView(R.id.tvFeedback)
TextView tvFeedback;
@BindView(R.id.tvSource)
TextView tvSource;
@NonNull
private final AboutBehavior behavior;

View File

@@ -24,7 +24,7 @@ import android.widget.*;
import org.isoron.androidbase.activities.*;
import org.isoron.uhabits.intents.*;
import org.isoron.uhabits.ui.about.*;
import org.isoron.uhabits.ui.screens.about.*;
import javax.inject.*;

View File

@@ -19,6 +19,7 @@
package org.isoron.uhabits.activities.common.dialogs;
import org.isoron.uhabits.ui.callbacks.*;
import org.isoron.uhabits.utils.*;
/**
@@ -26,16 +27,12 @@ import org.isoron.uhabits.utils.*;
*/
public class ColorPickerDialog extends com.android.colorpicker.ColorPickerDialog
{
public void setListener(OnColorSelectedListener listener)
public void setListener(OnColorPickedCallback callback)
{
super.setOnColorSelectedListener(c -> {
super.setOnColorSelectedListener(c ->
{
c = ColorUtils.colorToPaletteIndex(getContext(), c);
listener.onColorSelected(c);
callback.onColorPicked(c);
});
}
public interface OnColorSelectedListener
{
void onColorSelected(int color);
}
}

View File

@@ -20,12 +20,14 @@
package org.isoron.uhabits.activities.common.dialogs;
import android.content.*;
import android.support.annotation.*;
import android.support.v7.app.*;
import com.google.auto.factory.*;
import org.isoron.androidbase.activities.*;
import org.isoron.uhabits.R;
import org.isoron.uhabits.ui.callbacks.*;
import butterknife.*;
@@ -45,19 +47,14 @@ public class ConfirmDeleteDialog extends AlertDialog
protected String no;
protected ConfirmDeleteDialog(@Provided @ActivityContext Context context,
Callback callback)
@NonNull OnConfirmedCallback callback)
{
super(context);
ButterKnife.bind(this);
setTitle(R.string.delete_habits);
setMessage(question);
setButton(BUTTON_POSITIVE, yes, (dialog, which) -> callback.run());
setButton(BUTTON_POSITIVE, yes, (dialog, which) -> callback.onConfirmed());
setButton(BUTTON_NEGATIVE, no, (dialog, which) -> {});
}
public interface Callback
{
void run();
}
}

View File

@@ -24,10 +24,10 @@ import android.os.*;
import org.isoron.androidbase.activities.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.activities.*;
import org.isoron.uhabits.activities.habits.list.model.*;
import org.isoron.uhabits.preferences.*;
import org.isoron.uhabits.sync.*;
import org.isoron.uhabits.ui.*;
import org.isoron.uhabits.utils.*;
/**
@@ -81,6 +81,7 @@ public class ListHabitsActivity extends BaseActivity
screen.setMenu(menu);
screen.setController(controller);
screen.setListController(component.getListController());
screen.setSelectionMenu(selectionMenu);
rootView.setController(controller, selectionMenu);

View File

@@ -37,6 +37,8 @@ public interface ListHabitsComponent
ListHabitsController getController();
HabitCardListController getListController();
ListHabitsMenu getMenu();
NumberButtonControllerFactory getNumberButtonControllerFactory();

View File

@@ -28,7 +28,7 @@ import org.isoron.uhabits.activities.habits.list.model.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.tasks.*;
import org.isoron.uhabits.tasks.android.*;
import org.isoron.uhabits.ui.habits.list.*;
import org.isoron.uhabits.ui.screens.habits.list.*;
import java.io.*;

View File

@@ -24,58 +24,44 @@ import android.view.*;
import org.isoron.androidbase.activities.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.activities.*;
import org.isoron.uhabits.activities.habits.list.model.*;
import org.isoron.uhabits.preferences.*;
import org.isoron.uhabits.ui.*;
import org.isoron.uhabits.ui.screens.habits.list.*;
import javax.inject.*;
@ActivityScope
public class ListHabitsMenu extends BaseMenu
{
@NonNull
private final ListHabitsScreen screen;
private final ListHabitsMenuBehavior behavior;
private final HabitCardListAdapter adapter;
private boolean showArchived;
private boolean showCompleted;
private final AndroidPreferences preferences;
private final Preferences preferences;
private ThemeSwitcher themeSwitcher;
@Inject
public ListHabitsMenu(@NonNull BaseActivity activity,
@NonNull ListHabitsScreen screen,
@NonNull HabitCardListAdapter adapter,
@NonNull AndroidPreferences preferences,
@NonNull ThemeSwitcher themeSwitcher)
@NonNull Preferences preferences,
@NonNull ThemeSwitcher themeSwitcher,
@NonNull ListHabitsMenuBehavior behavior)
{
super(activity);
this.screen = screen;
this.adapter = adapter;
this.preferences = preferences;
this.themeSwitcher = themeSwitcher;
showCompleted = preferences.getShowCompleted();
showArchived = preferences.getShowArchived();
updateAdapterFilter();
this.behavior = behavior;
}
@Override
public void onCreate(@NonNull Menu menu)
{
MenuItem nightModeItem = menu.findItem(R.id.actionToggleNightMode);
nightModeItem.setChecked(themeSwitcher.isNightMode());
MenuItem hideArchivedItem = menu.findItem(R.id.actionHideArchived);
hideArchivedItem.setChecked(!showArchived);
MenuItem hideCompletedItem = menu.findItem(R.id.actionHideCompleted);
hideCompletedItem.setChecked(!showCompleted);
nightModeItem.setChecked(themeSwitcher.isNightMode());
hideArchivedItem.setChecked(!preferences.getShowArchived());
hideCompletedItem.setChecked(!preferences.getShowCompleted());
}
@Override
@@ -84,49 +70,49 @@ public class ListHabitsMenu extends BaseMenu
switch (item.getItemId())
{
case R.id.actionToggleNightMode:
screen.toggleNightMode();
behavior.onToggleNightMode();
return true;
case R.id.actionAdd:
screen.showCreateHabitScreen();
behavior.onCreateHabit();
return true;
case R.id.actionFAQ:
screen.showFAQScreen();
behavior.onViewFAQ();
return true;
case R.id.actionAbout:
screen.showAboutScreen();
behavior.onViewAbout();
return true;
case R.id.actionSettings:
screen.showSettingsScreen();
behavior.onViewSettings();
return true;
case R.id.actionHideArchived:
toggleShowArchived();
behavior.onToggleShowArchived();
invalidate();
return true;
case R.id.actionHideCompleted:
toggleShowCompleted();
behavior.onToggleShowCompleted();
invalidate();
return true;
case R.id.actionSortColor:
adapter.setOrder(HabitList.Order.BY_COLOR);
behavior.onSortByColor();
return true;
case R.id.actionSortManual:
adapter.setOrder(HabitList.Order.BY_POSITION);
behavior.onSortByManually();
return true;
case R.id.actionSortName:
adapter.setOrder(HabitList.Order.BY_NAME);
behavior.onSortByName();
return true;
case R.id.actionSortScore:
adapter.setOrder(HabitList.Order.BY_SCORE);
behavior.onSortByScore();
return true;
default:
@@ -140,26 +126,4 @@ public class ListHabitsMenu extends BaseMenu
return R.menu.list_habits;
}
private void toggleShowArchived()
{
showArchived = !showArchived;
preferences.setShowArchived(showArchived);
updateAdapterFilter();
}
private void toggleShowCompleted()
{
showCompleted = !showCompleted;
preferences.setShowCompleted(showCompleted);
updateAdapterFilter();
}
private void updateAdapterFilter()
{
adapter.setFilter(new HabitMatcherBuilder()
.setArchivedAllowed(showArchived)
.setCompletedAllowed(showCompleted)
.build());
adapter.refresh();
}
}

View File

@@ -22,7 +22,8 @@ package org.isoron.uhabits.activities.habits.list;
import org.isoron.androidbase.*;
import org.isoron.androidbase.activities.*;
import org.isoron.uhabits.ui.habits.list.*;
import org.isoron.uhabits.activities.habits.list.model.*;
import org.isoron.uhabits.ui.screens.habits.list.*;
import dagger.*;
@@ -34,12 +35,37 @@ public class ListHabitsModule extends ActivityModule
super(activity);
}
@Provides
ListHabitsMenuBehavior.Adapter getAdapter(HabitCardListAdapter adapter)
{
return adapter;
}
@Provides
ListHabitsMenuBehavior.Screen getMenuScreen(ListHabitsScreen screen)
{
return screen;
}
@Provides
ListHabitsBehavior.Screen getScreen(ListHabitsScreen screen)
{
return screen;
}
@Provides
ListHabitsSelectionMenuBehavior.Adapter getSelMenuAdapter(
HabitCardListAdapter adapter)
{
return adapter;
}
@Provides
ListHabitsSelectionMenuBehavior.Screen getSelMenuScreen(ListHabitsScreen screen)
{
return screen;
}
@Provides
ListHabitsBehavior.System getSystem(BaseSystem system)
{

View File

@@ -33,6 +33,7 @@ import org.isoron.uhabits.activities.habits.list.model.*;
import org.isoron.uhabits.activities.habits.list.views.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.tasks.*;
import org.isoron.uhabits.ui.screens.habits.list.*;
import org.isoron.uhabits.utils.*;
import javax.inject.*;
@@ -134,7 +135,8 @@ public class ListHabitsRootView extends BaseRootView
listController.setSelectionListener(menu);
listView.setController(listController);
menu.setListController(listController);
header.setScrollController(new ScrollableChart.ScrollController() {
header.setScrollController(new ScrollableChart.ScrollController()
{
@Override
public void onDataOffsetChanged(int newDataOffset)
{
@@ -187,7 +189,8 @@ public class ListHabitsRootView extends BaseRootView
private void updateProgressBar()
{
postDelayed(() -> {
postDelayed(() ->
{
int activeTaskCount = runner.getActiveTaskCount();
int newVisibility = activeTaskCount > 0 ? VISIBLE : GONE;
if (progressBar.getVisibility() != newVisibility)

View File

@@ -31,19 +31,21 @@ import android.widget.*;
import org.isoron.androidbase.activities.*;
import org.isoron.androidbase.utils.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.activities.*;
import org.isoron.uhabits.activities.common.dialogs.*;
import org.isoron.uhabits.activities.common.dialogs.ColorPickerDialog.*;
import org.isoron.uhabits.activities.habits.edit.*;
import org.isoron.uhabits.activities.habits.list.controllers.*;
import org.isoron.uhabits.commands.*;
import org.isoron.uhabits.intents.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.preferences.*;
import org.isoron.uhabits.ui.habits.list.*;
import org.isoron.uhabits.ui.*;
import org.isoron.uhabits.ui.callbacks.*;
import org.isoron.uhabits.ui.screens.habits.list.*;
import org.isoron.uhabits.utils.*;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import javax.inject.*;
@@ -52,7 +54,9 @@ import static android.view.inputmethod.EditorInfo.*;
@ActivityScope
public class ListHabitsScreen extends BaseScreen
implements CommandRunner.Listener, ListHabitsBehavior.Screen
implements CommandRunner.Listener, ListHabitsBehavior.Screen,
ListHabitsMenuBehavior.Screen,
ListHabitsSelectionMenuBehavior.Screen
{
public static final int REQUEST_OPEN_DOCUMENT = 6;
@@ -92,8 +96,10 @@ public class ListHabitsScreen extends BaseScreen
@NonNull
private AndroidPreferences prefs;
@NonNull
private final CommandParser commandParser;
@Nullable
private HabitCardListController listController;
private final ListHabitsRootView rootView;
@Inject
public ListHabitsScreen(@NonNull BaseActivity activity,
@@ -101,18 +107,14 @@ public class ListHabitsScreen extends BaseScreen
@NonNull ListHabitsRootView rootView,
@NonNull IntentFactory intentFactory,
@NonNull ThemeSwitcher themeSwitcher,
@NonNull
ConfirmDeleteDialogFactory confirmDeleteDialogFactory,
@NonNull
ColorPickerDialogFactory colorPickerFactory,
@NonNull
EditHabitDialogFactory editHabitDialogFactory,
@NonNull AndroidPreferences prefs,
@NonNull CommandParser commandParser)
@NonNull ConfirmDeleteDialogFactory confirmDeleteDialogFactory,
@NonNull ColorPickerDialogFactory colorPickerFactory,
@NonNull EditHabitDialogFactory editHabitDialogFactory,
@NonNull AndroidPreferences prefs)
{
super(activity);
this.commandParser = commandParser;
setRootView(rootView);
this.rootView = rootView;
this.prefs = prefs;
this.colorPickerFactory = colorPickerFactory;
this.commandRunner = commandRunner;
@@ -122,6 +124,35 @@ public class ListHabitsScreen extends BaseScreen
this.themeSwitcher = themeSwitcher;
}
public void setListController(HabitCardListController listController)
{
this.listController = listController;
}
@StringRes
private Integer getExecuteString(@NonNull Command command)
{
if(command instanceof ArchiveHabitsCommand)
return R.string.toast_habit_archived;
if(command instanceof ChangeHabitColorCommand)
return R.string.toast_habit_changed;
if(command instanceof CreateHabitCommand)
return R.string.toast_habit_created;
if(command instanceof DeleteHabitsCommand)
return R.string.toast_habit_deleted;
if(command instanceof EditHabitCommand)
return R.string.toast_habit_changed;
if(command instanceof UnarchiveHabitsCommand)
return R.string.toast_habit_unarchived;
return null;
}
public void onAttached()
{
commandRunner.addListener(this);
@@ -132,7 +163,7 @@ public class ListHabitsScreen extends BaseScreen
@Nullable Long refreshKey)
{
if (command.isRemote()) return;
showMessage(commandParser.getExecuteString(command));
showMessage(getExecuteString(command));
}
public void onDettached()
@@ -154,24 +185,24 @@ public class ListHabitsScreen extends BaseScreen
this.controller = controller;
}
@Override
public void applyTheme()
{
themeSwitcher.apply();
activity.restartWithFade(ListHabitsActivity.class);
}
@Override
public void showAboutScreen()
{
Intent intent = intentFactory.startAboutActivity(activity);
activity.startActivity(intent);
}
/**
* Displays a {@link ColorPickerDialog} to the user.
* <p>
* The selected color on the dialog is the color of the given habit.
*
* @param habit the habit
* @param callback
*/
public void showColorPicker(@NonNull Habit habit,
@NonNull OnColorSelectedListener callback)
{
ColorPickerDialog picker = colorPickerFactory.create(habit.getColor());
@Override
public void showColorPicker(int defaultColor,
@NonNull OnColorPickedCallback callback) {
ColorPickerDialog picker = colorPickerFactory.create(defaultColor);
picker.setListener(callback);
activity.showDialog(picker, "picker");
}
@@ -183,6 +214,7 @@ public class ListHabitsScreen extends BaseScreen
activity.showDialog(dialog, "editHabit");
}
@Override
public void showCreateHabitScreen()
{
if (!prefs.isNumericalHabitsFeatureEnabled())
@@ -203,18 +235,22 @@ public class ListHabitsScreen extends BaseScreen
dialog.show();
}
public void showDeleteConfirmationScreen(ConfirmDeleteDialog.Callback callback)
@Override
public void showDeleteConfirmationScreen(
@NonNull OnConfirmedCallback callback)
{
activity.showDialog(confirmDeleteDialogFactory.create(callback));
}
public void showEditHabitScreen(Habit habit)
@Override
public void showEditHabitsScreen(List<Habit> habits)
{
EditHabitDialog dialog;
dialog = editHabitDialogFactory.edit(habit);
dialog = editHabitDialogFactory.edit(habits.get(0));
activity.showDialog(dialog, "editNumericalHabit");
}
@Override
public void showFAQScreen()
{
Intent intent = intentFactory.viewFAQ(activity);
@@ -275,7 +311,8 @@ public class ListHabitsScreen extends BaseScreen
@Override
public void showNumberPicker(double value,
@NonNull String unit,
@NonNull ListHabitsBehavior.NumberPickerCallback callback)
@NonNull
ListHabitsBehavior.NumberPickerCallback callback)
{
LayoutInflater inflater = activity.getLayoutInflater();
View view = inflater.inflate(R.layout.number_picker_dialog, null);
@@ -332,18 +369,13 @@ public class ListHabitsScreen extends BaseScreen
showSendEmailScreen(to, subject, log);
}
@Override
public void showSettingsScreen()
{
Intent intent = intentFactory.startSettingsActivity(activity);
activity.startActivityForResult(intent, REQUEST_SETTINGS);
}
public void toggleNightMode()
{
themeSwitcher.toggleNightMode();
activity.restartWithFade(ListHabitsActivity.class);
}
private void onOpenDocumentResult(int resultCode, Intent data)
{
if (controller == null) return;

View File

@@ -24,12 +24,10 @@ import android.view.*;
import org.isoron.androidbase.activities.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.commands.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.activities.habits.list.controllers.*;
import org.isoron.uhabits.activities.habits.list.model.*;
import java.util.*;
import org.isoron.uhabits.commands.*;
import org.isoron.uhabits.ui.screens.habits.list.*;
import javax.inject.*;
@@ -43,25 +41,24 @@ public class ListHabitsSelectionMenu extends BaseSelectionMenu
@NonNull
CommandRunner commandRunner;
private ListHabitsSelectionMenuBehavior behavior;
@NonNull
private final HabitCardListAdapter listAdapter;
@Nullable
private HabitCardListController listController;
@NonNull
private final HabitList habitList;
@Inject
public ListHabitsSelectionMenu(@NonNull HabitList habitList,
@NonNull ListHabitsScreen screen,
public ListHabitsSelectionMenu(@NonNull ListHabitsScreen screen,
@NonNull HabitCardListAdapter listAdapter,
@NonNull CommandRunner commandRunner)
@NonNull CommandRunner commandRunner,
@NonNull ListHabitsSelectionMenuBehavior behavior)
{
this.habitList = habitList;
this.screen = screen;
this.listAdapter = listAdapter;
this.commandRunner = commandRunner;
this.behavior = behavior;
}
@Override
@@ -74,34 +71,26 @@ public class ListHabitsSelectionMenu extends BaseSelectionMenu
@Override
public boolean onItemClicked(@NonNull MenuItem item)
{
List<Habit> selected = listAdapter.getSelected();
if (selected.isEmpty()) return false;
Habit firstHabit = selected.get(0);
switch (item.getItemId())
{
case R.id.action_edit_habit:
showEditScreen(firstHabit);
finish();
behavior.onEditHabits();
return true;
case R.id.action_archive_habit:
performArchive(selected);
finish();
behavior.onArchiveHabits();
return true;
case R.id.action_unarchive_habit:
performUnarchive(selected);
finish();
behavior.onUnarchiveHabits();
return true;
case R.id.action_delete:
performDelete(selected);
behavior.onDeleteHabits();
return true;
case R.id.action_color:
showColorPicker(selected, firstHabit);
behavior.onChangeColor();
return true;
default:
@@ -112,28 +101,16 @@ public class ListHabitsSelectionMenu extends BaseSelectionMenu
@Override
public boolean onPrepare(@NonNull Menu menu)
{
List<Habit> selected = listAdapter.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()));
itemEdit.setVisible(behavior.canEdit());
itemArchive.setVisible(behavior.canArchive());
itemUnarchive.setVisible(behavior.canUnarchive());
setTitle(Integer.toString(listAdapter.getSelected().size()));
return true;
}
@@ -166,41 +143,4 @@ public class ListHabitsSelectionMenu extends BaseSelectionMenu
{
return R.menu.list_habits_selection;
}
private void performArchive(@NonNull List<Habit> selected)
{
commandRunner.execute(new ArchiveHabitsCommand(habitList, selected),
null);
}
private void performDelete(@NonNull List<Habit> selected)
{
screen.showDeleteConfirmationScreen(() -> {
listAdapter.performRemove(selected);
commandRunner.execute(new DeleteHabitsCommand(habitList, selected),
null);
finish();
});
}
private void performUnarchive(@NonNull List<Habit> selected)
{
commandRunner.execute(new UnarchiveHabitsCommand(habitList, selected),
null);
}
private void showColorPicker(@NonNull List<Habit> selected,
@NonNull Habit firstHabit)
{
screen.showColorPicker(firstHabit, color -> {
commandRunner.execute(
new ChangeHabitColorCommand(habitList, selected, color), null);
finish();
});
}
private void showEditScreen(@NonNull Habit firstHabit)
{
screen.showEditHabitScreen(firstHabit);
}
}

View File

@@ -21,16 +21,21 @@ package org.isoron.uhabits.activities.habits.list.controllers;
import android.support.annotation.*;
import org.isoron.androidbase.activities.*;
import org.isoron.uhabits.activities.habits.list.model.*;
import org.isoron.uhabits.activities.habits.list.views.*;
import org.isoron.uhabits.models.*;
import javax.inject.*;
/**
* Controller responsible for receiving and processing the events generated by a
* HabitListView. These include selecting and reordering items, toggling
* checkmarks and clicking habits.
*/
public class HabitCardListController implements HabitCardListView.Controller
@ActivityScope
public class HabitCardListController implements HabitCardListView.Controller,
ModelObservable.Listener
{
private final Mode NORMAL_MODE = new NormalMode();
@@ -48,10 +53,12 @@ public class HabitCardListController implements HabitCardListView.Controller
@NonNull
private Mode activeMode;
@Inject
public HabitCardListController(@NonNull HabitCardListAdapter adapter)
{
this.adapter = adapter;
this.activeMode = new NormalMode();
adapter.getObservable().addListener(this);
}
/**
@@ -119,6 +126,16 @@ public class HabitCardListController implements HabitCardListView.Controller
activeMode.onItemLongClick(position);
}
@Override
public void onModelChange()
{
if(adapter.isSelectionEmpty())
{
activeMode = new NormalMode();
if (selectionListener != null) selectionListener.onSelectionFinish();
}
}
/**
* Called when the selection operation is cancelled externally, by something
* other than this controller. This happens, for example, when the user
@@ -180,7 +197,6 @@ public class HabitCardListController implements HabitCardListView.Controller
{
adapter.clearSelection();
activeMode = new NormalMode();
if (selectionListener != null) selectionListener.onSelectionFinish();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
@@ -28,6 +28,7 @@ import org.isoron.uhabits.activities.habits.list.*;
import org.isoron.uhabits.activities.habits.list.views.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.preferences.*;
import org.isoron.uhabits.ui.screens.habits.list.*;
import org.isoron.uhabits.utils.*;
import java.util.*;
@@ -42,8 +43,11 @@ import javax.inject.*;
*/
@ActivityScope
public class HabitCardListAdapter
extends RecyclerView.Adapter<HabitCardViewHolder>
implements HabitCardListCache.Listener, MidnightTimer.MidnightListener
extends RecyclerView.Adapter<HabitCardViewHolder> implements
HabitCardListCache.Listener,
MidnightTimer.MidnightListener,
ListHabitsMenuBehavior.Adapter,
ListHabitsSelectionMenuBehavior.Adapter
{
@NonNull
private ModelObservable observable;
@@ -95,10 +99,12 @@ public class HabitCardListAdapter
/**
* Sets all items as not selected.
*/
@Override
public void clearSelection()
{
selected.clear();
notifyDataSetChanged();
observable.notifyListeners();
}
/**
@@ -133,6 +139,7 @@ public class HabitCardListAdapter
return observable;
}
@Override
@NonNull
public List<Habit> getSelected()
{
@@ -255,6 +262,7 @@ public class HabitCardListAdapter
*
* @param habits list of habits to be removed
*/
@Override
public void performRemove(List<Habit> habits)
{
for (Habit h : habits)
@@ -278,11 +286,13 @@ public class HabitCardListAdapter
cache.reorder(from, to);
}
@Override
public void refresh()
{
cache.refreshAllHabits();
}
@Override
public void setFilter(HabitMatcher matcher)
{
cache.setFilter(matcher);
@@ -300,6 +310,7 @@ public class HabitCardListAdapter
this.listView = listView;
}
@Override
public void setOrder(HabitList.Order order)
{
cache.setOrder(order);

View File

@@ -1,408 +0,0 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.activities.habits.list.model;
import android.support.annotation.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.commands.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.tasks.*;
import org.isoron.uhabits.utils.*;
import java.util.*;
import javax.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.
* <p>
* Note that this class is singleton-scoped, therefore it is shared among all
* activities.
*/
@AppScope
public class HabitCardListCache implements CommandRunner.Listener
{
private int checkmarkCount;
private Task currentFetchTask;
@NonNull
private Listener listener;
@NonNull
private CacheData data;
@NonNull
private HabitList allHabits;
@NonNull
private HabitList filteredHabits;
private final TaskRunner taskRunner;
private final CommandRunner commandRunner;
@Inject
public HabitCardListCache(@NonNull HabitList allHabits,
@NonNull CommandRunner commandRunner,
@NonNull TaskRunner taskRunner)
{
this.allHabits = allHabits;
this.commandRunner = commandRunner;
this.filteredHabits = allHabits;
this.taskRunner = taskRunner;
this.listener = new Listener() {};
data = new CacheData();
}
public void cancelTasks()
{
if (currentFetchTask != null) currentFetchTask.cancel();
}
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.habits.get(position);
}
public int getHabitCount()
{
return data.habits.size();
}
public HabitList.Order getOrder()
{
return filteredHabits.getOrder();
}
public double getScore(long habitId)
{
return data.scores.get(habitId);
}
public void onAttached()
{
refreshAllHabits();
commandRunner.addListener(this);
}
@Override
public void onCommandExecuted(@NonNull Command command,
@Nullable Long refreshKey)
{
if (refreshKey == null) refreshAllHabits();
else refreshHabit(refreshKey);
}
public void onDetached()
{
commandRunner.removeListener(this);
}
public void refreshAllHabits()
{
if (currentFetchTask != null) currentFetchTask.cancel();
currentFetchTask = new RefreshTask();
taskRunner.execute(currentFetchTask);
}
public void refreshHabit(long id)
{
taskRunner.execute(new RefreshTask(id));
}
public void remove(@NonNull Long id)
{
Habit h = data.id_to_habit.get(id);
if (h == null) return;
int position = data.habits.indexOf(h);
data.habits.remove(position);
data.id_to_habit.remove(id);
data.checkmarks.remove(id);
data.scores.remove(id);
listener.onItemRemoved(position);
}
public void reorder(int from, int to)
{
Habit fromHabit = data.habits.get(from);
data.habits.remove(from);
data.habits.add(to, fromHabit);
listener.onItemMoved(from, to);
}
public void setCheckmarkCount(int checkmarkCount)
{
this.checkmarkCount = checkmarkCount;
}
public void setFilter(HabitMatcher matcher)
{
filteredHabits = allHabits.getFiltered(matcher);
}
public void setListener(@NonNull Listener listener)
{
this.listener = listener;
}
public void setOrder(HabitList.Order order)
{
allHabits.setOrder(order);
filteredHabits.setOrder(order);
refreshAllHabits();
}
/**
* Interface definition for a callback to be invoked when the data on the
* cache has been modified.
*/
public interface Listener
{
default void onItemChanged(int position) {}
default void onItemInserted(int position) {}
default void onItemMoved(int oldPosition, int newPosition) {}
default void onItemRemoved(int position) {}
default void onRefreshFinished() {}
}
private class CacheData
{
@NonNull
public HashMap<Long, Habit> id_to_habit;
@NonNull
public List<Habit> habits;
@NonNull
public HashMap<Long, int[]> checkmarks;
@NonNull
public HashMap<Long, Double> scores;
/**
* Creates a new CacheData without any content.
*/
public CacheData()
{
id_to_habit = new HashMap<>();
habits = new LinkedList<>();
checkmarks = new HashMap<>();
scores = new HashMap<>();
}
public void copyCheckmarksFrom(@NonNull CacheData oldData)
{
int[] empty = new int[checkmarkCount];
for (Long id : id_to_habit.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 : id_to_habit.keySet())
{
if (oldData.scores.containsKey(id))
scores.put(id, oldData.scores.get(id));
else scores.put(id, 0.0);
}
}
public void fetchHabits()
{
for (Habit h : filteredHabits)
{
habits.add(h);
id_to_habit.put(h.getId(), h);
}
}
}
private class RefreshTask implements Task
{
@NonNull
private CacheData newData;
@Nullable
private Long targetId;
private boolean isCancelled;
private TaskRunner runner;
public RefreshTask()
{
newData = new CacheData();
targetId = null;
isCancelled = false;
}
public RefreshTask(long targetId)
{
newData = new CacheData();
this.targetId = targetId;
}
@Override
public void cancel()
{
isCancelled = true;
}
@Override
public void doInBackground()
{
newData.fetchHabits();
newData.copyScoresFrom(data);
newData.copyCheckmarksFrom(data);
long day = DateUtils.millisecondsInOneDay;
long dateTo = DateUtils.getStartOfDay(DateUtils.getLocalTime());
long dateFrom = dateTo - (checkmarkCount - 1) * day;
runner.publishProgress(this, -1);
for (int position = 0; position < newData.habits.size(); position++)
{
if (isCancelled) return;
Habit habit = newData.habits.get(position);
Long id = habit.getId();
if (targetId != null && !targetId.equals(id)) continue;
newData.scores.put(id, habit.getScores().getTodayValue());
newData.checkmarks.put(id,
habit.getCheckmarks().getValues(dateFrom, dateTo));
runner.publishProgress(this, position);
}
}
@Override
public void onAttached(@NonNull TaskRunner runner)
{
this.runner = runner;
}
@Override
public void onPostExecute()
{
currentFetchTask = null;
listener.onRefreshFinished();
}
@Override
public void onProgressUpdate(int currentPosition)
{
if (currentPosition < 0) processRemovedHabits();
else processPosition(currentPosition);
}
private void performInsert(Habit habit, int position)
{
Long id = habit.getId();
data.habits.add(position, habit);
data.id_to_habit.put(id, habit);
data.scores.put(id, newData.scores.get(id));
data.checkmarks.put(id, newData.checkmarks.get(id));
listener.onItemInserted(position);
}
private void performMove(Habit habit, int fromPosition, int toPosition)
{
data.habits.remove(fromPosition);
data.habits.add(toPosition, habit);
listener.onItemMoved(fromPosition, toPosition);
}
private void performUpdate(Long id, int position)
{
double oldScore = data.scores.get(id);
int[] oldCheckmarks = data.checkmarks.get(id);
double newScore = newData.scores.get(id);
int[] newCheckmarks = newData.checkmarks.get(id);
boolean unchanged = true;
if (oldScore != newScore) unchanged = false;
if (!Arrays.equals(oldCheckmarks, newCheckmarks)) unchanged = false;
if (unchanged) return;
data.scores.put(id, newScore);
data.checkmarks.put(id, newCheckmarks);
listener.onItemChanged(position);
}
private void processPosition(int currentPosition)
{
Habit habit = newData.habits.get(currentPosition);
Long id = habit.getId();
int prevPosition = data.habits.indexOf(habit);
if (prevPosition < 0) performInsert(habit, currentPosition);
else if (prevPosition == currentPosition)
performUpdate(id, currentPosition);
else performMove(habit, prevPosition, currentPosition);
}
private void processRemovedHabits()
{
Set<Long> before = data.id_to_habit.keySet();
Set<Long> after = newData.id_to_habit.keySet();
Set<Long> removed = new TreeSet<>(before);
removed.removeAll(after);
for (Long id : removed) remove(id);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*

View File

@@ -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.activities.habits.list.model;
import android.support.annotation.*;
import com.google.auto.factory.*;
import org.isoron.uhabits.preferences.*;
import org.isoron.uhabits.utils.*;
/**
* Provides a list of hints to be shown at the application startup, and takes
* care of deciding when a new hint should be shown.
*/
@AutoFactory
public class HintList
{
private final AndroidPreferences prefs;
@NonNull
private final String[] hints;
/**
* Constructs a new list containing the provided hints.
*
* @param hints initial list of hints
*/
public HintList(@Provided @NonNull AndroidPreferences prefs,
@NonNull String hints[])
{
this.prefs = prefs;
this.hints = hints;
}
/**
* 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);
}
}

View File

@@ -1,23 +0,0 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Provides models that are specific for {@link org.isoron.uhabits.activities.habits.list.ListHabitsActivity}.
*/
package org.isoron.uhabits.activities.habits.list.model;

View File

@@ -29,7 +29,7 @@ import android.widget.FrameLayout;
import android.widget.TextView;
import org.isoron.uhabits.R;
import org.isoron.uhabits.activities.habits.list.model.HintList;
import org.isoron.uhabits.ui.screens.habits.list.HintList;
import java.util.Random;

View File

@@ -23,7 +23,7 @@ import android.support.annotation.*;
import org.isoron.uhabits.activities.common.dialogs.*;
import org.isoron.uhabits.activities.habits.show.views.*;
import org.isoron.uhabits.ui.habits.show.*;
import org.isoron.uhabits.ui.screens.habits.show.*;
import javax.inject.*;

View File

@@ -24,7 +24,7 @@ import android.support.annotation.*;
import org.isoron.androidbase.*;
import org.isoron.androidbase.activities.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.ui.habits.show.*;
import org.isoron.uhabits.ui.screens.habits.show.*;
import dagger.*;

View File

@@ -26,7 +26,7 @@ import org.isoron.uhabits.*;
import org.isoron.uhabits.activities.common.dialogs.*;
import org.isoron.uhabits.activities.habits.edit.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.ui.habits.show.*;
import org.isoron.uhabits.ui.screens.habits.show.*;
import javax.inject.*;

View File

@@ -24,7 +24,7 @@ import android.view.*;
import org.isoron.androidbase.activities.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.ui.habits.show.*;
import org.isoron.uhabits.ui.screens.habits.show.*;
import javax.inject.*;

View File

@@ -25,6 +25,7 @@ import android.os.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.receivers.*;
import org.isoron.uhabits.ui.widgets.*;
import org.isoron.uhabits.utils.*;
import dagger.*;
@@ -63,7 +64,7 @@ public class FireSettingReceiver extends BroadcastReceiver
if (args == null) return;
long timestamp = DateUtils.getStartOfToday();
WidgetController controller = component.getWidgetController();
WidgetBehavior controller = component.getWidgetController();
switch (args.action)
{
@@ -102,7 +103,7 @@ public class FireSettingReceiver extends BroadcastReceiver
@Component(dependencies = HabitsComponent.class)
interface ReceiverComponent
{
WidgetController getWidgetController();
WidgetBehavior getWidgetController();
}
private class Arguments

View File

@@ -43,30 +43,6 @@ public class CommandParser
this.modelFactory = modelFactory;
}
@StringRes
public Integer getExecuteString(@NonNull Command command)
{
if(command instanceof ArchiveHabitsCommand)
return R.string.toast_habit_archived;
if(command instanceof ChangeHabitColorCommand)
return R.string.toast_habit_changed;
if(command instanceof CreateHabitCommand)
return R.string.toast_habit_created;
if(command instanceof DeleteHabitsCommand)
return R.string.toast_habit_deleted;
if(command instanceof EditHabitCommand)
return R.string.toast_habit_changed;
if(command instanceof UnarchiveHabitsCommand)
return R.string.toast_habit_unarchived;
return null;
}
@NonNull
public Command parse(@NonNull String json) throws JSONException
{

View File

@@ -33,6 +33,7 @@ import org.isoron.uhabits.intents.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.preferences.*;
import org.isoron.uhabits.tasks.*;
import org.isoron.uhabits.ui.*;
import org.isoron.uhabits.utils.*;
import java.util.*;
@@ -43,8 +44,9 @@ import static android.graphics.BitmapFactory.*;
import static org.isoron.uhabits.notifications.RingtoneManager.*;
@AppScope
public class NotificationTray
implements CommandRunner.Listener, AndroidPreferences.Listener
public class AndroidNotificationTray
implements CommandRunner.Listener, AndroidPreferences.Listener,
NotificationTray
{
@NonNull
private final Context context;
@@ -65,11 +67,11 @@ public class NotificationTray
private final HashMap<Habit, NotificationData> active;
@Inject
public NotificationTray(@AppContext @NonNull Context context,
@NonNull TaskRunner taskRunner,
@NonNull PendingIntentFactory pendingIntents,
@NonNull CommandRunner commandRunner,
@NonNull AndroidPreferences preferences)
public AndroidNotificationTray(@AppContext @NonNull Context context,
@NonNull TaskRunner taskRunner,
@NonNull PendingIntentFactory pendingIntents,
@NonNull CommandRunner commandRunner,
@NonNull AndroidPreferences preferences)
{
this.context = context;
this.taskRunner = taskRunner;
@@ -79,6 +81,7 @@ public class NotificationTray
this.active = new HashMap<>();
}
@Override
public void cancel(@NonNull Habit habit)
{
int notificationId = getNotificationId(habit);

View File

@@ -24,15 +24,13 @@ import android.preference.*;
import org.isoron.androidbase.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.activities.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.ui.*;
import java.util.*;
import javax.inject.*;
import dagger.*;
@AppScope
public class AndroidPreferences
implements SharedPreferences.OnSharedPreferenceChangeListener, Preferences
@@ -103,6 +101,7 @@ public class AndroidPreferences
*
* @return number of last hint shown
*/
@Override
public int getLastHintNumber()
{
return prefs.getInt("last_hint_number", -1);
@@ -113,6 +112,7 @@ public class AndroidPreferences
*
* @return timestamp of the day the last hint was shown
*/
@Override
public long getLastHintTimestamp()
{
return prefs.getLong("last_hint_timestamp", -1);
@@ -128,21 +128,25 @@ public class AndroidPreferences
prefs.edit().putLong("last_sync", timestamp).apply();
}
@Override
public boolean getShowArchived()
{
return prefs.getBoolean("pref_show_archived", false);
}
@Override
public void setShowArchived(boolean showArchived)
{
prefs.edit().putBoolean("pref_show_archived", showArchived).apply();
}
@Override
public boolean getShowCompleted()
{
return prefs.getBoolean("pref_show_completed", true);
}
@Override
public void setShowCompleted(boolean showCompleted)
{
prefs.edit().putBoolean("pref_show_completed", showCompleted).apply();
@@ -155,8 +159,7 @@ public class AndroidPreferences
public String getSyncAddress()
{
return prefs.getString("pref_sync_address",
"https://sync.loophabits.org:4000");
return prefs.getString("pref_sync_address", "https://sync.loophabits.org:4000");
}
public String getSyncClientId()
@@ -174,16 +177,19 @@ public class AndroidPreferences
return prefs.getString("pref_sync_key", "");
}
@Override
public int getTheme()
{
return prefs.getInt("pref_theme", ThemeSwitcher.THEME_LIGHT);
}
@Override
public void setTheme(int theme)
{
prefs.edit().putInt("pref_theme", theme).apply();
}
@Override
public void incrementLaunchCount()
{
int count = prefs.getInt("launch_count", 0);
@@ -200,16 +206,19 @@ public class AndroidPreferences
return prefs.getBoolean("pref_developer", false);
}
@Override
public void setDeveloper(boolean isDeveloper)
{
prefs.edit().putBoolean("pref_developer", isDeveloper).apply();
}
@Override
public boolean isFirstRun()
{
return prefs.getBoolean("pref_first_run", true);
}
@Override
public void setFirstRun(boolean isFirstRun)
{
prefs.edit().putBoolean("pref_first_run", isFirstRun).apply();
@@ -220,6 +229,7 @@ public class AndroidPreferences
return prefs.getBoolean("pref_feature_numerical_habits", false);
}
@Override
public boolean isPureBlackEnabled()
{
return prefs.getBoolean("pref_pure_black", false);
@@ -297,6 +307,7 @@ public class AndroidPreferences
* @param number number of the last hint shown
* @param timestamp timestamp for the day the last hint was shown
*/
@Override
public void updateLastHint(int number, long timestamp)
{
prefs

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
@@ -35,13 +35,13 @@ public class ReminderController
private final ReminderScheduler reminderScheduler;
@NonNull
private final NotificationTray notificationTray;
private final AndroidNotificationTray notificationTray;
private AndroidPreferences preferences;
@Inject
public ReminderController(@NonNull ReminderScheduler reminderScheduler,
@NonNull NotificationTray notificationTray,
@NonNull AndroidNotificationTray notificationTray,
@NonNull AndroidPreferences preferences)
{
this.reminderScheduler = reminderScheduler;

View File

@@ -1,71 +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.receivers;
import android.support.annotation.*;
import org.isoron.uhabits.commands.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.notifications.*;
import javax.inject.*;
@ReceiverScope
public class WidgetController
{
@NonNull
private final CommandRunner commandRunner;
private NotificationTray notificationTray;
@Inject
public WidgetController(@NonNull CommandRunner commandRunner,
@NonNull NotificationTray notificationTray)
{
this.commandRunner = commandRunner;
this.notificationTray = notificationTray;
}
public void onAddRepetition(@NonNull Habit habit, long timestamp)
{
Repetition rep = habit.getRepetitions().getByTimestamp(timestamp);
if (rep != null) return;
performToggle(habit, timestamp);
notificationTray.cancel(habit);
}
public void onRemoveRepetition(@NonNull Habit habit, long timestamp)
{
Repetition rep = habit.getRepetitions().getByTimestamp(timestamp);
if (rep == null) return;
performToggle(habit, timestamp);
}
public void onToggleRepetition(@NonNull Habit habit, long timestamp)
{
performToggle(habit, timestamp);
}
private void performToggle(@NonNull Habit habit, long timestamp)
{
commandRunner.execute(new ToggleRepetitionCommand(habit, timestamp),
habit.getId());
}
}

View File

@@ -26,6 +26,7 @@ import org.isoron.uhabits.*;
import org.isoron.uhabits.intents.*;
import org.isoron.uhabits.preferences.*;
import org.isoron.uhabits.sync.*;
import org.isoron.uhabits.ui.widgets.*;
import dagger.*;
@@ -60,7 +61,7 @@ public class WidgetReceiver extends BroadcastReceiver
.build();
IntentParser parser = app.getComponent().getIntentParser();
WidgetController controller = component.getWidgetController();
WidgetBehavior controller = component.getWidgetController();
AndroidPreferences prefs = app.getComponent().getPreferences();
if(prefs.isSyncFeatureEnabled())
@@ -96,6 +97,6 @@ public class WidgetReceiver extends BroadcastReceiver
@Component(dependencies = HabitsComponent.class)
interface WidgetComponent
{
WidgetController getWidgetController();
WidgetBehavior getWidgetController();
}
}