Simplify ListHabitsActivity

pull/309/head
Alinson S. Xavier 8 years ago
parent 50a6c6d9dd
commit f8a9da59dd

@ -57,7 +57,6 @@ public class CheckmarkPanelViewTest extends BaseViewTest
Checkmark.CHECKED_IMPLICITLY, Checkmark.CHECKED_EXPLICITLY};
view = new CheckmarkPanelView(targetContext);
view.setHabit(habit);
view.setValues(checkmarks);
view.setButtonCount(4);
view.setColor(PaletteUtils.getAndroidTestColor(7));

@ -28,8 +28,6 @@ import org.isoron.uhabits.core.utils.*;
import org.junit.*;
import org.junit.runner.*;
import static org.mockito.Mockito.mock;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class HabitCardViewTest extends BaseViewTest
@ -38,8 +36,6 @@ public class HabitCardViewTest extends BaseViewTest
public static final String PATH = "habits/list/HabitCardView/";
private HabitCardView.Controller controller;
private Habit habit;
@Override
@ -55,14 +51,11 @@ public class HabitCardViewTest extends BaseViewTest
long day = DateUtils.millisecondsInOneDay;
int[] values = checkmarks.getValues(today - 5 * day, today);
controller = mock(HabitCardView.Controller.class);
view = new HabitCardView(targetContext);
view.setHabit(habit);
view.setValues(values);
view.setSelected(false);
view.setScore(habit.getScores().getTodayValue());
view.setController(controller);
view.setButtonCount(6);
measureView(view, dpToPixels(400), dpToPixels(50));
}

@ -23,14 +23,12 @@ import android.support.test.filters.*;
import android.support.test.runner.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.activities.habits.list.controllers.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.CoreMatchers.*;
import static org.mockito.Mockito.*;
import static org.hamcrest.MatcherAssert.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
@ -40,7 +38,6 @@ public class NumberButtonViewTest extends BaseViewTest
private NumberButtonView view;
private NumberButtonController controller;
@Override
@Before
@ -54,9 +51,6 @@ public class NumberButtonViewTest extends BaseViewTest
view.setColor(PaletteUtils.getAndroidTestColor(8));
measureView(view, dpToPixels(48), dpToPixels(48));
controller = mock(NumberButtonController.class);
view.setController(controller);
}
@Test
@ -98,14 +92,4 @@ public class NumberButtonViewTest extends BaseViewTest
view.setValue(0);
assertRenders(view, PATH + "render_zero.png");
}
@Test
public void test_click()
{
view.performClick();
verify(controller).onClick();
view.performLongClick();
verify(controller).onLongClick();
}
}

@ -24,10 +24,10 @@ import org.isoron.uhabits.*;
import org.isoron.uhabits.activities.about.*;
import org.isoron.uhabits.activities.common.dialogs.*;
import org.isoron.uhabits.activities.habits.list.*;
import org.isoron.uhabits.activities.habits.list.controllers.*;
import org.isoron.uhabits.activities.habits.list.model.*;
import org.isoron.uhabits.activities.habits.list.views.*;
import org.isoron.uhabits.activities.habits.show.*;
import org.isoron.uhabits.core.ui.*;
import org.isoron.uhabits.core.ui.screens.habits.list.*;
import dagger.*;
@ -46,14 +46,12 @@ public interface HabitsActivityComponent
AboutScreen getAboutScreen();
BaseActivity getActivity();
CheckmarkButtonControllerFactory getCheckmarkButtonControllerFactory();
ColorPickerDialogFactory getColorPickerDialogFactory();
HabitCardListAdapter getHabitCardListAdapter();
ListHabitsBehavior getListHabitsBehavior();
ListHabitsController getListHabitsController();
ListHabitsMenu getListHabitsMenu();
@ -64,8 +62,6 @@ public interface HabitsActivityComponent
ListHabitsSelectionMenu getListHabitsSelectionMenu();
NumberButtonControllerFactory getNumberButtonControllerFactory();
ShowHabitScreen getShowHabitScreen();
ThemeSwitcher getThemeSwitcher();

@ -22,7 +22,7 @@ package org.isoron.uhabits.activities.habits.list;
import android.os.*;
import org.isoron.uhabits.activities.*;
import org.isoron.uhabits.activities.habits.list.model.*;
import org.isoron.uhabits.activities.habits.list.views.*;
import org.isoron.uhabits.core.preferences.*;
import org.isoron.uhabits.core.ui.*;
import org.isoron.uhabits.core.utils.*;

