package awais.instagrabber.fragments.directmessages; import android.annotation.SuppressLint; import android.content.Context; import android.content.IntentFilter; import android.content.res.Configuration; import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.fragment.app.Fragment; import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelStoreOwner; import androidx.navigation.NavController; import androidx.navigation.NavDirections; import androidx.navigation.fragment.NavHostFragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import com.google.android.material.badge.BadgeDrawable; import com.google.android.material.badge.BadgeUtils; import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.snackbar.Snackbar; import java.util.List; import awais.instagrabber.R; import awais.instagrabber.activities.MainActivity; import awais.instagrabber.adapters.DirectMessageInboxAdapter; import awais.instagrabber.broadcasts.DMRefreshBroadcastReceiver; import awais.instagrabber.customviews.helpers.RecyclerLazyLoaderAtEdge; import awais.instagrabber.databinding.FragmentDirectMessagesInboxBinding; import awais.instagrabber.repositories.responses.directmessages.DirectThread; import awais.instagrabber.viewmodels.DirectInboxViewModel; public class DirectMessageInboxFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener { private static final String TAG = "DirectMessagesInboxFrag"; private CoordinatorLayout root; private RecyclerLazyLoaderAtEdge lazyLoader; private DirectInboxViewModel viewModel; private boolean shouldRefresh = true; private FragmentDirectMessagesInboxBinding binding; private DMRefreshBroadcastReceiver receiver; private DirectMessageInboxAdapter inboxAdapter; private MainActivity fragmentActivity; private boolean scrollToTop = false; private boolean navigating; private Observer> threadsObserver; private MenuItem pendingRequestsMenuItem; private BadgeDrawable pendingRequestTotalBadgeDrawable; private boolean isPendingRequestTotalBadgeAttached; @Override public void onCreate(@Nullable final Bundle savedInstanceState) { super.onCreate(savedInstanceState); fragmentActivity = (MainActivity) getActivity(); if (fragmentActivity != null) { final NavController navController = NavHostFragment.findNavController(this); final ViewModelStoreOwner viewModelStoreOwner = navController.getViewModelStoreOwner(R.id.direct_messages_nav_graph); viewModel = new ViewModelProvider(viewModelStoreOwner).get(DirectInboxViewModel.class); } setHasOptionsMenu(true); } @Override public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { if (root != null) { shouldRefresh = false; return root; } binding = FragmentDirectMessagesInboxBinding.inflate(inflater, container, false); root = binding.getRoot(); return root; } @Override public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) { if (!shouldRefresh) return; init(); } @Override public void onRefresh() { lazyLoader.resetState(); scrollToTop = true; if (viewModel != null) { viewModel.refresh(); } } @Override public void onPause() { super.onPause(); unregisterReceiver(); isPendingRequestTotalBadgeAttached = false; if (pendingRequestTotalBadgeDrawable != null) { BadgeUtils.detachBadgeDrawable(pendingRequestTotalBadgeDrawable, fragmentActivity.getToolbar(), pendingRequestsMenuItem.getItemId()); pendingRequestTotalBadgeDrawable = null; } } @Override public void onResume() { super.onResume(); setupObservers(); final Context context = getContext(); if (context == null) return; receiver = new DMRefreshBroadcastReceiver(() -> { Log.d(TAG, "onResume: broadcast received"); // refreshInbox = true; }); context.registerReceiver(receiver, new IntentFilter(DMRefreshBroadcastReceiver.ACTION_REFRESH_DM)); } @SuppressLint("UnsafeExperimentalUsageError") @Override public void onDestroyView() { super.onDestroyView(); unregisterReceiver(); } @Override public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) { inflater.inflate(R.menu.dm_inbox_menu, menu); pendingRequestsMenuItem = menu.findItem(R.id.pending_requests); pendingRequestsMenuItem.setVisible(isPendingRequestTotalBadgeAttached); } @Override public boolean onOptionsItemSelected(@NonNull final MenuItem item) { if (item.getItemId() == R.id.pending_requests) { final NavDirections directions = DirectMessageInboxFragmentDirections.actionInboxToPendingInbox(); NavHostFragment.findNavController(this).navigate(directions); return true; } return super.onOptionsItemSelected(item); } private void unregisterReceiver() { if (receiver == null) return; final Context context = getContext(); if (context == null) return; context.unregisterReceiver(receiver); receiver = null; } @Override public void onConfigurationChanged(@NonNull final Configuration newConfig) { super.onConfigurationChanged(newConfig); init(); } @Override public void onDestroy() { super.onDestroy(); removeViewModelObservers(); viewModel.onDestroy(); } private void setupObservers() { removeViewModelObservers(); threadsObserver = list -> { if (inboxAdapter == null) return; inboxAdapter.submitList(list, () -> { if (!scrollToTop) return; binding.inboxList.smoothScrollToPosition(0); scrollToTop = false; }); }; viewModel.getThreads().observe(fragmentActivity, threadsObserver); viewModel.getInbox().observe(getViewLifecycleOwner(), inboxResource -> { if (inboxResource == null) return; switch (inboxResource.status) { case SUCCESS: binding.swipeRefreshLayout.setRefreshing(false); break; case ERROR: if (inboxResource.message != null) { Snackbar.make(binding.getRoot(), inboxResource.message, Snackbar.LENGTH_LONG).show(); } if (inboxResource.resId != 0) { Snackbar.make(binding.getRoot(), inboxResource.resId, Snackbar.LENGTH_LONG).show(); } binding.swipeRefreshLayout.setRefreshing(false); break; case LOADING: binding.swipeRefreshLayout.setRefreshing(true); break; } }); viewModel.getUnseenCount().observe(getViewLifecycleOwner(), unseenCountResource -> { if (unseenCountResource == null) return; final Integer unseenCount = unseenCountResource.data; setBottomNavBarBadge(unseenCount == null ? 0 : unseenCount); }); viewModel.getPendingRequestsTotal().observe(getViewLifecycleOwner(), this::attachPendingRequestsBadge); } @SuppressLint("UnsafeExperimentalUsageError") private void attachPendingRequestsBadge(@Nullable final Integer count) { if (pendingRequestsMenuItem == null) { final Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { attachPendingRequestsBadge(count); } }, 500); return; } if (pendingRequestTotalBadgeDrawable == null) { final Context context = getContext(); if (context == null) return; pendingRequestTotalBadgeDrawable = BadgeDrawable.create(context); } if (count == null || count == 0) { BadgeUtils.detachBadgeDrawable(pendingRequestTotalBadgeDrawable, fragmentActivity.getToolbar(), pendingRequestsMenuItem.getItemId()); isPendingRequestTotalBadgeAttached = false; pendingRequestTotalBadgeDrawable.setNumber(0); pendingRequestsMenuItem.setVisible(false); return; } pendingRequestsMenuItem.setVisible(true); if (pendingRequestTotalBadgeDrawable.getNumber() == count) return; pendingRequestTotalBadgeDrawable.setNumber(count); if (!isPendingRequestTotalBadgeAttached) { BadgeUtils.attachBadgeDrawable(pendingRequestTotalBadgeDrawable, fragmentActivity.getToolbar(), pendingRequestsMenuItem.getItemId()); isPendingRequestTotalBadgeAttached = true; } } private void removeViewModelObservers() { if (viewModel == null) return; if (threadsObserver != null) { viewModel.getThreads().removeObserver(threadsObserver); } // no need to explicitly remove observers whose lifecycle owner is getViewLifecycleOwner } private void init() { final Context context = getContext(); if (context == null) return; setupObservers(); binding.swipeRefreshLayout.setOnRefreshListener(this); binding.inboxList.setHasFixedSize(true); binding.inboxList.setItemViewCacheSize(20); final LinearLayoutManager layoutManager = new LinearLayoutManager(context); binding.inboxList.setLayoutManager(layoutManager); inboxAdapter = new DirectMessageInboxAdapter(thread -> { if (navigating) return; navigating = true; if (isAdded()) { final DirectMessageInboxFragmentDirections.ActionInboxToThread directions = DirectMessageInboxFragmentDirections .actionInboxToThread(thread.getThreadId(), thread.getThreadTitle()); NavHostFragment.findNavController(this).navigate(directions); } navigating = false; }); inboxAdapter.setHasStableIds(true); binding.inboxList.setAdapter(inboxAdapter); lazyLoader = new RecyclerLazyLoaderAtEdge(layoutManager, page -> { if (viewModel == null) return; viewModel.fetchInbox(); }); binding.inboxList.addOnScrollListener(lazyLoader); } private void setBottomNavBarBadge(final int unseenCount) { final BottomNavigationView bottomNavView = fragmentActivity.getBottomNavView(); final BadgeDrawable badge = bottomNavView.getOrCreateBadge(R.id.direct_messages_nav_graph); if (badge == null) return; if (unseenCount == 0) { badge.setVisible(false); badge.clearNumber(); return; } if (badge.getVerticalOffset() != 10) { badge.setVerticalOffset(10); } badge.setNumber(unseenCount); badge.setVisible(true); } }