Browse Source

Rearrange observers for DirectItemsAdapter so that it is never initialised without a thread

renovate/org.robolectric-robolectric-4.x
Ammar Githam 4 years ago
parent
commit
482cbdab97
  1. 19
      app/src/main/java/awais/instagrabber/adapters/DirectItemsAdapter.java
  2. 2
      app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemViewHolder.java
  3. 101
      app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java

19
app/src/main/java/awais/instagrabber/adapters/DirectItemsAdapter.java

@ -58,6 +58,7 @@ public final class DirectItemsAdapter extends RecyclerView.Adapter<RecyclerView.
private final ProfileModel currentUser; private final ProfileModel currentUser;
private DirectThread thread; private DirectThread thread;
private final AsyncListDiffer<DirectItemOrHeader> differ; private final AsyncListDiffer<DirectItemOrHeader> differ;
private List<DirectItem> items;
private static final DiffUtil.ItemCallback<DirectItemOrHeader> diffCallback = new DiffUtil.ItemCallback<DirectItemOrHeader>() { private static final DiffUtil.ItemCallback<DirectItemOrHeader> diffCallback = new DiffUtil.ItemCallback<DirectItemOrHeader>() {
@Override @Override
@ -97,8 +98,10 @@ public final class DirectItemsAdapter extends RecyclerView.Adapter<RecyclerView.
} }
}; };
public DirectItemsAdapter(@NonNull final ProfileModel currentUser) {
public DirectItemsAdapter(@NonNull final ProfileModel currentUser,
@NonNull final DirectThread thread) {
this.currentUser = currentUser; this.currentUser = currentUser;
this.thread = thread;
differ = new AsyncListDiffer<>(new AdapterListUpdateCallback(this), differ = new AsyncListDiffer<>(new AdapterListUpdateCallback(this),
new AsyncDifferConfig.Builder<>(diffCallback).build()); new AsyncDifferConfig.Builder<>(diffCallback).build());
// this.onClickListener = onClickListener; // this.onClickListener = onClickListener;
@ -118,9 +121,10 @@ public final class DirectItemsAdapter extends RecyclerView.Adapter<RecyclerView.
return getItemViewHolder(layoutInflater, baseBinding, directItemType); return getItemViewHolder(layoutInflater, baseBinding, directItemType);
} }
@NonNull
private DirectItemViewHolder getItemViewHolder(final LayoutInflater layoutInflater, private DirectItemViewHolder getItemViewHolder(final LayoutInflater layoutInflater,
final LayoutDmBaseBinding baseBinding, final LayoutDmBaseBinding baseBinding,
final DirectItemType directItemType) {
@NonNull final DirectItemType directItemType) {
switch (directItemType) { switch (directItemType) {
case TEXT: { case TEXT: {
final LayoutDmTextBinding binding = LayoutDmTextBinding.inflate(layoutInflater, baseBinding.message, false); final LayoutDmTextBinding binding = LayoutDmTextBinding.inflate(layoutInflater, baseBinding.message, false);
@ -232,6 +236,7 @@ public final class DirectItemsAdapter extends RecyclerView.Adapter<RecyclerView.
public void setThread(final DirectThread thread) { public void setThread(final DirectThread thread) {
if (thread == null) return; if (thread == null) return;
this.thread = thread; this.thread = thread;
notifyDataSetChanged();
} }
public void submitList(@Nullable final List<DirectItem> list) { public void submitList(@Nullable final List<DirectItem> list) {
@ -240,6 +245,7 @@ public final class DirectItemsAdapter extends RecyclerView.Adapter<RecyclerView.
return; return;
} }
differ.submitList(sectionAndSort(list)); differ.submitList(sectionAndSort(list));
this.items = list;
} }
public void submitList(@Nullable final List<DirectItem> list, @Nullable final Runnable commitCallback) { public void submitList(@Nullable final List<DirectItem> list, @Nullable final Runnable commitCallback) {
@ -248,6 +254,7 @@ public final class DirectItemsAdapter extends RecyclerView.Adapter<RecyclerView.
return; return;
} }
differ.submitList(sectionAndSort(list), commitCallback); differ.submitList(sectionAndSort(list), commitCallback);
this.items = list;
} }
private List<DirectItemOrHeader> sectionAndSort(final List<DirectItem> list) { private List<DirectItemOrHeader> sectionAndSort(final List<DirectItem> list) {
@ -289,6 +296,10 @@ public final class DirectItemsAdapter extends RecyclerView.Adapter<RecyclerView.
return differ.getCurrentList(); return differ.getCurrentList();
} }
public List<DirectItem> getItems() {
return items;
}
@Override @Override
public void onViewRecycled(@NonNull final RecyclerView.ViewHolder holder) { public void onViewRecycled(@NonNull final RecyclerView.ViewHolder holder) {
if (holder instanceof DirectItemViewHolder) { if (holder instanceof DirectItemViewHolder) {
@ -303,6 +314,10 @@ public final class DirectItemsAdapter extends RecyclerView.Adapter<RecyclerView.
} }
} }
public DirectThread getThread() {
return thread;
}
public static class DirectItemOrHeader { public static class DirectItemOrHeader {
Date date; Date date;
DirectItem item; DirectItem item;

2
app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemViewHolder.java

@ -49,7 +49,7 @@ public abstract class DirectItemViewHolder extends RecyclerView.ViewHolder {
public DirectItemViewHolder(@NonNull final LayoutDmBaseBinding binding, public DirectItemViewHolder(@NonNull final LayoutDmBaseBinding binding,
@NonNull final ProfileModel currentUser, @NonNull final ProfileModel currentUser,
final DirectThread thread,
@NonNull final DirectThread thread,
@NonNull final View.OnClickListener onClickListener) { @NonNull final View.OnClickListener onClickListener) {
super(binding.getRoot()); super(binding.getRoot());
this.binding = binding; this.binding = binding;

101
app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java

@ -14,6 +14,7 @@ import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable; import android.text.Editable;
import android.util.Log; import android.util.Log;
import android.util.Pair;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
@ -30,6 +31,8 @@ import androidx.appcompat.app.ActionBar;
import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MediatorLiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner; import androidx.lifecycle.ViewModelStoreOwner;
@ -313,10 +316,8 @@ public class DirectMessageThreadFragment extends Fragment {
super.onResume(); super.onResume();
fragmentActivity.getWindow().setSoftInputMode(SOFT_INPUT_ADJUST_NOTHING | SOFT_INPUT_STATE_HIDDEN); fragmentActivity.getWindow().setSoftInputMode(SOFT_INPUT_ADJUST_NOTHING | SOFT_INPUT_STATE_HIDDEN);
if (wasKbShowing) { if (wasKbShowing) {
appExecutors.mainThread().execute(() -> {
binding.input.requestFocus(); binding.input.requestFocus();
binding.input.post(this::showKeyboard); binding.input.post(this::showKeyboard);
});
wasKbShowing = false; wasKbShowing = false;
} }
setObservers(); setObservers();
@ -332,12 +333,6 @@ public class DirectMessageThreadFragment extends Fragment {
cleanup(); cleanup();
} }
@Override
public void onDestroy() {
super.onDestroy();
cleanup();
}
private void cleanup() { private void cleanup() {
if (prevTitleRunnable != null) { if (prevTitleRunnable != null) {
appExecutors.mainThread().cancel(prevTitleRunnable); appExecutors.mainThread().cancel(prevTitleRunnable);
@ -363,13 +358,14 @@ public class DirectMessageThreadFragment extends Fragment {
if (context == null) return; if (context == null) return;
if (getArguments() == null) return; if (getArguments() == null) return;
actionBar = fragmentActivity.getSupportActionBar(); actionBar = fragmentActivity.getSupportActionBar();
setObservers();
final DirectMessageThreadFragmentArgs fragmentArgs = DirectMessageThreadFragmentArgs.fromBundle(getArguments()); final DirectMessageThreadFragmentArgs fragmentArgs = DirectMessageThreadFragmentArgs.fromBundle(getArguments());
viewModel.getThreadTitle().postValue(fragmentArgs.getTitle()); viewModel.getThreadTitle().postValue(fragmentArgs.getTitle());
final String threadId = fragmentArgs.getThreadId(); final String threadId = fragmentArgs.getThreadId();
viewModel.setThreadId(threadId); viewModel.setThreadId(threadId);
setupList(); setupList();
root.post(this::setupInput); root.post(this::setupInput);
root.post(this::getInitialData);
setObservers();
} }
private void getInitialData() { private void getInitialData() {
@ -386,9 +382,6 @@ public class DirectMessageThreadFragment extends Fragment {
// setTitle(UPDATING_TITLE); // setTitle(UPDATING_TITLE);
final DirectThread thread = first.get(); final DirectThread thread = first.get();
viewModel.setThread(thread); viewModel.setThread(thread);
if (itemsAdapter != null) {
itemsAdapter.setThread(thread);
}
return; return;
} }
viewModel.fetchChats(); viewModel.fetchChats();
@ -554,15 +547,29 @@ public class DirectMessageThreadFragment extends Fragment {
} }
setTitle(viewModel.getThreadTitle().getValue()); setTitle(viewModel.getThreadTitle().getValue());
}); });
viewModel.getThread().observe(getViewLifecycleOwner(), thread -> {
if (thread != null && itemsAdapter != null) itemsAdapter.setThread(thread);
final ItemsAdapterDataMerger itemsAdapterDataMerger = new ItemsAdapterDataMerger(appStateViewModel.getCurrentUser(), viewModel.getThread());
itemsAdapterDataMerger.observe(getViewLifecycleOwner(), userThreadPair -> {
viewModel.setCurrentUser(userThreadPair.first);
setupItemsAdapter(userThreadPair.first, userThreadPair.second);
}); });
appStateViewModel.getCurrentUser().observe(getViewLifecycleOwner(), currentUser -> {
viewModel.setCurrentUser(currentUser);
setupItemsAdapter(currentUser);
viewModel.getItems().observe(
getViewLifecycleOwner(),
list -> itemsAdapter.submitList(list, () -> {
viewModel.getItems().observe(getViewLifecycleOwner(), this::submitItemsToAdapter);
final NavController navController = NavHostFragment.findNavController(this);
final NavBackStackEntry backStackEntry = navController.getCurrentBackStackEntry();
if (backStackEntry != null) {
final MutableLiveData<Object> resultLiveData = backStackEntry.getSavedStateHandle().getLiveData("result");
resultLiveData.observe(getViewLifecycleOwner(), result -> {
if (!(result instanceof Uri)) return;
final Uri uri = (Uri) result;
viewModel.sendUri(uri);
// clear result
resultLiveData.postValue(null);
});
}
}
private void submitItemsToAdapter(final List<DirectItem> items) {
if (itemsAdapter == null) return;
itemsAdapter.submitList(items, () -> {
itemOrHeaders = itemsAdapter.getList(); itemOrHeaders = itemsAdapter.getList();
binding.chats.post(() -> { binding.chats.post(() -> {
final RecyclerView.LayoutManager layoutManager = binding.chats.getLayoutManager(); final RecyclerView.LayoutManager layoutManager = binding.chats.getLayoutManager();
@ -574,26 +581,27 @@ public class DirectMessageThreadFragment extends Fragment {
} }
} }
}); });
})
);
});
final NavController navController = NavHostFragment.findNavController(this);
final NavBackStackEntry backStackEntry = navController.getCurrentBackStackEntry();
if (backStackEntry != null) {
final MutableLiveData<Object> resultLiveData = backStackEntry.getSavedStateHandle().getLiveData("result");
resultLiveData.observe(getViewLifecycleOwner(), result -> {
if (!(result instanceof Uri)) return;
final Uri uri = (Uri) result;
viewModel.sendUri(uri);
}); });
} }
}
private void setupItemsAdapter(final ProfileModel currentUser) {
if (itemsAdapter != null) return;
itemsAdapter = new DirectItemsAdapter(currentUser);
private void setupItemsAdapter(final ProfileModel currentUser, final DirectThread thread) {
if (itemsAdapter != null) {
if (itemsAdapter.getThread() == thread) return;
itemsAdapter.setThread(thread);
return;
}
itemsAdapter = new DirectItemsAdapter(currentUser, thread);
itemsAdapter.setHasStableIds(true); itemsAdapter.setHasStableIds(true);
itemsAdapter.setStateRestorationPolicy(RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY);
binding.chats.setAdapter(itemsAdapter); binding.chats.setAdapter(itemsAdapter);
registerDataObserver();
final List<DirectItem> items = viewModel.getItems().getValue();
if (items != null && itemsAdapter.getItems() != items) {
submitItemsToAdapter(items);
}
}
private void registerDataObserver() {
itemsAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { itemsAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
@Override @Override
@ -607,7 +615,6 @@ public class DirectMessageThreadFragment extends Fragment {
} }
} }
}); });
root.post(this::getInitialData);
} }
private void setupInput() { private void setupInput() {
@ -1097,4 +1104,26 @@ public class DirectMessageThreadFragment extends Fragment {
}); });
animatorSet.start(); animatorSet.start();
} }
public static class ItemsAdapterDataMerger extends MediatorLiveData<Pair<ProfileModel, DirectThread>> {
private ProfileModel user;
private DirectThread thread;
public ItemsAdapterDataMerger(final LiveData<ProfileModel> userLiveData,
final LiveData<DirectThread> threadLiveData) {
addSource(userLiveData, user -> {
this.user = user;
combine();
});
addSource(threadLiveData, thread -> {
this.thread = thread;
combine();
});
}
private void combine() {
if (user == null || thread == null) return;
setValue(new Pair<>(user, thread));
}
}
} }
Loading…
Cancel
Save