@ -23,8 +23,7 @@ import android.support.annotation.*;
import org.isoron.androidbase.activities.*;
import org.isoron.uhabits.R;
import org.isoron.uhabits.activities.habits.list.controllers.*;
import org.isoron.uhabits.activities.habits.list.model.*;
import org.isoron.uhabits.activities.habits.list.views.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.tasks.*;
import org.isoron.uhabits.core.ui.screens.habits.list.*;
@ -70,7 +69,6 @@ public class ListHabitsController
this.exportDBFactory = exportDBFactory;
}
@Override
public void onEdit(@NonNull Habit habit, long timestamp)
{
behavior.onEdit(habit, timestamp);
@ -127,7 +125,6 @@ public class ListHabitsController
}));
}
@Override
public void onInvalidEdit()
{
screen.showMessage(R.string.long_press_to_edit);

@ -24,7 +24,7 @@ import android.support.annotation.*;
import org.isoron.androidbase.*;
import org.isoron.uhabits.activities.*;
import org.isoron.uhabits.activities.habits.list.model.*;
import org.isoron.uhabits.activities.habits.list.views.*;
import org.isoron.uhabits.core.ui.screens.habits.list.*;
import javax.inject.*;

@ -29,8 +29,6 @@ import org.isoron.androidbase.activities.*;
import org.isoron.androidbase.utils.*;
import org.isoron.uhabits.R;
import org.isoron.uhabits.activities.common.views.*;
import org.isoron.uhabits.activities.habits.list.controllers.*;
import org.isoron.uhabits.activities.habits.list.model.*;
import org.isoron.uhabits.activities.habits.list.views.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.tasks.*;

@ -24,8 +24,7 @@ import android.view.*;
import org.isoron.androidbase.activities.*;
import org.isoron.uhabits.R;
import org.isoron.uhabits.activities.habits.list.controllers.*;
import org.isoron.uhabits.activities.habits.list.model.*;
import org.isoron.uhabits.activities.habits.list.views.*;
import org.isoron.uhabits.core.commands.*;
import org.isoron.uhabits.core.ui.screens.habits.list.*;

@ -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.activities.habits.list.controllers;
import android.support.annotation.*;
import com.google.auto.factory.*;
import org.isoron.uhabits.activities.habits.list.views.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.preferences.*;
@AutoFactory
public class CheckmarkButtonController
{
@Nullable
private CheckmarkButtonView view;
@Nullable
private Listener listener;
@NonNull
private final Preferences prefs;
@NonNull
private Habit habit;
private long timestamp;
public CheckmarkButtonController(@Provided @NonNull Preferences prefs,
@NonNull Habit habit,
long timestamp)
{
this.habit = habit;
this.timestamp = timestamp;
this.prefs = prefs;
}
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(@NonNull Habit habit, long timestamp);
}
}

