mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 01:08:50 -06:00
Fix most issues with the RecyclerView; improve loading
This commit is contained in:
@@ -24,13 +24,17 @@ package org.isoron.uhabits.tasks;
|
||||
*/
|
||||
public interface ProgressBar
|
||||
{
|
||||
/**
|
||||
* Shows the progress bar.
|
||||
*/
|
||||
void show();
|
||||
|
||||
/**
|
||||
* Hides the progress bar.
|
||||
*/
|
||||
void hide();
|
||||
default void hide() {}
|
||||
|
||||
default void setCurrent(int current) {}
|
||||
|
||||
default void setTotal(int total) {}
|
||||
|
||||
/**
|
||||
* Shows the progress bar.
|
||||
*/
|
||||
default void show() {}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,38 +268,106 @@ public class HabitCardListCache implements CommandRunner.Listener
|
||||
long day = DateUtils.millisecondsInOneDay;
|
||||
long dateFrom = dateTo - (checkmarkCount - 1) * day;
|
||||
|
||||
for(Habit h : newData.habits)
|
||||
{
|
||||
Long id = h.getId();
|
||||
newData.scores.put(id, h.getScores().getTodayValue());
|
||||
newData.checkmarks.put(id,
|
||||
h.getCheckmarks().getValues(dateFrom, dateTo));
|
||||
}
|
||||
publishProgress(-1);
|
||||
|
||||
publishProgress(0);
|
||||
for (int position = 0; position < newData.habits.size(); position++)
|
||||
{
|
||||
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,
|
||||
habit.getCheckmarks().getValues(dateFrom, dateTo));
|
||||
|
||||
publishProgress(position);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid)
|
||||
{
|
||||
lastLoadTimestamp = DateUtils.getStartOfToday();
|
||||
currentFetchTask = null;
|
||||
progressBar.hide();
|
||||
super.onPostExecute(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute()
|
||||
{
|
||||
super.onPreExecute();
|
||||
new Handler().postDelayed(() -> {
|
||||
if (getStatus() == Status.RUNNING) progressBar.show();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(Integer... values)
|
||||
{
|
||||
if(listener == null) throw new RuntimeException(
|
||||
"listener should have been attached");
|
||||
int currentPosition = values[0];
|
||||
|
||||
if (currentPosition < 0)
|
||||
{
|
||||
progressBar.setTotal(newData.habits.size() - 1);
|
||||
processRemovedHabits();
|
||||
}
|
||||
else
|
||||
{
|
||||
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 void performMove(Habit habit, int toPosition, int fromPosition)
|
||||
{
|
||||
data.habits.remove(fromPosition);
|
||||
data.habits.add(toPosition, habit);
|
||||
if (listener != null)
|
||||
listener.onItemMoved(fromPosition, toPosition);
|
||||
}
|
||||
|
||||
private void performUpdate(Long id, int position)
|
||||
{
|
||||
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 prevHabit = data.id_to_habit.get(id);
|
||||
int prevPosition = data.habits.indexOf(prevHabit);
|
||||
|
||||
if (prevPosition < 0) performInsert(habit, currentPosition);
|
||||
else if (prevPosition == currentPosition)
|
||||
performUpdate(id, currentPosition);
|
||||
else performMove(habit, currentPosition, prevPosition);
|
||||
}
|
||||
|
||||
private void processRemovedHabits()
|
||||
{
|
||||
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)
|
||||
for (Long id : removed)
|
||||
{
|
||||
Habit h = data.id_to_habit.get(id);
|
||||
int position = data.habits.indexOf(h);
|
||||
@@ -302,77 +375,8 @@ public class HabitCardListCache implements CommandRunner.Listener
|
||||
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));
|
||||
if (listener != null) listener.onItemRemoved(position);
|
||||
}
|
||||
|
||||
for(int k = 0; k < newData.habits.size(); k++)
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class RefreshHabitTask extends BaseTask
|
||||
{
|
||||
private final Long id;
|
||||
|
||||
public RefreshHabitTask(Long id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInBackground()
|
||||
{
|
||||
long dateTo = DateUtils.getStartOfDay(DateUtils.getLocalTime());
|
||||
long day = DateUtils.millisecondsInOneDay;
|
||||
long dateFrom = dateTo - (checkmarkCount - 1) * day;
|
||||
|
||||
Habit h = allHabits.getById(id);
|
||||
if (h == null) return;
|
||||
|
||||
data.scores.put(id, h.getScores().getTodayValue());
|
||||
data.checkmarks.put(id,
|
||||
h.getCheckmarks().getValues(dateFrom, dateTo));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid)
|
||||
{
|
||||
if (listener != null)
|
||||
{
|
||||
Habit h = data.id_to_habit.get(id);
|
||||
int position = data.habits.indexOf(h);
|
||||
listener.onItemChanged(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"
|
||||
|
||||
Reference in New Issue
Block a user