Fix most issues with the RecyclerView; improve loading

pull/151/head
Alinson S. Xavier 9 years ago
parent 1526f617c5
commit 17423b3ecd

@ -25,12 +25,16 @@ package org.isoron.uhabits.tasks;
public interface ProgressBar 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() {}
} }

@ -42,9 +42,26 @@ public class AndroidProgressBar implements ProgressBar
} }
@Override @Override
public void show() public void setTotal(int total)
{ {
if(total == 0)
progressBar.setIndeterminate(true); progressBar.setIndeterminate(true);
else
{
progressBar.setIndeterminate(false);
progressBar.setMax(total);
}
}
@Override
public void setCurrent(int current)
{
progressBar.setProgress(current);
}
@Override
public void show()
{
progressBar.setVisibility(View.VISIBLE); progressBar.setVisibility(View.VISIBLE);
} }
} }

@ -67,6 +67,8 @@ public class ListHabitsActivity extends BaseActivity
selectionMenu = new ListHabitsSelectionMenu(habits, screen, adapter); selectionMenu = new ListHabitsSelectionMenu(habits, screen, adapter);
controller = new ListHabitsController(habits, screen, system); controller = new ListHabitsController(habits, screen, system);
adapter.setProgressBar(
new AndroidProgressBar(rootView.getProgressBar()));
screen.setMenu(menu); screen.setMenu(menu);
screen.setController(controller); screen.setController(controller);
screen.setSelectionMenu(selectionMenu); screen.setSelectionMenu(selectionMenu);