@ -1,75 +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.controllers;
import android.support.annotation.*;
import org.isoron.uhabits.activities.habits.list.views.*;
import org.isoron.uhabits.core.models.*;
public class HabitCardController implements HabitCardView.Controller
{
@Nullable
private HabitCardView view;
@Nullable
private Listener listener;
@Override
public void onEdit(@NonNull Habit habit, long timestamp)
{
if(listener != null) listener.onEdit(habit, timestamp);
}
@Override
public void onInvalidEdit()
{
if(listener != null) listener.onInvalidEdit();
}
@Override
public void onInvalidToggle()
{
if (listener != null) listener.onInvalidToggle();
}
@Override
public void onToggle(@NonNull Habit habit, long timestamp)
{
if (view != null) view.triggerRipple(timestamp);
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,
NumberButtonController.Listener
{
}
}

@ -1,93 +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.controllers;
import android.support.annotation.*;
import com.google.auto.factory.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.preferences.*;
@AutoFactory
public class NumberButtonController
{
@Nullable
private Listener listener;
@NonNull
private final Preferences prefs;
@NonNull
private Habit habit;
private long timestamp;
public NumberButtonController(@Provided @NonNull Preferences prefs,
@NonNull Habit habit,
long timestamp)
{
this.habit = habit;
this.timestamp = timestamp;
this.prefs = prefs;
}
public void onClick()
{
if (prefs.isShortToggleEnabled()) performEdit();
else performInvalidToggle();
}
public boolean onLongClick()
{
performEdit();
return true;
}
public void performInvalidToggle()
{
if (listener != null) listener.onInvalidEdit();
}
public void performEdit()
{
if (listener != null) listener.onEdit(habit, timestamp);
}
public void setListener(@Nullable Listener listener)
{
this.listener = listener;
}
public interface Listener
{
/**
* Called when the user's attempt to edit the value is rejected.
*/
void onInvalidEdit();
/**
* Called when a the user's attempt to edit the value has been accepted.
* @param habit the habit being edited
* @param timestamp the timestamp being edited
*/
void onEdit(@NonNull Habit habit, long timestamp);
}
}

@ -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 controllers that are specific for {@link org.isoron.uhabits.activities.habits.list.ListHabitsActivity}.
*/
package org.isoron.uhabits.activities.habits.list.controllers;

@ -29,14 +29,17 @@ import android.view.*;
import org.isoron.androidbase.utils.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.activities.habits.list.controllers.*;
import org.isoron.uhabits.activities.*;
import org.isoron.uhabits.core.preferences.*;
import org.isoron.uhabits.utils.*;
import static android.view.View.MeasureSpec.*;
import static org.isoron.uhabits.core.models.Checkmark.*;
import static org.isoron.uhabits.utils.AttributeSetUtils.*;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.makeMeasureSpec;
import static org.isoron.androidbase.utils.InterfaceUtils.getDimension;
import static org.isoron.androidbase.utils.InterfaceUtils.getFontAwesome;
import static org.isoron.uhabits.core.models.Checkmark.CHECKED_EXPLICITLY;
import static org.isoron.uhabits.core.models.Checkmark.UNCHECKED;
import static org.isoron.uhabits.utils.AttributeSetUtils.getIntAttribute;
public class CheckmarkButtonView extends View
{
@ -52,6 +55,15 @@ public class CheckmarkButtonView extends View
private RectF rect;
@Nullable
private Preferences prefs;
@NonNull
private OnToggleListener onToggleListener;
@NonNull
private OnInvalidToggleListener onInvalidToggleListener;
public CheckmarkButtonView(@Nullable Context context)
{
super(context);
@ -68,7 +80,6 @@ public class CheckmarkButtonView extends View
int paletteColor = getIntAttribute(ctx, attrs, "color", 0);
setColor(PaletteUtils.getAndroidTestColor(paletteColor));
int value = getIntAttribute(ctx, attrs, "value", 0);
setValue(value);
}
@ -79,20 +90,15 @@ public class CheckmarkButtonView extends View
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()
public void performToggle()
{
onToggleListener.onToggle();
value = (value == CHECKED_EXPLICITLY ? UNCHECKED : CHECKED_EXPLICITLY);
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
postInvalidate();
@ -142,5 +148,47 @@ public class CheckmarkButtonView extends View
rect = new RectF();
color = Color.BLACK;
lowContrastColor = styledRes.getColor(R.attr.lowContrastTextColor);
onToggleListener = () -> {};
onInvalidToggleListener = () -> {};
if (getContext() instanceof HabitsActivity)
{
HabitsApplicationComponent component =
((HabitsActivity) getContext()).getAppComponent();
prefs = component.getPreferences();
}
setOnClickListener((v) -> {
if (prefs == null) return;
if (prefs.isShortToggleEnabled()) performToggle();
else onInvalidToggleListener.onInvalidToggle();
});
setOnLongClickListener(v -> {
performToggle();
return true;
});
}
public void setOnInvalidToggleListener(
@NonNull OnInvalidToggleListener onInvalidToggleListener)
{
this.onInvalidToggleListener = onInvalidToggleListener;
}
public void setOnToggleListener(@NonNull OnToggleListener onToggleListener)
{
this.onToggleListener = onToggleListener;
}
public interface OnInvalidToggleListener
{
void onInvalidToggle();
}
public interface OnToggleListener
{
void onToggle();
}
}

@ -25,9 +25,6 @@ import android.util.*;
import android.widget.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.activities.*;
import org.isoron.uhabits.activities.habits.list.controllers.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.preferences.*;
import org.isoron.uhabits.core.utils.*;
@ -35,9 +32,9 @@ import java.util.*;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.makeMeasureSpec;
import static org.isoron.androidbase.utils.InterfaceUtils.getDimension;
import static org.isoron.uhabits.utils.AttributeSetUtils.getIntAttribute;
import static org.isoron.uhabits.utils.PaletteUtils.getAndroidTestColor;
import static org.isoron.androidbase.utils.InterfaceUtils.getDimension;
public class CheckmarkPanelView extends LinearLayout
implements Preferences.Listener
@ -55,12 +52,13 @@ public class CheckmarkPanelView extends LinearLayout
private int color;
private Controller controller;
private int dataOffset;
@NonNull
private Habit habit;
private OnInvalidToggleListener onInvalidToggleListener;
private int dataOffset;
@NonNull
private OnToggleListener onToggleLister;
public CheckmarkPanelView(Context context)
{
@ -115,22 +113,21 @@ public class CheckmarkPanelView extends LinearLayout
setupButtons();
}
public void setController(Controller controller)
public void setDataOffset(int dataOffset)
{
this.controller = controller;
this.dataOffset = dataOffset;
setupButtons();
}
public void setDataOffset(int dataOffset)
public void setOnInvalidToggleListener(
@NonNull OnInvalidToggleListener onInvalidToggleListener)
{
this.dataOffset = dataOffset;
setupButtons();
this.onInvalidToggleListener = onInvalidToggleListener;
}
public void setHabit(@NonNull Habit habit)
public void setOnToggleLister(@NonNull OnToggleListener onToggleLister)
{
this.habit = habit;
setupButtons();
this.onToggleLister = onToggleLister;
}
public void setValues(int[] values)
@ -157,7 +154,8 @@ public class CheckmarkPanelView extends LinearLayout
protected void onMeasure(int widthSpec, int heightSpec)
{
float buttonWidth = getDimension(getContext(), R.dimen.checkmarkWidth);
float buttonHeight = getDimension(getContext(), R.dimen.checkmarkHeight);
float buttonHeight =
getDimension(getContext(), R.dimen.checkmarkHeight);
float width = buttonWidth * nButtons;
@ -190,6 +188,9 @@ public class CheckmarkPanelView extends LinearLayout
prefs = app.getComponent().getPreferences();
}
onInvalidToggleListener = () -> {};
onToggleLister = (t) -> {};
setWillNotDraw(false);
values = new int[0];
}
@ -207,19 +208,10 @@ public class CheckmarkPanelView extends LinearLayout
private void setupButtonControllers(long timestamp,
CheckmarkButtonView buttonView)
{
if (controller == null) return;
if (!(getContext() instanceof HabitsActivity)) return;
HabitsActivity activity = (HabitsActivity) getContext();
CheckmarkButtonControllerFactory buttonControllerFactory = activity
.getActivityComponent()
.getCheckmarkButtonControllerFactory();
CheckmarkButtonController buttonController =
buttonControllerFactory.create(habit, timestamp);
buttonController.setListener(controller);
buttonController.setView(buttonView);
buttonView.setController(buttonController);
buttonView.setOnInvalidToggleListener(
() -> onInvalidToggleListener.onInvalidToggle());
buttonView.setOnToggleListener(
() -> onToggleLister.onToggle(timestamp));
}
private void setupButtons()
@ -239,8 +231,13 @@ public class CheckmarkPanelView extends LinearLayout
}
}
public interface Controller extends CheckmarkButtonController.Listener
public interface OnInvalidToggleListener
{
void onInvalidToggle();
}
public interface OnToggleListener
{
void onToggle(long timestamp);
}
}

@ -17,7 +17,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.activities.habits.list.model;
package org.isoron.uhabits.activities.habits.list.views;
import android.support.annotation.*;
import android.support.v7.widget.*;
@ -25,7 +25,6 @@ import android.view.*;
import org.isoron.androidbase.activities.*;
import org.isoron.uhabits.activities.habits.list.*;
import org.isoron.uhabits.activities.habits.list.views.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.preferences.*;
import org.isoron.uhabits.core.ui.screens.habits.list.*;

@ -17,13 +17,11 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.activities.habits.list.controllers;
package org.isoron.uhabits.activities.habits.list.views;
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.core.models.*;
import javax.inject.*;
@ -200,9 +198,10 @@ public class HabitCardListController implements HabitCardListView.Controller,
if (selectionListener != null) selectionListener.onSelectionFinish();
}
public interface HabitListener extends CheckmarkButtonController.Listener,
NumberButtonController.Listener
public interface HabitListener
{
void onEdit(Habit habit, long timestamp);
/**
* Called when the user clicks a habit.
*
@ -218,6 +217,12 @@ public class HabitCardListController implements HabitCardListView.Controller,
* @param to habit that currently occupies the desired position
*/
void onHabitReorder(@NonNull Habit from, @NonNull Habit to);
void onInvalidEdit();
void onInvalidToggle();
void onToggle(Habit habit, long timestamp);
}
/**

@ -28,8 +28,6 @@ import android.util.*;
import android.view.*;
import org.isoron.uhabits.activities.common.views.*;
import org.isoron.uhabits.activities.habits.list.controllers.*;
import org.isoron.uhabits.activities.habits.list.model.*;
import org.isoron.uhabits.core.models.*;
import java.util.*;
@ -39,7 +37,7 @@ public class HabitCardListView extends RecyclerView
@Nullable
private HabitCardListAdapter adapter;
@Nullable
@NonNull
private Controller controller;
private final ItemTouchHelper touchHelper;
@ -62,6 +60,7 @@ public class HabitCardListView extends RecyclerView
touchHelper.attachToRecyclerView(this);
attachedHolders = new LinkedList<>();
controller = new Controller() {};
}
public void attachCardView(HabitCardViewHolder holder)
@ -97,7 +96,7 @@ public class HabitCardListView extends RecyclerView
cardView.setScore(score);
cardView.setUnit(habit.getUnit());
cardView.setThreshold(habit.getTargetValue());
if (controller != null) setupCardViewController(holder);
setupCardViewListeners(holder);
return cardView;
}
@ -123,7 +122,7 @@ public class HabitCardListView extends RecyclerView
this.checkmarkCount = checkmarkCount;
}
public void setController(@Nullable Controller controller)
public void setController(@NonNull Controller controller)
{
this.controller = controller;
}
@ -175,13 +174,15 @@ public class HabitCardListView extends RecyclerView
return new BundleSavedState(superState, bundle);
}
protected void setupCardViewController(@NonNull HabitCardViewHolder holder)
protected void setupCardViewListeners(@NonNull HabitCardViewHolder holder)
{
HabitCardView cardView = (HabitCardView) holder.itemView;
HabitCardController cardController = new HabitCardController();
cardController.setListener(controller);
cardView.setController(cardController);
cardController.setView(cardView);
cardView.setOnInvalidEditListener(() -> controller.onInvalidEdit());
cardView.setOnInvalidToggleListener(() -> controller.onInvalidToggle());
cardView.setOnToggleListener(
(habit, timestamp) -> controller.onToggle(habit, timestamp));
cardView.setOnEditListener(
(habit, timestamp) -> controller.onEdit(habit, timestamp));
GestureDetector detector = new GestureDetector(getContext(),
new CardViewGestureDetector(holder));
@ -193,15 +194,22 @@ public class HabitCardListView extends RecyclerView
}
public interface Controller
extends CheckmarkButtonController.Listener, HabitCardController.Listener
{
void drop(int from, int to);
default void drop(int from, int to) {}
default void onItemClick(int pos) {}
default void onItemLongClick(int pos) {}
default void startDrag(int position) {}
default void onInvalidToggle() {}
void onItemClick(int pos);
default void onInvalidEdit() {}
void onItemLongClick(int pos);
default void onToggle(@NonNull Habit habit, long timestamp) {}
void startDrag(int position);
default void onEdit(@NonNull Habit habit, long timestamp) {}
}
private class CardViewGestureDetector

@ -38,15 +38,16 @@ import org.isoron.uhabits.utils.*;
import java.util.*;
import static android.os.Build.VERSION.*;
import static android.os.Build.VERSION_CODES.*;
import static android.view.ViewGroup.LayoutParams.*;
import static org.isoron.androidbase.utils.InterfaceUtils.*;
import static android.os.Build.VERSION.SDK_INT;
import static android.os.Build.VERSION_CODES.LOLLIPOP;
import static android.os.Build.VERSION_CODES.M;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static org.isoron.androidbase.utils.InterfaceUtils.dpToPixels;
public class HabitCardView extends FrameLayout
implements ModelObservable.Listener
{
private static final String EDIT_MODE_HABITS[] = {
"Wake up early",
"Wash dishes",
@ -76,6 +77,18 @@ public class HabitCardView extends FrameLayout
private int dataOffset;
@NonNull
OnInvalidEditListener onInvalidEditListener;
@NonNull
OnEditListener onEditListener;
@NonNull
private OnToggleListener onToggleListener;
@NonNull
private OnInvalidToggleListener onInvalidToggleListener;
public HabitCardView(Context context)
{
super(context);
@ -100,15 +113,6 @@ public class HabitCardView extends FrameLayout
numberPanel.setButtonCount(buttonCount);
}
public void setController(Controller controller)
{
checkmarkPanel.setController(null);
numberPanel.setController(null);
if (controller == null) return;
checkmarkPanel.setController(controller);
numberPanel.setController(controller);
}
public void setDataOffset(int dataOffset)
{
this.dataOffset = dataOffset;
@ -119,11 +123,7 @@ public class HabitCardView extends FrameLayout
public void setHabit(@NonNull Habit habit)
{
if (isAttachedToWindow()) stopListening();
this.habit = habit;
checkmarkPanel.setHabit(habit);
numberPanel.setHabit(habit);
refresh();
if (isAttachedToWindow()) startListening();
postInvalidate();
@ -214,9 +214,25 @@ public class HabitCardView extends FrameLayout
initScoreRing();
initLabel();
onInvalidEditListener = () -> {};
onInvalidToggleListener = () -> {};
onEditListener = ((h, t) -> {});
onToggleListener = ((h,t) -> {});
checkmarkPanel = new CheckmarkPanelView(context);
checkmarkPanel.setOnToggleLister(
(t) -> onToggleListener.onToggle(habit, t));
checkmarkPanel.setOnInvalidToggleListener(
() -> onInvalidToggleListener.onInvalidToggle());
numberPanel = new NumberPanelView(context);
numberPanel.setVisibility(GONE);
numberPanel.setOnInvalidEditListener(() ->
onInvalidEditListener.onInvalidEdit());
numberPanel.setOnEditListener(((t) -> {
triggerRipple(t);
onEditListener.onEdit(habit, t);
}));
innerFrame.addView(scoreRing);
innerFrame.addView(label);
@ -346,9 +362,45 @@ public class HabitCardView extends FrameLayout
}
}
public interface Controller
extends CheckmarkPanelView.Controller, NumberPanelView.Controller
public void setOnEditListener(@NonNull OnEditListener onEditListener)
{
this.onEditListener = onEditListener;
}
public void setOnInvalidEditListener(
@NonNull OnInvalidEditListener onInvalidEditListener)
{
this.onInvalidEditListener = onInvalidEditListener;
}
public void setOnToggleListener(@NonNull OnToggleListener onToggleListener)
{
this.onToggleListener = onToggleListener;
}
public void setOnInvalidToggleListener(
@NonNull OnInvalidToggleListener onInvalidToggleListener)
{
this.onInvalidToggleListener = onInvalidToggleListener;
}
public interface OnEditListener
{
void onEdit(@NonNull Habit habit, long timestamp);
}
public interface OnInvalidEditListener
{
void onInvalidEdit();
}
public interface OnInvalidToggleListener
{
void onInvalidToggle();
}
public interface OnToggleListener
{
void onToggle(@NonNull Habit habit, long timestamp);
}
}

@ -17,7 +17,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.activities.habits.list.model;
package org.isoron.uhabits.activities.habits.list.views;
import android.support.v7.widget.*;
import android.view.*;

@ -28,15 +28,18 @@ import android.view.*;
import org.isoron.androidbase.utils.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.activities.habits.list.controllers.*;
import org.isoron.uhabits.activities.*;
import org.isoron.uhabits.core.preferences.*;
import org.isoron.uhabits.utils.*;
import java.text.*;
import static org.isoron.uhabits.utils.AttributeSetUtils.*;
import static org.isoron.androidbase.utils.InterfaceUtils.*;
import static org.isoron.androidbase.utils.InterfaceUtils.getDimension;
import static org.isoron.uhabits.utils.AttributeSetUtils.getAttribute;
import static org.isoron.uhabits.utils.AttributeSetUtils.getIntAttribute;
public class NumberButtonView extends View
public class NumberButtonView extends View implements View.OnClickListener,
View.OnLongClickListener
{
private static Typeface BOLD_TYPEFACE =
Typeface.create("sans-serif-condensed", Typeface.BOLD);
@ -64,6 +67,15 @@ public class NumberButtonView extends View
private int darkGrey;
@NonNull
private OnEditListener onEditListener;
@NonNull
private OnInvalidEditListener onInvalidEditListener;
@Nullable
private Preferences prefs;
public NumberButtonView(@Nullable Context context)
{
super(context);
@ -88,10 +100,6 @@ public class NumberButtonView extends View
}
}
/**
* @param v
* @return
*/
public static String formatValue(double v)
{
if (v >= 1e9) return String.format("%.1fG", v / 1e9);
@ -106,20 +114,25 @@ public class NumberButtonView extends View
return new DecimalFormat("#.##").format(v);
}
public void setColor(int color)
@Override
public void onClick(View v)
{
this.color = color;
postInvalidate();
if(prefs == null) return;
if (prefs.isShortToggleEnabled()) onEditListener.onEdit();
else onInvalidEditListener.onInvalidEdit();
}
@Override
public boolean onLongClick(View v)
{
onEditListener.onEdit();
return true;
}
public void setController(@NonNull NumberButtonController controller)
public void setColor(int color)
{
setOnClickListener(v -> controller.onClick());
setOnLongClickListener(v ->
{
controller.onLongClick();
return true;
});
this.color = color;
postInvalidate();
}
public void setThreshold(double threshold)
@ -140,6 +153,17 @@ public class NumberButtonView extends View
postInvalidate();
}
public void setOnEditListener(@NonNull OnEditListener onEditListener)
{
this.onEditListener = onEditListener;
}
public void setOnInvalidEditListener(
@NonNull OnInvalidEditListener onInvalidEditListener)
{
this.onInvalidEditListener = onInvalidEditListener;
}
@Override
protected void onDraw(Canvas canvas)
{
@ -188,5 +212,28 @@ public class NumberButtonView extends View
em = pBold.measureText("m");
lightGrey = sr.getColor(R.attr.lowContrastTextColor);
darkGrey = sr.getColor(R.attr.mediumContrastTextColor);
onEditListener = () -> {};
onInvalidEditListener = () -> {};
setOnClickListener(this);
setOnLongClickListener(this);
if(getContext() instanceof HabitsActivity)
{
HabitsApplicationComponent component =
((HabitsActivity) getContext()).getAppComponent();
prefs = component.getPreferences();
}
}
public interface OnEditListener
{
void onEdit();
}
public interface OnInvalidEditListener
{
void onInvalidEdit();
}
}

