diff --git a/app/src/main/java/org/isoron/uhabits/tasks/ProgressBar.java b/app/src/main/java/org/isoron/uhabits/tasks/ProgressBar.java index afbfdac7a..0be0e4052 100644 --- a/app/src/main/java/org/isoron/uhabits/tasks/ProgressBar.java +++ b/app/src/main/java/org/isoron/uhabits/tasks/ProgressBar.java @@ -25,12 +25,16 @@ package org.isoron.uhabits.tasks; public interface ProgressBar { /** - * Shows the progress bar. + * Hides the progress bar. */ - void show(); + default void hide() {} + + default void setCurrent(int current) {} + + default void setTotal(int total) {} /** - * Hides the progress bar. + * Shows the progress bar. */ - void hide(); + default void show() {} } diff --git a/app/src/main/java/org/isoron/uhabits/ui/AndroidProgressBar.java b/app/src/main/java/org/isoron/uhabits/ui/AndroidProgressBar.java index 22604c44c..07a468893 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/AndroidProgressBar.java +++ b/app/src/main/java/org/isoron/uhabits/ui/AndroidProgressBar.java @@ -41,10 +41,27 @@ public class AndroidProgressBar implements ProgressBar progressBar.setVisibility(View.GONE); } + @Override + public void setTotal(int total) + { + if(total == 0) + progressBar.setIndeterminate(true); + else + { + progressBar.setIndeterminate(false); + progressBar.setMax(total); + } + } + + @Override + public void setCurrent(int current) + { + progressBar.setProgress(current); + } + @Override public void show() { - progressBar.setIndeterminate(true); progressBar.setVisibility(View.VISIBLE); } } diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsActivity.java b/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsActivity.java index e6b379435..56d60a443 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsActivity.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsActivity.java @@ -67,6 +67,8 @@ public class ListHabitsActivity extends BaseActivity selectionMenu = new ListHabitsSelectionMenu(habits, screen, adapter); controller = new ListHabitsController(habits, screen, system); + adapter.setProgressBar( + new AndroidProgressBar(rootView.getProgressBar())); screen.setMenu(menu); screen.setController(controller); screen.setSelectionMenu(selectionMenu); diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/list/model/HabitCardListAdapter.java b/app/src/main/java/org/isoron/uhabits/ui/habits/list/model/HabitCardListAdapter.java index 2d73bacc7..a0f091e1b 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/list/model/HabitCardListAdapter.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/list/model/HabitCardListAdapter.java @@ -21,11 +21,11 @@ package org.isoron.uhabits.ui.habits.list.model; import android.support.annotation.*; import android.support.v7.widget.*; -import android.util.*; import android.view.*; import org.isoron.uhabits.*; import org.isoron.uhabits.models.*; +import org.isoron.uhabits.tasks.*; import org.isoron.uhabits.ui.habits.list.views.*; import java.util.*; @@ -137,22 +137,6 @@ public class HabitCardListAdapter cache.onAttached(); } -// @Override -// public View getView(int position, -// @Nullable View view, -// @Nullable ViewGroup parent) -// { -// if (listView == null) return null; -// -// Habit habit = cache.getHabitByPosition(position); -// int score = cache.getScore(habit.getId()); -// int checkmarks[] = cache.getCheckmarks(habit.getId()); -// boolean selected = this.selected.contains(habit); -// -// return listView.buildCardView((HabitCardView) view, habit, score, -// checkmarks, selected); -// } - @Override public void onBindViewHolder(@Nullable HabitCardViewHolder holder, int position) @@ -161,14 +145,10 @@ public class HabitCardListAdapter if (listView == null) return; Habit habit = cache.getHabitByPosition(position); - int score = cache.getScore(habit.getId()); + int score = cache.getScore(habit.getId()); int checkmarks[] = cache.getCheckmarks(habit.getId()); boolean selected = this.selected.contains(habit); - Log.d("HabitCardListView", - String.format("bind pos=%d itemId=%d, habit=%d:%s", position, - holder.getItemId(), habit.getId(), habit.getName())); - HabitCardView cardView = (HabitCardView) holder.itemView; listView.bindCardView(cardView, habit, score, checkmarks, selected, position); @@ -221,7 +201,7 @@ public class HabitCardListAdapter public void refresh() { - cache.refreshAllHabits(true); + cache.refreshAllHabits(); } /** @@ -259,6 +239,11 @@ public class HabitCardListAdapter this.listView = listView; } + public void setProgressBar(ProgressBar progressBar) + { + cache.setProgressBar(progressBar); + } + /** * Selects or deselects the item at a given position. * diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/list/model/HabitCardListCache.java b/app/src/main/java/org/isoron/uhabits/ui/habits/list/model/HabitCardListCache.java index 4d03f0e3e..6c622121c 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/list/model/HabitCardListCache.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/list/model/HabitCardListCache.java @@ -19,8 +19,8 @@ package org.isoron.uhabits.ui.habits.list.model; +import android.os.*; import android.support.annotation.*; -import android.util.*; import org.isoron.uhabits.*; import org.isoron.uhabits.commands.*; @@ -49,9 +49,6 @@ public class HabitCardListCache implements CommandRunner.Listener @Nullable private Listener listener; - @Nullable - private Long lastLoadTimestamp; - @NonNull private CacheData data; @@ -64,10 +61,14 @@ public class HabitCardListCache implements CommandRunner.Listener @NonNull private HabitList filteredHabits; + @NonNull + private ProgressBar progressBar; + public HabitCardListCache(@NonNull HabitList allHabits) { this.allHabits = allHabits; this.filteredHabits = allHabits; + this.progressBar = new ProgressBar() {}; data = new CacheData(); HabitsApplication.getComponent().inject(this); } @@ -100,12 +101,6 @@ public class HabitCardListCache implements CommandRunner.Listener return data.habits.size(); } - @Nullable - public Long getLastLoadTimestamp() - { - return lastLoadTimestamp; - } - public int getScore(long habitId) { return data.scores.get(habitId); @@ -113,7 +108,7 @@ public class HabitCardListCache implements CommandRunner.Listener public void onAttached() { - refreshAllHabits(true); + refreshAllHabits(); commandRunner.addListener(this); } @@ -121,7 +116,7 @@ public class HabitCardListCache implements CommandRunner.Listener public void onCommandExecuted(@NonNull Command command, @Nullable Long refreshKey) { - if (refreshKey == null) refreshAllHabits(true); + if (refreshKey == null) refreshAllHabits(); else refreshHabit(refreshKey); } @@ -130,17 +125,16 @@ public class HabitCardListCache implements CommandRunner.Listener commandRunner.removeListener(this); } - public void refreshAllHabits(final boolean refreshScoresAndCheckmarks) + public void refreshAllHabits() { - Log.d("HabitCardListCache", "Refreshing all habits"); if (currentFetchTask != null) currentFetchTask.cancel(true); - currentFetchTask = new RefreshAllHabitsTask(refreshScoresAndCheckmarks); + currentFetchTask = new RefreshTask(); currentFetchTask.execute(); } - public void refreshHabit(final Long id) + public void refreshHabit(long id) { - new RefreshHabitTask(id).execute(); + new RefreshTask(id).execute(); } public void reorder(int from, int to) @@ -148,8 +142,7 @@ public class HabitCardListCache implements CommandRunner.Listener Habit fromHabit = data.habits.get(from); data.habits.remove(from); data.habits.add(to, fromHabit); - if(listener != null) - listener.onItemMoved(from, to); + if (listener != null) listener.onItemMoved(from, to); } public void setCheckmarkCount(int checkmarkCount) @@ -167,6 +160,11 @@ public class HabitCardListCache implements CommandRunner.Listener this.listener = listener; } + public void setProgressBar(@NonNull ProgressBar progressBar) + { + this.progressBar = progressBar; + } + /** * Interface definition for a callback to be invoked when the data on the * cache has been modified. @@ -239,17 +237,24 @@ public class HabitCardListCache implements CommandRunner.Listener } } - private class RefreshAllHabitsTask extends BaseTask + private class RefreshTask extends BaseTask { @NonNull private CacheData newData; - private final boolean refreshScoresAndCheckmarks; + @Nullable + private Long targetId; + + public RefreshTask() + { + newData = new CacheData(); + targetId = null; + } - public RefreshAllHabitsTask(boolean refreshScoresAndCheckmarks) + public RefreshTask(Long targetId) { - this.refreshScoresAndCheckmarks = refreshScoresAndCheckmarks; newData = new CacheData(); + this.targetId = targetId; } @Override @@ -263,116 +268,115 @@ public class HabitCardListCache implements CommandRunner.Listener long day = DateUtils.millisecondsInOneDay; long dateFrom = dateTo - (checkmarkCount - 1) * day; - for(Habit h : newData.habits) + publishProgress(-1); + + for (int position = 0; position < newData.habits.size(); position++) { - Long id = h.getId(); - newData.scores.put(id, h.getScores().getTodayValue()); + 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, - h.getCheckmarks().getValues(dateFrom, dateTo)); - } + habit.getCheckmarks().getValues(dateFrom, dateTo)); - publishProgress(0); + publishProgress(position); + } } @Override protected void onPostExecute(Void aVoid) { - lastLoadTimestamp = DateUtils.getStartOfToday(); currentFetchTask = null; + progressBar.hide(); super.onPostExecute(null); } @Override - protected void onProgressUpdate(Integer... values) + protected void onPreExecute() { - if(listener == null) throw new RuntimeException( - "listener should have been attached"); - - Set before = data.id_to_habit.keySet(); - Set after = newData.id_to_habit.keySet(); + super.onPreExecute(); + new Handler().postDelayed(() -> { + if (getStatus() == Status.RUNNING) progressBar.show(); + }, 1000); + } - Set removed = new TreeSet<>(before); - removed.removeAll(after); + @Override + protected void onProgressUpdate(Integer... values) + { + int currentPosition = values[0]; - for(Long id : removed) + if (currentPosition < 0) { - Habit h = data.id_to_habit.get(id); - 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); - Log.d("HabitCardListCache", String.format("removed %d", - position)); + progressBar.setTotal(newData.habits.size() - 1); + processRemovedHabits(); } - - for(int k = 0; k < newData.habits.size(); k++) + else { - Habit h = newData.habits.get(k); - Long id = h.getId(); - - Habit prevHabit = data.id_to_habit.get(id); - int prevPosition = data.habits.indexOf(prevHabit); - if(prevPosition == k) continue; - - if(prevPosition < 0) - { - data.habits.add(k, h); - data.id_to_habit.put(id, h); - data.scores.put(id, newData.scores.get(id)); - data.checkmarks.put(id, newData.checkmarks.get(id)); - listener.onItemInserted(k); - Log.d("HabitCardListCache", String.format("inserted %d", - k)); - } - else - { - data.habits.remove(prevPosition); - data.habits.add(k, h); - listener.onItemMoved(prevPosition, k); - Log.d("HabitCardListCache", String.format("moved %d %d", - prevPosition, k)); - } + progressBar.setCurrent(currentPosition); + processPosition(currentPosition); } } - } - private class RefreshHabitTask extends BaseTask - { - private final Long id; + 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)); + if (listener != null) listener.onItemInserted(position); + } - public RefreshHabitTask(Long id) + private void performMove(Habit habit, int toPosition, int fromPosition) { - this.id = id; + data.habits.remove(fromPosition); + data.habits.add(toPosition, habit); + if (listener != null) + listener.onItemMoved(fromPosition, toPosition); } - @Override - protected void doInBackground() + private void performUpdate(Long id, int position) { - long dateTo = DateUtils.getStartOfDay(DateUtils.getLocalTime()); - long day = DateUtils.millisecondsInOneDay; - long dateFrom = dateTo - (checkmarkCount - 1) * day; + data.scores.put(id, newData.scores.get(id)); + data.checkmarks.put(id, newData.checkmarks.get(id)); + listener.onItemChanged(position); + } + + private void processPosition(int currentPosition) + { + Habit habit = newData.habits.get(currentPosition); + Long id = habit.getId(); - Habit h = allHabits.getById(id); - if (h == null) return; + Habit prevHabit = data.id_to_habit.get(id); + int prevPosition = data.habits.indexOf(prevHabit); - data.scores.put(id, h.getScores().getTodayValue()); - data.checkmarks.put(id, - h.getCheckmarks().getValues(dateFrom, dateTo)); + if (prevPosition < 0) performInsert(habit, currentPosition); + else if (prevPosition == currentPosition) + performUpdate(id, currentPosition); + else performMove(habit, currentPosition, prevPosition); } - @Override - protected void onPostExecute(Void aVoid) + private void processRemovedHabits() { - if (listener != null) + Set before = data.id_to_habit.keySet(); + Set after = newData.id_to_habit.keySet(); + + Set removed = new TreeSet<>(before); + removed.removeAll(after); + + for (Long id : removed) { Habit h = data.id_to_habit.get(id); int position = data.habits.indexOf(h); - listener.onItemChanged(position); + data.habits.remove(position); + data.id_to_habit.remove(id); + data.checkmarks.remove(id); + data.scores.remove(id); + if (listener != null) listener.onItemRemoved(position); } - - super.onPostExecute(null); } } } diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/list/views/CheckmarkButtonView.java b/app/src/main/java/org/isoron/uhabits/ui/habits/list/views/CheckmarkButtonView.java index 2fcd34406..26bbe6003 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/list/views/CheckmarkButtonView.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/list/views/CheckmarkButtonView.java @@ -19,19 +19,17 @@ package org.isoron.uhabits.ui.habits.list.views; -import android.content.Context; -import android.graphics.Canvas; -import android.view.HapticFeedbackConstants; -import android.widget.FrameLayout; -import android.widget.TextView; +import android.content.*; +import android.graphics.*; +import android.view.*; +import android.widget.*; -import org.isoron.uhabits.R; -import org.isoron.uhabits.models.Checkmark; -import org.isoron.uhabits.ui.habits.list.controllers.CheckmarkButtonController; -import org.isoron.uhabits.utils.InterfaceUtils; +import org.isoron.uhabits.*; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.ui.habits.list.controllers.*; +import org.isoron.uhabits.utils.*; -import butterknife.BindView; -import butterknife.ButterKnife; +import butterknife.*; public class CheckmarkButtonView extends FrameLayout { @@ -58,6 +56,10 @@ public class CheckmarkButtonView extends FrameLayout { setOnClickListener(v -> controller.onClick()); setOnLongClickListener(v -> controller.onLongClick()); + setOnTouchListener((v, ev) -> { + getParent().requestDisallowInterceptTouchEvent(true); + return false; + }); } public void setValue(int value) diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/list/views/HabitCardListView.java b/app/src/main/java/org/isoron/uhabits/ui/habits/list/views/HabitCardListView.java index 236eceaea..0eb898e46 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/list/views/HabitCardListView.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/list/views/HabitCardListView.java @@ -42,10 +42,11 @@ public class HabitCardListView extends RecyclerView { super(context, attrs); setLongClickable(true); + setHasFixedSize(true); setLayoutManager(new LinearLayoutManager(getContext())); -// TouchHelperCallback callback = new TouchHelperCallback(); -// new ItemTouchHelper(callback).attachToRecyclerView(this); + TouchHelperCallback callback = new TouchHelperCallback(); + new ItemTouchHelper(callback).attachToRecyclerView(this); } /** diff --git a/app/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java b/app/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java index 93249ed2a..43d8a7122 100644 --- a/app/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java +++ b/app/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java @@ -97,8 +97,11 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider if (widgetIds == null) throw new RuntimeException("widgetIds is null"); context.setTheme(R.style.TransparentWidgetTheme); - for (int id : widgetIds) - update(context, manager, id); + new Handler().postDelayed(() ->{ + for (int id : widgetIds) + update(context, manager, id); + }, 500); + } @NonNull diff --git a/app/src/main/res/layout/list_habits.xml b/app/src/main/res/layout/list_habits.xml index 0eddf17f2..a85a024d1 100644 --- a/app/src/main/res/layout/list_habits.xml +++ b/app/src/main/res/layout/list_habits.xml @@ -40,7 +40,7 @@