@ -21,11 +21,11 @@ package org.isoron.uhabits.ui.habits.list.model;
import android.support.annotation.*; import android.support.annotation.*;
import android.support.v7.widget.*; import android.support.v7.widget.*;
import android.util.*;
import android.view.*; import android.view.*;
import org.isoron.uhabits.*; import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*; import org.isoron.uhabits.models.*;
import org.isoron.uhabits.tasks.*;
import org.isoron.uhabits.ui.habits.list.views.*; import org.isoron.uhabits.ui.habits.list.views.*;
import java.util.*; import java.util.*;
@ -137,22 +137,6 @@ public class HabitCardListAdapter
cache.onAttached(); 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 @Override
public void onBindViewHolder(@Nullable HabitCardViewHolder holder, public void onBindViewHolder(@Nullable HabitCardViewHolder holder,
int position) int position)
@ -165,10 +149,6 @@ public class HabitCardListAdapter
int checkmarks[] = cache.getCheckmarks(habit.getId()); int checkmarks[] = cache.getCheckmarks(habit.getId());
boolean selected = this.selected.contains(habit); 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; HabitCardView cardView = (HabitCardView) holder.itemView;
listView.bindCardView(cardView, habit, score, checkmarks, selected, listView.bindCardView(cardView, habit, score, checkmarks, selected,
position); position);
@ -221,7 +201,7 @@ public class HabitCardListAdapter
public void refresh() public void refresh()
{ {
cache.refreshAllHabits(true); cache.refreshAllHabits();
} }
/** /**
@ -259,6 +239,11 @@ public class HabitCardListAdapter
this.listView = listView; this.listView = listView;
} }
public void setProgressBar(ProgressBar progressBar)
{
cache.setProgressBar(progressBar);
}
/** /**
* Selects or deselects the item at a given position. * Selects or deselects the item at a given position.
* *

@ -19,8 +19,8 @@
package org.isoron.uhabits.ui.habits.list.model; package org.isoron.uhabits.ui.habits.list.model;
import android.os.*;
import android.support.annotation.*; import android.support.annotation.*;
import android.util.*;
import org.isoron.uhabits.*; import org.isoron.uhabits.*;
import org.isoron.uhabits.commands.*; import org.isoron.uhabits.commands.*;
@ -49,9 +49,6 @@ public class HabitCardListCache implements CommandRunner.Listener
@Nullable @Nullable
private Listener listener; private Listener listener;
@Nullable
private Long lastLoadTimestamp;
@NonNull @NonNull
private CacheData data; private CacheData data;
@ -64,10 +61,14 @@ public class HabitCardListCache implements CommandRunner.Listener
@NonNull @NonNull
private HabitList filteredHabits; private HabitList filteredHabits;
@NonNull
private ProgressBar progressBar;
public HabitCardListCache(@NonNull HabitList allHabits) public HabitCardListCache(@NonNull HabitList allHabits)
{ {
this.allHabits = allHabits; this.allHabits = allHabits;
this.filteredHabits = allHabits; this.filteredHabits = allHabits;
this.progressBar = new ProgressBar() {};
data = new CacheData(); data = new CacheData();
HabitsApplication.getComponent().inject(this); HabitsApplication.getComponent().inject(this);
} }
@ -100,12 +101,6 @@ public class HabitCardListCache implements CommandRunner.Listener
return data.habits.size(); return data.habits.size();
} }
@Nullable
public Long getLastLoadTimestamp()
{
return lastLoadTimestamp;
}
public int getScore(long habitId) public int getScore(long habitId)
{ {
return data.scores.get(habitId); return data.scores.get(habitId);
@ -113,7 +108,7 @@ public class HabitCardListCache implements CommandRunner.Listener
public void onAttached() public void onAttached()
{ {
refreshAllHabits(true); refreshAllHabits();
commandRunner.addListener(this); commandRunner.addListener(this);
} }
@ -121,7 +116,7 @@ public class HabitCardListCache implements CommandRunner.Listener
public void onCommandExecuted(@NonNull Command command, public void onCommandExecuted(@NonNull Command command,
@Nullable Long refreshKey) @Nullable Long refreshKey)
{ {
if (refreshKey == null) refreshAllHabits(true); if (refreshKey == null) refreshAllHabits();
else refreshHabit(refreshKey); else refreshHabit(refreshKey);
} }
@ -130,17 +125,16 @@ public class HabitCardListCache implements CommandRunner.Listener
commandRunner.removeListener(this); commandRunner.removeListener(this);
} }
public void refreshAllHabits(final boolean refreshScoresAndCheckmarks) public void refreshAllHabits()
{ {
Log.d("HabitCardListCache", "Refreshing all habits");
if (currentFetchTask != null) currentFetchTask.cancel(true); if (currentFetchTask != null) currentFetchTask.cancel(true);
currentFetchTask = new RefreshAllHabitsTask(refreshScoresAndCheckmarks); currentFetchTask = new RefreshTask();
currentFetchTask.execute(); 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) public void reorder(int from, int to)
@ -148,8 +142,7 @@ public class HabitCardListCache implements CommandRunner.Listener
Habit fromHabit = data.habits.get(from); Habit fromHabit = data.habits.get(from);
data.habits.remove(from); data.habits.remove(from);
data.habits.add(to, fromHabit); data.habits.add(to, fromHabit);
if(listener != null) if (listener != null) listener.onItemMoved(from, to);
listener.onItemMoved(from, to);
} }
public void setCheckmarkCount(int checkmarkCount) public void setCheckmarkCount(int checkmarkCount)
@ -167,6 +160,11 @@ public class HabitCardListCache implements CommandRunner.Listener
this.listener = 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 * Interface definition for a callback to be invoked when the data on the
* cache has been modified. * 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 @NonNull
private CacheData newData; private CacheData newData;
private final boolean refreshScoresAndCheckmarks; @Nullable
private Long targetId;
public RefreshAllHabitsTask(boolean refreshScoresAndCheckmarks) public RefreshTask()
{ {
this.refreshScoresAndCheckmarks = refreshScoresAndCheckmarks;
newData = new CacheData(); newData = new CacheData();
targetId = null;
}
public RefreshTask(Long targetId)
{
newData = new CacheData();
this.targetId = targetId;
} }
@Override @Override
@ -263,116 +268,115 @@ public class HabitCardListCache implements CommandRunner.Listener
long day = DateUtils.millisecondsInOneDay; long day = DateUtils.millisecondsInOneDay;
long dateFrom = dateTo - (checkmarkCount - 1) * day; 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(); if (isCancelled()) return;
newData.scores.put(id, h.getScores().getTodayValue());
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, newData.checkmarks.put(id,
h.getCheckmarks().getValues(dateFrom, dateTo)); habit.getCheckmarks().getValues(dateFrom, dateTo));
}
publishProgress(0); publishProgress(position);
}
} }
@Override @Override
protected void onPostExecute(Void aVoid) protected void onPostExecute(Void aVoid)
{ {
lastLoadTimestamp = DateUtils.getStartOfToday();
currentFetchTask = null; currentFetchTask = null;
progressBar.hide();
super.onPostExecute(null); super.onPostExecute(null);
} }
@Override @Override
protected void onProgressUpdate(Integer... values) protected void onPreExecute()
{ {
if(listener == null) throw new RuntimeException( super.onPreExecute();
"listener should have been attached"); new Handler().postDelayed(() -> {
if (getStatus() == Status.RUNNING) progressBar.show();
Set<Long> before = data.id_to_habit.keySet(); }, 1000);
Set<Long> after = newData.id_to_habit.keySet();
Set<Long> 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);
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));
} }
for(int k = 0; k < newData.habits.size(); k++) @Override
protected void onProgressUpdate(Integer... values)
{ {
Habit h = newData.habits.get(k); int currentPosition = values[0];
Long id = h.getId();
Habit prevHabit = data.id_to_habit.get(id); if (currentPosition < 0)
int prevPosition = data.habits.indexOf(prevHabit);
if(prevPosition == k) continue;
if(prevPosition < 0)
{ {
data.habits.add(k, h); progressBar.setTotal(newData.habits.size() - 1);
data.id_to_habit.put(id, h); processRemovedHabits();
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 else
{ {
data.habits.remove(prevPosition); progressBar.setCurrent(currentPosition);
data.habits.add(k, h); processPosition(currentPosition);
listener.onItemMoved(prevPosition, k);
Log.d("HabitCardListCache", String.format("moved %d %d",
prevPosition, k));
}
} }
} }
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);
} }
private class RefreshHabitTask extends BaseTask private void performMove(Habit habit, int toPosition, int fromPosition)
{ {
private final Long id; data.habits.remove(fromPosition);
data.habits.add(toPosition, habit);
if (listener != null)
listener.onItemMoved(fromPosition, toPosition);
}
public RefreshHabitTask(Long id) private void performUpdate(Long id, int position)
{ {
this.id = id; data.scores.put(id, newData.scores.get(id));
data.checkmarks.put(id, newData.checkmarks.get(id));
listener.onItemChanged(position);
} }
@Override private void processPosition(int currentPosition)
protected void doInBackground()
{ {
long dateTo = DateUtils.getStartOfDay(DateUtils.getLocalTime()); Habit habit = newData.habits.get(currentPosition);
long day = DateUtils.millisecondsInOneDay; Long id = habit.getId();
long dateFrom = dateTo - (checkmarkCount - 1) * day;
Habit h = allHabits.getById(id); Habit prevHabit = data.id_to_habit.get(id);
if (h == null) return; int prevPosition = data.habits.indexOf(prevHabit);
data.scores.put(id, h.getScores().getTodayValue()); if (prevPosition < 0) performInsert(habit, currentPosition);
data.checkmarks.put(id, else if (prevPosition == currentPosition)
h.getCheckmarks().getValues(dateFrom, dateTo)); performUpdate(id, currentPosition);
else performMove(habit, currentPosition, prevPosition);
} }
@Override private void processRemovedHabits()
protected void onPostExecute(Void aVoid)
{ {
if (listener != null) 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)
{ {
Habit h = data.id_to_habit.get(id); Habit h = data.id_to_habit.get(id);
int position = data.habits.indexOf(h); 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);
} }
} }
} }

@ -19,19 +19,17 @@
package org.isoron.uhabits.ui.habits.list.views; package org.isoron.uhabits.ui.habits.list.views;
import android.content.Context; import android.content.*;
import android.graphics.Canvas; import android.graphics.*;
import android.view.HapticFeedbackConstants; import android.view.*;
import android.widget.FrameLayout; import android.widget.*;
import android.widget.TextView;
import org.isoron.uhabits.R; import org.isoron.uhabits.*;
import org.isoron.uhabits.models.Checkmark; import org.isoron.uhabits.models.*;
import org.isoron.uhabits.ui.habits.list.controllers.CheckmarkButtonController; import org.isoron.uhabits.ui.habits.list.controllers.*;
import org.isoron.uhabits.utils.InterfaceUtils; import org.isoron.uhabits.utils.*;
import butterknife.BindView; import butterknife.*;
import butterknife.ButterKnife;
public class CheckmarkButtonView extends FrameLayout public class CheckmarkButtonView extends FrameLayout
{ {
@ -58,6 +56,10 @@ public class CheckmarkButtonView extends FrameLayout
{ {
setOnClickListener(v -> controller.onClick()); setOnClickListener(v -> controller.onClick());
setOnLongClickListener(v -> controller.onLongClick()); setOnLongClickListener(v -> controller.onLongClick());
setOnTouchListener((v, ev) -> {
getParent().requestDisallowInterceptTouchEvent(true);
return false;
});
} }
public void setValue(int value) public void setValue(int value)

@ -42,10 +42,11 @@ public class HabitCardListView extends RecyclerView
{ {
super(context, attrs); super(context, attrs);
setLongClickable(true); setLongClickable(true);
setHasFixedSize(true);
setLayoutManager(new LinearLayoutManager(getContext())); setLayoutManager(new LinearLayoutManager(getContext()));
// TouchHelperCallback callback = new TouchHelperCallback(); TouchHelperCallback callback = new TouchHelperCallback();
// new ItemTouchHelper(callback).attachToRecyclerView(this); new ItemTouchHelper(callback).attachToRecyclerView(this);
} }
/** /**

@ -97,8 +97,11 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
if (widgetIds == null) throw new RuntimeException("widgetIds is null"); if (widgetIds == null) throw new RuntimeException("widgetIds is null");
context.setTheme(R.style.TransparentWidgetTheme); context.setTheme(R.style.TransparentWidgetTheme);
new Handler().postDelayed(() ->{
for (int id : widgetIds) for (int id : widgetIds)
update(context, manager, id); update(context, manager, id);
}, 500);
} }
@NonNull @NonNull

@ -40,7 +40,7 @@
<org.isoron.uhabits.ui.habits.list.views.HabitCardListView <org.isoron.uhabits.ui.habits.list.views.HabitCardListView
android:id="@+id/listView" android:id="@+id/listView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:background="?windowBackgroundColor" android:background="?windowBackgroundColor"
android:divider="?windowBackgroundColor" android:divider="?windowBackgroundColor"
android:dividerHeight="1dp" android:dividerHeight="1dp"

Loading…
Cancel
Save