@ -25,9 +25,6 @@ import android.util.*;
import android.widget.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.activities.*;
import org.isoron.uhabits.activities.habits.list.controllers.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.preferences.*;
import org.isoron.uhabits.core.utils.*;
@ -35,10 +32,10 @@ import java.util.*;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.makeMeasureSpec;
import static org.isoron.androidbase.utils.InterfaceUtils.getDimension;
import static org.isoron.uhabits.utils.AttributeSetUtils.getAttribute;
import static org.isoron.uhabits.utils.AttributeSetUtils.getIntAttribute;
import static org.isoron.uhabits.utils.PaletteUtils.getAndroidTestColor;
import static org.isoron.androidbase.utils.InterfaceUtils.getDimension;
public class NumberPanelView extends LinearLayout
implements Preferences.Listener
@ -58,14 +55,14 @@ public class NumberPanelView extends LinearLayout
private int color;
private Controller controller;
private String unit;
private int dataOffset;
@NonNull
private Habit habit;
private OnInvalidEditListener onInvalidEditListener;
private int dataOffset;
private OnEditListener onEditListener;
public NumberPanelView(Context context)
{
@ -87,30 +84,24 @@ public class NumberPanelView extends LinearLayout
setUnit(getAttribute(ctx, attrs, "unit", "min"));
}
if(isInEditMode()) initEditMode();
if (isInEditMode()) initEditMode();
}
public void setUnit(String unit)
public NumberButtonView indexToButton(int i)
{
this.unit = unit;
setupButtons();
int position = i;
if (getCheckmarkOrder() == RIGHT_TO_LEFT) position = nButtons - i - 1;
return (NumberButtonView) getChildAt(position);
}
public void initEditMode()
{
double values[] = new double[nButtons];
for(int i = 0; i < nButtons; i++)
for (int i = 0; i < nButtons; i++)
values[i] = new Random().nextDouble() * (threshold * 3);
setValues(values);
}
public NumberButtonView indexToButton(int i)
{
int position = i;
if (getCheckmarkOrder() == RIGHT_TO_LEFT) position = nButtons - i - 1;
return (NumberButtonView) getChildAt(position);
}
@Override
public void onCheckmarkOrderChanged()
{
@ -134,22 +125,21 @@ public class NumberPanelView extends LinearLayout
setupButtons();
}
public void setController(Controller controller)
public void setDataOffset(int dataOffset)
{
this.controller = controller;
this.dataOffset = dataOffset;
setupButtons();
}
public void setDataOffset(int dataOffset)
public void setOnInvalidEditListener(
@NonNull OnInvalidEditListener onInvalidEditListener)
{
this.dataOffset = dataOffset;
setupButtons();
this.onInvalidEditListener = onInvalidEditListener;
}
public void setHabit(@NonNull Habit habit)
public void setOnEditListener(OnEditListener onEditListener)
{
this.habit = habit;
setupButtons();
this.onEditListener = onEditListener;
}
public void setThreshold(double threshold)
@ -158,6 +148,12 @@ public class NumberPanelView extends LinearLayout
setupButtons();
}
public void setUnit(String unit)
{
this.unit = unit;
setupButtons();
}
public void setValues(double[] values)
{
this.values = values;
@ -196,7 +192,6 @@ public class NumberPanelView extends LinearLayout
private void addButtons()
{
removeAllViews();
for (int i = 0; i < nButtons; i++)
addView(new NumberButtonView(getContext()));
}
@ -218,23 +213,19 @@ public class NumberPanelView extends LinearLayout
setWillNotDraw(false);
values = new double[0];
onInvalidEditListener = () -> {};
onEditListener = (t) -> {};
}
private void setupButtonControllers(long timestamp,
NumberButtonView buttonView)
{
if (controller == null) return;
if (!(getContext() instanceof HabitsActivity)) return;
HabitsActivity activity = (HabitsActivity) getContext();
NumberButtonControllerFactory buttonControllerFactory = activity
.getActivityComponent()
.getNumberButtonControllerFactory();
NumberButtonController buttonController =
buttonControllerFactory.create(habit, timestamp);
buttonController.setListener(controller);
buttonView.setController(buttonController);
buttonView.setOnEditListener(
() -> onEditListener.onEdit(timestamp));
buttonView.setOnInvalidEditListener(
() -> onInvalidEditListener.onInvalidEdit());
}
private void setupButtons()
@ -256,7 +247,13 @@ public class NumberPanelView extends LinearLayout
}
}
public interface Controller extends NumberButtonController.Listener
public interface OnInvalidEditListener
{
void onInvalidEdit();
}
public interface OnEditListener
{
void onEdit(long timestamp);
}
}

