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

This commit is contained in:
Ammar Githam 2021-01-05 00:00:10 +09:00
parent 2b2f390f59
commit 482cbdab97
3 changed files with 87 additions and 43 deletions

View File

@ -58,6 +58,7 @@ public final class DirectItemsAdapter extends RecyclerView.Adapter<RecyclerView.
private final ProfileModel currentUser;
private DirectThread thread;
private final AsyncListDiffer<DirectItemOrHeader> differ;
private List<DirectItem> items;
private static final DiffUtil.ItemCallback<DirectItemOrHeader> diffCallback = new DiffUtil.ItemCallback<DirectItemOrHeader>() {
@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.thread = thread;
differ = new AsyncListDiffer<>(new AdapterListUpdateCallback(this),
new AsyncDifferConfig.Builder<>(diffCallback).build());
// this.onClickListener = onClickListener;
@ -118,9 +121,10 @@ public final class DirectItemsAdapter extends RecyclerView.Adapter<RecyclerView.
return getItemViewHolder(layoutInflater, baseBinding, directItemType);
}
@NonNull
private DirectItemViewHolder getItemViewHolder(final LayoutInflater layoutInflater,
final LayoutDmBaseBinding baseBinding,
final DirectItemType directItemType) {
@NonNull final DirectItemType directItemType) {
switch (directItemType) {
case TEXT: {
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) {
if (thread == null) return;
this.thread = thread;
notifyDataSetChanged();
}
public void submitList(@Nullable final List<DirectItem> list) {
@ -240,6 +245,7 @@ public final class DirectItemsAdapter extends RecyclerView.Adapter<RecyclerView.
return;
}
differ.submitList(sectionAndSort(list));
this.items = list;
}
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;
}
differ.submitList(sectionAndSort(list), commitCallback);
this.items = list;
}
private List<DirectItemOrHeader> sectionAndSort(final List<DirectItem> list) {
@ -289,6 +296,10 @@ public final class DirectItemsAdapter extends RecyclerView.Adapter<RecyclerView.
return differ.getCurrentList();
}
public List<DirectItem> getItems() {
return items;
}
@Override
public void onViewRecycled(@NonNull final RecyclerView.ViewHolder holder) {
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 {
Date date;
DirectItem item;

View File

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

View File

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