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
{
/**
* 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
public void show()
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.setVisibility(View.VISIBLE);
}
}

@ -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);

@ -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)
@ -165,10 +149,6 @@ public class HabitCardListAdapter
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.
*

@ -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 RefreshAllHabitsTask(boolean refreshScoresAndCheckmarks)
public RefreshTask()
{
this.refreshScoresAndCheckmarks = refreshScoresAndCheckmarks;
newData = new CacheData();
targetId = null;
}
public RefreshTask(Long targetId)
{
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<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);
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));
super.onPreExecute();
new Handler().postDelayed(() -> {
if (getStatus() == Status.RUNNING) progressBar.show();
}, 1000);
}
for(int k = 0; k < newData.habits.size(); k++)
@Override
protected void onProgressUpdate(Integer... values)
{
Habit h = newData.habits.get(k);
Long id = h.getId();
int currentPosition = values[0];
Habit prevHabit = data.id_to_habit.get(id);
int prevPosition = data.habits.indexOf(prevHabit);
if(prevPosition == k) continue;
if(prevPosition < 0)
if (currentPosition < 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));
progressBar.setTotal(newData.habits.size() - 1);
processRemovedHabits();
}
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 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
protected void doInBackground()
private void processPosition(int currentPosition)
{
long dateTo = DateUtils.getStartOfDay(DateUtils.getLocalTime());
long day = DateUtils.millisecondsInOneDay;
long dateFrom = dateTo - (checkmarkCount - 1) * day;
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<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);
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;
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)

@ -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);
}
/**

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

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

Loading…
Cancel
Save