diff --git a/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsActivity.java b/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsActivity.java index d5529c2a1..deb1b3649 100644 --- a/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsActivity.java +++ b/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsActivity.java @@ -25,6 +25,7 @@ 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.utils.*; /** * Activity that allows the user to see and modify the list of habits. @@ -43,6 +44,8 @@ public class ListHabitsActivity extends BaseActivity private Preferences prefs; + private MidnightTimer midnightTimer; + public ListHabitsComponent getListHabitsComponent() { return component; @@ -77,6 +80,8 @@ public class ListHabitsActivity extends BaseActivity screen.setSelectionMenu(selectionMenu); rootView.setController(controller, selectionMenu); + midnightTimer = component.getMidnightTimer(); + setScreen(screen); controller.onStartup(); } @@ -84,6 +89,7 @@ public class ListHabitsActivity extends BaseActivity @Override protected void onPause() { + midnightTimer.onPause(); screen.onDettached(); adapter.cancelRefresh(); super.onPause(); @@ -95,6 +101,7 @@ public class ListHabitsActivity extends BaseActivity adapter.refresh(); screen.onAttached(); rootView.postInvalidate(); + midnightTimer.onResume(); if (prefs.getTheme() == ThemeSwitcher.THEME_DARK && prefs.isPureBlackEnabled() != pureBlack) diff --git a/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsComponent.java b/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsComponent.java index 658d6c573..554c22a3b 100644 --- a/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsComponent.java +++ b/app/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsComponent.java @@ -23,13 +23,14 @@ import org.isoron.uhabits.*; import org.isoron.uhabits.activities.*; import org.isoron.uhabits.activities.habits.list.controllers.*; import org.isoron.uhabits.activities.habits.list.model.*; +import org.isoron.uhabits.utils.*; import dagger.*; @ActivityScope @Component(modules = { ActivityModule.class }, dependencies = { AppComponent.class }) -public interface ListHabitsComponent extends ActivityComponent +public interface ListHabitsComponent { CheckmarkButtonControllerFactory getCheckmarkButtonControllerFactory(); @@ -44,4 +45,6 @@ public interface ListHabitsComponent extends ActivityComponent ListHabitsScreen getScreen(); ListHabitsSelectionMenu getSelectionMenu(); + + MidnightTimer getMidnightTimer(); } diff --git a/app/src/main/java/org/isoron/uhabits/activities/habits/list/model/HabitCardListAdapter.java b/app/src/main/java/org/isoron/uhabits/activities/habits/list/model/HabitCardListAdapter.java index 7b91c3df8..55656b542 100644 --- a/app/src/main/java/org/isoron/uhabits/activities/habits/list/model/HabitCardListAdapter.java +++ b/app/src/main/java/org/isoron/uhabits/activities/habits/list/model/HabitCardListAdapter.java @@ -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.utils.*; import java.util.*; @@ -42,7 +43,7 @@ import javax.inject.*; @ActivityScope public class HabitCardListAdapter extends RecyclerView.Adapter - implements HabitCardListCache.Listener + implements HabitCardListCache.Listener, MidnightTimer.MidnightListener { @NonNull private ModelObservable observable; @@ -59,15 +60,20 @@ public class HabitCardListAdapter @NonNull private Preferences preferences; + private final MidnightTimer midnightTimer; + @Inject public HabitCardListAdapter(@NonNull HabitCardListCache cache, - @NonNull Preferences preferences) + @NonNull Preferences preferences, + @NonNull MidnightTimer midnightTimer) { this.preferences = preferences; this.selected = new LinkedList<>(); this.observable = new ModelObservable(); this.cache = cache; + this.midnightTimer = midnightTimer; + cache.setListener(this); cache.setCheckmarkCount(ListHabitsRootView.MAX_CHECKMARK_COUNT); cache.setOrder(preferences.getDefaultOrder()); @@ -75,6 +81,12 @@ public class HabitCardListAdapter setHasStableIds(true); } + @Override + public void atMidnight() + { + cache.refreshAllHabits(); + } + public void cancelRefresh() { cache.cancelTasks(); @@ -148,6 +160,7 @@ public class HabitCardListAdapter public void onAttached() { cache.onAttached(); + midnightTimer.addListener(this); } @Override @@ -180,6 +193,7 @@ public class HabitCardListAdapter public void onDetached() { cache.onDetached(); + midnightTimer.removeListener(this); } @Override diff --git a/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HeaderView.java b/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HeaderView.java index 70ed6ec91..8f9d7c4d0 100644 --- a/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HeaderView.java +++ b/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HeaderView.java @@ -26,12 +26,14 @@ import android.view.*; import android.widget.*; import org.isoron.uhabits.*; +import org.isoron.uhabits.activities.habits.list.*; import org.isoron.uhabits.preferences.*; import org.isoron.uhabits.utils.*; import java.util.*; -public class HeaderView extends LinearLayout implements Preferences.Listener +public class HeaderView extends LinearLayout + implements Preferences.Listener, MidnightTimer.MidnightListener { private final Context context; @@ -40,6 +42,9 @@ public class HeaderView extends LinearLayout implements Preferences.Listener @Nullable private Preferences prefs; + @Nullable + private MidnightTimer midnightTimer; + public HeaderView(Context context, AttributeSet attrs) { super(context, attrs); @@ -56,6 +61,18 @@ public class HeaderView extends LinearLayout implements Preferences.Listener HabitsApplication app = (HabitsApplication) appContext; prefs = app.getComponent().getPreferences(); } + + if (context instanceof ListHabitsActivity) + { + ListHabitsActivity activity = (ListHabitsActivity) context; + midnightTimer = activity.getListHabitsComponent().getMidnightTimer(); + } + } + + @Override + public void atMidnight() + { + post(() -> createButtons()); } @Override @@ -75,11 +92,13 @@ public class HeaderView extends LinearLayout implements Preferences.Listener { super.onAttachedToWindow(); if (prefs != null) prefs.addListener(this); + if (midnightTimer != null) midnightTimer.addListener(this); } @Override protected void onDetachedFromWindow() { + if (midnightTimer != null) midnightTimer.removeListener(this); if (prefs != null) prefs.removeListener(this); super.onDetachedFromWindow(); } diff --git a/app/src/main/java/org/isoron/uhabits/utils/DateUtils.java b/app/src/main/java/org/isoron/uhabits/utils/DateUtils.java index c85f90df2..cce452145 100644 --- a/app/src/main/java/org/isoron/uhabits/utils/DateUtils.java +++ b/app/src/main/java/org/isoron/uhabits/utils/DateUtils.java @@ -187,6 +187,11 @@ public abstract class DateUtils return getStartOfDay(DateUtils.getLocalTime()); } + public static long millisecondsUntilTomorrow() + { + return getStartOfToday() + millisecondsInOneDay - getLocalTime(); + } + public static GregorianCalendar getStartOfTodayCalendar() { return getCalendar(getStartOfToday()); diff --git a/app/src/main/java/org/isoron/uhabits/utils/MidnightTimer.java b/app/src/main/java/org/isoron/uhabits/utils/MidnightTimer.java new file mode 100644 index 000000000..30021ae71 --- /dev/null +++ b/app/src/main/java/org/isoron/uhabits/utils/MidnightTimer.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +package org.isoron.uhabits.utils; + +import org.isoron.uhabits.activities.*; + +import java.util.*; +import java.util.concurrent.*; + +import javax.inject.*; + +/** + * A class that emits events when a new day starts. + */ +@ActivityScope +public class MidnightTimer +{ + private final List listeners; + + private ScheduledExecutorService executor; + + @Inject + public MidnightTimer() + { + this.listeners = new LinkedList<>(); + } + + public synchronized void addListener(MidnightListener listener) + { + this.listeners.add(listener); + } + + public synchronized void onPause() + { + executor.shutdownNow(); + } + + public synchronized void onResume() + { + executor = Executors.newSingleThreadScheduledExecutor(); + executor.scheduleAtFixedRate(() -> notifyListeners(), + DateUtils.millisecondsUntilTomorrow() + 1000, + DateUtils.millisecondsInOneDay, TimeUnit.MILLISECONDS); + } + + public synchronized void removeListener(MidnightListener listener) + { + this.listeners.remove(listener); + } + + private synchronized void notifyListeners() + { + for (MidnightListener l : listeners) l.atMidnight(); + } + + public interface MidnightListener + { + void atMidnight(); + } +}