@ -1,96 +0,0 @@
/*
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.activities.habits.list.controllers;
import org.isoron.uhabits.*;
import org.isoron.uhabits.activities.habits.list.views.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.preferences.*;
import org.junit.*;
import static org.mockito.Mockito.*;
public class CheckmarkButtonControllerTest extends BaseAndroidUnitTest
{
private CheckmarkButtonController controller;
private CheckmarkButtonView view;
private CheckmarkButtonController.Listener listener;
private Habit habit;
private int timestamp;
private Preferences prefs;
@Override
@Before
public void setUp()
{
super.setUp();
timestamp = 0;
habit = mock(Habit.class);
prefs = mock(Preferences.class);
this.view = mock(CheckmarkButtonView.class);
this.listener = mock(CheckmarkButtonController.Listener.class);
this.controller =
new CheckmarkButtonController(prefs, habit, timestamp);
controller.setView(view);
controller.setListener(listener);
}
@Test
public void testOnClick_withShortToggle() throws Exception
{
doReturn(true).when(prefs).isShortToggleEnabled();
controller.onClick();
verifyToggle();
}
@Test
public void testOnClick_withoutShortToggle() throws Exception
{
doReturn(false).when(prefs).isShortToggleEnabled();
controller.onClick();
verifyInvalidToggle();
}
@Test
public void testOnLongClick() throws Exception
{
controller.onLongClick();
verifyToggle();
}
protected void verifyInvalidToggle()
{
verifyZeroInteractions(view);
verify(listener).onInvalidToggle();
}
protected void verifyToggle()
{
verify(view).toggle();
verify(listener).onToggle(habit, timestamp);
}
}

@ -1,72 +0,0 @@
/*
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.activities.habits.list.controllers;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.activities.habits.list.views.*;
import org.isoron.uhabits.core.utils.*;
import org.junit.*;
import static org.mockito.Mockito.*;
public class HabitCardControllerTest extends BaseAndroidUnitTest
{
private Habit habit;
private HabitCardController controller;
private HabitCardController.Listener listener;
private HabitCardView view;
@Override
@Before
public void setUp()
{
super.setUp();
this.habit = mock(Habit.class);
this.listener = mock(HabitCardController.Listener.class);
this.view = mock(HabitCardView.class);
this.controller = new HabitCardController();
controller.setListener(listener);
controller.setView(view);
view.setController(controller);
}
@Test
public void testOnInvalidToggle()
{
controller.onInvalidToggle();
verify(listener).onInvalidToggle();
}
@Test
public void testOnToggle()
{
long timestamp = DateUtils.getStartOfToday();
controller.onToggle(habit, timestamp);
verify(view).triggerRipple(timestamp);
verify(listener).onToggle(habit, timestamp);
}
}

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

Loading…
Cancel
Save