Ammar Githam
4 years ago
20 changed files with 794 additions and 95 deletions
-
68app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java
-
55app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java
-
125app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java
-
152app/src/main/java/awais/instagrabber/fragments/directmessages/DirectPendingInboxFragment.java
-
13app/src/main/java/awais/instagrabber/repositories/DirectMessagesRepository.java
-
7app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectInboxResponse.java
-
15app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectThread.java
-
21app/src/main/java/awais/instagrabber/repositories/responses/directmessages/ThreadContext.java
-
2app/src/main/java/awais/instagrabber/utils/DirectItemFactory.java
-
15app/src/main/java/awais/instagrabber/viewmodels/DirectInboxViewModel.java
-
141app/src/main/java/awais/instagrabber/viewmodels/DirectPendingInboxViewModel.java
-
6app/src/main/java/awais/instagrabber/viewmodels/DirectSettingsViewModel.java
-
77app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.java
-
32app/src/main/java/awais/instagrabber/webservices/DirectMessagesService.java
-
11app/src/main/res/drawable/ic_account_clock_24.xml
-
78app/src/main/res/layout/fragment_direct_messages_thread.xml
-
23app/src/main/res/layout/fragment_direct_pending_inbox.xml
-
43app/src/main/res/navigation/direct_messages_nav_graph.xml
-
1app/src/main/res/values/ids.xml
-
4app/src/main/res/values/strings.xml
@ -0,0 +1,152 @@ |
|||
package awais.instagrabber.fragments.directmessages; |
|||
|
|||
import android.content.Context; |
|||
import android.content.res.Configuration; |
|||
import android.os.Bundle; |
|||
import android.view.LayoutInflater; |
|||
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.fragment.NavHostFragment; |
|||
import androidx.recyclerview.widget.LinearLayoutManager; |
|||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; |
|||
|
|||
import java.util.List; |
|||
|
|||
import awais.instagrabber.R; |
|||
import awais.instagrabber.activities.MainActivity; |
|||
import awais.instagrabber.adapters.DirectMessageInboxAdapter; |
|||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoaderAtEdge; |
|||
import awais.instagrabber.databinding.FragmentDirectPendingInboxBinding; |
|||
import awais.instagrabber.repositories.responses.directmessages.DirectThread; |
|||
import awais.instagrabber.viewmodels.DirectPendingInboxViewModel; |
|||
|
|||
public class DirectPendingInboxFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener { |
|||
private static final String TAG = DirectPendingInboxFragment.class.getSimpleName(); |
|||
|
|||
private CoordinatorLayout root; |
|||
private RecyclerLazyLoaderAtEdge lazyLoader; |
|||
private DirectPendingInboxViewModel viewModel; |
|||
private boolean shouldRefresh = true; |
|||
private FragmentDirectPendingInboxBinding binding; |
|||
private DirectMessageInboxAdapter inboxAdapter; |
|||
private MainActivity fragmentActivity; |
|||
private boolean scrollToTop = false; |
|||
private boolean navigating; |
|||
private Observer<List<DirectThread>> threadsObserver; |
|||
|
|||
@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(DirectPendingInboxViewModel.class); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public View onCreateView(@NonNull final LayoutInflater inflater, |
|||
final ViewGroup container, |
|||
final Bundle savedInstanceState) { |
|||
if (root != null) { |
|||
shouldRefresh = false; |
|||
return root; |
|||
} |
|||
binding = FragmentDirectPendingInboxBinding.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 onResume() { |
|||
super.onResume(); |
|||
setupObservers(); |
|||
} |
|||
|
|||
@Override |
|||
public void onConfigurationChanged(@NonNull final Configuration newConfig) { |
|||
super.onConfigurationChanged(newConfig); |
|||
init(); |
|||
} |
|||
|
|||
@Override |
|||
public void onDestroy() { |
|||
super.onDestroy(); |
|||
removeViewModelObservers(); |
|||
viewModel.onDestroy(); |
|||
} |
|||
|
|||
private void setupObservers() { |
|||
threadsObserver = list -> { |
|||
if (inboxAdapter == null) return; |
|||
inboxAdapter.submitList(list, () -> { |
|||
if (!scrollToTop) return; |
|||
binding.pendingInboxList.smoothScrollToPosition(0); |
|||
scrollToTop = false; |
|||
}); |
|||
}; |
|||
viewModel.getThreads().observe(fragmentActivity, threadsObserver); |
|||
viewModel.getFetchingInbox().observe(getViewLifecycleOwner(), fetching -> binding.swipeRefreshLayout.setRefreshing(fetching)); |
|||
} |
|||
|
|||
private void removeViewModelObservers() { |
|||
if (viewModel == null) return; |
|||
if (threadsObserver != null) { |
|||
viewModel.getThreads().removeObserver(threadsObserver); |
|||
} |
|||
} |
|||
|
|||
private void init() { |
|||
final Context context = getContext(); |
|||
if (context == null) return; |
|||
setupObservers(); |
|||
binding.swipeRefreshLayout.setOnRefreshListener(this); |
|||
binding.pendingInboxList.setHasFixedSize(true); |
|||
binding.pendingInboxList.setItemViewCacheSize(20); |
|||
final LinearLayoutManager layoutManager = new LinearLayoutManager(context); |
|||
binding.pendingInboxList.setLayoutManager(layoutManager); |
|||
inboxAdapter = new DirectMessageInboxAdapter(thread -> { |
|||
if (navigating) return; |
|||
navigating = true; |
|||
if (isAdded()) { |
|||
final DirectPendingInboxFragmentDirections.ActionPendingInboxToThread directions = DirectPendingInboxFragmentDirections |
|||
.actionPendingInboxToThread(thread.getThreadId(), thread.getThreadTitle()); |
|||
directions.setPending(true); |
|||
NavHostFragment.findNavController(this).navigate(directions); |
|||
} |
|||
navigating = false; |
|||
}); |
|||
inboxAdapter.setHasStableIds(true); |
|||
binding.pendingInboxList.setAdapter(inboxAdapter); |
|||
lazyLoader = new RecyclerLazyLoaderAtEdge(layoutManager, page -> { |
|||
if (viewModel == null) return; |
|||
viewModel.fetchInbox(); |
|||
}); |
|||
binding.pendingInboxList.addOnScrollListener(lazyLoader); |
|||
} |
|||
} |
@ -0,0 +1,21 @@ |
|||
package awais.instagrabber.repositories.responses.directmessages; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
public class ThreadContext implements Serializable { |
|||
private final int type; |
|||
private final String text; |
|||
|
|||
public ThreadContext(final int type, final String text) { |
|||
this.type = type; |
|||
this.text = text; |
|||
} |
|||
|
|||
public int getType() { |
|||
return type; |
|||
} |
|||
|
|||
public String getText() { |
|||
return text; |
|||
} |
|||
} |
@ -0,0 +1,141 @@ |
|||
package awais.instagrabber.viewmodels; |
|||
|
|||
import android.util.Log; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.lifecycle.LiveData; |
|||
import androidx.lifecycle.MutableLiveData; |
|||
import androidx.lifecycle.ViewModel; |
|||
|
|||
import java.util.Collection; |
|||
import java.util.LinkedList; |
|||
import java.util.List; |
|||
|
|||
import awais.instagrabber.repositories.responses.User; |
|||
import awais.instagrabber.repositories.responses.directmessages.DirectBadgeCount; |
|||
import awais.instagrabber.repositories.responses.directmessages.DirectInbox; |
|||
import awais.instagrabber.repositories.responses.directmessages.DirectInboxResponse; |
|||
import awais.instagrabber.repositories.responses.directmessages.DirectThread; |
|||
import awais.instagrabber.utils.Constants; |
|||
import awais.instagrabber.utils.CookieUtils; |
|||
import awais.instagrabber.utils.TextUtils; |
|||
import awais.instagrabber.webservices.DirectMessagesService; |
|||
import retrofit2.Call; |
|||
import retrofit2.Callback; |
|||
import retrofit2.Response; |
|||
|
|||
import static awais.instagrabber.utils.Utils.settingsHelper; |
|||
|
|||
public class DirectPendingInboxViewModel extends ViewModel { |
|||
private static final String TAG = DirectPendingInboxViewModel.class.getSimpleName(); |
|||
|
|||
private final DirectMessagesService service; |
|||
private final MutableLiveData<Boolean> fetchingInbox = new MutableLiveData<>(false); |
|||
private final MutableLiveData<List<DirectThread>> threads = new MutableLiveData<>(); |
|||
|
|||
private Call<DirectInboxResponse> inboxRequest; |
|||
private Call<DirectBadgeCount> unseenCountRequest; |
|||
private long seqId; |
|||
private String cursor; |
|||
private boolean hasOlder = true; |
|||
private User viewer; |
|||
|
|||
public DirectPendingInboxViewModel() { |
|||
final String cookie = settingsHelper.getString(Constants.COOKIE); |
|||
final long userId = CookieUtils.getUserIdFromCookie(cookie); |
|||
final String deviceUuid = settingsHelper.getString(Constants.DEVICE_UUID); |
|||
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie); |
|||
if (TextUtils.isEmpty(csrfToken) || userId <= 0 || TextUtils.isEmpty(deviceUuid)) { |
|||
throw new IllegalArgumentException("User is not logged in!"); |
|||
} |
|||
service = DirectMessagesService.getInstance(csrfToken, userId, deviceUuid); |
|||
fetchInbox(); |
|||
} |
|||
|
|||
public LiveData<List<DirectThread>> getThreads() { |
|||
return threads; |
|||
} |
|||
|
|||
public void setThreads(final List<DirectThread> threads) { |
|||
this.threads.postValue(threads); |
|||
} |
|||
|
|||
public void addThreads(final Collection<DirectThread> threads) { |
|||
if (threads == null) return; |
|||
List<DirectThread> list = getThreads().getValue(); |
|||
list = list == null ? new LinkedList<>() : new LinkedList<>(list); |
|||
list.addAll(threads); |
|||
this.threads.postValue(list); |
|||
} |
|||
|
|||
public LiveData<Boolean> getFetchingInbox() { |
|||
return fetchingInbox; |
|||
} |
|||
|
|||
public User getViewer() { |
|||
return viewer; |
|||
} |
|||
|
|||
public void fetchInbox() { |
|||
if ((fetchingInbox.getValue() != null && fetchingInbox.getValue()) || !hasOlder) return; |
|||
stopCurrentInboxRequest(); |
|||
fetchingInbox.postValue(true); |
|||
inboxRequest = service.fetchPendingInbox(cursor, seqId); |
|||
inboxRequest.enqueue(new Callback<DirectInboxResponse>() { |
|||
@Override |
|||
public void onResponse(@NonNull final Call<DirectInboxResponse> call, @NonNull final Response<DirectInboxResponse> response) { |
|||
parseInboxResponse(response.body()); |
|||
fetchingInbox.postValue(false); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(@NonNull final Call<DirectInboxResponse> call, @NonNull final Throwable t) { |
|||
Log.e(TAG, "Failed fetching pending inbox", t); |
|||
fetchingInbox.postValue(false); |
|||
hasOlder = false; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
private void parseInboxResponse(final DirectInboxResponse response) { |
|||
if (response == null) { |
|||
hasOlder = false; |
|||
return; |
|||
} |
|||
if (!response.getStatus().equals("ok")) { |
|||
Log.e(TAG, "DM pending inbox fetch response: status not ok"); |
|||
hasOlder = false; |
|||
return; |
|||
} |
|||
seqId = response.getSeqId(); |
|||
if (viewer == null) { |
|||
viewer = response.getViewer(); |
|||
} |
|||
final DirectInbox inbox = response.getInbox(); |
|||
final List<DirectThread> threads = inbox.getThreads(); |
|||
if (!TextUtils.isEmpty(cursor)) { |
|||
addThreads(threads); |
|||
} else { |
|||
setThreads(threads); |
|||
} |
|||
cursor = inbox.getOldestCursor(); |
|||
hasOlder = inbox.hasOlder(); |
|||
} |
|||
|
|||
private void stopCurrentInboxRequest() { |
|||
if (inboxRequest == null || inboxRequest.isCanceled() || inboxRequest.isExecuted()) return; |
|||
inboxRequest.cancel(); |
|||
inboxRequest = null; |
|||
} |
|||
|
|||
public void refresh() { |
|||
cursor = null; |
|||
seqId = 0; |
|||
hasOlder = true; |
|||
fetchInbox(); |
|||
} |
|||
|
|||
public void onDestroy() { |
|||
stopCurrentInboxRequest(); |
|||
} |
|||
} |
@ -0,0 +1,11 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
|||
android:width="24dp" |
|||
android:height="24dp" |
|||
android:tint="?attr/colorControlNormal" |
|||
android:viewportWidth="24" |
|||
android:viewportHeight="24"> |
|||
<path |
|||
android:fillColor="#000" |
|||
android:pathData="M10.63,14.1C12.23,10.58 16.38,9.03 19.9,10.63C23.42,12.23 24.97,16.38 23.37,19.9C22.24,22.4 19.75,24 17,24C14.3,24 11.83,22.44 10.67,20H1V18C1.06,16.86 1.84,15.93 3.34,15.18C4.84,14.43 6.72,14.04 9,14C9.57,14 10.11,14.05 10.63,14.1V14.1M9,4C10.12,4.03 11.06,4.42 11.81,5.17C12.56,5.92 12.93,6.86 12.93,8C12.93,9.14 12.56,10.08 11.81,10.83C11.06,11.58 10.12,11.95 9,11.95C7.88,11.95 6.94,11.58 6.19,10.83C5.44,10.08 5.07,9.14 5.07,8C5.07,6.86 5.44,5.92 6.19,5.17C6.94,4.42 7.88,4.03 9,4M17,22A5,5 0 0,0 22,17A5,5 0 0,0 17,12A5,5 0 0,0 12,17A5,5 0 0,0 17,22M16,14H17.5V16.82L19.94,18.23L19.19,19.53L16,17.69V14Z" /> |
|||
</vector> |
@ -0,0 +1,23 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|||
xmlns:app="http://schemas.android.com/apk/res-auto" |
|||
xmlns:tools="http://schemas.android.com/tools" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent"> |
|||
|
|||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout |
|||
android:id="@+id/swipeRefreshLayout" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:background="?attr/colorSurface" |
|||
app:layout_behavior="@string/appbar_scrolling_view_behavior"> |
|||
|
|||
<androidx.recyclerview.widget.RecyclerView |
|||
android:id="@+id/pending_inbox_list" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:clipToPadding="false" |
|||
android:paddingBottom="?attr/actionBarSize" |
|||
tools:listitem="@layout/layout_dm_inbox_item" /> |
|||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout> |
|||
</androidx.coordinatorlayout.widget.CoordinatorLayout> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue